tmux-capture-driver
Drive an interactive terminal program (Claude Code, Codex, OpenCode, or any TUI) from an agent through tmux, step by step, and prove each step by an observable artefact. Carries the send-keys rules that were measured to work, the trust-prompt sequences per program, turn-completion detection, bounded condition waits, non-interactive fallbacks, and the failure modes seen in practice. Use when an agent must operate a TUI it cannot attach to, obtain files or output that only an interactive session produces, or hand a partly driven tmux session to another agent.
- License 许可
- MIT
- 最近更新
- 2026-11-11
- Stars
- ★ 2
- Forks
- 0
01 前言 Front Matter
- name
- tmux-capture-driver
- description
- Drive an interactive terminal program (Claude Code, Codex, OpenCode, or any TUI) from an agent through tmux, step by step, and prove each step by an observable artefact. Carries the send-keys rules that were measured to work, the trust-prompt sequences per program, turn-completion detection, bounded condition waits, non-interactive fallbacks, and the failure modes seen in practice. Use when an agent must operate a TUI it cannot attach to, obtain files or output that only an interactive session produces, or hand a partly driven tmux session to another agent.
02 资源文件 1
- 脚本scripts/tmux-step.sh854 B
03 SKILL.md 正文
Drive a TUI through tmux and prove every step
An agent has no terminal of its own. tmux gives it one: a detached session that runs the program, accepts keystrokes, and shows its screen on request. The protocol below turns that into something reliable. Every rule here came from a failure that happened once.
The five rules
- Read the pane before every keystroke.
tmux capture-pane -p -t NAMEshows the screen. Menus, trust prompts and spinners change what a key does. A key sent blind goes to the wrong widget. - Send text and Enter as two calls.
tmux send-keys -t NAME -l 'the text', pause about one second, thentmux send-keys -t NAME Enter. Text and Enter in one call left the prompt typed but unsent in Claude Code.-lsends the text literally, so backticks, semicolons and$reach the program unchanged. - Wait on a condition, never on time alone. The condition is the artefact the step must
produce: a file, or a pane line. Poll it in a bounded loop and report how long it took:
n=0; until [ -e "$FILE" ] || [ $n -ge 90 ]; do sleep 1; n=$((n+1)); done; echo "waited ${n}s". A step whose ceiling expired is a finding, not a retry. - One session per program, environment set in the launching shell. Start a shell in the session, export the variables there, then start the program. A hook or plugin the program spawns inherits that shell's environment, not the agent's.
- Record what you did. After each session: the keys sent, the prompts sent, the menu choices
taken, the pane text at each decision, and the artefacts listed with
ls -la. Another agent or the user must be able to see what produced the artefacts.
Session lifecycle
# Start: detached, sized, in the working directory.
tmux new-session -d -s NAME -x 180 -y 45 -c /path/to/project
# Environment and program, one line, then Enter (a shell command, so one call is fine here).
tmux send-keys -t NAME "export A=1 B=2; program" Enter
# Look.
tmux capture-pane -p -t NAME | grep -v '^\s*$' | tail -30
# Menus: move with Down/Up or type the item number, then Enter as a separate call.
tmux send-keys -t NAME Down; sleep 0.5; tmux send-keys -t NAME Enter
# Prompts: literal text, pause, Enter.
tmux send-keys -t NAME -l 'Run the shell command `ls -la` and tell me what you see.'; sleep 1
tmux send-keys -t NAME Enter
# End: the program's own quit command first, then remove the session.
tmux kill-session -t NAME
Sessions survive the agent's tool call, so a half-driven session can be handed to another agent by name. Give the receiver the session name, the exact pane text, and the next expected artefact.
Program-specific sequences (measured 2026-09-05)
Claude Code (claude, v2.1.261). Nested launches need unset CLAUDECODE CLAUDE_CODE_ENTRYPOINT
in the launching shell. First screen in a new directory is the folder-trust prompt with "No, exit"
selected: Down, then Enter. A turn is running while the status line shows esc to interrupt;
wait for that text to disappear before the next prompt. Slash commands (/compact, /exit) are
typed like prompts: literal text, pause, Enter. /compact produces a summary and a fresh
session start; /exit ends the session and prints a resume line.
Codex (codex, codex-cli 0.153.0). First screen is the directory-trust prompt with "1. Yes,
continue" selected: Enter. If the project carries .codex/hooks.json, the next screen is
"Hooks need review" with three items; hooks run only after "2. Trust all and continue" (type 2,
which moves the highlight, then Enter). Trust is written to ~/.codex/config.toml per project
and persists. Hooks registered for startup run at the start of the first turn, not at process
start, so expect their artefacts after the first prompt. Command approvals appear as menus; read
the pane. Quit with /quit.
OpenCode (opencode, 1.18.29). No trust prompt. On first start in a directory with
.opencode/plugins/, it installs its plugin npm package into .opencode/ before the prompt
appears; in practice about eight seconds. Non-interactive runs were seen to stall at "init"
twice for unknown reasons; a relaunch in the same directory recovered. Child processes the plugin
spawns inherit the TUI's stderr and draw over the screen until the next redraw. One Ctrl-C ends
the TUI and prints a resume line.
Non-interactive fallbacks
These fire the same lifecycle hooks and are easier to script when no TUI-only step is needed:
claude -p 'prompt', codex exec 'prompt' (only after the directory's hooks were trusted once
in the TUI; exec cannot grant trust), opencode run 'prompt'. They cannot do /compact, and
they cannot answer trust prompts.
The screen is never the evidence
A TUI screen is an unreliable observable: a corrupted or starved redraw can show a sent command as still typed (which invites a second Enter and a doubled event), or hide a finished turn. The durable observables are the artefact file and the host's own log. When the screen and a durable observable disagree, trust the durable one: check the capture directory or grep the host log (filtered to your own run or project) before repeating any key.
Failure modes seen
- Text typed but not submitted: text and Enter were in one
send-keyscall. SendEnteralone. - Program refused to start inside an agent session: inherited
CLAUDECODE; unset it. - A long background poll was killed by the harness's low-memory guard. Keep waits short and bounded inside the tool call; do not rely on a detached poller to wake you.
- Two agents typing into one session interleave keystrokes. One driver per session; announce the handoff before the other agent touches it.
- A stalled program was recovered with
pkill -f <program>, which killed EVERY instance of that program on the machine, including two other agents' live sessions mid-sitting (2026-09-05). Never kill by program name or pattern (pkill -f,killall). Read the pane, then kill only your own session (tmux kill-session -t NAME) or your own pane's process (tmux list-panes -t NAME -F '#{pane_pid}', thenkill <pid>). - Several sittings at once share the host's log file and the live store's fault file. Filter every log line by your own project directory before drawing a conclusion. Judge "live store untouched" by a byte digest of its database before and after, never by the fault file, which grows from the surrounding agent session's own hooks.
- A capture listing taken while the host could still emit undercounted (717 listed, 720 on disk; the host had been killed while bootstrap events were still arriving). You cannot tell from outside whether emission has stopped, and stopping the host is not by itself the guarantee. Stop the host, then list, then count a second time; two agreeing reads of a stopped directory are the evidence. Three of four re-counts did not move, which is what a discriminating check looks like.
- Several live hosts at once starved the terminal redraw (hook stderr drawn into the screen) and the redraw never recovered. Run one capture sitting of ours at a time: another sitting is identified by the campaign's project directories and by PASTURE variables in the process environment, never by the program name alone. A host that is not ours (the user's own editor session) is never a reason to stop, wait on or touch it; the load check is the only thing that measures it. Check the load average before starting and wait, bounded, if it is high.
Helper
scripts/tmux-step.sh NAME 'text' /path/expected-file [ceiling-seconds] sends the text and Enter
as two calls, waits on the file with a bounded loop, then prints the wait and the pane tail.
04 同仓库更多 Skills
afk-implement
Run an unattended ("AFK") implementation session as the supervisor — scope small progressive iterations, push feature branches, open pull requests, drive review waves to clean, and never merge. Use when the user says they are going AFK or to bed, asks for overnight autonomous implementation, says "don't block on me", "open PRs but don't merge", "progressive adoption", or "stack changes".
protocol
Aura protocol reference documentation — 12-phase workflow, agent roles, constraints, and coding standards. Read when you need to understand the full workflow or look up conventions.
protocol
暂无简介
example
暂无简介
example
暂无简介
example
暂无简介