refactor(skills): restructure product-launch-video into references + sub-agents#1597
refactor(skills): restructure product-launch-video into references + sub-agents#1597WaterrrForever wants to merge 18 commits into
Conversation
…-video Collapse the phase/style-preset-heavy product-launch-video skill into a leaner product-lunch-video layout (references + scripts + sub-agents), and extend the hyperframes core/creative/media skills with new format and preset references. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…unch-video Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| rmSync(td, { recursive: true, force: true }); | ||
| } catch {} | ||
| return r; | ||
| writeFileSync(storyboardPath, lines.join("\n")); |
| mkdirSync(dirname(outPath), { recursive: true }); | ||
| writeFileSync(outPath, html); | ||
| // ── write caption-overrides.json shim ── | ||
| if (!existsSync(overridesPath)) writeFileSync(overridesPath, "[]\n"); |
Extend the validation checklist and finalize gate to run `hyperframes inspect` and midpoint `snapshot`, and update the frame-worker self-check with template-transport and hero-visibility callouts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add biennale-yellow, blue-professional, bold-poster, broadside, cartesian, cobalt-grid, coral, creative-mode, daisy-days, and editorial-forest frame presets (FRAME.md + frame-showcase.html each), and list all 12 presets in the design-spec preset table with Look / Pick-when descriptions. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reframe the skill's planning prompts around a shot model (entrance → development → settle) so frames choreograph across their full duration instead of animating in then freezing: - visual-design: "every frame is a directed shot"; require ≥3 effects sequenced into phases; add Reproduce / Adapt / Compose blueprint postures; phased composition notes by default - motion-language / story-design: shot model, phases, idle-life budget, both failure modes (slideshow / screensaver) on the negative list - SKILL.md: capture gate now requires visible-text.txt + asset-descriptions.md as the canonical asset inventory - frame-worker: align with the directed-shot brief Also fix transitions.mjs: stamp a full-span anchor so window.__timelines["main"].duration() equals the composition total — without it the Studio reads a short master duration and collapses its timeline (clips dropped, blank stage). Render engine was unaffected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fetch-sfx passed no min_score to searchSounds, inheriting the HeyGen /v3/audio/sounds server default of 0.7. But good sound_effects matches score only ~0.52-0.67, so most named cues (click, pop, ding, sparkle, notification, impact, ...) silently returned 0 hits and were skipped — only whoosh/swoosh-family hits cleared 0.7. Floor the SFX search to 0.4 so authored cues resolve; BGM keeps the default (music scores high). Verified end-to-end: cues that previously dropped now download. Also refresh the searchSounds doc comment: data is a ranked array (not an object), query is required (>=1 char), limit caps at 50, and note the min_score gotcha. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Asset staging used to happen only inside assemble-index.mjs (Step 5), so during Step 4 the frame workers and the live preview referenced public/ files that weren't there yet. Extract the staging logic and run it earlier: - new lib/assets.mjs — shared stageAssets() (named-candidates only, first-wins, safe to call twice) - new stage-assets.mjs — runs at Step 4 close so workers + preview see real public/<basename> files - assemble-index.mjs drops its inline copy and calls the shared lib as an idempotent backstop for late-named assets Also condense SKILL.md (goal + gate per step; drop the example-phrasing and step tables) and tighten frame-worker's Reproduce / Adapt / Compose guidance. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- test-skills-fresh.sh: update the verification list to the current 10 workflows (drop removed footage-recut; add website-to-video, embedded-captions, graphic-overlays, slideshow) + refresh examples - captions.mjs: rank accent colors by chroma so the loudest hue maps to --cap-accent (e.g. biennale's solar sun over near-paper paper-deep) - biennale-yellow: add caption-skin.html (preset-local karaoke lower-third) and preview it in frame-showcase.html Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Frontmatter name was left as product-launch-video-refactor on this branch, so `skills add` registered/displayed the skill under that name — mismatching the directory, the /product-launch-video router references, and the test-skills-fresh.sh verification list. Rename back to product-launch-video. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…captions Give each hyperframes-creative frame-preset its own lower-third karaoke caption look (caption-skin.html) and preview it in each frame-showcase.html. Rework product-launch-video captions.mjs to load a project-local caption-skin.html and inject brand tokens derived from frame.md — colors mapped to a shared semantic vocab (--cap-ink/--cap-canvas/--cap-accent/ --cap-accent-2 by name + chroma), fonts to --font-display/--font-body, plus the keep-out band geometry — filling the skin's reserved holes and wrapping in a <template>. Falls back to the built-in pill when no skin is present. SKILL.md Step 2 now copies the chosen preset's caption-skin.html into the project so the look auto-applies; Step 5 documents the source. Presets: blockframe, blue-professional, bold-poster, broadside, capsule, cartesian, cobalt-grid, coral, creative-mode, daisy-days, editorial-forest (biennale-yellow landed earlier). Each skin uses the shared --cap-* vocab plus the preset's own border/shadow/corner/font/highlight identity. Also fix the ink-color resolver to match "ink" only as a whole word-segment so "pink"/"soft-pink" no longer resolve as the ink color. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…a preset onto brand tokens Add scripts/build-frame.mjs: the LLM only chooses a frame preset; the script copies its FRAME.md -> frame.md and remixes colors/typography onto the project's brand tokens (capture/extracted/tokens.json), copies the preset's caption-skin.html, and self-validates (exit 1 on a broken mapping). Colors map onto the preset's keys by role — ink/canvas by name+luminance, every other color repainted with the brand accent's hue+sat at its OWN lightness so tint families (sun/sun-soft/haze) stay families; fonts swap the preset's display + body families for the brand's. Empty brand tokens -> the preset palette is kept. Extract the shared color/font parsing + semantic role mapping into lib/tokens.mjs; captions.mjs now imports it, so frame.md and the captions derive tokens from one place and stay consistent. SKILL.md Step 2 is reduced to "pick a preset, run build-frame.mjs"; scripts list updated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… general-video
Consolidate the triplicated audio pipeline into one engine in hyperframes-media
so workflows consume it instead of vendoring copies.
- engine: scripts/audio.mjs reads a neutral audio_request.json, writes id-keyed
audio_meta.json. Three capabilities degrade on ONE switch (HeyGen credential
present?): TTS HeyGen REST -> ElevenLabs -> Kokoro; BGM retrieve ->
Lyria/MusicGen generate; SFX retrieve (min_score 0.4) -> bundled 21-file
library. lib/{heygen,tts,bgm,sfx}.mjs; --only subset+merge; detached BGM via
wait-bgm.mjs; heygen-tts.mjs now wraps lib/tts.
- product-launch: scripts/audio.mjs is now a thin storyboard->request adapter
that maps the engine's meta back to the frame-keyed shape captions/assemble
consume; sync-durations kept; vendored lib/heygen.mjs moved into the engine.
- general-video: SKILL.md routes audio to the engine with a minimal request example.
- docs: media SKILL.md + references/{bgm,sfx}.md rewritten -- engine is the
single source, SFX bundled library is first-class, reference impl points here.
Not migrated yet: pr-to-video, faceless-explainer (still vendored).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rename the staging destination from public/ to assets/ consistently across the staging scripts (lib/assets.mjs, stage-assets.mjs, assemble-index.mjs) and the docs that reference the asset_candidates path form (SKILL.md, story-design, visual-design, frame-worker). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| total_duration_s: totalDuration, | ||
| }; | ||
| mkdirSync(dirname(outPath), { recursive: true }); | ||
| writeFileSync(outPath, JSON.stringify(meta, null, 2)); |
| total_duration_s: totalDuration, | ||
| }; | ||
| mkdirSync(dirname(outPath), { recursive: true }); | ||
| writeFileSync(outPath, JSON.stringify(meta, null, 2)); |
| const opts = { method, headers: { ...headers } }; | ||
| if (body !== undefined) { | ||
| opts.headers["Content-Type"] = "application/json"; | ||
| opts.body = JSON.stringify(body); |
| if (!res.ok) throw new Error(`download HTTP ${res.status}: ${String(url).slice(0, 80)}`); | ||
| const bytes = Buffer.from(await res.arrayBuffer()); | ||
| mkdirSync(dirname(destPath), { recursive: true }); | ||
| writeFileSync(destPath, bytes); |
| function transcodeToWav(bytes, destWav) { | ||
| const td = mkdtempSync(join(tmpdir(), "hf-tts-")); | ||
| const tmp = join(td, "a.mp3"); | ||
| writeFileSync(tmp, bytes); |
| if (!transcodeToWav(bytes, wavAbs)) return { ok: false, words: null }; | ||
| } else { | ||
| mkdirSync(dirname(wavAbs), { recursive: true }); | ||
| writeFileSync(wavAbs, bytes); |
Update build-frame.mjs / lib/tokens.mjs brand-token remix and captions.mjs accent handling, and align SKILL.md Step 2 with the script-driven design system. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the phases/ + agents/ tree (style-presets, prep/validate scripts, bundled sfx) with the references/ + sub-agents/ layout and the shared build-frame / assets / storyboard / tokens scripts, matching the product-launch-video architecture. Update SKILL.md and the retained scripts (ingest, fetch-pr, captions, audio, transitions) accordingly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…itecture Replace the phases/ + agents/ tree (style-presets, prep/validate scripts, bundled sfx) with the references/ + sub-agents/ layout and the shared build-frame / assets / storyboard / tokens scripts, matching the product-launch-video architecture. Update SKILL.md and the retained scripts (captions, audio, transitions, assemble-index) accordingly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add the claude frame preset (FRAME.md, caption-skin.html, frame-showcase.html) and register it in references/design-spec.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ition preview A CSS identifier cannot start with a digit, so an authored rule like `#1-wall-pushes-back { ... }` is an invalid selector and the browser drops the whole rule — taking the root's size/background with it. A full composition masks this (the host stretches/paints the frame), but a standalone preview has no host, so the root collapses to height:0 + transparent and renders blank. extractFullDocumentParts now rewrites `#<digit-leading-id>` selectors to their escaped valid form (`#\30 1-...`, still matching the element id), scoped to ids actually present and matched only as `#id` not followed by an ident char so hex colors are never touched. Also harden the <template> inner-HTML extraction to use the DOM instead of a greedy regex. Tests added. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| // (broadside's literally says "<template>"), which the linter's tag scanner then picks | ||
| // up as the root element → false root_missing_composition_id / root_missing_dimensions. | ||
| // The comments are preview/authoring docs, not needed in the generated composition. | ||
| out = out.replace(/<!--[\s\S]*?-->/g, ""); |
| // (broadside's literally says "<template>"), which the linter's tag scanner then picks | ||
| // up as the root element → false root_missing_composition_id / root_missing_dimensions. | ||
| // The comments are preview/authoring docs, not needed in the generated composition. | ||
| out = out.replace(/<!--[\s\S]*?-->/g, ""); |
| // index.html's <head>, so tokens still resolve there too.) | ||
| html = html.replace(/[ \t]*<style data-brand-tokens>[\s\S]*?<\/style>\s*\n?/g, ""); | ||
| // ── write caption-overrides.json shim ── | ||
| if (!existsSync(overridesPath)) writeFileSync(overridesPath, "[]\n"); |
| `BGM: Lyria unavailable → installing local MusicGen fallback (${BGM_PY_DEPS.join(" ")})…`, | ||
| ); | ||
| const r = spawnSync("pip", ["install", "-q", ...BGM_PY_DEPS], { stdio: "ignore" }); | ||
| writeFileSync(storyboardPath, lines.join("\n")); |
| // (broadside's literally says "<template>"), which the linter's tag scanner then picks | ||
| // up as the root element → false root_missing_composition_id / root_missing_dimensions. | ||
| // The comments are preview/authoring docs, not needed in the generated composition. | ||
| out = out.replace(/<!--[\s\S]*?-->/g, ""); |
| // index.html's <head>, so tokens still resolve there too.) | ||
| html = html.replace(/[ \t]*<style data-brand-tokens>[\s\S]*?<\/style>\s*\n?/g, ""); | ||
| // ── write caption-overrides.json shim ── | ||
| if (!existsSync(overridesPath)) writeFileSync(overridesPath, "[]\n"); |
| const targetS = Math.max(1, totalS); | ||
| const seedS = Math.min(bgmSeedSeconds, 30); | ||
| const loops = targetS > seedS ? Math.ceil(targetS / seedS) : 1; | ||
| writeFileSync(storyboardPath, lines.join("\n")); |
Review — storyboard angle + live end-to-end testingReviewed the storyboard-facing surfaces statically, then drove 6 workflows end-to-end on this branch ( VerdictThe storyboard core is solid and the architecture is coherent, but driving real videos surfaced 3 blockers + a systemic validation gap that would hit real users. Suggest fixing the blockers and the root-background/validation issues before merge; the rest are good follow-ups. 🔴 Blockers
🟠 Major
🟡 Minor / polish
🟢 What's solid (verified)
Findings from live end-to-end runs on |
What
Restructure the
product-launch-videoskill from a phase/style-preset-heavylayout into a leaner one (
references/+scripts/+sub-agents/), and extendthe shared
hyperframes-core/-creative/-mediaskills with the formats andpresets the new layout depends on.
Net: 349 files, +3,721 / −41,964 — mostly deleting the old
phases/,agents/,bundled SFX assets, and oversized scripts.
Why
The old skill carried a large, drift-prone surface: per-phase guides, design/motion
rules duplicated between the skill and the shared references, ~20 bundled SFX files,
and multi-thousand-line scripts (validate/verify/prep/hoist). Consolidating the
rules into shared references + a single frame-worker sub-agent removes the
duplication and shrinks the skill to what the orchestrator actually needs.
How
product-launch-videoskillphases/+agents/withreferences/(composition, motion-language,story-design, visual-design) and
sub-agents/frame-worker.md.helpers); drop validate/verify/prep/hoist and the bundled
assets/sfx/.Shared skills
hyperframes-core: addreferences/script-format.md+storyboard-format.md.hyperframes-creative: addreferences/design-spec.mdandframe-presets/(blockframe, capsule) with showcase HTML.
hyperframes-media: extend the bgm/sfx references.hyperframes/general-video: update routing copy.CLI
contentExtractor.ts: surface downloaded video clips first, tagged[video],sourced from
video-manifest.json— hero motion clips are the strongest productmaterial and downstream planners key off the
[video]marker.Test plan
bun run buildnpx hyperframes lint/npx hyperframes validateon a sample composition/product-launch-videoend-to-end on a sample product URL