Reproducible screenshot capture for docs — drive a web app or CLI from a declarative shot list and capture polished feature screenshots.
Project description
capture
Screenshots for your docs — as code. One committed shot list captures your web pages, your real terminal windows, and stateful CLI sessions — and regenerates them all with a single command.
The problem
Documenting a feature means launching the app, clicking to the right state, screenshotting, naming the file, and embedding it — every time the UI changes. The screenshots drift out of date the moment you ship, and nobody notices until they're embarrassingly wrong.
capture makes them reproducible: describe how to start your app and what
to shoot once, in a committed .capture.yaml, then regenerate the whole set on
demand — locally or in CI. Same config + same app state → same screenshots.
Quickstart
pip install shotlist # installs the `capture` command
playwright install chromium # one-time browser download
capture init # writes a starter .capture.yaml
capture run # boots your app, captures every shot, tears it all down
One shot list, four kinds of shot
output:
dir: docs/screenshots
readme: README.md # optional: splice <img> snippets straight into the README
app: # optional — omit for static sites or pure-CLI shots
command: "npm run dev"
ready: { url: http://localhost:5173, timeout: 30 } # never shoot a half-booted app
shots:
- { name: dashboard, kind: web, url: http://localhost:5173/dashboard, full_page: true, alt: "Dashboard" }
- { name: cli-help, kind: cli, command: "mytool --help", alt: "Top-level help" }
| Kind | Captures | How |
|---|---|---|
web |
a browser page — with optional click/fill/wait steps first | Playwright / Chromium |
cli · native (macOS default) |
a real screenshot of your Terminal.app window — your font, your theme | AppleScript + screencapture |
cli · rendered (any OS, CI-safe) |
the command's output drawn as a styled terminal card | PTY → ANSI→HTML → Chromium |
session |
a stateful, multi-command flow in one persistent terminal — one shot per step | one Terminal window, captured after each step |
A session is how you screenshot a flow whose later steps depend on earlier ones —
the shell state (cwd, env, background processes) carries across. Background a
long-running process with & and a small wait_ms, keep capturing, and the
session tears it down on close.
Recipes
Copy-paste .capture.yaml configs for the common jobs — test-evidence proofs, CI
regeneration, long-running servers, web flows with interactions, versioned visual
history — live in docs/recipes.md.
Proof reports & pipelines
Every capture run also writes, next to the PNGs:
index.html— a self-contained gallery you can open and share as a proof report;manifest.json— a machine-readable record of the run (a pipeline artifact).
Attach manifest.json to a CI job, or open index.html as test-evidence. Gate CI
with capture check — it re-captures and fails when a screenshot drifts from
the committed baseline (capture check --update to accept intended changes; add
--diff DIR to render baseline·current·diff images) — or drop in the bundled
GitHub Action. Turn the report off with --no-report (or output.report: false). Details in docs/pipeline.md.
Why capture, and not the others
The pieces exist in isolation; capture is the one tool that does all of it under
a single committed config.
| web pages | real terminal | CLI sessions | README auto-embed | reproducible / CI | |
|---|---|---|---|---|---|
| capture | ✅ | ✅ | ✅ | ✅ | ✅ |
| shot-scraper | ✅ | ❌ | ❌ | ❌ | ✅ |
| freeze / carbon | ❌ | synthetic | ❌ | ❌ | ✅ |
| Percy / Chromatic | ✅ | ❌ | ❌ | ❌ | ✅ (cloud, paid) |
| doing it by hand | 😖 | 😖 | 😖 | ❌ | ❌ |
No cloud, no paid services, no special OS permissions for web/rendered shots. (Native Terminal capture needs macOS Screen-Recording permission; everything else needs nothing.)
How it works
.capture.yaml ─► load + validate ─► [ boot app, wait until ready ] ─► one engine
routes each
shot by kind:
web ───────► Playwright / Chromium
cli·native ► a real Terminal.app window
cli·render ► PTY → ANSI→HTML → Chromium
session ───► one persistent Terminal, a shot per step
─► NN-name.png
+ README splice
The clever part is what isn't here: no AI runs at capture time. Claude's only
job is to author the .capture.yaml once by reading your repo; after that the
engine is a plain, deterministic program — fast, free, and re-runnable in CI with
no model in the loop. See the full design in docs/design.md.
Robust by design. The readiness probe (HTTP / TCP port / log line) means you never screenshot a half-booted app, and the app is launched in its own process group and torn down — even on a crash or Ctrl-C — so a capture run never leaves an orphaned dev server behind.
Capture, captured by capture
This repo dogfoods itself: the shots below are produced by running capture run
on its own .capture.yaml and spliced in automatically.
The capture CLI
Run options
Use with Claude
capture ships an optional Claude integration in integrations/claude/:
- a
/captureskill that inspects your repo (routes,--help, README), writes the.capture.yamlfor you, and runs it; - an optional auto-snapshot hook that drops a raw snapshot when a dev server
starts (the honest "dumb snapshot"; the curated set always comes from
capture run).
Commands
| Command | What it does |
|---|---|
capture init |
Scaffold a starter .capture.yaml |
capture validate |
Check the shot list is well-formed |
capture run |
Capture every shot and write outputs |
capture run --only dashboard |
Capture a single shot by name |
capture run --version v2 |
Write into a versioned subfolder |
capture check |
Fail if a screenshot drifted from the committed baseline |
capture check --update |
Re-shoot and accept the current screenshots as the baseline |
capture check --diff DIR |
Also render baseline·current·diff images for changed shots |
Develop
git clone https://github.com/varmabudharaju/capture && cd capture
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
playwright install chromium
pytest # the suite is fully offline
The hero GIF is itself reproducible — demo.tape + vhs demo.tape.
License
MIT © Varma Budharaju
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file shotlist-0.1.0.tar.gz.
File metadata
- Download URL: shotlist-0.1.0.tar.gz
- Upload date:
- Size: 47.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8d63d017fde96462dcda929b51a910750b8f8141569f9f0cb74f5dd558cba2af
|
|
| MD5 |
82b43bc096c9d221567ca75cfbe87033
|
|
| BLAKE2b-256 |
cb3e2c5d40bd4f18c564fa40e66a18913f825d9eabb02abb0271f6bec6714dd8
|
Provenance
The following attestation bundles were made for shotlist-0.1.0.tar.gz:
Publisher:
publish.yml on varmabudharaju/capture
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
shotlist-0.1.0.tar.gz -
Subject digest:
8d63d017fde96462dcda929b51a910750b8f8141569f9f0cb74f5dd558cba2af - Sigstore transparency entry: 1960030203
- Sigstore integration time:
-
Permalink:
varmabudharaju/capture@20e043ab22c3df95e680700ed319898c95e81639 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/varmabudharaju
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@20e043ab22c3df95e680700ed319898c95e81639 -
Trigger Event:
release
-
Statement type:
File details
Details for the file shotlist-0.1.0-py3-none-any.whl.
File metadata
- Download URL: shotlist-0.1.0-py3-none-any.whl
- Upload date:
- Size: 30.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2a0c81e2ac1cd226d5f0773785873e35797a25b427bd67001f14d8cff7c76112
|
|
| MD5 |
1e9db274cb68a17f9b6b7da6186ed14f
|
|
| BLAKE2b-256 |
df9ea83ba013bfeacf7e52f3e40d65c6bc1b55941dcd556850b0c27f77bfe334
|
Provenance
The following attestation bundles were made for shotlist-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on varmabudharaju/capture
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
shotlist-0.1.0-py3-none-any.whl -
Subject digest:
2a0c81e2ac1cd226d5f0773785873e35797a25b427bd67001f14d8cff7c76112 - Sigstore transparency entry: 1960030392
- Sigstore integration time:
-
Permalink:
varmabudharaju/capture@20e043ab22c3df95e680700ed319898c95e81639 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/varmabudharaju
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@20e043ab22c3df95e680700ed319898c95e81639 -
Trigger Event:
release
-
Statement type: