Skip to content

CLI Reference

The @termless/cli package is a command-line tool built around the four recording-domain verbsrecord, view, play, compare — plus the config and diagnostic surfaces backends, doctor, themes, and the mcp server.

For the MCP server tool list, see the MCP Reference.

Installation

bash
npm install @termless/cli
bash
bun add @termless/cli
bash
pnpm add @termless/cli
bash
yarn add @termless/cli

Or run directly:

bash
$ bunx termless record -o demo.gif -- bun km view ~/Vault

termless record

Capture a terminal session into a Recording and write it to one or more output files. Alias: termless rec.

record has exactly one output flag — -o. The shape of each -o value picks the mode; there is no separate format flag and no separate screenshot flag.

InvocationOutput
termless record -- <cmd> (no -o)out.gif — a single file in the cwd
termless record -o demos/ -- <cmd>demos/ folder — out.ttyz · out.gif · out.cast · out.tape
termless record -o a.gif -o b.castexactly a.gif + b.cast (-o is repeatable)
termless record -o shot.png -- <cmd>a single still PNG
termless record (no command)shows a help gate, then records a live $SHELL on Enter

The -o extension picks the format; the format decides whether a renderer is involved:

ExtensionFormatRenderer
.gif .apngraster animationthe renderer — auto (see below)
.pngraster stillthe renderer — auto (see below)
.svgvector still/animationnone (vector)
.htmlscrubbable browser viewernone server-side
.ttyz .tty .tape .castrecording datanone

Renderers

A renderer rasterizes captured frames into pixels. --renderer picks one; the default is auto.

RendererHow it worksUse it for
swashRenders the cell grid directly (no SVG) via the pure-Rust swash crate — composites full-colour emoji from their native colour tables. Bundled font chain: JetBrains Mono + Noto Sans Symbols 2 + Symbols Nerd Font + Noto Emoji.The default — highest fidelity.
resvgRenders each frame's SVG via @resvg/resvg-js, handed the same bundled fonts. Clean glyph coverage; no native build.Cross-platform fallback.
canvasRasterizes the SVG through @napi-rs/canvas. Its drawImage path ignores registered fonts, so some glyphs tofu.Niche; prefer resvg.
browserRasterizes each frame's SVG in headless Chromium (Playwright) — a real browser text engine for Chrome-identical shaping, font fallback, ligatures, and colour emoji. Absolute-max fidelity.Premium opt-in: marketing assets + a fidelity oracle against swash/canvas.
autoResolves to swash, falling back to resvg then canvas. Never browser.The default.

auto prefers swash — it consumes the cell grid directly, so colour emoji, Nerd Font icons and box-drawing all render faithfully. Where the swash native binding is unavailable it falls back to resvg, which renders the SVG path with the same bundled fonts.

browser — the premium opt-in tier

browser is the highest-fidelity renderer: it hosts each frame's SVG in a headless-Chromium page and screenshots it, so output matches Chrome exactly (shaping, fallback, ligatures, colour emoji). It is opt-in only and never chosen by auto — Chromium is a hundreds-of-MB dependency, too heavy for a default or CI. Reach for it for one-off README/blog assets, or as a correctness oracle (render the same frame via browser and swash, diff them).

Playwright is an optional dependency — a default bun install omits it and swash/resvg/canvas are unaffected. Install it before using --renderer browser:

bash
bun add -d playwright
npx playwright install chromium

Without playwright, --renderer browser fails fast with that exact install hint.

bash
# Bare record — shows a help gate, then records a live $SHELL on Enter
$ termless record

# Record a command — no -o → out.gif in the cwd
$ termless record -- bun km view ~/Vault

# Folder bundle — a trailing slash writes out.{rec,gif,cast,tape}
$ termless record -o demos/ -- bun km view ~/Vault

# Named files — -o is repeatable; the extension picks the format
$ termless record -o demo.gif -o demo.cast -- bun km view ~/Vault

# A single still PNG
$ termless record -o shot.png -- bun km view ~/Vault

# Force the resvg renderer (the common case never touches --renderer)
$ termless record --renderer resvg -o demo.gif -- bun km view ~/Vault

# Native raster resolution; default is crisp 2x
$ termless record --scale 1 -o demo.gif -- bun km view ~/Vault

