| name | codex-tmux |
| type | utility |
| description | Run Codex in a persistent tmux session with signal-based completion so long tasks survive conversation death. Use when work may take 5+ minutes, needs background execution, or must remain retrievable after the calling session ends. |
| license | MIT |
Codex Tmux
Run Codex non-interactively in a detached tmux session. The calling skill
launches it, starts a background waiter, and continues — or dies. Either
way the work completes and the result is retrievable.
This skill is a detached transport wrapper, not an NTM-style swarm
orchestrator. Use it only when detached single-session survivability is the
actual requirement.
First Progress Marker
Start the first progress update with the exact prefix Using codex-tmux.
Preferred format:
Using codex-tmux to run a detached Codex session. First I will launch the tmux worker and capture the result handle.
Shared cross-skill rules live in
references/orchestration-contract.md.
Use that file for detached review and background-result collection semantics.
When to Use
| Use Claude Code subagents when | Use codex-tmux when |
|---|
| Task completes in < 2 minutes | Task may take 5+ minutes |
| Needs Claude Code tools (Edit, Read, Glob, Grep, Task) | Needs only filesystem + git |
| Multiple parallel fast tasks | Single slow quality gate / review |
| Context from conversation is useful | Fresh context is better (no orchestrator bias) |
| Must complete within this session | Must survive session death |
Architecture
Calling skill or workflow
├── Builds prompt string
├── Calls: python3 codex-tmux/scripts/run.py launch --task "..." --cd <repo>
├── Starts background wait helper: python3 codex-tmux/scripts/run.py wait --session <name>
│ └── BLOCKS until Codex signals, then prints result.json
├── Tells user the session name only when detached survivability matters
└── Continues conversation (or session dies — both are fine)
Path A (conversation alive):
Codex finishes → signals channel → background wait helper unblocks
→ runtime task/session handle returns result.json → calling skill reports to user
Path B (conversation died):
Codex finishes → signals channel (no listener, fine)
→ macOS notification → tmux display-message
→ User checks: tmux a -t <session> OR cat <result_file>
No recursive agents. No claude --resume. The signal is a dumb tmux primitive.
Model Policy
- Default to
gpt-5.6-sol. If SOL is unavailable and the task is planning,
architecture, synthesis, review, or final authority, use gpt-5.6-terra at
ultra. For bounded or budget-sensitive non-authority work, gpt-5.4,
gpt-5.4-mini, and codex-mini-latest are also allowed. Older
gpt-5.x-codex variants remain out of scope for this skill.
- Default SOL to
medium. Use SOL max for pivotal/high-consequence planning
or when another model is demonstrably struggling. Use ultra for the Terra
planning/authority fallback, and only when SOL is unavailable.
- When unsure between two reasoning tiers, choose the next higher one.
Usage Protocol
Step 1: Build your prompt
The calling skill builds the full prompt string. codex-tmux does NOT generate prompts — it's a transport layer.
The calling skill also owns most user messaging. Treat codex-tmux as infrastructure unless the user explicitly needs the detached-session handle.
Step 2: Launch
python3 ~/.claude/skills/codex-tmux/scripts/run.py launch \
--task "<your prompt string>" \
--cd "<repo working directory>"
Defaults are gpt-5.6-sol and medium reasoning. Use --reasoning-effort max for pivotal planning or when another model is struggling. If SOL is unavailable for a planning/authority task, pass --model gpt-5.6-terra --reasoning-effort ultra. For routine or budget-sensitive work, pass --model gpt-5.4, --model gpt-5.4-mini, or --model codex-mini-latest.
The launcher runs codex --search exec --dangerously-bypass-approvals-and-sandbox
inside the tmux wrapper so detached research and commit tasks do not hang on
browser/search or approval prompts.
The script outputs JSON to stdout:
{
"session": "codex-20260220-143022",
"signal_channel": "codex-20260220-143022-done",
"result_file": "/tmp/codex-tmux/codex-20260220-143022.json",
"wait_command": "python3 ~/.claude/skills/codex-tmux/scripts/run.py wait --session codex-20260220-143022 --result-dir /tmp/codex-tmux"
}
And a human-friendly summary to stderr:
Codex launched: codex-20260220-143022
Watch live: tmux a -t codex-20260220-143022
Result: /tmp/codex-tmux/codex-20260220-143022.json
Signal: tmux wait-for codex-20260220-143022-done
Kill: tmux kill-session -t codex-20260220-143022
Step 3: Start background waiter
Immediately after launching, start a background wait helper:
python3 ~/.claude/skills/codex-tmux/scripts/run.py wait --session <session-name>
Step 4: Tell user the session name
Only surface the session details when:
- the task is expected to outlive the current conversation
- the user explicitly asked for detached/background execution details
- you are handing off a long-running quality gate the user may want to inspect manually
Otherwise keep the detail minimal and continue with the calling workflow.
Codex running in: codex-20260220-143022
Watch live: tmux a -t codex-20260220-143022
Status: python3 ~/.claude/skills/codex-tmux/scripts/run.py status --session codex-20260220-143022
Step 5: Collect result
If the conversation is still alive, prefer the background wait helper above.
Use the session status / result commands only for polling, timeout recovery, or manual inspection:
- First check after ~60 seconds
- Subsequent checks every ~30 seconds
- If the background task timed out, check the result file directly:
python3 ~/.claude/skills/codex-tmux/scripts/run.py result --session <session-name>
CLI Reference
launch
python3 scripts/run.py launch \
--task "prompt string" \
--cd ~/repos/myapp \
[--prefix codex] # session name prefix (default: codex)
[--result-dir /tmp/codex-tmux] # where to write results (default: /tmp/codex-tmux)
[--model gpt-5.6-sol] # default; Terra is the SOL-unavailable planning/authority fallback
[--reasoning-effort medium] # default; SOL escalation=max, Terra fallback=ultra
[--codex-bin codex] # path to codex binary (default: codex)
status
python3 scripts/run.py status --session <session-name>
Returns JSON with status (running | completed | completed_no_result), has_result, and optional tail (last 30 lines of tmux pane output if still running).
wait
python3 scripts/run.py wait --session <session-name>
Blocks on the session completion signal, then prints the same JSON as result.
Use this for background-task handles instead of spelling raw tmux wait-for ... && cat ... in calling skills.
result
python3 scripts/run.py result --session <session-name>
Returns the result JSON written by the wrapper script. Shape:
{
"session": "codex-20260220-143022",
"exit_code": 0,
"commit_hash": "abc1234..." | null,
"commit_message": "feat: ...",
"completed_at": "2026-02-20T14:35:25Z"
}
Result File
The wrapper script always writes a result JSON, even on failure. It detects new commits by comparing HEAD before and after the Codex run. On error, the tmux session stays alive for tmux a -t <session> inspection.
Verification And Closeout
Before reporting completion, inspect the result JSON with run.py result or
the background waiter output and verify:
- the tmux session name and result file match the launch output
- the Codex process exited or produced an explicit blocker
- any commit hash reported by the wrapper is present in the target repo
- no required background waiter is still running unnoticed
- any caller-requested validation command, such as
pytest, has been run or
explicitly reported as unavailable
If the result file is missing, report completed_no_result as a transport
failure and inspect the live tmux tail before deciding whether to relaunch.
Degraded Mode
If tmux is unavailable, do not pretend this skill ran. Fall back to direct
Codex only when detached survivability is no longer required, and state that
the work will not survive the caller session. If the Codex binary is missing,
surface the prerequisite blocker with the exact launch command that failed.
Completion Signals
Three channels fire on completion (any may be dead, all are best-effort):
- tmux wait-for -S — unblocks the orchestrator's background Bash task
- macOS notification — reaches user even if conversation died
- tmux display-message — visible if user is in another tmux window
Related