fix: quote CLAUDE_PLUGIN_ROOT in hooks.json to handle paths with spaces - #2704
fix: quote CLAUDE_PLUGIN_ROOT in hooks.json to handle paths with spaces#2704Tom Meschter (tmeschter) with Copilot wants to merge 3 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes a Windows-specific hook execution failure for Claude Code when the plugin root path contains spaces by ensuring the track-telemetry.sh script path is passed to bash as a single, properly-quoted argument.
Changes:
- Quote the
${CLAUDE_PLUGIN_ROOT}-based script path inplugin/hooks/hooks.jsonto prevent bash word-splitting on paths with spaces.
Show a summary per file
| File | Description |
|---|---|
| plugin/hooks/hooks.json | Wrapes the telemetry hook script path in escaped quotes so ${CLAUDE_PLUGIN_ROOT} expansions containing spaces remain a single argument. |
Copilot's findings
- Files reviewed: 1/1 changed files
- Comments generated: 0
|
The fix is also needed in .github/plugins/azure-skills/hooks/hooks.json. Do we need it in other hooks.json as well? Edit: I see, that file is replicated from the edited hooks.json. However, I feel we need to make the same fix to cursor-hooks.json as well. |
Jon Gallant (jongio)
left a comment
There was a problem hiding this comment.
Agreeing with JasonYeMSFT (@JasonYeMSFT)'s observation about sibling files. Beyond cursor-hooks.json, note that copilot-hooks.json also references an unquoted path:
"bash": "${PLUGIN_ROOT}/hooks/scripts/track-telemetry.sh"Depending on how the consumer of that file invokes the script, the same word-splitting issue could apply. Worth checking whether the runner passes the bash field value through a shell or uses it as a direct file argument.
Not blocking since this is a targeted fix, but a follow-up to address both siblings (cursor-hooks.json and copilot-hooks.json) would close the gap entirely.
Also update the Cursor and Copilot hooks.
|
JasonYeMSFT (@JasonYeMSFT) Jon Gallant (@jongio) I've updated the other hooks files to quote the path to the script. |
Jon Gallant (jongio)
left a comment
There was a problem hiding this comment.
Verified the incremental change addressing the sibling-file feedback.
One non-blocking question on copilot-hooks.json: the powershell field value includes literal double quotes after JSON deserialization. This works if the hook runner evaluates through a shell (where quotes prevent splitting on spaces). It would break if the runner passes the expanded value directly to powershell -File (literal quote chars become part of the filename). Worth a quick smoke test on Windows with a spaced profile path to confirm the runner's PowerShell invocation model handles this correctly.
| "bash": "${PLUGIN_ROOT}/hooks/scripts/track-telemetry.sh", | ||
| "powershell": "${PLUGIN_ROOT}/hooks/scripts/track-telemetry.ps1" | ||
| "bash": "\"${PLUGIN_ROOT}/hooks/scripts/track-telemetry.sh\"", | ||
| "powershell": "\"${PLUGIN_ROOT}/hooks/scripts/track-telemetry.ps1\"" |
There was a problem hiding this comment.
nit (non-blocking): After JSON parse, this value is "${PLUGIN_ROOT}/hooks/scripts/track-telemetry.ps1" with literal double quotes. Works correctly if the hook runner evaluates through a shell or -Command, but would fail if it passes the path directly to powershell -File (quotes become part of the filename). Worth verifying the runner's invocation model on Windows with spaces in the user profile path.
There was a problem hiding this comment.
As it turns out, VS Code is calling powershell -Command <hook>. With the escaped quotes PowerShell sees "C:\<full>\<path>\<to>\track-telemetry.ps1" (with the double quotes), interprets it as a string, and returns the path as-is rather than executing it. So at least for "powershell" and VS Code the quotes are the wrong thing.
There was a problem hiding this comment.
Tom Meschter (@tmeschter) - that confirms the regression. With -Command, the quoted path becomes a string expression instead of an invocation, so the hook silently does nothing regardless of whether there are spaces.
Does the same apply to the bash field in this file? If VS Code passes it to bash as a direct file argument, the literal quote characters would become part of the filename and fail. If it evaluates through bash -c, the quotes would work as intended.
For the powershell field specifically, the fix would likely need the call operator (& '...') instead of double quotes. But whether single quotes or the & prefix survive the runner's variable expansion depends on when ${PLUGIN_ROOT} gets resolved.
|
This fix was rolled into #2719. |
On Windows, when the user profile path contains spaces (e.g.
C:\Users\First Last), bash word-splits the unquoted${CLAUDE_PLUGIN_ROOT}expansion inhooks.json, causing thePostToolUsehook to fail on every tool call beforetrack-telemetry.shever runs.Change
plugin/hooks/hooks.json— wrap the script path in escaped quotes:This ensures the expanded path is treated as a single argument regardless of embedded spaces.