Fix Windows ENOENT/uv_spawn when spawning Agent CLIs - #5
Open
Kelvinz-89757 wants to merge 1 commit into
Open
Conversation
Bun.spawn performs no PATHEXT-style resolution and executes the literal command string it is given. npm installs a cross-platform executable as sibling files sharing one base name: an extension-less POSIX shell script, a `.cmd` batch wrapper, and sometimes a `.ps1` wrapper. Both CodexAgent and ClaudeAgent handed Bun.spawn the bare, extension-less command name, which native Windows process creation cannot execute directly, raising `ENOENT: no such file or directory, uv_spawn`. Both agents now resolve the configured command through resolveExecutable(), which prefers a `.exe`/`.cmd`/`.bat` sibling on win32 before falling back to the bare name, and pass the resolved path to Bun.spawn instead. Fixes deerwork-ai#4. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #4.
Bun.spawnperforms no PATHEXT-style resolution — it executes the literalcommand string it is given. npm installs a cross-platform executable (like
claude) as a set of sibling files sharing one base name: an extension-lessPOSIX shell script, a
.cmdbatch wrapper, and sometimes a.ps1wrapper.Both
CodexAgentandClaudeAgenthandBun.spawnthe bare, extension-lesscommand name (
this.#config.command, defaulting to"claude"/"codex"),which native Windows process creation cannot execute directly, raising
ENOENT: no such file or directory, uv_spawn '<command>'— exactly thefailure reported in #4, and root-caused there via
Bun.spawn/uv_spawnnotfollowing the same PATHEXT resolution a shell (or Node's own
child_process.spawn, which special-cases.cmd/.bat) would.resolveExecutable()(src/agents/resolve-executable.ts): onwin32, tries the configured command with.exe,.cmd,.batextensions (in that order) via
Bun.which, falling back to the bare name;on every other platform, and for any already-absolute path, behavior is
unchanged (passthrough to
Bun.which).CodexAgentandClaudeAgentnow resolve the command once perrun()call and spawn the resolved path instead of the bare configured name. The
existing
CodexCliNotFoundError/ClaudeCliNotFoundErrornow fire off thesame resolution call, so behavior when the CLI truly isn't installed is
unchanged.
Why both agents
Both harnesses share the identical
Bun.spawn(command)/Bun.which(command)pattern, so this isn't Claude-specific — Codex is exposed to the same bug on
Windows, it just doesn't appear to have been reported yet.
Important caveat — please test on real Windows before merging
I implemented and verified this on macOS, reasoning from Bun's documented
Bun.spawn/Bun.whichbehavior (Bun.spawn's options exposewindowsVerbatimArguments/windowsHidebut no shell-resolution option) andfrom the diagnosis already done in #4. I do not have a Windows machine to
reproduce the original failure or confirm this fix resolves it end-to-end.
The new
resolveExecutable()unit tests exercise the win32 branch'sresolution logic deterministically (by injecting
platformand a mockwhich, so they run correctly on any host OS), but they cannot exercise theactual
Bun.spawncall on Windows.@guohui666 — you already have a working repro environment and did the
original diagnostic legwork in #4 (thank you, it made root-causing this
straightforward). It would be great if you could pull this branch and confirm
deer-workflow create --agent claude "..."now works without themklink/PATH workarounds. Happy to iterate if anything's off.Test plan
bun run typecheckbun run lintbun run format:checkbun test(all passing, including 8 newresolveExecutableunit testscovering: non-Windows passthrough, Windows
.exe/.cmdpreferenceorder, fallback to the bare name,
nullwhen nothing resolves, andabsolute-path passthrough for both Windows- and POSIX-style paths)
requesting help from the issue reporter or another Windows user)