Skip to content

fix(copilot): represent the documented skill frontmatter fields and keep unmodeled rule frontmatter - #2580

Merged
dyoshikawa merged 3 commits into
mainfrom
resolve-issue-2401-copilot-skill-fields
Aug 4, 2026
Merged

fix(copilot): represent the documented skill frontmatter fields and keep unmodeled rule frontmatter#2580
dyoshikawa merged 3 commits into
mainfrom
resolve-issue-2401-copilot-skill-fields

Conversation

@dyoshikawa

Copy link
Copy Markdown
Owner

Fixes the two oversights recorded in the 2026-07-30 re-check comment on #2401. The open design items (gaps 5 and 7, and chat.permissions.default) are untouched, so the issue stays open.

1. Four documented skill frontmatter fields were unrepresentable for copilot

VS Code documents name, description, argument-hint, user-invocable, disable-model-invocation and context: fork; CopilotSkillFrontmatterSchema emitted only name/description/license/allowed-tools, and the copilot: section of the canonical skill frontmatter accepted only license/allowed-tools. As the comment notes, this is an oversight rather than a scope call — the sibling copilotcli already carries three of the four and both targets write the same SKILL.md.

All four are now in the tool schema and in the canonical copilot: section, in both directions.

The two invocation gates are resolved through the shared resolveUserInvocable / resolveDisableModelInvocation helpers, so the top-level user-invocable / disable-model-invocation defaults apply and a section value — including an explicit false — still wins. copilotcli read only its own section before; it now goes through the same resolvers, so the two siblings cannot disagree about the same key for one skill. The docstrings and the docs list both targets accordingly.

2. CopilotRuleFrontmatterSchema silently dropped frontmatter on import

It was a closed z.object, so importing a hand-written .github/instructions/*.instructions.md carrying any field beyond the schema parsed fine but lost it, and the next generate wrote the file back without it — the one Copilot adapter violating the project's own looseObject frontmatter guideline.

It is a z.looseObject now, and toRulesyncRule carries every field other than description/applyTo (which have canonical homes) into the tool-scoped copilot: section, which fromRulesyncRule spreads back out. That is the shape copilot-command.ts and copilot-subagent.ts already use. The canonical copilot: rule section was already a looseObject, so nothing else had to change. One existing test asserted the old lossy behavior and now asserts the field survives.

Verification

New tests cover the skill round-trip of all four fields, the top-level-default resolution with a section override, and an instructions file with an unmodeled field surviving import → generate. Full pnpm cicheck is green, plus e2e-skills.spec.ts and e2e-rules.spec.ts.

Part of #2401

🤖 Generated with Claude Code

…ing rule frontmatter on import

The copilot skill schema carried only name/description/license/allowed-tools, so four documented fields — argument-hint, user-invocable, disable-model-invocation and context — could not be expressed for the target even though the sibling copilotcli already carries three of them and both write the same SKILL.md. All four are now in the tool schema and the copilot section of the canonical skill frontmatter, in both directions, and the two invocation gates read the top-level defaults through the shared resolvers (a section value, including false, still wins). copilotcli reads those defaults the same way now, so the siblings do not disagree about the same key.

CopilotRuleFrontmatterSchema was a closed z.object, so importing a hand-written .github/instructions/*.instructions.md parsed fine but lost any field beyond the schema, and the next generate wrote the file back without it. It is a looseObject now and the rest-fields ride the tool-scoped copilot section, matching copilot-command.ts and copilot-subagent.ts and the project's own frontmatter guideline.
…ecedence

Review follow-up: copilot and copilotcli write the same SKILL.md at both scopes and copilotcli writes last, so a value set in only one section does not survive when both targets are generated; context has no copilotcli counterpart. Also records that description/applyTo written in the copilot rule section lose to the canonical values, and adds the copilotcli counterpart of the top-level-defaults test.
The generation order follows the order the targets are listed in, not the tuple order, so which of copilot/copilotcli writes last is not fixed.
@dyoshikawa
dyoshikawa merged commit 5ab18d7 into main Aug 4, 2026
9 checks passed
@dyoshikawa
dyoshikawa deleted the resolve-issue-2401-copilot-skill-fields branch August 4, 2026 08:25
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