fix(copilot): map edits/urls approvals, stop clobbering mcp.json inputs/sandbox, add the user-scope hooks file - #2467
Merged
Conversation
…ts/sandbox, add the user-scope hooks file
Five verified drifts against the VS Code and GitHub Copilot references:
- chat.tools.edits.autoApprove and chat.tools.urls.autoApprove had no mapping; canonical edit and webfetch now drive them alongside the existing terminal map. read has no approval surface and write stays unmapped so it does not become indistinguishable from edit on import.
- Regenerating .vscode/mcp.json wrote {servers} over the whole document, deleting any hand-authored inputs or sandbox section; VS Code recommends committing that file, so a dropped inputs entry left ${input:...} unresolvable and servers failed to start. Only servers is replaced now, and an unparseable file aborts the write instead of being overwritten.
- CopilotHooks ignored its global option, so the documented ~/.copilot/hooks user scope emitted nothing. It now writes copilot-ide-hooks.json there, a name that cannot collide with the Copilot CLI global file already in that folder.
- The name frontmatter of *.instructions.md could not be authored or imported.
The preCompact/subagentStart event set and the REVIEW.md surface are left open; both hinge on a maintainer decision.
…pproval map the config does not state Two regressions the review caught in the previous commit: - Reading .vscode/mcp.json with JSON.parse aborted the whole generate run on the commented file VS Code's own 'MCP: Add Server' scaffold writes. The file is now declared in the shared-config gateway (format jsonc, mcp owns only 'servers'), which parses it correctly, fails closed on a real syntax error, and drops the hand-rolled merge. - Every managed key went into the patch, so a config stating only bash rules retracted the user's hand-written chat.tools.edits.autoApprove and chat.tools.urls.autoApprove. A key is now touched only when the canonical config states its category; a category that is stated but yields nothing still retracts, as before. Also fold the duplicate global hooks dir constant into the existing one and document that ~/.copilot/hooks loads every *.json, so generating both copilot and copilotcli globally makes each hook fire twice.
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 1-4 and 6 of #2401. Gaps 5 and 7 are deliberately left open, so this PR does not close the issue.
Gaps 1-2 —
chat.tools.edits.autoApprove/chat.tools.urls.autoApproveThe adapter was hard-coded to a single category (
bash→ the terminal map). It now drives three flat dotted keys from three canonical categories, all sharing the same pattern-to-boolean shape and the sameallow→true/deny→false/ask→ omitted semantics:bashchat.tools.terminal.autoApproveeditchat.tools.edits.autoApprovewebfetchchat.tools.urls.autoApprovereadhas no VS Code approval surface.writeis deliberately not folded into the edits map alongsideedit— doing so would make the two indistinguishable on import, the same lossiness the Antigravity adapter documents for itswrite_filecollapse. VS Code's per-URL{approveRequest, approveResponse}object form has no canonical action, so it is skipped on import; since rulesync owns the key outright it is replaced when the canonical config carries anywebfetchrule, which is the same ownership rule the terminal map already had.The two new keys are registered in the
.vscode/settings.jsonownedKeysdeclaration — the shared-config gateway rejected the write until they were, which is the guard working as intended.Gap 3 —
.vscode/mcp.jsonno longer clobbersinputs/sandboxfromRulesyncMcpnever read the existing file: it built{ servers }and wrote the whole document, so a hand-authoredinputsorsandboxsection was deleted on the nextgenerate. VS Code recommends committing this file, so the loss was user-visible — once${input:…}becomes unresolvable, the affected servers fail to start.It now reads the file first and replaces only
servers, preservinginputs,sandboxand any future top-level section (theClaudecodeMcppattern). An existing file that cannot be parsed aborts with an error rather than being overwritten, so a malformed file never silently loses its contents.Gap 4 — user-scope hooks at
~/.copilot/hooksCopilotHooks.getSettablePaths()ignored itsglobaloption,fromRulesyncHooksnever read it, and the processor declaredsupportsGlobal: false, so a global run emitted no Copilot hooks at all. The VS Code hooks doc and the coding-agent hooks doc both document~/.copilot/hooksas the user scope and state that every*.jsonin the folder is loaded. Global mode now writes~/.copilot/hooks/copilot-ide-hooks.json— a distinct name, because the Copilot CLI's global hooks file already occupiescopilot-hooks.jsonin that same folder.Gap 6 —
namefrontmatter on*.instructions.mdAdded to
CopilotRuleFrontmatterSchemaand to thecopilotsection ofRulesyncRuleFrontmatter, carried through in both directions alongsideexcludeAgent.Gaps 5 and 7 — left open
Gap 5 (
preCompact/subagentStart): VS Code's side is now confirmed — the hooks doc lists exactly eight events includingPreCompactandSubagentStart. What is still unconfirmed is the gating question the issue itself raised:.github/hooks/*.jsonis shared with the cloud coding agent, which does not document those two, and whether it ignores or rejects unknown event keys is not stated anywhere. Emitting keys that might break the cloud agent for everyone who enables thecopilottarget is not a call to make on inference.Gap 7 (
REVIEW.md): the issue notes this is an alternative location rather than a missing capability, since.github/copilot-instructions.mdis still read by code review. Whether rulesync should own a review-only rules surface is a scope decision.Verification
pnpm cicheckgreen.e2e-hooks(75),e2e-mcp/e2e-permissions/e2e-rules(308) all pass; the global hooks e2e matrix gained acopilotcell.