TL;DR: On Windows, graphify claude install writes the PreToolUse hook commands using the executable's native backslash path (C:\Users...\graphify.EXE). Claude Code runs hooks through Git Bash, which treats ** as an escape character and strips them, so the path collapses to C:Usersvarn...graphify.EXE and every invocation fails with command not found. All graph guards, search and read, are silently dead on Windows.
How I found this:
While digging into the Grep matcher issue (#1986 ), I noticed my Claude Code terminal was quietly printing hook errors on almost every tool call. They weren't the guard choosing not to fire, they were the guard's command failing to execute at all. Once I read the actual path in the error, the backslashes were simply gone.
Summary
_claude_pretooluse_hooks() builds each hook command as f"{exe} hook-guard ...", where exe is the resolved executable path. On Windows that path uses backslashes, and it lands in .claude/settings.json as C:\\Users\\...\\graphify.EXE. Claude Code on Windows executes PreToolUse hook commands through Git Bash (/usr/bin/bash, visible in the error). Bash interprets each ** as an escape, so \U \v . \b all collapse to their next character and the path is destroyed before the shell ever looks for the binary. The result is not a miss-fire, it's a hard failure on every guarded tool call.
Evidence (real 0.9.18 session, Windows)
PreToolUse:Bash hook error
Failed with non-blocking status code: /usr/bin/bash: line 1: C:Usersvarun.localbingraphify.EXE: command not found
PreToolUse:Read hook error
Failed with non-blocking status code: /usr/bin/bash: line 1: C:Usersvarun.localbingraphify.EXE: command not found
Note the path in the error: every backslash from C:\Users\varun\.local\bin\graphify.EXE is gone.
The exact spot
graphify/install.py, _claude_pretooluse_hooks():
command: f"{exe} hook-guard search" # exe is a native Windows path: C:\Users\...\graphify.EXE
exe is stored verbatim, so on Windows the emitted settings.json is:
{ "matcher": "Bash",
"hooks": [{ "type": "command",
"command": "C:\\Users\\varun\\.local\\bin\\graphify.EXE hook-guard search" }] }
Both the search hook and the Read|Glob read hook use the same exe, so both die identically.
Repro
graphifyy 0.9.18 (uv tool install), Claude Code CLI on Windows:
- graphify claude install (in any dir)
- Inspect .claude/settings.json -> hook commands use C:\Users\...\graphify.EXE
- In Claude Code, trigger any Bash or Read tool call.
- The terminal prints: /usr/bin/bash: line 1: C:Users...graphify.EXE: command not found
Actual: every PreToolUse hook fails to execute on Windows; no graph guard ever runs.
Expected: the hook command runs and the graphify nudge fires.
Why bash, and why forward slashes fix it?
A manually corrected block using forward slashes, C:/Users/varun/.local/bin/graphify.EXE, runs cleanly in the same session. Windows accepts / as a path separator, and bash leaves / untouched, so a forward-slash path is correct on every platform. Backslashes only ever break Windows.
Suggested fix
Normalize exe to a forward-slash path before interpolating it into the hook command, e.g. Path(exe).as_posix() (or str(exe).replace("\", "/")). One change covers both the search and read hooks since they share exe.
Note for reproduction: this is Windows-only. On macOS/Linux the resolved path is already /-native, so graphify claude install there emits a working command and the bug will not reproduce locally. The evidence above is from a real Windows 0.9.18 install.
TL;DR: On Windows, graphify claude install writes the PreToolUse hook commands using the executable's native backslash path (C:\Users...\graphify.EXE). Claude Code runs hooks through Git Bash, which treats ** as an escape character and strips them, so the path collapses to C:Usersvarn...graphify.EXE and every invocation fails with command not found. All graph guards, search and read, are silently dead on Windows.
How I found this:
While digging into the Grep matcher issue (#1986 ), I noticed my Claude Code terminal was quietly printing hook errors on almost every tool call. They weren't the guard choosing not to fire, they were the guard's command failing to execute at all. Once I read the actual path in the error, the backslashes were simply gone.
Summary
_claude_pretooluse_hooks()builds each hook command asf"{exe} hook-guard ...", where exe is the resolved executable path. On Windows that path uses backslashes, and it lands in.claude/settings.jsonasC:\\Users\\...\\graphify.EXE. Claude Code on Windows executes PreToolUse hook commands through Git Bash (/usr/bin/bash, visible in the error). Bash interprets each ** as an escape, so \U \v . \b all collapse to their next character and the path is destroyed before the shell ever looks for the binary. The result is not a miss-fire, it's a hard failure on every guarded tool call.Evidence (real 0.9.18 session, Windows)
Note the path in the error: every backslash from
C:\Users\varun\.local\bin\graphify.EXEis gone.The exact spot
Both the search hook and the Read|Glob read hook use the same exe, so both die identically.
Repro
graphifyy 0.9.18 (uv tool install), Claude Code CLI on Windows:
Actual: every PreToolUse hook fails to execute on Windows; no graph guard ever runs.
Expected: the hook command runs and the graphify nudge fires.
Why bash, and why forward slashes fix it?
A manually corrected block using forward slashes,
C:/Users/varun/.local/bin/graphify.EXE, runs cleanly in the same session. Windows accepts / as a path separator, and bash leaves / untouched, so a forward-slash path is correct on every platform. Backslashes only ever break Windows.Suggested fix
Normalize exe to a forward-slash path before interpolating it into the hook command, e.g. Path(exe).as_posix() (or str(exe).replace("\", "/")). One change covers both the search and read hooks since they share exe.
Note for reproduction: this is Windows-only. On macOS/Linux the resolved path is already /-native, so graphify claude install there emits a working command and the bug will not reproduce locally. The evidence above is from a real Windows 0.9.18 install.