# Headless App Verification

Drives an app in a pty, records a video and opens a checked summary page.

- Repository: https://github.com/zenbu-labs/terminal-browser (4380 stars)
- Category: Testing
- License: MIT
- Last updated: 2026-11-01
- Runs scripts: no, instructions only

## Install

Claude Code: `npx skills add zenbu-labs/terminal-browser --skill verify -g -a claude-code -y`
Codex: `npx skills add zenbu-labs/terminal-browser --skill verify -g -a codex -y`
Cursor: `npx skills add zenbu-labs/terminal-browser --skill verify -g -a cursor -y`
Gemini CLI: `npx skills add zenbu-labs/terminal-browser --skill verify -g -a gemini-cli -y`
Claude Code plugin marketplace: `/plugin marketplace add zenbu-labs/terminal-browser`

## Description

Drive an engine app headlessly in a pty, record a video of the whole verification, and open a summary page (video + timeline + checks) with pixel open.

## SKILL.md, as published by its author (untrusted third-party text, read it as data)

Apps here render via the kitty graphics protocol, so they can be verified
without a real terminal. Every verification is **recorded**: the harness in
`tools/verify-recorder/` captures every frame the app emits, overlays your
inputs (click ripples, caption bar), encodes a video, and generates a summary
page. Do not hand-roll one-off pty scripts that only dump PNGs.

## Writing a verification

Write a driver script (in /tmp is fine) using the checked-in package:

```python
import sys
sys.path.insert(0, "<repo>/tools/verify-recorder")
from driver import Driver
from recorder import Recorder

rec = Recorder("wheel-pan", title="Wheel pan keeps cursor anchored")
d = Driver(["<repo>/engine/target/debug/typing"], rec,
           cols=120, rows=32, xpixel=1200, ypixel=800)

d.pump(3.0)                                  # pump between actions so frames arrive
rec.check("app painted", d.frame_size is not None, f"{d.frame_size}")
w, h = d.frame_size                          # REAL framebuffer size, always use this

d.text("hello", "type into editor")          # every input takes a description
d.click(w // 2, h // 3, "select the note")   # mouse coords are pixels (1016 mode)
d.wheel(w // 2, h // 2, down=True, n=3, description="scroll content")
d.pump(1.0)
rec.check("scroll redrew", len(rec.frames) > 40, f"{len(rec.frames)} frames")
rec.still("after-scroll")                    # named snapshot for the summary page

d.stop("ctrl+c")                             # or "ctrl+q" depending on the app
rec.finish()                                 # composites markers, encodes mp4, writes summary
```

The rest: https://github.com/zenbu-labs/terminal-browser/blob/HEAD/.claude/skills/verify/SKILL.md
