Terminal Use
The orchestrator routed you here for any terminal target. Select the backend, then execute your plan through tctl.
Select the backend
| Need | Backend | Mechanics |
|---|---|---|
Routine TUI automation, regression checks, deterministic wait / wait-idle, text snapshots of what the user would see, demo recordings | tuistory (default) | This file |
| Real terminal rendering, or proof of what Ghostty or Kitty actually emits for a keystroke | true-input | Load true-input; it owns the compositor, VM, and platform mechanics |
Use tuistory unless the claim is about the real terminal itself. The rest of this file is the tuistory backend: a target command in a virtual PTY with Playwright-style CLI for typing, pressing keys, waiting, snapshotting, and recording.
Prerequisites
npm install -g tuistory # or: bun add -g tuistory
Optional: tmux (scrollback flows), asciinema (recordings), agg (.cast to .gif).
Core pattern
TCTL=${DROID_PLUGIN_ROOT}/bin/tctl
$TCTL launch "droid-dev" -s demo --backend tuistory \
--repo-root /path/to/worktree \
--cols 120 --rows 36 \
--env FORCE_COLOR=3 --env COLORTERM=truecolor
$TCTL -s demo wait ">" --timeout 15000
$TCTL -s demo type "hello"
$TCTL -s demo press enter
$TCTL -s demo snapshot --trim
$TCTL -s demo close
Note: --repo-root is mandatory for droid-dev launches — tctl enforces it.
Always pass --env FORCE_COLOR=3 --env COLORTERM=truecolor when launching. The virtual PTY doesn't advertise color support, so Node.js apps (Ink/chalk) suppress all color escape codes without these.
Command reference (via tctl)
| Command | Purpose |
|---|---|
launch <cmd> -s <name> --backend tuistory | Start a tuistory session |
type <text> | Send literal text |
press <key> [keys...] | Send key chord (e.g., press shift enter) |
wait <pattern> | Block until text or /regex/ appears |
wait-idle | Block until output stabilizes |
snapshot [--trim] | Print cleaned text (--trim strips trailing blanks) |
close | Tear down session |
Launch options: --cols <n>, --rows <n>, --cwd <path> (child working directory; defaults to --repo-root when set), --env KEY=VALUE, --record <path>.
Recording
Pass --record at launch. tctl wraps asciinema rec around the PTY, so recording must be set at launch time (raw tuistory cannot record):
$TCTL launch "droid-dev" -s demo --backend tuistory \
--repo-root /path/to/worktree \
--cols 120 --rows 36 --record /tmp/demo.cast \
--env FORCE_COLOR=3 --env COLORTERM=truecolor
# ... interact ...
$TCTL -s demo close # finalizes the .cast
Before/after comparison -- launch two sessions against different worktrees:
$TCTL launch "droid-dev" -s before --backend tuistory \
--repo-root /path/to/baseline-worktree \
--cols 120 --rows 36 --record /tmp/before.cast \
--env FORCE_COLOR=3 --env COLORTERM=truecolor
$TCTL launch "droid-dev" -s after --backend tuistory \
--repo-root /path/to/candidate-worktree \
--cols 120 --rows 36 --record /tmp/after.cast \
--env FORCE_COLOR=3 --env COLORTERM=truecolor
Playback: asciinema play /tmp/demo.cast
tmux (scrollback flows only)
Only needed when the demo requires terminal-emulator scrollback and the app uses the standard buffer (not alternate screen). True-input sessions rarely need tmux because the real terminal owns native scrollback.
Use --tmux at launch. tctl wraps the command in tmux, starts tmux with TERM=xterm-256color, and preconfigures default-terminal=tmux-256color, terminal-features=...,xterm-256color:RGB, terminal-overrides=...,xterm-256color:Tc (fallback for tmux < 3.2), COLORTERM=truecolor (via set-environment), escape-time=50, and mode-keys=vi.
Copy-mode: ctrl-b [ to enter, g g top, shift-g bottom, ctrl-u/ctrl-d half-page, / search, q to exit (not esc).
Launch with tmux:
$TCTL launch "droid-dev" -s demo --backend tuistory --tmux \
--repo-root /path/to/worktree \
--cols 120 --rows 36 --record /tmp/demo.cast \
--env FORCE_COLOR=3 --env COLORTERM=truecolor
When recording includes tmux redraws, asciinema must wrap tmux, not the reverse.
Known dead ends
- Raw
asciinema rec: Do not callasciinema recdirectly.tctl --recordwrapsasciinema recaround the PTY so that tuistory-relay still owns the session and interactive TUIs (Ink/React) receive stdin correctly. Callingasciinema recmanually bypasses this wiring — stdin forwarding breaks, typed keys echo on the outer PTY instead of reaching the child, and tuistory commands (wait,snapshot,close) cannot find the session. - Raw
tuistory launchwith tctl flags:tuistoryhas no--record,--backend,--repo-root, or--envflags. Passing them crashestuistory-relay. Usetctlfor all launches.
Recovery
$TCTL -s demo press esc # bail out of a stuck dialog
$TCTL -s demo snapshot --trim # check visible state
$TCTL -s demo close # hard reset
Escape hatch: raw tuistory (last resort)
If tctl itself is broken or unavailable, you can fall back to raw tuistory for non-recording sessions only. Raw tuistory accepts only --cols, --rows, and -s — no other flags. Do not pass --record, --backend, --repo-root, --env, or --tmux.
tuistory launch "my-tui-app" -s demo --cols 120 --rows 36
tuistory -s demo wait ">" --timeout 15000
tuistory -s demo snapshot --trim
tuistory -s demo close