Description
On Windows, Claude Code dispatches hook commands through a POSIX shell. ${CLAUDE_PLUGIN_ROOT} expands to a backslash-separated path (C:\Users\<user>\.claude\plugins\cache\...), and because the hook commands leave it unquoted, the shell eats the backslashes before node ever sees it.
Every hook of the plugin therefore fails with MODULE_NOT_FOUND. Nothing is recorded, and nothing says so: the hook is non-blocking, and journal.cjs exits 0 unconditionally by design. aidd telemetry check reports hook fired -- no run file in aidd_docs/runs yet, which reads as "not started yet" rather than "broken".
Affected file(s)
plugins/aidd-telemetry/hooks/hooks.json — SessionStart, Stop, PostToolUse
plugins/aidd-context/hooks/hooks.json — SessionStart, same line
Expected behaviour
A session on Windows writes its run file to aidd_docs/runs/.
Observed behaviour
Nothing is ever written. Proof captured from a real Claude Code session transcript (hook_non_blocking_error attachment). Two hooks pointing at the same file, differing only by quoting:
{
"hookEvent": "SessionStart",
"command": "node C:\Users\<user>\.claude\probe.cjs UNQUOTED",
"stderr": "Error: Cannot find module 'C:\Users<user>.claudeprobe.cjs'",
"exitCode": 1
}
C:\Users\<user>\.claude\ became C:\Users<user>.claude. The quoted variant of the same command resolved and executed normally.
Proposed solution
Quote the path argument, as ralph-loop already does (bash "${CLAUDE_PLUGIN_ROOT}/hooks/stop-hook.sh"):
- "command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/journal.cjs session-start"
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/journal.cjs\" session-start"
Applies to all three aidd-telemetry commands and the one in aidd-context. Quoting is a no-op under cmd.exe and required under sh, so it is safe regardless of which shell the host picks.
Verified locally: after quoting, a fresh Windows session produced a complete run file — session_start, task_declared, file_written, turn_end — and aidd telemetry check returned session journalled ok / records join ok — 26 of 26 records, 0 unattributed.
Context / Technical constraints
Why this stayed invisible, and why it is worth a guard rather than only a patch:
journal.cjs catches everything and exits 0 — "a measurement layer that breaks a session is worse than one that misses a session". Sound, but it also means a hook that never starts is indistinguishable from one that ran and had nothing to do.
- On macOS and Linux the expansion carries no backslash, so the defect cannot appear there.
-- in aidd telemetry check is ambiguous between "measurement has not run yet" and "measurement can never run". A line stating whether the recorder's own command resolves would have turned a multi-hour investigation into one look.
Related: #707 (a real session per tool, on Windows and on Linux) — this is exactly the class of defect that would have caught.
Environment
|
|
| AI tool |
Claude Code |
| aidd-cli version |
5.3.0 |
| OS |
Windows 11 Pro (10.0.26100) |
| node |
24.12.0 |
| aidd-telemetry |
0.2.0 |
QA
- On Windows, install
aidd-telemetry and enable telemetry.enabled in .aidd/config.json.
- Start a Claude Code session in that repository and complete one exchange.
aidd_docs/runs/*.jsonl must exist and carry at least session_start and turn_end.
- Before the fix: the directory is never created, and no error surfaces anywhere.
I agree to follow this project's Code of Conduct.
Description
On Windows, Claude Code dispatches hook commands through a POSIX shell.
${CLAUDE_PLUGIN_ROOT}expands to a backslash-separated path (C:\Users\<user>\.claude\plugins\cache\...), and because the hook commands leave it unquoted, the shell eats the backslashes beforenodeever sees it.Every hook of the plugin therefore fails with
MODULE_NOT_FOUND. Nothing is recorded, and nothing says so: the hook is non-blocking, andjournal.cjsexits 0 unconditionally by design.aidd telemetry checkreportshook fired -- no run file in aidd_docs/runs yet, which reads as "not started yet" rather than "broken".Affected file(s)
plugins/aidd-telemetry/hooks/hooks.json—SessionStart,Stop,PostToolUseplugins/aidd-context/hooks/hooks.json—SessionStart, same lineExpected behaviour
A session on Windows writes its run file to
aidd_docs/runs/.Observed behaviour
Nothing is ever written. Proof captured from a real Claude Code session transcript (
hook_non_blocking_errorattachment). Two hooks pointing at the same file, differing only by quoting:{ "hookEvent": "SessionStart", "command": "node C:\Users\<user>\.claude\probe.cjs UNQUOTED", "stderr": "Error: Cannot find module 'C:\Users<user>.claudeprobe.cjs'", "exitCode": 1 }C:\Users\<user>\.claude\becameC:\Users<user>.claude. The quoted variant of the same command resolved and executed normally.Proposed solution
Quote the path argument, as
ralph-loopalready does (bash "${CLAUDE_PLUGIN_ROOT}/hooks/stop-hook.sh"):Applies to all three
aidd-telemetrycommands and the one inaidd-context. Quoting is a no-op undercmd.exeand required undersh, so it is safe regardless of which shell the host picks.Verified locally: after quoting, a fresh Windows session produced a complete run file —
session_start,task_declared,file_written,turn_end— andaidd telemetry checkreturnedsession journalled ok/records join ok — 26 of 26 records, 0 unattributed.Context / Technical constraints
Why this stayed invisible, and why it is worth a guard rather than only a patch:
journal.cjscatches everything and exits 0 — "a measurement layer that breaks a session is worse than one that misses a session". Sound, but it also means a hook that never starts is indistinguishable from one that ran and had nothing to do.--inaidd telemetry checkis ambiguous between "measurement has not run yet" and "measurement can never run". A line stating whether the recorder's own command resolves would have turned a multi-hour investigation into one look.Related: #707 (a real session per tool, on Windows and on Linux) — this is exactly the class of defect that would have caught.
Environment
QA
aidd-telemetryand enabletelemetry.enabledin.aidd/config.json.aidd_docs/runs/*.jsonlmust exist and carry at leastsession_startandturn_end.I agree to follow this project's Code of Conduct.