Skip to content

fix(scripts): claw.js spawn 'claude' fails with ENOENT on Windows#1471

Open
mike-batrakov wants to merge 1 commit intoaffaan-m:mainfrom
mike-batrakov:fix/claw-windows-spawn
Open

fix(scripts): claw.js spawn 'claude' fails with ENOENT on Windows#1471
mike-batrakov wants to merge 1 commit intoaffaan-m:mainfrom
mike-batrakov:fix/claw-windows-spawn

Conversation

@mike-batrakov
Copy link
Copy Markdown
Contributor

@mike-batrakov mike-batrakov commented Apr 16, 2026

Summary

Fixes #1469scripts/claw.js calls spawnSync('claude', args, { ... }) with no shell option. On Windows the installed claude binary is claude.cmd, which Node's spawn() cannot resolve via PATH without shell: true. The call fails with spawn claude ENOENT and askClaude() returns an error string.

Fix

Mirror the pattern applied in #1456 for the MCP health-check hook: pass shell: process.platform === 'win32' to spawnSync. 'claude' is a hardcoded literal (not user input), so enabling shell on Windows only is safe — no metacharacter validation needed.

Scope

Audited every spawn/spawnSync/execFile call under scripts/ for the same Windows ENOENT pattern. This is the only remaining instance; other sites either explicitly handle .cmd (post-edit-format.js, stop-format-typecheck.js) or use absolute paths (process.execPath, resolved formatter bins).

Related

Test plan

  • Windows: node scripts/claw.js <session> "hello" reaches the claude CLI instead of erroring with ENOENT
  • macOS/Linux: no behavior change (shell flag only set on win32)
  • Existing tests remain green

Summary by cubic

Fixes the Windows ENOENT error in scripts/claw.js by enabling shell for spawnSync('claude', ...), allowing PATH to resolve claude.cmd. Windows runs the claude CLI correctly; macOS/Linux behavior is unchanged.

Written for commit e09af12. Summary will update on new commits.

Summary by CodeRabbit

Bug Fixes

  • Fixed a critical Windows platform compatibility issue that prevented the Claude command from executing properly on Windows systems. Windows users can now run the command successfully without errors, ensuring consistent functionality across all supported operating systems.

Fixes affaan-m#1469.

On Windows the `claude` binary installed via `npm i -g @anthropic-ai/claude-code`
is `claude.cmd`, and Node's spawn() cannot resolve .cmd wrappers via PATH
without shell: true. The call failed with `spawn claude ENOENT` and claw.js
returned an error string to the caller.

Mirrors the fix pattern applied in PR affaan-m#1456 for the MCP health-check hook.
'claude' is a hardcoded literal (not user input), so enabling shell on Windows
only is safe.
Copilot AI review requested due to automatic review settings April 16, 2026 06:08
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 16, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 050908b1-670a-407b-acef-9da0f6a6b860

📥 Commits

Reviewing files that changed from the base of the PR and between 1a50145 and e09af12.

📒 Files selected for processing (1)
  • scripts/claw.js

📝 Walkthrough

Walkthrough

A conditional flag enabling shell execution is added to the spawnSync call for Windows platforms in scripts/claw.js, allowing the npm-installed claude.cmd wrapper to resolve correctly and preventing ENOENT failures on Windows.

Changes

Cohort / File(s) Summary
Windows Process Execution Fix
scripts/claw.js
Conditionally enables shell: true in spawnSync when running on Windows (process.platform === 'win32') to resolve the npm-installed claude.cmd wrapper and prevent spawn ENOENT errors.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~3 minutes

Possibly related issues

Possibly related PRs

Poem

🐰 On Windows where CMD wrappers hide,
A shell flag opens the executable door wide,
No ENOENT ghosts shall roam,
Claude finds its path back home! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and clearly describes the main fix: resolving a Windows-specific ENOENT spawn failure for the 'claude' command in claw.js.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 16, 2026

Greptile Summary

This PR adds shell: process.platform === 'win32' to the spawnSync('claude', ...) call in scripts/claw.js, mirroring the fix already applied to scripts/hooks/mcp-health-check.js. On Windows, the npm-installed claude binary is a .cmd wrapper that Node.js spawn cannot resolve through PATH without invoking a shell, causing an ENOENT failure on every askClaude() call.

Confidence Score: 5/5

