Summary
On Windows, codex-companion cancel <jobId> fails when process.env.SHELL points to Git Bash (or any MSYS-rooted shell). The taskkill flags get path-converted by MSYS into nonsense like C:/Program Files/Git/PID 12345 C:/Program Files/Git/T C:/Program Files/Git/F, taskkill rejects them, and the cancel surfaces as Error: taskkill ... exit=1: ERROR: ....
Plugin version: 1.0.3
File: `scripts/lib/process.mjs`
Repro
- Windows + Git for Windows installed.
- `export SHELL="/usr/bin/bash"` (Git Bash default) or run inside a Git Bash session that exports `SHELL`.
- Start a long codex job: `node codex-companion.mjs task --background -- "sleep 60"`.
- `node codex-companion.mjs cancel --json`.
Expected: job cancelled. Actual: `terminateProcessTree` throws because spawned `taskkill` ran with mangled args.
Root cause
`scripts/lib/process.mjs:12`:
```js
shell: process.platform === "win32" ? (process.env.SHELL || true) : false,
```
When `SHELL` is set to a bash from a Git for Windows install, `spawnSync` runs the command through bash. MSYS path conversion then rewrites every argv element starting with `/` (like `/PID`, `/T`, `/F`) into a `C:/Program Files/Git/...` path, because MSYS treats them as Unix-style paths. `taskkill` sees no recognizable options and exits non-zero.
Suggested fix
`taskkill` is a Win32 .exe — there's no need to go through a shell at all. Either:
- Direct invocation (preferred): in `terminateProcessTree`, call `runCommand` with an option that forces `shell: false` (or spawn `taskkill.exe` directly). The function already has a clean argv.
- Drop `SHELL` reliance in `runCommand` for Windows: `shell: true` (cmd.exe) instead of `shell: SHELL || true`. `cmd.exe` doesn't path-convert.
Option 1 is the smaller and safer change.
Related (separate but adjacent)
`looksLikeMissingProcessMessage` only matches English (`not found|no running instance|cannot find|...`). On Korean Windows the localized "프로세스를 찾을 수 없습니다" doesn't match, so an already-dead PID still throws via `formatCommandFailure`. Worth either matching by `errorlevel` (taskkill returns 128 for "not found") or extending the regex with non-English variants — the former is locale-proof.
Workaround
In my MCP wrapper (`codex-mcp/src/codex-exec.mjs`) I strip `SHELL` from the child env before spawning the companion on Windows. This forces the cmd.exe fallback inside `runCommand`. Confirmed `taskkill /PID /T /F` then runs cleanly.
Summary
On Windows,
codex-companion cancel <jobId>fails whenprocess.env.SHELLpoints to Git Bash (or any MSYS-rooted shell). Thetaskkillflags get path-converted by MSYS into nonsense likeC:/Program Files/Git/PID 12345 C:/Program Files/Git/T C:/Program Files/Git/F, taskkill rejects them, and the cancel surfaces asError: taskkill ... exit=1: ERROR: ....Plugin version: 1.0.3
File: `scripts/lib/process.mjs`
Repro
Expected: job cancelled. Actual: `terminateProcessTree` throws because spawned `taskkill` ran with mangled args.
Root cause
`scripts/lib/process.mjs:12`:
```js
shell: process.platform === "win32" ? (process.env.SHELL || true) : false,
```
When `SHELL` is set to a bash from a Git for Windows install, `spawnSync` runs the command through bash. MSYS path conversion then rewrites every argv element starting with `/` (like `/PID`, `/T`, `/F`) into a `C:/Program Files/Git/...` path, because MSYS treats them as Unix-style paths. `taskkill` sees no recognizable options and exits non-zero.
Suggested fix
`taskkill` is a Win32 .exe — there's no need to go through a shell at all. Either:
Option 1 is the smaller and safer change.
Related (separate but adjacent)
`looksLikeMissingProcessMessage` only matches English (`not found|no running instance|cannot find|...`). On Korean Windows the localized "프로세스를 찾을 수 없습니다" doesn't match, so an already-dead PID still throws via `formatCommandFailure`. Worth either matching by `errorlevel` (taskkill returns 128 for "not found") or extending the regex with non-English variants — the former is locale-proof.
Workaround
In my MCP wrapper (`codex-mcp/src/codex-exec.mjs`) I strip `SHELL` from the child env before spawning the companion on Windows. This forces the cmd.exe fallback inside `runCommand`. Confirmed `taskkill /PID /T /F` then runs cleanly.