# Compat capture: record in a real desktop terminal app (macOS)
$ termless record --compat -o c.png -- bun km view ~/Vault

Options

OptionDescriptionDefault
-o, --output <path...>Output path — extension picks the format, trailing / a folder bundle (repeatable)out.gif
--renderer <kind>Raster renderer: resvg, swash, canvas, browser, or autoauto
-t, --tape <commands>Inline tape commands (scripted mode)--
-b, --backend <name>Backend for scripted modevterm
--cols <n>Terminal columns80
--rows <n>Terminal rows30
--scale <n>Raster resolution multiplier for .gif/.apng/.png1 native, 2 retina2
--timeout <ms>Wait timeout in ms5000
--keys <keys>Comma-separated key names to press, then capture a still--
--wait-for <text>Wait for text before pressing keyscontent
--textPrint terminal text to stdoutoff
--chrome <style>Window chrome on rendered output: none, macos, windowsnone
--title <text>Title text in the window chrome barthe command
--compatCompat capture — record in a real desktop terminal appoff
--terminal <name>Compat terminal app: ghostty, kitty, iterm, terminalauto-detect
--cwd <path>Working directory for the recorded command (with --compat)--

record's defaults are tuned for a README-droppable artifact: the ghostty backend (truecolor + real glyph shaping), 80×30 (GitHub renders README images at ~880px content width), ~12 fps, and a ~300-frame cap so a long session never produces a 50 MB GIF. Power users override every default.

--scale controls raster resolution for .gif, .apng, and .png output. The default 2 writes retina-size artifacts that downscale cleanly in GitHub READMEs. Use --scale 1 for native resolution and smaller files.

Window chrome

--chrome wraps the rendered output in a window frame — the finishing touch for a README or blog image. It composites uniformly across PNG, SVG, and GIF output (the frame is baked into every captured frame).

StyleLook
noneNo chrome — the bare terminal grid (default; byte-identical to omitting the flag)
macosRounded top corners, three traffic-light dots, a soft drop shadow, a small margin
windowsA flat title bar with minimize / maximize / close glyphs

The bar background is derived from the terminal theme (a touch lighter on dark themes, darker on light ones). --title sets the bar's title text; it defaults to the recorded command. The macOS preset centers the title, the Windows preset left-aligns it.

bash
# A framed GIF, ready to drop into a README
$ termless record --chrome macos -o demo.gif -- bun km view ~/Vault

# A Windows-style framed still, with a custom title
$ termless record --chrome windows --title "km — Knowledge Machine" \
    --keys "j,j,Enter" -o shot.png -- bun km view ~/Vault

Compat capture (macOS)

termless record --compat records against the peekaboo backend — it spawns the user's real desktop terminal app (Ghostty / kitty / iTerm / Terminal.app), runs the command after --, screenshots the window, and cleans up. Pixel-perfect for that terminal plus the user's font and theme.

bash
$ termless record --compat -- bun km view ~/Vault
$ termless record --compat --terminal ghostty --cols 140 --rows 40 -o c.png -- bun km

Compat capture is macOS-only and needs a GUI session plus Screen Recording permission. For routine visual iteration use plain record (the resvg renderer) — the compat path is slow and pops a real window.

See Recording Sessions for detailed usage.

termless view

Present a recording — a sealed .ttyz archive or a .tty bundle directory. By default view writes a self-contained, scrubbable viewer.html alongside the recording; with --format gif it animates the recording's frames into a GIF.

bash
# Scrub a recording in the browser (writes viewer.html next to it)
$ termless view ./mysession.ttyz

# Animate a recording to a GIF
$ termless view ./trace --format gif -o demo.gif

Options

OptionDescriptionDefault
-o, --output <path>Output file for --format--
--format <type>Animate the recording to a file: gifscrub

Writing a GIF is not a separate "export" command — it is view with an animation format and a file sink. See Tracing Visual Bugs for the scrubbable viewer.

termless play

Re-execute a recording into a terminal. Accepts any recording format. Produce terminal text, screenshots, or animated output.

bash
# Play a recording (prints terminal text)
$ termless play demo.tape
$ termless play demo.cast
$ termless play demo.ttyz
$ termless play --source=io demo.tty

# Generate an animated GIF
$ termless play -o demo.gif demo.tape

# Generate an animated SVG
$ termless play -o demo.svg demo.tape

Options

OptionDescriptionDefault
-o, --output <path>Output file, format detected from extension--
-b, --backend <name>Backend(s), comma-separated; use all for every ready backendvterm
--compare <mode>Comparison mode — a thin alias for termless compare--
--cols <n>Override terminal columns--
--rows <n>Override terminal rows--
--source <track>Native source: auto, commands, or ioauto

For .tty and .ttyz, auto prefers non-empty commands. If none exist, play prints replaying from io (no commands present) and replays the io track. Forcing commands or io refuses when the requested track is absent or empty. Other input formats reject --source instead of silently ignoring it.

termless compare

Play one recording across two or more backends and diff the results — "does my TUI look the same in every terminal?" compare shares its execution core with play; play --compare <mode> is a thin alias for it.

bash
# Side-by-side comparison
$ termless compare demo.tape -b vterm,ghostty --compare side-by-side -o comparison.svg

# Compare every installed, ready backend
$ termless compare demo.tape -b all --compare grid -o all-backends.svg

# Diff mode — pixel-diff overlays against the baseline backend
$ termless compare demo.tape -b vterm,ghostty --compare diff -o diff.svg

Options

OptionDescriptionDefault
-b, --backend <name>Backend(s), comma-separated; use all for every ready backendvterm
--compare <mode>separate, side-by-side, grid, diff--
-o, --output <path>Output file or directory--

See Recording Sessions for the comparison modes.

Backend Management

Manage backend installation with Playwright-inspired commands. Backend versions are pinned in backends.json — upgrading termless upgrades all backends together.

termless backends

List all available backends with their type, install status, and version. termless backends list is the same command.

bash
$ termless backends
$ termless backends list

termless backends install

Install backends. With no arguments, installs the default set (xtermjs, ghostty, vt100).

bash
$ termless backends install
$ termless backends install ghostty
$ termless backends install ghostty alacritty

termless backends update

Check upstream registries (npm, crates.io, GitHub) for newer backend versions, compared against the versions pinned in backends.json.

bash
$ termless backends update           # dry run
$ termless backends update --apply   # apply to backends.json

termless doctor

Check installation health: verify installed backends load correctly, detect version mismatches, and report missing dependencies.

bash
$ termless doctor

termless themes

List the color themes available for recording screenshots and animations.

bash
$ termless themes

Use a theme with termless play --theme dracula demo.tape, or Set Theme "dracula" inside a .tape file.

termless mcp

Start a stdio MCP server for AI agents (Claude Code, etc.).

bash
$ termless mcp

The full tool list and Claude Code integration are documented in the MCP Reference.

Session Manager (Programmatic)

The session manager is also available as a library:

typescript
import { createSessionManager } from "@termless/cli"

const manager = createSessionManager()

const { id, terminal } = await manager.createSession({
  command: ["my-app"],
  cols: 120,
  rows: 40,
  waitFor: "ready>",
  timeout: 5000,
})

terminal.press("ArrowDown")
await terminal.waitForStable()
console.log(terminal.getText())

await manager.stopSession(id)
// or: await manager.stopAll()

createSession(options?)

OptionTypeDefaultDescription
commandstring[]--Command to spawn
envRecord<string, string>--Additional env vars
cwdstring--Working directory
colsnumber120Terminal columns
rowsnumber40Terminal rows
waitForstring | "content" | "stable""content"What to wait for after spawn
timeoutnumber5000Wait timeout in ms

getSession(id)

Get a Terminal instance by session ID. Throws if not found.

listSessions()

Returns an array of { id, command, cols, rows, alive } for all sessions.

stopSession(id) / stopAll()

Close the terminal and kill the process for one or all sessions.

Key Names

Keys use the same format as terminal.press():

  • Single characters: a, 1, /
  • Named keys: Enter, Tab, Backspace, Delete, Escape, Space
  • Arrow keys: ArrowUp, ArrowDown, ArrowLeft, ArrowRight
  • Navigation: Home, End, PageUp, PageDown
  • Function keys: F1 through F12
  • With modifiers: Ctrl+c, Shift+Tab, Alt+x