TUI-testing landscape¶
Other tools that drive or snapshot terminal programs, what each one is built on, and the ideas in each that are worth isolating. Repository facts (stars, last push, language, dependencies) were read from the repositories themselves on 2026-10-26. Where a line says mcp-tui-test: it describes what this project does today; see the synthesis for the aggregate view.
How mcp-tui-test fits¶
mcp-tui-test spawns the program under test in a real PTY (pexpect in Python,
creack/pty in Go) and optionally feeds its output through a VT emulator
(pyte in Python, vt10x in Go) to get a screen grid. It exposes that as MCP
tools with structured results, as a tui://{session_id}/screen resource, and
as a pytest fixture. It has text and regex waits, substring and position
assertions, region and line reads, cursor position, and session caps and
reaping. It has no idle or stable-screen wait, no style or colour reads, no
mouse or resize, no screenshots or recordings, no snapshot comparison, and no
scrollback.
Tools¶
microsoft/tui-test¶
github.com/microsoft/tui-test
(formerly microsoft/shell-use; the old URL redirects) · Rust · 295 stars ·
pushed 2026-10-23.
- Approach: real PTY on Windows, Linux and macOS, with a selectable emulator backend: Alacritty (default), Ghostty, Rio or xterm.js. One engine behind a CLI and in-process Rust, Python, JavaScript and Go libraries.
- Assertions and snapshots: Playwright-style text locators
(
find/expect/click/highlight text) with regex, whitespace, style (--fg,--bold, …), OSC 8 link and before/after-anchor filters;expect snapshot NAME; waits on idle, title, clipboard, bell, command, exit and shell readiness;cells X Y W Hreturns styled cells. - Capture: SVG and PNG screenshots; APNG, GIF, MP4 and asciinema
recordings; on-failure traces (
trace.md,trace.json,timeline.json,trace.html). - Agent integration:
agent-contextprints the command schema as JSON,skill --addinstalls an agent skill,--jsoneverywhere, and a session daemon so each CLI call is one step.
Core ideas:
- Locators over the screen grid instead of byte-stream matching. A locator
is a query (text, regex, style, link, anchor) that resolves to cell spans
and is retried until it passes. mcp-tui-test: has
assert_containsandassert_at_position, but no query that returns match coordinates and no style or anchor filters. - One engine, many front ends. The CLI and four language bindings share
one native core, so a test written for an agent and a test in a
cargo testbehave identically. mcp-tui-test: has the same shape in miniature (MCP server and pytest fixture sharecore.py), but Python and Go are separate implementations that share only the tool schema. - Failure artefacts for agents first. A failed assertion writes
structured artefacts, and the docs tell agents to read
trace.mdand the JSON rather than the HTML. mcp-tui-test: attachesscreen_excerptto a failed assertion and nothing else. - Swappable real emulators. Backends are real terminal cores, so a test can be run against the emulator a user actually has. mcp-tui-test: one fixed emulator per language.
antithesishq/bombadil¶
github.com/antithesishq/bombadil · Rust · 1,564 stars · pushed 2026-10-23 · manual.
- Approach: property-based testing of web and terminal UIs. Terminal mode
runs the program on a real PTY (
portable-pty) and parses its output withlibghostty-vt. The loop is: extract state, check every property, pick the next action from the current state, perform it, wait for the next chunk of output or a timeout, repeat. - Assertions: properties are TypeScript modules using temporal operators
(
always,eventually,next,now,implies) over a sequence of states. Terminal defaults includeexitSuccessandnoReplacementChars; the default action generator types Unicode input and escape sequences.bombadil terminal shrinkreduces a violating trace to a shorter one (marked experimental). - Results: Oskar Wickström's 2026-04-30 write-up reports a freeze in vitetris, two btop bugs (one already fixed in 1.4.6, one newly reported) and an rlwrap segfault from a few days of random input, scrolling and resizing at roughly 300 captured states per second.
- Agent integration: none; it is a CI and local tool with a GitHub Action.
Core ideas:
- Property-based exploration instead of scripted examples. Random, state-aware action generation reaches sequences nobody scripted. mcp-tui-test: every action is chosen by the caller.
- Temporal properties over a state sequence. Invariants are checked after every step, not at one chosen moment. mcp-tui-test: single-moment assertions only.
- Shrinking a violating trace. A long random trace is reduced to a minimal reproduction. mcp-tui-test: no trace recording, so nothing to shrink.
- Default properties that need no spec. "Exits zero" and "never prints a byte the terminal can't render" catch real bugs with zero configuration. mcp-tui-test: no built-in invariants.
montanaflynn/headless-terminal (ht)¶
github.com/montanaflynn/headless-terminal · Go · 121 stars · pushed 2026-08-31.
- Approach: a Unix-socket daemon owns one PTY per session and feeds output
through
libghostty-vt(statically linked via cgo); thehtCLI talks JSON lines to it. Vim-style key notation (<CR>,<Esc>,<C-c>). - Assertions and snapshots:
ht viewin plain, ANSI, HTML, PNG or JSON;ht waiton text, cursor, idle, change or exit; asciicast recording viaht record;ht watchstreams a session live to another pane. - Agent integration: an Anthropic-format skill in
skills/headless-terminal/that teaches the key notation and a "wait-strategy decision tree (the part agents get wrong)".
Core ideas:
- Send synchronisation as a first-class concept. Default 20 ms keystroke
pacing, plus
--wait-text,--wait-cursor,--wait-idle,--wait-changeand--wait-exitthat compose with AND on the samesend. mcp-tui-test:send_keyssleeps a fixeddelay(default 0.1 s); waiting is a separateexpect_textcall and only on text. - Daemon-owned sessions, thin CLI. Sessions outlive any one command, so an agent issues one shell command per step. mcp-tui-test: equivalent via the MCP server process holding sessions.
- Shoulder-surfing.
ht watchlets a human see live what the agent is driving. mcp-tui-test: no live view. - Skill as the integration surface. Instead of an MCP server, a
SKILL.mdthat teaches an agent when and how to call the CLI. mcp-tui-test: MCP tools only; no skill file.
beorn/termless¶
github.com/beorn/termless · TypeScript (Bun and Node) · 36 stars · pushed 2026-10-24.
- Approach: a
TerminalBackendinterface with twelve implementations: xterm.js (default), Ghostty via WASM, Ghostty native, Alacritty, WezTerm, vt100 (TypeScript), vt100 (Rust), vt220, vterm.js, libvterm, Kitty and Peekaboo (drives a real macOS terminal through accessibility APIs). Tests canfeed()bytes with no process, orspawn()a real PTY (Bun native, ornode-ptyon Node). - Assertions and snapshots: region selectors (
screen,scrollback,buffer,viewport,row(n),cell(r, c),range) and 21+ Vitest matchers for text, cell style, cursor, mode, title, scrollback andtoMatchTerminalSnapshot();term.outmatches raw protocol bytes that render nothing (Kitty graphics, OSC 52). SVG and PNG screenshots; recordings in a.tty/.ttyzjournal format with codecs to asciicast,.tapeand ttyrec. - Agent integration:
@termless/clishipstermless record,termless playand an MCP server.
Core ideas:
- Multi-backend conformance. The same test runs against every emulator, and a differential corpus mined from the emulators' own test suites grades backends against each other. mcp-tui-test: pyte and vt10x are implementation details, not selectable, and never compared.
- Three inlets, one path.
feed(),spawn()and a replayed recording all arrive at the backend as ordered byte writes, so an assertion written against a live process runs unchanged against a stored journal. mcp-tui-test: live PTY only. - Region selectors separate "where" from "what".
expect(term.row(0))andexpect(term.cell(0, 8)).toHaveFg(…)compose a location with a matcher. mcp-tui-test:get_screen_regionandget_linereturn text; there is no cell-level style view. - Raw-output assertions for invisible protocol. Some behaviour changes no cell, so the byte stream stays inspectable next to the grid. mcp-tui-test: stream mode exposes the stream with ANSI optionally retained, but not alongside a grid in the same session.
vyncint/termlens¶
github.com/vyncint/termlens · Rust ·
27 stars · pushed 2026-10-23 · recommended in ratatui's
snapshot-testing recipe as
the PTY layer to pair with TestBackend.
- Approach: real PTY,
vt100crate behind an internal trait (with a second parser recovering blink, conceal and strikethrough, whichvt100drops). A reader thread drains the PTY continuously and answers the queries a real terminal would answer (cursor position, DA,DECRQM,XTGETTCAP, background colour), so capability-probing apps do not hang. EveryScreenis an immutable snapshot taken under one lock. Windows via ConPTY. - Assertions and snapshots:
wait_until(pred),wait_frame(pred)for complete DEC 2026 synchronized frames,snapshot_after(pred),wait_stable,wait_idle,wait_exit;assert_screen_snapshot!via insta;mask_matching,mask_rectandmask_matchesblank volatile cells on the grid without shifting columns;Screen::diffreports cell-by-cell changes;Screen::parsereads a printed snapshot back in. Mode-aware input: mouse, paste and keys are encoded for the modes the app enabled.graphics()captures Kitty and sixel transmissions. AtermlensCLI doesinspect,diffandrender --svg. - Agent integration: none beyond the CLI and JSON output
(
serdefeature).
Core ideas:
- Wait on a predicate over the screen, not on text. Every wait takes a
closure over the full
Screen(cells, styles, cursor, modes, counters) and is deadline-bounded with the screen attached on timeout; there is nosleep. mcp-tui-test:expect_textwaits on a regex over the stream; the pytest plugin'swait_until_containspolls the buffer text. - Frame-complete snapshots.
wait_framereturns only complete DEC 2026 frames, so a snapshot is never torn mid-repaint;snapshot_afterwaits for the predicate and then for stillness. mcp-tui-test:capture_screenreads whatever has arrived. - Mask the grid, not the text. Volatile content is replaced cell-for-cell so layout and styles still compare. mcp-tui-test: no snapshot comparison.
- Answer the terminal's questions. Replying to capability probes is what
lets real apps run under a fake terminal. mcp-tui-test: pexpect and pyte
do not answer
CSI 6nor DA requests; apps that wait for a reply stall until their own timeout.
mwunsch/termscope¶
github.com/mwunsch/termscope · Zig · 19 stars · pushed 2026-04-08.
- Approach: single static binary on
libghostty-vt.termscope snapshot -- cmdfor one-shot capture,termscope execwith--wait-for-text,--type,--press,--wait-idle,--expectfor scripted runs, andtermscope sessionas a JSON-lines request/response protocol over stdin/stdout. Emacs-style key notation (C-c,M-x,RET). - Assertions and snapshots:
--expect TEXTsets the exit code; output as numbered text ("optimized for LLMs"), style spans, JSON, HTML or SVG;queryreturns cursor, style, title and alt-screen state. - Agent integration:
npx skills add mwunsch/termscope.
Core ideas:
- JSON-lines session protocol on stdio. A TUI driver that any harness can speak without an SDK or a daemon socket. mcp-tui-test: MCP over stdio plays the same role with a schema.
- Numbered-line text format for models. Row numbers in the text output
so an agent can refer to a line without counting. mcp-tui-test:
get_line(row)exists butcapture_screenreturns unnumbered text. - Exit-code assertions for CI.
exec --expectmakes a one-liner a test. mcp-tui-test: assertion results are structured fields, not an exit code, because the caller is an MCP client.
fcoury/termwright¶
github.com/fcoury/termwright · Rust · 23 stars · pushed 2026-02-10.
- Approach:
portable-ptyand thevt100crate, async (tokio). Library, CLI (run,screenshot,run-stepsfor YAML/JSON step files) and adaemonmode exposing a JSON-RPC Unix socket. Emulates replies to OSC 10/11/12 colour queries andCSI 6ncursor-position requests. - Assertions and snapshots: wait for text, regex, screen stability
(
wait_for_idle) or exit; screen as text or JSON with colours, cursor and cell attributes; PNG screenshots rendered with system fonts; box detection that finds UI boundaries from box-drawing characters. - Agent integration: JSON screen output described as "for AI agents"; no MCP server or skill.
Core ideas:
- Box detection. Deriving panel and dialog boundaries from box-drawing characters gives a semantic region without the app's cooperation. mcp-tui-test: regions are caller-specified coordinates.
- Declarative step files. A YAML or JSON scenario replaces a program for simple E2E flows. mcp-tui-test: no scenario format; the harness scripts calls.
- Shell-scriptable daemon.
socatplusjqis enough to write tests. mcp-tui-test: needs an MCP client.
charmbracelet/vhs¶
github.com/charmbracelet/vhs · Go · 21,201 stars · pushed 2026-10-01.
- Approach: a
.tapeDSL (Type,Enter,Sleep,Wait /regex/,Set,Require,Hide/Show,Screenshot,Source,Env) executed againstttydserving xterm.js in a headless Chromium driven byrod; frames are assembled byffmpeg.vhs recordwrites a tape from a live session. - Assertions and snapshots:
Output golden.asciior.txtwrites a text rendering for golden-file diffs in git;Wait[+Screen][+Line] /regex/is the only synchronisation primitive. GIF, MP4, WebM and PNG frame outputs. - Agent integration: none built in. Agents use it to produce a GIF or
.asciithey can read; testty compiles its scenarios to tapes for the same reason.
Core ideas:
- Tape DSL compiled to recordings. One short script yields a reproducible demo and a golden text file. mcp-tui-test: no DSL and no recording.
- Golden text outputs from the same run as the video. The
.asciirender is the test; the GIF is the documentation. mcp-tui-test: no golden files. - A real browser-hosted emulator. Rendering through xterm.js in Chromium makes the picture match what a web-embedded terminal shows, at the cost of heavy dependencies. mcp-tui-test: lightweight emulator, no pixels.
charmbracelet x/exp/teatest¶
github.com/charmbracelet/x (package
exp/teatest, with a v2 for Bubble Tea v2) · Go.
- Approach: in-process.
NewTestModelruns atea.Modelwith a fake terminal size;Send(tea.Msg)injects messages;WaitFor(reader, cond)polls the output reader;FinalModelandFinalOutputwait for the program to quit. - Assertions and snapshots:
RequireEqualOutputcompares the final output bytes with a golden file intestdata/, updated with-update, diffed with the systemdiff. - Agent integration: none.
Core ideas:
- Golden output of the final frame. A single byte-exact file per test. mcp-tui-test: none.
- Inject messages, not keystrokes. Tests speak the framework's own event type, skipping the terminal. mcp-tui-test: always keystrokes through a PTY, which is what you want when the app is a black box.
ratatui TestBackend with insta¶
ratatui.rs/recipes/testing/snapshots · Rust.
- Approach: in-process.
Terminal::new(TestBackend::new(80, 20)), draw the widget,assert_snapshot!(terminal.backend()). Snapshots are text, one row per line; colour is not included (ratatui issue #1402 is open for that). The recipe then points to termlens for the layersTestBackendcannot see: the event loop, raw mode, alternate screen, exit codes. - Agent integration: none.
Core ideas:
- In-process buffer snapshots with a review workflow.
cargo insta reviewmakes accepting intentional changes cheap. mcp-tui-test: none. - Two layers, explicitly. Widget tests in-process, a few flows through a PTY. mcp-tui-test: is the second layer only.
Textual Pilot and pytest-textual-snapshot¶
Textual testing guide and github.com/Textualize/pytest-textual-snapshot · Python · 52 stars · pushed 2025-04-26.
- Approach: in-process.
App.run_test()runs the app headless and yields aPilotwithpress,click(by CSS selector or offset, with modifiers and click counts),hoverandpause;sizeis set onrun_test. - Assertions and snapshots: plain
assertagainst widget state;snap_compare(app, press=[…], run_before=…, terminal_size=…)renders an SVG screenshot and compares it with the stored one; an HTML report shows the diff;--snapshot-updateaccepts changes. - Agent integration: none.
Core ideas:
- Selector-addressed interaction.
pilot.click("#number-5")targets a widget, not a coordinate. Needs the framework's cooperation. mcp-tui-test: coordinates and text only. - SVG screenshot as the snapshot artefact. Diffable in a browser, styles included. mcp-tui-test: none.
pexpect / expect, pyte, libvterm¶
pexpect, expect, pyte, libvterm.
- Approach: pexpect and expect spawn a PTY and match the byte stream against patterns; pyte (Python) and libvterm (C, used by Neovim) are VT emulators that turn that stream into a grid. mcp-tui-test's Python implementation is pexpect plus pyte.
- Assertions:
expect(pattern)with a timeout; pyte exposesscreen.display,screen.buffer[row][col]with styles, and cursor;HistoryScreenadds scrollback. - Agent integration: none.
Core ideas:
- Stream matching for sequential programs. Cheap and exact when the program prints lines. mcp-tui-test: stream mode.
- Separate the PTY from the emulator. Any stream can be fed to any
emulator. mcp-tui-test: same split (
ScreenSessionowns both).
testty¶
docs.rs/testty, source in
github.com/agentty-xyz/agentty
(crates/testty) · Rust. The testty crate exists on crates.io (0.18.1 as
of 2026-10-21); it is published from the agentty monorepo, and the latest
crate-level description there is agentty's rather than testty's.
- Approach: real PTY (
portable-pty) with thevt100crate.Scenariobuilder (wait_for_stable_frame,press_key,write_text,eventually,capture) run against aPtySessionBuilder; atestty run scenario.yamlCLI for non-Rust projects. Answers DA1 so capability probes return immediately. - Assertions and snapshots:
match_text_in_region, recipes such asexpect_selected_tabthat read highlight styles; text or pixel baselines; "proof" reports as text, screenshot, GIF, HTML or JUnit XML. Feature GIFs are produced by compiling the same steps to a VHS.tape, hash-cached on the PTY frames and rendering settings so they re-record only when the UI changed. - Agent integration: none specific.
Core ideas:
- Semantic recipes over styles. "Selected tab" is defined by highlight attributes, not by text. mcp-tui-test: no style access.
- Compile scenarios to VHS tapes. The test and the demo recording are the same steps. mcp-tui-test: none.
- Hash-cached recordings. Re-render only when the frames changed. mcp-tui-test: none.
hegner123/tui-test-ghost and mjpowersjr/vterm-mcp¶
github.com/hegner123/tui-test-ghost · Zig · 2 stars · pushed 2026-05-16. github.com/mjpowersjr/vterm-mcp · Rust · 3 stars · pushed 2026-04-15.
Both exist. Both are MCP servers over a real PTY and libghostty-vt, so they
are the closest in shape to mcp-tui-test.
- tui-test-ghost tools:
tui_start,tui_send(key tokens such asdown*3, withsettle_ms),tui_screen,tui_cell(char, bold, italic, underline, strikethrough, dim, fg, bg),tui_wait(text,stable_ms, or cursor position),tui_resize,tui_snapshot(golden_pathcompares or creates a baseline),tui_stop,tui_record_start/stopto a JSONL file, andtui-test-ghost replay test.jsonlthat re-executes action tools and compares assertion tools' recorded results. Every tool accepts aregion. - vterm-mcp tools:
start_session,stop_session,list_sessions,get_screen(text, cursor, optional per-cell styles),send_keys(named keys withC-,M-,S-prefixes),wait_for_text(substring or regex).
Core ideas:
- Record the MCP session, replay it as a test. The agent's exploratory calls become a JSONL file; replay re-runs the actions and checks the recorded assertion results, turning an interactive session into a regression test. mcp-tui-test: no recording.
- Region parameter on every read. Any tool can scope itself to a
sub-rectangle, so screen text sent to the model stays small.
mcp-tui-test: only
get_screen_region. - Golden snapshot as a tool.
tui_snapshotwith a path compares or creates the baseline from inside the agent loop. mcp-tui-test: none. - Settle time on send.
settle_msandstable_mswait for the screen to stop changing. mcp-tui-test: fixeddelay.
libghostty-vt and go-libghostty¶
mitchellh.com/writing/libghostty-is-coming (2025-09-22) and github.com/mitchellh/go-libghostty · Go bindings · 155 stars · pushed 2026-10-21 (a mirror; source of truth is on tangled.org).
libghostty-vt is "a zero-dependency library that provides an API for parsing
terminal sequences and maintaining terminal state", extracted from Ghostty.
It is the emulator under bombadil, ht, termscope, tui-test-ghost, vterm-mcp
and one of the backends of microsoft/tui-test and termless. go-libghostty is
a cgo binding that statically links it; its example feeds bytes to a
Terminal and formats the grid as plain text with a Formatter.
Mitchell Hashimoto did not build a TUI test framework. On 2026-04-30 he wrote:
Libghostty can now be used to fuzz TUIs, thanks to @owickstrom + @AntithesisHQ. They already found bugs in multiple including btop. I always imagined libghostty would be useful for testing TUIs, super happy to see this is both practical and valuable.
This is another example of where speed matters! "Why does Ghostty need to be so fast?" Well, if you're running hundreds or thousands of unit tests that each use a clean in-memory terminal, you want that to be fast. If you're fuzz testing and trying to push an unlimited amount of data through a terminal, you want that terminal to be fast.
Core ideas:
- A production emulator as a library. The grid a test asserts on is the same code a real terminal renders, so emulator bugs are not the test's bugs. mcp-tui-test: pyte and vt10x are small independent emulators.
- Throughput as a testing feature. Fast parsing is what makes thousands of in-memory terminals per test run and fuzzing at hundreds of states per second practical. mcp-tui-test: one session per PTY, speed not a goal.
Synthesis¶
Idea by tool. A filled cell means the tool has the idea as a documented feature. The last column is mcp-tui-test today.
| Idea | tui-test | bombadil | ht | termless | termlens | termscope | termwright | vhs | teatest | ratatui | Textual | testty | ghost / vterm-mcp | mcp-tui-test |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Real PTY | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | |||
| Production emulator (Ghostty, Alacritty, xterm.js, …) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | |||||||
| Multi-backend conformance | ✓ | ✓ | ||||||||||||
| Wait for idle / stable screen | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | |||||||
| Wait on predicate over full screen state | ✓ | ✓ | ✓ | ✓ | ||||||||||
| Frame-complete (DEC 2026) snapshots | ✓ | |||||||||||||
| Locators returning match coordinates | ✓ | ✓ | ✓ | |||||||||||
| Cell styles and colours readable | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ||||
| Region-scoped reads | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ||||||||
| Semantic regions (box detection, tab recipes) | ✓ | ✓ | ✓ | |||||||||||
| Golden / snapshot comparison | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | |||||
| Grid masking of volatile cells | ✓ | |||||||||||||
| SVG / PNG screenshots | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | |||||
| Recording (asciicast, GIF, tape) | ✓ | ✓ | ✓ | ✓ | ✓ | |||||||||
| Replay a recording through the same assertions | ✓ | ✓ | ||||||||||||
| Tape / scenario DSL | ✓ | ✓ | ✓ | ✓ | ||||||||||
| Answers terminal capability queries | ✓ | ✓ | ✓ | |||||||||||
| Mouse input | ✓ | ✓ | ✓ | ✓ | ||||||||||
| Resize / SIGWINCH | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ||||||||
| Scrollback | ✓ | ✓ | ✓ | ✓ | ✓ | |||||||||
| Property-based exploration with shrinking | ✓ | |||||||||||||
| Temporal properties over a trace | ✓ | |||||||||||||
| Structured (JSON) results | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | |||||||
| Failure artefacts beyond the message | ✓ | ✓ | ✓ | ✓ | ✓ | partial | ||||||||
| MCP server | ✓ | ✓ | ✓ | |||||||||||
| Agent skill file | ✓ | ✓ | ✓ | |||||||||||
| Session daemon or server | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ||||||||
| Live human view of the session | ✓ | ✓ | ||||||||||||
| In-process framework hooks | ✓ | ✓ | ✓ |
"partial" for mcp-tui-test: failed assertions carry screen_excerpt; there
are no traces, screenshots or recordings.
Ideas mcp-tui-test could adopt next¶
Ordered by payoff for an agent-driven TUI test server, highest first. This is a list, not a plan; none of it is implemented.
- Wait for a stable screen. A
wait_stable(quiet_ms)tool (termlens,ht --wait-idle, tui-test-ghoststable_ms) replaces the fixeddelayonsend_keysand the guesswork beforecapture_screen. It is the single most common cause of flaky agent runs and costs one loop over the buffer. - Find text, return coordinates. A
find_text(pattern)returning row and column spans (tui-test's locators, termlens'sfind_all) turnsassert_at_positionfrom "the caller guesses the position" into "the caller asserts what it found". - Expose cell styles. pyte and vt10x both carry fg, bg, bold and reverse
per cell; a
get_cell(row, col)or a styledcapture_screenlets an agent verify a selected item or an error colour (termless, tui-test-ghosttui_cell, testty's recipes). - Record the tool calls, replay as a test. tui-test-ghost's JSONL recording and replay converts an agent's exploratory session into a deterministic pytest case with no extra authoring. The pytest plugin already has the engine for the replay side.
- Golden screen snapshots with grid masking. A
snapshot(name)that compares the buffer text to a stored file, with termlens-style cell masks for clocks and PIDs, gives the pytest plugin a regression story and the MCP server a one-call assertion. - Answer capability queries. Replying to
CSI 6n, DA1 andDECRQM(termlens, termwright, testty) stops capability-probing apps from stalling on launch. Small code, removes a class of mysterious timeouts. - Region on every read. A
regionparameter oncapture_screenand the assertions (tui-test-ghost) keeps screen text sent to the model small without a second call. - Resize.
resize(cols, rows)withSIGWINCH(most tools) tests the layout path that in-process frameworks cannot reach. - Default invariants. Bombadil's zero-config properties, "exited zero" and
"never printed a replacement character", are cheap checks the registry
could report on every
close_session. - An agent skill file. A
SKILL.mddescribing when to use which mode and how to wait (the parthtsays agents get wrong) complements the MCP schema for clients that load skills. - Screenshots. SVG rendering of the buffer (termscope, termlens) gives a human a picture of what the agent saw. Useful, but the text grid already serves the model.
- Property-based exploration. Random action generation over the existing tools is possible from the harness side today; shrinking and temporal properties are Bombadil's whole product and out of scope for this server.