Skip to content

refactor(skills): restructure product-launch-video into references + sub-agents#1597

Open
WaterrrForever wants to merge 18 commits into
mainfrom
refactor/product-launch-video-skill
Open

refactor(skills): restructure product-launch-video into references + sub-agents#1597
WaterrrForever wants to merge 18 commits into
mainfrom
refactor/product-launch-video-skill

Conversation

@WaterrrForever

Copy link
Copy Markdown
Collaborator

What

Restructure the product-launch-video skill from a phase/style-preset-heavy
layout into a leaner one (references/ + scripts/ + sub-agents/), and extend
the shared hyperframes-core / -creative / -media skills with the formats and
presets 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-video skill

  • Replace phases/ + agents/ with references/ (composition, motion-language,
    story-design, visual-design) and sub-agents/frame-worker.md.
  • Slim the scripts to audio / captions / transitions / assemble-index (+ lib
    helpers); drop validate/verify/prep/hoist and the bundled assets/sfx/.

Shared skills

  • hyperframes-core: add references/script-format.md + storyboard-format.md.
  • hyperframes-creative: add references/design-spec.md and frame-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 product
    material and downstream planners key off the [video] marker.

Test plan

  • bun run build
  • npx hyperframes lint / npx hyperframes validate on a sample composition
  • Smoke-run /product-launch-video end-to-end on a sample product URL

WaterrrForever and others added 2 commits June 20, 2026 01:39
…-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"));
Comment thread skills/product-launch-video/scripts/audio.mjs Fixed
mkdirSync(dirname(outPath), { recursive: true });
writeFileSync(outPath, html);
// ── write caption-overrides.json shim ──
if (!existsSync(overridesPath)) writeFileSync(overridesPath, "[]\n");
Comment thread skills/product-launch-video/scripts/audio.mjs Fixed
Comment thread skills/hyperframes-media/scripts/lib/heygen.mjs Fixed
WaterrrForever and others added 11 commits June 20, 2026 21:37
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);
WaterrrForever and others added 5 commits June 22, 2026 02:36
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"));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants