| name | terminal-screenshot |
| description | Capture a CLI's real terminal output and render it as an SVG for a README or docs page. Use when asked to add a screenshot, demo, or example output to a README, when a project's README shows fabricated or placeholder output that should be replaced with a real run, or when documenting any command-line tool. Also use before publishing a CLI repo, since a terminal image is the single highest-impact thing a CLI README can have. |
Terminal screenshots for READMEs
A CLI README lives or dies on whether a reader can see the tool working within
two seconds of landing. A fenced code block is fine; an image of the real
output is better, and it is what makes a repo look finished.
This skill uses runshot to run a command, capture what it actually printed
including colour, and render it as an SVG.
It is not on npm. Clone https://github.com/daronthedragon/runshot, run
npm install && npm run build && npm link, and runshot is on PATH.
Never npx runshot - that unscoped name is an unrelated package.
The one rule
Never fabricate output. Run the tool and capture what it really printed.
Invented sample output is the most common failure here and the most damaging:
it looks plausible, it survives review, and it is wrong the moment anyone runs
the thing. If the tool cannot produce real output yet — missing API key, no
test data, not implemented — say so and stop. Do not compose a realistic
looking block and present it as a run.
If the output legitimately needs a stand-in address, key, or path, use one and
label the image as illustrative. That is honest. A fabricated result is not.
Usage
runshot --title mytool -- mytool --demo
Common flags:
| Flag | Use |
|---|
--title <text> | Label in the window chrome. Usually the tool name. |
--prompt <text> | Prepends a fake prompt line so it reads as a session, e.g. $ mytool status |
--cwd <path> | Run the command somewhere else. Use this instead of embedding cd. |
--max-lines <n> | Trim long output. 20-25 lines is the sweet spot for a README. |
--text <path> | Also write the plain-text version, for a fallback block. |
--theme light | For docs sites with a light background. Default dark suits both GitHub themes. |
--stream stdout | When progress chatter on stderr would clutter the image. |
Recommended README pattern
Image first, with the plain text tucked behind a disclosure so it stays
copy-pasteable and reachable for screen readers and anyone the image fails for:
<p align="center">
<img src="assets/demo.svg" width="720" alt="Terminal session showing ...">
</p>
<details>
<summary>Same output as text</summary>
...contents of the --text file...
</details>
Write a real alt describing what the output shows, not "screenshot".
Generate both files in one command so they can never drift apart:
runshot --title mytool --max-lines 22 \
-o assets/demo.svg --text assets/demo.txt -- mytool --demo
Getting colour out of the program
runshot sets FORCE_COLOR, CLICOLOR_FORCE, COLORTERM and TERM, which
covers chalk, picocolors, colorette, supports-color and most Rust and Go CLIs.
If the capture comes out monochrome, the program is checking isatty directly
and ignoring those variables. Options, in order of preference:
- Check for a flag the tool already has (
--color=always is common).
- Capture through a pty yourself and pass the file to
runshot --input.
- Accept monochrome. It still beats a fabricated block.
Checks before committing
- Look at the file. Open the SVG. Do not push an image you have not seen.
- Does anything clip? Every line is pinned with
textLength, so the canvas
fits by construction — but confirm nothing was truncated by --max-lines
mid-sentence.
- Any secrets? Real runs print real data. Scan for API keys, tokens,
internal hostnames, personal addresses and email. Regenerate with redacted
input rather than editing the SVG by hand.
- Is the claim in the README still true? If the caption says "real output",
it must be from a real run of the current code.
When a screenshot is the wrong answer
- Output that is one or two lines. Use a code block.
- Output that changes every run in ways that matter (timestamps, live prices).
Either accept it as a point-in-time snapshot and say so, or use a code block.
- Interactive TUIs. A static frame misrepresents them; record a GIF instead.