--- name: tui-verify description: Drive and visually verify this TUI (claude-cloak, or any terminal app / embedded claude pane) by running it inside a headless tmux session, sending real keystrokes, and capturing the rendered screen as text. Use whenever you need to confirm a UI/pane/rendering change actually works end-to-end — not just that tests pass — e.g. "verify the pane expands", "does @ show the menu", "check the layout at a small size". Claude Code's own TUI cannot run in a non-tty, so tmux is the way to give it a real terminal. --- # Verifying a TUI with tmux The app renders to a real terminal and cannot run in a plain pipe. tmux gives it a genuine tty in the background, lets you type into it, and lets you read back exactly what's on screen as plain text — so you can *see* a rendering change instead of guessing from tests. ## The safety rule (read first) **Never use `pkill`, `killall`, `kill -9 `, or any broad process-matching kill.** The user runs their own real claude-cloak instances (including the one hosting your session) on the same machine. `pkill -f claude-cloak` will kill all of them. Only ever tear down tmux sessions **you created, by their exact name**: ```bash tmux kill-session -t # only your own named session ``` Pick a distinctive session name (e.g. `ccverify`) so you never touch anything else. Before starting, `tmux ls` to see what's already there and avoid name clashes — never kill a session you didn't create. ## Core loop 1. **Build** the thing you're testing (`cargo build --release`). 2. **Launch** it in a detached tmux session, sized to the scenario you care about. Small sizes (e.g. `-x 100 -y 20`) are what expose layout/overflow bugs — a big terminal hides them. ```bash tmux new-session -d -s ccverify -x 100 -y 20 "./target/release/claude-cloak" sleep 2 # give it time to draw the first frame ``` 3. **Capture** the rendered screen as text: ```bash tmux capture-pane -t ccverify -p ``` Add `-e` to include ANSI escapes (then `| cat -A`) when you need to inspect colors/attributes — e.g. which menu row is highlighted: ```bash tmux capture-pane -t ccverify -p -e | cat -A ``` 4. **Send keystrokes**, then `sleep` briefly (the app redraws on its own ~30fps tick — give it 1–2s) and capture again: ```bash tmux send-keys -t ccverify "a" # a literal key tmux send-keys -t ccverify "Down" "Enter" # named keys tmux send-keys -t ccverify "C-u" # ctrl-u (clear line) tmux send-keys -t ccverify "@src" # a literal string ``` 5. **Tear down your session by name** when done (see safety rule). ## send-keys gotchas - Each space-separated token is interpreted as a **key name** (`Down`, `Enter`, `Space`, `C-f`, `Escape`). So `send-keys "Down Down Down"` sends three Down keys — but if the app has meanwhile changed state (e.g. a menu collapsed to a single match and closed), the same tokens can land as literal text. If you see literal `Down Down Down` in an input box, that's this — resend as needed. - To send a literal string that contains spaces or key-like words, send it as one quoted argument and/or use `-l` (literal): `tmux send-keys -l -t s "Down"`. - Control/modifier chords: `C-u`, `C-f`, `C-q`, `S-Tab` (shift-tab). This app avoids Alt on purpose — don't test Alt chords. - After any keystroke, **sleep before capturing.** Captures taken mid-repaint show a transient frame (a half-drawn box), which can look like a bug that isn't there. 1–1.5s is usually enough. ## Reading the claude-cloak screen - The bottom footer line shows the bound proxy port and the active keybinding hints — a quick sanity check that the app is alive and in the expected mode. - The embedded pane is the lower bordered box titled `claude · `. Its compact framing (`src/term.rs`) shows one context row, the input box (bracketed by `──── … ──` rules), and either the statusLine (idle) or an `@`/`/` menu. - To spawn an embedded claude pane for pane tests: press `a` (model picker), navigate with `Down`, `Enter` to spawn. It resumes-then-clears to a fresh chat. `ctrl-f` toggles fullscreen (shows the child's raw screen verbatim — useful to see what Ink actually drew vs. what we crop). ## Why this beats tests alone Unit tests over `compact_frame` / `compact_view_range` pin the framing math, but they can't catch a PTY-sizing feedback loop where Ink never draws the rows we'd measure — that only shows up when a real `claude` child renders into a real PTY. Run the tmux loop for any change to pane sizing, cropping, cursor, or layout.