shotlist 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. shotlist-0.1.0/.capture.yaml +22 -0
  2. shotlist-0.1.0/.github/workflows/ci.yml +35 -0
  3. shotlist-0.1.0/.github/workflows/publish.yml +45 -0
  4. shotlist-0.1.0/.gitignore +22 -0
  5. shotlist-0.1.0/CONTRIBUTING.md +58 -0
  6. shotlist-0.1.0/LICENSE +21 -0
  7. shotlist-0.1.0/PKG-INFO +203 -0
  8. shotlist-0.1.0/README.md +177 -0
  9. shotlist-0.1.0/action.yml +28 -0
  10. shotlist-0.1.0/demo.tape +39 -0
  11. shotlist-0.1.0/docs/design.md +153 -0
  12. shotlist-0.1.0/docs/pipeline.md +116 -0
  13. shotlist-0.1.0/docs/recipes.md +161 -0
  14. shotlist-0.1.0/integrations/claude/README.md +61 -0
  15. shotlist-0.1.0/integrations/claude/hooks/README.md +72 -0
  16. shotlist-0.1.0/integrations/claude/hooks/auto-snapshot.sh +21 -0
  17. shotlist-0.1.0/integrations/claude/skills/capture/SKILL.md +237 -0
  18. shotlist-0.1.0/pyproject.toml +71 -0
  19. shotlist-0.1.0/src/capture/__init__.py +8 -0
  20. shotlist-0.1.0/src/capture/backends/__init__.py +6 -0
  21. shotlist-0.1.0/src/capture/backends/cli.py +96 -0
  22. shotlist-0.1.0/src/capture/backends/native_terminal.py +234 -0
  23. shotlist-0.1.0/src/capture/backends/web.py +38 -0
  24. shotlist-0.1.0/src/capture/check.py +65 -0
  25. shotlist-0.1.0/src/capture/cli.py +258 -0
  26. shotlist-0.1.0/src/capture/config.py +201 -0
  27. shotlist-0.1.0/src/capture/diff.py +123 -0
  28. shotlist-0.1.0/src/capture/engine.py +192 -0
  29. shotlist-0.1.0/src/capture/lifecycle.py +165 -0
  30. shotlist-0.1.0/src/capture/output.py +135 -0
  31. shotlist-0.1.0/src/capture/render.py +39 -0
  32. shotlist-0.1.0/src/capture/report.py +173 -0
  33. shotlist-0.1.0/tests/__init__.py +0 -0
  34. shotlist-0.1.0/tests/conftest.py +23 -0
  35. shotlist-0.1.0/tests/test_check.py +97 -0
  36. shotlist-0.1.0/tests/test_cli_app.py +213 -0
  37. shotlist-0.1.0/tests/test_cli_backend.py +65 -0
  38. shotlist-0.1.0/tests/test_config.py +290 -0
  39. shotlist-0.1.0/tests/test_diff.py +50 -0
  40. shotlist-0.1.0/tests/test_engine.py +281 -0
  41. shotlist-0.1.0/tests/test_lifecycle.py +76 -0
  42. shotlist-0.1.0/tests/test_native_terminal.py +123 -0
  43. shotlist-0.1.0/tests/test_output.py +183 -0
  44. shotlist-0.1.0/tests/test_report.py +110 -0
  45. shotlist-0.1.0/tests/test_web.py +70 -0
