Claude Code and Codex Devs
Stop Trusting
the Exit Code
Run Codex or Claude Code as a subprocess and get a verified answer back, not a hang, a false success, or a silent switch to API billing.
Codex and Claude Code lie about success. Get the flags wrong and they hang, or bill your API key.
The external problem
You spawn `codex exec` from a script on Windows and it hangs forever because stdin was never closed, or `claude -p` exits 0 while `git status` shows nothing actually changed.
The internal problem
You stop trusting your own automation. Every headless run becomes a run you have to babysit and re-check by hand, which defeats the point of scripting it.
What's at stake
Keep guessing at flags and you either burn real API spend you meant to avoid, or ship a report that says “done” over work that never happened.
Five steps. Each one closes a failure mode this skill has actually hit.
Run the whole flow before your first scripted call, or jump straight to the step where your last run broke.
Lock In the Rules
Close stdin on every Codex call so it can’t hang on an unwritten pipe, own the clock since neither CLI enforces a timeout, and set sandbox, approval, and working directory explicitly instead of inheriting global config.
Choose the Call Shape
Pick a quick read-only question or a long write-capable run, for either `codex exec` or `claude -p`, down to the specific flags each shape needs, from `--sandbox workspace-write` to `--permission-mode acceptEdits`.
Run Under a Timeout
Foreground short calls with the Bash tool’s own timeout, or use `run_in_background` for anything that could pass ten minutes, since the harness notifies on completion instead of making you poll.
Verify Against Ground Truth
Read the answer from `--json` output or the `.result` field, never from an exit code, since both CLIs are effectively 0 or 1 and Codex has exited 0 on Ctrl+C. Then check git status, file existence, or a real test run before trusting a claimed success.
Retrieve It Later
If the run outlives the conversation, the full transcript is still on disk (Codex under `~/.codex/sessions/`, Claude under `~/.claude/projects/`). Read it through the `estack-read-agent-history` skill, never by raw-parsing the `.jsonl`.
Flags, exit codes, and traps, per CLI.
Every claim carries a source URL: official docs for both CLIs, plus the GitHub issues where a behavior is undocumented and had to be reproduced firsthand, like the Windows stdin hang and Codex exiting 0 on Ctrl+C.
Reference
Codex Exec
Flags
| Flag | What it does | ||
|---|---|---|---|
--cd, -C <path> | Workspace root for the run | ||
| `--sandbox, -s <read-only\ | workspace-write\ | danger-full-access>` | Sandbox policy. Default with no flags: read-only |
| `--ask-for-approval, -a <untrusted\ | on-request\ | never>` | Approval policy. Use never headless |
--dangerously-bypass-approvals-and-sandbox (--yolo) | Full bypass — "only use inside an isolated runner" | ||
--full-auto | Deprecated; use explicit --sandbox instead | ||
-c, --config key=value | Inline config override, repeatable (e.g. -c sandbox_workspace_write.network_access=true) | ||
--ignore-user-config | Skip $CODEX_HOME/config.toml entirely — use when the global config must not interfere | ||
--json | stdout becomes a JSONL event stream (see below) | ||
--model, -m <id> | Model override | ||
-o, --output-last-message <path> | Write final message to a file | ||
--output-schema <path> | Enforce final response against a JSON Schema file | ||
--ephemeral | Don't persist a session rollout file | ||
--skip-git-repo-check | Allow running outside a git repo (required otherwise) | ||
codex exec resume --last / resume <SESSION_ID> | Continue a prior exec session |
Prompt input: positional arg, - for stdin-as-prompt, or pipe + positional arg (arg = instruction, piped content = context).
Output
- Plain mode: progress → stderr, final agent message → stdout (only that).
--jsonmode: JSONL events on stdout. Types:thread.started(hasthread_id— this is the SESSION_ID forresume),turn.started,item.started/item.updated/item.completed(each withitem: {id, type, status, ...}),turn.completed(hasusagetoken counts),turn.failed,error.- Final answer = the
item.completedevent whereitem.type == "agent_message"(itstextfield). - Field-level shapes beyond the ones above aren't fully documented — inspect one real
codex exec --json "ls" < /dev/nullrun before hard-coding a parser.
Exit codes — don't trust them
No official exit-code table exists, and Codex has exited 0 on Ctrl+C/SIGINT ([#4721](https://github.com/openai/codex/issues/4721)). Verify success via turn.completed in the --json stream or via the -o file's existence + content, then verify claimed *effects* against git/filesystem ground truth.
Sandbox semantics
read-only: no writes.workspace-write: writes inside workspace root only, network blocked by default — enable per-run with-c sandbox_workspace_write.network_access=true.danger-full-access: no boundaries.- Under
-a never, a sandbox-blocked action fails back to the model as a tool-call error — no hang, no prompt. - Non-zero exit from a tool the model runs can hide that tool's output from the model ([#1367](https://github.com/openai/codex/issues/1367)) — for linters/tests where non-zero means "findings", have the prompt tell Codex to append
|| true. - Sandboxed "build/tests pass" is not trustworthy when the sandbox network posture differs from the real environment (e.g. a build failing at a font fetch before typechecking ever ran).
- MCP tool calls in exec mode get auto-cancelled (stdin closed → approval prompt reads EOF as reject); only the full bypass flag works around it — avoid designs that need MCP tools inside headless Codex ([#24135](https://github.com/openai/codex/issues/24135)).
Auth (subscription, no API keys)
codex login= browser OAuth against the ChatGPT plan; credentials at~/.codex/auth.json(treat as a password). Tokens auto-refresh during use; mid-run expiry behavior is undocumented.- Setting
OPENAI_API_KEYorCODEX_API_KEYswitches billing to API rates. Precedence when both auths exist is buggy ([#3286](https://github.com/openai/codex/issues/3286)) — just never set them.
Timeouts and process management
- No whole-run timeout, and no per-tool-call timeout inside a turn. A runaway shell command inside Codex can hang the turn indefinitely and silently drop turn state. Enforce the deadline from the caller: Bash-tool
timeoutforeground, orrun_in_backgroundfor long runs. - If a run must be killed: kill the process tree (
taskkill /T /F /PID <pid>on Windows), then read the session rollout for partial work. - Rollouts:
~/.codex/sessions/YYYY/MM/DD/rollout-<local-timestamp>-<uuid>.jsonl, written incrementally (partial state generally survives a kill; no fsync guarantee documented).--ephemeralorhistory.persistence = "none"disables. Resume replays history but does not resurrect an in-flight tool call.
Windows specifics
- The stdin hang (SKILL.md rule 1) is the big one — reproduced with PowerShell spawned by Claude Code, exactly this use case ([#20919](https://github.com/openai/codex/issues/20919)). PowerShell has no
<redirection operator, so run Codex calls through Git Bash (< /dev/null) or cmd (< NUL). - Two sandbox implementations via
[windows] sandbox = "elevated" | "unelevated"in config.toml; unelevated (the non-admin fallback) has documented weaker network isolation ([source](https://learn.chatgpt.com/docs/windows/windows-sandbox)). - Sandbox-setup helper can crash with
STATUS_DLL_INIT_FAILED(0xC0000142) on specific directories (stale sandbox-user SIDs from old versions) — every command in the run fails instantly with that status. Check~/.codex/.sandbox/sandbox.<date>.log; often transient.
Use it now. It's free.
Works on all platforms. Pick yours and get set up in under a minute.
Download & upload in 60 seconds
One click downloads the zip and opens Claude.ai.
Click below — the zip downloads and Claude.ai opens.
Click the + button in the skills column on the left.
Select Create a skill.
Select Upload a skill and upload the zip.
Start your cli delegation session by running /estack-drive-cli-agent.
Drive CLI Agent ships in E-Stack, a set of 20 free skills installed by one command. See the whole stack.