All skills
Skillintermediate

ECC Hook Fix — 2026-04-21

Claude Code CLI v2.1.116 on Windows was failing all Bash tool hook invocations with:

Claude Code Knowledge Pack7/10/2026

Overview

ECC Hook Fix — 2026-04-21

Summary

Claude Code CLI v2.1.116 on Windows was failing all Bash tool hook invocations with:

PreToolUse:Bash hook error
Failed with non-blocking status code:
C:\\Program Files\\Git\\bin\\bash.exe: C:\\Program Files\\Git\\bin\\bash.exe:
cannot execute binary file

PostToolUse:Bash hook error  (同上)

Result: observations.jsonl stopped updating after 2026-04-20T23:03:38Z (last entry was a parse_error from an earlier BOM-on-stdin issue).

Root Cause

C:\\Users\\sugig\\.claude\\settings.local.json had two defects:

Defect 1 — UTF-8 BOM + CRLF line endings

The file started with EF BB BF (UTF-8 BOM) and used CRLF line terminators. This is the PowerShell ConvertTo-Json | Out-File default behavior, and it is what patch_settings_cl_v2_simple.ps1 leaves behind when it rewrites the file.

00000000: efbb bf7b 0d0a 2020 2020 2268 6f6f 6b73  ...{..    "hooks

Defect 2 — Double-wrapped bash.exe invocation

The command string explicitly re-invoked bash.exe:

"command": "\\"C:\\\\Program Files\\\\Git\\\\bin\\\\bash.exe\\" \\"C:\\\\Users\\\\sugig\\\\.claude\\\\skills\\\\continuous-learning\\\\hooks\\\\observe-wrapper.sh\\""

When Claude Code spawns this on Windows, argument splitting does not preserve the quoted "C:\\Program Files\\..." token correctly. The embedded space in Program Files splits argv[0], and bash.exe ends up being passed to itself as a script file, producing:

bash.exe: bash.exe: cannot execute binary file

Prior working shape (for reference)

Before patch_settings_cl_v2_simple.ps1 ran, the command was simply:

"command": "C:\\\\Users\\\\sugig\\\\.claude\\\\skills\\\\continuous-learning\\\\hooks\\\\observe.sh"

Claude Code on Windows detects .sh and invokes it via Git Bash itself — no manual bash.exe wrapping needed.

Fix

C:\\Users\\sugig\\.claude\\settings.local.json rewritten as UTF-8 (no BOM), LF line endings, with the command pointing directly at the wrapper .sh and passing the hook phase as a plain argument:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "command",
            "command": "C:/Users/sugig/.claude/skills/continuous-learning/hooks/observe-wrapper.sh pre"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "command",
            "command": "C:/Users/sugig/.claude/skills/continuous-learning/hooks/observe-wrapper.sh post"
          }
        ]
      }
    ]
  }
}

Side benefit: the pre / post argument is now routed to observe.sh's HOOK_PHASE variable so events are correctly logged as tool_start vs tool_complete (previously everything was recorded as tool_complete).

Verification

Direct invocation of the new command format, emulating both hook phases:

# PostToolUse path
echo '{"tool_name":"Bash","tool_input":{"command":"pwd"},"session_id":"post-fix-verify-001","cwd":"...","hook_event_name":"PostToolUse"}' \\
  | "C:/Users/sugig/.claude/skills/continuous-learning/hooks/observe-wrapper.sh" post
# exit=0

# PreToolUse path
echo '{"tool_name":"Bash","tool_input":{"command":"ls"},"session_id":"post-fix-verify-pre-001","cwd":"...","hook_event_name":"PreToolUse"}' \\
  | "C:/Users/sugig/.claude/skills/continuous-learning/hooks/observe-wrapper.sh" pre
# exit=0

observations.jsonl gained:

{"timestamp":"2026-04-21T05:57:54Z","event":"tool_complete","tool":"Bash","session":"post-fix-verify-001",...}
{"timestamp":"2026-04-21T05:57:55Z","event":"tool_start","tool":"Bash","session":"post-fix-verify-pre-001","input":"{\\"command\\":\\"ls\\"}",...}

Both phases now produce correctly typed events.

Note on live CLI verification: settings changes take effect on the next claude CLI session launch. Restart the CLI and run a Bash tool call to confirm new rows appear in observations.jsonl from the actual CLI session.

Files Touched

  • C:\\Users\\sugig\\.claude\\settings.local.json — rewritten
  • C:\\Users\\sugig\\.claude\\settings.local.json.bak-hookfix-20260421-145718 — pre-fix backup

Known Upstream Bugs (not fixed here)

  • install_hook_wrapper.ps1 — halts at step [3/4], never reaches [4/4].
  • patch_settings_cl_v2_simple.ps1 — overwrites settings.local.json with UTF-8-BOM + CRLF and re-introduces the double-wrapped bash.exe command. Should be replaced with a patcher that emits UTF-8 (no BOM), LF, and a direct .sh path.

Branch

claude/hook-fix-20260421