@@ -0,0 +1,22 @@
1
+ # capture dogfoods itself: these shots screenshot capture's own CLI for the README.
2
+ # Regenerate them any time with: capture run
3
+ output:
4
+ dir: docs/screenshots
5
+ readme: README.md
6
+
7
+ shots:
8
+ - name: The capture CLI
9
+ kind: cli
10
+ command: ".venv/bin/capture --help"
11
+ cols: 90
12
+ rows: 16
13
+ style: native # a real screenshot of Terminal.app (macOS)
14
+ alt: "capture --help showing the init, validate, and run commands"
15
+
16
+ - name: Run options
17
+ kind: cli
18
+ command: ".venv/bin/capture run --help"
19
+ cols: 90
20
+ rows: 20
21
+ style: native # a real screenshot of Terminal.app (macOS)
22
+ alt: "capture run options: --config, --only, and --version"
@@ -0,0 +1,35 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ jobs:
8
+ test:
9
+ runs-on: ubuntu-latest
10
+ strategy:
11
+ fail-fast: false
12
+ matrix:
13
+ python-version: ["3.11", "3.12"]
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+
17
+ - uses: actions/setup-python@v5
18
+ with:
19
+ python-version: ${{ matrix.python-version }}
20
+ cache: pip
21
+
22
+ - name: Install dependencies
23
+ run: pip install -e ".[dev]"
24
+
25
+ - name: Install Playwright Chromium
26
+ run: python -m playwright install --with-deps chromium
27
+
28
+ - name: Lint (ruff)
29
+ run: ruff check src tests
30
+
31
+ - name: Type check (mypy)
32
+ run: mypy src tests
33
+
34
+ - name: Test (pytest)
35
+ run: python -m pytest -q
@@ -0,0 +1,45 @@
1
+ name: Publish to PyPI
2
+
3
+ # Publishes to PyPI when a GitHub Release is published, using PyPI Trusted
4
+ # Publishing (OIDC) — no API token or secret is stored in the repo.
5
+ on:
6
+ release:
7
+ types: [published]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ build:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+
18
+ - uses: actions/setup-python@v5
19
+ with:
20
+ python-version: "3.11"
21
+
22
+ - name: Build sdist + wheel
23
+ run: |
24
+ python -m pip install --upgrade build twine
25
+ python -m build
26
+ python -m twine check dist/*
27
+
28
+ - uses: actions/upload-artifact@v4
29
+ with:
30
+ name: dist
31
+ path: dist/
32
+
33
+ publish:
34
+ needs: build
35
+ runs-on: ubuntu-latest
36
+ permissions:
37
+ id-token: write # required for Trusted Publishing (OIDC)
38
+ steps:
39
+ - uses: actions/download-artifact@v4
40
+ with:
41
+ name: dist
42
+ path: dist/
43
+
44
+ - name: Publish to PyPI
45
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,22 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ build/
6
+ dist/
7
+
8
+ # Environments
9
+ .venv/
10
+ venv/
11
+ .env
12
+
13
+ # Tooling caches
14
+ .pytest_cache/
15
+ .ruff_cache/
16
+ .mypy_cache/
17
+
18
+ # OS
19
+ .DS_Store
20
+
21
+ # Auto-snapshot scratch output (the curated set under docs/screenshots/ IS committed)
22
+ docs/screenshots/_auto/
@@ -0,0 +1,58 @@
1
+ # Contributing to capture
2
+
3
+ Thanks for your interest in improving `capture`. This guide covers local setup
4
+ and the checks every change must pass.
5
+
6
+ ## Dev setup
7
+
8
+ `capture` targets Python 3.11+. From the repository root:
9
+
10
+ ```bash
11
+ python3 -m venv .venv
12
+ source .venv/bin/activate
13
+ pip install -e ".[dev]"
14
+ playwright install chromium
15
+ ```
16
+
17
+ The editable install (`-e`) means source edits take effect immediately. The
18
+ `[dev]` extra pulls in `pytest`, `ruff`, and `mypy`. Both backends render through
19
+ Playwright/Chromium, so `playwright install chromium` is required before the
20
+ tests can run — there are no other external binaries or OS permissions to set up.
21
+
22
+ ## Running the checks
23
+
24
+ Run all three from the repository root (with the virtualenv activated):
25
+
26
+ ```bash
27
+ ruff check src tests # lint + import order
28
+ mypy src tests # strict type checking
29
+ pytest # test suite
30
+ ```
31
+
32
+ CI runs the same commands on Python 3.11 and 3.12 (see
33
+ [`.github/workflows/ci.yml`](.github/workflows/ci.yml)); a green local run
34
+ should mean a green CI run.
35
+
36
+ ### Tooling notes
37
+
38
+ - **ruff** — line length 100; rule sets `E, F, I, UP, B, C4, SIM` (configured in
39
+ `pyproject.toml`). Let `ruff check --fix` handle import sorting.
40
+ - **mypy** — runs in `strict` mode. Every function needs full type annotations,
41
+ and every module needs a docstring.
42
+ - Use modern syntax: `X | Y` unions (not `typing.Union`), `list[...]` /
43
+ `dict[...]` builtins, and `from __future__` is unnecessary on 3.11+.
44
+
45
+ ## Development style
46
+
47
+ `capture` is built test-first (TDD): tests live in `tests/` alongside the module
48
+ they exercise — `test_config.py` for `config.py`, `test_lifecycle.py` for
49
+ `lifecycle.py`, `test_web.py` / `test_cli_backend.py` for the backends, and so
50
+ on. When adding a feature or fixing a bug:
51
+
52
+ 1. Write a failing test that captures the desired behaviour.
53
+ 2. Implement the smallest change that makes it pass.
54
+ 3. Run `ruff check src tests`, `mypy src tests`, and `pytest` before opening a PR.
55
+
56
+ Keep changes focused, match the surrounding conventions (Pydantic `_Strict`
57
+ models, clear error types, module + function docstrings), and make sure the full
58
+ check suite is green.
shotlist-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Varma Budharaju
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,203 @@
1
+ Metadata-Version: 2.4
2
+ Name: shotlist
3
+ Version: 0.1.0
4
+ Summary: Reproducible screenshot capture for docs — drive a web app or CLI from a declarative shot list and capture polished feature screenshots.
5
+ Project-URL: Homepage, https://github.com/varmabudharaju/capture
6
+ Project-URL: Repository, https://github.com/varmabudharaju/capture
7
+ Project-URL: Issues, https://github.com/varmabudharaju/capture/issues
8
+ Author-email: Varma Budharaju <sairam.vzf33@gmail.com>
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: cli,documentation,playwright,readme,screenshots
12
+ Requires-Python: >=3.11
13
+ Requires-Dist: ansi2html>=1.9
14
+ Requires-Dist: pillow>=10.0
15
+ Requires-Dist: playwright>=1.40
16
+ Requires-Dist: pydantic>=2.6
17
+ Requires-Dist: pyyaml>=6.0
18
+ Requires-Dist: rich>=13.7
19
+ Requires-Dist: typer>=0.12
20
+ Provides-Extra: dev
21
+ Requires-Dist: mypy>=1.10; extra == 'dev'
22
+ Requires-Dist: pytest>=8.0; extra == 'dev'
23
+ Requires-Dist: ruff>=0.5; extra == 'dev'
24
+ Requires-Dist: types-pyyaml; extra == 'dev'
25
+ Description-Content-Type: text/markdown
26
+
27
+ # capture
28
+
29
+ [![CI](https://github.com/varmabudharaju/capture/actions/workflows/ci.yml/badge.svg)](https://github.com/varmabudharaju/capture/actions/workflows/ci.yml)
30
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue)](https://www.python.org/downloads/)
31
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE)
32
+
33
+ **Screenshots for your docs — as code.** One committed shot list captures your
34
+ web pages, your *real* terminal windows, and stateful CLI sessions — and
35
+ regenerates them all with a single command.
36
+
37
+ <img src="https://raw.githubusercontent.com/varmabudharaju/capture/main/docs/demo.gif" width="100%" alt="The old way: dragging Screen Shot 2026-... files into ever-more-cursed filenames, then shipping a UI tweak that makes them all stale. The capture way: one `capture run`."/>
38
+
39
+ ## The problem
40
+
41
+ Documenting a feature means launching the app, clicking to the right state,
42
+ screenshotting, naming the file, and embedding it — **every time the UI changes.**
43
+ The screenshots drift out of date the moment you ship, and nobody notices until
44
+ they're embarrassingly wrong.
45
+
46
+ `capture` makes them **reproducible**: describe *how to start your app* and *what
47
+ to shoot* once, in a committed `.capture.yaml`, then regenerate the whole set on
48
+ demand — locally or in CI. Same config + same app state → same screenshots.
49
+
50
+ ## Quickstart
51
+
52
+ ```bash
53
+ pip install shotlist # installs the `capture` command
54
+ playwright install chromium # one-time browser download
55
+
56
+ capture init # writes a starter .capture.yaml
57
+ capture run # boots your app, captures every shot, tears it all down
58
+ ```
59
+
60
+ ## One shot list, four kinds of shot
61
+
62
+ ```yaml
63
+ output:
64
+ dir: docs/screenshots
65
+ readme: README.md # optional: splice <img> snippets straight into the README
66
+
67
+ app: # optional — omit for static sites or pure-CLI shots
68
+ command: "npm run dev"
69
+ ready: { url: http://localhost:5173, timeout: 30 } # never shoot a half-booted app
70
+
71
+ shots:
72
+ - { name: dashboard, kind: web, url: http://localhost:5173/dashboard, full_page: true, alt: "Dashboard" }
73
+ - { name: cli-help, kind: cli, command: "mytool --help", alt: "Top-level help" }
74
+ ```
75
+
76
+ | Kind | Captures | How |
77
+ | --- | --- | --- |
78
+ | **`web`** | a browser page — with optional click/fill/wait steps first | Playwright / Chromium |
79
+ | **`cli` · `native`** *(macOS default)* | a **real screenshot of your Terminal.app window** — your font, your theme | AppleScript + `screencapture` |
80
+ | **`cli` · `rendered`** *(any OS, CI-safe)* | the command's output drawn as a styled terminal card | PTY → ANSI→HTML → Chromium |
81
+ | **`session`** | a **stateful, multi-command flow** in one persistent terminal — one shot per step | one Terminal window, captured after each step |
82
+
83
+ A `session` is how you screenshot a flow whose later steps depend on earlier ones —
84
+ the shell state (cwd, env, background processes) carries across. Background a
85
+ long-running process with `&` and a small `wait_ms`, keep capturing, and the
86
+ session tears it down on close.
87
+
88
+ ## Recipes
89
+
90
+ Copy-paste `.capture.yaml` configs for the common jobs — test-evidence proofs, CI
91
+ regeneration, long-running servers, web flows with interactions, versioned visual
92
+ history — live in **[`docs/recipes.md`](docs/recipes.md)**.
93
+
94
+ ## Proof reports & pipelines
95
+
96
+ Every `capture run` also writes, next to the PNGs:
97
+
98
+ - **`index.html`** — a self-contained gallery you can open and share as a **proof report**;
99
+ - **`manifest.json`** — a machine-readable record of the run (a pipeline artifact).
100
+
101
+ <img src="https://raw.githubusercontent.com/varmabudharaju/capture/main/docs/proof-report.png" width="100%" alt="The generated index.html gallery: a header with the shot count and timestamp, then a card per shot showing the screenshot, its name, a kind badge, and its alt text."/>
102
+
103
+ Attach `manifest.json` to a CI job, or open `index.html` as test-evidence. Gate CI
104
+ with **`capture check`** — it re-captures and fails when a screenshot drifts from
105
+ the committed baseline (`capture check --update` to accept intended changes; add
106
+ `--diff DIR` to render baseline·current·diff images) — or drop in the bundled
107
+ **GitHub Action**. Turn the report off with `--no-report` (or `output.report:
108
+ false`). Details in **[`docs/pipeline.md`](docs/pipeline.md)**.
109
+
110
+ ## Why capture, and not the others
111
+
112
+ The pieces exist in isolation; `capture` is the one tool that does all of it under
113
+ a single committed config.
114
+
115
+ | | web pages | real terminal | CLI sessions | README auto-embed | reproducible / CI |
116
+ | --- | :---: | :---: | :---: | :---: | :---: |
117
+ | **capture** | ✅ | ✅ | ✅ | ✅ | ✅ |
118
+ | shot-scraper | ✅ | ❌ | ❌ | ❌ | ✅ |
119
+ | freeze / carbon | ❌ | synthetic | ❌ | ❌ | ✅ |
120
+ | Percy / Chromatic | ✅ | ❌ | ❌ | ❌ | ✅ (cloud, paid) |
121
+ | doing it by hand | 😖 | 😖 | 😖 | ❌ | ❌ |
122
+
123
+ No cloud, no paid services, no special OS permissions for web/rendered shots.
124
+ (Native Terminal capture needs macOS Screen-Recording permission; everything else
125
+ needs nothing.)
126
+
127
+ ## How it works
128
+
129
+ ```
130
+ .capture.yaml ─► load + validate ─► [ boot app, wait until ready ] ─► one engine
131
+ routes each
132
+ shot by kind:
133
+ web ───────► Playwright / Chromium
134
+ cli·native ► a real Terminal.app window
135
+ cli·render ► PTY → ANSI→HTML → Chromium
136
+ session ───► one persistent Terminal, a shot per step
137
+ ─► NN-name.png
138
+ + README splice
139
+ ```
140
+
141
+ The clever part is what *isn't* here: **no AI runs at capture time.** Claude's only
142
+ job is to *author* the `.capture.yaml` once by reading your repo; after that the
143
+ engine is a plain, deterministic program — fast, free, and re-runnable in CI with
144
+ no model in the loop. See the full design in [`docs/design.md`](docs/design.md).
145
+
146
+ **Robust by design.** The readiness probe (HTTP / TCP port / log line) means you
147
+ never screenshot a half-booted app, and the app is launched in its own process
148
+ group and torn down — even on a crash or Ctrl-C — so a capture run never leaves an
149
+ orphaned dev server behind.
150
+
151
+ ## Capture, captured by capture
152
+
153
+ This repo dogfoods itself: the shots below are produced by running `capture run`
154
+ on its own [`.capture.yaml`](.capture.yaml) and spliced in automatically.
155
+
156
+ <!-- capture:start -->
157
+ ### The capture CLI
158
+
159
+ <img src="https://raw.githubusercontent.com/varmabudharaju/capture/main/docs/screenshots/01-the-capture-cli.png" width="100%" alt="capture --help showing the init, validate, and run commands"/>
160
+
161
+ ### Run options
162
+
163
+ <img src="https://raw.githubusercontent.com/varmabudharaju/capture/main/docs/screenshots/02-run-options.png" width="100%" alt="capture run options: --config, --only, and --version"/>
164
+
165
+ <!-- capture:end -->
166
+
167
+ ## Use with Claude
168
+
169
+ `capture` ships an optional Claude integration in [`integrations/claude/`](integrations/claude/):
170
+
171
+ - a **`/capture` skill** that inspects your repo (routes, `--help`, README), writes
172
+ the `.capture.yaml` for you, and runs it;
173
+ - an optional **auto-snapshot hook** that drops a raw snapshot when a dev server
174
+ starts (the honest "dumb snapshot"; the curated set always comes from `capture run`).
175
+
176
+ ## Commands
177
+
178
+ | Command | What it does |
179
+ | --- | --- |
180
+ | `capture init` | Scaffold a starter `.capture.yaml` |
181
+ | `capture validate` | Check the shot list is well-formed |
182
+ | `capture run` | Capture every shot and write outputs |
183
+ | `capture run --only dashboard` | Capture a single shot by name |
184
+ | `capture run --version v2` | Write into a versioned subfolder |
185
+ | `capture check` | Fail if a screenshot drifted from the committed baseline |
186
+ | `capture check --update` | Re-shoot and accept the current screenshots as the baseline |
187
+ | `capture check --diff DIR` | Also render baseline·current·diff images for changed shots |
188
+
189
+ ## Develop
190
+
191
+ ```bash
192
+ git clone https://github.com/varmabudharaju/capture && cd capture
193
+ python3 -m venv .venv && source .venv/bin/activate
194
+ pip install -e ".[dev]"
195
+ playwright install chromium
196
+ pytest # the suite is fully offline
197
+ ```
198
+
199
+ The hero GIF is itself reproducible — [`demo.tape`](demo.tape) + `vhs demo.tape`.
200
+
201
+ ## License
202
+
203
+ MIT © Varma Budharaju
@@ -0,0 +1,177 @@
1
+ # capture
2
+
3
+ [![CI](https://github.com/varmabudharaju/capture/actions/workflows/ci.yml/badge.svg)](https://github.com/varmabudharaju/capture/actions/workflows/ci.yml)
4
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue)](https://www.python.org/downloads/)
5
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE)
6
+
7
+ **Screenshots for your docs — as code.** One committed shot list captures your
8
+ web pages, your *real* terminal windows, and stateful CLI sessions — and
9
+ regenerates them all with a single command.
10
+
11
+ <img src="https://raw.githubusercontent.com/varmabudharaju/capture/main/docs/demo.gif" width="100%" alt="The old way: dragging Screen Shot 2026-... files into ever-more-cursed filenames, then shipping a UI tweak that makes them all stale. The capture way: one `capture run`."/>
12
+
13
+ ## The problem
14
+
15
+ Documenting a feature means launching the app, clicking to the right state,
16
+ screenshotting, naming the file, and embedding it — **every time the UI changes.**
17
+ The screenshots drift out of date the moment you ship, and nobody notices until
18
+ they're embarrassingly wrong.
19
+
20
+ `capture` makes them **reproducible**: describe *how to start your app* and *what
21
+ to shoot* once, in a committed `.capture.yaml`, then regenerate the whole set on
22
+ demand — locally or in CI. Same config + same app state → same screenshots.
23
+
24
+ ## Quickstart
25
+
26
+ ```bash
27
+ pip install shotlist # installs the `capture` command
28
+ playwright install chromium # one-time browser download
29
+
30
+ capture init # writes a starter .capture.yaml
31
+ capture run # boots your app, captures every shot, tears it all down
32
+ ```
33
+
34
+ ## One shot list, four kinds of shot
35
+
36
+ ```yaml
37
+ output:
38
+ dir: docs/screenshots
39
+ readme: README.md # optional: splice <img> snippets straight into the README
40
+
41
+ app: # optional — omit for static sites or pure-CLI shots
42
+ command: "npm run dev"
43
+ ready: { url: http://localhost:5173, timeout: 30 } # never shoot a half-booted app
44
+
45
+ shots:
46
+ - { name: dashboard, kind: web, url: http://localhost:5173/dashboard, full_page: true, alt: "Dashboard" }
47
+ - { name: cli-help, kind: cli, command: "mytool --help", alt: "Top-level help" }
48
+ ```
49
+
50
+ | Kind | Captures | How |
51
+ | --- | --- | --- |
52
+ | **`web`** | a browser page — with optional click/fill/wait steps first | Playwright / Chromium |
53
+ | **`cli` · `native`** *(macOS default)* | a **real screenshot of your Terminal.app window** — your font, your theme | AppleScript + `screencapture` |
54
+ | **`cli` · `rendered`** *(any OS, CI-safe)* | the command's output drawn as a styled terminal card | PTY → ANSI→HTML → Chromium |
55
+ | **`session`** | a **stateful, multi-command flow** in one persistent terminal — one shot per step | one Terminal window, captured after each step |
56
+
57
+ A `session` is how you screenshot a flow whose later steps depend on earlier ones —
58
+ the shell state (cwd, env, background processes) carries across. Background a
59
+ long-running process with `&` and a small `wait_ms`, keep capturing, and the
60
+ session tears it down on close.
61
+
62
+ ## Recipes
63
+
64
+ Copy-paste `.capture.yaml` configs for the common jobs — test-evidence proofs, CI
65
+ regeneration, long-running servers, web flows with interactions, versioned visual
66
+ history — live in **[`docs/recipes.md`](docs/recipes.md)**.
67
+
68
+ ## Proof reports & pipelines
69
+
70
+ Every `capture run` also writes, next to the PNGs:
71
+
72
+ - **`index.html`** — a self-contained gallery you can open and share as a **proof report**;
73
+ - **`manifest.json`** — a machine-readable record of the run (a pipeline artifact).
74
+
75
+ <img src="https://raw.githubusercontent.com/varmabudharaju/capture/main/docs/proof-report.png" width="100%" alt="The generated index.html gallery: a header with the shot count and timestamp, then a card per shot showing the screenshot, its name, a kind badge, and its alt text."/>
76
+
77
+ Attach `manifest.json` to a CI job, or open `index.html` as test-evidence. Gate CI
78
+ with **`capture check`** — it re-captures and fails when a screenshot drifts from
79
+ the committed baseline (`capture check --update` to accept intended changes; add
80
+ `--diff DIR` to render baseline·current·diff images) — or drop in the bundled
81
+ **GitHub Action**. Turn the report off with `--no-report` (or `output.report:
82
+ false`). Details in **[`docs/pipeline.md`](docs/pipeline.md)**.
83
+
84
+ ## Why capture, and not the others
85
+
86
+ The pieces exist in isolation; `capture` is the one tool that does all of it under
87
+ a single committed config.
88
+
89
+ | | web pages | real terminal | CLI sessions | README auto-embed | reproducible / CI |
90
+ | --- | :---: | :---: | :---: | :---: | :---: |
91
+ | **capture** | ✅ | ✅ | ✅ | ✅ | ✅ |
92
+ | shot-scraper | ✅ | ❌ | ❌ | ❌ | ✅ |
93
+ | freeze / carbon | ❌ | synthetic | ❌ | ❌ | ✅ |
94
+ | Percy / Chromatic | ✅ | ❌ | ❌ | ❌ | ✅ (cloud, paid) |
95
+ | doing it by hand | 😖 | 😖 | 😖 | ❌ | ❌ |
96
+
97
+ No cloud, no paid services, no special OS permissions for web/rendered shots.
98
+ (Native Terminal capture needs macOS Screen-Recording permission; everything else
99
+ needs nothing.)
100
+
101
+ ## How it works
102
+
103
+ ```
104
+ .capture.yaml ─► load + validate ─► [ boot app, wait until ready ] ─► one engine
105
+ routes each
106
+ shot by kind:
107
+ web ───────► Playwright / Chromium
108
+ cli·native ► a real Terminal.app window
109
+ cli·render ► PTY → ANSI→HTML → Chromium
110
+ session ───► one persistent Terminal, a shot per step
111
+ ─► NN-name.png
112
+ + README splice
113
+ ```
114
+
115
+ The clever part is what *isn't* here: **no AI runs at capture time.** Claude's only
116
+ job is to *author* the `.capture.yaml` once by reading your repo; after that the
117
+ engine is a plain, deterministic program — fast, free, and re-runnable in CI with
118
+ no model in the loop. See the full design in [`docs/design.md`](docs/design.md).
119
+
120
+ **Robust by design.** The readiness probe (HTTP / TCP port / log line) means you
121
+ never screenshot a half-booted app, and the app is launched in its own process
122
+ group and torn down — even on a crash or Ctrl-C — so a capture run never leaves an
123
+ orphaned dev server behind.
124
+
125
+ ## Capture, captured by capture
126
+
127
+ This repo dogfoods itself: the shots below are produced by running `capture run`
128
+ on its own [`.capture.yaml`](.capture.yaml) and spliced in automatically.
129
+
130
+ <!-- capture:start -->
131
+ ### The capture CLI
132
+
133
+ <img src="https://raw.githubusercontent.com/varmabudharaju/capture/main/docs/screenshots/01-the-capture-cli.png" width="100%" alt="capture --help showing the init, validate, and run commands"/>
134
+
135
+ ### Run options
136
+
137
+ <img src="https://raw.githubusercontent.com/varmabudharaju/capture/main/docs/screenshots/02-run-options.png" width="100%" alt="capture run options: --config, --only, and --version"/>
138
+
139
+ <!-- capture:end -->
140
+
141
+ ## Use with Claude
142
+
143
+ `capture` ships an optional Claude integration in [`integrations/claude/`](integrations/claude/):
144
+
145
+ - a **`/capture` skill** that inspects your repo (routes, `--help`, README), writes
146
+ the `.capture.yaml` for you, and runs it;
147
+ - an optional **auto-snapshot hook** that drops a raw snapshot when a dev server
148
+ starts (the honest "dumb snapshot"; the curated set always comes from `capture run`).
149
+
150
+ ## Commands
151
+
152
+ | Command | What it does |
153
+ | --- | --- |
154
+ | `capture init` | Scaffold a starter `.capture.yaml` |
155
+ | `capture validate` | Check the shot list is well-formed |
156
+ | `capture run` | Capture every shot and write outputs |
157
+ | `capture run --only dashboard` | Capture a single shot by name |
158
+ | `capture run --version v2` | Write into a versioned subfolder |
159
+ | `capture check` | Fail if a screenshot drifted from the committed baseline |
160
+ | `capture check --update` | Re-shoot and accept the current screenshots as the baseline |
161
+ | `capture check --diff DIR` | Also render baseline·current·diff images for changed shots |
162
+
163
+ ## Develop
164
+
165
+ ```bash
166
+ git clone https://github.com/varmabudharaju/capture && cd capture
167
+ python3 -m venv .venv && source .venv/bin/activate
168
+ pip install -e ".[dev]"
169
+ playwright install chromium
170
+ pytest # the suite is fully offline
171
+ ```
172
+
173
+ The hero GIF is itself reproducible — [`demo.tape`](demo.tape) + `vhs demo.tape`.
174
+
175
+ ## License
176
+
177
+ MIT © Varma Budharaju
@@ -0,0 +1,28 @@
1
+ name: "capture screenshots"
2
+ description: "Regenerate or drift-check your documentation screenshots with capture."
3
+ branding:
4
+ icon: "camera"
5
+ color: "purple"
6
+
7
+ inputs:
8
+ command:
9
+ description: "Subcommand to run: 'check' (fail on drift) or 'run' (regenerate)."
10
+ required: false
11
+ default: "check"
12
+ config:
13
+ description: "Path to the shot list."
14
+ required: false
15
+ default: ".capture.yaml"
16
+
17
+ runs:
18
+ using: "composite"
19
+ steps:
20
+ - name: Install capture
21
+ shell: bash
22
+ run: pip install shotlist
23
+ - name: Install Chromium
24
+ shell: bash
25
+ run: python -m playwright install --with-deps chromium
26
+ - name: capture ${{ inputs.command }}
27
+ shell: bash
28
+ run: capture ${{ inputs.command }} --config "${{ inputs.config }}"
@@ -0,0 +1,39 @@
1
+ # demo.tape — the hero GIF for capture's README (renders docs/demo.gif).
2
+ # Re-render anytime with: vhs demo.tape (run from the repo root)
3
+ Output docs/demo.gif
4
+
5
+ Set Shell "bash"
6
+ Set FontSize 18
7
+ Set Width 1200
8
+ Set Height 280
9
+ Set Padding 24
10
+ Set Theme "Catppuccin Mocha"
11
+ Set TypingSpeed 18ms
12
+
13
+ # --- stage a throwaway playground, off camera ---
14
+ Hide
15
+ Type "source demo/_setup.sh" Enter
16
+ Sleep 1.5s
17
+ Show
18
+
19
+ # --- Scene 1: adding docs screenshots, the old way ---
20
+ Type "# adding a screenshot to the docs, the old way..." Enter
21
+ Sleep 600ms
22
+ Type `mv "Desktop/Screen Shot 2026-06-24 at 3.47.11 PM.png" docs/dashboard.png` Enter
23
+ Sleep 350ms
24
+ Type `mv "Desktop/Screen Shot 2026-06-24 at 3.52.02 PM.png" docs/dashboard-final.png` Enter
25
+ Sleep 350ms
26
+ Type `mv "Desktop/Screen Shot 2026-06-24 at 4.02.59 PM.png" docs/dashboard-final-FINAL-v2-USE-THIS.png` Enter
27
+ Sleep 700ms
28
+ Type "# ...then you ship one UI tweak and every screenshot is stale." Enter
29
+ Sleep 1.6s
30
+ Type "clear" Enter
31
+ Sleep 300ms
32
+
33
+ # --- Scene 2: the capture way (a real run) ---
34
+ Type "# the capture way:" Enter
35
+ Sleep 500ms
36
+ Type "capture run" Enter
37
+ Sleep 5s
38
+ Type "# one shot list. one command. never stale again." Enter
39
+ Sleep 2s