@vosjs/cli 0.81.0 → 0.82.0

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/dist/cli.js CHANGED
@@ -873,7 +873,7 @@ async function cmdCheck(argv) {
873
873
  return result.ok ? EXIT_OK : EXIT_ERROR;
874
874
  }
875
875
  async function delegate(argv) {
876
- const { run } = await import("./run-MEDD2Q74.js");
876
+ const { run } = await import("./run-CPFCYXVL.js");
877
877
  return await run(argv);
878
878
  }
879
879
  async function printHelp() {
@@ -907,7 +907,7 @@ async function main() {
907
907
  const engine = HELP_ENGINE.split("\n").filter(
908
908
  (l) => l.startsWith(` vos ${cmd} `)
909
909
  );
910
- const { verbHelp } = await import("./run-MEDD2Q74.js");
910
+ const { verbHelp } = await import("./run-CPFCYXVL.js");
911
911
  const take = verbHelp(cmd);
912
912
  process.stdout.write(
913
913
  `${engine.join("\n")}
@@ -935,7 +935,7 @@ ${take.includes("no such verb") ? "" : take}`
935
935
  main().then(async (code) => {
936
936
  if (code === EXIT_OK) {
937
937
  const verb = process.argv[2] ?? "";
938
- const { HELP } = await import("./run-MEDD2Q74.js");
938
+ const { HELP } = await import("./run-CPFCYXVL.js");
939
939
  const documented = [
940
940
  ...helpFlagsFor(HELP_ENGINE, verb),
941
941
  ...helpFlagsFor(HELP, verb)
package/dist/index.d.ts CHANGED
@@ -220,6 +220,7 @@ interface PlanSummary {
220
220
  motion?: {
221
221
  notes: string[];
222
222
  skipped: string[];
223
+ held: string[];
223
224
  };
224
225
  /** `--reuse`: what the re-anchor did and what it could not. */
225
226
  reuse?: ReuseReport & {
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ import {
14
14
  startTakeServer,
15
15
  validateActions,
16
16
  waitForPageDone
17
- } from "./chunk-S3RI2SHH.js";
17
+ } from "./chunk-5VGIQKVT.js";
18
18
  import {
19
19
  BrowserUnavailableError,
20
20
  configDuration,
@@ -9,7 +9,7 @@ import {
9
9
  takeBackdrop,
10
10
  takeBrowserArgs,
11
11
  verbHelp
12
- } from "./chunk-S3RI2SHH.js";
12
+ } from "./chunk-5VGIQKVT.js";
13
13
  import "./chunk-B373NVJE.js";
14
14
  import "./chunk-352AC3V6.js";
15
15
  import "./chunk-J3KHIZCH.js";
@@ -25,4 +25,4 @@ export {
25
25
  takeBrowserArgs,
26
26
  verbHelp
27
27
  };
28
- //# sourceMappingURL=run-MEDD2Q74.js.map
28
+ //# sourceMappingURL=run-CPFCYXVL.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vosjs/cli",
3
- "version": "0.81.0",
3
+ "version": "0.82.0",
4
4
  "description": "The vos CLI: record the real product from a scripted browser flow, auto-zoom from the cursor track, cut as data in doc.json, render deterministic video, deliver a release's video cuts per destination spec, and sync with vos.so. One binary, every verb, MIT.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -54,8 +54,8 @@
54
54
  "@vosjs/core": "^0.31.0",
55
55
  "@vosjs/editor": "^1.6.0",
56
56
  "@vosjs/elements": "^0.12.1",
57
- "@vosjs/shared": "^0.7.0",
58
57
  "@vosjs/render-core": "^0.10.0",
58
+ "@vosjs/shared": "^0.7.0",
59
59
  "@vosjs/studio-core": "^0.46.0",
60
60
  "@vosjs/tween": "^0.8.3",
61
61
  "@vosjs/timeline": "^0.5.0"
package/skills/VERSION CHANGED
@@ -1 +1 @@
1
- 0.26.0
1
+ 0.27.0
@@ -244,3 +244,10 @@ by data. Never spread before the seed is signed off.
244
244
  - A hand-typed dimension: every size comes from `channel-specs.json`.
245
245
  - The save beat on a demo instance that disables writes: say so in
246
246
  `skipped`, never fake the click.
247
+ - A zoom trusted because the track lists it: `frames --at-zooms` renders
248
+ each apex beside its zoom-off twin and names one that matches; read the
249
+ stills before `deliver`, never the list.
250
+ - A rehearsal that presses a step that spends or creates in the shipped
251
+ product: `--dry-run` CLICKS. Guard the spend (`"guard": "spend"` + `id`,
252
+ recorded only with `--confirm <id>`) and write the side-effect ledger
253
+ before the first rehearsal (the `product-video` skill's pre-flight).
@@ -52,6 +52,69 @@ failure / 3 no browser / 4 the recorder met a sign-in instead of the page
52
52
  | **Quick demo** (issue, PR, chat) | 1280×720 | webm, defaults | speed over polish; drafts acceptable here ONLY |
53
53
 
54
54
  Per-channel dimensions and byte budgets: `references/destinations.md`.
55
+ A **how-to** (one flow in a product, from a door to its result, for a docs
56
+ page or a changelog) takes the quick demo's viewport or the launch video's,
57
+ and its own grammar: `references/how-to.md`.
58
+
59
+ ## Pre-flight: six answers before the first take
60
+
61
+ A flow in somebody's product has consequences the footage does not show:
62
+ two rehearsals left two untitled drafts in a founder's projects and both
63
+ appeared in the recorded sidebar; a rehearsal that reaches "Generate"
64
+ spends a credit; a result that arrives twelve minutes later is a second
65
+ take without a plan for it. Answer these BEFORE `vos look`, the script or a
66
+ rehearsal. **Each answer either changes the script or is said to the
67
+ person before recording**; an answer that does neither was not an answer.
68
+
69
+ 1. **The door.** Which way through the product does the video walk, and
70
+ which others exist (a guided flow, a prompt box, an apps menu, a
71
+ template)? A how-to walks ONE. When there are several, list them and
72
+ confirm the door with the person before scripting; a door that works
73
+ under automation beats a recommended one that does not, and hiding the
74
+ automation from the site is never the answer (find another door, or
75
+ hand the shot list to a person: `references/sessions.md`, rung 4).
76
+ 2. **Whose account, face and work.** What the signed-in account shows ships
77
+ in the video: its avatar, its name, its recent items, the titles of its
78
+ other projects. A demo account is the answer; when the person records
79
+ from their own, say what will show before the take. The rehearsal
80
+ reports an address, a key or a card as EXPOSED (mask those), and the
81
+ account's own name and its recent lists as NOTICES: those are the
82
+ person's call. Say they will be on screen and offer the one-line `mask`;
83
+ never mask a name by default, and a face is yours to notice and theirs
84
+ to decide.
85
+ 3. **What the flow CREATES.** Write the side-effect ledger: for each step,
86
+ what it leaves in the product (a draft, a project, a message, a file, a
87
+ row), remembering that `--dry-run` CLICKS, so every rehearsal creates
88
+ them too. Say the ledger to the person before the first rehearsal when
89
+ the product is a real account, and report it at the end so they can
90
+ delete what was made. A product where creating is cheap (a sandbox, a
91
+ seeded local instance) needs the ledger written, not asked.
92
+ 4. **What it SPENDS.** A step that costs something real (a generation, a
93
+ purchase, a message sent, a deploy) carries `"guard": "spend"` and an
94
+ `id`. A rehearsal hovers it and never presses it; `vos record` refuses
95
+ the take before a browser opens unless the command names the step
96
+ (`--confirm <id>`), and the done event records that it was pressed.
97
+ Name the spend to the person in words ("one generation") and get the
98
+ yes before the record; the consent then lives in the file and the
99
+ command line, where a reviewer can read it.
100
+ 5. **Is the result async, and how long.** A product that finishes later (a
101
+ render, a generation, a deploy) is ONE take with an `await` step: the
102
+ capture stops, the page is polled off camera, the capture resumes when
103
+ the predicate holds, and the cut carries the measured time
104
+ (`{elapsed}` in the next caption; `references/how-to.md`). Wait for
105
+ what only the NEW result has (a percentage that appears, then goes;
106
+ never a title an older item shares), size `timeout` from what the
107
+ product measured last time, and `download` the output into
108
+ `take/outputs/` so the join can play it. On the hosted recorder the
109
+ waits come out of the footage cap: say how long before choosing it.
110
+ 6. **Does sound matter.** The recorder captures frames. Sound a page plays
111
+ through a `<video>` or `<audio>` element is kept (`@vosjs/cli` 0.79 and
112
+ later: each heard source lands in `meta.media` and the plan places a
113
+ clip per interval); sound a page synthesizes with Web Audio is not
114
+ heard, and a downloaded output's own sound rides as a clip through
115
+ `vos join`. When the sound IS the point (a voice preview, a generated
116
+ clip) and the product synthesizes it, say so before recording and plan
117
+ the download; when it is not, say the video is silent and score it.
55
118
 
56
119
  ## The core loop (every destination)
57
120
 
@@ -81,6 +144,16 @@ Per-channel dimensions and byte budgets: `references/destinations.md`.
81
144
  take directory and out of git, and record from a demo account: what the
82
145
  account shows ships in the video.
83
146
 
147
+ **Never script a signed-in page blind.** `vos look <url> --session <app>`
148
+ (or `--storage-state`; `@vosjs/cli` 0.78 and later) opens the page
149
+ through the door the recorder will use and prints a screenshot plus
150
+ every control in view with ONE proposed selector each, counted UNIQUE
151
+ or AMBIGUOUS by the recorder's own engine; `--click <selector>` shows
152
+ what a click opens before the script relies on it, and refuses a control
153
+ whose words mean spend, delete or submit. Take UNIQUE selectors into
154
+ steps as they are. Then the side-effect ledger (pre-flight, 3), then
155
+ rehearse.
156
+
84
157
  **Verified the feature with agent-browser already?** Keep that walk and
85
158
  skip the second script. agent-browser's `--json` result does not say
86
159
  what ran (`scroll` answers `{scrolled:true}`), so wrap each call so the
@@ -170,9 +243,13 @@ Per-channel dimensions and byte budgets: `references/destinations.md`.
170
243
  `references/sessions.md`.
171
244
 
172
245
  3. **Record**: `vos record --actions actions.json --out take --strict --json`
173
- `--strict` always: skipped selector / networkidle timeout → exit 2 with
174
- `skipped[]` in the done event. A skip means the flow is broken — fix it,
175
- never ship around it. The take auto-encodes and auto-plans.
246
+ `--strict` always: a skipped selector, a failed `expect`, a click on a
247
+ disabled control, a timed-out `await` or a capped take → exit 2 with
248
+ `skipped[]` in the done event (a first load that never goes network-idle
249
+ is a note, not a failure, since `@vosjs/cli` 0.78). A skip means the
250
+ flow is broken — fix it, never ship around it. A guarded step records
251
+ only with `--confirm <id>` (pre-flight, 4). The take auto-encodes and
252
+ auto-plans.
176
253
  (`vos create --actions actions.json out.webm --strict` is the one-shot
177
254
  record+render verb — fine for a quick first pass, but THIS skill's loop
178
255
  reviews frames before rendering, so prefer the separate verbs here.)
@@ -295,3 +372,14 @@ when the ask is a release, not one video.
295
372
  one per gesture): mark the payoff steps and plan `--zooms per-beat`.
296
373
  - Trusting `--strict` for the scene: it proves the steps ran, not that the
297
374
  app holds what the beat shows. Check with `expect` + `js` + `equals`.
375
+ - Trusting a zoom because the track lists it: two of a film's four zooms
376
+ never rendered and the list said they did. `frames --at-zooms` renders
377
+ each apex beside its zoom-off twin and names an apex that matches its
378
+ twin; read the stills, never the list.
379
+ - A rehearsal that presses a step that spends: `--dry-run` CLICKS. A
380
+ spending step carries `"guard": "spend"` and an `id`, so the rehearsal
381
+ hovers it and `record` refuses it without `--confirm <id>`; a step that
382
+ only creates goes in the side-effect ledger before the first rehearsal.
383
+ - Scripting a signed-in product blind: `vos look` first, then the ledger,
384
+ then the rehearsal. Guessing selectors from a public page costs a take
385
+ per guess.
@@ -0,0 +1,78 @@
1
+ # The how-to: one flow, one door, the result first
2
+
3
+ A how-to shows a person how to reach ONE result in a product: "make a
4
+ cinematic shot in HeyGen", "put a video on vos.so". It is the clip a docs
5
+ page, a changelog entry or a support answer embeds, and it is judged by
6
+ whether a viewer can repeat the path. The marketing rules in `taste.md`
7
+ still hold (one idea, open on the money shot, end settled); these are the
8
+ rules that are specific to the genre. They came out of three agent-made
9
+ how-tos and the cuts a person had to redo on them.
10
+
11
+ ## The grammar
12
+
13
+ 1. **One door.** A product usually has several ways to the same result
14
+ (a guided flow, a prompt box, an apps menu, a template). The video walks
15
+ ONE, and the person named it before the script was written (the
16
+ pre-flight in `SKILL.md`). Two doors in one clip read as two videos;
17
+ a door that works under automation beats the recommended one that does
18
+ not (never hide the automation from a site; pick the door that works, or
19
+ hand the shot list to a person).
20
+
21
+ 2. **One caption per step, in the product's own words for the control.**
22
+ The caption names the control the way the product labels it, as a verb
23
+ phrase: "Open Apps", "Choose Cinematic Shots", "Press Generate". Not a
24
+ sentence about the feature, not marketing copy, nothing the footage does
25
+ not show. It sits on the step whose action it describes (`caption` on
26
+ the step in `actions.json`; `vos plan` and `vos join --captions` land
27
+ each one anchored to its step, so a re-record carries it). One caption
28
+ is one beat; a step with no caption is part of the beat before it.
29
+
30
+ 3. **The result is the payoff, and the cold open.** Open on the finished
31
+ result for a beat (a repeated segment at camera rest,
32
+ `segments[].camera: "rest"`, the `vos-cut` skill's "Assemble"), then
33
+ the path from the door, then the result again, settled, as the last
34
+ frame. The result's step is the beat marked `"zoom": true`; the path's
35
+ clicks stay wide unless one of them is the point. `still` sits on the
36
+ result, never on the door.
37
+
38
+ 4. **A recap card that lists the path.** The last thing on screen is the
39
+ path as a list, in the captions' own words, in order ("Apps → Cinematic
40
+ Shots → Describe the shot → Generate"), on the plate after the card
41
+ fades (`frame.anim.exit: "fade"` plus layers past the footage), never
42
+ over a frozen frame. A viewer who looked away for the middle leaves with
43
+ the path.
44
+
45
+ 5. **Measured times only.** When the product takes time off camera (`await`
46
+ in the script), the cut says how long it took in the words the
47
+ measurement gives: `{elapsed}` in the caption after the wait prints the
48
+ measured time ("8 minutes later, the shot is ready"), or write the
49
+ number read from `meta.skips[].ms` ("About 8 minutes later"). Never a
50
+ round number nobody measured, never "instantly" or "in seconds", and
51
+ never a speed read off a capture (the recorder loads the page too).
52
+
53
+ 6. **Music lands on the result.** `vos score --track <slug> --at <result
54
+ step id>` puts the track's swell on the payoff; a how-to with a voice
55
+ or the product's own sound (a generated video playing) keeps the bed
56
+ under it (`--gain`). The downloaded output's own sound rides as a clip
57
+ through `vos join`.
58
+
59
+ 7. **As long as its path.** A how-to is not held to the 30 s marketing
60
+ band; it is held to its beats: about 1 s of reading after a page
61
+ changes, 0.6 s after a control, the waits cut out, nothing padded.
62
+ Run 3's how-to was 36 s for seven steps and an 8-minute generation.
63
+
64
+ ## The checks, in order
65
+
66
+ ```bash
67
+ vos validate actions.json # the captions sit on the steps they describe
68
+ vos record --actions actions.json --out take --dry-run # the door opens, the guard hovers, the wait is timed
69
+ vos record --actions actions.json --out take --strict --json --confirm <spend id>
70
+ vos plan take --zooms per-beat --still <result output s>
71
+ vos frames take --at-zooms # every apex beside its zoom-off twin
72
+ vos frames take --times <each caption start + 1> # every caption on its own ground, in the product's words
73
+ vos check-video out.mp4 # one cut per 5 s at most, the first and last frames stand alone
74
+ ```
75
+
76
+ A caption the footage does not show, a time that was not measured, a second
77
+ door, or a result that is not the first and last frame each send the cut
78
+ back a round, not the recording.
@@ -25,6 +25,10 @@ The rubric for judging a take before its render ships. Judge stills
25
25
  Launch videos may run longer but every scene must still earn its seconds.
26
26
  3. **End settled.** Close on a resolved state (result visible, motion parked),
27
27
  never mid-action — the loop point should read as intentional.
28
+ A **how-to** (one flow to one result, for a docs page or a changelog)
29
+ keeps all three and adds its own: one door, one caption per step in
30
+ the product's words for the control, the result as the cold open and
31
+ the last frame, a recap card, measured times only (`how-to.md`).
28
32
 
29
33
  ## Flow authoring (actions.json)
30
34
 
@@ -49,14 +49,18 @@ selector matched no element, so whatever it was for may be showing.
49
49
 
50
50
  ## Strict-mode failures (exit 2)
51
51
 
52
- `vos record --strict` exits 2 when a selector was skipped or a navigation
53
- never reached networkidle, with `skipped[]` in the `--json` done event.
52
+ `vos record --strict` exits 2 when a selector was skipped, an `expect`
53
+ failed, a click landed on a disabled control, an `await` timed out or the
54
+ take hit its cap, with `skipped[]` in the `--json` done event.
54
55
 
55
56
  - A skipped selector means the flow is broken: the element wasn't there, the
56
57
  selector is unstable (nth-child chains), or the page needed more settle
57
58
  time. Fix the actions.json — never ship a take that silently skipped steps.
58
- - Networkidle timeouts on pages with long-polling/websockets: add explicit
59
- `wait` steps after navigation instead of relying on networkidle.
59
+ - A first load that never goes network-idle (long-polling, websockets: most
60
+ SaaS) is a note (`navTimeout`), never a strict failure, since `@vosjs/cli`
61
+ 0.78, and since 0.79 a take starts once the DOM has stopped changing
62
+ (`ready: "dom"`, the default; `networkidle` is the opt-in). On an older
63
+ CLI, add explicit `wait` steps after navigation instead of relying on it.
60
64
  - Always pass `--strict`. The lenient default exits 0 over broken flows,
61
65
  which is how bad takes ship.
62
66
 
@@ -102,6 +102,27 @@ video PLAYING, not idle. Neither wants the planner's proposal.
102
102
  If a folder is involved, read EVERY `.md` in it (own and inherited) before
103
103
  you decide anything.
104
104
 
105
+ **Before the first cut, four answers from the frames** (a human's take has
106
+ no mask and no exposure list; the scan needs the page):
107
+
108
+ 1. **Whose account, face and work is on screen.** An address, a key or a
109
+ card in a frame is said before anything is pushed, and the honest fix
110
+ is a re-record from a demo account, since a blur in `doc.json` leaves
111
+ the value in the footage `vos push` uploads. The person's own name,
112
+ face, avatar and recent items are theirs to show: say they are on
113
+ screen, never treat them as a defect.
114
+ 2. **Which door the footage walks.** A how-to names ONE way to the result.
115
+ A recording that wanders through two (a guided flow abandoned for an
116
+ apps menu) is cut to the one that reached the result, and the note says
117
+ which.
118
+ 3. **Where time was skipped.** A scripted take's off-camera wait is in
119
+ `meta.skips` and `vos info` prints it; the caption after it carries the
120
+ MEASURED time, never a round number nobody measured.
121
+ 4. **What the sound is.** The take's own sound (a mic), the sound the page
122
+ played (`meta.media`, placed as `source: "page"` clips by the plan),
123
+ the sound a downloaded output brought through `vos join`. The cut keeps
124
+ a sound the beat is about and never speeds over it.
125
+
105
126
  ## 2. Narrate
106
127
 
107
128
  Write 3-6 beats into your working notes, each with its moment ids, what it
@@ -163,6 +184,16 @@ Per beat, against `plan` and the recipe:
163
184
  light (an editor's timeline is), and judge every caption at its own
164
185
  instant. One caption per film is too sparse for a film that explains a
165
186
  flow; zero is right only when the recipe or the ask says no text.
187
+ - **A how-to** (one flow to one result, for a docs page or a changelog)
188
+ has a grammar of its own: one caption per STEP, in the product's own
189
+ words for the control as a verb phrase ("Open Apps", "Press Generate"),
190
+ anchored to the step (`anchor: { step }`) so a re-record carries it;
191
+ the result as the cold open (a repeated segment at camera rest) and as
192
+ the last frame, with `still` on it and the one `"zoom": true` mark;
193
+ a recap card listing the path in the captions' words on the plate after
194
+ the card fades; an off-camera wait said in measured time (`{elapsed}`
195
+ in the caption after it); music scored `--at` the result's step. Never
196
+ a caption the footage does not show, never a second door.
166
197
  - **Style.** In a series, `vos plan --style <seed>` BEFORE you cut: it copies
167
198
  the seed's `zoomStyle`/`zoomParams`/`speedParams`/`tiltStyle`/`frame`/
168
199
  `cursor`/`cam`/`export` and re-plans the auto spans under them. Never
@@ -254,6 +285,9 @@ never write a status line.
254
285
  start, under the first caption);
255
286
  - open a zoom before the click it frames, or aim it at a `type` verb's
256
287
  field centre (frame the text; start the span after the click);
288
+ - trust a zoom because the track lists it: `frames --at-zooms` renders
289
+ each apex beside its zoom-off twin and names one that matches its twin;
290
+ a listed zoom that never rendered has shipped in a film before;
257
291
  - leave the cold open frozen (a static page records as freeze-then-bang:
258
292
  trim it, or speed the load);
259
293
  - end wide on an empty page: the last frame is the poster, so the closing