Summary
On Windows, codex-companion.mjs setup reports codex, npm, and auth as "not found" even when all are correctly installed and available from the shell.
Root Cause
runCommand() in scripts/lib/process.mjs calls spawnSync(command, args, ...) with shell defaulting to false.
On Windows, npm global installs create .cmd batch wrappers (e.g., codex.cmd, npm.cmd). Node.js spawnSync with shell: false does not resolve .cmd extensions, so the call fails with ENOENT.
Environment
- OS: Windows 11 Pro (10.0.26200)
- Node.js: v23.10.0
- npm: 11.12.1 (via Volta)
- Codex CLI: 0.117.0 (installed at
AppData/Roaming/npm/codex)
- Claude Code: 2.1.87
Reproduction
// ENOENT — fails
spawnSync("codex", ["--version"], { encoding: "utf8" });
// Works — returns "codex-cli 0.117.0"
spawnSync("codex", ["--version"], { encoding: "utf8", shell: true });
Setup output before fix:
{
"ready": false,
"npm": { "available": false, "detail": "not found" },
"codex": { "available": false, "detail": "not found" },
"auth": { "available": false, "loggedIn": false, "detail": "not found" }
}
Suggested Fix
In scripts/lib/process.mjs, add shell: true on Windows:
export function runCommand(command, args = [], options = {}) {
+ const useShell = options.shell ?? (process.platform === "win32");
const result = spawnSync(command, args, {
cwd: options.cwd,
env: options.env,
encoding: "utf8",
input: options.input,
- stdio: options.stdio ?? "pipe"
+ stdio: options.stdio ?? "pipe",
+ shell: useShell
});
This is safe because:
- Only activates on
win32, no impact on macOS/Linux
options.shell allows callers to override if needed
- Fixes detection for all npm-global-installed binaries (
codex, npm, etc.)
After Fix
{
"ready": true,
"npm": { "available": true, "detail": "11.12.1" },
"codex": { "available": true, "detail": "codex-cli 0.117.0; advanced runtime available" },
"auth": { "available": true, "loggedIn": true, "detail": "authenticated" }
}
Summary
On Windows,
codex-companion.mjs setupreports codex, npm, and auth as "not found" even when all are correctly installed and available from the shell.Root Cause
runCommand()inscripts/lib/process.mjscallsspawnSync(command, args, ...)withshelldefaulting tofalse.On Windows, npm global installs create
.cmdbatch wrappers (e.g.,codex.cmd,npm.cmd). Node.jsspawnSyncwithshell: falsedoes not resolve.cmdextensions, so the call fails withENOENT.Environment
AppData/Roaming/npm/codex)Reproduction
Setup output before fix:
{ "ready": false, "npm": { "available": false, "detail": "not found" }, "codex": { "available": false, "detail": "not found" }, "auth": { "available": false, "loggedIn": false, "detail": "not found" } }Suggested Fix
In
scripts/lib/process.mjs, addshell: trueon Windows:export function runCommand(command, args = [], options = {}) { + const useShell = options.shell ?? (process.platform === "win32"); const result = spawnSync(command, args, { cwd: options.cwd, env: options.env, encoding: "utf8", input: options.input, - stdio: options.stdio ?? "pipe" + stdio: options.stdio ?? "pipe", + shell: useShell });This is safe because:
win32, no impact on macOS/Linuxoptions.shellallows callers to override if neededcodex,npm, etc.)After Fix
{ "ready": true, "npm": { "available": true, "detail": "11.12.1" }, "codex": { "available": true, "detail": "codex-cli 0.117.0; advanced runtime available" }, "auth": { "available": true, "loggedIn": true, "detail": "authenticated" } }