AI coding tools · Copilot ·

GitHub Copilot hooks not working? Port them to the harness

Your VS Code chat hooks used to run, and now a new chat on the Copilot harness skips them, or the hook file loads but nothing happens. As of October 2026, VS Code's migration guide says "there is no automatic hook migration", so hooks written for the older Local harness have to be moved and rewritten by hand. This guide covers the checks the guide and two user reports point to.

If you want the next step, read VS Code Copilot skills not working? Fix personal skills.

Quick fix

  • Check where the hook lives. The Copilot harness reads .github/hooks/*.json, ~/.copilot/hooks/ and inline hooks in .github/copilot/settings.json, ~/.copilot/settings.json and the repository's .claude/settings.json. It doesn't read ~/.claude/settings.json or chat.hookFilesLocations.
  • Use the Copilot format: a standalone file with version: 1, camelCase events such as preToolUse, and bash or powershell commands.
  • Rewrite the script, not just the JSON. Tool input arrives as toolName and toolArgs, and decisions go in a top-level permissionDecision.

Step 1: move the hook to a source the Copilot harness reads

The migration guide lists the sources the Copilot harness reads: repository hook files in .github/hooks/*.json, user hook files under ~/.copilot/hooks/ (or %USERPROFILE%\.copilot\hooks\ on Windows, or $COPILOT_HOME/hooks/ when COPILOT_HOME is set), and inline hooks maps in .github/copilot/settings.json, .github/copilot/settings.local.json, the repository's .claude/settings.json and .claude/settings.local.json, and ~/.copilot/settings.json.

It also warns: "Do not assume a Local hook in ~/.claude/settings.json is discovered by Copilot", and says chat.hookFilesLocations "does not configure Copilot discovery". The settings chat.useHooks, chat.useClaudeHooks and chat.hookFilesLocations don't carry over either, so the guide notes that "a previously ignored file can become active without being moved".

Before you change anything, the guide says to back up the original configuration and scripts, and to stage untested files with a .json.disabled suffix so they aren't discovered yet. Don't run the original and converted hook together.

Step 2: switch to the Copilot hook format

Per the migration guide, the Copilot harness uses a standalone JSON file with version: 1, or an inline hooks map in a supported settings file. Events change name, for example:

Step 2: switch to the Copilot hook format
Local event Copilot event
SessionStart sessionStart
UserPromptSubmit userPromptSubmitted
PreToolUse preToolUse
PostToolUse postToolUse
Stop agentStop

For commands, the guide says standalone command hooks "document command as a fallback and bash or powershell as platform commands". On timeouts, Copilot "prefers timeoutSec over its timeout alias", so set only one.

As of October 2026, two user reports in open VS Code issues match this:

  • In #342664 (19 Oct 2026), one user reports that a SessionStart hook using the VS Code-style windows field didn't run in the Copilot harness, and that it ran after replacing windows with powershell.
  • In #343595 (22 Oct 2026), one user reports that VS Code's JSON validation underlines version: 1 hooks that only set command, while bash passes. The reporter says live execution wasn't tested, so this may be a validation warning only. One other user commented on the issue.
{
  "version": 1,
  "hooks": {
    "preToolUse": [
      {
        "type": "command",
        "bash": "node .github/hooks/guard.cjs",
        "powershell": "node .github/hooks/guard.cjs",
        "timeoutSec": 10
      }
    ]
  }
}

Step 3: rewrite what the script reads and returns

The guide says "copying a file or changing an event name alone does not verify a migration". It lists these changes for camelCase events:

  • Tool input: tool_name and tool_input become toolName and toolArgs. Tool names aren't a one-to-one match, so inspect the real arguments.
  • Tool result: postToolUse receives toolResult instead of tool_response.
  • Decisions: use top-level permissionDecision, permissionDecisionReason and modifiedArgs instead of hookSpecificOutput. Return {} when there is no decision, rather than allow.
  • Exit codes: the guide says GitHub documents preToolUse errors and nonzero exits as denying the tool, but timeouts as continuing through normal permissions. Test each event's error path.

Plugin hooks that load but never run

As of October 2026, in #342924 (20 Oct 2026), one user reports that an agent plugin's hooks are listed as loaded in the Copilot harness but don't execute, while they work in the Local harness. The issue is still open. The migration guide says to update a plugin's source package or get an updated version from its publisher, and "Do not patch an installation cache".

If your skills stopped loading too, see Copilot skills not working in VS Code.

Port your hooks with the kit

Disclosure: this is our own paid product. The Copilot Harness Port Kit is designed to help you keep chat on Local, move personal skills to folders the Copilot harness reads, and port your hooks. Its hook config converter turns a Local or Claude-style hooks map into the Copilot version: 1 format (event renames, timeoutSec, flattened matcher groups), saves the result as a staged .json.disabled file and warns about what still needs a human, such as matchers and the hook script itself. It also has a dual-format guard template that reads both tool_name/tool_input and toolName/toolArgs and fails closed on bad input. Its 62 automated tests ran against local fixtures and temporary folders, not a live Copilot session or VS Code install, and the hook converter and guard are backed only by fixture tests. It needs Node.js 20 or later. It costs $12 (plus VAT where applicable).

Sources

As of October 2026 (sources checked 24 Oct 2026):

GitHub, GitHub Copilot, Visual Studio Code (VS Code), Microsoft, Claude Code, Anthropic and other names belong to their owners. Names are used only to identify products and compatibility. Stack & Ship is not affiliated with or endorsed by any of them.