Skip to main content

tui-verify-change

Verify a change to Warp's headless TUI front-end (crates/warp_tui) by running it — locally via ./script/run-tui, or in a headless cloud runner via a WARP_API_KEY dogfood build — and reading the rendered screen back (under tmux when it's installed, otherwise directly). Use whenever you change TUI UI, rendering, input, or behavior and need to confirm the real on-screen result.

Source facts

Repository
warpdotdev/warp
Last source activity
July 27, 2026 at 17:38
Detected SKILL.md language
English
Stars
65,700
Forks
5,704

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
tui-verify-change
description
Verify a change to Warp's headless TUI front-end (crates/warp_tui) by running it — locally via ./script/run-tui, or in a headless cloud runner via a WARP_API_KEY dogfood build — and reading the rendered screen back (under tmux when it's installed, otherwise directly). Use whenever you change TUI UI, rendering, input, or behavior and need to confirm the real on-screen result.
# tui-verify-change Verify a change to Warp's **headless TUI** front-end (`crates/warp_tui`, and the cell-grid element library in `crates/warpui_core/src/elements/tui`) by running it and reading back the actual rendered screen. The whole point: **the TUI is a console program, so you can run it in a real terminal and read the actual rendered screen straight back.** When `tmux` is available it's the ideal driver — you run the TUI in a tmux pane, drive it with `tmux send-keys`, and read the frame back with `tmux capture-pane` — but tmux is **not required**: if it isn't installed you can still run and observe the TUI directly (see **If tmux isn't installed** under Step 2). Either way you (the agent) see the actual screen text — no `computer_use`, no real display, no cloud screenshot agent, and no relying on a separate watcher's description of what it saw. (Contrast the GUI-only `gui-onboarding-verification-skill`, `gui-integration-test`, `gui-integration-test-video`, and the `computer_use` / `verify-ui-change-in-cloud` flow.) This is the fast, preferred way to confirm a TUI change end-to-end. This skill covers the **manual live-run** verification. For a durable regression that runs in CI, also add a render-to-lines unit test per `tui-testing` — the two are complementary: the live run confirms real behavior; the snapshot test locks it in. ## When to use - You changed anything a TUI user sees or interacts with: an element/layout in `crates/warpui_core/src/elements/tui`, a TUI view/screen in `crates/warp_tui` (transcript, input, zero state, login placeholder, etc.), TUI keybindings, or TUI rendering/behavior. - You want to confirm the *actual* rendered result, not just that it compiles or a unit test passes. If your change is GUI-only (`app/`, WarpUI pixel `Element`/`View`), this is the wrong skill — use the GUI verification path instead. ## Local vs cloud verification (pick your context first) *How* you build, run, and log in depends on **where you're running**. Decide this up front — it determines whether you use `./script/run-tui` or the `WARP_API_KEY` path below. - **Local context** — you're working in a local dev checkout, typically alongside the user. Just run **`./script/run-tui`** directly: it builds `warp_tui` and runs it, selecting the internal `local` channel when the `warp-channel-config` generator is available and falling back to `warp-tui-oss` otherwise. **Do not reach for `WARP_API_KEY`** here — it's generally **not set** in a local environment, and you don't need it: a local build reaches the authenticated state through the normal interactive device-auth login flow (or you're already signed in on this machine). The non-interactive `WARP_API_KEY` login below is a *cloud-runner* affordance, not a local one, so don't let a missing key block you locally. - **Cloud context** — you're a headless cloud agent (e.g. the factory-client runner) with no browser for device-auth, so reaching a **signed-in** surface relies on the non-interactive `WARP_API_KEY` already in the environment. That does **not** mean bypassing `./script/run-tui`: when the `warp-channel-config` generator is available, `./script/run-tui` selects the internal `local` binary; otherwise it falls back to `warp-tui-oss`. Both accept the inherited `WARP_API_KEY`, so prefer the maintained runner unless you need a specific binary or profile. In the cloud, the inherited key signs you in without the browser device-auth flow. The logged-**out** surface (the `Sign in to continue` placeholder and any pure element/layout) needs neither login path — a plain OSS build is enough in either context. ## Step 1 — Build the TUI Always build with a small job count so the large `warp` dependency tree doesn't OOM the machine: ```bash cd <warp-repo-root> CARGO_BUILD_JOBS=2 cargo build -p warp_tui --bin warp-tui-oss ``` - `warp-tui-oss` is the OSS channel binary and the safest default (no internal `warp-channel-config` generator required). `./script/run-tui` does the equivalent, selecting the internal `local` binary when the generator is available and falling back to `warp-tui-oss` otherwise. - Fix all compile errors before running (see `fix-errors`). The first build of this tree takes a while; **incremental rebuilds after a one-line change are fast (~10s)**, so the edit → rebuild → re-capture loop below is quick. ### Logged-in vs logged-out (important) The OSS build starts **logged out** and stops at a `Sign in to continue` placeholder (it drives a device-authorization login flow that needs a browser). The login-gated root has three pre-session states you may see: - `AwaitingLogin` → a centered placeholder that reads `Sign in to continue`, then `Opening your browser…` (or, once the device code is known, `Open <uri> in your browser` and `and enter code: <code>`). It does **not** show a Ctrl-C hint. - `LoggedIn` → briefly `Starting terminal…`, then the **zero state** (`Warp Agent` + version, a "What's new" list, and the project context section) with the input view. - `Failed` → `Login failed: <message>` followed by `Press Ctrl-C to exit.` (Exact strings live in `crates/warp_tui/src/ui.rs` — verify against it if you're asserting on placeholder text.) So: if your change is on the **login placeholder** or a pure element/layout, the logged-out OSS build is enough. If your change is in the **live terminal / transcript / input** surface, you must reach the authenticated state (see the next section), or your change will sit behind the login gate and you'll only ever see `Sign in to continue`. ### Logging in non-interactively (`WARP_API_KEY`) — cloud context **This is the cloud path.** In a local checkout, skip it: run `./script/run-tui` and log in interactively (see **Local vs cloud verification** above), since `WARP_API_KEY` usually isn't set locally. Use the flow here when you're a headless cloud runner where the key *is* set and there's no browser for device-auth. You can reach the authenticated (`LoggedIn`) state headlessly — no browser, no device-auth flow — by launching any TUI channel binary with a `WARP_API_KEY` in the environment. This is the fast way to verify live terminal/transcript/input changes. Key constraints: - **All channels are supported.** API-key login works with `warp-tui-oss` as well as the Preview, Dev, Local, and Stable binaries. - **The key must already be in the environment.** In a sandbox where `WARP_API_KEY` is set, a freshly started `tmux` server inherits it. Never echo, print, or inline the secret value in a command — just rely on the inherited environment variable. (`--api-key <key>` on the command line also works but would expose the secret, so prefer the env var.) ```bash cd <warp-repo-root> CARGO_BUILD_JOBS=2 cargo build -p warp_tui --bin warp-tui-oss tmux kill-session -t tuicheck 2>/dev/null # WARP_API_KEY is inherited from the environment by the new tmux server. tmux new-session -d -s tuicheck -x 120 -y 40 './target/debug/warp-tui-oss' sleep 20 # login + session start tmux capture-pane -t tuicheck -p # expect the logged-in zero state ``` When it works you'll see the **zero state** (`Warp Agent` + input view + model selector) instead of `Sign in to continue`, and you can `send-keys` a real prompt and read the agent's reply back with `capture-pane`. (This login path was added in warpdotdev/warp#13583.) ## Step 2 — Run under tmux and read the frame back The TUI needs a **real interactive PTY** — a tmux pane is exactly that, and it lets you both send input and read the rendered screen back as text. Start the TUI in a detached session with an explicit size (don't skip `-x`/`-y`; a degenerate 1-row pane renders nothing useful): ```bash cd <warp-repo-root> tmux kill-session -t tuicheck 2>/dev/null # clear any prior run tmux new-session -d -s tuicheck -x 120 -y 40 './target/debug/warp-tui-oss' sleep 1 # let it draw + probe the terminal tmux capture-pane -t tuicheck -p # <-- the rendered screen, as text ``` `tmux capture-pane -p` prints the pane's current contents (the TUI's alternate screen) to stdout, so **you read the real frame directly** and assert on it. Add `-e` to include ANSI escape sequences when you need to check colors/styles: ```bash tmux capture-pane -t tuicheck -p -e # includes color/style escapes ``` Drive interactions with `tmux send-keys`, sleeping to let the UI settle, then capture again. For example (type a line, submit it, wait, then read the screen): ```bash tmux send-keys -t tuicheck "What is 2+2? Answer in one short sentence." && \ sleep 1 && tmux send-keys -t tuicheck Enter && sleep 5 && \ tmux capture-pane -t tuicheck -p -e ``` Send special keys by name (`Enter`, `Escape`, `C-c` for Ctrl-C, `Up`/`Down`). When done, tear the session down: `tmux kill-session -t tuicheck`. ### If tmux isn't installed `tmux` is the preferred driver because it gives you programmatic `send-keys` + `capture-pane`, but it is **not** a hard requirement — never block verification just because tmux is missing. Check with `command -v tmux`; if it's absent, fall back: - **Local context:** the simplest path is to run `./script/run-tui` directly in a real terminal and read the rendered output yourself — you already have a PTY. When you're working alongside the user, you can also have them run it and report what renders. Installing tmux is optional, not a prerequisite. - **Cloud / no-tmux context:** run the built binary inside another PTY wrapper so you can still capture output — e.g. `script` (util-linux): `script -qe -c './target/debug/warp-tui-oss' /tmp/tui.log`, then read `/tmp/tui.log`. If tmux is installable in your environment (`apt-get install -y tmux`) and that's cheaper, do that and use the flow above instead. If none of these work, run the binary directly, capture whatever output you can, and **say so** in the PR/thread rather than implying a tmux-driven capture. Everything else in this skill (what to look for, the snapshot test, the evidence) is identical whether or not tmux drove the run. ### Iterate loop Because incremental rebuilds are ~10s, iterate tightly: edit the TUI code → `cargo build -p warp_tui --bin warp-tui-oss` → `tmux kill-session` + restart the session → `capture-pane` and compare. Verified before/after example: changing the login placeholder string and rebuilding flips the captured line from `Sign in to continue` to the new text, visible directly in `capture-pane` output. ## Step 3 — Check the captured frame against your change You have the real screen text, so verify it yourself: grep/scan the `capture-pane` output for the **exact string or layout** your change should produce, and diff the before/after captures. No watcher interpretation needed — if the expected text isn't in the capture, the change isn't rendering. ## Pitfalls (learned hands-on) - **Run it in a real terminal / tmux pane** (a PTY), and drive + capture it via tmux as above. - **If it exits (code 101) right after the first frame instead of staying up:** don't assume it's a terminal/stdin problem — **check the TUI log first**: `tail -40 ~/.local/state/warp-terminal-tui/oz/warp-tui.log`. One cause seen in the headless OSS/logged-out sandbox build is a debug-only binding-validation panic: `crates/warpui_core/src/keymap/matcher.rs` (`validate_bindings`, gated on `#[cfg(debug_assertions)]`) panics with `Bindings failed validation` when a *keystroke* binding matches a TUI keymap context without being TUI-owned (it was `app:reopen_closed_session`, Ctrl+Alt+T). It does **not** reproduce in every setup — it depends on which keystroke bindings the running config loads, and the validator exempts non-keystroke (palette/custom) triggers — so treat this as one thing to check, not a guarantee. If you hit it, two ways to still verify a change: - **Build `--release`** — the validator is compiled out, so the TUI stays up and you can `send-keys`/`capture-pane` freely: `cargo build --release -p warp_tui --bin warp-tui-oss` then run `./target/release/warp-tui-oss`. - **Or poll `capture-pane` right after launch** on the debug build to grab the first frame before the panic: ```bash tmux new-session -d -s tuicheck -x 120 -y 40 './target/debug/warp-tui-oss' for i in $(seq 1 15); do frame=$(tmux capture-pane -t tuicheck -p | sed 's/[[:space:]]*$//' | grep .) [ -n "$frame" ] && { echo "$frame"; break; } sleep 0.2 done tmux kill-session -t tuicheck 2>/dev/null ``` If the debug build stays up on your machine, you can `send-keys`/`capture-pane` repeatedly without racing. - **Alt screen is handled for you.** `capture-pane` reads the alternate screen, so you don't need to fight escape-sequence soup the way piping stdout would. - **Startup probe.** On launch the TUI emits terminal probes (OSC `10`/`11` + a device-attributes query) to pick a theme; a normal tmux pane answers them. Give it ~0.5–1s (a `sleep`) before the first capture. - **Quitting.** Ctrl-C is `tmux send-keys -t tuicheck C-c`. On the login placeholder one Ctrl-C exits; in a live session it's press-again-to-exit (first press cancels/clears input and arms a ~1s window; a second within the window exits). Always `tmux kill-session` at the end so a stray session doesn't linger. ## Step 4 — Capture screenshots and video (asciinema + agg) `capture-pane` text is the fast inner-loop check (Step 3) and enough to *assert* on a change. But for PR evidence — and for attaching durable image/video artifacts — you often want an actual **screenshot** or a short **video** of the rendered TUI. Because the TUI is a console program, capture it by recording its PTY session with **asciinema**, rendering that recording with **agg**, and transcoding it to an **MP4** (the same format Warp's `computer_use` screen recording produces) with `ffmpeg`; pull a still frame out with `ffmpeg` too. **Install the tooling (cloud runner — one-time).** asciinema and ffmpeg are packaged; `agg` ships as a prebuilt binary rather than in apt: ```bash sudo apt-get update && sudo apt-get install -y asciinema ffmpeg tmux # agg is not in apt — install a PINNED release binary and verify its checksum before # installing as root (don't pull an unpinned `latest`). The checksum below is for the # x86_64 build; on another arch use that asset's published checksum from the release. AGG_VERSION=v1.9.0 AGG_SHA256=f111e315cd71056b116302342553dd765b7297579ed511f111d0cedb442aeda6 curl -fsSL -o /tmp/agg \ "https://github.com/asciinema/agg/releases/download/${AGG_VERSION}/agg-$(uname -m)-unknown-linux-gnu" echo "${AGG_SHA256} /tmp/agg" | sha256sum -c - # aborts on mismatch sudo install -m 0755 /tmp/agg /usr/local/bin/agg ``` **Record the session.** asciinema needs a real PTY, so run it **inside tmux** (a bare `asciinema rec` in a non-interactive runner shell fails with "not a terminal"). Drive the TUI with `tmux send-keys` exactly as in Step 2 — the keys reach the binary running under asciinema: ```bash cd <warp-repo-root> tmux kill-session -t tuicap 2>/dev/null # clear only THIS capture session (not kill-server) # asciinema records the TUI's PTY; -c runs the binary; --overwrite replaces a prior cast. tmux new-session -d -s tuicap -x 120 -y 40 \ 'asciinema rec --overwrite -c "./target/debug/warp-tui-oss" /tmp/tui.cast' sleep 1 # let it draw + answer the theme probe # ...drive the interaction you want to show, e.g.: # tmux send-keys -t tuicap "hello" Enter && sleep 3 # Quit the TUI so asciinema finalizes the cast. The logged-out placeholder exits on one # Ctrl-C, but a live/logged-in session needs a SECOND press within its ~1s window (see # "Quitting" under Pitfalls) — so send two; the extra press is a harmless no-op if it # already exited (the session is gone, hence 2>/dev/null). tmux send-keys -t tuicap C-c && sleep 0.5 && tmux send-keys -t tuicap C-c 2>/dev/null sleep 1 ``` For a **logged-in** capture, build/run `warp-tui-dev` with `WARP_API_KEY` per Step 1 instead of `warp-tui-oss`. **Render the video (MP4).** Match the format Warp's `computer_use` screen recording uses — an **H.264 / yuv420p MP4** with `+faststart` (see `crates/computer_use/src/linux/recording.rs`) — so TUI captures are consistent with GUI/computer-use recordings. `agg` only emits GIF, so render to GIF and then transcode to MP4 with those settings. libx264 + yuv420p require **even** dimensions, so pad up by a pixel when the terminal render is odd-sized: ```bash agg --cols 120 --rows 40 /tmp/tui.cast /tmp/tui.gif ffmpeg -y -i /tmp/tui.gif -vf "pad=ceil(iw/2)*2:ceil(ih/2)*2" \ -c:v libx264 -pix_fmt yuv420p -movflags +faststart /tmp/tui.mp4
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub