Capture store + social promo assets from a built browser extension — with Playwright.
Screenshots · promo images · demo screencast · listing copy. One command.
English | 한국어
Part of Starter Series — reusable tooling, not just clone-templates.
shotkitis the first capability extracted into an installable package so any repo (and any agent) can use it without copy-paste.
- Currently implemented — A Playwright capture engine (build → launch the built extension via
launchPersistentContext(--load-extension)→ drive scenes → screenshot → caption/disclaimer band → promo tile from HTML → demowebm→ listing copy fromSTORE_LISTING.md), a CLI (shotkit) with an agent contract (--jsonmachine output, optionalpathargument,0/1/2exit codes), size presets for both audiences (CWS1280×800/440×280, SNS1200×675/1200×630/1080×1080), a path-traversal-safe localhost fixture server, a programmatic API (capture()), a Claude Code skill (skills/capture/), an AGENTS.md run-block so any shell-having coding agent can invoke it, and the npm package@starter-series/shotkit. Consumed bybrowser-extension-starterandskillBridge. - Planned — a listing in the
starter-seriesplugin marketplace; video editing (webm → mp4, trim, captions) for SNS. - Design intent — One engine, many surfaces — matched to the tool's nature. shotkit is a heavy, file-producing build tool, so its surfaces are CLI (+
--json), skill, and CI — not MCP (see Non-goals). Captures are deterministic (login-free fixtures, frozen data) and the run doubles as a real-bundle smoke test — a screenshot only appears if that feature rendered from the shipped code. Trademark-safe by construction: a disclaimer band is composited onto every shot. - Non-goals — An MCP server (dropped by design: agents with a shell get a better contract from
--json+ the skill, without MCP's per-session context cost; nothing here is a fast structured query). Removing the per-repo scene config (which screens are your money shots is irreducible intent — it lives in yourshotkit.config.js). A general-purpose video editor (v1 records a clean screencast; editing is Planned). A hosted service (file-touching capture is local by nature). - Redacted — none. Ships no private data, credentials, or third-party identifiers.
npm i -D @starter-series/shotkit
npx playwright install chromium # one-time: the browser shotkit drivesZero-install in any repo that has a config:
npx @starter-series/shotkitshotkit launches the full Chromium (
channel: 'chromium') — never the default headless-shell, which strips the extension subsystem. Headless works (HEADED=0; verified on macOS and Linux CI, video included) and is the CI default in the starter's capture workflow; the local default stays headed for easy debugging. Headed-under-xvfb proved unreliable on CI runners (the 8-bit default breaks Chromium's screenshot capture, and a 24-bit screen still failed silently) — run headless in CI.
Add a shotkit.config.js (the per-repo seam — see the contract below), then:
shotkit # produce everything into outDir
shotkit --scene 01-feature # just one scene/promoTile/demo, or "description"
shotkit --no-video # skip the screencast (faster/CI)
shotkit --no-build # use an already-built bundle
shotkit ../my-extension --json # run against another checkout; JSON result on stdoutOutputs land in outDir (default store-assets/): <scene>.png, <promoTile>.png, <demo>.webm, description.md.
shotkit [path] --json prints exactly one JSON object to stdout (progress
logs move to stderr):
{ "ok": true, "outDir": "/abs/store-assets", "produced": ["/abs/store-assets/01-popup.png"] }Exit codes: 0 ok · 1 runtime failure (stderr carries {"ok":false,"error":…}) ·
2 usage / no config found. Drop-in agent wiring: the run-block in
AGENTS.md (read by Claude Code, Codex, Cursor, Gemini CLI, …) and
the skills/capture/ skill (Agent Skills format —
copy the folder into any compatible tool's skills directory).
const { serveDirectory, stageExtension, patchManifestForLocalhost } = require('@starter-series/shotkit');
module.exports = {
build: 'npm run build', // run first → real-bundle smoke test (optional)
prepareExtension: () => '<unpacked dir>', // dir to --load-extension (often a patched temp copy)
outDir: 'store-assets',
disclaimer: 'Unofficial · not affiliated with …', // composited onto every shot (optional)
description: { from: 'store-assets/STORE_LISTING.md' }, // → description.md (optional)
async setup({ context, extensionId, flags }) { // e.g. start a fixture server / stubs
return { env: { baseUrl }, teardown: async () => {} };
},
scenes: [
{ name: '01-feature', preset: 'cws-screenshot', caption: 'What this shows',
async run({ page, context, extensionId, env, baseUrl, flags }) {
await page.goto(`${env.baseUrl}/page`); // drive the UI, wait until rendered
} },
],
promoTiles: [{ name: 'promo', template: 'path/to/promo.html', preset: 'cws-promo-small',
replacements: { NAME: 'My Ext' } }],
demo: { name: 'demo', preset: 'cws-screenshot', async run({ page, env }) { /* walkthrough */ } },
};- A scene/tile takes a
presetname or an explicit{ width, height }(seePRESETS). - The harness reduces a captioned scene's capture height by the band height and stacks the band under it, so the final image is exactly the preset size and no UI is hidden.
require('@starter-series/shotkit') →
capture(config, opts) · serveDirectory · stageExtension · patchManifestForLocalhost ·
launchWithExtension · closeContext · compositeCaption · renderPromoTile ·
extractListing · renderDescriptionDoc · PRESETS · resolveSize.
| Surface | Status | For |
|---|---|---|
CLI (shotkit, npx) with --json + path |
✅ now | humans / CI / shell-having agents |
Programmatic capture() |
✅ now | embedding |
Claude Code skill (skills/capture/) |
✅ now | Claude Code (portable to Codex/Cursor/Gemini via the Agent Skills format) |
AGENTS.md run-block |
✅ now | every agent that reads AGENTS.md |
npm package (@starter-series/shotkit) |
✅ now | npx zero-install |
| Capture-in-CI GitHub Action | ✅ now — ships in browser-extension-starter's capture.yml (headless) |
zero-local-browser runs + CI smoke test |
starter-series marketplace entry |
planned | discovery |
Video editing (webm→mp4, trim, captions) |
planned | SNS clips |
An MCP stdio tool was considered and dropped — see Non-goals: shotkit is a heavy, file-producing build tool, so a --json CLI + skill serves agents better than an MCP server's per-session context cost.
Generalization rule (for the next capability in the series): one npm package (engine + thin CLI), one *.config.js seam for irreducible per-repo intent, agent surfaces matched to the tool's nature (fast/structured: an MCP tool taking a path; heavy/build-time: a --json CLI + skill + AGENTS.md run-block), one marketplace entry. The engine never reads project specifics except through the config seam.
MIT © heznpc