Overview
This skill provides a "general-purpose tmux interaction orchestration toolchain" for reliably controlling any interactive CLI (Codex/Claude Code/REPL/TUI, etc.):
- Reliably create/reuse tmux sessions (avoid shell initialization prompts stealing input)
- Send text and key sequences (supports
Tab/Enter/Escape+Enter, etc.) - Poll and capture pane output, waiting for "ready / progress / done" signatures to appear (avoid relying on
sleep Nguessing at timing) - Supports backchannel return: after the worker finishes, it sends a summary back to the controller's pane (supports two modes: "inject draft only" and "auto-submit as a conversation message")
Note: this skill does not by defaultappend
--yolo/--full-autoand other parameters for Codex; whether to enable them is determined by the--cmdpassed in by the caller.
⚠️ Risk warning (must read)
The core capability of this skill is tmux send-keys: it can "type" text/keystrokes into any tmux pane. This capability is very powerful, which also meansonce the target is chosen incorrectly, commands may be executed in the wrong window。
Please understand and accept the following risks and boundaries before use:
- Misdelivery risk (high): if the controller/target is identified incorrectly, the return or keystrokes may land in another pane (or even a production shell).
- "Keystroke = execution" risk (high):
--report-submit keys(especially withEnter) may directly trigger shell execution; only recommended when you clearly know what the controller is and explicitly specify--scheduler-target. - Explicit override is more dangerous:
--report-submitwill override--report-mode's safety policy; treat it as an "advanced/dangerous switch". - Privacy risk of reading screen contents (medium): the script will
capture-paneto detect the Codex pane/wait for output; avoid displaying secrets (tokens, private keys, sensitive logs) in the controller pane. - worker
--cmdis an arbitrary command (high):dispatch.shwill execute the--cmdyou pass in the worker session (the default iscodex --no-alt-screen). If you pass in a high-privilege/destructive command, you bear the risk. - Risk control is only heuristic (medium):
risk-gateonly performs simple pattern matching; it is not a security sandbox; it can only "remind/block the first line of defense", and cannot guarantee absolute safety.
Recommended safe usage (strongly advised)
- Use a dedicated scheduler pane: open a separate tmux session/window dedicated to running the scheduling-side Codex; do not mix it with your everyday shell pane.
- Prefer the default return strategy:
--report-mode auto(Codex controller sends automatically; non-Codex automatically degrades to inject-draft-only). - When more stability is needed, explicitly bind the controller pane: dispatch within the scheduler pane; or set
CODEX_TMUX_ECHO_CONTROLLER_TARGET; or pass--scheduler-target %<pane_id>。 - Unless you clearly know what you are doing, do not use:
--report-submit keys --report-keys Enter。
Socket Strategy
--socket auto(default):
- If you are currently inside tmux (environment variable
TMUXexists): reuse the current tmux server (so the worker can send back to your pane) - If not in tmux: use an isolated socket (the default path is under
${TMPDIR:-/tmp}), to avoid polluting the system tmux
Scripts
scripts/tmuxctl.sh: tmux controller (new/send/wait/capture/report/whoami)scripts/interactive_runner.sh: general-purpose runner (start worker → send prompt → auto-submit → wait for done)scripts/start_scheduler.sh: one-click start of the "scheduler-side Codex" (system tmux)scripts/dispatch.sh: one-click dispatch of a natural-language task to a worker, and report back to the scheduler side (conversational collaboration; supports automatic identification of the scheduler/controller pane)scripts/codex-tmux-echo: shorter wrapper (treat arguments directly as a natural-language task)scripts/selftest.sh: offline self-test (does not depend on network/account)
Dependencies
tmuxbashpython3(waitsubcommands are matched using Python regex, to avoid grep differences)
Command Reference
1) Offline self-test (recommended to run first)
bash scripts/selftest.sh
2) Get the current controller pane (for the scenario where you are inside tmux)
bash scripts/tmuxctl.sh whoami
3) Start a clean shell and send a command
SESSION=demo
bash scripts/tmuxctl.sh --session "$SESSION" new --dir "$(pwd)" --cmd "bash --noprofile --norc"
bash scripts/tmuxctl.sh --target "$SESSION" send --text 'echo READY' --keys 'Enter'
bash scripts/tmuxctl.sh --target "$SESSION" wait --pattern 'READY' --timeout 10
4) General-purpose runner: start any interactive CLI in tmux and send a prompt
bash scripts/interactive_runner.sh \
--session tmtest \
--workdir "$PWD" \
--cmd 'codex --dangerously-bypass-approvals-and-sandbox --no-alt-screen' \
--prompt 'hello,你好呀。请在完成后回传 DONE。' \
--wait-pattern 'DONE' \
--timeout 120
4.1) Recommended workflow: natural-language dispatch (the scheduler side is fixed in system tmux)
- Start the scheduler-side Codex:
bash scripts/start_scheduler.sh --socket system --session scheduler --workdir "$PWD"
tmux attach -t scheduler
- In the scheduler-side pane (or anywhere that can locate the scheduler-side pane), dispatch a natural-language task to a worker:
bash scripts/codex-tmux-echo 'hello,你好呀'
4.2) Make the skill "globally available" (optional)
If your Skill Runner scans a certain skills directory, you can execute this in the repository root (copy install, do not use symlinks):
SKILLS_DIR=<skills-scan-dir>
mkdir -p "$SKILLS_DIR"
cp -R "$(pwd)/agent/skills/codex-tmux-echo" "$SKILLS_DIR/codex-tmux-echo"
If you also want to turn the dispatch command into a global CLI (codex-tmux-echo), you can copy the wrapper into PATH (for example ~/.local/bin):
mkdir -p ~/.local/bin
cp -f "$SKILLS_DIR/codex-tmux-echo/scripts/codex-tmux-echo" ~/.local/bin/codex-tmux-echo
dispatch.sh by default automatically identifies the scheduler-side controller pane (preferring TMUX_PANE / CODEX_TMUX_ECHO_CONTROLLER_TARGET), therefore usually does not need to specify the session name。
4.1.2) Report-back mode selection (Draft vs Send)
There are two common semantics for report-back:
- Draft (only injected into the input box/Pane, not automatically sent): more suitable for tasks such as "reports/notifications/requiring manual confirmation", and it will not interrupt a draft currently being edited on the scheduler side.
- Send (submit as a conversation message): more suitable for scenarios where the result needs to be incorporated into the scheduler-side context, or where two Codex instances need to form conversational collaboration (after the worker reports back, the scheduler side will immediately start processing).
The default behavior is --report-mode auto:
- If the controller is identified as Codex pane: default Send(otherwise automatic conversational collaboration is impossible).
- If the controller is not Codex: default Draft(to avoid carrying the assumption that "keypress = execute/send" into general-purpose CLIs and causing accidental execution).
You can specify explicitly:
# 强制仅注入草稿(不自动发送)
bash scripts/codex-tmux-echo --report-mode draft '...'
# 强制提交为对话消息(仅当 controller 是 Codex)
bash scripts/codex-tmux-echo --report-mode send '...'
The report-back text will include source information (to make it easier for the scheduler side to locate the source in follow-up dialogue):
ECHO-REPORT: from=<worker_session> | result=<ok|fail|blocked> | details=<one-line>
4.1.1) Semantic rewriting and risk control (enabled by default)
To avoid passing "scheduler-layer meta-instructions" unchanged to the worker (causing the worker to "start another codex"),dispatch.sh by default will:
--normalize auto: try to rewrite "natural language/meta-instructions" into concrete steps the worker can directly execute, and add[我的理解]paragraphs to the prompt.--risk auto: upon encountering high-risk operations (such assudo, writing to system directories,rm -rf /, etc.) it willfirst report back to the scheduler and block execution。- If the scheduler confirms it is allowed, you can dispatch again and add:
--risk allow
- If the scheduler confirms it is allowed, you can dispatch again and add:
5) Worker-side backchannel (send a message to the scheduling-side Codex and process it immediately)
If the runner injects the following environment variables:
CODEX_TMUX_ECHO_CONTROLLER_TARGETCODEX_TMUX_ECHO_HOME
Then the worker can send back a message at the end of the task (recommended --submit codex, which will attempt to submit and detect that the scheduling side has started processing):
"$CODEX_TMUX_ECHO_HOME/scripts/tmuxctl.sh" report \
--to "$CODEX_TMUX_ECHO_CONTROLLER_TARGET" \
--text 'DONE: ok' \
--submit codex
If you only want to "write + press the key to submit" (without waiting for detection), you can use --submit keys + --keys "Tab":
"$CODEX_TMUX_ECHO_HOME/scripts/tmuxctl.sh" report \
--to "$CODEX_TMUX_ECHO_CONTROLLER_TARGET" \
--text 'DONE: ok' \
--submit keys \
--keys "Tab"