* fix(#1693): don't double-quote $CLAUDE_PROJECT_DIR-anchored hook paths on Windows The installer's #2979 legacy-node rewrite ran every managed node hook path through JSON.stringify on Windows. For local installs the path already carries a "$CLAUDE_PROJECT_DIR"-anchored quoted prefix, so stringifying produced "\"$CLAUDE_PROJECT_DIR\"/...". Node then received an argument starting with a literal " , treated it as relative, and failed MODULE_NOT_FOUND — breaking every node managed hook at once (a self-locking PreToolUse-guard deadlock). projectLegacySettingsHookCommand now emits an already-anchored token verbatim and only JSON.stringify-quotes bare absolute paths (which may contain spaces). Scoped to win32 so non-Windows token-shape behavior is unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(#1693): add changeset Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
committed by
GitHub
parent
901dabe6f1
commit
dcd1d7f973
@@ -253,6 +253,15 @@ export function isManagedHookCommand(commandText: unknown, opts: { surface?: str
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect a `"$VAR"/rest` anchored hook-script token — a path whose leading
|
||||
* shell variable is already double-quoted with the remainder left bare (the
|
||||
* shape `projectLocalHookPrefix` emits for local installs, e.g.
|
||||
* `"$CLAUDE_PROJECT_DIR"/.claude/hooks/gsd-x.js`). Such a token is ALREADY a
|
||||
* valid, correctly-quoted shell argument and must never be re-quoted.
|
||||
*/
|
||||
const ANCHORED_HOOK_SCRIPT_TOKEN = /^"\$[A-Za-z_][A-Za-z0-9_]*"\//;
|
||||
|
||||
/**
|
||||
* Projection helper for legacy settings.json hook rewrites.
|
||||
*
|
||||
@@ -275,8 +284,20 @@ export function projectLegacySettingsHookCommand({
|
||||
}): string | null {
|
||||
if (!absoluteRunner || !scriptPath) return null;
|
||||
const normalizedScriptPath = platform === 'win32' ? scriptPath.replace(/\\/g, '/') : scriptPath;
|
||||
// #1693: a script path already carrying a `"$CLAUDE_PROJECT_DIR"`-anchored
|
||||
// quoted prefix (local installs) is already a valid shell token — only the
|
||||
// variable is quoted, the rest is bare. JSON.stringify-ing it on Windows
|
||||
// yields `"\"$CLAUDE_PROJECT_DIR\"/..."` (escaped quotes inside an outer
|
||||
// quote); node then receives an argument that *starts* with a `"`, treats it
|
||||
// as relative, and dies with MODULE_NOT_FOUND. Emit anchored tokens verbatim;
|
||||
// only bare absolute paths (which may contain spaces, e.g. "Program Files")
|
||||
// need the JSON.stringify quoting. Scoped to win32: the non-Windows branch
|
||||
// already preserves the caller's `scriptToken` (which is the bare anchored
|
||||
// token for these inputs), so it never had the double-quote bug.
|
||||
const commandScriptToken = platform === 'win32'
|
||||
? JSON.stringify(normalizedScriptPath)
|
||||
? (ANCHORED_HOOK_SCRIPT_TOKEN.test(normalizedScriptPath)
|
||||
? normalizedScriptPath
|
||||
: JSON.stringify(normalizedScriptPath))
|
||||
: (scriptToken || JSON.stringify(normalizedScriptPath));
|
||||
return projectShellCommandText({
|
||||
runnerToken: absoluteRunner,
|
||||
|
||||
Reference in New Issue
Block a user