{"slug": "show-hn-shotlist-make-your-ai-agent-prove-its-work-with-real-screenshots", "title": "Show HN: Shotlist – Make your AI agent prove its work with real screenshots", "summary": "Shotlist, a new open-source tool, lets developers capture reproducible screenshots of web pages, terminal windows, and CLI sessions as code, regenerating them on demand to prevent documentation drift. The tool supports multiple shot types and integrates with CI pipelines for automated visual drift checking.", "body_md": "**Screenshots for your docs — as code.** One committed shot list captures your\nweb pages, your *real* terminal windows, and stateful CLI sessions — and\nregenerates them all with a single command.\n\n[The problem](#the-problem)[Quickstart](#quickstart)[One shot list, four kinds of shot](#one-shot-list-four-kinds-of-shot)[Use cases](#use-cases)[Proof reports & pipelines](#proof-reports--pipelines)[Why shotlist, and not the others](#why-shotlist-and-not-the-others)[How it works](#how-it-works)[Use with Claude](#use-with-claude)[Commands](#commands)[Develop](#develop)\n\nDocumenting a feature means launching the app, clicking to the right state,\nscreenshotting, naming the file, and embedding it — **every time the UI changes.**\nThe screenshots drift out of date the moment you ship, and nobody notices until\nthey're embarrassingly wrong.\n\n`shotlist`\n\nmakes them **reproducible**: describe *how to start your app* and *what\nto shoot* once, in a committed `.shotlist.yaml`\n\n, then regenerate the whole set on\ndemand — locally or in CI. Same config + same app state → same screenshots.\n\n```\npip install shotlist             # installs the `shotlist` command\nplaywright install chromium      # one-time browser download\n\nshotlist init        # writes a starter .shotlist.yaml\nshotlist run         # boots your app, captures every shot, tears it all down\noutput:\n  dir: docs/screenshots\n  readme: README.md            # optional: splice <img> snippets straight into the README\n\napp:                           # optional — omit for static sites or pure-CLI shots\n  command: \"npm run dev\"\n  ready: { url: http://localhost:5173, timeout: 30 }   # never shoot a half-booted app\n\nshots:\n  - { name: dashboard, kind: web, url: http://localhost:5173/dashboard, full_page: true, alt: \"Dashboard\" }\n  - { name: cli-help,  kind: cli, command: \"mytool --help\", alt: \"Top-level help\" }\n```\n\n| Kind | Captures | How |\n|---|---|---|\n`web` |\na browser page — with optional click/fill/wait steps first | Playwright / Chromium |\n`cli` · `native` (macOS default) |\na real screenshot of your Terminal.app window — your font, your theme |\nAppleScript + `screencapture` |\n`cli` · `rendered` (any OS, CI-safe) |\nthe command's output drawn as a styled terminal card | PTY → ANSI→HTML → Chromium |\n`session` |\na stateful, multi-command flow in one persistent terminal — one shot per step |\none Terminal window, captured after each step |\n\nA `session`\n\nis how you screenshot a flow whose later steps depend on earlier ones —\nthe shell state (cwd, env, background processes) carries across. Background a\nlong-running process with `&`\n\nand a small `wait_ms`\n\n, keep capturing, and the\nsession tears it down on close.\n\n`shotlist`\n\nfits anywhere a screenshot would otherwise go stale:\n\n**README & docs screenshots**— the core: regenerate the whole set on every UI change.** Test-evidence / proof**— capture a feature flow step by step (a`session`\n\n) and share the generated`index.html`\n\nas proof it works.**CI drift-checking**—`shotlist check`\n\nfails the build when a screenshot changes unexpectedly (with a visual`--diff`\n\n).**Blog posts & tutorials**— polished web*and*CLI shots from one config.**Onboarding & demo galleries**— versioned sets you keep across releases.** Long-running processes**— background a dev server with`&`\n\n+`wait_ms`\n\nand shoot it live.\n\nEach one has a complete, copy-paste `.shotlist.yaml`\n\nin the recipes cookbook,\n.\n\n`docs/recipes.md`\n\nEvery `shotlist run`\n\nalso writes, next to the PNGs:\n\n— a self-contained gallery you can open and share as a`index.html`\n\n**proof report**;— a machine-readable record of the run (a pipeline artifact).`manifest.json`\n\nAttach `manifest.json`\n\nto a CI job, or open `index.html`\n\nas test-evidence. Gate CI\nwith ** shotlist check** — it re-captures and fails when a screenshot drifts from\nthe committed baseline (\n\n`shotlist check --update`\n\nto accept intended changes; add\n`--diff DIR`\n\nto render baseline·current·diff images) — or drop in the bundled\n**GitHub Action**. Turn the report off with\n\n`--no-report`\n\n(or `output.report: false`\n\n). Details in **.**\n\n`docs/pipeline.md`\n\nThe pieces exist in isolation; `shotlist`\n\nis the one tool that does all of it under\na single committed config.\n\n| web pages | real terminal | CLI sessions | README auto-embed | reproducible / CI | |\n|---|---|---|---|---|---|\nshotlist |\n✅ | ✅ | ✅ | ✅ | ✅ |\n| shot-scraper | ✅ | ❌ | ❌ | ❌ | ✅ |\n| freeze / carbon | ❌ | synthetic | ❌ | ❌ | ✅ |\n| Percy / Chromatic | ✅ | ❌ | ❌ | ❌ | ✅ (cloud, paid) |\n| doing it by hand | 😖 | 😖 | 😖 | ❌ | ❌ |\n\nNo cloud, no paid services, no special OS permissions for web/rendered shots. (Native Terminal capture needs macOS Screen-Recording permission; everything else needs nothing.)\n\n```\n.shotlist.yaml ─► load + validate ─► [ boot app, wait until ready ] ─► one engine\n                                                                        routes each\n                                                                        shot by kind:\n        web ───────► Playwright / Chromium\n        cli·native ► a real Terminal.app window\n        cli·render ► PTY → ANSI→HTML → Chromium\n        session ───► one persistent Terminal, a shot per step\n                                                                      ─► NN-name.png\n                                                                         + README splice\n```\n\nThe clever part is what *isn't* here: **no AI runs at capture time.** Claude's only\njob is to *author* the `.shotlist.yaml`\n\nonce by reading your repo; after that the\nengine is a plain, deterministic program — fast, free, and re-runnable in CI with\nno model in the loop. See the full design in [ docs/design.md](/varmabudharaju/shotlist/blob/main/docs/design.md).\n\n**Robust by design.** The readiness probe (HTTP / TCP port / log line) means you\nnever screenshot a half-booted app, and the app is launched in its own process\ngroup and torn down — even on a crash or Ctrl-C — so a shotlist run never leaves an\norphaned dev server behind.\n\nThis repo dogfoods itself: the shots below are produced by running `shotlist run`\n\non its own [ .shotlist.yaml](/varmabudharaju/shotlist/blob/main/.shotlist.yaml) and spliced in automatically.\n\n`shotlist`\n\nships an optional Claude integration in [ integrations/claude/](/varmabudharaju/shotlist/blob/main/integrations/claude):\n\n- a\nthat inspects your repo (routes,`/shotlist`\n\nskill`--help`\n\n, README), writes the`.shotlist.yaml`\n\nfor you, and runs it; - an optional\n**auto-snapshot hook** that drops a raw snapshot when a dev server starts (the honest \"dumb snapshot\"; the curated set always comes from`shotlist run`\n\n).\n\n| Command | What it does |\n|---|---|\n`shotlist init` |\nScaffold a starter `.shotlist.yaml` |\n`shotlist validate` |\nCheck the shot list is well-formed |\n`shotlist run` |\nCapture every shot and write outputs |\n`shotlist run --only dashboard` |\nCapture a single shot by name |\n`shotlist run --version v2` |\nWrite into a versioned subfolder |\n`shotlist check` |\nFail if a screenshot drifted from the committed baseline |\n`shotlist check --update` |\nRe-shoot and accept the current screenshots as the baseline |\n`shotlist check --diff DIR` |\nAlso render baseline·current·diff images for changed shots |\n\n```\ngit clone https://github.com/varmabudharaju/shotlist && cd shotlist\npython3 -m venv .venv && source .venv/bin/activate\npip install -e \".[dev]\"\nplaywright install chromium\npytest                       # the suite is fully offline\n```\n\nCI runs ruff, mypy, and pytest on Python 3.11 and 3.12. A separate\n** verify-action** workflow dogfoods the bundled GitHub Action on every PR —\nrunning\n\n`shotlist run`\n\nthen `shotlist check`\n\non a Linux runner — so a regression in\nthe action is caught before it ships. Releases publish to PyPI automatically via\nTrusted Publishing.The hero GIF is itself reproducible — [ demo.tape](/varmabudharaju/shotlist/blob/main/demo.tape) +\n\n`vhs demo.tape`\n\n.MIT © Varma Budharaju", "url": "https://wpnews.pro/news/show-hn-shotlist-make-your-ai-agent-prove-its-work-with-real-screenshots", "canonical_source": "https://github.com/varmabudharaju/shotlist", "published_at": "2026-06-26 04:05:25+00:00", "updated_at": "2026-06-26 04:35:10.076296+00:00", "lang": "en", "topics": ["developer-tools", "ai-agents"], "entities": ["Shotlist", "Playwright", "Chromium", "AppleScript", "GitHub"], "alternates": {"html": "https://wpnews.pro/news/show-hn-shotlist-make-your-ai-agent-prove-its-work-with-real-screenshots", "markdown": "https://wpnews.pro/news/show-hn-shotlist-make-your-ai-agent-prove-its-work-with-real-screenshots.md", "text": "https://wpnews.pro/news/show-hn-shotlist-make-your-ai-agent-prove-its-work-with-real-screenshots.txt", "jsonld": "https://wpnews.pro/news/show-hn-shotlist-make-your-ai-agent-prove-its-work-with-real-screenshots.jsonld"}}