diff --git a/codex-cli/src/utils/agent/exec.ts b/codex-cli/src/utils/agent/exec.ts index 22b75c02..876e144a 100644 --- a/codex-cli/src/utils/agent/exec.ts +++ b/codex-cli/src/utils/agent/exec.ts @@ -1,5 +1,6 @@ import type { ExecInput, ExecResult } from "./sandbox/interface.js"; import type { SpawnOptions } from "child_process"; +import type { ParseEntry } from "shell-quote"; import { process_patch } from "./apply-patch.js"; import { SandboxType } from "./sandbox/interface.js"; @@ -8,9 +9,17 @@ import { exec as rawExec } from "./sandbox/raw-exec.js"; import { formatCommandForDisplay } from "../../format-command.js"; import fs from "fs"; import os from "os"; +import { parse } from "shell-quote"; const DEFAULT_TIMEOUT_MS = 10_000; // 10 seconds +function requiresShell(cmd: Array): boolean { + return cmd.some((arg) => { + const tokens = parse(arg) as Array; + return tokens.some((token) => typeof token === "object" && "op" in token); + }); +} + /** * This function should never return a rejected promise: errors should be * mapped to a non-zero exit code and the error message should be in stderr. @@ -33,6 +42,7 @@ export function exec( const opts: SpawnOptions = { timeout: timeoutInMillis || DEFAULT_TIMEOUT_MS, + ...(requiresShell(cmd) ? { shell: true } : {}), ...(workdir ? { cwd: workdir } : {}), }; // Merge default writable roots with any user-specified ones.