Skip to content

Windows: all hooks fail with SyntaxError ([eval]:1) because inline node -e regex backslashes are collapsed by bash -c #3746

Open

Description

@plnamente

Summary

On native Windows (Claude Code + Git Bash), every ECC hook in hooks/hooks.json (v2.2.3) fails with a JavaScript SyntaxError before the bootstrap ever runs. Only SessionStart is visible to the user; the other hooks fail silently (non-blocking), so effectively no ECC hook runs on Windows.

What the user sees on startup:

SessionStart:startup hook error
Failed with non-blocking status code: [eval]:1
SessionStart:startup hook error
Failed with non-blocking status code: [eval]:1

Full stderr (from the session transcript):

[eval]:1
const p=require('path');const r=(function(){ ... require(s)
                                                  ^^^^^^^
Expected ',', got 'string literal'

SyntaxError: missing ) after argument list
    at makeContextifyScript (node:internal/vm:194:14)
    at compileScript (node:internal/process/execution:388:10)
    at evalTypeScript (node:internal/process/execution:260:22)

Root cause

The inline node -e "..." root-resolver contains backslash-heavy regexes in the WSL branch:

d.match(/^([A-Za-z]):[\\\\/]/)
d.slice(2).replace(/\\\\/g,'/')

On Windows, Claude Code spawns Git Bash with the hook as bash.exe -c "<command>" through the Windows command line. The backslashes get collapsed along the way (Windows argv quoting + MSYS argv parsing + bash double-quote parsing), so node receives:

d.match(/^([A-Za-z]):[\/]/)          // still valid
d.slice(2).replace(/\/g,'/')...      // regex literal now swallows the rest -> SyntaxError

Running the same command from a script file (bash file.sh) works, which is why this is easy to miss when testing manually.

Reproduction

Windows 11, Git for Windows bash, Node v24.22.1, Claude Code on native Windows (not WSL), ECC 2.2.3 installed as a plugin.

// repro.js — run from the plugin root
const { spawnSync } = require('child_process');
const cmd = require('./hooks/hooks.json').hooks.SessionStart[0].hooks[0].command;
const r = spawnSync('C:\\Program Files\\Git\\usr\\bin\\bash.exe', ['-c', cmd],
  { input: '{}', encoding: 'utf8', env: { ...process.env, CLAUDE_PLUGIN_ROOT: process.cwd() } });
console.log(r.status, r.stderr.match(/SyntaxError.*/)?.[0]);
// -> 1 SyntaxError: missing ) after argument list

Suggested fix

Avoid backslashes in the inline code entirely. This local patch fixed all 27 hooks for me (verified with the repro above, exit 0):

- var m=d.match(/^([A-Za-z]):[\\\\/]/);
+ var m=d.match(/^([A-Za-z]):/);
...
- d='/mnt/'+dl+d.slice(2).replace(/\\\\/g,'/')
+ d='/mnt/'+dl+d.slice(2).split(String.fromCharCode(92)).join('/')

(The code already uses d.charCodeAt(1)===92 elsewhere for the same reason.) Longer term, moving the resolver out of node -e into a small script file would avoid shell-quoting issues on all platforms and may also help with #2687, which is about the same inline bootstrap.

A regression test that runs each hooks.json command through bash -c via child_process.spawnSync on a Windows CI runner would catch this.

Environment

  • ECC: 2.2.3 (plugin, ecc@ecc, project-scoped install)
  • OS: Windows 11 Pro (native, not WSL)
  • Shell used by Claude Code for hooks: Git for Windows bash
  • Node: v24.22.1

Activity

  1. drummondbr-com commented on Oct 26, 2026

    @drummondbr-com

    Confirming on another machine, same errors on native Windows:

    • ECC 2.2.3 (ecc@ecc, user scope, installed with npx ecc-universal@2.2.3 setup)
    • Windows x64 (native, not WSL), Git for Windows 2.55.0
    • Node v24.18.0, Claude Code 2.1.310

    Same stderr: Expected ',', got 'string literal' / SyntaxError: missing ) after argument list.

    One extra data point: re-running setup with hooks=off does not remove the SessionStart errors. Presumably the inline bootstrap fails to parse before it can read the hook preference, so Windows users currently can't silence it through ECC's own setting.

    +1 for moving the resolver into a script file and adding the Windows CI regression test.

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

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions