Terminal screenshots that replicate your real terminal — your shell prompt, your iTerm2 colors and font, and the program's true colored output. Dark or light mode follows your system automatically.
termrender exec --theme auto --window-bar rings -o demo-basic.png -- echo "Hello, termrender!"Most terminal-to-image tools render text you hand them, with a generic theme and no prompt. termrender instead runs the command in a real PTY and asks your own shell to render its prompt, so the screenshot looks like what you'd actually see in your terminal. The image above was generated with a single command — your real fish prompt, iTerm2 colors, and the command's output, all captured automatically.
Built on termless for headless terminal emulation and PNG rendering.
Requires Bun.
bun install
bun link # registers the global `termrender` command (~/.bun/bin)termrender exec --theme auto -o ls.png \
--window-bar rings -- eza -la
# Pin the prompt style and working directory
termrender exec --prompt fish --cwd ~/some/repo -o git.png -- git log --oneline -5| Option | Default | Description |
|---|---|---|
--prompt <mode> |
auto |
Prompt source: auto, fish, zsh, none. auto resolves the terminal's real shell: iTerm2 profile command → login shell (dscl) → $SHELL |
--cwd <dir> |
cwd | Working dir for the command and the prompt's path/git segments |
--timeout <ms> |
30000 | Max wait for the command to exit |
--no-auto-rows |
— | Keep the terminal's full height instead of trimming empty rows |
--no-trailing-prompt |
— | Don't repeat the prompt after the command exits |
Commands must exit on their own — for servers, watch modes, and TUIs, record a .cast with asciinema and use render.
termrender render recording.cast -o screenshot.png # cols/rows from the cast header
echo -e "\e[32mgreen\e[0m" | termrender render -o out.pngAvailable on both exec and render:
| Option | Default | Description |
|---|---|---|
-o, --output <path> |
output.png |
Output PNG path |
--theme <path|auto> |
system theme | .itermcolors file, auto (live iTerm2 profile), or omit for system dark/light |
--profile <name> |
default profile | iTerm2 profile name for --theme auto |
--cols <n> / --rows <n> |
cast header, else 80×24 | Terminal size |
--font-family <family> |
profile or bundled | CSS font family |
--font-size <n> |
profile or 16 | Font size in px; cell geometry scales with it |
--font-system |
auto | Match arbitrary system fonts (profile fonts, CJK) — ~5× slower. Auto-selected when the terminal text contains wide (CJK) characters |
--padding <n> |
12 | Padding around terminal content |
--border-radius <n> |
8 | Border radius for the terminal frame |
--window-bar <style> |
none | Window bar: rings, colorful, none |
--margin <n> |
0 | Outer image margin |
--margin-fill <color> |
— | Margin fill color (hex) |
| Source | What you get |
|---|---|
(default, no --theme) |
System theme — dark or light mode automatically follows your macOS appearance |
--theme auto |
Your live iTerm2 profile: 16-color palette, fg/bg/cursor, profile font + size. Reads the preferences plist directly (macOS) — no export step |
.itermcolors file |
Full exported iTerm2 color scheme |
termrender uses fish shell's token-level algorithm to color commands, options, pipes, and redirections exactly as fish would:
| Token | Color | Example |
|---|---|---|
| Command | cyan | git, echo |
| Option | cyan | --oneline, -la |
| Pipe/operator | magenta | |, && |
| Redirection | bold cyan | >, >> |
termrender exec --theme auto --window-bar rings -o demo-highlight.png -- git log --oneline -5Without any --theme flag, termrender automatically picks dark or light based on your macOS system appearance. Here it shows a directory listing with your live iTerm2 palette:
termrender exec --theme auto --window-bar rings -o demo-theme.png -- ls -laChoose rings (macOS traffic lights), colorful (solid dots), or none:
termrender exec --theme auto --window-bar colorful -o demo-windowbar.png -- echo "termrender"Use --prompt fish or --prompt zsh to match your shell's prompt. termrender invokes the actual shell to render the prompt line — path abbreviations, git branch detection, and color variables all work as they would in your real terminal:
termrender exec --prompt zsh --theme auto --window-bar rings -o demo-omz.png -- git log --oneline -5Rendering is a single pass: exec starts on a small screen, grows the terminal while output scrolls (so long output is never truncated), shrinks it to the used row count, and renders once. Each render loads only the bundled JetBrains Mono set instead of rescanning every system font.
Measured on macOS (Bun 1.3, Apple M-series):
| Scenario | Before | After |
|---|---|---|
exec echo (steady state) |
~590ms | ~210ms |
200-row render (pinned --rows) |
~2300ms | ~330ms |
| 1500-row output | truncated at 200 rows | complete |
| first render (cold start) | ~2300ms | ~215ms |
bin/termrender-mcp.ts exposes termrender to agents as two MCP tools — render_command and render_text — with the rendered PNG returned inline as an image content block, so the agent sees the result immediately and can self-correct (wrong theme, misaligned columns, …) in the same loop.
| Tool | Description | Key arguments |
|---|---|---|
render_command |
Run a command in a real PTY and screenshot it exactly as the user's terminal would show it | command (argv array), cwd, prompt (auto/fish/zsh/none), timeoutMs, plus common options |
render_text |
Render pre-captured terminal output (ANSI honored) as a styled screenshot | text, plus common options |
Common arguments: theme ("auto" — live iTerm2 profile, "default" — built-in dark, or a path to an .itermcolors file), cols, rows, fontSize, windowBar (none/rings/colorful), fontSystem, outputPath (defaults to a temp file; the image is returned either way).
Claude Code:
claude mcp add --scope user --transport stdio termrender -- bun run /path/to/termrender/bin/termrender-mcp.tsCodex (OpenAI):
codex mcp add termrender -- bun run /path/to/termrender/bin/termrender-mcp.tsOr in ~/.codex/config.toml:
[mcp_servers.termrender]
enabled = true
transport = { type = "stdio", command = "bun", args = ["run", "/path/to/termrender/bin/termrender-mcp.ts"] }OpenCode:
Add to opencode.json (project root) or ~/.config/opencode/opencode.json:
Note: OpenCode uses
"type": "local"(not"stdio") and"command"as an array of strings — this avoids shell injection.
The DeepSeek Harness ships an MCP client bridge (@deepseek-ai/dsh-mcp-client) that mounts one stdio server per config row. Add the entry to $DSH_HOME/config.yaml (or a --config overlay) — the TUI and Web surfaces hot-reload it:
- insert:
- id: mcp-termrender
name: '@deepseek-ai/dsh-mcp-client'
config:
serverName: termrender
transport: stdio
command: /opt/homebrew/bin/bun
args:
- run
- /path/to/termrender/bin/termrender-mcp.ts
cwd: /path/to/termrenderThe agent then sees mcp__termrender__render_command and mcp__termrender__render_text. Note the MCP child runs outside the harness sandbox (dsh's design), and image content blocks appear as placeholders in model context — the full JSON (including the base64 PNG) is preserved for the GUI.
- Real output:
execspawns the command in a PTY (termlessTerminal.spawn, Bun's native terminal support), so programs emit the same colors they would interactively. - Real prompt: the prompt is rendered by the actual shell —
fish -i -c fish_prompt(interactive flag required, or fish skips its color variables), orzsh -ic 'print -rP "$PROMPT"'— with the right--cwd, so path and git segments are live. - Real theme:
--theme autoextracts the default profile fromcom.googlecode.iterm2.plistkey-by-key viaplutil -extract(the plist contains binary blobs that break whole-file conversion). Supports both sRGB and Display P3 color spaces. - Auto-sized images:
execstarts on a small screen and grows the terminal while output scrolls (up to 2000 rows — long output is no longer truncated), then shrinks it to the used row count and renders once. No replay pass. - Fast rendering: screenshots render with the bundled JetBrains Mono set (~130ms) instead of rescanning every system font per image (~600ms+). When the terminal text contains CJK or other wide characters, termrender automatically switches to a system-font scan so those glyphs draw at full width;
--font-system/ MCPfontSystem: trueforces it, andfontSystem: falseforces the bundled set. - System theme:
defaults read -g AppleInterfaceStyletells us whether macOS is in Dark or Light mode; the corresponding built-in theme is used automatically.
- The fast rendering path uses the bundled JetBrains Mono set, which cannot draw CJK glyphs — wide-character text is auto-detected and rendered with system fonts, but an explicit
--font-systemoff (orfontSystem: false) renders those characters narrow. - CJK wide characters render with visible gaps between cells on the fast path (system-font path draws them full width).
--theme autoand prompt detection are macOS-centric (iTerm2 plist,dscl).execoutput is capped at 2000 rows (the automatic-growth limit); for larger listings, pipe the raw output or use--rows.
MIT





{ "mcp": { "termrender": { "type": "local", "command": ["bun", "run", "bin/termrender-mcp.ts"] } } }