Skip to content

fix(aidd-telemetry): hooks pass ${CLAUDE_PLUGIN_ROOT} unquoted — every hook dies silently on Windows #862

Description

@Tkotm76

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.jsonSessionStart, Stop, PostToolUse
  • plugins/aidd-context/hooks/hooks.jsonSessionStart, 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:

  1. 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.
  2. On macOS and Linux the expansion carries no backslash, so the defect cannot appear there.
  3. -- 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

  1. On Windows, install aidd-telemetry and enable telemetry.enabled in .aidd/config.json.
  2. Start a Claude Code session in that repository and complete one exchange.
  3. aidd_docs/runs/*.jsonl must exist and carry at least session_start and turn_end.
  4. Before the fix: the directory is never created, and no error surfaces anywhere.

I agree to follow this project's Code of Conduct.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions