Imported from cgaravitoq/motion-shorts (
AGENTS.md). Install upstream withnpx skills add cgaravitoq/motion-shorts. Copyright stays with the author.
AGENTS.md — motion-shorts
Canonical file.
CLAUDE.mdis a symlink to this file. IfCLAUDE.mdis a regular file, run once:rm CLAUDE.md && ln -s AGENTS.md CLAUDE.md
Read this first when working in the repo. It's a map, not a manual — navigate deeper as needed via the pointers below.
What this is
Turborepo monorepo for vertical 9:16 motion-graphics shorts (YouTube, TikTok, LinkedIn, Reels).
script.txt -> ElevenLabs TTS -> voice.mp3 + word-level captions.json
|
scene-spec.json (typed scenes) -> bun run assemble -> monolithic index.html (1:1, generated)
|
per-scene scene-qa (snapshot + inspect) -> bun run render:episode -> mp4 / mov / webm
Shorts are authored as a typed scene-spec.json (an ordered list of parametric scene-types + their slots). A deterministic assembler turns it into the monolithic index.html — identical spec ⇒ identical bytes. Agents fill parameters; they never hand-write HTML. index.html is generated; edit scene-spec.json and re-assemble.
apps/hyperframe/ Shorts pipeline (Hyperframes 0.6.x + GSAP 3.15.x)
templates/_shell/ Universal shell: look + paused timeline + tracks, emitted into every episode
templates/scenes/<type>/v1/ 44 scene-types - 29 general (hook, title-cards, flow, fanout, metric, bars,
big-stat, comparison, timeline, quote, code, social-card,
progress-ring, line-chart, contrib-heatmap, decision-tree,
media-split, annotated-asset, code-output, dashboard-composite,
statement-lower-third, logo-grid, before-after, editorial-hook,
editorial-lineup, editorial-profile, editorial-cheat-sheet,
editorial-outro, outro)
plus 15 brand-pack-driven promo-* story-ad types
scripts/lib/ Engine: scene-instantiator, assemble-episode, scene-spec, scene-router
src/episodes/<slug>/ scene-spec.json (source of truth) -> generated index.html
packages/audio/ TTS + STT + ffprobe + script pacing (@cgaravitoq/audio)
.agents/skills/ Agent workflow skills (canonical-short, new-episode, etc.)
docs/ Rules, layout, commands, decisions (read on-demand)
Critical constraints
Breaking any of these will corrupt the render. They are non-negotiable.
- Monolithic single-file (generated). Each
apps/hyperframe/src/episodes/<slug>/index.htmlcontains ALL CSS + HTML + GSAP + captions JSON inline and is generated by the assembler fromscene-spec.json— never hand-edited. Zerodata-composition-src— the runtime force-appliesposition: absolute; top:0; left:0; 100%x100%on tracked stage children, collapsing sub-comp flex layouts. - GSAP:
paused: true+window.__timelines["<id>"]registry. Frame-accurate seek requires both. Missing registry = no animation. Missingpaused: true= frame seek breaks. - Times in SECONDS.
data-durationis seconds, GSAP timeline matches. Frame/30 = sec. - Deterministic only. No
Math.random,Date.now,repeat: -1, or async timeline construction. Lint catches it. - CWD for CLI. Run
bunx hyperframesandbun run <script>fromapps/hyperframe/(or viaturbo run). Running from root fails to find episodes. - bun + biome.
bun installalways from repo root. Biome for TS/JSON lint;bunx hyperframes lint <dir>for HTML compositions. No eslint, no prettier, no npm. - Seek-safe GSAP.
onStart/onComplete/onUpdate/tl.call()do NOT fire during seek — Hyperframes seeks frame-by-frame, never plays. Usetl.set(target, props, t)(zero-duration tween) for discrete transitions; it materialises at any seek position. For animated counters, bar fills, and discrete transitions use staggeredtl.set(target, props, t)keyframes (the pattern used bymetric/big-stat). - Track-index convention (assembler-allocated).
0..3BG layers,4,5,6,8,9..scenes,7for theoutroscene,97for#brand-corner,98for audio,99for captions. The assembler owns allocation — don't hand-assign. - Artifact persistence. R2 + remote manifests (
render.remote.json,assets.remote.json) are canonical for render/audio/image artifacts. Local generated media are cache/working copies only; do not commit heavy episode binaries. Hydrate first-time clones withbun run hydrate:episode <slug>when assets live only in R2.
Scene-hub authoring
A short is a scene-spec.json. To author one (CWD apps/hyperframe/):
- Pick scene-types.
bun run scene:gallery(gallery episode) to choose from the 44 types. Each type's exact slots: readtemplates/scenes/<type>/v1/manifest.json. Repeatable slots have ranges (e.g.title-cards.cards2-6,flow.steps2-6,metric.stats1-4).outrois the pinned brand sign-off, always last. Seven types are asset-led (media-split, annotated-asset, code-output, dashboard-composite, statement-lower-third, logo-grid, before-after) - image slots bind paths under the episode'sassets/. - Scaffold + fill.
bun run new:episode <slug> [--intent=...]writes a starter spec; editsrc/episodes/<slug>/scene-spec.jsonslots, thenbun run scene:check <slug-spec>to validate. - Assemble.
bun run assemble <slug>regeneratesindex.html(run after every spec edit). - Per-scene QA (HITL).
bun run scripts/scene-qa.ts <slug> [--scenes=id1,id2]snapshots each scene + runshyperframes inspect(overflow/overlap), no full render. Iterate only rejected scenes. - Render.
bun run render:episode <slug> --format=mp4once all scenes pass.
Add a new scene-type only when an existing one can't express the layout — see docs/templates.md for the 5-file contract.
Visual framing rule
Do not wrap a self-framed object in a generic glass/card container. Terminal windows, code editors, browser/app windows, social post cards, phone/device mockups, and media player cards are already containers; make them the primary scene object and animate that object directly. Use a glass/card frame only for loose content that needs grouping: metric lists, labels, unframed diagrams, badge groups, or abstract blocks.
For diagrams, choose the frame based on density. Compact charts or small decision diagrams can sit in a card. Workflow graphs, pipelines, and multi-node flowcharts should use an open canvas or a full-scene frame so the graph has breathing room in 9:16.
Navigation — read on-demand
| Need | Go to |
|---|---|
| Full rules reference | docs/rules.md |
| Env vars and local defaults | .env.example |
| Voice IDs, TTS gotchas, pause injection | docs/voice-config.md |
| Scene-type hub + adding a new scene-type | docs/templates.md + apps/hyperframe/templates/scenes/ |
| List/preview every scene-type | bun run scene:gallery (gallery episode) |
| Typography roles (informational design reference) | .agents/skills/canonical-short/references/typography-system.md |
| Render profile (9:16 short, safe zones, lint) | docs/formats.md |
| Brand packs (white-label) | docs/brand-packs.md |
| Build a new short (e2e playbook) | .agents/skills/canonical-short/SKILL.md |
| Generate audio + captions | .agents/skills/audio-pipeline/SKILL.md |
| Scaffold a new episode | .agents/skills/new-episode/SKILL.md |
| Produce a short from a source URL/idea (e2e + archive) | .agents/skills/produce-from-source/SKILL.md |
| Per-platform copy + voice gate + Notion archive | .agents/skills/generate-distribution-copy/SKILL.md |
| Composite a talking-head presenter cutout | .agents/skills/short-talking-head/SKILL.md |
Environment
No process.env in .ts source. All env reads go through the package's env.ts.
Catalog tracking
This project is tracked in the personal project catalog: private repo cgaravitoq/projects, file projects/motion-shorts.md. Before ending a work session here, update that entry — status/next/updated frontmatter + one ## Log line — and push the catalog (or use the projects-catalog MCP update_project tool).