fix(copilotcli): map userPromptTransformed, honor every documented matcher event, read portable hook fields and skill invocation flags - #2466
Merged
Conversation
…tcher event, read portable hook fields and skill invocation flags Four verified drifts against the Copilot CLI reference docs: - userPromptTransformed had no canonical mapping; it now maps to the existing canonical userPromptExpansion event (the same concept Qwen Code's UserPromptExpansion maps to), so it is authorable and imports as a known event. - matcher was only emitted on preToolUse/postToolUse, but the hooks reference documents it on permissionRequest, notification, preCompact and subagentStart too; a matcher on those was silently discarded and the drop warning was factually wrong. - The portable command field and the timeout alias were not read on import, so an existing .github/hooks/*.json using them imported with no command or no timeout at all. - The skill user-invocable and disable-model-invocation flags were dropped on generate. The .claude/commands/ surface reported in the same issue is left open: it would make copilotcli write into Claude Code's directory, which is a maintainer call.
… selector, not the build machine Generate hard-coded bash on Unix and powershell on Windows, so the same canonical config produced different output per machine — and an imported portable command entry came back out narrowed to one shell. The shell selector now chooses the field, and an entry without one writes upstream's portable command field, which Copilot CLI copies to both shells. Import records shell for shell-specific entries so they still round-trip.
This was referenced Jul 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Background
Addresses gaps 2-5 of #2402. Gap 1 is deliberately left open (see below), so this PR does not close the issue.
Every gap was checked against the authoritative
github/docsmarkdown source rather than the rendered SPA.Gap 2 —
userPromptTransformedhad no canonical mappingThe hooks reference lists it in the 14-event set. Rather than adding a new canonical event, it maps to the existing canonical
userPromptExpansion— the same concept Qwen Code'sUserPromptExpansionalready maps to (a mutation-only hook that runs on the transformed prompt and can rewrite model-facing content). It is now authorable from.rulesync/hooks.jsoncand imports as a known event instead of an unknown passthrough.Gap 3 — matcher dropped on four matcher-aware events
The hooks reference documents
matcheron six events, each filtering a different field:preToolUse/postToolUse/permissionRequest(tool name),notification(notification type),preCompact(trigger,manualorauto) andsubagentStart(agent name).COPILOTCLI_MATCHER_EVENTSwas hard-coded to the first two, so a matcher on the other four was silently discarded — and the drop warning named onlypreToolUse/postToolUse, which was factually wrong. The set now covers all six and the warning is derived from it.Gap 4 — portable
commandand thetimeoutalias not read on importUpstream:
commandis the "cross-platform fallback ... copied to bothbashandpowershell" when those are absent, andtimeoutis an "alias fortimeoutSec... used only whentimeoutSecis absent". Neither was in the entry schema, so importing an existing.github/hooks/*.jsonwritten in the portable form produced a hook definition with no command at all, and atimeout-only entry lost its timeout. Both are now accepted on import with upstream's precedence (an explicit shell field wins overcommand;timeoutSecwins overtimeout). Generate is unchanged and still writes the platform-specific shell field.Gap 5 — skill invocation flags dropped
user-invocable(defaulttrue) anddisable-model-invocation(defaultfalse) are documented skill frontmatter and actively honored upstream (v1.0.71 marks disabled skills inskill list; v1.0.74 "fully honors" the disable flag). Both are added toCopilotcliSkillFrontmatterSchemaand to thecopilotclisection ofRulesyncSkillFrontmatter, and copied in both directions. Afalsevalue survives rather than being treated as absent.Gap 1 (
.claude/commands/*.md) — left open, needs a maintainer decisionThe surface is real and correctly quoted in the issue. But supporting it means the
copilotclitarget writes into.claude/commands/, a directory that today only theclaudecodetarget owns — so enablingcopilotclialone would materialize Claude Code files, and enabling both targets would have two different products' command frontmatter fighting over the same path. That is a scope call about directory ownership across products, not a template-following addition, so it is not implemented here. Worth noting that upstream also documents commands as having lower priority than skills of the same name, and rulesync already emits the Copilot CLI skills surface at.github/skills/.Verification
pnpm cicheckgreen.npx vitest run --config vitest.e2e.config.ts src/e2e/e2e-hooks.spec.ts src/e2e/e2e-skills.spec.ts— 202 passed.userPromptTransformedround-trip, the portablecommand/timeoutprecedence rules, and both skill flags including thefalsecase.