hyperframes 0.8.37 → 0.8.39
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 +136 -148
- package/dist/docs/examples.md +1 -1
- package/dist/fontLocalizeCli.js +18 -95
- package/dist/hyperframes-player.global.js +1 -1
- package/dist/skills/hyperframes/SKILL.md +1 -1
- package/dist/skills/hyperframes/references/brief-contract.md +108 -0
- package/dist/skills/hyperframes/references/brief-format.md +72 -0
- package/dist/skills/hyperframes/references/capability-menu.md +1 -1
- package/dist/skills/hyperframes/references/frame-worker-core.md +80 -0
- package/dist/skills/hyperframes/references/intent-interview.md +8 -8
- package/dist/skills/hyperframes/references/production-loop.md +26 -0
- package/dist/skills/hyperframes/references/review-loop.md +41 -0
- package/dist/skills/hyperframes/references/route-briefs.md +1 -1
- package/dist/skills/hyperframes/references/script-format.md +52 -0
- package/dist/skills/hyperframes/references/storyboard-format.md +130 -0
- package/dist/skills/hyperframes/references/subagent-dispatch.md +35 -0
- package/dist/skills/hyperframes/scripts/lib/frame-packets-core.mjs +212 -0
- package/dist/skills/hyperframes-cli/SKILL.md +9 -8
- package/dist/skills/hyperframes-cli/references/init-and-scaffold.md +6 -6
- package/dist/skills/hyperframes-cli/references/preview-render.md +6 -3
- package/dist/studio/assets/{hyperframes-player-DrGk-lf5.js → hyperframes-player-BBFfad14.js} +1 -1
- package/dist/studio/assets/{index-DMsdtB-j.js → index-3XC5KvGR.js} +4 -4
- package/dist/studio/assets/{index-xK1YGiPD.js → index-CtYBUtk8.js} +1 -1
- package/dist/studio/assets/{index-BtGF7Kl-.js → index-De_8OqJf.js} +1 -1
- package/dist/studio/index.html +1 -1
- package/dist/studio/index.js +1 -1
- package/dist/studio/index.js.map +1 -1
- package/dist/templates/blank/index.html +17 -37
- package/dist/templates/from-file/index.html +74 -0
- package/package.json +1 -1
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Production loop — from an approved plan to a delivered video
|
|
2
|
+
|
|
3
|
+
The stages between a plan the user has agreed to and a video in their hands, written as **dependencies, not numbered steps**: order between independent stages is free — audio renders in the background while frames build; that is the standard trick — order inside a dependency chain is not. Nothing in this file addresses the user: every user-facing pause lives in `review-loop.md`, and this file only marks where those passes attach. Some routes bring their own spine (a beat grid, existing footage) — the stages compose around it. A stage whose need is absent simply doesn't run: no narration, no audio stage; a single scene, no transitions. An edit request enters at the artifact it touches and re-runs verify.
|
|
4
|
+
|
|
5
|
+
The shipped narrative workflows implement these stages with their own scripts; a freeform build follows this file directly, borrowing tools where the capability menu says they live (`hyperframes/references/capability-menu.md`).
|
|
6
|
+
|
|
7
|
+
| Stage | Needs | Produces | Where the capability lives |
|
|
8
|
+
| ------------------- | ------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
9
|
+
| **Blocks & assets** | the approved plan | registry blocks installed **once, before any parallel work** (parallel workers race the registry); user assets staged; logos / images / grades resolved | `npx hyperframes add <block>` per block the plan names; staging, adoption, and `resolve` via the menu's media rows |
|
|
10
|
+
| **Audio** | narration text (when narrated); the storyboard's `music:` mood | voice files + **word timings** + BGM + SFX → `audio_meta.json`; **when BGM plays under a voice, the bed is carved** (`hyperframes-audio/scripts/carve.mjs`) before Verify — a duck alone is not a finished mix | the one engine — `media-use/audio/scripts/audio.mjs`, run in the background; `wait-bgm.mjs` before render when BGM generates |
|
|
11
|
+
| **Frames** | design spec + the plan (+ a confirmed sketch when one exists — dress that layout, never redraw it: `review-loop.md` § 3) | each scene at `compositions/frames/NN-*.html`, marked `animated` in the storyboard as it lands | `frame.md` + `hyperframes-animation` blueprints / rules (+ the genre lens, menu § Genre lenses); parallel dispatch per `subagent-dispatch.md` |
|
|
12
|
+
| **Duration sync** | word timings + frames | scene durations trued to real voice length — real duration wins, silent scenes keep estimates, synced values are never hand-edited | a mechanical rule; the narrative workflows' audio scripts apply it, a freeform build applies it by hand |
|
|
13
|
+
| **Assembly** | frames | the index composition — scenes as sub-compositions on tracks | `sub-compositions.md` + `tracks-and-clips.md`; borrowable `assemble-index.mjs` (menu) |
|
|
14
|
+
| **Transitions** | the assembled index | scene handoffs injected | `hyperframes-animation/transitions/overview.md` → `catalog.md`; borrowable `transitions.mjs` (menu) |
|
|
15
|
+
| **Captions** | word timings + the index | the caption track | borrowable `captions.mjs` (menu); no script to time against → `media-use` `scripts/transcribe.mjs` first |
|
|
16
|
+
| **Verify** | the index (+ captions / transitions when present) | `npx hyperframes lint` and `npx hyperframes check` **passing**; a contact-sheet glance (`snapshot --at <frame-midpoints>`) | `hyperframes-cli` |
|
|
17
|
+
| **Deliver** | verify passing | the final-look pause → on approval `render` → optionally `publish` (a stable hosted link, private by default) → the recipe offer | final approval and recipe offer: `review-loop.md` § 4; render / publish: `hyperframes-cli` |
|
|
18
|
+
|
|
19
|
+
The Frames stage follows the plan's citations: a scene planned on a blueprint or on named rules is built by reading that recipe's body (`hyperframes-animation/blueprints/<id>.md`, `rules/<id>.md`) before its motion is written — names come from the indexes, never invented, and a scene the plan left uncited gets its citation at build time, not improvised motion.
|
|
20
|
+
|
|
21
|
+
## Scheduling economics (facts you can't see from inside the session)
|
|
22
|
+
|
|
23
|
+
- **External generations are independent work.** Image plates, TTS, BGM, video gen: fire every generation whose prompt is already known **concurrently or in the background**, and overlap the wait with reading or building. Three image plates generated one-after-another cost ~3× the wall time of firing them together.
|
|
24
|
+
- **Attaching an image re-prices your whole context.** A mid-session image inspection (especially at original detail) invalidates the prompt cache — the next request re-sends your entire history at full price. Batch visual checks (one contact sheet beats N single-frame views) and schedule them at phase boundaries, not mid-build.
|
|
25
|
+
|
|
26
|
+
Two attach points carry the user's voice into this loop: the plan that starts it was approved at `review-loop.md` § 1 (collaborative) or posted as a heads-up (autonomous), and nothing renders before the § 4 final look. Everything between those two is yours to schedule.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# The review loop — plan, sketch, build
|
|
2
|
+
|
|
3
|
+
How a `storyboard: yes` run earns fidelity one pass at a time: the plan is reviewed as text on a live board, the layouts as wireframe sketches, and the finished piece as the assembled video. Collaborative mode waits at each checkpoint. Autonomous mode posts the same checkpoint summaries and continues, keeping exactly one question before render.
|
|
4
|
+
|
|
5
|
+
This is the shared process for any workflow that plans on a storyboard. The contracts it leans on live next door: interaction mode, gate types, and the comments channel in `brief-contract.md`; the `STORYBOARD.md` format, the `outline → built → animated` statuses, and the comments sidecar in `storyboard-format.md`. A workflow's SKILL.md says **when** its steps hit each pass and supplies its **sketch stand-ins** (what the plain blocks represent); how the loop runs is defined here, once. The stage mechanics between the passes — audio, frames, assembly, transitions, captions, verify — live in `production-loop.md`; this file owns only the user-facing pauses.
|
|
6
|
+
|
|
7
|
+
## § 1 — The plan, on a live board
|
|
8
|
+
|
|
9
|
+
Open the **storyboard board** before presenting the plan: run `npx hyperframes preview --background` from the project directory, confirm it is serving, and open `http://localhost:<port>/?view=storyboard#project/<project-name>`. This is an early planning surface, not the final composition preview; it may open before composition checks. The plan appears as frame cards and refreshes as work lands.
|
|
10
|
+
|
|
11
|
+
Present the plan as a proposal (shape: `hyperframes-creative/references/story-spine.md` § 3): open by echoing **"This video tells [audience] that [message]"**, then the frame table — one row per frame: frame · beat (type, duration) · on screen · why (its `narrativeRole`, traced to the message). Hand the board URL with it, noting feedback lands in both places — comment on the board or reply here, one revision loop — and that a board submit still needs one reply here (anything) to get picked up.
|
|
12
|
+
|
|
13
|
+
In the same message ask two things: **(a)** approve or request changes, and **(b)** **sketches first** (recommended — a quick wireframe look check right after this approval) or skip sketches and build in one go. Iterate until approved — feedback arrives in chat or as the comments file (`brief-contract.md` § 1, the comments channel): revise exactly the frames it names, clear the file, re-present.
|
|
14
|
+
|
|
15
|
+
This is a **checkpoint gate** (`brief-contract.md` § 1). A run that starts autonomous normally has `storyboard: no` and does not enter this loop. If mode switches to autonomous after a board exists, keep updating the board, post the same summary as a heads-up, and continue without waiting; the one kept question comes at § 4.
|
|
16
|
+
|
|
17
|
+
## § 2 — The sketch pass (collaborative, unless skipped)
|
|
18
|
+
|
|
19
|
+
The moment the plan is approved, wireframe every frame yourself — no sub-agents, no waiting on other steps (sketches don't use timings), straight from the approved frame table.
|
|
20
|
+
|
|
21
|
+
A sketch is a **wireframe with the real words, not a styled frame**: the frame's layout at its key moment — the actual headline / stat / label text placed where it will live, plain blocks for panels, charts, diagrams, and media (the workflow says what its blocks stand in for), `frame.md`'s background and ink plus one accent on the focal, nothing else. No decoration, no full brand treatment, **no motion** — all of that arrives with the build pass.
|
|
22
|
+
|
|
23
|
+
Keep each sketch a real composition file at `compositions/frames/NN-*.html` (template wrapper, `data-composition-id`, `#root` styling, one paused **empty** timeline registered at `window.__timelines["<frame_id>"]`) so the Studio poster renders — and the poster is the only picture this pass needs: **run no CLI here** — no `snapshot`, no `lint` / `check`, no rendering. A sketch is a few dozen lines of HTML; the whole board lands in minutes.
|
|
24
|
+
|
|
25
|
+
Mark each frame `built` as its sketch lands — the user's open board fills in blue by itself. When every frame is `built`, pause and ask one thing: does the board look right, or which frames change? This is a **checkpoint gate**; the user reviews in Studio (open since § 1 — restart and re-hand the same URL if the server died), and feedback arrives in chat or as the comments file — check the file first when the reply arrives: revise **only the sketches named**, re-present, and loop until the layout is confirmed. Only then does the workflow's visual design get written onto the confirmed layouts.
|
|
26
|
+
|
|
27
|
+
A confirmed board is also a valid place to **stop**. When the user asked for a storyboard rather than a finished video — a plan to pitch, review, or hand off — the sketched board is the deliverable: confirm it, hand the board URL, and go no further unless asked to build.
|
|
28
|
+
|
|
29
|
+
In autonomous mode, or when the user chose to skip sketches at § 1, skip this pass — frames go straight from `outline` to `animated` in the build.
|
|
30
|
+
|
|
31
|
+
## § 3 — Building on confirmed layouts
|
|
32
|
+
|
|
33
|
+
However the workflow builds — sub-agent workers per frame, or inline scene by scene — a confirmed sketch's **composition is settled**: placement, hierarchy, and copy were approved on the board, so building means dressing that layout (full design treatment, real assets, motion), never redrawing it. Workflows that dispatch workers put "this frame has a **confirmed sketch** on disk" in the worker's context and carry the keep-the-layout rule in their worker prompt; a landed frame must still read as the approved wireframe, fully dressed.
|
|
34
|
+
|
|
35
|
+
Mark each frame `animated` as it lands. The build gate carries the loop's condition: in collaborative mode, the sketch board was confirmed at § 2.
|
|
36
|
+
|
|
37
|
+
## § 4 — The final look
|
|
38
|
+
|
|
39
|
+
After the workflow's checks pass, use the **final composition preview**. In collaborative mode Studio may already be serving from § 1; hand the timeline URL and ask one thing: render now, or what changes? In autonomous mode this is the one question the mode keeps: ask “preview first, or render?” Open the final preview on yes; render on an explicit render answer. Render only on approval.
|
|
40
|
+
|
|
41
|
+
**After approval, offer the recipe — once.** An approved run is a proven bundle. At delivery, offer to freeze it: `media-use` → `scripts/recipe.mjs freeze --name <name>` (the workflow comes from BRIEF.md; pass `--workflow` only in a project without one) keeps the design spec, the storyboard skeleton (structure kept, content blanked), the brief skeleton, and the confirmed brief values, and the next run of this type starts from it (the intent layer checks for a matching recipe before its first question). When the freeze lands, teach the recall in the confirmation — "Saved as **<name>** (v<N>). Next time say _make another <name>_, or just _like last time_." — the name is something the system reminds the user of, never something they must remember. In autonomous mode don't ask — name the freeze command in the delivery note instead.
|
|
@@ -8,4 +8,4 @@ route's own file, together with its input/output/trigger contract:
|
|
|
8
8
|
`routes/pr-to-video.md` (including the PR-size → length table).
|
|
9
9
|
|
|
10
10
|
The interview procedure itself (the eight steps) is `references/intent-interview.md`.
|
|
11
|
-
Field semantics and question rules: `hyperframes
|
|
11
|
+
Field semantics and question rules: `hyperframes/references/brief-contract.md` § 2–3.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# `SCRIPT.md` — locked narration (optional)
|
|
2
|
+
|
|
3
|
+
The **locked narration** for a project: the final spoken lines + voice + delivery. It is an _optional_ plan-layer file — a video with no narration (bgm-only, silent overlay) has none. The storyboard's per-frame `voiceover` is the lighter, editable _guide_; `SCRIPT.md` is the _commit_. (Storyboard format → `references/storyboard-format.md`.)
|
|
4
|
+
|
|
5
|
+
This file defines the SCRIPT.md **shape** only. Synthesizing the spoken lines into audio is a capability owned by `media-use` → `../../media-use/audio/references/tts.md`.
|
|
6
|
+
|
|
7
|
+
Free-form markdown — there is no strict parser; the Studio renders it read-only beside the Storyboard board, and the TTS step extracts the indented spoken lines.
|
|
8
|
+
|
|
9
|
+
## Shape
|
|
10
|
+
|
|
11
|
+
A header block, then one section per spoken line.
|
|
12
|
+
|
|
13
|
+
| Part | Holds |
|
|
14
|
+
| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
|
|
15
|
+
| Header | `**Voice:**` (provider + voice), `**Voice settings:**` (e.g. stability / similarity / style), `**Voice direction:**` (overall delivery) |
|
|
16
|
+
| `## Line N — <label> (Frame N)` | one spoken line, tied to its storyboard frame |
|
|
17
|
+
| `**Time:**` | the board's rough window — a _guide_, not authoritative (real timing comes from TTS word timestamps) |
|
|
18
|
+
| `**Delivery:**` | per-line delivery note |
|
|
19
|
+
| indented block | the **spoken text** — the only part fed to TTS |
|
|
20
|
+
|
|
21
|
+
## Example
|
|
22
|
+
|
|
23
|
+
```markdown
|
|
24
|
+
# SCRIPT — acme-launch
|
|
25
|
+
|
|
26
|
+
**Voice:** Rachel (ElevenLabs)
|
|
27
|
+
**Voice settings:** stability 0.35 · similarity 0.75 · style 0.20
|
|
28
|
+
**Voice direction:** Confident, warm, a little playful.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Line 1 — Hook (Frame 1)
|
|
33
|
+
|
|
34
|
+
**Time:** 0.0 – 3.0s
|
|
35
|
+
**Delivery:** Land the promise on the beat.
|
|
36
|
+
|
|
37
|
+
Ship a launch video in an afternoon.
|
|
38
|
+
|
|
39
|
+
## Line 2 — The problem (Frame 2)
|
|
40
|
+
|
|
41
|
+
**Time:** 3.0 – 7.0s
|
|
42
|
+
**Delivery:** Wry, a touch tired.
|
|
43
|
+
|
|
44
|
+
The old way? Prompt, wait twenty minutes, get something that misses.
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## To TTS
|
|
48
|
+
|
|
49
|
+
Feed each line's spoken text to the provider documented in
|
|
50
|
+
`media-use/audio/references/tts.md`. The `hyperframes tts` command is
|
|
51
|
+
Kokoro-only; use its `--voice` flag, or use the bundled HeyGen helper when word
|
|
52
|
+
timestamps are required. Real per-word timing replaces the `**Time:**` guides.
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# Storyboard format — `STORYBOARD.md` + parsed manifest
|
|
2
|
+
|
|
3
|
+
Defines the storyboard's **base data format** only: the `STORYBOARD.md` file shape and the `StoryboardManifest` it parses into. How a workflow _generates_ a storyboard lives in that workflow; the optional narration/TTS file (`SCRIPT.md`) is a separate concern owned by the TTS step, not here.
|
|
4
|
+
|
|
5
|
+
A storyboard is the **plan layer** for a video — an ordered set of **frames** (key moments) in one markdown file. HyperFrames Studio renders it as a contact sheet (the Storyboard view, available by default in every Studio session). Parser: `@hyperframes/core/storyboard` → `StoryboardManifest`; read API: `GET /api/projects/<id>/storyboard`.
|
|
6
|
+
|
|
7
|
+
## Frontmatter (global direction)
|
|
8
|
+
|
|
9
|
+
YAML block at the top. Unknown keys are kept under `globals.extra`.
|
|
10
|
+
|
|
11
|
+
| Key | Meaning | Example |
|
|
12
|
+
| ---------- | ----------------------------------------------------------------- | ----------------------------------------- |
|
|
13
|
+
| `format` | Canvas size | `1920x1080` |
|
|
14
|
+
| `duration` | The brief's rough length expectation (advisory, not a hard limit) | `22s` |
|
|
15
|
+
| `message` | One-line thesis | `Ship a launch video in an afternoon` |
|
|
16
|
+
| `arc` | Narrative arc | `Hook → Problem → Solution → Proof → CTA` |
|
|
17
|
+
| `audience` | Who it's for | `indie devs on X` |
|
|
18
|
+
| `mode` | Interaction mode (see `brief-contract.md`; default collaborative) | `autonomous` |
|
|
19
|
+
|
|
20
|
+
Set `duration` from the brief's `length` when the storyboard is first written. It is an expectation, not a gate: assembly reports where the cut actually lands against it and flags a large gap — judge whether the drift serves the piece, and update the value when the intended length genuinely changes.
|
|
21
|
+
|
|
22
|
+
## Per-frame sections
|
|
23
|
+
|
|
24
|
+
One `## Frame N — Title` heading per frame (`Frame` / `Beat` / `Scene` accepted at H2/H3). Metadata as `- key: value` bullets; everything below them until the next heading is the free-form **narrative**.
|
|
25
|
+
|
|
26
|
+
| Key | Meaning |
|
|
27
|
+
| --------------- | ------------------------------------------------------------------------------------------------------------- |
|
|
28
|
+
| `status` | `outline` → `built` → `animated` (defaults `outline`) |
|
|
29
|
+
| `src` | project-relative path to the frame's HTML sub-composition (the tile poster renders from it) |
|
|
30
|
+
| `duration` | e.g. `4s` |
|
|
31
|
+
| `transition_in` | `crossfade` / `cut` / `wipe` … (alias `transition`) |
|
|
32
|
+
| `scene` | one-line contact-sheet caption (aliases `description` / `summary` / `caption`) |
|
|
33
|
+
| `voiceover` | the frame's narration _guide_ (aliases `vo` / `voice_over` / `narration`) |
|
|
34
|
+
| `poster` | seconds to seek for the tile poster (past the intro animation) |
|
|
35
|
+
| _any other key_ | kept verbatim under the frame's `extra` — a workflow carries its own per-frame data (effects, assets, …) here |
|
|
36
|
+
|
|
37
|
+
## Parsed manifest
|
|
38
|
+
|
|
39
|
+
The parser is **lenient**: it never throws and records anything surprising as a `warning`.
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
StoryboardManifest {
|
|
43
|
+
globals: { format?, message?, arc?, audience?, extra: {…} }
|
|
44
|
+
frames: Array<{
|
|
45
|
+
index, number?, title?,
|
|
46
|
+
status, // "outline" | "built" | "animated"
|
|
47
|
+
src?, duration? / durationSeconds?, transitionIn?,
|
|
48
|
+
scene?, voiceover?, poster?,
|
|
49
|
+
narrative, // markdown below the metadata
|
|
50
|
+
extra: {…} // unknown keys, preserved
|
|
51
|
+
}>
|
|
52
|
+
warnings: Array<{ message, line?, frameIndex? }>
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
The read API also adds `srcExists` per frame and attaches the optional `SCRIPT.md` payload when present.
|
|
57
|
+
|
|
58
|
+
## `SCRIPT.md` (out of scope here)
|
|
59
|
+
|
|
60
|
+
Optional, free-form, **not parsed into the manifest** — the locked-narration file that drives TTS. Its format is defined in `references/script-format.md`, and it is absent for videos with no narration/TTS. The per-frame `voiceover` above is the storyboard's own narration guide.
|
|
61
|
+
|
|
62
|
+
## Frame comments — `.hyperframes/frame-comments.json`
|
|
63
|
+
|
|
64
|
+
The storyboard review's **structured feedback channel** — the file Studio's per-frame comment boxes write on submit (chat feedback follows the same rule — `brief-contract.md` § 1, the comments channel). Like `SCRIPT.md`, it is a sibling of the storyboard, not parsed into the manifest.
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"version": 1,
|
|
69
|
+
"pass": "sketch",
|
|
70
|
+
"submitted_at": "2026-07-09T12:04:00Z",
|
|
71
|
+
"comments": [
|
|
72
|
+
{
|
|
73
|
+
"frame": 3,
|
|
74
|
+
"src": "compositions/frames/03-mechanism.html",
|
|
75
|
+
"title": "Mechanism",
|
|
76
|
+
"text": "Swap the bar chart for a before/after slider."
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
| Field | Meaning |
|
|
83
|
+
| ------------------------ | ------------------------------------------------------------------------------------------------------------------- |
|
|
84
|
+
| `pass` | which review the batch belongs to: `storyboard` (text layer) / `sketch` (static frames) / `final` (assembled video) |
|
|
85
|
+
| `comments[].frame` | the frame's 1-based `index` in the manifest — the key |
|
|
86
|
+
| `comments[].src` `title` | copied from the frame at submit time — if frames get reordered after submit, the mismatch shows |
|
|
87
|
+
| `comments[].text` | the feedback, verbatim |
|
|
88
|
+
|
|
89
|
+
Lifecycle — the whole contract: a workflow finding this file at a checkpoint treats it as the revision feedback — **revise exactly the frames named, delete the file, re-present**. Writers create it only on submit; it never lingers across rounds.
|
|
90
|
+
|
|
91
|
+
## Example
|
|
92
|
+
|
|
93
|
+
```markdown
|
|
94
|
+
---
|
|
95
|
+
format: 1920x1080
|
|
96
|
+
message: "Ship a launch video in an afternoon"
|
|
97
|
+
arc: Hook → Problem → Solution → Proof → CTA
|
|
98
|
+
audience: indie devs on X
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Frame 1 — Hook
|
|
102
|
+
|
|
103
|
+
- scene: Big type punches in on the beat
|
|
104
|
+
- duration: 3s
|
|
105
|
+
- poster: 2s
|
|
106
|
+
- transition_in: cut
|
|
107
|
+
- status: animated
|
|
108
|
+
- voiceover: "Ship a launch video in an afternoon."
|
|
109
|
+
- src: compositions/frames/01-hook.html
|
|
110
|
+
|
|
111
|
+
Open cold on the promise. This is the thesis — everything after pays it off.
|
|
112
|
+
|
|
113
|
+
## Frame 2 — The problem
|
|
114
|
+
|
|
115
|
+
- scene: A 20-minute timer spins on a stack of rejected takes
|
|
116
|
+
- duration: 4s
|
|
117
|
+
- transition_in: crossfade
|
|
118
|
+
- status: built
|
|
119
|
+
- voiceover: "The old way? Prompt, wait twenty minutes, get something that misses."
|
|
120
|
+
- src: compositions/frames/02-problem.html
|
|
121
|
+
|
|
122
|
+
The old way: prompt, wait, get something that misses. Establish the pain we remove.
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Notes
|
|
126
|
+
|
|
127
|
+
- A frame with `status: outline` and no built `src` renders as an outline placeholder.
|
|
128
|
+
- `built` is the middle rung: the frame's HTML exists and its **layout is confirmed** (a wireframe sketch or better) — motion not yet added. Studio chips it blue.
|
|
129
|
+
- The process that walks these statuses — plan, sketch, build, each pass reviewed on the board — is `review-loop.md`.
|
|
130
|
+
- Multi-line `voiceover` values collapse to one line on save.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Subagent dispatch — harness adapter
|
|
2
|
+
|
|
3
|
+
The video workflows (`product-launch-video` / `faceless-explainer` / `pr-to-video` / `motion-graphics` / `general-video`) describe subagent dispatch in harness-neutral verbs. This file maps those verbs to the primitives of whatever agent harness you are running on. Read it once per run, before the first dispatch; everything else in the workflows (dispatch packets, file artifacts, exit-code gates, Resume tables) is harness-independent and needs no translation.
|
|
4
|
+
|
|
5
|
+
## The contract (identical on every harness)
|
|
6
|
+
|
|
7
|
+
- **DISPATCH(role_file, dispatch_context)** — start one child agent whose prompt is the **full contents of the named role file** (a builder-assembled payload like `.hyperframes/frame-packets/_role.md`, or a workflow's `sub-agents/<role>.md`) followed by the `## Dispatch context` block from the workflow, copied **verbatim** (never digested or paraphrased). Every harness below accepts arbitrary task text, so this works everywhere; never rely on the child seeing your conversation, memory, or skills — the prompt and the files on disk are its entire world.
|
|
8
|
+
- **Parallel fan-out** — when a step says "start N workers in parallel", the workers are mutually independent (no ordering, no shared state beyond the filesystem). Run as many concurrently as your harness allows.
|
|
9
|
+
- **WAIT** — a step's completion criterion is always **the expected artifact existing on disk** (e.g. `compositions/<scene-id>.html`), never the harness's completion notification (some harnesses deliver results best-effort). After waiting, verify the artifacts; a missing artifact means that child failed — re-dispatch it once with the same prompt before surfacing an error.
|
|
10
|
+
|
|
11
|
+
## Concurrency cap → batching rule (cap never changes scope)
|
|
12
|
+
|
|
13
|
+
A harness concurrency limit **reduces parallelism, not work**: every scene still gets built, one scene per dispatch, with the available slots chewing through the full list.
|
|
14
|
+
|
|
15
|
+
- When the harness queues excess children internally, submit **all N at once** and let the queue drain.
|
|
16
|
+
- Harness hard-caps active children (e.g. OpenClaw `maxChildrenPerAgent`) → dispatch in **waves of the cap size**: start `cap` workers, wait for their artifacts, start the next wave, until all N scenes exist. Example: 9 scenes on a cap-3 harness = 3 waves of 3 — never drop scenes, never merge scenes into one worker to fit the cap.
|
|
17
|
+
|
|
18
|
+
## Harness mapping
|
|
19
|
+
|
|
20
|
+
Use the current harness's native delegation and waiting tools when they are available. The workflow contract stays the same:
|
|
21
|
+
|
|
22
|
+
- **DISPATCH** sends the complete role file and dispatch context to one worker.
|
|
23
|
+
- **Parallel fan-out** starts independent workers concurrently up to the harness limit.
|
|
24
|
+
- **WAIT** verifies the expected artifacts on disk, not only a completion notification.
|
|
25
|
+
- **Re-dispatch** starts a fresh worker with the same context plus the gate failure.
|
|
26
|
+
|
|
27
|
+
When native delegation is unavailable, use the existing fallback ladder: launch headless CLI workers that share the project filesystem, then fall back to inline serial execution.
|
|
28
|
+
|
|
29
|
+
On Codex, native delegation requires the user's explicit permission. Fold a one-line request into the workflow's first existing user pause before dispatch; a standing grant in `AGENTS.md` or the kickoff prompt also counts. Without it, use the fallback ladder rather than silently skipping work.
|
|
30
|
+
|
|
31
|
+
## Vocabulary mapping
|
|
32
|
+
|
|
33
|
+
- A request to work "in the background" means dispatch concurrently when the harness supports it.
|
|
34
|
+
- Load a named skill through the harness's skill mechanism, or read `<skills-root>/<skill>/SKILL.md` directly.
|
|
35
|
+
- Map generic read, write, edit, and shell verbs to the current harness's equivalent tools.
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
// Shared frame-packet builder — the script half of the frame-worker core/delta split.
|
|
2
|
+
//
|
|
3
|
+
// Each narrative workflow ships a thin `scripts/frame-packets.mjs` wrapper that pins
|
|
4
|
+
// its own paths (animation skill, role delta, design-truth resolution, extra packet
|
|
5
|
+
// sections) and delegates everything else here, exactly as the markdown half already
|
|
6
|
+
// does with `references/frame-worker-core.md` + each workflow's delta. One owner for
|
|
7
|
+
// the packet-building logic; the wrappers own only what genuinely differs per workflow.
|
|
8
|
+
//
|
|
9
|
+
// Packet (<frame_id>.md) = project inputs + the frame's exact `## Frame N` block
|
|
10
|
+
// + the blueprint body + every cited rule recipe, inlined — so a worker never opens
|
|
11
|
+
// the shared STORYBOARD.md or any skill document. Cited motions are found
|
|
12
|
+
// mechanically: the explicit `- rules:` field when present, plus every valid rule id
|
|
13
|
+
// (a filename under the animation skill's rules/) mentioned in the block.
|
|
14
|
+
//
|
|
15
|
+
// _role.md = frame-worker-core.md + the workflow's sub-agents/frame-worker.md,
|
|
16
|
+
// concatenated verbatim — the complete worker role, assembled from the two source
|
|
17
|
+
// documents so nothing is hand-maintained twice.
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
existsSync,
|
|
21
|
+
mkdirSync,
|
|
22
|
+
readFileSync,
|
|
23
|
+
readdirSync,
|
|
24
|
+
realpathSync,
|
|
25
|
+
writeFileSync,
|
|
26
|
+
} from "node:fs";
|
|
27
|
+
import { basename, join, resolve } from "node:path";
|
|
28
|
+
import { pathToFileURL } from "node:url";
|
|
29
|
+
|
|
30
|
+
export function field(block, name) {
|
|
31
|
+
const match = block.match(new RegExp(`^-\\s+${name}:\\s*(.+)$`, "im"));
|
|
32
|
+
return match?.[1]?.trim() ?? null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function splitFrames(storyboard) {
|
|
36
|
+
const matches = [...storyboard.matchAll(/^## Frame\s+([^\n]+)$/gm)];
|
|
37
|
+
return matches.map((match, index) => {
|
|
38
|
+
const start = match.index;
|
|
39
|
+
const end = matches[index + 1]?.index ?? storyboard.length;
|
|
40
|
+
return {
|
|
41
|
+
heading: match[1].trim(),
|
|
42
|
+
block: storyboard.slice(start, end).trim(),
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function frameId(frame) {
|
|
48
|
+
const src = field(frame.block, "src");
|
|
49
|
+
if (!src) throw new Error(`${frame.heading}: missing src`);
|
|
50
|
+
return basename(src).replace(/\.html?$/i, "");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function selectedFile(path, heading) {
|
|
54
|
+
if (!path || !existsSync(path)) return "";
|
|
55
|
+
return `\n## ${heading}\n\n${readFileSync(path, "utf8").trim()}\n`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function escapeRegExp(id) {
|
|
59
|
+
return id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function knownRuleIds(animationDir) {
|
|
63
|
+
const rulesDir = join(animationDir, "rules");
|
|
64
|
+
if (!existsSync(rulesDir)) {
|
|
65
|
+
console.warn(
|
|
66
|
+
`frame-packets: no rules dir at ${rulesDir} — packets will inline no motion recipes`,
|
|
67
|
+
);
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
return readdirSync(rulesDir)
|
|
71
|
+
.filter((name) => name.endsWith(".md"))
|
|
72
|
+
.map((name) => name.replace(/\.md$/, ""));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function citedRules(block, ruleIds) {
|
|
76
|
+
const explicit = (field(block, "rules") ?? "")
|
|
77
|
+
.split(/[,\s]+/)
|
|
78
|
+
.map((rule) => rule.trim())
|
|
79
|
+
.filter(Boolean);
|
|
80
|
+
const mentioned = ruleIds.filter((id) =>
|
|
81
|
+
new RegExp(`(?<![\\w-])${escapeRegExp(id)}(?![\\w-])`, "i").test(block),
|
|
82
|
+
);
|
|
83
|
+
return [...new Set([...explicit, ...mentioned])].filter((id) => ruleIds.includes(id));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// visual-design.md tells the author to write the blueprint as `<id> (Reproduce)`
|
|
87
|
+
// or `<id> (Adapt)` — the qualifier is direction for the frame worker, not part of
|
|
88
|
+
// the filename. Parse the field into the id it names (or null for `compose`), so
|
|
89
|
+
// no caller ever resolves a raw field value against the blueprints directory.
|
|
90
|
+
export function blueprintId(block) {
|
|
91
|
+
const raw = field(block, "blueprint");
|
|
92
|
+
if (!raw) return null;
|
|
93
|
+
const id = raw.replace(/\s*\([^)]*\)\s*$/, "").trim();
|
|
94
|
+
return id && id.toLowerCase() !== "compose" ? id : null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function resourceSections(block, { animationDir, ruleIds, frameId }) {
|
|
98
|
+
let sections = "";
|
|
99
|
+
const blueprint = blueprintId(block);
|
|
100
|
+
if (blueprint) {
|
|
101
|
+
const blueprintsDir = join(animationDir, "blueprints");
|
|
102
|
+
const path = join(blueprintsDir, `${blueprint}.md`);
|
|
103
|
+
// A blueprint that resolved to nothing used to inline an empty string, so the
|
|
104
|
+
// packet shipped without the one document the frame was designed against and
|
|
105
|
+
// the run still reported success. Name it instead — but only when the library
|
|
106
|
+
// is actually there to be named against. The animation skill installs on
|
|
107
|
+
// demand, so an absent blueprints/ is a missing install, not a bad id, and it
|
|
108
|
+
// degrades with a warning exactly like an absent rules/ (see knownRuleIds).
|
|
109
|
+
if (!existsSync(blueprintsDir)) {
|
|
110
|
+
console.warn(
|
|
111
|
+
`frame-packets: no blueprints dir at ${blueprintsDir} — packets will inline no blueprint`,
|
|
112
|
+
);
|
|
113
|
+
} else if (!existsSync(path)) {
|
|
114
|
+
throw new Error(`${frameId ?? "frame"}: blueprint "${blueprint}" has no file at ${path}`);
|
|
115
|
+
} else {
|
|
116
|
+
sections += selectedFile(path, `Selected blueprint: ${blueprint}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
for (const rule of citedRules(block, ruleIds)) {
|
|
120
|
+
sections += selectedFile(
|
|
121
|
+
join(animationDir, "rules", `${rule}.md`),
|
|
122
|
+
`Selected motion rule: ${rule}`,
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
return sections;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function buildRolePayload({ corePath, deltaPath, outDir }) {
|
|
129
|
+
const core = readFileSync(corePath, "utf8").trim();
|
|
130
|
+
const delta = readFileSync(deltaPath, "utf8").trim();
|
|
131
|
+
const role = `${core}\n\n---\n\n${delta}\n`;
|
|
132
|
+
mkdirSync(outDir, { recursive: true });
|
|
133
|
+
const path = join(outDir, "_role.md");
|
|
134
|
+
writeFileSync(path, role);
|
|
135
|
+
return { path, bytes: Buffer.byteLength(role) };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function buildFramePackets({
|
|
139
|
+
projectDir,
|
|
140
|
+
storyboardPath = join(projectDir, "STORYBOARD.md"),
|
|
141
|
+
outDir = join(projectDir, ".hyperframes", "frame-packets"),
|
|
142
|
+
maxPacketBytes = 48_000,
|
|
143
|
+
animationDir,
|
|
144
|
+
corePath,
|
|
145
|
+
deltaPath,
|
|
146
|
+
// Per-workflow hooks (all optional):
|
|
147
|
+
// designTruthLine(projectDir) -> the packet's design-truth input line
|
|
148
|
+
// validateFrame(frame, id) -> throw to reject a frame before packing
|
|
149
|
+
// extraSections(block) -> extra packet sections appended after the rule recipes
|
|
150
|
+
designTruthLine = (dir) => `- Design tokens: ${join(resolve(dir), "frame.md")}`,
|
|
151
|
+
validateFrame,
|
|
152
|
+
extraSections,
|
|
153
|
+
}) {
|
|
154
|
+
const storyboard = readFileSync(storyboardPath, "utf8");
|
|
155
|
+
const frames = splitFrames(storyboard);
|
|
156
|
+
if (frames.length === 0) throw new Error("STORYBOARD.md has no frame blocks");
|
|
157
|
+
const ruleIds = knownRuleIds(animationDir);
|
|
158
|
+
|
|
159
|
+
const packets = frames.map((frame) => {
|
|
160
|
+
const id = frameId(frame);
|
|
161
|
+
if (validateFrame) validateFrame(frame, id);
|
|
162
|
+
const packet = `# Frame packet: ${id}\n\n## Project inputs\n\n- Project: ${resolve(projectDir)}\n${designTruthLine(projectDir)}\n- RULES_DIR: ${join(animationDir, "rules")}\n\n## Assigned storyboard block\n\n${frame.block}\n${resourceSections(frame.block, { animationDir, ruleIds, frameId: id })}${extraSections ? extraSections(frame.block) : ""}`;
|
|
163
|
+
const bytes = Buffer.byteLength(packet);
|
|
164
|
+
if (bytes > maxPacketBytes) {
|
|
165
|
+
throw new Error(`${id}: frame packet is ${bytes} bytes (limit ${maxPacketBytes})`);
|
|
166
|
+
}
|
|
167
|
+
return { frameId: id, path: join(outDir, `${id}.md`), bytes, packet };
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
mkdirSync(outDir, { recursive: true });
|
|
171
|
+
for (const { path, packet } of packets) writeFileSync(path, packet);
|
|
172
|
+
buildRolePayload({ corePath, deltaPath, outDir });
|
|
173
|
+
return packets.map(({ packet: _packet, ...result }) => result);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function flag(argv, name, fallback) {
|
|
177
|
+
const index = argv.indexOf(`--${name}`);
|
|
178
|
+
return index >= 0 && argv[index + 1] ? argv[index + 1] : fallback;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// realpath both sides: on macOS /tmp → /private/tmp, and node resolves the main
|
|
182
|
+
// module's symlinks in import.meta.url while argv[1] keeps the invoked spelling —
|
|
183
|
+
// a raw compare silently skips main() when invoked through any symlinked path.
|
|
184
|
+
export function isMainModule(importMetaUrl) {
|
|
185
|
+
if (!process.argv[1]) return false;
|
|
186
|
+
try {
|
|
187
|
+
return pathToFileURL(realpathSync(process.argv[1])).href === importMetaUrl;
|
|
188
|
+
} catch {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export function runCli({ buildFramePackets: build, buildRolePayload: buildRole }) {
|
|
194
|
+
const argv = process.argv.slice(2);
|
|
195
|
+
const projectDir = resolve(flag(argv, "project", "."));
|
|
196
|
+
const outDir = resolve(flag(argv, "out-dir", join(projectDir, ".hyperframes", "frame-packets")));
|
|
197
|
+
try {
|
|
198
|
+
const packets = build({
|
|
199
|
+
projectDir,
|
|
200
|
+
storyboardPath: resolve(flag(argv, "storyboard", join(projectDir, "STORYBOARD.md"))),
|
|
201
|
+
outDir,
|
|
202
|
+
});
|
|
203
|
+
const role = buildRole({ outDir });
|
|
204
|
+
console.log(`✓ frame packets: ${packets.length} bounded packet(s)`);
|
|
205
|
+
for (const packet of packets)
|
|
206
|
+
console.log(` ${packet.frameId}: ${packet.bytes} bytes → ${packet.path}`);
|
|
207
|
+
console.log(` worker role: ${role.bytes} bytes → ${role.path}`);
|
|
208
|
+
} catch (error) {
|
|
209
|
+
console.error(`✗ frame packets: ${error.message}`);
|
|
210
|
+
process.exit(1);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
@@ -15,15 +15,15 @@ Run commands as `npx hyperframes ...` unless project instructions provide a wrap
|
|
|
15
15
|
|
|
16
16
|
## Development loop
|
|
17
17
|
|
|
18
|
-
1. **Scaffold:** `npx hyperframes init <project>`
|
|
18
|
+
1. **Scaffold:** `npx hyperframes init <project>` (centered blank). Or capture a site. Pass `--example=<name>` only to start from a named example.
|
|
19
19
|
2. **Find the move:** before authoring motion by hand, search for a primitive that already does it: `npx hyperframes catalog --query "reveal a headline one line at a time"`. Ask for the effect you want rather than the mechanism you have in mind. Install with `npx hyperframes add <name>` (see `/hyperframes-registry`). Author by hand only once nothing fits.
|
|
20
20
|
3. **Author:** write the composition using `/hyperframes-core`.
|
|
21
21
|
4. **Get fast feedback while editing:** run `npx hyperframes lint` after the first HTML pass and after structural changes.
|
|
22
22
|
5. **Run the final gate:** run `npx hyperframes check`; it reruns lint before opening the browser. Do not prepend a redundant standalone lint invocation. Add `--snapshots` for annotated overview frames and finding crops.
|
|
23
23
|
6. **Inspect sub-compositions:** when `index.html` mounts `data-composition-src`, capture midpoint snapshots and inspect each mounted scene.
|
|
24
24
|
7. **Open the final Studio preview:** run `npx hyperframes preview --background`, verify the URL returns HTTP 200, hand the timeline project URL to the user, and ask whether to revise or render. Keep it alive until review ends.
|
|
25
|
-
8. **Render only after approval:** use draft quality for
|
|
26
|
-
9. **Verify the output:** confirm the file exists
|
|
25
|
+
8. **Render only after approval:** use `--quality draft` while iterating, `--quality looks` for the first real encode (the CLI default), and `--quality delivery` for final delivery.
|
|
26
|
+
9. **Verify the output:** confirm the file exists and is non-empty. Read the render summary's second line (`beginframe` vs `screenshot`, GPU, stage timings). `screenshot` + `software gpu` on Linux is the slow path. `ffprobe -v error -show_format -show_streams` and compare duration (and fps if the brief set it) to the root `data-duration`.
|
|
27
27
|
|
|
28
28
|
## Mandatory creator-edit cross-references
|
|
29
29
|
|
|
@@ -46,9 +46,9 @@ npx hyperframes lint
|
|
|
46
46
|
# Required final gate; includes lint.
|
|
47
47
|
npx hyperframes check
|
|
48
48
|
npx hyperframes preview --background
|
|
49
|
-
npx hyperframes render --quality
|
|
49
|
+
npx hyperframes render --quality looks --output out.mp4
|
|
50
50
|
test -s out.mp4
|
|
51
|
-
ffprobe -v error -show_format out.mp4
|
|
51
|
+
ffprobe -v error -show_format -show_streams out.mp4
|
|
52
52
|
```
|
|
53
53
|
|
|
54
54
|
`check` runs lint first, then uses one browser session and one seek pass to audit runtime errors, failed requests, layout, `*.motion.json` assertions, and WCAG contrast. Persistent findings gate the exit code; transient entrance or exit findings are informational. Use `--strict` to gate warnings. `validate`, `inspect`, and `layout` remain aliases for compatibility but must not appear in new instructions or scripts.
|
|
@@ -62,7 +62,7 @@ Do not confuse these states:
|
|
|
62
62
|
| Storyboard board | Before composition checks, only when `storyboard: yes` | Review plan cards and wireframe sketches. Open `?view=storyboard#project/<name>`. |
|
|
63
63
|
| Final composition preview | After `check` passes | Review the assembled timeline before render. Open `#project/<name>`. |
|
|
64
64
|
|
|
65
|
-
The early board is not approval of the final video. Rendering always requires the final approval defined by `hyperframes
|
|
65
|
+
The early board is not approval of the final video. Rendering always requires the final approval defined by `hyperframes/references/review-loop.md`.
|
|
66
66
|
|
|
67
67
|
## Sub-composition smoke test
|
|
68
68
|
|
|
@@ -90,7 +90,7 @@ Treat tiny unstyled content, canvas-sized icons, missing hero elements, or timel
|
|
|
90
90
|
npx hyperframes doctor --json | jq -e '.ok' >/dev/null
|
|
91
91
|
```
|
|
92
92
|
|
|
93
|
-
- Non-TTY mode is automatic.
|
|
93
|
+
- Non-TTY mode is automatic and scaffolds the centered blank. Pass `--example` only to start from a named example. Use `--non-interactive` to force flag-only mode on a TTY.
|
|
94
94
|
- Use one `HYPERFRAMES_RUN_ID` for all commands in the same verification loop.
|
|
95
95
|
- Use `--strict`, `--strict-all`, and `--strict-variables` when the corresponding warnings, variables, or CI conditions must gate the render.
|
|
96
96
|
- JSON paths redact the home directory as `$HOME`; do not try to reverse the redaction.
|
|
@@ -112,7 +112,8 @@ Use `selection.target.hfId` when available, otherwise its selector and source fi
|
|
|
112
112
|
| Need | Command |
|
|
113
113
|
| ---------------------------------------- | ----------------------------------------------------------------------------- |
|
|
114
114
|
| Fast local iteration | `npx hyperframes render --quality draft` |
|
|
115
|
-
|
|
|
115
|
+
| First real encode | `npx hyperframes render --quality looks --output out.mp4` |
|
|
116
|
+
| Final local delivery | `npx hyperframes render --quality delivery --output out.mp4` |
|
|
116
117
|
| Reproducible container render | `npx hyperframes render --docker --strict --output out.mp4` |
|
|
117
118
|
| Local variable-driven batch render | `npx hyperframes render --batch rows.json --output "renders/{name}.mp4"` |
|
|
118
119
|
| HeyGen-hosted zero-infrastructure render | `npx hyperframes cloud render` |
|
|
@@ -7,18 +7,18 @@ Scaffolding commands. Use these instead of creating files by hand — they set u
|
|
|
7
7
|
## init
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
npx hyperframes init my-video # TTY:
|
|
10
|
+
npx hyperframes init my-video # centered blank (TTY: wizard)
|
|
11
11
|
npx hyperframes init my-video --example warm-grain # pick an example
|
|
12
|
-
npx hyperframes init my-video --
|
|
12
|
+
npx hyperframes init my-video --resolution portrait
|
|
13
13
|
npx hyperframes init my-video --video clip.mp4 # with video file
|
|
14
14
|
npx hyperframes init my-video --audio track.mp3 # with audio file
|
|
15
|
-
npx hyperframes init my-video --
|
|
16
|
-
npx hyperframes init my-video --non-interactive
|
|
15
|
+
npx hyperframes init my-video --tailwind # Tailwind v4 browser runtime
|
|
16
|
+
npx hyperframes init my-video --non-interactive # CI — flag-only, same blank
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
**Default depends on TTY**: in a terminal, the CLI prompts for example/options. Outside a TTY (CI, agents, piped output) it auto-switches to non-interactive and
|
|
19
|
+
**Default depends on TTY**: in a terminal, the CLI prompts for example/options (default: centered blank). Outside a TTY (CI, agents, piped output) it auto-switches to non-interactive and scaffolds that blank. Pass `--example` only to start from a named example. Pass `--non-interactive` to force flag-only mode on a TTY.
|
|
20
20
|
|
|
21
|
-
Templates: `blank`, `warm-grain`, `play-mode`, `swiss-grid`, `vignelli`, `decision-tree`, `kinetic-type`, `product-promo`, `nyt-graph`, `vscode-theme-visualizer`. (The closed set of `hyperframes:example` items in `registry/registry.json
|
|
21
|
+
Templates: `blank`, `warm-grain`, `play-mode`, `swiss-grid`, `vignelli`, `decision-tree`, `kinetic-type`, `product-promo`, `nyt-graph`, `vscode-theme-visualizer`. (The closed set of `hyperframes:example` items in `registry/registry.json` plus the bundled `blank` template. `hyperframes catalog` does not list examples — its `--type` takes only `block` or `component` — so this list has no live equivalent and is checked by `bun run lint:skills`.)
|
|
22
22
|
|
|
23
23
|
Other useful flags:
|
|
24
24
|
|