Capture the live pi TUI as a pixel-perfect PNG. Compose synthetic conversations from CLI flags, drive real slash commands, load existing sessions, swap themes — whatever you need to make the screenshot you want.
pi is a terminal-first AI coding agent and its TUI does a lot of work
(syntax-highlighted reads, real diffs on edits, colored bash backgrounds,
custom extension messages, theme support). Most screenshot tools either:
- snapshot a static frozen frame and lose all the live behavior, or
- run the real terminal and inherit your font/theme/window chrome.
pi-shot drives a real headless pi instance inside node-pty + @xterm/headless,
walks the buffer cell-by-cell, and renders the result through HTML +
Playwright. The output uses pi's actual ANSI colors, the actual theme JSON,
and a clean macOS-style window frame. The image is auto-trimmed to fit the
content so there's no dead space below the conversation.
npm install -g @miclivs/pi-shotYou also need pi itself installed and on your PATH:
npm install -g @mariozechner/pi-coding-agent# render a synthetic two-turn chat with a bash tool call
pi-shot \
--no-banner --pi-arg --offline \
-m "user:list files" \
-m "bash:ls -la:total 12\ndrwxr-xr-x 3 user staff 96 ." \
-m "assistant:Three files." \
-o chat.png# capture an existing pi session as-is
pi-shot --session ~/.pi/agent/sessions/proj/abc.jsonl -o session.png# pre-fill the editor box with a half-typed prompt
pi-shot --prompt "add tests for the auth flow" -o prompt.png# drive an extension's slash command live
pi-shot --no-banner --keys "/psst-set" --keys "\r" -o ext.pngThe examples/ folder has 15 self-contained scripts covering
every feature. Each one writes a numbered PNG next to itself. Run any of them
individually, or bash run-all.sh to generate them all.
| File | Shows |
|---|---|
01-empty.sh |
Empty pi at rest |
02-simple-chat.sh |
A two-turn user/assistant chat |
03-bash-tool.sh |
A bash tool call with multi-line output |
04-edit-tool.sh |
An edit tool rendered as a real unified diff |
05-all-tools.sh |
Every tool kind in one shot: read, write, edit, bash, grep, find, ls |
06-thinking.sh |
An assistant turn with a thinking block |
07-custom-message.sh |
A custom-typed message of the kind extensions emit |
08-prefilled-prompt.sh |
The editor box pre-filled with text |
09-slash-command.sh |
The slash-command autocomplete popup |
10-multi-step-keys.sh |
Multiple --keys bursts driving a multi-step flow |
11-real-session.sh |
An existing pi session JSONL rendered as-is |
12-light-theme.sh |
The same conversation in pi's built-in light theme |
13-custom-theme.sh |
A custom theme JSON file applied to pi |
14-with-extension.sh |
An extension's slash command rendered live |
15-pi-psst-end-to-end.sh |
Full pi-psst story with a real LLM call |
For quick captures, the -m "kind:body" shorthand covers everything:
user:<text>
assistant:<text>
thinking:<text>
bash:<command>:<output>
read:<path>:<content>
write:<path>:<content>
edit:<path>:<oldText>:<newText>
grep:<pattern>:<path>:<matches>
find:<pattern>:<path>:<results>
ls:<path>:<entries>
custom:<type>:<content>
model:<provider>:<model-id>
thinking_level:<off|minimal|low|medium|high|xhigh>
Use \n inside any field to insert a real newline.
For more complex flows (multi-content assistant turns, custom tool details),
write a JSON file matching the ComposeMessage union and pass --compose file.json instead:
The compose builder uses pi's own typed SessionManager under the hood, so
the resulting JSONL is guaranteed to match pi's real session schema.
Anything you can type into pi, you can send via --keys. Each --keys flag
is one keystroke burst, and pi-shot waits for pi to settle between bursts so
multi-step flows work cleanly:
pi-shot \
--no-banner \
--keys "/psst-set DEMO_KEY sk-live-abc demo,prod\r" \
--keys "echo the value of DEMO_KEY using bash\r" \
--idle-ms 8000 \
-o demo.pngEscape sequences supported: \r, \n, \t, \e, and \xHH.
# built-in light theme
pi-shot --theme light -o light.png
# any pi theme JSON file
pi-shot --theme my-theme.json -o custom.pngThe theme JSON schema is documented in pi's own theme directory. pi-shot
copies your real ~/.pi/agent dir into a private temp dir and layers the
theme override on top, so all your packages, credentials, and model
preferences still apply.
pi-shot writes the output file path to stdout so you can pipe it:
convert "$(pi-shot -m 'user:hi' -o /tmp/x.png -q)" -resize 50% small.pngAll status messages go to stderr. Use -q/--quiet to silence them
entirely. NO_COLOR is honored.
USAGE
pi-shot [INPUT] -o OUTPUT.png [OPTIONS]
INPUT (one of)
--session <file> open an existing pi session JSONL
--compose <file> load compose messages from a JSON array file
-m, --message <kind:body> compose a synthetic message (repeatable)
OUTPUT
-o, --output <file> output PNG path (required)
PROMPT BOX & DRIVING
--prompt <text> pre-fill the editor input box
--keys <text> send raw keystrokes (repeatable)
--extension <path> load a pi extension before capture (repeatable)
TERMINAL SIZE
--cols <n> terminal columns (default: 120)
--rows <n> terminal rows (default: 35)
CAPTURE TIMING
--idle-ms <n> ms of pty silence to consider pi idle (default: 2500)
--step-timeout-ms <n> hard timeout per message step (default: 30000)
--delay <n> extra delay after final idle, before capture
--capture <mode> "viewport" (default) or "all" (full scrollback)
WINDOW CHROME
--no-window disable the macOS-style window frame
--padding <px> padding inside the window (default: 20)
--background <color> window background color
--border-radius <px> window border radius (default: 8)
STARTUP CHROME
--no-banner skip pi's startup overview (welcome banner,
[Context], [Extensions], [Skills], etc)
THEMING
--theme <name|file> pi theme: "dark", "light", or path to JSON
--window-bg <color> override the screen background only
--font-family <css> override the monospace font stack
--font-size <px> override font size (default: 14)
--line-height <n> override line-height multiplier (default: 1.15)
PI PASSTHROUGH
--cwd <path> working directory pi runs in
--pi-arg <arg> pass an extra argument to pi (repeatable)
GENERAL
-q, --quiet suppress pi-shot's own status output
-h, --help show full help
--version print version
Run pi-shot --help for the complete reference with all message kinds and
inline examples.
- PtyRunner spawns
piinsidenode-ptywithCOLORTERM=truecolorand feeds its output into an@xterm/headlessTerminal instance. - SessionBuilder uses pi's exported
SessionManagerto write a real session JSONL file from the compose messages, guaranteeing the shape pi expects. - The runner waits for pi to go idle (no output for
--idle-msms), then any--promptis typed into the editor and any--keysbursts are sent. - HtmlRenderer walks the xterm cell buffer, builds an HTML document with the exact ANSI colors pi emitted, integer-pixel row heights to avoid sub-pixel gaps in colored backgrounds, and auto-trims trailing empty rows.
- Playwright screenshots the HTML and writes the PNG.
The whole pipeline takes 5-10 seconds for a typical capture.
MIT

[ { "kind": "user", "text": "hello" }, { "kind": "assistant", "text": "hi", "thinking": "they greeted me" }, { "kind": "bash", "command": "ls", "output": "file.txt" }, { "kind": "edit", "path": "src/a.ts", "oldText": "foo", "newText": "bar" } ]