| name | core |
| description | agent-tui usage — spawn, snapshot, wait, interact with PTY apps via @eN refs |
| allowed-tools | Bash(agent-tui:*) |
agent-tui core
agent-tui drives terminal / TUI apps (vim, shell, htop, claude-code,
opencode, …) from an AI agent. A per-session daemon owns the PTY and
serves an outline-based snapshot with @eN element refs so agents can
read screens in a few hundred tokens instead of parsing terminal bytes.
This page is the canonical entry point. Specialized skills cover
specific PTY apps:
agent-tui skills list
agent-tui skills get core --full
Pick a verb (start here)
| You want to… | Use |
|---|
| Run a headless CLI, get its stdout + exit code | run -- <cmd> (feed input with --stdin) |
| Observe / drive an interactive TUI (vim, htop, less) | spawn → wait → snapshot → press |
| Edit a file in one shot and capture the result | edit <path> |
| Stream a long / following command's output | watch -- <cmd> (or tail --follow) |
| Ask an AI CLI a question | ask <provider> "<prompt>" |
Rule of thumb: headless data → run; an interactive screen → the
spawn/snapshot/press loop. Everything below expands on these.
Timeouts
Each sugar/wait verb carries its own deadline, set with --max <ms>.
The client read timeout is derived from --max, so a long deadline
no longer trips a fixed client-side cap. The defaults differ by verb —
pass --max explicitly for long work:
| Verb | default --max |
|---|
run | 60000 ms (60 s) |
ask | 120000 ms (120 s) |
wait | 25000 ms (25 s) |
A timed-out wait exits non-zero with the screen state at exit and a
hint to raise --max — intentional, not a bug: raise --max when what
you're waiting for legitimately takes longer than the default.
Two modes
There are two distinct ways to drive a child:
Mode A — subprocess as data (agent-tui run). The child has a
non-interactive mode (claude -p, gh api, gpg, jq, …). You
want stdin in, stdout/exit-code out. One verb does it all.
Mode B — interactive driving (spawn + snapshot + press +
wait + …). The child has no headless mode (vim, htop, lazygit) or
you specifically want to observe its TUI. The capability primitives
let you drive it step by step.
Mode A: just run a CLI
agent-tui run --stdin "what is 40+2" -- claude -p
agent-tui --json run --stdin "what is 40+2" -- claude -p
agent-tui run -- gh api /user
run bundles spawn --stdin pipe + write-stdin + close-stdin +
wait --exit + tail --strip-ansi + cleanup. It shuts the
per-session daemon down on exit (use --keep-daemon to keep it for
follow-up calls).
Mode B: the interactive core loop
agent-tui spawn -- vim /fixtures/sample.txt
agent-tui wait --text "sample.txt"
agent-tui snapshot --mode outline
agent-tui press ":q!<cr>"
agent-tui die
Per-app adapters emit durable refs keyed by what they identify
(@vim.buffer, @shell.prompt, @tmux.pane[%2]). These survive
across snapshots — same ref, same logical node. You do NOT need to
re-snapshot before each ref-based interaction.
The generic adapter (when no per-app adapter matches) still emits
positional @eN refs that are frame-local — that's the only place
the old "re-snapshot first" rule applies. Check the node's durable
field if you need to know which kind you got.
See agent-tui skills get addressing for the selector grammar and
the press --to '<selector>' routing model.
Spawning a process
agent-tui spawn -- htop --no-color
agent-tui spawn --stdin pipe -- claude -p
agent-tui spawn --stdin closed -- env
--stdin pipe gives the child a pipe instead of a TTY for stdin —
required by CLIs that do isatty(0) and refuse a TTY stdin
(claude -p, gh api, gpg). The daemon keeps the write end so
later stdin / close-stdin calls can feed it.
--stdin closed ties stdin to /dev/null. Programs that try to
read see EOF immediately. Right for --print-style CLIs that take
the prompt as an argument.
Spawn returns a JSON envelope with the pane id (e.g. p1). The
session has one pane by default; multi-pane workflows pass --pane
explicitly.
Writing to a child's stdin pipe
For panes spawned with --stdin pipe:
agent-tui stdin --text "hello world"
agent-tui stdin --bytes-hex "68690a"
agent-tui close-stdin
For PTY-stdin panes, use type / press / send-ansi instead —
those write through the master PTY (which IS stdin for PTY mode).
Reading raw output bytes
agent-tui tail
agent-tui tail --strip-ansi
agent-tui tail --since 1024
agent-tui tail --follow --strip-ansi
agent-tui --json tail --follow
tail returns next_since so a follow-up call (tail --since <next_since>) returns only NEW bytes. For live observation of a
slow child, use --follow: the daemon streams chunks as bytes
arrive and terminates with a {type:"eof", exit_code:N} envelope
when the child exits.
Editing a file and saving
agent-tui spawn -- vim /work/notes.md
agent-tui wait --text "notes.md"
agent-tui press "i hello<esc>"
agent-tui wait --text "\[\+\]"
agent-tui press ":w<cr>"
agent-tui wait --text "written"
agent-tui snapshot
agent-tui press ":q<cr>"
press accepts vim-style key notation: <cr> newline, <esc>,
<f5>, <c-c>, etc. Plain text inside is typed literally.
Typing literal text
agent-tui spawn -- bash -c "echo -e 'apple\nbanana\ncherry' | fzf"
agent-tui wait --text "3/3"
agent-tui type "ban"
agent-tui wait --text "1/3"
agent-tui snapshot
agent-tui press "<c-c>"
type is for literal text (no key-notation parsing). press is for
key combinations. send-ansi is for hex-encoded byte sequences when
you need to emit bytes the PTY isn't parsing through readline.
Sending raw ANSI / escape sequences
agent-tui spawn -- less /work/log.txt
agent-tui wait --text "log.txt"
agent-tui send-ansi 2f6e6565646c650d
agent-tui wait --text "needle"
agent-tui snapshot
agent-tui press "q"
Use send-ansi for application-level byte sequences (slash-search in
less, OSC sequences, mode toggles). Pass bytes as hex; for ordinary
typed text plus key tokens, prefer press.
Waiting for screen state
agent-tui spawn -- bash -c "echo -e 'a\nb\nc' | fzf"
agent-tui wait --text "3/3"
agent-tui wait --idle 150
agent-tui snapshot
wait has five shapes. Pick the most specific one:
| Form | Use it when |
|---|
wait --ref '<selector>' [--gone] | A structured node is the ground truth (preferred — see addressing) |
wait --text "regex" | No adapter outline available; matching rendered cells |
wait --hash <hash> | Loop continuation — "wait until the screen differs from this hash" |
wait --sequence <n> | Event-stream sync — "wait until event >= n" |
wait --idle <ms> | Last-resort settling time (read references/wait-and-events.md first) |
See references/wait-and-events.md for
the sequence-number model and why --idle is a code smell when used
alone.
Reading the screen
agent-tui snapshot
agent-tui snapshot --mode outline
agent-tui snapshot --mode text
agent-tui snapshot --mode cells
agent-tui snapshot --mode adapter
agent-tui snapshot --mode hybrid
outline is the default and the right answer for most TUI apps. Use
text when the pane is just unstructured output and you want "what
does the screen say" as a plain string — much easier than parsing
the RLE/base64 cells.
Reach for cells when you need exact cell positions / wide chars /
SGR colors (engine-correctness tests). adapter when the per-program
adapter has extra structure (vim's mode + filename, shell's
prompt-vs-running).
Snapshot output includes @eN refs you can use in subsequent commands
(planned in P2; v0.1 returns refs as part of the outline tree). See
references/snapshot-refs.md for the
deep dive on ref lifetime and the four modes.
Resizing the pane
agent-tui resize 120 40
agent-tui snapshot
PTY-aware apps repaint on receipt of SIGWINCH. Some redraw
immediately; others wait for input. Pair with wait --idle if you
need the new layout before snapshotting.
Sending signals
agent-tui spawn -- htop --no-color
agent-tui wait --text "F10"
agent-tui press "q"
agent-tui signal TERM
agent-tui signal KILL
die is the cleanup-and-exit shortcut that any test should call at
the end of a scenario.
Focusing across multiple panes
agent-tui pane focus p1
agent-tui pane list
Focus has three states: Auto (most-recently-spawned wins), Focused
(this pane explicitly), Held (focused-pane death promotes to Held;
explicit refocus required).
Recording a session
The daemon writes an asciicast-v3 trace to
$XDG_STATE_HOME/agent-tui/<session>/<pane>.cast for every session.
Replay with asciinema play <file>.
ls $XDG_STATE_HOME/agent-tui/default/
asciinema play $XDG_STATE_HOME/agent-tui/default/p1.cast
See references/recording.md for rotation,
retention, and the v3-extension event types (markers, checkpoints).
Diagnosing install issues
agent-tui doctor
agent-tui doctor --json
doctor checks: bwrap availability (sandboxed scenarios),
PTY/terminfo basics, recorder write path, socket-dir permissions,
agent-tui binary version vs. embedded skill version. Run before
filing a bug.
Cleaning up
agent-tui die
agent-tui daemon shutdown
The daemon also exits on PR_SET_PDEATHSIG when the CLI process tree
dies, on idle timeout (default 5min), or when the monitored parent
PID exits. Integration tests rely on the parent-PID monitor for
zombie-free teardown.
When to load another skill
- Selectors, refs, and routing (DOM-lite addressing model — read
this whenever you're using
--ref, --select, or --to):
agent-tui skills get addressing
- Shell sessions (bash/zsh with OSC 133):
agent-tui skills get shell
- vim / nvim editing:
agent-tui skills get vim
- Driving an AI CLI (opencode, pi, codex from outside):
agent-tui skills get ai-cli
Full reference
agent-tui skills get core --full
Pulls in:
references/commands.md — every subcommand, flag, alias (autogenerated from --help)
references/snapshot-refs.md — snapshot modes + @eN lifetime
references/wait-and-events.md — wait --text/--hash/--idle/--sequence semantics