
Collaborating With Codex
- 1 installs
- 130 repo stars
- Updated July 11, 2026
- appautomaton/agent-designer
collaborating-with-codex is a Claude skill that drives the Codex CLI headlessly through a bridge script so a primary agent can delegate coding tasks and receive structured JSON.
About
This skill uses the Codex CLI as an independent collaborator through a bridge script (scripts/codex_bridge.py) that wraps codex exec in JSON mode. A developer uses it for prototyping, debugging, code review, cross-model second opinions, and implementation handoff while the primary agent verifies results. It streams progress to stderr, returns structured JSON, and controls authority up front via sandbox, network, and approval flags with multi-turn continuity through SESSION_ID.
- Delegates tasks to the Codex CLI via a bridge that wraps codex exec in JSON mode
- Sandbox model: read-only, workspace-write, or danger-full-access
- Supports multi-turn sessions via SESSION_ID and up-front network/approval control
Collaborating With Codex by the numbers
- 1 all-time installs (skills.sh)
- Ranked #14,098 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Jul 30, 2026 (Skillselion catalog sync)
collaborating-with-codex capabilities & compatibility
- Capabilities
- agent delegation · code review · debugging
- Use cases
- code review · debugging · orchestration
What collaborating-with-codex says it does
Use Codex CLI as an independent collaborator while the primary agent remains responsible for verification, synthesis, and final user-facing decisions.
The bridge script (`scripts/codex_bridge.py`) wraps `codex exec` in JSON mode, streams progress to stderr, returns structured JSON, and manages multi-turn continuity via `SESSION_ID`.
npx skills add https://github.com/appautomaton/agent-designer --skill collaborating-with-codexAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1 |
|---|---|
| repo stars | ★ 130 |
| Last updated | July 11, 2026 |
| Repository | appautomaton/agent-designer ↗ |
What it does
Delegate code review, debugging, or implementation handoff to a headless Codex CLI session with a controlled sandbox.
Who is it for?
Cross-model second opinions, diff review, and implementation handoff via a sandboxed Codex session.
Skip if: Trivial one-shot tasks, or anything involving secrets, private keys, production data, or irreversible operations.
When should I use this skill?
You want to hand a coding subtask to a headless Codex CLI session and get JSON back.
What you get
The bridge returns structured JSON with SESSION_ID and sandbox-controlled authority.
- Structured JSON result with SESSION_ID and command telemetry
By the numbers
- Three sandbox modes: read-only, workspace-write, danger-full-access
Files
Collaborating with Codex
Use Codex CLI as an independent collaborator while the primary agent remains responsible for verification, synthesis, and final user-facing decisions.
The bridge script (scripts/codex_bridge.py) wraps codex exec in JSON mode, streams progress to stderr, returns structured JSON, and manages multi-turn continuity via SESSION_ID.
In Claude Code, run bridge calls in the background by default for non-trivial tasks:
Bash tool call:
command: python3 <skill_dir>/scripts/codex_bridge.py --cd "/project" --PROMPT "Analyze auth flow in src/auth/"
run_in_background: truerun_in_background is a host tool parameter, not a shell argument. Use the host's task-output view to monitor timestamped stderr progress, commands Codex ran, response previews, stalls, and completion.
Safety model
Default to read-only delegation:
--sandbox read-only- default; use for review, diagnosis, research, and second opinions.--sandbox workspace-write- use only after write access is appropriate; prefer an isolated worktree under/tmp.--sandbox danger-full-access- use only in an externally sandboxed environment.--bypass-sandbox- forwards Codex's dangerous bypass flag; requires explicit user consent.--full-auto- deprecated bridge compatibility alias only; maps toworkspace-writeand is not forwarded to Codex CLI.
Do not hand secrets, private keys, production data, or irreversible operations to Codex.
On a new host, probe sandbox support once with codex sandbox -- true (exit 0 means healthy). If sandboxed commands all fail with exit 182, the host kernel cannot enforce Codex's sandbox (common under containers, PRoot, and older WSL); the bridge warns when it sees this signature. On such hosts, delegate only from an externally sandboxed environment using --sandbox danger-full-access with explicit user consent.
Network access and approvals
codex exec is non-interactive: nothing can be approved mid-run. Actions that would prompt simply fail and the failure is returned to the model. Every authority decision is made up front by the primary agent through --sandbox, --add-dir, --search, and --network — get user consent before granting anything beyond read-only. -a on-request and -a untrusted therefore add nothing in bridge calls; use -a never or omit the flag.
Codex has two separate network paths:
- Web search: without
--search, Codex'sweb_searchtool answers from an OpenAI-maintained cached index and fetches no live pages.--searchswitches it to live search with no per-call approval, so passing the flag is itself the approval. - Shell network (
curl,pip,npm): blocked in bothread-onlyandworkspace-write. Grant it only when the task needs it (dependency installs, integration tests) via--sandbox workspace-write --network, preferably in an isolated worktree.
Quick start
Backticks in prompts trigger shell command substitution. Use a single-quoted heredoc; see references/shell-quoting.md.
PROMPT="$(cat <<'EOF'
Review src/auth.py around login() and propose fixes.
OUTPUT: Unified Diff Patch ONLY.
EOF
)"
python3 skills/collaborating-with-codex/scripts/codex_bridge.py \
--cd "." \
--PROMPT "$PROMPT"For large or generated handoffs, write the prompt under /tmp and avoid argv and shell-quoting limits:
python3 skills/collaborating-with-codex/scripts/codex_bridge.py \
--cd "." \
--prompt-file /tmp/codex-prompt.mdTypical response:
{
"success": true,
"SESSION_ID": "019...",
"agent_messages": "Findings...",
"commands_ran": 2
}For long-running calls, run the command in the host's background-command mode when available, then monitor stderr progress and the final JSON result.
Multi-turn sessions
Capture SESSION_ID from the first response and pass it back:
python3 skills/collaborating-with-codex/scripts/codex_bridge.py \
--cd "." \
--PROMPT "Analyze the bug in foo()."
python3 skills/collaborating-with-codex/scripts/codex_bridge.py \
--cd "." \
--SESSION_ID "<id>" \
--PROMPT "Now propose the smallest safe fix."
python3 skills/collaborating-with-codex/scripts/codex_bridge.py \
--cd "." \
--last \
--PROMPT "Check edge cases before finalizing."Bridge flags
| Flag | Purpose | Default |
|---|---|---|
--PROMPT | Prompt text | required unless --prompt-file is used |
--prompt-file | Read prompt from a file and stream it to Codex stdin | off |
--stdin-file | Pipe an additional context file while using --PROMPT | off |
--cd | Workspace root passed to Codex | required |
--SESSION_ID | Resume a previous session | new session |
--last | Resume the most recent session | off |
--resume-all | With resume, disable Codex cwd filtering | off |
--model | Override Codex model | CLI default |
--sandbox | read-only, workspace-write, or danger-full-access | read-only |
-a, --ask-for-approval | untrusted, on-request, never, or deprecated on-failure | CLI default |
--profile | Load a Codex config profile | off |
-c, --config | Override Codex config values | none |
--enable, --disable | Toggle Codex feature flags | none |
--image | Attach image files; repeatable | none |
--add-dir | Additional writable directories | none |
--skip-git-repo-check | Allow non-git directories | on |
--require-git-repo | Disable the default non-git allowance | off |
--ephemeral | Do not persist session files | off |
--bypass-sandbox | Forward Codex dangerous bypass flag | off |
--bypass-hook-trust | Forward Codex dangerous hook-trust bypass flag | off |
--search | Enable live web search by forwarding top-level codex --search before exec | off |
--network | Allow shell network in the workspace-write sandbox (sandbox_workspace_write.network_access=true) | off |
--oss, --local-provider | Use OSS/local provider mode | off |
--ignore-user-config, --ignore-rules, --strict-config | Config loading controls | off |
--output-schema | JSON Schema file for final response | none |
-o, --output-last-message | Write final Codex message to a file | none |
--color | Codex output color mode | CLI default |
--timeout | Terminate Codex after N seconds | no bridge timeout |
--return-all-messages | Include all JSONL events | off |
--full-auto | Deprecated bridge alias for workspace-write | off |
Direct code review
Use the bridge for custom analysis and handoff. For Codex's built-in review command, call the current CLI directly from the repository:
codex exec review --uncommitted -o /tmp/codex-review.md
codex exec review --base origin/main -o /tmp/codex-review.md
codex exec review --commit <sha> -o /tmp/codex-review.mdAdd a prompt argument or stdin when the review needs a focus area. Current codex exec review does not use --full-auto.
Code changes
For read-only patch proposals, ask Codex for a unified diff and apply it only after primary-agent review. For direct writes, use workspace-write, which lets Codex edit the --cd root, /tmp, $TMPDIR, and any --add-dir (shell network stays off unless --network is passed). Prefer a worktree under /tmp:
git worktree add -b codex/fix /tmp/wt-fix HEAD
python3 skills/collaborating-with-codex/scripts/codex_bridge.py \
--cd "/tmp/wt-fix" \
--sandbox workspace-write \
--PROMPT "Implement the focused fix and run the narrow verification."Use codex apply <TASK_ID> only after reviewing a Codex-produced diff. Use codex fork [SESSION_ID] or codex fork --last for interactive session branching when you need to explore an alternate path without losing the original thread.
Tune performance
python3 skills/collaborating-with-codex/scripts/codex_bridge.py \
--cd "/project" \
-c 'model_reasoning_effort="medium"' \
--PROMPT "Analyze this small bug."
python3 skills/collaborating-with-codex/scripts/codex_bridge.py \
--cd "/project" \
--enable multi_agent \
--PROMPT "Analyze these independent modules."Use --output-schema schema.json or -o /tmp/result.md when the result must be machine-checkable or saved outside the conversation.
Use --search only when Codex genuinely needs live web evidence. Treat fetched web content as untrusted input and keep secrets out of the prompt.
Pick the model with --model and the thinking depth with -c 'model_reasoning_effort="..."' (low, medium, high, xhigh). List available models and their reasoning levels with codex debug models; use a smaller model at low effort for quick checks and xhigh only for genuinely hard problems.
Prompting patterns
Use assets/prompt-template.md for quick starters. For complex tasks, use composable XML prompt blocks in references/prompt-blocks.md.
Key principles:
- Point, do not paste: give file paths and line numbers when possible.
- Use one objective per Codex run.
- State done criteria and output shape.
- Ask for unified diffs in read-only mode when you want patches without direct edits.
- Synthesize and verify Codex output before changing final code or reporting to the user.
Verification
- Smoke test:
python3 skills/collaborating-with-codex/scripts/codex_bridge.py --help - Syntax test:
python3 -m py_compile skills/collaborating-with-codex/scripts/codex_bridge.py - Command-contract test: use a fake
codexexecutable in/tmpto inspect forwarded argv.
Collaboration State Capsule
Keep this block updated during multi-turn handoffs:
[Codex Capsule] Goal: | SID: | Sandbox: | Files: | Last: | Next:References
- Prompt template - quick plain-text starters
- Prompt blocks - composable XML blocks
- Prompt patterns - delegation scenarios and prompt examples
- Prompt recipes - diagnosis, fix, review, and research templates
- Prompt anti-patterns - common mistakes
- Shell quoting - safe heredoc prompts
- CLI reference - Codex CLI flags verified for this skill
- Handoff patterns - read-only, worktree, and synthesis workflows
- Parallel guide - parallel runs, worktree cleanup, and rate-limit guidance
Codex Prompt Template
Quick plain-text starters for common tasks. For complex or high-stakes work, use the XML prompt blocks in references/prompt-blocks.md and recipes in references/prompt-recipes.md.
Analysis / Plan (read-only)
Task:
- <what to analyze>
Repo pointers:
- <file paths + approximate line numbers>
Constraints:
- Keep it concise and actionable.
- Reference files/lines instead of pasting code.
Output:
- Bullet list of findings and a proposed plan.Patch (Unified Diff only)
Task:
- <what to change>
Repo pointers:
- <file paths + approximate line numbers>
Constraints:
- OUTPUT: Unified Diff Patch ONLY.
- Minimal, focused changes. No unrelated refactors.
Output:
- A single unified diff patch.Review
Task:
- Review the following unified diff for correctness, edge cases, and missing tests.
Constraints:
- Return a checklist of issues + suggested fixes (no code unless requested).
Input diff:
<paste unified diff here>Implementation (workspace-write sandbox)
Task:
- <what to implement>
Repo pointers:
- <entry file paths + approximate line numbers>
Done criteria:
- <specific acceptance criteria>
Constraints:
- Stay focused on the stated task. No unrelated refactors.
- Run the narrowest test that proves correctness.
Output:
- List of files changed and a brief summary.When to upgrade to XML blocks
Use XML prompt blocks (references/prompt-blocks.md) when:
- The task is multi-step and you need a
<completeness_contract>. - Correctness matters and you want a
<verification_loop>. - You're doing review/research and need
<grounding_rules>. - Codex keeps stopping early — add
<default_follow_through_policy>.
See references/prompt-recipes.md for ready-to-use end-to-end templates.
Codex CLI Reference
Verified for this skill against local codex-cli 0.139.0.
Commands
| Command | Use |
|---|---|
codex exec | Non-interactive execution used by the bridge |
codex exec resume | Continue a saved exec session |
codex exec review | Built-in non-interactive code review |
codex review | Top-level review alias |
codex apply <TASK_ID> | Apply the latest diff produced by a Codex agent |
codex fork [SESSION_ID] | Fork an interactive session |
codex mcp list/add/remove | Manage Codex MCP servers |
codex features list | Inspect feature flags |
codex sandbox -- <cmd> | Run a command under the Codex sandbox; codex sandbox -- true probes sandbox health |
codex doctor | Diagnose install, auth, config, and sandbox status |
codex debug models | Render the model catalog (slugs and reasoning levels) as JSON |
codex exec
Use this shape for bridge calls:
codex exec --json -C /path/to/repo -s read-only -- "Prompt text"For live web search or explicit approval policy, place the global flags before exec:
codex --search -a never exec --json -C /path/to/repo -s read-only -- "Prompt text"Supported options used by the bridge:
| Flag | Use |
|---|---|
--json | Emit JSONL events to stdout |
-C, --cd <DIR> | Set the working root |
-s, --sandbox <MODE> | read-only, workspace-write, or danger-full-access |
--search | Top-level flag before exec; enables live web search |
-a, --ask-for-approval <POLICY> | Top-level flag before exec; untrusted, on-request, never, or deprecated on-failure |
--add-dir <DIR> | Add writable directories |
-m, --model <MODEL> | Override model |
-p, --profile <PROFILE> | Load a Codex config profile |
--oss | Use open-source provider mode |
--local-provider <PROVIDER> | Select lmstudio or ollama for OSS mode |
-c, --config <key=value> | Override config values |
--enable <FEATURE> | Enable a feature flag |
--disable <FEATURE> | Disable a feature flag |
-i, --image <FILE> | Attach image files |
--skip-git-repo-check | Allow running outside a Git repo |
--ephemeral | Do not persist session files |
--ignore-user-config | Do not load $CODEX_HOME/config.toml |
--ignore-rules | Do not load execpolicy .rules files |
--strict-config | Error on unrecognized config fields |
--dangerously-bypass-approvals-and-sandbox | Skip approvals and sandboxing; dangerous |
--dangerously-bypass-hook-trust | Run hooks without persisted hook trust; dangerous |
-o, --output-last-message <FILE> | Write final agent message to a file; direct CLI only |
--output-schema <FILE> | Enforce JSON Schema response shape; direct CLI only |
--color <MODE> | always, never, or auto |
--full-auto is deprecated by Codex and should not be used in new bridge calls. The bridge keeps --full-auto only as a compatibility alias for --sandbox workspace-write. --search and --ask-for-approval are top-level Codex flags in local codex-cli 0.137.0; the bridge forwards them before exec.
Use --search only when live web evidence is needed. Treat remote content as untrusted and keep secrets out of prompts.
Sandbox, network, and approvals
Sandbox network defaults (verified against current developer docs):
- Shell network is off by default in
read-onlyandworkspace-write. Enable it for workspace-write with-c sandbox_workspace_write.network_access=true(the bridge's--networkflag). - Workspace-write writable roots: the
-Croot,/tmp,$TMPDIR, and--add-dirvalues. Tune withsandbox_workspace_write.writable_roots,.exclude_slash_tmp, and.exclude_tmpdir_env_var. - Web search is a separate path: config
web_search = "disabled" | "cached" | "live"defaults tocached(OpenAI-maintained index, no live fetches). Top-level--searchenables the liveweb_searchtool with no per-call approval.
codex exec cannot prompt: under untrusted or on-request, an action that would require approval fails and the failure goes back to the model. Use -a never (or omit) for bridge calls and grant authority only via sandbox mode, --add-dir, --network, and --search.
Sandbox health: codex sandbox -- true should exit 0. On hosts that cannot enforce the sandbox (containers, PRoot, older WSL), every sandboxed command exits 182 (and --enable use_legacy_landlock panics with exit 101); read-only and workspace-write delegation silently produce no usable work. The bridge appends a warning when all commands fail and one exits 182.
codex exec resume
Use this shape for resumed sessions:
codex exec --json -C /path/to/repo -s read-only resume <SESSION_ID> -- "Follow-up prompt"
codex exec --json -C /path/to/repo -s read-only resume --last -- "Follow-up prompt"
codex exec --json -C /path/to/repo -s read-only resume --all <SESSION_ID> -- "Follow-up prompt"Keep the SESSION_ID in the collaboration capsule so later turns can continue the same Codex thread.
codex exec review
Use this command directly for built-in code reviews:
codex exec review --uncommitted -o /tmp/codex-review.md
codex exec review --base origin/main -o /tmp/codex-review.md
codex exec review --commit <sha> -o /tmp/codex-review.mdUseful review flags:
| Flag | Use |
|---|---|
--uncommitted | Review staged, unstaged, and untracked changes |
--base <BRANCH> | Review changes against a base branch |
--commit <SHA> | Review a specific commit |
--title <TITLE> | Add a review title |
-o <FILE> | Write final review text |
--json | Emit JSONL events |
Current codex exec review does not use --full-auto.
codex apply
Use only after reviewing a Codex-produced diff:
codex apply <TASK_ID>It applies the latest diff from the specified task as git apply to the local working tree.
codex fork
Use for interactive session branching:
codex fork <SESSION_ID> "Explore an alternate approach"
codex fork --last "Try a smaller variant"codex fork is not the same as codex exec resume; it branches a saved interactive session rather than continuing the bridge's normal JSON handoff.
JSONL events
The bridge relies on these event shapes:
{"type":"thread.started","thread_id":"019..."}
{"type":"turn.started"}
{"type":"item.completed","item":{"type":"agent_message","text":"..."}}
{"type":"item.completed","item":{"type":"command_execution","command":"...","exit_code":0,"status":"completed"}}
{"type":"item.completed","item":{"type":"file_change","status":"completed","changes":[{"path":"...","kind":"update"}]}}
{"type":"turn.completed","usage":{"input_tokens":123,"output_tokens":45}}Item types include agent messages, reasoning, command executions, file changes, MCP tool calls, web searches, and todo/plan updates. command_execution, file_change, and mcp_tool_call items carry a status field (completed, failed, or declined for commands); file_change is emitted once per patch whether it succeeded or failed. The bridge returns activity_counts plus compact counters for commands (commands_ran, commands_failed), web searches, MCP activity, file activity (files_changed, files_failed), and todo/plan updates when those events appear.
If Codex changes event names, update scripts/codex_bridge.py and this reference together.
Config and feature examples
-m gpt-5.4-mini
-c 'model_reasoning_effort="medium"'
-c 'model_reasoning_effort="xhigh"'
-c 'sandbox_workspace_write.network_access=true'
-c 'sandbox_permissions=["disk-full-read-access"]'
--enable multi_agent
--disable fast_modecodex debug models lists current model slugs and reasoning levels (low, medium, high, xhigh); at the time of verification: gpt-5.5 (default, medium), gpt-5.4, gpt-5.4-mini, and gpt-5.3-codex-spark (default high).
Current multi_agent, fast_mode, shell_snapshot, skill_mcp_dependency_install, guardian_approval, and hooks are stable according to local codex features list. Removed flags such as steer, request_rule, remote_models, search_tool, and js_repl should not be used.
Codex Handoff Patterns
Use these patterns to decide how much authority to give Codex and how to bring its work back into the primary session.
Read-only analysis
Use for diagnosis, architecture opinions, reviews, and research.
python3 skills/collaborating-with-codex/scripts/codex_bridge.py \
--cd "/path/to/repo" \
--sandbox read-only \
--PROMPT "$PROMPT"Ask Codex for evidence, file paths, line numbers, and a compact recommendation. The primary agent verifies the cited files before acting.
File-backed handoff
Use when a generated plan, review packet, or issue bundle is too large or too shell-sensitive for argv.
python3 skills/collaborating-with-codex/scripts/codex_bridge.py \
--cd "/path/to/repo" \
--sandbox read-only \
--prompt-file /tmp/codex-handoff.mdUse --stdin-file /tmp/context.txt with --PROMPT "..." when another command has already produced logs, diffs, or JSON that should be passed as context instead of pasted into the prompt.
Read-only patch proposal
Use when you want implementation help but do not want Codex to edit files.
Prompt requirements:
- Ask for
OUTPUT: Unified Diff Patch ONLY. - Include the expected behavior and tests to satisfy.
- Tell Codex not to modify files directly.
After Codex returns a patch, inspect it before applying.
Isolated write handoff
Use write access only in an isolated worktree, preferably under /tmp.
git worktree add -b codex/<task-name> /tmp/codex-<task-name> HEAD
python3 /path/to/skills/collaborating-with-codex/scripts/codex_bridge.py \
--cd "/tmp/codex-<task-name>" \
--sandbox workspace-write \
--PROMPT "$PROMPT"Shell network stays blocked in workspace-write; add --network only when the task needs dependency installs or live integration tests, with user consent.
After completion:
git -C /tmp/codex-<task-name> diff
git -C /tmp/codex-<task-name> status --shortReview and port the changes deliberately. Do not merge blind.
Parallel read-only runs
Split independent questions into separate Codex sessions:
- one prompt per subsystem or concern
- separate
SESSION_IDvalues - read-only sandbox
- explicit output contracts
Synthesize by comparing contradictions, shared evidence, and gaps. Do not stack unrelated tasks into one Codex run.
State capsule
Keep this compact state in the primary conversation:
[Codex Capsule] Goal: | SID: | Sandbox: | Files: | Last: | Next:Update it after each Codex turn so compaction or handoff does not lose the thread.
Parallel Execution and Worktree Isolation
Use this when a task naturally splits into independent Codex runs or when write access must be isolated.
Parallel read-only analysis
For independent analyses that do not modify files, run multiple bridge calls concurrently. In Claude Code, set run_in_background: true on each Bash tool call and monitor each task with TaskOutput.
python3 <skill_dir>/scripts/codex_bridge.py \
--cd "/project" \
--sandbox read-only \
--PROMPT "Analyze auth module for correctness risks."
python3 <skill_dir>/scripts/codex_bridge.py \
--cd "/project" \
--sandbox read-only \
--PROMPT "Analyze payment module for correctness risks."For direct CLI output files, use current flags:
codex exec --json -C /project -s read-only -o /tmp/result-auth.md -- "Analyze auth"
codex exec --json -C /project -s read-only -o /tmp/result-pay.md -- "Analyze payments"After all tasks complete, synthesize:
- contradictions between analyses
- shared dependencies or repeated findings
- gaps no agent covered
- concrete next action for the primary agent
Codex-managed subagents
Codex subagent workflows are explicit: ask Codex to spawn parallel agents only when the task truly splits into independent analysis or verification lanes.
python3 <skill_dir>/scripts/codex_bridge.py \
--cd "/project" \
--sandbox read-only \
--enable multi_agent \
--PROMPT "Use parallel subagents: one for security risks, one for test gaps, and one for maintainability. Wait for all agents, then summarize only final findings with file references."Keep subagent work read-heavy unless each write lane has its own worktree. Ask for summaries, not raw logs, so the primary agent receives distilled results.
Worktree isolation
When multiple Codex instances need write access, use one git worktree per task.
git worktree add -b codex/auth-fix /tmp/wt-auth HEAD
git worktree add -b codex/perf-fix /tmp/wt-perf HEADThen run each write task in its own worktree:
python3 <skill_dir>/scripts/codex_bridge.py \
--cd "/tmp/wt-auth" \
--sandbox workspace-write \
--PROMPT "Fix auth bug in src/auth/login.py. Run the narrow verification."
python3 <skill_dir>/scripts/codex_bridge.py \
--cd "/tmp/wt-perf" \
--sandbox workspace-write \
--PROMPT "Optimize query in src/db/queries.py. Run the narrow verification."Review each result before merging:
git -C /tmp/wt-auth diff
git -C /tmp/wt-perf diff
cd /original/repo
git merge codex/auth-fix
git merge codex/perf-fixCleanup after review:
git worktree remove /tmp/wt-auth
git worktree remove /tmp/wt-perfWorktree tips
- Put worktrees in
/tmpor outside the repo. - Use
git worktree add --detach /tmp/wt-readonly HEADfor read-only codebase snapshots. node_modules, virtualenvs, caches, and build outputs are not shared automatically.- The same branch cannot be checked out in multiple worktrees.
- Use absolute paths for result files and review artifacts.
- If Codex's environment lacks
rg, prompts should allow fallback tofind,ls,sed, or language-native tooling.
Rate limits
- Start with 2-3 concurrent Codex runs.
- Stagger launches if rate limit errors appear.
- Split by independent concern; do not run duplicate agents on the same vague task.
Prompt Patterns
Use this for deciding when to delegate and how to shape the prompt.
When to delegate
| Good fit | Poor fit |
|---|---|
| Large-scale code search or call-chain tracing | Simple edits faster for the primary agent |
| Bug investigation in unfamiliar code | Tasks requiring real-time user interaction |
| Cross-model code review | Work involving secrets or production data |
| Architecture comparison grounded in repo files | Questions already answered by current context |
| Parallel analysis of independent concerns | Vague prompts with no scope or expected output |
Pattern overview
| Pattern | Scenario | Sandbox |
|---|---|---|
| Deep analysis | Root cause, architecture, data flow | read-only |
| Code review | Pre-commit, PR, security pass | read-only |
| Parallel research | Multiple independent questions | read-only |
| Prototyping | Draft code or scaffold | read-only diff or isolated workspace-write |
| Architecture comparison | Evaluate design alternatives | read-only |
Deep analysis
In this codebase, we are seeing:
<symptom, error, or failing command>
Known clues:
- <file or module>
- <recent change>
- <related stack trace>
Analyze:
1. root cause with file:line evidence
2. full code path involved
3. smallest safe fix approach
Do not modify files.Bug fix proposal
Bug: <what is happening>
Reproduction: <steps or failing test>
Expected: <correct behavior>
Investigate root cause. Check:
- <paths>
- <related components>
Output: root cause analysis with file:line evidence, then a unified diff fix.
Do not modify files directly.After review, apply manually, use git apply, or use codex apply <TASK_ID> only when you have reviewed the Codex-produced diff.
Code review
Use direct review for repository diffs:
codex exec review --uncommitted -o /tmp/review.md
codex exec review --base origin/main -o /tmp/review.mdFor focused review prompts:
Review the changes in src/auth/ for:
- SQL injection vulnerabilities
- missing validation
- race conditions in session handling
Skip formatting-only changes.
Output prioritized findings with severity and suggested fixes.Prototyping
Implement <feature> in <project>.
Requirements:
- <requirement>
Reference:
- <existing similar code path>
Constraints:
- follow existing patterns
- no unrelated refactors
Output: unified diff patch only.
Do not modify files directly.For direct writes, move to an isolated worktree and use --sandbox workspace-write.
Architecture comparison
We need to implement <feature>. Compare:
Option A: <description>
Option B: <description>
Based on actual code under <paths>, evaluate:
1. implementation complexity
2. performance implications
3. impact on existing code
4. maintainability
Output a comparison table with a recommendation.Multi-file refactoring
Refactor <what> across <scope>.
Rules:
- <rule>
Files:
- <glob or directory>
Analyze impact first, list affected files, then produce a unified diff.
Do not modify files directly.Tips
- Set boundaries: say what not to touch.
- Pin file paths and line numbers.
- Provide clues: failing commands, stack traces, recent commits.
- Request structured output: table, JSON, or unified diff.
- Split unrelated work into separate Codex runs.
- Match effort with
-c 'model_reasoning_effort="medium"'or"xhigh"when needed.
Prompt Anti-Patterns
Common mistakes when prompting Codex. Each shows the problem and a fix.
Vague task framing
Bad:
Take a look at this and let me know what you think.Fix — state the job:
<task>
Review this change for material correctness and regression risks.
</task>Missing output contract
Bad:
Investigate and report back.Fix — define the shape:
<structured_output_contract>
Return:
1. root cause
2. evidence
3. smallest safe next step
</structured_output_contract>No follow-through default
Bad:
Debug this failure.Fix — tell Codex when to stop:
<default_follow_through_policy>
Keep going until you have enough evidence to identify the root cause confidently.
</default_follow_through_policy>Asking for more reasoning instead of a better contract
Bad:
Think harder and be very smart.Fix — add a verification loop:
<verification_loop>
Before finalizing, verify that the answer matches the observed evidence and task requirements.
</verification_loop>Mixing unrelated jobs into one run
Bad:
Review this diff, fix the bug you find, update the docs, and suggest a roadmap.Fix — one task per run: 1. Run review first. 2. Run a separate fix prompt if needed. 3. Use a third run for docs or roadmap.
Unsupported certainty
Bad:
Tell me exactly why production failed.Fix — require grounding:
<grounding_rules>
Ground every claim in the provided context or tool outputs.
If a point is an inference, label it clearly.
</grounding_rules>Prompt Blocks
Composable XML-tagged blocks for structuring Codex prompts. Use selectively — pick only the blocks your task needs.
Core
<task>
Use in every prompt. State the job, the context, and what done looks like.
<task>
Describe the concrete job, the relevant repository or failure context, and the expected end state.
</task>Output shape
<structured_output_contract>
Use when the response shape matters (reviews, diagnostics, recommendations).
<structured_output_contract>
Return exactly the requested output shape and nothing else.
Keep the answer compact.
Put the highest-value findings or decisions first.
</structured_output_contract><compact_output_contract>
Use when you want concise prose instead of a schema.
<compact_output_contract>
Keep the final answer compact and structured.
Do not include long scene-setting or repeated recap.
</compact_output_contract>Follow-through
<default_follow_through_policy>
Use when Codex should act without asking routine questions.
<default_follow_through_policy>
Default to the most reasonable low-risk interpretation and keep going.
Only stop to ask questions when a missing detail changes correctness, safety, or an irreversible action.
</default_follow_through_policy><completeness_contract>
Use for debugging, implementation, or any multi-step task that should not stop early.
<completeness_contract>
Resolve the task fully before stopping.
Do not stop at the first plausible answer.
Check whether there are follow-on fixes, edge cases, or cleanup needed for a correct result.
</completeness_contract><verification_loop>
Use when correctness matters.
<verification_loop>
Before finalizing, verify the result against the task requirements and the changed files or tool outputs.
If a check fails, revise the answer instead of reporting the first draft.
</verification_loop>Grounding
<missing_context_gating>
Use when Codex might otherwise guess about repository facts.
<missing_context_gating>
Do not guess missing repository facts.
If required context is absent, retrieve it with tools or state exactly what remains unknown.
</missing_context_gating><grounding_rules>
Use for review, research, or root-cause analysis.
<grounding_rules>
Ground every claim in the provided context or your tool outputs.
Do not present inferences as facts.
If a point is a hypothesis, label it clearly.
</grounding_rules><citation_rules>
Use when external research or source references matter.
<citation_rules>
Back important claims with citations or explicit references to the source material you inspected.
Prefer primary sources.
</citation_rules>Safety and scope
<action_safety>
Use for write-capable or potentially broad tasks.
<action_safety>
Keep changes tightly scoped to the stated task.
Avoid unrelated refactors, renames, or cleanup unless they are required for correctness.
Call out any risky or irreversible action before taking it.
</action_safety><tool_persistence_rules>
Use for long-running tool-heavy tasks.
<tool_persistence_rules>
Keep using tools until you have enough evidence to finish the task confidently.
Do not abandon the workflow after a partial read when another targeted check would change the answer.
</tool_persistence_rules>Task-specific
<research_mode>
Use for exploration, comparisons, or recommendations.
<research_mode>
Separate observed facts, reasoned inferences, and open questions.
Prefer breadth first, then go deeper only where the evidence changes the recommendation.
</research_mode><dig_deeper_nudge>
Use for review and adversarial inspection.
<dig_deeper_nudge>
After you find the first plausible issue, check for second-order failures, empty-state behavior, retries, stale state, and rollback paths before you finalize.
</dig_deeper_nudge>Prompt Recipes
End-to-end prompt templates for common Codex tasks. Copy the smallest recipe that fits, then trim what you don't need.
Blocks reference: prompt-blocks.md
Diagnosis
Find the root cause of a failing test, command, or runtime error.
<task>
Diagnose why [failing test / command / error] is breaking in this repository.
Use the available repository context and tools to identify the most likely root cause.
</task>
<compact_output_contract>
Return a compact diagnosis with:
1. most likely root cause
2. evidence
3. smallest safe next step
</compact_output_contract>
<default_follow_through_policy>
Keep going until you have enough evidence to identify the root cause confidently.
Only stop to ask questions when a missing detail changes correctness materially.
</default_follow_through_policy>
<verification_loop>
Before finalizing, verify that the proposed root cause matches the observed evidence.
</verification_loop>
<missing_context_gating>
Do not guess missing repository facts.
If required context is absent, state exactly what remains unknown.
</missing_context_gating>Narrow fix
Implement the smallest safe fix for an identified issue.
<task>
Implement the smallest safe fix for [issue] in this repository.
Preserve existing behavior outside the failing path.
</task>
<structured_output_contract>
Return:
1. summary of the fix
2. touched files
3. verification performed
4. residual risks or follow-ups
</structured_output_contract>
<default_follow_through_policy>
Default to the most reasonable low-risk interpretation and keep going.
</default_follow_through_policy>
<completeness_contract>
Resolve the task fully before stopping.
Do not stop after identifying the issue without applying the fix.
</completeness_contract>
<verification_loop>
Before finalizing, verify that the fix matches the task requirements and that the changed code is coherent.
</verification_loop>
<action_safety>
Keep changes tightly scoped to the stated task.
Avoid unrelated refactors or cleanup.
</action_safety>Root-cause review
Analyze a change for correctness and regression risks.
<task>
Analyze this change for material correctness and regression issues.
Focus on the provided repository context only.
</task>
<structured_output_contract>
Return:
1. findings ordered by severity
2. supporting evidence for each finding
3. brief next steps
</structured_output_contract>
<grounding_rules>
Ground every claim in the repository context or tool outputs.
If a point is an inference, label it clearly.
</grounding_rules>
<dig_deeper_nudge>
Check for second-order failures, empty-state handling, retries, stale state, and rollback paths before finalizing.
</dig_deeper_nudge>
<verification_loop>
Before finalizing, verify that each finding is material and actionable.
</verification_loop>Research / recommendation
Explore options and recommend a path.
<task>
Research the available options and recommend the best path for [topic].
</task>
<structured_output_contract>
Return:
1. observed facts
2. reasoned recommendation
3. tradeoffs
4. open questions
</structured_output_contract>
<research_mode>
Separate observed facts, reasoned inferences, and open questions.
Prefer breadth first, then go deeper only where the evidence changes the recommendation.
</research_mode>
<citation_rules>
Back important claims with explicit references to the sources you inspected.
Prefer primary sources.
</citation_rules>Shell quoting for --PROMPT
When invoking codex_bridge.py, remember that your shell parses the command line before Python receives the prompt.
The pitfall: Markdown backticks
Markdown inline code uses backticks (` like/this `). In bash/zsh, backticks mean command substitution, even inside double quotes, so this breaks before Codex runs:
python3 skills/collaborating-with-codex/scripts/codex_bridge.py \
--cd "." \
--PROMPT "Analyze `tmp/raw.json` and summarize."Typical symptoms:
zsh: permission denied: tmp/raw.jsonzsh: command not found: as_of
Recommended: heredoc
Build the prompt with a single-quoted heredoc delimiter (<<'EOF') so backticks, $VARS, and $(...) are not expanded by the shell:
PROMPT="$(cat <<'EOF'
Analyze `tmp/raw.json` and summarize.
Set `as_of` to `YYYY-MM-DD`.
EOF
)"
python3 skills/collaborating-with-codex/scripts/codex_bridge.py \
--cd "." \
--PROMPT "$PROMPT"Alternatives
- Escape backticks manually as `
\``. - Avoid Markdown backticks in CLI prompts.
#!/usr/bin/env python3
"""
Codex Bridge Script for Codex Skills.
Wraps Codex CLI exec mode to provide a JSON interface and multi-turn
continuity via SESSION_ID.
"""
from __future__ import annotations
import argparse
import json
import os
import queue
import re
import shutil
import subprocess
import sys
import threading
import time
from pathlib import Path
from typing import Any, Dict, Generator, List, Optional, Tuple
SANDBOX_MODES = ("read-only", "workspace-write", "danger-full-access")
PATH_WARNING_PREFIX = "WARNING: proceeding, even though we could not update PATH:"
STDIN_INFO_PREFIX = "Reading additional input from stdin..."
APPROVAL_POLICIES = ("untrusted", "on-failure", "on-request", "never")
def emit_json(result: Dict[str, Any], exit_code: int = 0) -> None:
print(json.dumps(result, indent=2, ensure_ascii=False))
raise SystemExit(exit_code)
def find_executable(name: str) -> str:
"""Locate an executable while handling Windows npm shims."""
found = shutil.which(name)
if found:
if os.name == "nt" and not Path(found).suffix:
for ext in (".cmd", ".bat", ".exe"):
alt = Path(found).parent / f"{name}{ext}"
if alt.is_file():
return str(alt)
return found
if os.name == "nt":
for env_var in ("APPDATA", "LOCALAPPDATA"):
base = os.environ.get(env_var, "")
if base:
for ext in (".cmd", ".bat", ".exe"):
candidate = Path(base) / "npm" / f"{name}{ext}"
if candidate.is_file():
return str(candidate)
return name
def preflight_check(cd: Path) -> Optional[str]:
if shutil.which("codex") is None:
return "Codex CLI not found in PATH. Install it and ensure `codex` is available."
if not cd.exists():
return f"Workspace root `{cd.absolute().as_posix()}` does not exist."
if not cd.is_dir():
return f"Workspace root `{cd.absolute().as_posix()}` is not a directory."
has_env_key = bool(os.environ.get("OPENAI_API_KEY"))
has_auth_file = Path.home().joinpath(".codex", "auth.json").is_file()
if not has_env_key and not has_auth_file:
return "No Codex auth found. Run `codex login` or export `OPENAI_API_KEY`."
return None
def normalize_sandbox(args: argparse.Namespace, warnings: List[str]) -> str:
sandbox = args.sandbox
if args.full_auto:
sandbox = "workspace-write"
warnings.append(
"`--full-auto` is deprecated for this bridge and is not forwarded to Codex; "
"using `--sandbox workspace-write` instead."
)
return sandbox
def build_command(args: argparse.Namespace, cd: Path, sandbox: str, prompt_arg: str) -> List[str]:
"""Build a Codex CLI command compatible with current `codex exec`."""
cmd = ["codex"]
if args.search:
cmd.append("--search")
if args.ask_for_approval:
cmd.extend(["-a", args.ask_for_approval])
cmd.extend(["exec", "--json", "-C", cd.absolute().as_posix(), "-s", sandbox])
for add_dir in args.add_dir:
cmd.extend(["--add-dir", add_dir])
if args.profile:
cmd.extend(["-p", args.profile])
if args.oss:
cmd.append("--oss")
if args.local_provider:
cmd.extend(["--local-provider", args.local_provider])
if args.color:
cmd.extend(["--color", args.color])
if args.SESSION_ID or args.last:
cmd.append("resume")
if args.model:
cmd.extend(["-m", args.model])
if args.bypass_sandbox:
cmd.append("--dangerously-bypass-approvals-and-sandbox")
if args.bypass_hook_trust:
cmd.append("--dangerously-bypass-hook-trust")
if args.skip_git_repo_check:
cmd.append("--skip-git-repo-check")
if args.ephemeral:
cmd.append("--ephemeral")
if args.ignore_user_config:
cmd.append("--ignore-user-config")
if args.ignore_rules:
cmd.append("--ignore-rules")
if args.strict_config:
cmd.append("--strict-config")
if args.output_schema:
cmd.extend(["--output-schema", str(args.output_schema)])
if args.output_last_message:
cmd.extend(["-o", str(args.output_last_message)])
for img in args.image:
cmd.extend(["-i", img])
if args.network:
cmd.extend(["-c", "sandbox_workspace_write.network_access=true"])
for cfg in args.config:
cmd.extend(["-c", cfg])
for feature in args.enable:
cmd.extend(["--enable", feature])
for feature in args.disable:
cmd.extend(["--disable", feature])
if args.SESSION_ID or args.last:
if args.resume_all:
cmd.append("--all")
if args.last:
cmd.append("--last")
elif args.SESSION_ID:
cmd.append(args.SESSION_ID)
cmd.extend(["--", prompt_arg])
return cmd
def stream_command(
cmd: List[str],
cwd: Optional[Path] = None,
stdin_file: Optional[Path] = None,
timeout_seconds: float = 0,
) -> Generator[str, None, int]:
"""Execute a command and yield stdout JSONL lines while forwarding stderr progress."""
resolved = cmd.copy()
resolved[0] = find_executable(cmd[0])
if os.name == "nt" and Path(resolved[0]).suffix.lower() in {".cmd", ".bat"}:
comspec = os.environ.get("COMSPEC", "cmd.exe")
resolved = [comspec, "/d", "/s", "/c", " ".join(f'"{arg}"' for arg in resolved)]
stdin_handle = None
try:
stdin = subprocess.DEVNULL
if stdin_file is not None:
stdin_handle = stdin_file.open("r", encoding="utf-8", errors="replace")
stdin = stdin_handle
proc = subprocess.Popen(
resolved,
shell=False,
stdin=stdin,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True,
encoding="utf-8",
errors="replace",
cwd=str(cwd) if cwd is not None else None,
)
finally:
if stdin_handle is not None:
stdin_handle.close()
out_q: queue.Queue[Optional[str]] = queue.Queue()
started_at = time.time()
def read_stdout() -> None:
assert proc.stdout is not None
for line in iter(proc.stdout.readline, ""):
stripped = line.strip()
if stripped:
out_q.put(stripped)
try:
if json.loads(stripped).get("type") == "turn.completed":
time.sleep(0.3)
proc.terminate()
break
except (json.JSONDecodeError, AttributeError, TypeError):
pass
proc.stdout.close()
out_q.put(None)
def read_stderr() -> None:
assert proc.stderr is not None
for line in iter(proc.stderr.readline, ""):
text = line.rstrip()
if text and not text.startswith(PATH_WARNING_PREFIX) and not text.startswith(STDIN_INFO_PREFIX):
print(f"[codex stderr] {text}", file=sys.stderr, flush=True)
proc.stderr.close()
t_out = threading.Thread(target=read_stdout, daemon=True)
t_err = threading.Thread(target=read_stderr, daemon=True)
t_out.start()
t_err.start()
while True:
if timeout_seconds and time.time() - started_at > timeout_seconds:
print(
f"[codex stderr] timeout after {timeout_seconds:g}s; terminating Codex",
file=sys.stderr,
flush=True,
)
proc.kill()
proc.wait()
return 124
try:
line = out_q.get(timeout=0.5)
if line is None:
break
yield line
except queue.Empty:
if proc.poll() is not None and not t_out.is_alive():
break
t_out.join(timeout=5)
t_err.join(timeout=2)
try:
return proc.wait(timeout=5)
except subprocess.TimeoutExpired:
proc.kill()
return proc.wait()
def summarize_event(
event: Dict[str, Any],
all_messages: List[Dict[str, Any]],
state: Dict[str, Any],
start_time: float,
) -> None:
all_messages.append(event)
def status(message: str) -> None:
elapsed = time.time() - start_time
print(f"[codex {elapsed:5.1f}s] {message}", file=sys.stderr, flush=True)
event_type = event.get("type", "")
if event_type == "thread.started" and event.get("thread_id"):
state["session_id"] = event["thread_id"]
status(f"Session: {event['thread_id']}")
elif event_type == "turn.started":
status("Codex is working...")
elif event_type == "turn.completed":
state["turn_completed"] = True
usage = event.get("usage", {})
if usage:
state["usage"] = usage
tokens_in = usage.get("input_tokens")
tokens_out = usage.get("output_tokens")
if tokens_in is not None or tokens_out is not None:
status(f"Done. Tokens: {tokens_in or 0} in / {tokens_out or 0} out")
else:
status("Done.")
item = event.get("item", {})
if isinstance(item, dict):
item_type = item.get("type", "")
if event_type == "item.completed" and item_type:
counts = state["activity_counts"]
counts[item_type] = counts.get(item_type, 0) + 1
item_type_lower = str(item_type).lower()
if "web_search" in item_type_lower or "web-search" in item_type_lower:
state["web_searches"] += 1
status("Web search completed")
elif "mcp" in item_type_lower:
state["mcp_tools_ran"] += 1
status(f"MCP activity: {item_type}")
elif "file" in item_type_lower and (
"change" in item_type_lower or "patch" in item_type_lower or "edit" in item_type_lower
):
if str(item.get("status", "")).lower() == "failed":
state["files_failed"] += 1
status(f"File activity FAILED: {item_type}")
else:
state["files_changed"] += 1
status(f"File activity: {item_type}")
elif "plan" in item_type_lower or item_type_lower == "todo_list":
state["plan_updates"] += 1
status("Plan updated")
if item_type == "agent_message" and isinstance(item.get("text"), str):
state["agent_messages"] += item["text"]
preview = item["text"][:80].replace("\n", " ")
if preview:
status(f"Response: {preview}{'...' if len(item['text']) > 80 else ''}")
elif item_type == "command_execution":
command = str(item.get("command", ""))
exit_code = item.get("exit_code")
if exit_code is not None:
state["commands_ran"] += 1
state["command_exit_codes"].append(exit_code)
if exit_code != 0 or str(item.get("status", "")).lower() in ("failed", "declined"):
state["commands_failed"] += 1
status(f"Ran: {command[:60]} (exit {exit_code})")
elif command:
status(f"Running: {command[:60]}")
if "fail" in event_type or "error" in event_type:
message = ""
error_obj = event.get("error")
if isinstance(error_obj, dict):
message = str(error_obj.get("message", ""))
message = message or str(event.get("message", ""))
if message and not re.match(r"^Reconnecting\.\.\.\s+\d+/\d+$", message):
state["errors"].append(message)
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Codex Bridge")
parser.add_argument("--PROMPT", default="", help="Instruction to send to Codex.")
parser.add_argument("--prompt-file", type=Path, default=None, help="Read the Codex prompt from a file.")
parser.add_argument(
"--stdin-file",
type=Path,
default=None,
help="Pipe a file to Codex stdin as additional context when --PROMPT is used.",
)
parser.add_argument("--cd", required=True, type=Path, help="Workspace root for Codex.")
parser.add_argument("--SESSION_ID", default="", help="Resume a previous session by thread ID.")
parser.add_argument("--last", action="store_true", help="Resume the most recent session.")
parser.add_argument(
"--resume-all",
action="store_true",
help="With --SESSION_ID or --last, disable Codex's cwd filtering while selecting sessions.",
)
parser.add_argument("--model", default="", help="Override the Codex model.")
parser.add_argument(
"--sandbox",
default="read-only",
choices=SANDBOX_MODES,
help="Sandbox policy for model-generated commands. Default: read-only.",
)
parser.add_argument(
"--full-auto",
action="store_true",
help="Deprecated compatibility alias: use workspace-write sandbox; not forwarded to Codex.",
)
parser.add_argument("--image", action="append", default=[], help="Attach image files to the prompt.")
parser.add_argument("--add-dir", action="append", default=[], help="Additional writable directories.")
parser.add_argument(
"--skip-git-repo-check",
action="store_true",
default=True,
help="Allow running outside a Git repo. Default: on for parity with codex-collab.",
)
parser.add_argument(
"--require-git-repo",
action="store_true",
help="Do not pass --skip-git-repo-check to Codex.",
)
parser.add_argument("--ephemeral", action="store_true", help="Do not persist session files.")
parser.add_argument("--profile", default="", help="Config profile from CODEX_HOME.")
parser.add_argument(
"--bypass-sandbox",
action="store_true",
help="Forward Codex's dangerous bypass flag. Use only with explicit user consent.",
)
parser.add_argument(
"--bypass-hook-trust",
action="store_true",
help="Forward Codex's dangerous hook-trust bypass flag. Use only with explicit user consent.",
)
parser.add_argument(
"--search",
action="store_true",
help="Enable live web search by forwarding top-level `codex --search` before exec.",
)
parser.add_argument(
"--network",
action="store_true",
help="Allow outbound shell network inside the workspace-write sandbox "
"(forwards `-c sandbox_workspace_write.network_access=true`).",
)
parser.add_argument(
"-a",
"--ask-for-approval",
choices=APPROVAL_POLICIES,
default="",
help="Approval policy for model-generated commands.",
)
parser.add_argument("--oss", action="store_true", help="Use Codex open-source provider mode.")
parser.add_argument("--local-provider", default="", help="Local OSS provider such as lmstudio or ollama.")
parser.add_argument("--ignore-user-config", action="store_true", help="Do not load CODEX_HOME config.")
parser.add_argument("--ignore-rules", action="store_true", help="Do not load user/project execpolicy rules.")
parser.add_argument("--strict-config", action="store_true", help="Error on unrecognized config fields.")
parser.add_argument("--output-schema", type=Path, default=None, help="JSON Schema file for final response.")
parser.add_argument("-o", "--output-last-message", type=Path, default=None, help="Write final message to file.")
parser.add_argument(
"--color",
choices=("always", "never", "auto"),
default="",
help="Codex output color mode.",
)
parser.add_argument(
"-c",
"--config",
action="append",
default=[],
metavar="key=value",
help="Override a Codex config value. Repeatable.",
)
parser.add_argument("--enable", action="append", default=[], metavar="FEATURE", help="Enable a feature.")
parser.add_argument("--disable", action="append", default=[], metavar="FEATURE", help="Disable a feature.")
parser.add_argument(
"--return-all-messages",
action="store_true",
help="Include all JSONL events in output.",
)
parser.add_argument(
"--timeout",
type=float,
default=0,
help="Terminate Codex after this many seconds. Default: no bridge timeout.",
)
return parser.parse_args()
def resolve_prompt_input(args: argparse.Namespace) -> Tuple[str, Optional[Path]]:
if args.PROMPT and args.prompt_file:
emit_json({"success": False, "error": "Use either `--PROMPT` or `--prompt-file`, not both."}, exit_code=2)
if args.prompt_file and args.stdin_file:
emit_json({"success": False, "error": "Use either `--prompt-file` or `--stdin-file`, not both."}, exit_code=2)
if not args.PROMPT and args.prompt_file is None:
emit_json({"success": False, "error": "Provide `--PROMPT` or `--prompt-file`."}, exit_code=2)
stdin_file: Optional[Path] = None
prompt_arg = args.PROMPT
if args.prompt_file is not None:
if not args.prompt_file.is_file():
emit_json({"success": False, "error": f"Prompt file not found: {args.prompt_file}"}, exit_code=2)
prompt_arg = "-"
stdin_file = args.prompt_file
elif args.stdin_file is not None:
if not args.stdin_file.is_file():
emit_json({"success": False, "error": f"Stdin context file not found: {args.stdin_file}"}, exit_code=2)
stdin_file = args.stdin_file
if os.name == "nt" and prompt_arg != "-":
prompt_arg = prompt_arg.replace("\n", "\\n").replace("\r", "\\r")
return prompt_arg, stdin_file
def main() -> None:
args = parse_args()
warnings: List[str] = []
if args.SESSION_ID and args.last:
emit_json({"success": False, "error": "Use either `--SESSION_ID` or `--last`, not both."}, exit_code=2)
if args.resume_all and not (args.SESSION_ID or args.last):
emit_json({"success": False, "error": "`--resume-all` requires `--SESSION_ID` or `--last`."}, exit_code=2)
if args.timeout < 0:
emit_json({"success": False, "error": "`--timeout` must be zero or a positive number of seconds."}, exit_code=2)
if args.require_git_repo:
args.skip_git_repo_check = False
if args.ask_for_approval == "on-failure":
warnings.append("`--ask-for-approval on-failure` is deprecated by Codex; prefer `on-request` or `never`.")
cd: Path = args.cd
error = preflight_check(cd)
if error:
emit_json({"success": False, "error": error}, exit_code=1)
sandbox = normalize_sandbox(args, warnings)
if args.bypass_sandbox:
warnings.append("Forwarding Codex dangerous bypass flag; this skips approvals and sandboxing.")
if args.bypass_hook_trust:
warnings.append("Forwarding Codex dangerous hook-trust bypass flag.")
if args.network and sandbox != "workspace-write":
warnings.append(
"`--network` only affects the workspace-write sandbox; "
f"it has no effect under `{sandbox}`."
)
prompt_arg, stdin_file = resolve_prompt_input(args)
cmd = build_command(args, cd, sandbox, prompt_arg)
cwd = cd.absolute() if args.SESSION_ID or args.last else None
all_messages: List[Dict[str, Any]] = []
state: Dict[str, Any] = {
"agent_messages": "",
"commands_ran": 0,
"commands_failed": 0,
"command_exit_codes": [],
"errors": [],
"session_id": None,
"turn_completed": False,
"activity_counts": {},
"files_changed": 0,
"files_failed": 0,
"mcp_tools_ran": 0,
"plan_updates": 0,
"usage": {},
"web_searches": 0,
}
start_time = time.time()
returncode = 1
try:
generator = stream_command(cmd, cwd=cwd, stdin_file=stdin_file, timeout_seconds=args.timeout)
while True:
try:
line = next(generator)
except StopIteration as finished:
returncode = int(finished.value or 0)
break
try:
event = json.loads(line)
except json.JSONDecodeError:
state["errors"].append(f"[json decode error] {line}")
continue
except Exception as exc: # pragma: no cover - defensive boundary
state["errors"].append(f"[unexpected parse error] {exc}. Line: {line!r}")
continue
summarize_event(event, all_messages, state, start_time)
except Exception as exc:
emit_json({"success": False, "error": f"Failed to run Codex CLI: {exc}", "warnings": warnings}, exit_code=1)
exit_codes = state["command_exit_codes"]
if (
sandbox in ("read-only", "workspace-write")
and not args.bypass_sandbox
and exit_codes
and 182 in exit_codes
and all(code != 0 for code in exit_codes)
):
warnings.append(
"Every sandboxed command failed and at least one exited with code 182. "
"Codex's sandbox is likely unsupported on this host (common under containers, "
"PRoot, and older WSL), so Codex could not actually run commands; treat "
"`agent_messages` as unverified. Probe with `codex sandbox -- true` and see "
"the skill's Safety model section for options."
)
success = returncode == 0 or bool(state["turn_completed"])
if state["session_id"] is None and not args.ephemeral:
success = False
state["errors"].append("Failed to get `SESSION_ID` from the Codex session.")
if not state["agent_messages"] and state["commands_ran"] == 0:
success = False
state["errors"].append("Failed to get agent output from Codex.")
if returncode != 0 and not state["turn_completed"]:
state["errors"].append(f"Codex CLI exited with non-zero status: {returncode}")
if args.timeout and returncode == 124 and not state["turn_completed"]:
state["errors"].append(f"Codex may have timed out after {args.timeout:g} seconds.")
result: Dict[str, Any] = {"success": success}
if state["session_id"] is not None:
result["SESSION_ID"] = state["session_id"]
result["agent_messages"] = state["agent_messages"]
if state["commands_ran"]:
result["commands_ran"] = state["commands_ran"]
for key in ("commands_failed", "web_searches", "mcp_tools_ran", "files_changed", "files_failed", "plan_updates"):
if state[key]:
result[key] = state[key]
if state["usage"]:
result["usage"] = state["usage"]
if state["activity_counts"]:
result["activity_counts"] = state["activity_counts"]
if warnings:
result["warnings"] = warnings
if not success:
result["error"] = "\n".join(str(err) for err in state["errors"] if err) or "No response from Codex."
if args.return_all_messages:
result["all_messages"] = all_messages
print(json.dumps(result, indent=2, ensure_ascii=False))
raise SystemExit(0 if success else 1)
if __name__ == "__main__":
main()
Related skills
FAQ
What sandbox modes does it support?
read-only (default), workspace-write, and danger-full-access, selected up front since codex exec is non-interactive.
Does it support sessions?
Yes, it captures a SESSION_ID from the first response and lets you resume it or use --last.