| name | tmux-workflow |
| description | Portable tmux-based workflow to drive one or multiple long-running Codex CLI “workers” from another process (e.g. Claude CLI) by (1) starting/reusing a tmux session running `codex`, (2) injecting prompts into the Codex pane via `tmux send-keys`/buffer paste, and (3) polling the worker’s Codex JSONL session logs to extract the next assistant reply. Each worker runs with an isolated `CODEX_HOME` (default `~/.codex-workers/<worker_id>`) to prevent log cross-talk when multiple Codex workers run concurrently. Use when you need to submit/wait/read Codex replies in tmux, manage multiple Codex workers, or troubleshoot the tmux worker and log binding. |
tmux-workflow
This skill is a self-contained toolkit under scripts/ (copy this whole folder to any repo’s .codex/skills/ or to ~/.codex/skills/).
Quick start (Claude → Codex)
- (Optional) Check deps:
bash .codex/skills/tmux-workflow/scripts/check_deps.sh
- Start a worker and ask with short names (state directory is configurable; default is
<skill_root>/.twf/):bash .codex/skills/tmux-workflow/scripts/twf codex-abash .codex/skills/tmux-workflow/scripts/twf codex-a "你好"
Scripts
scripts/codex_up_tmux.sh: start/reuse the worker and write./.codex-tmux-session.json(JSON) in the current directory; sync~/.codexinto per-workerCODEX_HOME.scripts/codex_ask.py: inject text into the tmux pane and poll Codexsessions/*.jsonluntil the next assistant reply appears.scripts/codex_pend.py: print the latest reply (or last N Q/A pairs) from the bound or auto-detected Codex log.scripts/codex_ping.py: health check for tmux worker and log binding.scripts/sync_codex_home.py: sync~/.codexinto a per-workerCODEX_HOME(excludessessions/,log/,history.jsonl).scripts/twf: recommended wrapper for managing many workers:up/ask/pend/ping/stop/resume/spawn/tree/list/removewith automatic naming and “pick latest” behavior.scripts/install_claude_cmds.sh(optional): install Claude custom commands (/cask,/cpend,/cping) that call this skill’s scripts.
Recommended usage (twf)
Naming rules (base vs full name)
- Base name: short label like
codex-a. - Full name: auto-generated unique worker id:
<base>-YYYYmmdd-HHMMSS-<pid>(also used as tmux session name). twf <base>creates a new full-name worker and writes state to<state_dir>/<full>.json.twf <base> "msg"will use the latest full-name worker for that base (by state-file mtime). If none exists, it auto-creates one.removealways requires full name to avoid deleting the wrong worker.
State directory:
- default: configured via
scripts/twf_config.yaml(twf_state_dir_mode, defaultauto)auto:<skill_root>/.twf/(project install default:./.codex/skills/tmux-workflow/.twf/)global:~/.twf/manual:twf_state_dir(must be set in config; relative paths resolve from current directory)
- override:
TWF_STATE_DIR=/some/path(highest priority; ignorestwf_state_dir_mode)
Core commands
- Start a worker (returns the state json path on stdout):
bash .codex/skills/tmux-workflow/scripts/twf codex-abash .codex/skills/tmux-workflow/scripts/twf up codex-a
- Ask a worker and print Codex reply to stdout:
bash .codex/skills/tmux-workflow/scripts/twf codex-a "your prompt"bash .codex/skills/tmux-workflow/scripts/twf ask codex-a "your prompt"
- Inspect:
bash .codex/skills/tmux-workflow/scripts/twf pend codex-a [N]bash .codex/skills/tmux-workflow/scripts/twf ping codex-a
Stop / Resume (resume keeps logs)
twf stop <name|full-name>: stop tmux session, keep state +CODEX_HOME(so it can be resumed later).twf resume <name|full-name>: resume a stopped worker.- default: resume the worker and its subtree (children).
--no-tree: resume only this node.
Resume source id:
twf stopstorescodex_resume_from_idby reading the latestsession_meta.payload.idfrom that worker’s newest*.jsonllog.twf resumeusescodex resume <id>whencodex_resume_from_idexists; otherwise falls back to a freshcodexsession.
Parent/Child workers (“sub-codex”)
Create a child worker and link it to a parent:
twf spawn <parent-full> <child-base> [up-args...]
Notes:
- parent must be full name (unique).
- child is created as a new full-name worker and recorded into:
- child:
parent=<parent-full> - parent:
children[] += <child-full>
- child:
Show structure:
twf tree [root-full]twf list [--running|--stopped|--orphans]
Remove (destructive)
twf remove <full-name>: default recursive delete of the node + its subtree:- kills tmux sessions
- deletes worker
CODEX_HOME - deletes state json files
twf remove <full-name> --no-recursive: delete only the single node
Safety:
removeonly deletescodex_homeif it is underTWF_WORKERS_DIR(or default~/.codex-workers); otherwise it refuses and asks you to delete manually.
Environment knobs
Low-level (codex_up_tmux.sh and friends)
TWF_SESSION_FILE: session file path (default:./.codex-tmux-session.json).TWF_TMUX_SESSION: override tmux session name (default:codex-<hash(cwd)>).TWF_CODEX_CMD_CONFIG: YAML config path (default:scripts/twf_config.yamlin this skill). Keys:model(defaultgpt-5.2)model_reasoning_effort(defaultxhigh)twf_state_dir_mode(defaultauto;auto|global|manual)twf_state_dir(required whentwf_state_dir_mode=manual)twf_use_load_balancer(defaultfalse; whentrue, askscodex-load-balancerto choose aCODEX_HOMEsource per worker)twf_load_balancer_cmd(optional path override to the load balancer command; otherwise auto-detected)
TWF_CODEX_CMD: override the full command used inside tmux (if unset, it is built fromTWF_CODEX_CMD_CONFIG).TWF_WORKERS_DIR: per-workerCODEX_HOMEbase dir (default:~/.codex-workers).TWF_CODEX_HOME_SRC: sourceCODEX_HOMEto copy from (default:~/.codex).TWF_AUTH_SRC: optional auth file to copy into worker asauth.json(overrides syncedauth.json).TWF_LOAD_BALANCER_CMD: optional path tocodex-load-balancer/scripts/clb(takes precedence overtwf_load_balancer_cmd).TWF_CODEX_SESSION_ROOT/CODEX_SESSION_ROOT/CODEX_HOME: where to scan logs (default:~/.codex/sessions).TWF_WATCH_MODE(defaultauto):auto: on Linux/WSL useinotifyto block until the log file changes; otherwise fall back to polling.poll: always polling withTWF_POLL_INTERVAL.inotify: forceinotify(falls back to polling if unavailable).
TWF_POLL_INTERVAL(seconds, default0.05),TWF_TIMEOUT(seconds, default3600).TWF_STATE_DIR: override twf wrapper state dir (highest priority).TWF_SUBMIT_DELAY(seconds, default0.5): delay between injecting text and pressing Enter in tmux (workaround for Codex TUI paste-burst behavior).TWF_SUBMIT_NUDGE_AFTER(seconds, default0.7) andTWF_SUBMIT_NUDGE_MAX(default2): if the prompt appears to be typed but not submitted, send extra Enter keypresses while waiting for logs to acknowledge the user message.