Repository navigation
Description
The PostToolUse entry in plugins/upgrade-agent/hooks.json declares no matcher or
scope, so it is invoked after every tool call, in every session, under every agent —
including sessions and agents unrelated to upgrades.
Environment
- Plugin:
upgrade-agent1.1.404 (commit a4f718e) - Installed via the
ms-dotnettools.upgrade-agentVS Code extension 1.1.290 - Host: VS Code 1.133.0, Copilot Chat
- OS: macOS 26.6 (25G72), arm64
Observed
A plain chat session — default agent, no upgrade context — asking "read plan.md and
summarize it" fires the hook on each file-search call.
Additional symptom: the chat transcript is polluted
The unconditional invocation is also directly user-visible. Because the hook runs on
every tool call, its output is interleaved with the actual work on every step. A
trivial two-step request produces:
Half the visible steps are hook noise. This scales linearly with tool use, so longer
agentic runs become hard to read, and the warnings look alarming despite being
completely inert — the script always returns {"continue":true} and never blocks the
host call. Users have no way to tell they're harmless, or that they come from a plugin
unrelated to what they're currently doing.
The two defects compound here: # makes each invocation fail loudly, and the missing
scope makes it invoked everywhere. Fixing # alone silences the transcript but leaves
the spawn cost described above; adding scope alone reduces the noise to file-read calls
only. Both are worth fixing.
Separately, it may be worth considering whether a non-blocking hook failure should
surface in the user-facing transcript at all, rather than in a log — that would protect
users from any misbehaving third-party hook, not just this one.
Why this matters
All filtering happens inside the script, after the process has already spawned:
TOOL_NAME=$(extract_field "$INPUT" tool_name toolName) # spawns jq or python3
case "$TOOL_NAME" in
read_file|view|Read) ;;
*) write_success ;; # ...only to discard
esacAn unrelated tool call therefore costs a bash spawn plus a jq/python3 spawn purely
to determine the hook isn't interested. Per tool call, in every session, indefinitely.
The whitelist is static and known ahead of time (read_file, view, Read), so this
decision could be made before any process is created.
To be clear
The script itself is well behaved: it never blocks the host tool call, makes no network
requests, and records only the names of its own SKILL.md loads to a temp directory. It
does not capture user code, file contents, or prompts. The concern is solely that the
invocation is unconditional when the filtering criteria are static.
Suggested fix
Scope the hook at the manifest level so the runner can skip it without spawning a
process — e.g. restrict it to the file-read tools the script already whitelists.
If the current hooks schema has no such mechanism, this may belong with #29
(Adopt Agent Plugins 1.0).
Related
See: #33
Fixing that will make this one's cost real rather than theoretical.
Activity
lonitra commentedon Aug 27, 2026
Thank you for reporting this issue!
After investigating this, we found that a
matcherfield can be used to scope hooks to specific tools. Unfortunately, VS Code silently discards the matcher field during hook parsing, so adding "matcher": "Read" to our hooks.json will have no effect today in VS Code as it strips matcher out and runs all hooks for every tool call regardless.I've filed this upstream: microsoft/vscode#332988
The Copilot CLI on the other hand does honor the matcher field currently, so we'll update it so that it doesn't fire for all tools at least in CLI. Once the VS Code upstream fix lands, I'll double check that everything works properly before closing out this issue. In the meantime, a fix for #33 is already merged and pending release, so this should help with the chat pollution at least until upstream is resolved.
lonitra commentedon Sep 15, 2026
The matcher has been added to the hooks as of plugin version 1.1.485
This is resolved in CLI. For VSCode, based on upstream issue, there looks to be no plans to honor the matcher in the native VS Code agent hooks implementation. This native VSCode agent will eventually be deprecated and superseded by the shared Copilot SDK implementation. Until then the recommended workaround for this is to select 'Copilot' as the session target - this will honor the matcher:
