Terminal Testing Skill
Drive interactive terminal programs over a PTY using the Terminal DevTools CLI. Spawn a shell / REPL / TUI attached to a pseudo-terminal, type into it, synchronize on its output, and read what it produced — the way a human (or tmux) would.
When to Use
This skill activates when:
- User wants to test or automate a CLI, REPL, or full-screen TUI
- User needs a program to run on a real TTY (colors, readline, password prompts, ncurses UIs)
- User wants to type commands/keystrokes into an interactive program and assert on its output
- User wants a one-shot command's full output + exit code
- User mentions a PTY, pseudo-terminal, shell session,
expect-style automation, or drivingvim/psql/python
The platform spawns the process itself — it does not attach to a pre-existing external process. For web pages use browser-testing; for server APIs use backend-testing; for Node.js process debugging use the node CLI.
Synchronization model (wait, don't sleep)
There is no assert tool and no fixed-delay step. After sending input, block on expected output with sync wait-for (regex) or sync wait-for-idle (output went quiet) before sending more input or reading. Chain incremental reads by passing the returned cursor as --since to the next content capture / sync wait-for, so you only ever see new output. Guessing with sleeps is unreliable; matching on output is not.
Capabilities
Pane lifecycle
ironbee-terminal-devtools-cli --json pty start # default shell
ironbee-terminal-devtools-cli --json pty start --command python --cols 100 --rows 30
ironbee-terminal-devtools-cli --json pty list
ironbee-terminal-devtools-cli pty resize --pane-id <id> --cols 120 --rows 40
ironbee-terminal-devtools-cli pty stop --pane-id <id>
Send input (tmux key syntax)
ironbee-terminal-devtools-cli interaction send-keys --pane-id <id> --keys '["ls -la","Enter"]'
ironbee-terminal-devtools-cli interaction send-keys --pane-id <id> --keys '["C-c"]'
ironbee-terminal-devtools-cli interaction send-text --pane-id <id> --text "SELECT 1;"
Synchronize on output
ironbee-terminal-devtools-cli --json sync wait-for --pane-id <id> --pattern '\$ $'
ironbee-terminal-devtools-cli --json sync wait-for --pane-id <id> --pattern 'Server listening' --since 1024
ironbee-terminal-devtools-cli --json sync wait-for-idle --pane-id <id>
Read output
ironbee-terminal-devtools-cli --json content capture --pane-id <id> --since 4096 --strip-ansi
ironbee-terminal-devtools-cli --json content capture --pane-id <id> --mode screen # rendered grid for TUIs
ironbee-terminal-devtools-cli --json content get-cursor --pane-id <id>
Signals and one-shots
ironbee-terminal-devtools-cli pty signal --pane-id <id> --signal SIGINT
ironbee-terminal-devtools-cli --json pty run --command "npm test"
Basic Testing Workflow
- Spawn:
pty start(a shell, or--commanda specific program); keep thepaneId. - Wait for ready:
sync wait-foron the prompt / banner before sending input. - Send:
interaction send-keys(type a command +Enter) orinteraction send-text. - Wait:
sync wait-foron the next prompt (orsync wait-for-idlewhen there is no clean marker). - Read:
content capturewith--since <cursor>for only the new output (--mode screenfor TUIs). - Verify: judge the captured output yourself; repeat from step 3.
- Stop:
pty stopthe pane.
End-to-End: drive a psql session
SESSION="--session-id psql"
PANE=$(ironbee-terminal-devtools-cli $SESSION --json pty start --command psql --args '["-U","app","appdb"]' | jq -r '.paneId')
# Wait for the psql prompt, run a query, wait for the next prompt, read the result
ironbee-terminal-devtools-cli $SESSION --json sync wait-for --pane-id "$PANE" --pattern 'appdb=#'
ironbee-terminal-devtools-cli $SESSION interaction send-keys --pane-id "$PANE" --keys '["SELECT count(*) FROM users;","Enter"]'
CUR=$(ironbee-terminal-devtools-cli $SESSION --json sync wait-for --pane-id "$PANE" --pattern 'appdb=#' | jq -r '.cursor')
ironbee-terminal-devtools-cli $SESSION --json content capture --pane-id "$PANE" --strip-ansi
ironbee-terminal-devtools-cli $SESSION interaction send-keys --pane-id "$PANE" --keys '["\\q","Enter"]'
ironbee-terminal-devtools-cli $SESSION pty stop --pane-id "$PANE"
End-to-End: assert a TUI renders correctly
SESSION="--session-id tui"
PANE=$(ironbee-terminal-devtools-cli $SESSION --json pty start --command htop --cols 120 --rows 40 | jq -r '.paneId')
# Let it paint, then read the rendered grid (not the raw stream)
ironbee-terminal-devtools-cli $SESSION --json sync wait-for-idle --pane-id "$PANE"
ironbee-terminal-devtools-cli $SESSION --json content capture --pane-id "$PANE" --mode screen
# Quit
ironbee-terminal-devtools-cli $SESSION interaction send-keys --pane-id "$PANE" --keys '["q"]'
ironbee-terminal-devtools-cli $SESSION pty stop --pane-id "$PANE"
Best Practices
- Use sessions (
--session-id) for multi-step flows — panes are pinned to the session. - Always
sync wait-forbefore sending more input — never guess with delays. - Chain
--since <cursor>across captures/waits so you only read new output. screenmode for TUIs,streammode for shells/REPLs — pick the capture mode to match the program.C-ckeystroke vspty signal—send-keys ["C-c"]may be trapped by the program;pty signal --signal SIGINTdelivers a real process signal.pty runfor non-interactive commands — one call returns full output + exit code; no pane lifecycle to manage.- Stop panes you no longer need — long-idle panes may be reaped by the daemon.
- Batch tight flows with
run execute --code "..."andcallTool()to cut round-trips (onlycallToolis available — nopage).