termscope

April 4, 2026 · View on GitHub

Headless terminal emulator CLI powered by libghostty-vt.

Spawn any command in a real virtual terminal, interact with it programmatically, and capture the state — all from a single binary with zero runtime dependencies. Built for AI agents, CI pipelines, and TUI testing. Inspired by Playwright-style automation, but for the terminal.

Install

curl -fsSL https://raw.githubusercontent.com/mwunsch/termscope/main/install.sh | sh

Or build from source (requires Zig 0.15.x):

git clone https://github.com/mwunsch/termscope.git
cd termscope
zig build -Doptimize=ReleaseSafe

Quick Start

Snapshot a TUI

termscope snapshot -- htop
termscope snapshot --format json -- btop
termscope snapshot --format svg -o screenshot.svg -- my-tui

Interact then capture

termscope exec \
  --wait-for-text "Search:" \
  --type "hello" \
  --press RET \
  --wait-idle 200 \
  --snapshot \
  -- my-tui

Assert in CI

termscope exec --expect "Connection refused" -- my-app
# exit 0 if found, exit 1 if not

Drive from an agent (session mode)

termscope session -- vim test.txt

Reads JSON-line requests from stdin, writes JSON-line responses to stdout:

{"id":1,"method":"snapshot"}
{"id":1,"result":{"cols":80,"rows":24,"cursor":[0,0],"screen":"primary","title":"vim","text":"..."}}

{"id":2,"method":"type","params":{"text":"ihello world"}}
{"id":2,"result":{}}

{"id":3,"method":"press","params":{"key":"ESC"}}
{"id":3,"result":{}}

{"id":4,"method":"query"}
{"id":4,"result":{"cols":80,"rows":24,"cursor":[0,12],"cursor_style":"block","cursor_visible":true,"title":"vim","alt_screen":true}}

{"id":5,"method":"close"}
{"id":5,"result":{"exit_code":0}}

Key Notation

Emacs-style, the established standard:

NotationMeaning
C-cCtrl+C
M-xAlt+X
RETEnter
TABTab
ESCEscape
SPCSpace
DELBackspace
<up> <down> <left> <right>Arrow keys
<f1><f12>Function keys
C-x C-sKey sequence

Output Formats

FormatUse
text (default)Numbered lines, optimized for LLMs
spansText + per-line style runs
jsonStructured JSON
htmlStyled <pre> with <span> elements
svgVisual screenshot

Session Protocol

MethodParamsResponse
snapshotformat?Snapshot data
typetext{}
presskey{}
wait_for_textpattern, timeout?{found, row, col}
wait_for_idleduration?{}
wait_for_cursorrow, col, timeout?{}
queryTerminal state
resizecols, rows{}
close{exit_code}

Errors: {"id":N,"error":{"code":"...","message":"..."}}. The session continues on errors.

Agent Skill

npx skills add mwunsch/termscope