| name | new-episode |
| description | Use when the user wants to start a new video episode -- scaffolding the project directory, starter scene-spec.json, and metadata under apps/hyperframe/src/episodes/. Defer to this skill whenever the user says "create a new episode", "scaffold a short", or "start a new video", even without mentioning the scaffolder tool directly. Skip for rendering or editing existing episodes.
|
New episode
CWD: all bash commands below assume cd apps/hyperframe first. Paths like examples/<slug>.txt, public/voice/<slug>/ are app-relative.
Scaffold a new Hyperframes episode at apps/hyperframe/src/episodes/<slug>/. An episode is a typed scene-spec.json; a deterministic assembler turns that spec into the monolithic index.html. You author by editing the spec and re-assembling — never hand-edit index.html (it is generated, and identical specs produce identical bytes).
Producing a vertical 9:16 short? This skill only scaffolds the starter project. After scaffolding, follow .agents/skills/canonical-short/SKILL.md for the e2e playbook (voice config, scene-type selection, per-scene QA gate, render).
Pre-flight
- Slug uniqueness.
ls apps/hyperframe/src/episodes/ -- abort if the slug already exists with content (the scaffolder also refuses a non-empty dir).
- Slug format. Lowercase kebab-case, regex
^[a-z0-9][a-z0-9-]*$. Convention: short-NN for vertical reels, topic-slug for one-offs.
- Use the vertical format. Default and supported production format is 9:16 (
--width=1080 --height=1920). Do not introduce horizontal or square episodes in the current pipeline.
- Pick an intent (optional).
--intent= seeds the spec from an intent skeleton (a sensible hook-first / outro-last scene order). One of: informative, data, workflow, social, brand, vfx. Omit for a generic hook -> title-cards -> outro starter.
Run
bun run new:episode <slug> [--intent=informative|data|workflow|social|brand|vfx] [--width=1080] [--height=1920]
The scaffolder seeds the starter scene-spec.json from the intent skeleton (each scene pre-filled with its scene-type's sample.json params), then assembles index.html so the episode is immediately previewable.
What the scaffolder writes
apps/hyperframe/src/episodes/<slug>/
scene-spec.json # the source of truth: slug, lang, width/height, palette, scenes[]
index.html # GENERATED from the spec by the assembler — never hand-edit
meta.json # { "id": "<slug>", "name": "<slug>", "description": "", "tail": 3 }
hyperframes.json # { paths: { assets: "assets" } } registry/paths config
assets/.gitkeep # placeholder; voice.mp3 + captions.json drop in later
lib -> ../../lib # symlink so relative lib/... imports resolve
The starter scene-spec.json looks like this (intent skeleton + sample slots):
{
"slug": "<slug>",
"lang": "es",
"width": 1080,
"height": 1920,
"palette": { "accent": "#5b6cff", "accent2": "#e9ff00" },
"scenes": [
{ "id": "hook", "type": "hook", "slots": { "eyebrow": "...", "title": "...", "subtitle": "..." } },
{ "id": "title-cards", "type": "title-cards", "slots": { "title": "...", "cards": [ ] } },
{ "id": "outro", "type": "outro", "slots": { "source": "" } }
]
}
The assembler owns everything universal — background layers, the track-97 #brand-corner watermark, the single paused GSAP timeline + crossfades, track allocation (scenes on 4,5,6,8,9..; outro fixed on 7; 97 corner; 98 audio; 99 captions), captions/audio tracks, and the window.__timelines["<slug>"] registry. Scene-types own only their content + entrance motion. There are no per-episode JSON props files beyond the spec.
The scaffold does NOT create out/<slug>/ or public/voice/<slug>/ — those come from bun run audio.
After scaffolding (author the spec)
- Pick scene-types & fill slots. Edit
scene-spec.json: per scene set id (unique kebab), type (one of the 44 scene-types), optional duration (else the type default), optional status (draft/approved for the HITL loop), and slots. To see every type and each type's exact slots/repeat ranges, run bun run scene:gallery or read templates/scenes/<type>/v1/manifest.json. Keep an outro-role scene as the final scene on fixed track 7. Self-framed types (code, social-card, code-output, annotated-asset, before-after) already encode the no-double-frame rule.
- Validate fast (no assembly):
bun run scene:check src/episodes/<slug>/scene-spec.json
- Re-assemble after every spec edit:
bun run assemble <slug>
- Per-scene visual QA:
bun run scripts/scene-qa.ts <slug> [--scenes=id1,id2]
Re-assembles, captures one settled "final" frame per scene plus a contact-sheet.jpg grid (--frames=3 for entry/mid/late motion debugging), and runs hyperframes inspect for overflow/overlap (no full mp4). Show the contact sheet in the chat for review; iterate only the scenes you changed via --scenes=<id>.
- Generate voice + captions:
bun run audio examples/<slug>.txt --lang=es --out=public/voice/<slug>/
- Stage the assets (the renderer does not auto-copy):
cp public/voice/<slug>/voice.mp3 src/episodes/<slug>/assets/voice.mp3
cp public/voice/<slug>/captions.json src/episodes/<slug>/assets/captions.json
- Final render (only after per-scene approval):
bun run render:episode <slug> --format=mp4
Gotchas
- Never hand-edit
index.html. It is fully generated from scene-spec.json. Any manual change is lost on the next assemble. Edit the spec, then bun run assemble <slug>.
outro is always last. It's the pinned brand sign-off on fixed track 7; the brand corner fades out as it enters. Do not replace it with a plain text @handle card — the brand presence is the outro scene-type plus the shell's #brand-corner.
render-episode.ts does NOT auto-copy assets. Copy voice.mp3 and captions.json manually from public/voice/<slug>/ into the episode's assets/. Render is silent if assets/voice.mp3 is absent.
- Re-assemble after editing the spec, before QA or render.
scene-qa re-assembles for you, but render:episode reads whatever index.html is on disk.
When NOT to use
- For a render of an existing episode — just run
bun run render:episode <slug>.
- For editing an existing short — edit its
scene-spec.json and bun run assemble <slug>; no scaffold needed.