versioncam 0.1.1
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.
- package/CHANGELOG.md +94 -0
- package/LICENSE.md +105 -0
- package/README.md +463 -0
- package/bin/versioncam.js +29 -0
- package/dist/.types-render/render-page/draw.d.ts +28 -0
- package/dist/.types-render/render-page/main.d.ts +24 -0
- package/dist/.types-render/render-page/theme.d.ts +37 -0
- package/dist/app-server.d.ts +59 -0
- package/dist/app-server.js +328 -0
- package/dist/app-server.js.map +1 -0
- package/dist/cli/app.d.ts +13 -0
- package/dist/cli/app.js +21 -0
- package/dist/cli/app.js.map +1 -0
- package/dist/cli/commands/check.d.ts +8 -0
- package/dist/cli/commands/check.js +51 -0
- package/dist/cli/commands/check.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +8 -0
- package/dist/cli/commands/doctor.js +130 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/dsl.d.ts +16 -0
- package/dist/cli/commands/dsl.js +22 -0
- package/dist/cli/commands/dsl.js.map +1 -0
- package/dist/cli/commands/frame.d.ts +8 -0
- package/dist/cli/commands/frame.js +72 -0
- package/dist/cli/commands/frame.js.map +1 -0
- package/dist/cli/commands/init.d.ts +23 -0
- package/dist/cli/commands/init.js +109 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/inspect.d.ts +1 -0
- package/dist/cli/commands/inspect.js +32 -0
- package/dist/cli/commands/inspect.js.map +1 -0
- package/dist/cli/commands/install.d.ts +33 -0
- package/dist/cli/commands/install.js +66 -0
- package/dist/cli/commands/install.js.map +1 -0
- package/dist/cli/commands/login.d.ts +10 -0
- package/dist/cli/commands/login.js +49 -0
- package/dist/cli/commands/login.js.map +1 -0
- package/dist/cli/commands/measure.d.ts +1 -0
- package/dist/cli/commands/measure.js +36 -0
- package/dist/cli/commands/measure.js.map +1 -0
- package/dist/cli/commands/open-app.d.ts +14 -0
- package/dist/cli/commands/open-app.js +45 -0
- package/dist/cli/commands/open-app.js.map +1 -0
- package/dist/cli/commands/preview.d.ts +8 -0
- package/dist/cli/commands/preview.js +55 -0
- package/dist/cli/commands/preview.js.map +1 -0
- package/dist/cli/commands/record.d.ts +10 -0
- package/dist/cli/commands/record.js +86 -0
- package/dist/cli/commands/record.js.map +1 -0
- package/dist/cli/commands/render.d.ts +1 -0
- package/dist/cli/commands/render.js +93 -0
- package/dist/cli/commands/render.js.map +1 -0
- package/dist/cli/commands/review.d.ts +6 -0
- package/dist/cli/commands/review.js +89 -0
- package/dist/cli/commands/review.js.map +1 -0
- package/dist/cli/commands/sheet.d.ts +1 -0
- package/dist/cli/commands/sheet.js +48 -0
- package/dist/cli/commands/sheet.js.map +1 -0
- package/dist/cli/commands/stability.d.ts +14 -0
- package/dist/cli/commands/stability.js +110 -0
- package/dist/cli/commands/stability.js.map +1 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.js +69 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/usage.d.ts +10 -0
- package/dist/cli/usage.js +46 -0
- package/dist/cli/usage.js.map +1 -0
- package/dist/config.d.ts +250 -0
- package/dist/config.js +154 -0
- package/dist/config.js.map +1 -0
- package/dist/core/camera.d.ts +30 -0
- package/dist/core/camera.js +94 -0
- package/dist/core/camera.js.map +1 -0
- package/dist/core/compose.d.ts +38 -0
- package/dist/core/compose.js +81 -0
- package/dist/core/compose.js.map +1 -0
- package/dist/core/cursor.d.ts +38 -0
- package/dist/core/cursor.js +103 -0
- package/dist/core/cursor.js.map +1 -0
- package/dist/core/easing.d.ts +15 -0
- package/dist/core/easing.js +33 -0
- package/dist/core/easing.js.map +1 -0
- package/dist/core/loop.d.ts +30 -0
- package/dist/core/loop.js +96 -0
- package/dist/core/loop.js.map +1 -0
- package/dist/core/motion-defaults.d.ts +61 -0
- package/dist/core/motion-defaults.js +62 -0
- package/dist/core/motion-defaults.js.map +1 -0
- package/dist/core/rng.d.ts +13 -0
- package/dist/core/rng.js +27 -0
- package/dist/core/rng.js.map +1 -0
- package/dist/core/sse.d.ts +15 -0
- package/dist/core/sse.js +16 -0
- package/dist/core/sse.js.map +1 -0
- package/dist/core/timeline.d.ts +146 -0
- package/dist/core/timeline.js +81 -0
- package/dist/core/timeline.js.map +1 -0
- package/dist/core/timing.d.ts +31 -0
- package/dist/core/timing.js +29 -0
- package/dist/core/timing.js.map +1 -0
- package/dist/core/typing.d.ts +12 -0
- package/dist/core/typing.js +35 -0
- package/dist/core/typing.js.map +1 -0
- package/dist/driver/clip.d.ts +71 -0
- package/dist/driver/clip.js +120 -0
- package/dist/driver/clip.js.map +1 -0
- package/dist/driver/compare.d.ts +34 -0
- package/dist/driver/compare.js +40 -0
- package/dist/driver/compare.js.map +1 -0
- package/dist/driver/gate.d.ts +36 -0
- package/dist/driver/gate.js +27 -0
- package/dist/driver/gate.js.map +1 -0
- package/dist/driver/launch.d.ts +43 -0
- package/dist/driver/launch.js +47 -0
- package/dist/driver/launch.js.map +1 -0
- package/dist/driver/page-hooks.d.ts +72 -0
- package/dist/driver/page-hooks.js +129 -0
- package/dist/driver/page-hooks.js.map +1 -0
- package/dist/driver/reports.d.ts +34 -0
- package/dist/driver/reports.js +42 -0
- package/dist/driver/reports.js.map +1 -0
- package/dist/driver/session.d.ts +285 -0
- package/dist/driver/session.js +773 -0
- package/dist/driver/session.js.map +1 -0
- package/dist/driver/settle.d.ts +41 -0
- package/dist/driver/settle.js +82 -0
- package/dist/driver/settle.js.map +1 -0
- package/dist/env.d.ts +11 -0
- package/dist/env.js +41 -0
- package/dist/env.js.map +1 -0
- package/dist/fixtures.d.ts +13 -0
- package/dist/fixtures.js +13 -0
- package/dist/fixtures.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/inspect/inspect.d.ts +70 -0
- package/dist/inspect/inspect.js +176 -0
- package/dist/inspect/inspect.js.map +1 -0
- package/dist/inspect/measure.d.ts +40 -0
- package/dist/inspect/measure.js +107 -0
- package/dist/inspect/measure.js.map +1 -0
- package/dist/loader.d.ts +28 -0
- package/dist/loader.js +143 -0
- package/dist/loader.js.map +1 -0
- package/dist/page/assets/index-DUom5amc.js +1 -0
- package/dist/page/index.html +18 -0
- package/dist/render/encode.d.ts +40 -0
- package/dist/render/encode.js +183 -0
- package/dist/render/encode.js.map +1 -0
- package/dist/render/ffmpeg.d.ts +13 -0
- package/dist/render/ffmpeg.js +72 -0
- package/dist/render/ffmpeg.js.map +1 -0
- package/dist/render/presentation.d.ts +19 -0
- package/dist/render/presentation.js +27 -0
- package/dist/render/presentation.js.map +1 -0
- package/dist/render/render.d.ts +59 -0
- package/dist/render/render.js +144 -0
- package/dist/render/render.js.map +1 -0
- package/dist/render/sampling.d.ts +47 -0
- package/dist/render/sampling.js +129 -0
- package/dist/render/sampling.js.map +1 -0
- package/dist/render/sequence.d.ts +24 -0
- package/dist/render/sequence.js +105 -0
- package/dist/render/sequence.js.map +1 -0
- package/dist/render/serve.d.ts +35 -0
- package/dist/render/serve.js +124 -0
- package/dist/render/serve.js.map +1 -0
- package/dist/review/review.d.ts +54 -0
- package/dist/review/review.js +229 -0
- package/dist/review/review.js.map +1 -0
- package/dist/scene.d.ts +23 -0
- package/dist/scene.js +2 -0
- package/dist/scene.js.map +1 -0
- package/dsl.md +119 -0
- package/package.json +76 -0
- package/plugin/.claude-plugin/plugin.json +9 -0
- package/plugin/README.md +105 -0
- package/plugin/agents/versioncam-reviewer.md +63 -0
- package/plugin/skills/versioncam/SKILL.md +235 -0
- package/plugin/skills/versioncam/authoring.md +226 -0
- package/plugin/skills/versioncam/onboarding.md +199 -0
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "versioncam",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Record demo clips of a real web app: scripted, deterministic, regenerated on deploy.",
|
|
6
|
+
"license": "FSL-1.1-ALv2",
|
|
7
|
+
"author": "Peter Bulovec",
|
|
8
|
+
"homepage": "https://version.cam",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/petbul/versioncam.git",
|
|
12
|
+
"directory": "packages/recorder"
|
|
13
|
+
},
|
|
14
|
+
"bugs": "https://github.com/petbul/versioncam-home/issues",
|
|
15
|
+
"keywords": [
|
|
16
|
+
"demo",
|
|
17
|
+
"video",
|
|
18
|
+
"screen-recording",
|
|
19
|
+
"playwright",
|
|
20
|
+
"docs",
|
|
21
|
+
"ci",
|
|
22
|
+
"deterministic",
|
|
23
|
+
"agent-skill"
|
|
24
|
+
],
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
28
|
+
"bin": {
|
|
29
|
+
"versioncam": "bin/versioncam.js"
|
|
30
|
+
},
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"import": "./dist/index.js"
|
|
35
|
+
},
|
|
36
|
+
"./scene": {
|
|
37
|
+
"types": "./dist/scene.d.ts",
|
|
38
|
+
"import": "./dist/scene.js"
|
|
39
|
+
},
|
|
40
|
+
"./fixtures": {
|
|
41
|
+
"types": "./dist/fixtures.d.ts",
|
|
42
|
+
"import": "./dist/fixtures.js"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"files": [
|
|
46
|
+
"dist",
|
|
47
|
+
"!dist/**/*.test.*",
|
|
48
|
+
"bin",
|
|
49
|
+
"dsl.md",
|
|
50
|
+
"plugin",
|
|
51
|
+
"CHANGELOG.md"
|
|
52
|
+
],
|
|
53
|
+
"engines": {
|
|
54
|
+
"node": ">=22"
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"build": "tsc -b --force tsconfig.json && vite build -c vite.render.config.ts",
|
|
58
|
+
"test:integration": "env -u NODE_OPTIONS playwright test -c test/playwright.config.ts",
|
|
59
|
+
"typecheck": "tsc -p tsconfig.render.json"
|
|
60
|
+
},
|
|
61
|
+
"dependencies": {
|
|
62
|
+
"esbuild": "^0.25.0",
|
|
63
|
+
"playwright": "1.59.1"
|
|
64
|
+
},
|
|
65
|
+
"optionalDependencies": {
|
|
66
|
+
"ffmpeg-static": "^5.2.0"
|
|
67
|
+
},
|
|
68
|
+
"devDependencies": {
|
|
69
|
+
"@playwright/test": "1.59.1",
|
|
70
|
+
"@types/node": "^22.10.0",
|
|
71
|
+
"@types/pngjs": "^6.0.5",
|
|
72
|
+
"jsdom": "^26.0.0",
|
|
73
|
+
"pngjs": "^7.0.0",
|
|
74
|
+
"vite": "^8.0.16"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "versioncam",
|
|
3
|
+
"description": "Record a short, true demo clip of a web app from one sentence: a skill that sets the repository up on its first run, writes the clip, and has a reviewer pass it from a contact sheet.",
|
|
4
|
+
"author": {
|
|
5
|
+
"name": "Peter Bulovec"
|
|
6
|
+
},
|
|
7
|
+
"homepage": "https://version.cam",
|
|
8
|
+
"keywords": ["demo", "recording", "playwright", "screencast"]
|
|
9
|
+
}
|
package/plugin/README.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# The versioncam skill
|
|
2
|
+
|
|
3
|
+
`/versioncam "<what the clip should show>"` records a clip of your web app: it
|
|
4
|
+
writes the script, records it, has a reviewer look at the result, and fixes
|
|
5
|
+
what the reviewer finds — until the reviewer passes it or the rounds run out.
|
|
6
|
+
The first time it runs in a repository, it sets the repository up first.
|
|
7
|
+
|
|
8
|
+
Nothing here calls an API. The model is whichever agent session runs the
|
|
9
|
+
skill, and the vision step is that session's file reader opening a contact
|
|
10
|
+
sheet. Everything is markdown, and you can edit all of it.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
The skill ships inside the `versioncam` npm package:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npx versioncam init # .claude/skills/versioncam/ and .claude/agents/versioncam-reviewer.md
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`init` installs the skill and nothing else — no config: the first run writes
|
|
21
|
+
that. It refuses to overwrite a file that is already there, and refuses the
|
|
22
|
+
whole install if any one is; `--force` overwrites. For an agent host that reads
|
|
23
|
+
skills from somewhere other than `.claude/`, `--into <dir>` puts the skill
|
|
24
|
+
directory at `<dir>/versioncam/`, with the reviewer inside it.
|
|
25
|
+
|
|
26
|
+
Or, without copying anything, load the plugin directory as it is:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
claude --plugin-dir node_modules/versioncam/plugin
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Run it
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
/versioncam "Create a shipment for a new recipient and show it appearing in the list"
|
|
36
|
+
/versioncam "Find a shipment by filtering the list" --id 02-find --max-rounds 4
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## What it does
|
|
40
|
+
|
|
41
|
+
**The first run** — no `versioncam.config.ts` in the directory — sets the
|
|
42
|
+
repository up (`skills/versioncam/onboarding.md`): it works out how the app
|
|
43
|
+
starts and where it is served, from `package.json`, the framework's config and
|
|
44
|
+
the README, and asks when there is more than one plausible answer; asks whether
|
|
45
|
+
the app signs in and how you want that handled; writes the config, with a
|
|
46
|
+
`webServer` so every command starts the app itself; runs `doctor` and
|
|
47
|
+
`inspect` to prove the app starts and settles; and records a three-second clip
|
|
48
|
+
twice with `stability`, to prove the app records the same way twice before a
|
|
49
|
+
clip is written for it. Nothing is committed.
|
|
50
|
+
|
|
51
|
+
**Every run** then (`skills/versioncam/SKILL.md`):
|
|
52
|
+
|
|
53
|
+
1. `versioncam doctor` — which starts the app — and `inspect /`.
|
|
54
|
+
2. The session writes the clip itself, following
|
|
55
|
+
`skills/versioncam/authoring.md`: it reads `versioncam dsl` and the existing
|
|
56
|
+
clips, writes a beat sheet, writes the clip, records it in draft, and runs
|
|
57
|
+
the deterministic checks in `versioncam review`. Three recordings at most,
|
|
58
|
+
then on. On a page too big to hold in context, it hands this to a subagent
|
|
59
|
+
and keeps that subagent for later rounds.
|
|
60
|
+
3. A contact sheet sampled at the beats, and a **fresh reviewer** on it — one
|
|
61
|
+
picture, a five-item checklist, a JSON verdict. Fresh every round,
|
|
62
|
+
deliberately: it sees the clip, never the argument for it.
|
|
63
|
+
4. On a pass, a full-quality record and render. Otherwise the fixes the verdict
|
|
64
|
+
names, and another round.
|
|
65
|
+
|
|
66
|
+
The reviewer is `agents/versioncam-reviewer.md`, one file used three ways:
|
|
67
|
+
registered as an agent, which is what Claude Code does with it; as the prompt
|
|
68
|
+
of a plain subagent, on a host that cannot register agents; or as a checklist
|
|
69
|
+
the session applies to its own sheet, on a host with no subagents at all. The
|
|
70
|
+
verdict records which, as `"reviewer": "restricted"`, `"unrestricted"` or
|
|
71
|
+
`"self"` — a clip passed by its own author is a weaker claim, and says so.
|
|
72
|
+
|
|
73
|
+
The skill never runs `git`. The clip it writes lands in your clips directory as
|
|
74
|
+
an ordinary file, for you to read and commit yourself.
|
|
75
|
+
|
|
76
|
+
## What it leaves behind
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
.versioncam/author/<id>/
|
|
80
|
+
beats.json the beat sheet: { label, expect } per beat
|
|
81
|
+
summary.json { id, rounds, passed, wallMs, reviewer }
|
|
82
|
+
<id>-sheet.png the final contact sheet
|
|
83
|
+
round-1/
|
|
84
|
+
author.md what the round produced, including what was awkward
|
|
85
|
+
beats.json
|
|
86
|
+
verdict.json the reviewer's JSON, verbatim
|
|
87
|
+
round-2/ …
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
`.versioncam/` is a build product and is gitignored. Keep the clip; the
|
|
91
|
+
transcripts are for reading when something goes wrong, and for finding the
|
|
92
|
+
next thing to fix in the recorder — the paragraph on awkward tooling is there
|
|
93
|
+
because the first agent to write one produced a list that became a work
|
|
94
|
+
package.
|
|
95
|
+
|
|
96
|
+
## What it costs
|
|
97
|
+
|
|
98
|
+
On the example app, headless, with an earlier two-agent version of this loop:
|
|
99
|
+
one clip took between six and twelve minutes, in one or two rounds, and all six
|
|
100
|
+
intents across two apps passed. Most of the time is model latency rather than
|
|
101
|
+
the recorder: a round is a handful of recordings of a few seconds each, and a
|
|
102
|
+
reviewer's single look at a single picture still takes a minute or two end to
|
|
103
|
+
end. It runs on a subscription rather than a metered key, so the setup cost is
|
|
104
|
+
close to zero; for anything higher-volume the prompts in these files are what
|
|
105
|
+
would move to a direct SDK loop, unchanged.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: versioncam-reviewer
|
|
3
|
+
description: Judges one recorded demo clip from its contact sheet against a fixed five-item checklist and returns a JSON verdict. Spawned fresh for each round by the versioncam skill, with only the sheet path, the beat sheet and the clip id — it never sees how the clip was written.
|
|
4
|
+
tools: Read, Bash(npx versioncam frame *)
|
|
5
|
+
model: sonnet
|
|
6
|
+
effort: low
|
|
7
|
+
maxTurns: 8
|
|
8
|
+
omitClaudeMd: true
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
You are given one contact sheet: a single PNG of tiles from one clip, in time
|
|
12
|
+
order, left to right then top to bottom. You are also given the clip's beat
|
|
13
|
+
sheet and its id. Read the sheet and say whether the clip is watchable.
|
|
14
|
+
|
|
15
|
+
## The checklist
|
|
16
|
+
|
|
17
|
+
1. The cursor starts clear of the controls in the first tile — not hovering a
|
|
18
|
+
button, a row or a field.
|
|
19
|
+
2. Each beat's expected effect is visible in some tile.
|
|
20
|
+
3. Nothing a beat depends on is cropped by the camera framing.
|
|
21
|
+
4. A caption is present and legible.
|
|
22
|
+
5. The final tile has nothing left open — no dialog, menu or tooltip.
|
|
23
|
+
|
|
24
|
+
## Rules
|
|
25
|
+
|
|
26
|
+
- **Judge only what you can see.** The beat sheet says what was intended; it is
|
|
27
|
+
not evidence that it happened. If a tile does not show it, it did not
|
|
28
|
+
happen as far as this review is concerned.
|
|
29
|
+
- If a tile is too small to be sure, run `npx versioncam frame <id> <n>` for that
|
|
30
|
+
frame at full size and read the PNG it prints. That is the only shell
|
|
31
|
+
command you run.
|
|
32
|
+
- **Tile *n* is not frame *n*.** You are given the frame number behind each
|
|
33
|
+
tile; pull those. A reviewer that guessed pulled frames 1 to 16 of a clip
|
|
34
|
+
whose beat fired at frame 29, watched nothing happen, and failed a clip that
|
|
35
|
+
was fine. If you were not given the numbers, say so in `legibility` and judge
|
|
36
|
+
from the tiles alone rather than inventing frames to pull.
|
|
37
|
+
- Say which tile each observation comes from.
|
|
38
|
+
- If you genuinely cannot tell after looking at the full-size frame, fail the
|
|
39
|
+
item and say what you could not make out. Guessing in either direction is
|
|
40
|
+
worse than saying so.
|
|
41
|
+
|
|
42
|
+
## Return
|
|
43
|
+
|
|
44
|
+
Only JSON. No preamble, no commentary around it.
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"items": [
|
|
49
|
+
{ "item": "cursor starts clear of controls", "pass": true, "note": "tile 1: cursor sits over empty page margin" },
|
|
50
|
+
{ "item": "beat: shipment created", "pass": false, "note": "no tile shows a new row; tiles 4-6 show the spinner" }
|
|
51
|
+
],
|
|
52
|
+
"pass": false,
|
|
53
|
+
"legibility": "1440x900 downscaled to 8 tiles; body text unreadable, headings and buttons clear",
|
|
54
|
+
"reviewer": "restricted"
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
One entry in `items` for each of the five checklist points, plus one for each
|
|
59
|
+
beat in the beat sheet, labelled `beat: <label>`. `pass` at the top level is
|
|
60
|
+
true only when every item passed. `legibility` is one sentence on how much of
|
|
61
|
+
the sheet you could actually read, so the next round knows how much to trust
|
|
62
|
+
this verdict. `reviewer` is always `"restricted"`: it records that the verdict
|
|
63
|
+
came from this file, run as an agent that saw only the sheet.
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: versioncam
|
|
3
|
+
description: Record a short demo clip — a screencast, a short video, a GIF of a feature — of this repository's web app from one sentence of what it should show, and iterate until a reviewer passes it from a contact sheet. Use when someone asks to record, film or make a demo of a feature, or to set versioncam up in a repository. Works in any repository with a web app — the first run writes versioncam.config.ts, works out how to start the app, and proves the app records the same way twice before anything is authored.
|
|
4
|
+
user-invocable: true
|
|
5
|
+
argument-hint: "\"<what the clip should show>\" [--id <slug>] [--max-rounds 6]"
|
|
6
|
+
allowed-tools:
|
|
7
|
+
- Agent
|
|
8
|
+
- SendMessage
|
|
9
|
+
- AskUserQuestion
|
|
10
|
+
- Read
|
|
11
|
+
- Write
|
|
12
|
+
- Edit
|
|
13
|
+
- Glob
|
|
14
|
+
- Grep
|
|
15
|
+
- Bash(npx versioncam *)
|
|
16
|
+
- Bash(mkdir *)
|
|
17
|
+
- Bash(cp *)
|
|
18
|
+
- Bash(ls *)
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
# Record a demo clip
|
|
22
|
+
|
|
23
|
+
`$ARGUMENTS`
|
|
24
|
+
|
|
25
|
+
You turn one sentence into a clip: a script in this repository that drives the
|
|
26
|
+
running app through its own controls, and a video recorded from it. You write
|
|
27
|
+
the clip; a reviewer who has never seen how it was written looks at a picture
|
|
28
|
+
of it and says whether it shows what it claims; and you go round again until it
|
|
29
|
+
does, or the rounds run out.
|
|
30
|
+
|
|
31
|
+
Follow these steps in order. Two more files sit beside this one, in this
|
|
32
|
+
skill's directory: `onboarding.md` and `authoring.md`. They are briefs, read
|
|
33
|
+
when a step says to.
|
|
34
|
+
|
|
35
|
+
## 0. Which phase
|
|
36
|
+
|
|
37
|
+
First, check versioncam is installed where Node will find it:
|
|
38
|
+
`ls -d node_modules/versioncam`, then the same in each parent directory up to
|
|
39
|
+
the repository root (`../node_modules/versioncam`, …) — a workspace hoists it.
|
|
40
|
+
If none has it, **stop** and say it must be installed first
|
|
41
|
+
(`npm i -D versioncam`, then `npx versioncam install`). Every step below runs
|
|
42
|
+
`npx versioncam`, and `npx` with nothing installed downloads the latest
|
|
43
|
+
published version — not necessarily the one this repository will pin, and
|
|
44
|
+
with no Chromium to record with.
|
|
45
|
+
|
|
46
|
+
Then: is there a `versioncam.config.ts` (or `.js`, `.mjs`) in the working
|
|
47
|
+
directory?
|
|
48
|
+
|
|
49
|
+
- **No** → this is the repository's first run. Read `onboarding.md` and do all
|
|
50
|
+
of it, then continue with step 1.
|
|
51
|
+
- **Yes** → continue with step 1.
|
|
52
|
+
|
|
53
|
+
## 1. Set up
|
|
54
|
+
|
|
55
|
+
Parse `$ARGUMENTS`. The first quoted string is the **intent**. `--id <slug>`
|
|
56
|
+
names the clip; without it, derive a slug from the intent — lowercase, hyphens,
|
|
57
|
+
at most 40 characters, cut at a word boundary. `--max-rounds <n>` defaults to 6.
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
mkdir -p .versioncam/author/<id>
|
|
61
|
+
npx versioncam doctor
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`doctor` checks the machine and the config, and **starts the app** through the
|
|
65
|
+
config's `webServer` when nothing is serving it, then stops it again. Every
|
|
66
|
+
later command does the same, so you never start a dev server yourself. If the
|
|
67
|
+
app check fails, **stop** and say so: nothing below works without the app, and
|
|
68
|
+
every round would spend a recording discovering that. If another check fails,
|
|
69
|
+
print it and stop too.
|
|
70
|
+
|
|
71
|
+
Then capture the start page once:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
npx versioncam inspect /
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## 2. Author — this session
|
|
78
|
+
|
|
79
|
+
Read `authoring.md` now: it is how to write and repair a clip, and it applies
|
|
80
|
+
to you. Then, as it says: `npx versioncam dsl`, the existing clips, `inspect`
|
|
81
|
+
of every page the intent touches; write `.versioncam/author/<id>/beats.json`;
|
|
82
|
+
write the clip; `record --draft`; `review --beats …`; repair — **at most three
|
|
83
|
+
recordings**, then on to the sheet, passed or not.
|
|
84
|
+
|
|
85
|
+
Write what the round produced to `.versioncam/author/<id>/round-N/author.md` —
|
|
86
|
+
the four things `authoring.md` asks for, including the paragraph on what was
|
|
87
|
+
awkward about the tools. That paragraph is the point of keeping these
|
|
88
|
+
directories; do not summarise it away. Copy `beats.json` beside it.
|
|
89
|
+
|
|
90
|
+
**Delegation.** If the first `inspect /` printed more than 400 lines, or your
|
|
91
|
+
host makes subagents cheap, hand the authoring to a subagent instead of doing
|
|
92
|
+
it here. Spawn a plain one (general-purpose) whose prompt is the full text of
|
|
93
|
+
`authoring.md`, followed by the intent, the clip id, the clips directory (the
|
|
94
|
+
config's `clips`), the `inspect /` output inline, and the beats path. **Keep the
|
|
95
|
+
agent id it returns**: that id is how you continue the same author in later
|
|
96
|
+
rounds, with `SendMessage`, everything it learned about the page still in its
|
|
97
|
+
context — spawning a new one would start from nothing and repeat the same
|
|
98
|
+
recordings. Save its reply as `round-N/author.md`. You then orchestrate; the
|
|
99
|
+
rest of this file is the same either way.
|
|
100
|
+
|
|
101
|
+
## 3. The sheet, then a fresh reviewer
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
npx versioncam sheet <id> --at-marks
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
`--at-marks` samples frame 0, each beat's settle mark, and the last frame,
|
|
108
|
+
instead of eight evenly spaced points — so the tiles the reviewer sees are the
|
|
109
|
+
beats, not whatever happened to fall on an eighth of the duration. It prints
|
|
110
|
+
the sheet's path, then the frame number behind each tile.
|
|
111
|
+
|
|
112
|
+
The reviewer is given only:
|
|
113
|
+
|
|
114
|
+
- the sheet path,
|
|
115
|
+
- **the frame numbers `sheet --at-marks` just printed**, in order, one per tile,
|
|
116
|
+
- the contents of `.versioncam/author/<id>/beats.json`,
|
|
117
|
+
- the clip id (so it can call `npx versioncam frame <id> <n>` — from this
|
|
118
|
+
directory, which you name if a subagent on your host starts somewhere else).
|
|
119
|
+
|
|
120
|
+
The frame numbers are not optional. A reviewer given only the id assumed tile
|
|
121
|
+
*n* was frame *n*, pulled frames 1 to 16 — every one of them before the beat
|
|
122
|
+
fired at frame 29 — and reported that the clip never filtered anything. It was
|
|
123
|
+
looking at the right clip and the wrong seconds. `sheet` prints the list for
|
|
124
|
+
exactly this reason; pass it on verbatim.
|
|
125
|
+
|
|
126
|
+
The reviewer's brief is `versioncam-reviewer.md`: in `agents/` two levels up
|
|
127
|
+
from this skill's directory when the skill came from the plugin, in
|
|
128
|
+
`.claude/agents/` when `versioncam init` installed it, and beside this file
|
|
129
|
+
when it was installed with `--into`. Take the first rung of this ladder your
|
|
130
|
+
host allows:
|
|
131
|
+
|
|
132
|
+
1. **Registered.** If an agent named `versioncam-reviewer` is available — it is
|
|
133
|
+
`versioncam:versioncam-reviewer` when it came with the plugin — spawn it
|
|
134
|
+
with those inputs. It is blind by construction: a fresh context holding the
|
|
135
|
+
sheet and the beats and nothing of how the clip was made. Its JSON says
|
|
136
|
+
`"reviewer": "restricted"`.
|
|
137
|
+
2. **A plain subagent.** Otherwise, spawn a general-purpose subagent whose
|
|
138
|
+
prompt is the full text of `versioncam-reviewer.md` followed by those
|
|
139
|
+
inputs. It is blind by instruction rather than by construction. Its JSON
|
|
140
|
+
will still say `"restricted"`, because the file it was given says so; save
|
|
141
|
+
it with `"reviewer": "unrestricted"`, which is what it was.
|
|
142
|
+
3. **No subagents.** Otherwise, read `versioncam-reviewer.md` and judge the
|
|
143
|
+
sheet yourself, in a step of its own — *before* you look at your beats or
|
|
144
|
+
your clip again, with only the four inputs in front of you — and save the
|
|
145
|
+
verdict with `"reviewer": "self"`. The summary says so: a clip passed by its
|
|
146
|
+
own author is a weaker claim, and whoever reads it should know.
|
|
147
|
+
|
|
148
|
+
A fresh reviewer every round is the point, not an accident. It must not see
|
|
149
|
+
the author's reasoning, an earlier verdict, or how hard the clip was to get
|
|
150
|
+
working — only this round's picture. Never continue a reviewer, and never pass
|
|
151
|
+
it the author's report.
|
|
152
|
+
|
|
153
|
+
Save its JSON verbatim, with `reviewer` as above, as
|
|
154
|
+
`.versioncam/author/<id>/round-N/verdict.json`.
|
|
155
|
+
|
|
156
|
+
### If you think the verdict is wrong
|
|
157
|
+
|
|
158
|
+
You will sometimes believe a reviewer has erred, and sometimes you will be
|
|
159
|
+
right. **Do not override it.** Overriding is the one move that undoes the
|
|
160
|
+
design: a fresh reviewer is trusted precisely because it has not watched the
|
|
161
|
+
clip being built, and a session that has watched three rounds is the least
|
|
162
|
+
independent judge available.
|
|
163
|
+
|
|
164
|
+
Get **one** second opinion, from a fresh reviewer on the same rung — same
|
|
165
|
+
sheet, same beats, same frame numbers, and nothing about the first verdict or
|
|
166
|
+
what you think it got wrong. Save it as `round-N/verdict-2.json`. That verdict
|
|
167
|
+
stands, whichever way it goes. If it agrees with the first, the clip has failed
|
|
168
|
+
and the round continues; if it disagrees, take it and move on, and keep both
|
|
169
|
+
files so the disagreement is on the record.
|
|
170
|
+
|
|
171
|
+
One second opinion, once per round. Two reviewers who disagree is information.
|
|
172
|
+
A third is shopping for the answer you wanted.
|
|
173
|
+
|
|
174
|
+
## 4. If the verdict passes
|
|
175
|
+
|
|
176
|
+
Take the real recording and render it:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
npx versioncam record <id>
|
|
180
|
+
npx versioncam render <id>
|
|
181
|
+
npx versioncam sheet <id> --at-marks
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Copy that final sheet into `.versioncam/author/<id>/`. Write
|
|
185
|
+
`.versioncam/author/<id>/summary.json`:
|
|
186
|
+
|
|
187
|
+
```json
|
|
188
|
+
{ "id": "<id>", "rounds": 2, "passed": true, "wallMs": 812000, "reviewer": "restricted" }
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
`reviewer` is the rung the passing verdict came from. `wallMs` only if you can
|
|
192
|
+
measure it — leave it out rather than guess; a harness running you measures it
|
|
193
|
+
from outside. After a first run that found the app unstable, add
|
|
194
|
+
`"stable": false`.
|
|
195
|
+
|
|
196
|
+
Name the clip file and the rendered video, and stop.
|
|
197
|
+
|
|
198
|
+
## 5. If this was the last round
|
|
199
|
+
|
|
200
|
+
If the verdict failed and `N == max-rounds`, write `summary.json` with
|
|
201
|
+
`"passed": false` and the last verdict inline. Say which items failed, and
|
|
202
|
+
stop; in a headless run, say plainly in your final message that it failed.
|
|
203
|
+
|
|
204
|
+
Do not quietly raise `max-rounds`. A clip that has not converged in six rounds
|
|
205
|
+
is a finding about the intent or the tools, and it is worth more written down
|
|
206
|
+
than ground out.
|
|
207
|
+
|
|
208
|
+
## 6. Otherwise — fix it, and go round again
|
|
209
|
+
|
|
210
|
+
Fix what the verdict names — the items that failed, read as written — and
|
|
211
|
+
nothing it did not name. When the authoring is delegated, send the author the
|
|
212
|
+
verdict JSON **verbatim** with `SendMessage`, by the id you kept — not
|
|
213
|
+
summarised, not reordered, not softened — and the instruction: fix the failing
|
|
214
|
+
items, re-record in draft, run `npx versioncam review`, and return. Add no
|
|
215
|
+
opinion of your own: the reviewer has looked at the clip in a way you have
|
|
216
|
+
not, and the author trusts the verdict because it is a reviewer's and not a
|
|
217
|
+
relayed impression.
|
|
218
|
+
|
|
219
|
+
Re-record in draft, run `review`, make `.versioncam/author/<id>/round-N/` for
|
|
220
|
+
the new round with its `author.md` and `beats.json`, and go back to step 3.
|
|
221
|
+
|
|
222
|
+
## What this leaves behind
|
|
223
|
+
|
|
224
|
+
```
|
|
225
|
+
.versioncam/author/<id>/
|
|
226
|
+
beats.json
|
|
227
|
+
summary.json { id, rounds, passed, wallMs?, reviewer }
|
|
228
|
+
<id>-sheet.png the final sheet
|
|
229
|
+
round-1/ author.md beats.json verdict.json (verdict-2.json)
|
|
230
|
+
round-2/ …
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
`.versioncam/` is gitignored — a build product, none of it to be committed. The
|
|
234
|
+
clip is not: it lives in the clips directory and is the thing worth reading and
|
|
235
|
+
keeping. You commit nothing; that is the person's call.
|