Safe to merge — single-line targeted fix with a clear precedent in the codebase and no behavioral change on non-Windows platforms.

The change is minimal (one option added to an existing spawnSync call), mirrors an already-reviewed pattern from mcp-health-check.js, and only activates on Windows. The only finding is a P2 observation about shell metacharacters in user-supplied args, which carries negligible practical risk for a local developer REPL where the user controls their own input.

No files require special attention.

Important Files Changed

Filename Overview
scripts/claw.js Adds shell: process.platform === 'win32' to the spawnSync options in askClaude(); minimal, targeted fix for Windows ENOENT with a clear inline comment explaining the reasoning.

Sequence Diagram

sequenceDiagram
    participant U as User (claw REPL)
    participant M as main()
    participant A as askClaude()
    participant S as spawnSync
    participant W as Windows cmd.exe
    participant C as claude CLI

    U->>M: types message
    M->>A: askClaude(eccContext, history, message, model)
    A->>A: buildPrompt(systemPrompt, history, userMessage)
    Note over A: shell = process.platform === 'win32'
    A->>S: spawnSync('claude', args, {shell: win32?})
    alt Windows (shell: true)
        S->>W: cmd.exe /d /s /c "claude --model ... -p ..."
        W->>C: resolves claude.cmd via PATH
    else macOS/Linux (shell: false)
        S->>C: direct spawn via PATH
    end
    C-->>S: stdout response
    S-->>A: result
    A-->>M: response string
    M->>U: prints response
Loading

Reviews (1): Last reviewed commit: "fix(scripts): resolve claude.cmd on Wind..." | Re-trigger Greptile

Comment thread scripts/claw.js
Comment on lines 98 to 104
const result = spawnSync('claude', args, {
encoding: 'utf8',
stdio: ['pipe', 'pipe', 'pipe'],
env: { ...process.env, CLAUDECODE: '' },
timeout: 300000,
shell: process.platform === 'win32',
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Shell metacharacters in -p prompt on Windows

With shell: true, Node.js joins the command and all args with spaces and passes the result to cmd.exe /d /s /c "...". The fullPrompt (which contains live user input) is included unescaped in that command string, so a Windows user whose message contains " or & <command> could trigger unintended cmd.exe interpretation.

In practice the risk is self-inflicted since the person typing is also the one running the REPL, but it's worth noting that the "safe because claude is a hardcoded literal" reasoning in the comment applies only to the command name, not to the args. A future-proof alternative is to resolve the .cmd path explicitly rather than relying on the shell:

const command = process.platform === 'win32'
  ? (process.env.CLAUDE_CMD_PATH || 'claude.cmd')
  : 'claude';
const result = spawnSync(command, args, {
  encoding: 'utf8',
  stdio: ['pipe', 'pipe', 'pipe'],
  env: { ...process.env, CLAUDECODE: '' },
  timeout: 300000,
  shell: process.platform === 'win32', // still needed to execute .cmd
});

Or, to avoid the shell entirely and protect args, use where.exe / which to pre-resolve the absolute path to claude.cmd before spawning. No action required for this PR — just worth keeping in mind if prompt content ever comes from a less-trusted source.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid observation on attack surface. One note: the snippet in this comment still sets shell: process.platform === 'win32', so switching to process.env.CLAUDE_CMD_PATH || 'claude.cmd' by itself doesn't remove the cmd.exe parse of joined args — the metacharacter risk in fullPrompt would remain.

The only way to actually eliminate shell parsing of args when executing a .cmd wrapper is to bypass the wrapper entirely — i.e., invoke the underlying node script that claude.cmd dispatches to (e.g. spawnSync(process.execPath, [<path-to-claude.js>, ...args])). That's a broader refactor than this ENOENT fix.

Agreed this is a self-inflicted concern in a REPL context (the user typing is the user running it). Leaving as-is for this PR and filing a follow-up for proper arg hardening — will add it to TECH_DEBT.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes Windows spawnSync('claude', ...) failures in scripts/claw.js by enabling shell execution on win32 so claude.cmd can be resolved from PATH.

Changes:

  • Add Windows-specific shell: true behavior to the spawnSync('claude', ...) invocation in askClaude().
  • Add inline rationale comments explaining the Windows .cmd / ENOENT behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 1 file

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(scripts): claw.js spawn 'claude' fails with ENOENT on Windows

2 participants