Files
msd-core/bin
Tom Boucher 55298b2f70 fix(#2876): yamlQuote SKILL.md description for Copilot/Antigravity/Trae/CodeBuddy (#2881)
* fix(#2876): yamlQuote description in Copilot/Antigravity/Trae/CodeBuddy SKILL.md

A description starting with `[BETA]` (or any YAML flow indicator —
`{`, `*`, `&`, `!`, `|`, `>`, `%`, `@`, backtick) is parsed as a flow
sequence/mapping by YAML 1.2-strict loaders. gh-copilot's frontmatter
loader fails closed:

  ✖ ~/.copilot/skills/gsd-ultraplan-phase/SKILL.md: failed to parse YAML
    frontmatter: Unexpected scalar at node end at line 2, column 21:
    description: [BETA] Offload plan phase to Claude Code's ultraplan…

Six emission sites in `bin/install.js` re-wrote the description without
quoting, while the Claude variant (`convertClaudeCommandToClaudeSkill`)
already routed it through `yamlQuote`. Brought all six in line:

- convertClaudeCommandToCopilotSkill
- convertClaudeAgentToCopilotAgent
- convertClaudeCommandToAntigravitySkill
- convertClaudeAgentToAntigravityAgent
- convertClaudeCommandToTraeSkill
- convertClaudeCommandToCodebuddySkill

Each now wraps the value in `yamlQuote(...)` so any leading character is
parser-safe.

Regression test (tests/bug-2876-skill-frontmatter-quote.test.cjs) drives
the four command converters and two agent converters through the
reporter's exact "[BETA] …" description plus a grab-bag of YAML flow
indicators, asserting the emitted `description:` value is a quoted YAML
scalar. Also round-trips the value through `JSON.parse` for converters
that don't apply runtime-name substitution to confirm fidelity.

Updated 7 pre-existing substring assertions in copilot-install.test.cjs
and antigravity-install.test.cjs that hard-coded the unquoted form.
Round trip: 5893/5893 pass on `npm test`.

Closes #2876

* test(#2876): structurally parse frontmatter instead of substring-grep

Addresses CodeRabbit's two nitpicks on PR #2881: the pre-existing
substring assertions in copilot-install.test.cjs (4 sites) and
antigravity-install.test.cjs (3 sites) only got bumped from the unquoted
form (`description: Diagnose...`) to the quoted-prefix form
(`description: "Diagnose...`). Both are still raw-string checks against
rendered YAML and drift on any quoting/order change — exactly what the
project's CONTRIBUTING.md "no-source-grep" testing standard exists to
prevent.

Add `parseFrontmatter()` to tests/helpers.cjs — a small parser that
handles the YAML scalar forms the install converters emit (double-quoted
JSON, single-quoted with `''` escape, bare). Throws if the content has
no closed `---` block so a regression in the emitter shape fails loudly
rather than silently returning {}.

Refactor the 7 description-substring sites to compare on parsed values:
the assertion now reads as `fm.description === 'Diagnose planning
directory health'` rather than `result.includes('description: "Diagnose
planning directory health')`. Same coverage of the #2876 quoting
behavior, no coupling to byte-level quote style.

`npm test`: 5893/5893 pass.

Closes #2876

* test(#2876): make parseFrontmatter delimiter check CRLF/whitespace tolerant

CR nitpick on PR #2881 (review at 03:08:08Z): parseFrontmatter()
splits on '\n' and compares each line strictly to '---'. A
Windows-authored skill file (CRLF endings) leaves a trailing '\r'
on every line, so '---\r' fails the equality check, and the helper
throws "no closed --- block" on perfectly valid input. Same problem
with whitespace-padded delimiter lines.

Switch to splitting on /\r?\n/ and comparing the trimmed line.
Helper is used by tests/copilot-install.test.cjs and
tests/antigravity-install.test.cjs, so this also de-flakes those
suites on Windows runners.

5893/5893 on `npm test`.
2026-04-29 23:27:27 -04:00
..