Repository navigation
Description
Summary
parseFrontmatter (packages/squad-sdk/src/skills/skill-loader.ts:48) is a naive line-based parser that only understands inline arrays. Two consequences, both of which silently disable skill discovery — no error, no warning, skill just never matches.
Found while strengthening a test in #1946 (the weak assertion there had been masking it).
Defect 1 — YAML block sequences produce no triggers
triggers:
- "set up Squad agentic workflows"The triggers: line has an empty post-colon value, so the parser stores the empty string. parseSkillFile then does Array.isArray(meta.triggers) ? meta.triggers : [] (skill-loader.ts:131) → []. The - "…" lines contain no colon and are skipped entirely.
Defect 2 — inline arrays retain quote characters
The inline branch splits on , and trims, but never strips quotes, so triggers come back as "\"squad commands\"" — with literal " characters that will never match user text.
Measured
block -> "" (undiscoverable)
inline+quoted -> ["\"a phrase\"", ...] (quotes retained — never matches)
inline bare -> ["a phrase", ...] (correct)
Affected skills
27 skills exist; only 3 declare triggers at all:
| Skill | Form | Runtime triggers |
|---|---|---|
.squad/skills/cross-squad/SKILL.md |
block | [] — fully undiscoverable |
.squad/skills/gh-aw-enlistment/SKILL.md |
block | fixed in #1946 by converting to bare inline |
.squad/skills/squad-commands/SKILL.md |
inline + quoted | quote-polluted, will not match |
So every skill that declares triggers is currently mis-parsed, and #1946 worked around it locally rather than fixing the parser.
This is the array the runtime matcher iterates (packages/squad-sdk/src/skills/index.ts:75), so the impact is real skill-matching failure, not cosmetic.
Reproduce
const { parseFrontmatter } = require('packages/squad-sdk/dist/skills/skill-loader.js');
const t = (l, fm) => console.log(l, '->', JSON.stringify(parseFrontmatter('---\n'+fm+'\n---\nbody').meta.triggers));
t('block ', 'triggers:\n - "a phrase"');
t('inline+quot', 'triggers: ["a phrase"]');
t('inline bare', 'triggers: [a phrase]');Blast radius
parseFrontmatter is shared — skill-source.ts calls it in 6 places (lines 68, 87, 107, 160, 177, 195). There is no real YAML parser anywhere in this path, so fixing it changes behavior for all skill loading. Notably, fixing quote-stripping will change squad-commands' runtime triggers from quoted to unquoted, which is the intended correction but is still a behavior change worth calling out in review.
Suggested fix
- Support block sequences: after a key with an empty value, consume following
- itemlines into an array. - Strip surrounding matched quotes from scalar and array values.
- Convert
cross-squadandsquad-commandsto whatever form is then canonical, and pick one documented form for skill authors. - Add a guard asserting that every skill declaring
triggers:parses to a non-empty array — that single assertion would have caught all of this.
Acceptance criteria
- Block-sequence
triggers:parses to a non-empty string array - Quotes stripped from both scalar and array frontmatter values
-
cross-squadandsquad-commandsverified discoverable at runtime - Guard test fails if any skill's declared triggers parse to
[] - Existing 27 skills still load; no regression in
skill-source.tsconsumers
Activity
fix(sdk): parse block-sequence and quoted values in skill frontmatter (…