Skip to content

fix(skills): keep unmodeled Copilot SKILL.md frontmatter through the round-trip - #2587

Merged
dyoshikawa merged 2 commits into
mainfrom
resolve-issue-2401-copilot-skill-passthrough
Aug 6, 2026
Merged

fix(skills): keep unmodeled Copilot SKILL.md frontmatter through the round-trip#2587
dyoshikawa merged 2 commits into
mainfrom
resolve-issue-2401-copilot-skill-passthrough

Conversation

@dyoshikawa

@dyoshikawa dyoshikawa commented Aug 6, 2026

Copy link
Copy Markdown
Owner

Summary

Two independent items from the Copilot follow-up issues.

(a) Residual-key skill round-trip (#2401)

CopilotSkillFrontmatterSchema is a z.looseObject, so a hand-authored SKILL.md field Rulesync does not model parsed fine — but toRulesyncSkill built the copilot: section by explicitly copying six known keys, so the field was dropped on import and then erased from the file on the next generate. Same fix class as #2580 applied to CopilotRuleFrontmatterSchema:

  • toRulesyncSkill spreads all residual keys (everything except name/description, which have canonical homes) into the tool section; fromRulesyncSkill spreads the section back out, so the value survives regeneration rather than only living in .rulesync. The section is spread first, so the canonical name/description and the resolved invocation gates still own their keys.
  • Applied to both copilot-skill.ts and copilotcli-skill.ts. The two targets write the same .github/skills/<name>/SKILL.md path and copilotcli is generated last, so fixing only copilot would have left the field dropped again in the common both-targets configuration.
  • Documented in the copilot: and copilotcli: skills sections of docs/reference/file-formats.md, including the caveat that a residual key rides one section only, exactly like the modeled fields.

Note: because the section is now spread before name/description, generated SKILL.md files put the section keys first. This is a key-order change only, no semantic difference, and it matches what copilot-rule.ts already does.

(b) Stale comment (#2402)

copilotcli-hooks.ts said the global hooks location was "a rulesync convention pending official documentation". Upstream documents it: "User-level hook files — *.json files in the user-level hooks directory. By default this is ~/.copilot/hooks/ on macOS and Linux, or %USERPROFILE%\\.copilot\\hooks\\ on Windows" (github/docs content/copilot/reference/hooks-reference.md). The comment now says the directory is documented and only the filename is Rulesync's choice, and notes that COPILOT_HOME relocates it upstream while Rulesync does not read that variable yet.

Testing

  • Round-trip test in each of copilot-skill.test.ts / copilotcli-skill.test.ts: a field beyond the schema survives import into the tool section and is written back to the generated SKILL.md.
  • A precedence test in each: a section carrying name/description does not shadow the canonical values, while its other keys are still kept.
  • pnpm cicheck green; vitest run --config vitest.e2e.config.ts src/e2e/e2e-skills.spec.ts green (130 tests).

Part of #2401
Part of #2402

cm-dyoshikawa and others added 2 commits August 6, 2026 00:50
…round-trip

CopilotSkill.toRulesyncSkill built the copilot: section by copying six
known keys, so a hand-authored SKILL.md field rulesync does not model was
dropped on import and erased from the file on the next generate — even
though the frontmatter schema is a looseObject that parsed it fine. Both
directions now carry the residual keys, as PR #2580 did for
CopilotRuleFrontmatterSchema.

Also refresh the stale copilotcli-hooks note: upstream documents
~/.copilot/hooks/ as the user-level hooks directory, so the location is
no longer a rulesync convention (only the filename is).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Address review: copilot and copilotcli write the same SKILL.md path and
copilotcli is generated last, so with both targets enabled the residual
keys the previous commit preserved were dropped again on generate. The
copilotcli class now spreads the same way, and the docs note that a
residual key rides one section only, like the modeled fields.

Also pin the section-vs-canonical precedence both classes rely on: the
section is spread first, so name and description are never shadowed by
it. Note that this reorders existing SKILL.md output, putting section
keys ahead of name/description.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants