diff --git a/plugins/codex/scripts/lib/process.mjs b/plugins/codex/scripts/lib/process.mjs index dd8fc3751..ab913b2f4 100644 --- a/plugins/codex/scripts/lib/process.mjs +++ b/plugins/codex/scripts/lib/process.mjs @@ -66,7 +66,13 @@ export function terminateProcessTree(pid, options = {}) { if (platform === "win32") { const result = runCommandImpl("taskkill", ["/PID", String(pid), "/T", "/F"], { cwd: options.cwd, - env: options.env + env: options.env, + // runCommand prefers process.env.SHELL on Windows, so under Git Bash this + // ran as `bash -c "taskkill /PID ..."` and MSYS path conversion rewrote + // the /PID switch into C:/Program Files/Git/PID, failing every kill with + // "ERROR: Invalid argument/option". taskkill takes /-switches and an + // argument array, so it does not need a shell. + shell: false }); if (!result.error && result.status === 0) { diff --git a/tests/process.test.mjs b/tests/process.test.mjs index 80e0715b0..f59bd56d4 100644 --- a/tests/process.test.mjs +++ b/tests/process.test.mjs @@ -7,8 +7,8 @@ test("terminateProcessTree uses taskkill on Windows", () => { let captured = null; const outcome = terminateProcessTree(1234, { platform: "win32", - runCommandImpl(command, args) { - captured = { command, args }; + runCommandImpl(command, args, options) { + captured = { command, args, shell: options?.shell }; return { command, args, @@ -26,7 +26,10 @@ test("terminateProcessTree uses taskkill on Windows", () => { assert.deepEqual(captured, { command: "taskkill", - args: ["/PID", "1234", "/T", "/F"] + args: ["/PID", "1234", "/T", "/F"], + // Not through a shell: under Git Bash, MSYS path conversion turns the /PID + // switch into C:/Program Files/Git/PID and taskkill rejects it. + shell: false }); assert.equal(outcome.delivered, true); assert.equal(outcome.method, "taskkill");