product-launch-video - dispatch entry
Confirm the route before Step 0. This skill makes a video for a
product being marketed / launched / promoted. If it's really a
general (non-launch) site → video (site tour / showcase, not selling a product) →
; a
topic / concept with no product →
; a
GitHub PR →
; an
existing video to caption / package →
·
.
Out of scope (decline, don't fake): live / at-render-time data (every value is baked in at author time), or footage / screenshots / an avatar that doesn't exist yet (HyperFrames can't record or capture). Routed here on a vague "make a video", or unsure product-vs-topic / launch-vs-general-site?
Read first.
All artifacts are written to
PROJECT_DIR = videos/<project-name>/
(created in Step 0). Paths below are relative to
. You (the orchestrator) run the Bash steps and dispatch the subagents; per-phase details live in the linked guides/agents/scripts — do not expand them here. Dispatch is harness-portable: before the first subagent dispatch, read
<SKILL_DIR>/../hyperframes-core/references/subagent-dispatch.md
once — it maps the dispatch verbs (parallel fan-out / background / wait) to your harness's primitives; a concurrency cap below N means waves of the cap size, never fewer scenes.
This file is a binding runbook, not background reading: execute the steps in order and produce every phase artifact with its designated script or agent role — do not substitute a freestyle pipeline (hand-written narration, ad-hoc TTS calls, one hand-authored composition), and do not skip a pause step because the request seems clear. A step you cannot perform → stop and report; improvising past it breaks every downstream contract.
| Phase | Execution | Primary artifact | Detailed flow |
|---|
| init | Bash directly | | Step 0 (this file) |
| capture | Bash directly () | capture/extracted/tokens.json
| |
| design-system | subagent | design-system/design.html
+ | |
| story-design | subagent | | |
| audio | Bash directly () | | |
| visual-design | subagent | | |
| prep | Bash directly () | | header |
| captions | Bash directly ( -> ) | + compositions/captions.html
| header |
| scenes | N x subagent (parallel, one scene each) | compositions/scene_*.html
| agents/hyperframes-scene.md
|
| finalize | Bash prelude (wait-bgm + assemble + transitions + hoist + sfx-verify) -> finalize subagent | | Step 7 / agents/hyperframes-finalize.md
|
Prerequisites (install before first run)
macOS Apple Silicon or Linux x64:
bash
brew install python@3.11 node ffmpeg # On Linux, use the apt/dnf equivalent
npx hyperframes doctor # One-time check that Chrome / dependencies are ready
- — Homebrew Python, not system (PEP 668 blocks otherwise); used by the MusicGen fallback
- + ( uses )
- Chrome downloads automatically on first . (Step 7, runs only when a scene declares footage) reuses that cached Chrome; if it reports deps missing, run
node <SKILL_DIR>/scripts/hoist-videos.mjs --ensure-deps
once (~5s)
Optional API keys (unset -> local fallbacks; injection in Step 0.5;
≡
):
| Key | Used for | Default voice / fallback |
|---|
| (or ) | TTS (cloud, with word-level timestamps) | voice: auto (first English starfish voice; override ) |
| TTS (cloud; requires ) | voice (Rachel) |
| Neither, and not logged in | TTS | local Kokoro, voice (for non-English, pass ) |
| (one key for both uses) | Capture vision caption + Lyria BGM | unset -> captions use DOM context only; BGM uses local MusicGen (first run downloads ~300 MB) |
Flow
Step 0.0 - Confirm the brief (one round, then build)
Before Step 0, in
one message confirm only what materially shapes the launch video and you can't infer — lead with a recommended default, skip anything the user already gave: the
angle / focus (the product overall, a headline feature, an offer / CTA),
length (default ~30-90s; up to ~3 min), and — if
did not already set them —
aspect (default 16:9; 9:16 for vertical / social) and
language. The preset is derived from brand capture, not asked. For a fully specified request, skip this and build.
Step 0 - Initialize the video project
cwd is the agent workspace root; all video artifacts go in
PROJECT_DIR = videos/<project-name>/
.
Naming : an explicit user-given directory wins; otherwise choose a short kebab-case name like
(
never the workspace basename or a timestamp). From a URL, derive it from the domain/page title; the name is fixed once
is written.
Initialization (only when
$PROJECT_DIR/hyperframes.json
does not exist):
bash
PROJECT_DIR="${LAUNCH_VIDEO_DIR:-videos/<project-name>}"
mkdir -p "$(dirname "$PROJECT_DIR")"
npx hyperframes init "$PROJECT_DIR" --non-interactive --skip-skills --example=blank
drops a generic
/
into
; leave them in place but do not treat their generic guidance as run-time constraints — this skill is the source of truth.
Constraints (each violation breaks later phases):
- Do not run (or generate / ) in the workspace root; do not create a subproject inside .
- Every subagent dispatch context contains a line; the subagent treats it as the project root.
- cwd discipline (master too): every Bash command runs as a
(cd "$PROJECT_DIR" && ...)
subshell — never bare (persistent cwd drift makes later relative paths wrong).
Step 0.5 - API key guidance
Skip when exists or
is non-empty. Otherwise detect what's configured (HeyGen TTS =
/
/
; ElevenLabs / Gemini = their env keys), then
always pause and ask — do not proceed on your own, even when a workable config is detected:
Detected: <summary>. Cloud keys are optional — without them, unconfigured providers fall back locally (TTS -> Kokoro unless HeyGen is configured; BGM -> MusicGen). Reply with:
- paste keys -> I will write them to
- "go" -> proceed with what is configured now
- "skip" -> proceed with local fallbacks for anything unconfigured
Pasted keys -> Write/Edit
, one
per line (overwrite same-name keys, do not judge values). "go" / "skip" -> Step 1.
Step 1 - Capture (Phase 1)
- Resolve and any explicit from the prompt; ensure Step 0 ran.
- Read if it exists and use the Resume table below to skip completed phases.
- Classify the input (Step 1.0) to set and , then run the matching path. Both paths share the same downstream commands.
Step 1.0 - Classify the input (set CAPTURE + VO_MODE)
| Input shape | What to do |
|---|
| Explicit URL in the prompt | = that URL; ; no voice-over question (narration comes from the captured site). Path (A). |
| User pasted / pointed at a script or brief | (1) Save the verbatim text to $PROJECT_DIR/user_script.txt
. (2) Ask the voice-over question once (below) → set . (3) Resolve a capture target from the script (below) → set . |
| A topic / brief with no script prose, no product | ; no voice-over question. Path (B). |
Voice-over question (only when the user supplied actual script prose) — ask one short line and wait:
Should I use your script verbatim as the voice-over, or restructure it into more screen-ready scene narration?
- Verbatim — keep the original wording; I only split scenes and pair visuals. Duration follows the script.
- Restructure — treat it as a brief and rewrite tighter narration, 1-2 sentences per scene.
VO_MODE = verbatim | restructure
(default
); threaded to story-design in Step 2.
Resolve a capture target from the script — default to finding and crawling a site (real brand tokens beat preset fallbacks); skip only when the user opted out ("no web / text-only / no capture"). In order: (1) explicit
URL in the script → use it, announce,
; (2) clear brand/product name →
for the official site,
confirm the resolved URL with the user in one line before crawling (decline / nothing credible →
); (3) nothing derivable →
.
Capture + user script coexist: the crawl supplies only brand tokens + assets + visual register; the narration spine stays
(honored via
), never the site's own copy.
bash
(cd "$PROJECT_DIR" && npx hyperframes capture "<TARGET_URL>" -o ./capture)
(B) No-capture path () — synthesize a minimal capture package; downstream is identical.
You (master) choose the preset (no site to infer from; pick from the 19 presets per user intent, or ask one short question). The full script/brief goes into
;
triggers the preset-palette fallback (fill
only if the user named brand colors):
bash
(cd "$PROJECT_DIR" && mkdir -p capture/extracted capture/assets)
(cd "$PROJECT_DIR" && cat > capture/extracted/tokens.json <<'JSON'
{ "title": "<brand/title>", "description": "<one-line>", "colors": [], "fonts": [], "headings": [], "sections": [], "ctas": [], "svgs": [], "cssVariables": {} }
JSON
)
(cd "$PROJECT_DIR" && echo '{}' > capture/extracted/design-styles.json)
(cd "$PROJECT_DIR" && printf '%s\n' "<full user script / brief>" > capture/extracted/visible-text.txt)
If the user already has a final
, place it in
; the Resume table skips story-design.
Shared downstream for both paths (Path B appends
to build-design; Path A omits it for auto-inference):
bash
(cd "$PROJECT_DIR" && node <SKILL_DIR>/scripts/derive-context-pack.mjs --capture ./capture)
(cd "$PROJECT_DIR" && node <SKILL_DIR>/phases/design-system/scripts/build-design.mjs ./design-system --no-emit) # Path B: append --style <chosen-preset>
Validation (stop and report if anything is missing; if
exists, the site blocked the crawl — follow the instructions inside it):
bash
[ -s "$PROJECT_DIR/capture/extracted/tokens.json" ] && \
[ -s "$PROJECT_DIR/capture/extracted/design-styles.json" ] && \
[ -s "$PROJECT_DIR/capture/context_pack.md" ] && \
[ -s "$PROJECT_DIR/design-system/inference.json" ] && \
[ -d "$PROJECT_DIR/capture/assets" ] && echo ok || echo missing
Step 1b + Step 2 - design-system ∥ story-design (parallel fork)
Both subagents depend only on Step 1 artifacts and do not read each other's output — after capture validates, start them in parallel (two concurrent background dispatches, per the dispatch adapter); do not serialize:
-
design-system: prompt = full
+
with
/
/
+ the full text of
design-system/inference.json
inlined via
(~2-4 KB, saves the subagent one Read).
-
story-design: prompt = full
+
:
SKILL_DIR: <absolute path>
PROJECT_DIR: <video project root>
Schema validator: <SKILL_DIR>/scripts/validate-narrator.mjs
Design DNA: ./design-system/inference.json # read site_dna once to set the narrative register
Provided script: ./user_script.txt # ONLY when the user supplied a script; omit the line otherwise
Voice-over mode: <verbatim | restructure> # pair with Provided script; omit otherwise
Script style: Keep each scene's script concise - 1-2 sentences, no more than 20 words # suspended in verbatim mode (length follows the script)
Orientation: <landscape | portrait | square> # from the user's aspect (16:9→landscape, 9:16→portrait, 1:1→square; default landscape). Echoed verbatim into narrator_scripts.orientation → sets the canvas for the whole pipeline
Step 3 - Audio (Phase 2.5)
After story-design returns (
exists) — audio does not wait for design-system:
bash
(cd "$PROJECT_DIR" && node <SKILL_DIR>/scripts/audio.mjs \
--narrator-scripts ./narrator_scripts.json \
--hyperframes . \
--out ./audio_meta.json \
--lyria-recipe <SKILL_DIR>/phases/audio/lyria-recipe.py)
BGM runs detached in the background when available (
→ Lyria cloud; else installed
transformers torch soundfile numpy
→ local MusicGen, first run ~300 MB) and is silently skipped otherwise; all flags (
/
/
/ ...) are documented at the top of
.
- exit 0 -> voice + transcribe complete (BGM may still be running; records / ), continue.
- exit 1 -> zero scenes produced voice; report and stop.
Step 4 - Visual design (Phase 3)
Join point:
design-system/chunks/index.json
+
+
all exist. Build one dispatch packet (the subagent reads it once, zero extra Reads):
bash
# Dispatch packets live in $PROJECT_DIR/.dispatch/ (transient; safe to delete after the run).
# NEVER use a fixed /tmp path: it persists across runs/projects, so a failed write silently
# reuses another project's stale packet and contaminates every worker.
mkdir -p "$PROJECT_DIR/.dispatch"
DP="$PROJECT_DIR/.dispatch/vd-dispatch.txt"
{
# Section order is deliberate: contracts first, static references middle, work items last
echo "## Design chunks"
(cd "$PROJECT_DIR" && cat design-system/chunks/index.json \
design-system/chunks/composition-hints.md design-system/chunks/voice.md \
design-system/chunks/tokens.css design-system/chunks/easings.js 2>/dev/null)
echo "## Effects catalog"; cat <SKILL_DIR>/phases/visual-design/effects-catalog.md
echo "## Blueprints index"; cat <SKILL_DIR>/phases/visual-design/blueprints-index.md
echo "## Design rules"; cat <SKILL_DIR>/phases/visual-design/rules/{typography,color-system,composition,motion-language}.md
echo "## SFX library"; cat <SKILL_DIR>/assets/sfx/manifest.json
echo "## Narrator scripts"; (cd "$PROJECT_DIR" && cat narrator_scripts.json)
echo "## Audio meta"; (cd "$PROJECT_DIR" && cat audio_meta.json 2>/dev/null) # optional; overrides Duration on >10% drift
} > "$DP"
# Guard: a partially-failed build must fail LOUDLY here, not downstream in the subagent
grep -q '^## Narrator scripts' "$DP" || { echo "FATAL: vd-dispatch.txt incomplete — rebuild before dispatching"; }
# Captions planning hint for the Captions: dispatch line below
(cd "$PROJECT_DIR" && node -e 'try{const m=require("./audio_meta.json");process.stdout.write(Object.values(m.scenes||{}).some(s=>s.wordsPath)?"enabled":"disabled")}catch{process.stdout.write("enabled")}')
Dispatch the subagent: prompt = full
+
(copy verbatim, do not digest):
SKILL_DIR: <absolute path>
PROJECT_DIR: <video project root>
Schema validator: <SKILL_DIR>/scripts/validate-section.mjs
Canvas: <width>×<height> # 1920×1080 default; 1080×1920 portrait / 1080×1080 square when narrator_scripts.orientation says so
Captions: <enabled | disabled> # the node -e hint above; enabled => plan keeps key content in the upper ~83%
Dispatch packet: <PROJECT_DIR>/.dispatch/vd-dispatch.txt
The
line is an optimistic hint; the authoritative gate is
group_spec.captions_enabled
from Step 5 prep (mismatch is safe — Step 6/7 keep-out always follows group_spec).
Step 5 - prep (deterministic, NO subagent)
bash
(cd "$PROJECT_DIR" && node <SKILL_DIR>/scripts/prep.mjs \
--section-plan ./section_plan.md \
--narrator-scripts ./narrator_scripts.json \
$( [ -f audio_meta.json ] && echo "--audio-meta ./audio_meta.json" ) \
--rules-dir <SKILL_DIR>/../hyperframes-animation/rules \
--capture ./capture \
--design-system ./design-system \
--hyperframes . \
--sfx-lib <SKILL_DIR>/assets/sfx \
--out ./group_spec.json)
- exit 0 -> append the stdout summary to .
- exit 1 -> stderr names the failing scene + anchor; re-dispatch visual-design (Step 4) with the error passed through.
Step 5.5 + Step 6 - Captions (deterministic) + scene worker fan-out
Captions are two Bash scripts, no subagent (run after prep, before fan-out):
bash
(cd "$PROJECT_DIR" && node <SKILL_DIR>/scripts/captions.mjs group \
--group-spec ./group_spec.json --hyperframes . \
--tokens design-system/chunks/tokens.css --out ./caption_groups.json)
(cd "$PROJECT_DIR" && node <SKILL_DIR>/scripts/captions.mjs html \
--hyperframes . --groups ./caption_groups.json \
--tokens design-system/chunks/tokens.css \
--inference design-system/inference.json \
--out compositions/captions.html)
exit 0 = normal.
captions: skipped (<reason>)
= legal skip — no
, assemble will not mount track 12; continue.
Do not run
npx hyperframes lint <file>
on
(lint takes a project directory; a file path exits 1).
Scene worker fan-out: read
for worker count N and
group_spec.captions_enabled
for the
flag, then build the per-worker dispatch packets and start
N workers in parallel (concurrent background dispatches; a harness concurrency cap below N means waves of the cap size until all N scenes exist — one scene per worker either way, never fewer scenes):
bash
# Same rule as Step 4: packets go in $PROJECT_DIR/.dispatch/, never a fixed /tmp path
# (a stale /tmp file from a previous project survives a failed write and silently
# poisons every worker with the wrong design system).
mkdir -p "$PROJECT_DIR/.dispatch/scene-dispatch"
# Shared header (identical for every worker), computed once:
# `## Film direction` = the film-level invariants from group_spec.film_direction
# (palette system / motion defaults + budget / ambient system / negative list);
# each scene's creative_brief carries only scene-specific deltas on top of it.
{
echo "## Film direction"
(cd "$PROJECT_DIR" && node -p 'JSON.parse(require("fs").readFileSync("group_spec.json","utf8")).film_direction || ""')
echo "## Tokens / easings / voice"
(cd "$PROJECT_DIR" && cat design-system/chunks/tokens.css design-system/chunks/easings.js design-system/chunks/voice.md 2>/dev/null)
} > "$PROJECT_DIR/.dispatch/scene-shared.txt"
# Guard BEFORE fan-out: header structure + the project's own brand token must both be present;
# a contaminated packet here costs a full re-author round across every affected worker.
grep -q '^## Film direction' "$PROJECT_DIR/.dispatch/scene-shared.txt" && \
grep -q -- '--brand-primary' "$PROJECT_DIR/.dispatch/scene-shared.txt" || \
{ echo "FATAL: scene-shared.txt incomplete/stale — rebuild before dispatching workers"; }
# Per-worker packet: shared header + that worker's Scenes YAML -> $PROJECT_DIR/.dispatch/scene-dispatch/w<N>.txt
Each worker's prompt = full
agents/hyperframes-scene.md
+
with:
/
/
/
+
(=
/
) /
Captions: <enabled|disabled>
/
Dispatch packet: <PROJECT_DIR>/.dispatch/scene-dispatch/w<N>.txt
, plus the shared header body (
+
## Tokens / easings / voice
) and the worker's
list
copied verbatim from group_spec.json.groups[i].scenes[<sid>]
(
/
/
/
/
/
/
/
/
).
When , also pass = height − round(height × 0.1667)
and = (landscape → 900 / 880; portrait → 1600 / 1580).
(anomaly already reported by prep) -> the worker falls back to reading
design-system/design.html
.
After all workers return, run the static composition gate (scans
compositions/scene_*.html
per
;
is covered by its own self-lint + Step 7 whole-project lint):
bash
(cd "$PROJECT_DIR" && node <SKILL_DIR>/scripts/check-compositions.mjs \
--hyperframes . \
--group-spec ./group_spec.json)
- 0 -> continue to Step 7.
- 1 -> stderr names the violating scene + rule; re-dispatch that worker (do not Edit in the master).
Step 7 - Assembly prelude + finalize (Phase 4c)
(1) Deterministic Bash prelude (each script documents its internals in its own header; you only branch on exit codes):
bash
(cd "$PROJECT_DIR" && node <SKILL_DIR>/scripts/wait-bgm.mjs \
--audio-meta ./audio_meta.json \
--hyperframes . \
--timeout-ms 120000 \
--interval-ms 2000)
(cd "$PROJECT_DIR" && node <SKILL_DIR>/scripts/assemble-index.mjs --group-spec ./group_spec.json --hyperframes .)
(cd "$PROJECT_DIR" && node <SKILL_DIR>/scripts/transitions.mjs inject --group-spec ./group_spec.json --hyperframes .)
(cd "$PROJECT_DIR" && node <SKILL_DIR>/scripts/transitions.mjs verify --group-spec ./group_spec.json --index ./index.html)
(cd "$PROJECT_DIR" && node <SKILL_DIR>/scripts/hoist-videos.mjs --group-spec ./group_spec.json --hyperframes .)
(cd "$PROJECT_DIR" && node <SKILL_DIR>/scripts/verify-output.mjs sfx --group-spec ./group_spec.json --index ./index.html)
No agent hand-writes
or manually checks BGM. Exit-code branches:
- assemble exit 1 -> names a scene (root ≠ group_spec, or file missing) = worker contract break -> re-dispatch that worker (Step 6), then rerun this step.
- transitions inject/verify exit 1 -> injector bug (prep already validated ) -> report for investigation; do not roll back workers.
- hoist-videos exit 1 -> an invalid declaration (stderr names scene + reason); the scene file (or re-dispatch for a real relayout), rerun this step. exit 2 -> run
node <SKILL_DIR>/scripts/hoist-videos.mjs --ensure-deps
from the workspace root, rerun.
- sfx-verify exit 1 -> assembler bug -> report for investigation.
(2) Dispatch the finalize subagent: prompt = full
agents/hyperframes-finalize.md
+
:
SKILL_DIR: <absolute path>
PROJECT_DIR: <video project root>
Render quality: high # or draft / standard
Captions: <enabled | disabled> # = group_spec.captions_enabled
BGM: <one-line wait-bgm verdict, e.g. "ready (lyria)" / "skipped (no key)" / "timeout">
Film direction: | # = group_spec.film_direction (film-level invariants the briefs assume)
<verbatim>
Scenes: # one line per scene, copied verbatim from group_spec.json
- { scene_id, start_s, estimatedDuration_s, effects: [...], creative_brief: |
<Phase 3 prose for this scene> }
Finalize runs lint+validate, takes one contact-sheet look, fixes what the pixels show, renders, and verifies — its prompt owns that flow. Outcomes:
- Reports the verified mp4 + fixes in place -> complete.
- STOP (a scene needs real recomposition — exception, not default) -> re-dispatch that worker (Step 6) with normal dispatch context + a block carrying finalize's verbatim findings and the flag -> rerun (1) -> re-dispatch finalize. Same finding survives two rounds -> stop and surface to the user.
Completion report
Summarize per phase from
+ each step's stdout: capture URL / asset counts, preset, archetype, scene count + total duration, transitions, gate status, fixes in place, final mp4 path + bytes + duration.
Offer a live preview — never auto-open one. The deliverable is the mp4. Do NOT run
/
during any earlier phase (a mid-run preview shows half-edited compositions and dies with that phase's server). Only when the user asks, after the render:
bash
(cd "$PROJECT_DIR" && npx hyperframes preview) # Studio UI, e.g. http://localhost:3002/#project/<project-name>
(cd "$PROJECT_DIR" && npx hyperframes play) # or a plain shareable player at http://localhost:<port>
Report the actual URL with the real port. Flags live in the
skill.
Resume table
Read
and resume from the first missing artifact:
| State | Continue from |
|---|
| log missing or empty | Full pipeline |
capture/extracted/tokens.json
missing | Rerun Step 1 (capture + derive-context-pack + build-design.mjs --no-emit
) |
exists, design-system/inference.json
missing | Rerun only build-design.mjs --no-emit
(deterministic, seconds) |
| exists, but or missing | Step 1b/2 fill-in: dispatch whichever subagent's artifact is missing (both missing -> both in parallel) |
| exists, missing | Step 3 (audio) |
| exists, missing | Step 4 (visual-design) |
| exists, missing | Step 5 (prep) |
exists, compositions/scene_*.html
missing or missing | Step 5.5+6: run the captions scripts first, then dispatch workers for whichever scenes are missing, in parallel. Captions-ran criterion = exists (a legal skip writes no ; keying on would re-skip forever) |
All compositions/scene_*.html
exist + captions state decided, missing | Step 7: rerun the full Bash prelude (overwrite — upstream scenes may have changed), then dispatch finalize |
| exists | Report completed and stop |