What happened?
When a SKILL.md frontmatter uses YAML block scalar syntax for the description field:
---
name: my-skill
description: >
This is a multiline folded
description for the skill.
---
The description displays as the literal character > instead of the full multiline text.
Root cause
The custom YAML parser at packages/core/src/utils/yaml-parser.ts splits on the first : and takes the remainder as a plain string value (line 72-96). It has no handling for YAML block scalar indicators (>, >-, |, |-). The subsequent indented continuation lines are either misinterpreted as nested object keys or silently dropped.
The same parser is used in both skill loading paths:
packages/core/src/skills/skill-load.ts (extension skills)
packages/core/src/skills/skill-manager.ts (project/user/bundled skills)
skill-manager.ts already imports the full yaml npm package (v2.8.1) and uses it for hooks: parsing (lines 707-719), with the comment:
// Use full YAML parser for hooks as they have nested structures
But the description field still goes through the custom parser.
Expected behavior
Block scalar descriptions should be parsed correctly per the YAML 1.2 spec. Claude Code handles this correctly by using a full YAML parser (yaml npm v2.8.3+) for all frontmatter fields.
Suggested fix
Replace parseYaml(frontmatterYaml) with yaml.parse(frontmatterYaml) (the yaml npm package already installed) for all frontmatter fields, not just hooks:. This is a one-line change per call site and the downstream field access (frontmatter['description'], frontmatter['name'], etc.) stays identical since both return plain JS objects.
One thing to watch: the custom parser silently returns partial results on malformed YAML, while yaml.parse() throws. The existing try/catch in both skill-load.ts and skill-manager.ts already covers this, but worth verifying edge cases. A fallback to the simple parser on yaml.parse() failure preserves backward compatibility.
Reproduction
- Create a skill with block scalar description:
---
name: test-multiline
description: >
This is a multiline
description that should be folded.
---
# Test skill body
- Load the skill in Qwen Code
- Check the skill picker — description shows
> instead of the full text
What happened?
When a SKILL.md frontmatter uses YAML block scalar syntax for the
descriptionfield:The description displays as the literal character
>instead of the full multiline text.Root cause
The custom YAML parser at
packages/core/src/utils/yaml-parser.tssplits on the first:and takes the remainder as a plain string value (line 72-96). It has no handling for YAML block scalar indicators (>,>-,|,|-). The subsequent indented continuation lines are either misinterpreted as nested object keys or silently dropped.The same parser is used in both skill loading paths:
packages/core/src/skills/skill-load.ts(extension skills)packages/core/src/skills/skill-manager.ts(project/user/bundled skills)skill-manager.tsalready imports the fullyamlnpm package (v2.8.1) and uses it forhooks:parsing (lines 707-719), with the comment:// Use full YAML parser for hooks as they have nested structuresBut the
descriptionfield still goes through the custom parser.Expected behavior
Block scalar descriptions should be parsed correctly per the YAML 1.2 spec. Claude Code handles this correctly by using a full YAML parser (
yamlnpm v2.8.3+) for all frontmatter fields.Suggested fix
Replace
parseYaml(frontmatterYaml)withyaml.parse(frontmatterYaml)(theyamlnpm package already installed) for all frontmatter fields, not justhooks:. This is a one-line change per call site and the downstream field access (frontmatter['description'],frontmatter['name'], etc.) stays identical since both return plain JS objects.One thing to watch: the custom parser silently returns partial results on malformed YAML, while
yaml.parse()throws. The existingtry/catchin bothskill-load.tsandskill-manager.tsalready covers this, but worth verifying edge cases. A fallback to the simple parser onyaml.parse()failure preserves backward compatibility.Reproduction
>instead of the full text