@vibeframe/mcp-server 0.72.0 → 0.74.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -11
- package/dist/index.js +795 -490
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -7330,6 +7330,441 @@ var require_dist = __commonJS({
|
|
|
7330
7330
|
}
|
|
7331
7331
|
});
|
|
7332
7332
|
|
|
7333
|
+
// ../cli/src/commands/_shared/scene-project.ts
|
|
7334
|
+
import { mkdir, readFile, writeFile, access } from "node:fs/promises";
|
|
7335
|
+
import { resolve, basename } from "node:path";
|
|
7336
|
+
function aspectToDims(aspect) {
|
|
7337
|
+
return ASPECT_DIMS[aspect];
|
|
7338
|
+
}
|
|
7339
|
+
function defaultVibeProjectConfig(name) {
|
|
7340
|
+
return {
|
|
7341
|
+
name,
|
|
7342
|
+
aspect: "16:9",
|
|
7343
|
+
defaultSceneDuration: 5,
|
|
7344
|
+
providers: { image: null, tts: null, transcribe: null },
|
|
7345
|
+
budget: { maxUsd: 0 }
|
|
7346
|
+
};
|
|
7347
|
+
}
|
|
7348
|
+
function buildHyperframesConfig() {
|
|
7349
|
+
return {
|
|
7350
|
+
$schema: "https://hyperframes.heygen.com/schema/hyperframes.json",
|
|
7351
|
+
registry: "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry",
|
|
7352
|
+
paths: {
|
|
7353
|
+
blocks: "compositions",
|
|
7354
|
+
components: "compositions/components",
|
|
7355
|
+
assets: "assets"
|
|
7356
|
+
}
|
|
7357
|
+
};
|
|
7358
|
+
}
|
|
7359
|
+
function buildHyperframesMeta(name, now = /* @__PURE__ */ new Date()) {
|
|
7360
|
+
return { id: name, name, createdAt: now.toISOString() };
|
|
7361
|
+
}
|
|
7362
|
+
function mergeHyperframesConfig(existing, defaults) {
|
|
7363
|
+
const out = { ...defaults, ...existing };
|
|
7364
|
+
if (existing.paths || defaults.paths) {
|
|
7365
|
+
out.paths = { ...defaults.paths ?? {}, ...existing.paths ?? {} };
|
|
7366
|
+
}
|
|
7367
|
+
return out;
|
|
7368
|
+
}
|
|
7369
|
+
function buildEmptyRootHtml(opts) {
|
|
7370
|
+
const { width, height } = ASPECT_DIMS[opts.aspect];
|
|
7371
|
+
return `<!doctype html>
|
|
7372
|
+
<html lang="en">
|
|
7373
|
+
<head>
|
|
7374
|
+
<meta charset="UTF-8" />
|
|
7375
|
+
<meta name="viewport" content="width=${width}, height=${height}" />
|
|
7376
|
+
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
|
7377
|
+
<style>
|
|
7378
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
7379
|
+
html, body {
|
|
7380
|
+
margin: 0;
|
|
7381
|
+
width: ${width}px;
|
|
7382
|
+
height: ${height}px;
|
|
7383
|
+
overflow: hidden;
|
|
7384
|
+
background: #000;
|
|
7385
|
+
}
|
|
7386
|
+
</style>
|
|
7387
|
+
</head>
|
|
7388
|
+
<body>
|
|
7389
|
+
<div
|
|
7390
|
+
id="root"
|
|
7391
|
+
data-composition-id="main"
|
|
7392
|
+
data-start="0"
|
|
7393
|
+
data-duration="${opts.duration}"
|
|
7394
|
+
data-width="${width}"
|
|
7395
|
+
data-height="${height}"
|
|
7396
|
+
>
|
|
7397
|
+
<!-- Scenes added via \`vibe scene add\` are inserted here. -->
|
|
7398
|
+
<!-- Each scene reference: data-composition-id, data-composition-src, data-start, data-duration, data-track-index. -->
|
|
7399
|
+
<!-- See compositions/*.html for sub-composition contents. -->
|
|
7400
|
+
|
|
7401
|
+
</div>
|
|
7402
|
+
|
|
7403
|
+
<script>
|
|
7404
|
+
window.__timelines = window.__timelines || {};
|
|
7405
|
+
window.__timelines["main"] = gsap.timeline({ paused: true });
|
|
7406
|
+
</script>
|
|
7407
|
+
</body>
|
|
7408
|
+
</html>
|
|
7409
|
+
`;
|
|
7410
|
+
}
|
|
7411
|
+
function buildDesignMd(opts) {
|
|
7412
|
+
const { name, style } = opts;
|
|
7413
|
+
const intro = style ? `Visual identity for **${name}**, scaffolded from the **${style.name}** style (after ${style.designer}). Customise freely \u2014 this file is the single source of truth for every scene's palette, typography, and motion.` : `Visual identity for **${name}**. Fill the sections below before authoring any scene HTML or generating any backdrop. Pick a named style with \`vibe scene styles\` if you want a credible starting point.`;
|
|
7414
|
+
const moodLine = style ? `**Mood:** ${style.mood} \xB7 **Best for:** ${style.bestFor}` : `**Mood:** _(one line \u2014 what should the viewer FEEL?)_`;
|
|
7415
|
+
const palette = style ? `${style.palette.map((c) => `- \`${c}\``).join("\n")}
|
|
7416
|
+
|
|
7417
|
+
${style.paletteNotes}` : `- _hex_ \u2014 primary
|
|
7418
|
+
- _hex_ \u2014 accent
|
|
7419
|
+
|
|
7420
|
+
_2\u20133 colours max. Declare explicit hex values; never name colours abstractly._`;
|
|
7421
|
+
const typography = style ? style.typography : `_One family, two weights. State the role of each (headline / label / body)._`;
|
|
7422
|
+
const composition = style ? style.composition : `_Grid? Centered? Layered? How does negative space behave?_`;
|
|
7423
|
+
const motion = style ? `${style.motion}
|
|
7424
|
+
|
|
7425
|
+
**GSAP signature:** ${style.gsapSignature}` : `_How fast? Snappy or fluid? Overshoot or precision?_
|
|
7426
|
+
|
|
7427
|
+
**GSAP signature:** _e.g. \`expo.out\`, \`sine.inOut\`, \`back.out(1.8)\`_`;
|
|
7428
|
+
const transition = style ? style.transition : `_Which Hyperframes shader matches the energy? (Cinematic Zoom, Cross-Warp Morph, Glitch, Domain Warp, \u2026)_`;
|
|
7429
|
+
const avoid = style ? style.avoid.map((a) => `- ${a}`).join("\n") : `- _anti-pattern 1_
|
|
7430
|
+
- _anti-pattern 2_
|
|
7431
|
+
- _anti-pattern 3_`;
|
|
7432
|
+
return `# ${name} \u2014 Design
|
|
7433
|
+
|
|
7434
|
+
> **Hard-gate.** This file defines the visual identity of every scene.
|
|
7435
|
+
> Author it before generating any HTML, backdrop image, or motion.
|
|
7436
|
+
> The Hyperframes \`hyperframes\` skill enforces this: scenes that
|
|
7437
|
+
> contradict DESIGN.md are rejected.
|
|
7438
|
+
|
|
7439
|
+
${intro}
|
|
7440
|
+
|
|
7441
|
+
## Style
|
|
7442
|
+
|
|
7443
|
+
${moodLine}
|
|
7444
|
+
|
|
7445
|
+
## Palette
|
|
7446
|
+
|
|
7447
|
+
${palette}
|
|
7448
|
+
|
|
7449
|
+
## Typography
|
|
7450
|
+
|
|
7451
|
+
${typography}
|
|
7452
|
+
|
|
7453
|
+
## Composition
|
|
7454
|
+
|
|
7455
|
+
${composition}
|
|
7456
|
+
|
|
7457
|
+
## Motion
|
|
7458
|
+
|
|
7459
|
+
${motion}
|
|
7460
|
+
|
|
7461
|
+
## Transition
|
|
7462
|
+
|
|
7463
|
+
${transition}
|
|
7464
|
+
|
|
7465
|
+
## What NOT to do
|
|
7466
|
+
|
|
7467
|
+
${avoid}
|
|
7468
|
+
|
|
7469
|
+
---
|
|
7470
|
+
|
|
7471
|
+
_Browse other named styles: \`vibe scene styles\`_
|
|
7472
|
+
${style ? `_This file was seeded by \`vibe scene init --visual-style "${style.name}"\`._` : `_Seed this file from a named style: \`vibe scene init <dir> --visual-style "<name>"\`._`}
|
|
7473
|
+
`;
|
|
7474
|
+
}
|
|
7475
|
+
function buildStoryboardMd(name, duration = 12) {
|
|
7476
|
+
return `---
|
|
7477
|
+
title: ${name}
|
|
7478
|
+
duration: ${duration}
|
|
7479
|
+
aspect: 16:9
|
|
7480
|
+
tts: auto
|
|
7481
|
+
imageProvider: openai
|
|
7482
|
+
---
|
|
7483
|
+
|
|
7484
|
+
# ${name} \u2014 Storyboard
|
|
7485
|
+
|
|
7486
|
+
Edit these beats before running \`vibe build\`. Each beat starts with
|
|
7487
|
+
YAML cues that drive narration, backdrop generation, and timing.
|
|
7488
|
+
|
|
7489
|
+
## Beat hook \u2014 Hook
|
|
7490
|
+
|
|
7491
|
+
\`\`\`yaml
|
|
7492
|
+
narration: "Introduce the promise in one crisp sentence."
|
|
7493
|
+
backdrop: "Cinematic abstract technology backdrop, precise light, premium editorial feel"
|
|
7494
|
+
duration: 4
|
|
7495
|
+
\`\`\`
|
|
7496
|
+
|
|
7497
|
+
Show the core visual identity immediately. Keep copy short enough for one
|
|
7498
|
+
screen and one spoken breath.
|
|
7499
|
+
|
|
7500
|
+
## Beat proof \u2014 Proof
|
|
7501
|
+
|
|
7502
|
+
\`\`\`yaml
|
|
7503
|
+
narration: "Show the mechanism or proof point that makes the promise believable."
|
|
7504
|
+
backdrop: "Layered interface details, subtle motion trails, high-contrast product storytelling"
|
|
7505
|
+
duration: 4
|
|
7506
|
+
\`\`\`
|
|
7507
|
+
|
|
7508
|
+
Use this beat for the concrete differentiator: command, workflow, metric, or
|
|
7509
|
+
before/after.
|
|
7510
|
+
|
|
7511
|
+
## Beat close \u2014 Close
|
|
7512
|
+
|
|
7513
|
+
\`\`\`yaml
|
|
7514
|
+
narration: "Close with the action the viewer should remember."
|
|
7515
|
+
backdrop: "Resolved hero frame, confident final composition, clean negative space"
|
|
7516
|
+
duration: 4
|
|
7517
|
+
\`\`\`
|
|
7518
|
+
|
|
7519
|
+
End on the product name, offer, or command. Avoid adding a new idea in the
|
|
7520
|
+
final beat.
|
|
7521
|
+
`;
|
|
7522
|
+
}
|
|
7523
|
+
function buildProjectClaudeMd(name) {
|
|
7524
|
+
return `# ${name} \u2014 Scene Authoring Project
|
|
7525
|
+
|
|
7526
|
+
This project is **bilingual**: it works with both VibeFrame (\`vibe\`) and
|
|
7527
|
+
HeyGen Hyperframes (\`hyperframes\`). You can run either CLI inside this
|
|
7528
|
+
directory.
|
|
7529
|
+
|
|
7530
|
+
## Visual identity hard-gate
|
|
7531
|
+
|
|
7532
|
+
**Author \`DESIGN.md\` before any scene HTML.** It defines palette,
|
|
7533
|
+
typography, motion, and transition rules. Both the agent-driven path and
|
|
7534
|
+
the fallback emit reference it; scenes that contradict DESIGN.md are
|
|
7535
|
+
rejected by the Hyperframes \`hyperframes\` skill.
|
|
7536
|
+
|
|
7537
|
+
Browse named styles: \`vibe scene styles\`. Re-seed from one with
|
|
7538
|
+
\`vibe scene init . --visual-style "Swiss Pulse"\` (idempotent).
|
|
7539
|
+
|
|
7540
|
+
## Skills \u2014 USE THESE FIRST
|
|
7541
|
+
|
|
7542
|
+
**Always invoke the relevant skill before authoring scenes.** Skills encode
|
|
7543
|
+
framework-specific patterns (GSAP timeline registration, data-attribute
|
|
7544
|
+
semantics, VibeFrame pipeline conventions) that are NOT in generic web docs.
|
|
7545
|
+
|
|
7546
|
+
| Skill | Command | When to use |
|
|
7547
|
+
| ----------------- | ---------------- | ------------------------------------------------------------------------------------- |
|
|
7548
|
+
| **hyperframes** | \`/hyperframes\` | Cinematic-quality composition \u2014 DESIGN.md hard-gate, named styles, motion principles |
|
|
7549
|
+
| **vibe-scene** | \`/vibe-scene\` | VibeFrame's authoring loop, AI assets, lint feedback, pipeline integration |
|
|
7550
|
+
| **gsap** | \`/gsap\` | GSAP tweens, timelines, easing |
|
|
7551
|
+
|
|
7552
|
+
Optional: install the upstream Hyperframes skills once per machine when your agent supports skill commands:
|
|
7553
|
+
|
|
7554
|
+
\`\`\`bash
|
|
7555
|
+
npx skills add heygen-com/hyperframes
|
|
7556
|
+
\`\`\`
|
|
7557
|
+
|
|
7558
|
+
Restart your agent session (or reload the skill list) after installing.
|
|
7559
|
+
If skills aren't available, follow the **Key Rules** below \u2014 they cover
|
|
7560
|
+
the framework-level minimum, not the cinematic craft layer.
|
|
7561
|
+
|
|
7562
|
+
## Project structure
|
|
7563
|
+
|
|
7564
|
+
- \`DESIGN.md\` \u2014 visual identity contract (palette, type, motion, transitions)
|
|
7565
|
+
- \`STORYBOARD.md\` \u2014 per-beat narration/backdrop/duration cues for \`vibe build\`
|
|
7566
|
+
- \`index.html\` \u2014 root composition (timeline)
|
|
7567
|
+
- \`compositions/scene-*.html\` \u2014 per-scene HTML authored by you or the agent
|
|
7568
|
+
- \`assets/\` \u2014 shared media (narration audio, images, video)
|
|
7569
|
+
- \`transcript.json\` \u2014 Whisper word-level transcript (if narration exists)
|
|
7570
|
+
- \`hyperframes.json\` \u2014 HF registry config (speak to both toolchains)
|
|
7571
|
+
- \`vibe.project.yaml\` \u2014 VibeFrame config (providers, budget)
|
|
7572
|
+
- \`renders/\` \u2014 output MP4s
|
|
7573
|
+
|
|
7574
|
+
## Commands
|
|
7575
|
+
|
|
7576
|
+
\`\`\`bash
|
|
7577
|
+
vibe scene add <name> --narration "..." --visuals "..." # Author a new scene via AI
|
|
7578
|
+
vibe build # STORYBOARD.md \u2192 narrated MP4
|
|
7579
|
+
vibe scene lint # Validate scenes (in-process HF linter)
|
|
7580
|
+
vibe scene render # Render to MP4
|
|
7581
|
+
|
|
7582
|
+
# Hyperframes CLI (if installed \u2014 works in this project too)
|
|
7583
|
+
npx hyperframes preview
|
|
7584
|
+
npx hyperframes render
|
|
7585
|
+
\`\`\`
|
|
7586
|
+
|
|
7587
|
+
## Key Rules (for hand-authored scene HTML)
|
|
7588
|
+
|
|
7589
|
+
1. Every timed element needs \`data-start\`, \`data-duration\`, and \`data-track-index\`.
|
|
7590
|
+
2. Elements with timing **MUST** have \`class="clip"\` \u2014 the framework uses this for visibility control.
|
|
7591
|
+
3. Timelines must be paused and registered on \`window.__timelines\`:
|
|
7592
|
+
\`\`\`js
|
|
7593
|
+
window.__timelines = window.__timelines || {};
|
|
7594
|
+
window.__timelines["composition-id"] = gsap.timeline({ paused: true });
|
|
7595
|
+
\`\`\`
|
|
7596
|
+
4. Videos use \`muted\` with a separate \`<audio>\` element for the audio track.
|
|
7597
|
+
5. Sub-compositions use \`data-composition-src="compositions/file.html"\`.
|
|
7598
|
+
6. Only deterministic logic \u2014 no \`Date.now()\`, \`Math.random()\`, or network fetches.
|
|
7599
|
+
|
|
7600
|
+
## Linting \u2014 run after changes
|
|
7601
|
+
|
|
7602
|
+
\`\`\`bash
|
|
7603
|
+
vibe scene lint # preferred \u2014 in-process, no network
|
|
7604
|
+
vibe scene lint --fix # auto-fix mechanical issues
|
|
7605
|
+
vibe scene lint --json # structured output for agent loops
|
|
7606
|
+
\`\`\`
|
|
7607
|
+
`;
|
|
7608
|
+
}
|
|
7609
|
+
function buildSceneGitignore() {
|
|
7610
|
+
return `# VibeFrame caches
|
|
7611
|
+
.vibeframe/cache/
|
|
7612
|
+
.vibeframe/checkpoints/
|
|
7613
|
+
|
|
7614
|
+
# Render outputs
|
|
7615
|
+
renders/*.mp4
|
|
7616
|
+
tmp/
|
|
7617
|
+
|
|
7618
|
+
# OS / editor
|
|
7619
|
+
.DS_Store
|
|
7620
|
+
*.log
|
|
7621
|
+
`;
|
|
7622
|
+
}
|
|
7623
|
+
function isSceneScaffoldProfile(value) {
|
|
7624
|
+
return value === "minimal" || value === "agent" || value === "full";
|
|
7625
|
+
}
|
|
7626
|
+
function describeSceneScaffold(opts) {
|
|
7627
|
+
const dir = resolve(opts.dir);
|
|
7628
|
+
const profile = opts.profile ?? "full";
|
|
7629
|
+
const groups = {
|
|
7630
|
+
authoring: [
|
|
7631
|
+
resolve(dir, "STORYBOARD.md"),
|
|
7632
|
+
resolve(dir, "DESIGN.md"),
|
|
7633
|
+
resolve(dir, "vibe.project.yaml"),
|
|
7634
|
+
resolve(dir, ".gitignore")
|
|
7635
|
+
],
|
|
7636
|
+
render: [],
|
|
7637
|
+
agent: []
|
|
7638
|
+
};
|
|
7639
|
+
if (profile === "full") {
|
|
7640
|
+
groups.render = [
|
|
7641
|
+
resolve(dir, "index.html"),
|
|
7642
|
+
resolve(dir, "compositions"),
|
|
7643
|
+
resolve(dir, "assets"),
|
|
7644
|
+
resolve(dir, "renders"),
|
|
7645
|
+
resolve(dir, "hyperframes.json"),
|
|
7646
|
+
resolve(dir, "meta.json")
|
|
7647
|
+
];
|
|
7648
|
+
}
|
|
7649
|
+
if (profile === "agent" || profile === "full") {
|
|
7650
|
+
groups.agent = [
|
|
7651
|
+
resolve(dir, "SKILL.md"),
|
|
7652
|
+
resolve(dir, "references"),
|
|
7653
|
+
resolve(dir, "CLAUDE.md")
|
|
7654
|
+
];
|
|
7655
|
+
}
|
|
7656
|
+
return groups;
|
|
7657
|
+
}
|
|
7658
|
+
async function pathExists(p) {
|
|
7659
|
+
try {
|
|
7660
|
+
await access(p);
|
|
7661
|
+
return true;
|
|
7662
|
+
} catch {
|
|
7663
|
+
return false;
|
|
7664
|
+
}
|
|
7665
|
+
}
|
|
7666
|
+
async function scaffoldSceneProject(opts) {
|
|
7667
|
+
const dir = resolve(opts.dir);
|
|
7668
|
+
const name = opts.name ?? basename(dir);
|
|
7669
|
+
const aspect = opts.aspect ?? "16:9";
|
|
7670
|
+
const duration = opts.duration ?? 10;
|
|
7671
|
+
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
7672
|
+
const profile = opts.profile ?? "full";
|
|
7673
|
+
await mkdir(dir, { recursive: true });
|
|
7674
|
+
if (profile === "full") {
|
|
7675
|
+
await mkdir(resolve(dir, "compositions"), { recursive: true });
|
|
7676
|
+
await mkdir(resolve(dir, "assets"), { recursive: true });
|
|
7677
|
+
await mkdir(resolve(dir, "renders"), { recursive: true });
|
|
7678
|
+
}
|
|
7679
|
+
const created = [];
|
|
7680
|
+
const skipped2 = [];
|
|
7681
|
+
const merged = [];
|
|
7682
|
+
if (profile === "full") {
|
|
7683
|
+
const hfPath = resolve(dir, "hyperframes.json");
|
|
7684
|
+
const hfDefaults = buildHyperframesConfig();
|
|
7685
|
+
if (await pathExists(hfPath)) {
|
|
7686
|
+
const existingRaw = await readFile(hfPath, "utf-8");
|
|
7687
|
+
const existing = JSON.parse(existingRaw);
|
|
7688
|
+
const mergedConfig = mergeHyperframesConfig(existing, hfDefaults);
|
|
7689
|
+
await writeFile(hfPath, JSON.stringify(mergedConfig, null, 2) + "\n", "utf-8");
|
|
7690
|
+
merged.push(hfPath);
|
|
7691
|
+
} else {
|
|
7692
|
+
await writeFile(hfPath, JSON.stringify(hfDefaults, null, 2) + "\n", "utf-8");
|
|
7693
|
+
created.push(hfPath);
|
|
7694
|
+
}
|
|
7695
|
+
const metaPath = resolve(dir, "meta.json");
|
|
7696
|
+
if (await pathExists(metaPath)) {
|
|
7697
|
+
skipped2.push(metaPath);
|
|
7698
|
+
} else {
|
|
7699
|
+
await writeFile(metaPath, JSON.stringify(buildHyperframesMeta(name, now), null, 2) + "\n", "utf-8");
|
|
7700
|
+
created.push(metaPath);
|
|
7701
|
+
}
|
|
7702
|
+
const rootPath = resolve(dir, "index.html");
|
|
7703
|
+
if (await pathExists(rootPath)) {
|
|
7704
|
+
skipped2.push(rootPath);
|
|
7705
|
+
} else {
|
|
7706
|
+
await writeFile(rootPath, buildEmptyRootHtml({ aspect, duration }), "utf-8");
|
|
7707
|
+
created.push(rootPath);
|
|
7708
|
+
}
|
|
7709
|
+
}
|
|
7710
|
+
const vibePath = resolve(dir, "vibe.project.yaml");
|
|
7711
|
+
if (await pathExists(vibePath)) {
|
|
7712
|
+
skipped2.push(vibePath);
|
|
7713
|
+
} else {
|
|
7714
|
+
const cfg = { ...defaultVibeProjectConfig(name), aspect };
|
|
7715
|
+
await writeFile(vibePath, (0, import_yaml.stringify)(cfg), "utf-8");
|
|
7716
|
+
created.push(vibePath);
|
|
7717
|
+
}
|
|
7718
|
+
if (profile === "agent" || profile === "full") {
|
|
7719
|
+
const claudePath = resolve(dir, "CLAUDE.md");
|
|
7720
|
+
if (await pathExists(claudePath)) {
|
|
7721
|
+
skipped2.push(claudePath);
|
|
7722
|
+
} else {
|
|
7723
|
+
await writeFile(claudePath, buildProjectClaudeMd(name), "utf-8");
|
|
7724
|
+
created.push(claudePath);
|
|
7725
|
+
}
|
|
7726
|
+
}
|
|
7727
|
+
const designPath = resolve(dir, "DESIGN.md");
|
|
7728
|
+
if (await pathExists(designPath)) {
|
|
7729
|
+
skipped2.push(designPath);
|
|
7730
|
+
} else {
|
|
7731
|
+
await writeFile(
|
|
7732
|
+
designPath,
|
|
7733
|
+
buildDesignMd({ name, style: opts.visualStyle }),
|
|
7734
|
+
"utf-8"
|
|
7735
|
+
);
|
|
7736
|
+
created.push(designPath);
|
|
7737
|
+
}
|
|
7738
|
+
const storyboardPath = resolve(dir, "STORYBOARD.md");
|
|
7739
|
+
if (await pathExists(storyboardPath)) {
|
|
7740
|
+
skipped2.push(storyboardPath);
|
|
7741
|
+
} else {
|
|
7742
|
+
await writeFile(storyboardPath, buildStoryboardMd(name, duration), "utf-8");
|
|
7743
|
+
created.push(storyboardPath);
|
|
7744
|
+
}
|
|
7745
|
+
const gitignorePath = resolve(dir, ".gitignore");
|
|
7746
|
+
if (await pathExists(gitignorePath)) {
|
|
7747
|
+
skipped2.push(gitignorePath);
|
|
7748
|
+
} else {
|
|
7749
|
+
await writeFile(gitignorePath, buildSceneGitignore(), "utf-8");
|
|
7750
|
+
created.push(gitignorePath);
|
|
7751
|
+
}
|
|
7752
|
+
return { created, skipped: skipped2, merged, groups: describeSceneScaffold({ dir, profile }) };
|
|
7753
|
+
}
|
|
7754
|
+
var import_yaml, ASPECT_DIMS;
|
|
7755
|
+
var init_scene_project = __esm({
|
|
7756
|
+
"../cli/src/commands/_shared/scene-project.ts"() {
|
|
7757
|
+
"use strict";
|
|
7758
|
+
import_yaml = __toESM(require_dist(), 1);
|
|
7759
|
+
ASPECT_DIMS = {
|
|
7760
|
+
"16:9": { width: 1920, height: 1080 },
|
|
7761
|
+
"9:16": { width: 1080, height: 1920 },
|
|
7762
|
+
"1:1": { width: 1080, height: 1080 },
|
|
7763
|
+
"4:5": { width: 1080, height: 1350 }
|
|
7764
|
+
};
|
|
7765
|
+
}
|
|
7766
|
+
});
|
|
7767
|
+
|
|
7333
7768
|
// ../../node_modules/.pnpm/commander@12.1.0/node_modules/commander/lib/error.js
|
|
7334
7769
|
var require_error = __commonJS({
|
|
7335
7770
|
"../../node_modules/.pnpm/commander@12.1.0/node_modules/commander/lib/error.js"(exports) {
|
|
@@ -13713,12 +14148,12 @@ var init_api_keys = __esm({
|
|
|
13713
14148
|
label: "ImgBB",
|
|
13714
14149
|
showInSetup: false,
|
|
13715
14150
|
// not prompted in setup wizard — internal upload host
|
|
13716
|
-
envExampleComment: "ImgBB API Key (image hosting \u2014 used by Kling and
|
|
14151
|
+
envExampleComment: "ImgBB API Key (image hosting \u2014 used by Kling and Seedance for image-to-video uploads)",
|
|
13717
14152
|
envExampleUrl: "https://api.imgbb.com/",
|
|
13718
14153
|
// ImgBB has no provider class (envvar-only); doctor still shows what it
|
|
13719
14154
|
// unlocks at the apiKey level.
|
|
13720
14155
|
commandsUnlocked: [
|
|
13721
|
-
"generate video -p kling/
|
|
14156
|
+
"generate video -p kling/seedance (image-to-video upload host)"
|
|
13722
14157
|
]
|
|
13723
14158
|
});
|
|
13724
14159
|
defineProvider({
|
|
@@ -23787,13 +24222,18 @@ var init_fal = __esm({
|
|
|
23787
24222
|
defineProvider({
|
|
23788
24223
|
id: "fal",
|
|
23789
24224
|
label: "fal.ai (Seedance 2.0)",
|
|
24225
|
+
displayName: "Seedance 2.0",
|
|
24226
|
+
gateway: "fal.ai",
|
|
24227
|
+
aliases: ["seedance"],
|
|
24228
|
+
models: ["seedance-2.0", "seedance-2.0-fast"],
|
|
24229
|
+
capabilities: ["text-to-video", "image-to-video", "native-audio"],
|
|
23790
24230
|
apiKey: "fal",
|
|
23791
24231
|
kinds: ["video"],
|
|
23792
24232
|
resolverPriority: { video: 1 },
|
|
23793
24233
|
commandsUnlocked: [
|
|
23794
|
-
"generate video -p
|
|
23795
|
-
"generate video -p
|
|
23796
|
-
"generate video -p
|
|
24234
|
+
"generate video -p seedance (Seedance 2.0 via fal.ai \u2014 default since v0.57)",
|
|
24235
|
+
"generate video -p seedance --seedance-model fast (lower-latency variant)",
|
|
24236
|
+
"generate video -p seedance -i <image> (image-to-video)"
|
|
23797
24237
|
]
|
|
23798
24238
|
});
|
|
23799
24239
|
}
|
|
@@ -446848,7 +447288,9 @@ function buildAudioMuxFilter(audios) {
|
|
|
446848
447288
|
const inputIdx = i + 1;
|
|
446849
447289
|
const delayMs = Math.max(0, Math.round(a.absoluteStart * 1e3));
|
|
446850
447290
|
const volume = Number.isFinite(a.volume) ? a.volume : 1;
|
|
446851
|
-
const
|
|
447291
|
+
const durationHint = typeof a.durationHint === "number" && Number.isFinite(a.durationHint) ? Math.max(0, a.durationHint) : null;
|
|
447292
|
+
const clipCap = Math.max(0, a.clipDurationCap);
|
|
447293
|
+
const trimSec = durationHint === null ? clipCap : Math.min(durationHint, clipCap);
|
|
446852
447294
|
const label = `a${i}`;
|
|
446853
447295
|
const stage = [
|
|
446854
447296
|
`[${inputIdx}:a]`,
|
|
@@ -447107,6 +447549,57 @@ var init_scene_render = __esm({
|
|
|
447107
447549
|
}
|
|
447108
447550
|
});
|
|
447109
447551
|
|
|
447552
|
+
// ../cli/src/utils/audio.ts
|
|
447553
|
+
async function getAudioDuration(filePath) {
|
|
447554
|
+
try {
|
|
447555
|
+
return await ffprobeDuration(filePath);
|
|
447556
|
+
} catch (error) {
|
|
447557
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
447558
|
+
throw new Error(`Failed to get audio duration: ${message}`);
|
|
447559
|
+
}
|
|
447560
|
+
}
|
|
447561
|
+
async function getVideoDuration(filePath) {
|
|
447562
|
+
try {
|
|
447563
|
+
return await ffprobeDuration(filePath);
|
|
447564
|
+
} catch (error) {
|
|
447565
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
447566
|
+
throw new Error(`Failed to get video duration: ${message}`);
|
|
447567
|
+
}
|
|
447568
|
+
}
|
|
447569
|
+
async function extendVideoNaturally(videoPath, targetDuration, outputPath) {
|
|
447570
|
+
const videoDuration = await getVideoDuration(videoPath);
|
|
447571
|
+
const ratio = targetDuration / videoDuration;
|
|
447572
|
+
if (ratio <= 1) {
|
|
447573
|
+
const { copyFile: copyFile5 } = await import("node:fs/promises");
|
|
447574
|
+
await copyFile5(videoPath, outputPath);
|
|
447575
|
+
return;
|
|
447576
|
+
}
|
|
447577
|
+
if (ratio <= 1.15) {
|
|
447578
|
+
const slowFactor = (1 / ratio).toFixed(4);
|
|
447579
|
+
await execSafe("ffmpeg", ["-y", "-i", videoPath, "-filter:v", `setpts=${slowFactor}*PTS`, "-an", outputPath]);
|
|
447580
|
+
} else if (ratio <= 1.4) {
|
|
447581
|
+
const slowFactor = (1 / ratio).toFixed(4);
|
|
447582
|
+
await execSafe("ffmpeg", ["-y", "-i", videoPath, "-filter:v", `minterpolate=fps=60:mi_mode=mci,setpts=${slowFactor}*PTS`, "-an", outputPath]);
|
|
447583
|
+
} else {
|
|
447584
|
+
const slowRatio = 0.7;
|
|
447585
|
+
const slowedDuration = videoDuration / slowRatio;
|
|
447586
|
+
const freezeDuration = targetDuration - slowedDuration;
|
|
447587
|
+
if (freezeDuration <= 0) {
|
|
447588
|
+
const slowFactor = (1 / ratio).toFixed(4);
|
|
447589
|
+
await execSafe("ffmpeg", ["-y", "-i", videoPath, "-filter:v", `minterpolate=fps=60:mi_mode=mci,setpts=${slowFactor}*PTS`, "-an", outputPath]);
|
|
447590
|
+
} else {
|
|
447591
|
+
const slowFactor = (1 / slowRatio).toFixed(4);
|
|
447592
|
+
await execSafe("ffmpeg", ["-y", "-i", videoPath, "-filter:v", `setpts=${slowFactor}*PTS,tpad=stop_mode=clone:stop_duration=${freezeDuration.toFixed(2)}`, "-an", outputPath]);
|
|
447593
|
+
}
|
|
447594
|
+
}
|
|
447595
|
+
}
|
|
447596
|
+
var init_audio = __esm({
|
|
447597
|
+
"../cli/src/utils/audio.ts"() {
|
|
447598
|
+
"use strict";
|
|
447599
|
+
init_exec_safe();
|
|
447600
|
+
}
|
|
447601
|
+
});
|
|
447602
|
+
|
|
447110
447603
|
// ../cli/src/commands/_shared/hf-skill-bundle/bundle-content.ts
|
|
447111
447604
|
var SKILL_MD, HOUSE_STYLE_MD, MOTION_PRINCIPLES_MD, TYPOGRAPHY_MD, TRANSITIONS_MD;
|
|
447112
447605
|
var init_bundle_content = __esm({
|
|
@@ -448703,7 +449196,7 @@ async function getComposePrompts(opts) {
|
|
|
448703
449196
|
return baseError(`DESIGN.md not found at ${designPath}. Run \`vibe scene init <dir>\` first.`);
|
|
448704
449197
|
}
|
|
448705
449198
|
if (!existsSync24(storyboardPath)) {
|
|
448706
|
-
return baseError(`STORYBOARD.md not found at ${storyboardPath}. Run \`vibe scene init <dir>\`
|
|
449199
|
+
return baseError(`STORYBOARD.md not found at ${storyboardPath}. Run \`vibe scene init <dir>\` to create a starter, or add STORYBOARD.md with per-beat cues.`);
|
|
448707
449200
|
}
|
|
448708
449201
|
if (!existsSync24(skillPath)) {
|
|
448709
449202
|
warnings.push(
|
|
@@ -448929,7 +449422,10 @@ async function executeSceneBuild(opts) {
|
|
|
448929
449422
|
const mode = resolveSceneBuildMode(opts);
|
|
448930
449423
|
const storyboardPath = join25(projectDir, "STORYBOARD.md");
|
|
448931
449424
|
if (!existsSync26(storyboardPath)) {
|
|
448932
|
-
return failBeforePrimitives(
|
|
449425
|
+
return failBeforePrimitives(
|
|
449426
|
+
`STORYBOARD.md not found at ${storyboardPath}. Run \`vibe scene init <dir>\` to create a starter, or add STORYBOARD.md with per-beat cues.`,
|
|
449427
|
+
startedAt
|
|
449428
|
+
);
|
|
448933
449429
|
}
|
|
448934
449430
|
const storyboardMd = await readFile9(storyboardPath, "utf-8");
|
|
448935
449431
|
const parsed = parseStoryboard(storyboardMd);
|
|
@@ -449009,6 +449505,13 @@ async function executeSceneBuild(opts) {
|
|
|
449009
449505
|
}
|
|
449010
449506
|
composeData = composeResult.data;
|
|
449011
449507
|
}
|
|
449508
|
+
if (!existsSync26(join25(projectDir, "index.html"))) {
|
|
449509
|
+
await scaffoldSceneProject({
|
|
449510
|
+
dir: projectDir,
|
|
449511
|
+
name: projectDir.split(/[\\/]/).filter(Boolean).pop(),
|
|
449512
|
+
profile: "full"
|
|
449513
|
+
});
|
|
449514
|
+
}
|
|
449012
449515
|
await syncRootClipReferences(parsed.beats, projectDir, beatOutcomes);
|
|
449013
449516
|
let outputPath;
|
|
449014
449517
|
let renderResult;
|
|
@@ -449107,6 +449610,7 @@ async function dispatchBackdrop(beat, ctx) {
|
|
|
449107
449610
|
ctx.onProgress({ type: "backdrop-cached", beatId: beat.id, path: rel });
|
|
449108
449611
|
return { status: "cached", path: rel };
|
|
449109
449612
|
}
|
|
449613
|
+
loadSceneBuildEnv(ctx.projectDir);
|
|
449110
449614
|
const apiKey = process.env.OPENAI_API_KEY ?? "";
|
|
449111
449615
|
if (!apiKey) {
|
|
449112
449616
|
const error = "OPENAI_API_KEY not set \u2014 cannot dispatch backdrop";
|
|
@@ -449135,6 +449639,18 @@ async function dispatchBackdrop(beat, ctx) {
|
|
|
449135
449639
|
});
|
|
449136
449640
|
return { status: "generated", path: rel };
|
|
449137
449641
|
}
|
|
449642
|
+
function loadSceneBuildEnv(projectDir) {
|
|
449643
|
+
(0, import_dotenv2.config)({ path: join25(projectDir, ".env"), quiet: true });
|
|
449644
|
+
(0, import_dotenv2.config)({ path: resolve19(process.cwd(), ".env"), quiet: true });
|
|
449645
|
+
let dir = process.cwd();
|
|
449646
|
+
while (dir !== dirname15(dir)) {
|
|
449647
|
+
if (existsSync26(join25(dir, "pnpm-workspace.yaml"))) {
|
|
449648
|
+
(0, import_dotenv2.config)({ path: join25(dir, ".env"), quiet: true });
|
|
449649
|
+
return;
|
|
449650
|
+
}
|
|
449651
|
+
dir = dirname15(dir);
|
|
449652
|
+
}
|
|
449653
|
+
}
|
|
449138
449654
|
async function skipped(kind, beatId, reason, ctx) {
|
|
449139
449655
|
ctx.onProgress({ type: `${kind}-skipped`, beatId, reason });
|
|
449140
449656
|
return { status: "skipped" };
|
|
@@ -449165,20 +449681,24 @@ async function syncRootClipReferences(beats, projectDir, outcomes) {
|
|
|
449165
449681
|
const clipLines = [];
|
|
449166
449682
|
const audioLines = [];
|
|
449167
449683
|
for (const beat of beats) {
|
|
449168
|
-
const
|
|
449684
|
+
const outcome = outcomes.find((o) => o.beatId === beat.id);
|
|
449685
|
+
const duration = await resolveBeatDuration({
|
|
449686
|
+
beatDuration: beat.duration,
|
|
449687
|
+
narrationPath: outcome?.narrationPath,
|
|
449688
|
+
projectDir
|
|
449689
|
+
});
|
|
449169
449690
|
const compositionId = `scene-${beat.id}`;
|
|
449170
449691
|
clipLines.push(
|
|
449171
449692
|
` <div class="clip" data-composition-id="${compositionId}" data-composition-src="compositions/${compositionId}.html" data-start="${cursor}" data-duration="${duration}" data-track-index="0"></div>`
|
|
449172
449693
|
);
|
|
449173
|
-
const outcome = outcomes.find((o) => o.beatId === beat.id);
|
|
449174
449694
|
if (outcome?.narrationPath) {
|
|
449175
449695
|
audioLines.push(
|
|
449176
|
-
` <audio src="${outcome.narrationPath}" data-start="${cursor}" data-duration="${duration}" data-track-index="2"></audio>`
|
|
449696
|
+
` <audio id="narration-${beat.id}" src="${outcome.narrationPath}" data-start="${cursor}" data-duration="${duration}" data-track-index="2"></audio>`
|
|
449177
449697
|
);
|
|
449178
449698
|
}
|
|
449179
449699
|
cursor += duration;
|
|
449180
449700
|
}
|
|
449181
|
-
const totalDuration = cursor;
|
|
449701
|
+
const totalDuration = Number(cursor.toFixed(2));
|
|
449182
449702
|
const block = " <!-- vibe-scene-build: clip refs (auto-generated; safe to re-run) -->\n" + clipLines.join("\n") + (audioLines.length > 0 ? "\n" + audioLines.join("\n") : "") + "\n <!-- /vibe-scene-build -->";
|
|
449183
449703
|
let next;
|
|
449184
449704
|
const markerRe = /\n? *<!-- vibe-scene-build: clip refs.*?<!-- \/vibe-scene-build -->/s;
|
|
@@ -449196,22 +449716,36 @@ ${block}
|
|
|
449196
449716
|
}
|
|
449197
449717
|
}
|
|
449198
449718
|
next = next.replace(
|
|
449199
|
-
/(id="root"[\s\S]*?data-duration=")(
|
|
449719
|
+
/(id="root"[\s\S]*?data-duration=")([^"]*)(")/,
|
|
449200
449720
|
`$1${totalDuration}$3`
|
|
449201
449721
|
);
|
|
449202
449722
|
if (next !== html) {
|
|
449203
449723
|
await writeFile8(rootPath, next, "utf-8");
|
|
449204
449724
|
}
|
|
449205
449725
|
}
|
|
449726
|
+
async function resolveBeatDuration(opts) {
|
|
449727
|
+
const storyboardMin = opts.beatDuration ?? 3;
|
|
449728
|
+
if (!opts.narrationPath) return Number(storyboardMin.toFixed(2));
|
|
449729
|
+
try {
|
|
449730
|
+
const audioDuration = await getAudioDuration(join25(opts.projectDir, opts.narrationPath));
|
|
449731
|
+
return Number(Math.max(storyboardMin, audioDuration + 0.5).toFixed(2));
|
|
449732
|
+
} catch {
|
|
449733
|
+
return Number(storyboardMin.toFixed(2));
|
|
449734
|
+
}
|
|
449735
|
+
}
|
|
449736
|
+
var import_dotenv2;
|
|
449206
449737
|
var init_scene_build = __esm({
|
|
449207
449738
|
"../cli/src/commands/_shared/scene-build.ts"() {
|
|
449208
449739
|
"use strict";
|
|
449740
|
+
import_dotenv2 = __toESM(require_main(), 1);
|
|
449209
449741
|
init_dist();
|
|
449742
|
+
init_audio();
|
|
449210
449743
|
init_compose_scenes_skills();
|
|
449211
449744
|
init_compose_prompts();
|
|
449212
449745
|
init_agent_host_detect();
|
|
449213
449746
|
init_scene_render();
|
|
449214
449747
|
init_storyboard_parse();
|
|
449748
|
+
init_scene_project();
|
|
449215
449749
|
init_tts_resolve();
|
|
449216
449750
|
}
|
|
449217
449751
|
});
|
|
@@ -449221,8 +449755,10 @@ var output_exports = {};
|
|
|
449221
449755
|
__export(output_exports, {
|
|
449222
449756
|
COST_ESTIMATES: () => COST_ESTIMATES,
|
|
449223
449757
|
ExitCode: () => ExitCode,
|
|
449758
|
+
_resetDeprecationMemoryForTesting: () => _resetDeprecationMemoryForTesting,
|
|
449224
449759
|
apiError: () => apiError,
|
|
449225
449760
|
authError: () => authError,
|
|
449761
|
+
emitDeprecationWarning: () => emitDeprecationWarning,
|
|
449226
449762
|
exitWithError: () => exitWithError,
|
|
449227
449763
|
generalError: () => generalError,
|
|
449228
449764
|
isJsonMode: () => isJsonMode,
|
|
@@ -449367,6 +449903,19 @@ function suggestNext(tip) {
|
|
|
449367
449903
|
Tip: ${tip}`));
|
|
449368
449904
|
}
|
|
449369
449905
|
}
|
|
449906
|
+
function emitDeprecationWarning(oldName, newName, removeIn) {
|
|
449907
|
+
if (isJsonMode() || isQuietMode()) return;
|
|
449908
|
+
if (!process.stderr.isTTY) return;
|
|
449909
|
+
const key2 = `${oldName}\u2192${newName}`;
|
|
449910
|
+
if (_seenDeprecations.has(key2)) return;
|
|
449911
|
+
_seenDeprecations.add(key2);
|
|
449912
|
+
process.stderr.write(
|
|
449913
|
+
source_default.yellow(`[deprecated] '${oldName}' is deprecated; use '${newName}' instead. Alias will be removed in ${removeIn}.`) + "\n"
|
|
449914
|
+
);
|
|
449915
|
+
}
|
|
449916
|
+
function _resetDeprecationMemoryForTesting() {
|
|
449917
|
+
_seenDeprecations.clear();
|
|
449918
|
+
}
|
|
449370
449919
|
function outputError(error, details) {
|
|
449371
449920
|
if (isJsonMode()) {
|
|
449372
449921
|
console.error(JSON.stringify({ success: false, error, ...details }, null, 2));
|
|
@@ -449374,7 +449923,7 @@ function outputError(error, details) {
|
|
|
449374
449923
|
console.error(error);
|
|
449375
449924
|
}
|
|
449376
449925
|
}
|
|
449377
|
-
var ExitCode, PROVIDER_ERROR_HINTS, COST_ESTIMATES;
|
|
449926
|
+
var ExitCode, PROVIDER_ERROR_HINTS, COST_ESTIMATES, _seenDeprecations;
|
|
449378
449927
|
var init_output = __esm({
|
|
449379
449928
|
"../cli/src/commands/output.ts"() {
|
|
449380
449929
|
"use strict";
|
|
@@ -449409,7 +449958,7 @@ var init_output = __esm({
|
|
|
449409
449958
|
{ pattern: /context_length_exceeded|maximum.*context.*length|token.*limit.*exceeded|prompt.*too.*long/i, suggestion: "Input exceeds the model's context window. Shorten the prompt, or use a model with larger context (run 'vibe schema <command>' for options).", retryable: false },
|
|
449410
449959
|
{ pattern: /model.*not.*found|invalid.*model|unknown.*model|model_not_found/i, suggestion: "The specified model is unavailable. Check 'vibe schema <command>' for valid model options.", retryable: false },
|
|
449411
449960
|
// Provider-specific
|
|
449412
|
-
{ pattern: /voice.*not.*found|voice_not_found|invalid.*voice.?id/i, suggestion: "Voice ID not found. Run 'vibe audio voices' to list available voices, then pass --voice <id>.", retryable: false },
|
|
449961
|
+
{ pattern: /voice.*not.*found|voice_not_found|invalid.*voice.?id/i, suggestion: "Voice ID not found. Run 'vibe audio list-voices' to list available voices, then pass --voice <id>.", retryable: false },
|
|
449413
449962
|
{ pattern: /character.*(count|limit).*exceeded|invalid_character_count/i, suggestion: "Text exceeds the TTS provider's character limit. Shorten the text or split into chunks.", retryable: false },
|
|
449414
449963
|
{ pattern: /invalid.*aspect.*ratio|unsupported.*aspect.*ratio|unsupported.*resolution/i, suggestion: "This aspect ratio or resolution isn't supported by the chosen model. Check 'vibe schema <command>' for supported values.", retryable: false },
|
|
449415
449964
|
{ pattern: /invalid.*file.*format|unsupported.*(format|codec)|unsupported.*media.?type/i, suggestion: "Input file format not supported. Convert to MP4/MP3/PNG first with 'vibe export' or 'ffmpeg'.", retryable: false },
|
|
@@ -449460,57 +450009,7 @@ var init_output = __esm({
|
|
|
449460
450009
|
// pre-flight). Range covers a 1-beat preview to a 10-beat long-form.
|
|
449461
450010
|
"compose scenes with skills": { min: 0.05, max: 1.5, unit: "per pipeline" }
|
|
449462
450011
|
};
|
|
449463
|
-
|
|
449464
|
-
});
|
|
449465
|
-
|
|
449466
|
-
// ../cli/src/utils/audio.ts
|
|
449467
|
-
async function getAudioDuration(filePath) {
|
|
449468
|
-
try {
|
|
449469
|
-
return await ffprobeDuration(filePath);
|
|
449470
|
-
} catch (error) {
|
|
449471
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
449472
|
-
throw new Error(`Failed to get audio duration: ${message}`);
|
|
449473
|
-
}
|
|
449474
|
-
}
|
|
449475
|
-
async function getVideoDuration(filePath) {
|
|
449476
|
-
try {
|
|
449477
|
-
return await ffprobeDuration(filePath);
|
|
449478
|
-
} catch (error) {
|
|
449479
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
449480
|
-
throw new Error(`Failed to get video duration: ${message}`);
|
|
449481
|
-
}
|
|
449482
|
-
}
|
|
449483
|
-
async function extendVideoNaturally(videoPath, targetDuration, outputPath) {
|
|
449484
|
-
const videoDuration = await getVideoDuration(videoPath);
|
|
449485
|
-
const ratio = targetDuration / videoDuration;
|
|
449486
|
-
if (ratio <= 1) {
|
|
449487
|
-
const { copyFile: copyFile5 } = await import("node:fs/promises");
|
|
449488
|
-
await copyFile5(videoPath, outputPath);
|
|
449489
|
-
return;
|
|
449490
|
-
}
|
|
449491
|
-
if (ratio <= 1.15) {
|
|
449492
|
-
const slowFactor = (1 / ratio).toFixed(4);
|
|
449493
|
-
await execSafe("ffmpeg", ["-y", "-i", videoPath, "-filter:v", `setpts=${slowFactor}*PTS`, "-an", outputPath]);
|
|
449494
|
-
} else if (ratio <= 1.4) {
|
|
449495
|
-
const slowFactor = (1 / ratio).toFixed(4);
|
|
449496
|
-
await execSafe("ffmpeg", ["-y", "-i", videoPath, "-filter:v", `minterpolate=fps=60:mi_mode=mci,setpts=${slowFactor}*PTS`, "-an", outputPath]);
|
|
449497
|
-
} else {
|
|
449498
|
-
const slowRatio = 0.7;
|
|
449499
|
-
const slowedDuration = videoDuration / slowRatio;
|
|
449500
|
-
const freezeDuration = targetDuration - slowedDuration;
|
|
449501
|
-
if (freezeDuration <= 0) {
|
|
449502
|
-
const slowFactor = (1 / ratio).toFixed(4);
|
|
449503
|
-
await execSafe("ffmpeg", ["-y", "-i", videoPath, "-filter:v", `minterpolate=fps=60:mi_mode=mci,setpts=${slowFactor}*PTS`, "-an", outputPath]);
|
|
449504
|
-
} else {
|
|
449505
|
-
const slowFactor = (1 / slowRatio).toFixed(4);
|
|
449506
|
-
await execSafe("ffmpeg", ["-y", "-i", videoPath, "-filter:v", `setpts=${slowFactor}*PTS,tpad=stop_mode=clone:stop_duration=${freezeDuration.toFixed(2)}`, "-an", outputPath]);
|
|
449507
|
-
}
|
|
449508
|
-
}
|
|
449509
|
-
}
|
|
449510
|
-
var init_audio = __esm({
|
|
449511
|
-
"../cli/src/utils/audio.ts"() {
|
|
449512
|
-
"use strict";
|
|
449513
|
-
init_exec_safe();
|
|
450012
|
+
_seenDeprecations = /* @__PURE__ */ new Set();
|
|
449514
450013
|
}
|
|
449515
450014
|
});
|
|
449516
450015
|
|
|
@@ -459164,9 +459663,14 @@ var init_ai_script_pipeline = __esm({
|
|
|
459164
459663
|
import { resolve as resolve49 } from "node:path";
|
|
459165
459664
|
import { readFile as readFile25, writeFile as writeFile33 } from "node:fs/promises";
|
|
459166
459665
|
function registerVideoCommand(parent) {
|
|
459167
|
-
parent.command("video").alias("vid").description("Generate video using AI (Kling, Runway,
|
|
459666
|
+
parent.command("video").alias("vid").description("Generate video using AI (Seedance, Grok, Kling, Runway, or Veo)").argument("[prompt]", "Text prompt describing the video (interactive if omitted)").option("-p, --provider <provider>", "Provider: seedance (ByteDance Seedance 2.0 via fal.ai), grok, kling, runway, veo. `fal` is a backwards-compatible alias for seedance.").option("-k, --api-key <key>", "API key (or set FAL_KEY / XAI_API_KEY / RUNWAY_API_SECRET / KLING_API_KEY / GOOGLE_API_KEY env)").option("-o, --output <path>", "Output file path (downloads video)").option("-i, --image <path>", "Reference image for image-to-video").option(
|
|
459667
|
+
"-d, --duration <sec>",
|
|
459668
|
+
"Duration in seconds. Seedance accepts 4-15 (`fal` alias supported); Kling accepts 5 or 10; Veo maps to 6 or 8.",
|
|
459669
|
+
"5"
|
|
459670
|
+
).option("-r, --ratio <ratio>", "Aspect ratio: 16:9, 9:16, or 1:1 (auto-detected from image if omitted)").option("-s, --seed <number>", "Random seed for reproducibility (Runway only)").option("-m, --mode <mode>", "Generation mode: std or pro (Kling only)", "std").option("--seedance-model <model>", "Seedance variant: quality or fast (fal.ai only)", "quality").option("-n, --negative <prompt>", "Negative prompt - what to avoid (Kling/Veo)").option("--resolution <res>", "Video resolution: 720p, 1080p, 4k (Veo only)").option("--last-frame <path>", "Last frame image for frame interpolation (Veo only)").option("--ref-images <paths...>", "Reference images for character consistency (Veo 3.1 only, max 3)").option("--person <mode>", "Person generation: allow_all, allow_adult (Veo only)").option("--veo-model <model>", "Veo model: 3.0, 3.1, 3.1-fast (default: 3.1-fast)", "3.1-fast").option("--runway-model <model>", "Runway model: gen4.5 (default, text+image-to-video), gen4_turbo (image-to-video only)", "gen4.5").option("--no-wait", "Start generation and return task ID without waiting").option("--dry-run", "Preview parameters without executing").addHelpText("after", `
|
|
459168
459671
|
Examples:
|
|
459169
|
-
$ vibe generate video "dancing cat" -o cat.mp4 #
|
|
459672
|
+
$ vibe generate video "dancing cat" -o cat.mp4 # Seedance when FAL_KEY is set
|
|
459673
|
+
$ vibe gen vid "cinematic city timelapse" -o city.mp4 -p seedance # Seedance via fal.ai
|
|
459170
459674
|
$ vibe gen vid "city timelapse" -o city.mp4 -p kling # Kling
|
|
459171
459675
|
$ vibe gen vid "epic scene" -i frame.png -o out.mp4 -p runway # Image-to-video
|
|
459172
459676
|
$ vibe gen vid "ocean waves" -o waves.mp4 -p veo --resolution 1080p # Veo
|
|
@@ -459192,12 +459696,13 @@ Examples:
|
|
|
459192
459696
|
if (options.output) {
|
|
459193
459697
|
validateOutputPath(options.output);
|
|
459194
459698
|
}
|
|
459195
|
-
const validProviders = ["runway", "kling", "veo", "grok", "fal"];
|
|
459699
|
+
const validProviders = ["runway", "kling", "veo", "grok", "seedance", "fal"];
|
|
459196
459700
|
const videoEnvMap = {
|
|
459197
459701
|
grok: "XAI_API_KEY",
|
|
459198
459702
|
veo: "GOOGLE_API_KEY",
|
|
459199
459703
|
kling: "KLING_API_KEY",
|
|
459200
459704
|
runway: "RUNWAY_API_SECRET",
|
|
459705
|
+
seedance: "FAL_KEY",
|
|
459201
459706
|
fal: "FAL_KEY"
|
|
459202
459707
|
};
|
|
459203
459708
|
let provider;
|
|
@@ -459207,7 +459712,7 @@ Examples:
|
|
|
459207
459712
|
exitWithError(
|
|
459208
459713
|
usageError(
|
|
459209
459714
|
`Invalid provider: ${provider}`,
|
|
459210
|
-
|
|
459715
|
+
"Available providers: seedance, grok, kling, runway, veo. `fal` is a backwards-compatible alias for seedance."
|
|
459211
459716
|
)
|
|
459212
459717
|
);
|
|
459213
459718
|
}
|
|
@@ -459270,14 +459775,15 @@ Examples:
|
|
|
459270
459775
|
data: {
|
|
459271
459776
|
params: {
|
|
459272
459777
|
prompt: prompt3,
|
|
459273
|
-
provider,
|
|
459778
|
+
provider: provider === "fal" ? "seedance" : provider,
|
|
459274
459779
|
duration: options.duration,
|
|
459275
459780
|
ratio: options.ratio,
|
|
459276
459781
|
image: options.image,
|
|
459277
459782
|
mode: options.mode,
|
|
459278
459783
|
negative: options.negative,
|
|
459279
459784
|
resolution: options.resolution,
|
|
459280
|
-
veoModel: options.veoModel
|
|
459785
|
+
veoModel: options.veoModel,
|
|
459786
|
+
seedanceModel: options.seedanceModel
|
|
459281
459787
|
}
|
|
459282
459788
|
}
|
|
459283
459789
|
});
|
|
@@ -459288,6 +459794,7 @@ Examples:
|
|
|
459288
459794
|
kling: "KLING_API_KEY",
|
|
459289
459795
|
veo: "GOOGLE_API_KEY",
|
|
459290
459796
|
grok: "XAI_API_KEY",
|
|
459797
|
+
seedance: "FAL_KEY",
|
|
459291
459798
|
fal: "FAL_KEY"
|
|
459292
459799
|
};
|
|
459293
459800
|
const providerNameMap = {
|
|
@@ -459295,7 +459802,8 @@ Examples:
|
|
|
459295
459802
|
kling: "Kling",
|
|
459296
459803
|
veo: "Veo",
|
|
459297
459804
|
grok: "Grok",
|
|
459298
|
-
|
|
459805
|
+
seedance: "Seedance 2.0 via fal.ai",
|
|
459806
|
+
fal: "Seedance 2.0 via fal.ai"
|
|
459299
459807
|
};
|
|
459300
459808
|
const envKey = envKeyMap[provider];
|
|
459301
459809
|
const providerName = providerNameMap[provider];
|
|
@@ -459514,15 +460022,15 @@ Examples:
|
|
|
459514
460022
|
},
|
|
459515
460023
|
3e5
|
|
459516
460024
|
);
|
|
459517
|
-
} else if (provider === "fal") {
|
|
460025
|
+
} else if (provider === "fal" || provider === "seedance") {
|
|
459518
460026
|
const fal = new FalProvider();
|
|
459519
460027
|
await fal.initialize({ apiKey });
|
|
459520
460028
|
let falImage = referenceImage;
|
|
459521
460029
|
if (falImage && falImage.startsWith("data:")) {
|
|
459522
|
-
spinner2.text = "Uploading image to ImgBB for
|
|
460030
|
+
spinner2.text = "Uploading image to ImgBB for Seedance...";
|
|
459523
460031
|
const imgbbKey = await getApiKeyFromConfig("imgbb") || process.env.IMGBB_API_KEY;
|
|
459524
460032
|
if (!imgbbKey) {
|
|
459525
|
-
spinner2.fail("ImgBB API key required for
|
|
460033
|
+
spinner2.fail("ImgBB API key required for Seedance image-to-video");
|
|
459526
460034
|
exitWithError(authError("IMGBB_API_KEY", "ImgBB"));
|
|
459527
460035
|
}
|
|
459528
460036
|
const base64Data = falImage.split(",")[1];
|
|
@@ -459534,8 +460042,9 @@ Examples:
|
|
|
459534
460042
|
}
|
|
459535
460043
|
falImage = uploadResult.url;
|
|
459536
460044
|
}
|
|
459537
|
-
spinner2.text = "Generating video with Seedance 2.0 (this may take 1-3 minutes)...";
|
|
459538
|
-
const
|
|
460045
|
+
spinner2.text = "Generating video with fal.ai Seedance 2.0 (this may take 1-3 minutes)...";
|
|
460046
|
+
const seedanceModel = String(options.seedanceModel ?? "quality").toLowerCase();
|
|
460047
|
+
const falModel = seedanceModel === "fast" || seedanceModel === "seedance-2.0-fast" ? "seedance-2.0-fast" : "seedance-2.0";
|
|
459539
460048
|
result = await fal.generateVideo(prompt3, {
|
|
459540
460049
|
prompt: prompt3,
|
|
459541
460050
|
referenceImage: falImage,
|
|
@@ -459660,7 +460169,8 @@ var init_generate = __esm({
|
|
|
459660
460169
|
Examples:
|
|
459661
460170
|
$ vibe generate image "a sunset over the ocean" -o sunset.png
|
|
459662
460171
|
$ vibe generate image "logo design" -o logo.png -p openai
|
|
459663
|
-
$ vibe generate video "dancing cat" -o cat.mp4 #
|
|
460172
|
+
$ vibe generate video "dancing cat" -o cat.mp4 # Seedance when FAL_KEY is set
|
|
460173
|
+
$ vibe generate video "city timelapse" -o city.mp4 -p seedance # Seedance via fal.ai
|
|
459664
460174
|
$ vibe generate video "city timelapse" -o city.mp4 -p kling # Kling
|
|
459665
460175
|
$ vibe generate video "epic scene" -i frame.png -o out.mp4 -p runway # Image-to-video
|
|
459666
460176
|
$ vibe generate speech "Hello world" -o hello.mp3
|
|
@@ -459670,7 +460180,8 @@ Examples:
|
|
|
459670
460180
|
API Keys (per provider):
|
|
459671
460181
|
GOOGLE_API_KEY Image (default), Veo video
|
|
459672
460182
|
OPENAI_API_KEY Image (-p openai)
|
|
459673
|
-
|
|
460183
|
+
FAL_KEY Seedance video (-p seedance, default video)
|
|
460184
|
+
XAI_API_KEY Grok image/video
|
|
459674
460185
|
KLING_API_KEY Kling video (-p kling)
|
|
459675
460186
|
RUNWAY_API_SECRET Runway video (-p runway)
|
|
459676
460187
|
ELEVENLABS_API_KEY Speech, sound effects, music
|
|
@@ -459718,12 +460229,20 @@ async function executeVideoGenerate(options) {
|
|
|
459718
460229
|
negative,
|
|
459719
460230
|
resolution,
|
|
459720
460231
|
veoModel = "3.1-fast",
|
|
460232
|
+
seedanceModel = "quality",
|
|
459721
460233
|
output: output3,
|
|
459722
460234
|
wait = true,
|
|
459723
460235
|
apiKey
|
|
459724
460236
|
} = options;
|
|
459725
460237
|
try {
|
|
459726
|
-
const envKeyMap = {
|
|
460238
|
+
const envKeyMap = {
|
|
460239
|
+
grok: "XAI_API_KEY",
|
|
460240
|
+
runway: "RUNWAY_API_SECRET",
|
|
460241
|
+
kling: "KLING_API_KEY",
|
|
460242
|
+
veo: "GOOGLE_API_KEY",
|
|
460243
|
+
seedance: "FAL_KEY",
|
|
460244
|
+
fal: "FAL_KEY"
|
|
460245
|
+
};
|
|
459727
460246
|
const key2 = apiKey || process.env[envKeyMap[provider] || ""];
|
|
459728
460247
|
if (!key2) return { success: false, error: `${envKeyMap[provider]} required for ${provider}` };
|
|
459729
460248
|
let referenceImage;
|
|
@@ -459735,7 +460254,36 @@ async function executeVideoGenerate(options) {
|
|
|
459735
460254
|
const mimeType = mimeTypes[ext || "png"] || "image/png";
|
|
459736
460255
|
referenceImage = `data:${mimeType};base64,${imageBuffer.toString("base64")}`;
|
|
459737
460256
|
}
|
|
459738
|
-
if (provider === "
|
|
460257
|
+
if (provider === "seedance" || provider === "fal") {
|
|
460258
|
+
const fal = new FalProvider();
|
|
460259
|
+
await fal.initialize({ apiKey: key2 });
|
|
460260
|
+
let falImage = referenceImage;
|
|
460261
|
+
if (falImage && falImage.startsWith("data:")) {
|
|
460262
|
+
const imgbbKey = process.env.IMGBB_API_KEY;
|
|
460263
|
+
if (!imgbbKey) return { success: false, error: "IMGBB_API_KEY required for Seedance image-to-video" };
|
|
460264
|
+
const base64Data = falImage.split(",")[1];
|
|
460265
|
+
const uploadResult = await uploadToImgbb(Buffer.from(base64Data, "base64"), imgbbKey);
|
|
460266
|
+
if (!uploadResult.success || !uploadResult.url) return { success: false, error: `ImgBB upload failed: ${uploadResult.error}` };
|
|
460267
|
+
falImage = uploadResult.url;
|
|
460268
|
+
}
|
|
460269
|
+
const model = seedanceModel === "fast" || seedanceModel === "seedance-2.0-fast" ? "seedance-2.0-fast" : "seedance-2.0";
|
|
460270
|
+
const result = await fal.generateVideo(prompt3, {
|
|
460271
|
+
prompt: prompt3,
|
|
460272
|
+
referenceImage: falImage,
|
|
460273
|
+
duration,
|
|
460274
|
+
aspectRatio: ratio,
|
|
460275
|
+
negativePrompt: negative,
|
|
460276
|
+
model
|
|
460277
|
+
});
|
|
460278
|
+
if (result.status === "failed") return { success: false, error: result.error || "Seedance generation failed" };
|
|
460279
|
+
let outputPath;
|
|
460280
|
+
if (output3 && result.videoUrl) {
|
|
460281
|
+
const buffer = await downloadVideo(result.videoUrl, key2);
|
|
460282
|
+
outputPath = resolve50(process.cwd(), output3);
|
|
460283
|
+
await writeFile34(outputPath, buffer);
|
|
460284
|
+
}
|
|
460285
|
+
return { success: true, taskId: result.id, status: "completed", videoUrl: result.videoUrl, outputPath, provider: "seedance" };
|
|
460286
|
+
} else if (provider === "runway") {
|
|
459739
460287
|
const runway = new RunwayProvider();
|
|
459740
460288
|
await runway.initialize({ apiKey: key2 });
|
|
459741
460289
|
const result = await runway.generateVideo(prompt3, {
|
|
@@ -460950,334 +461498,8 @@ function visualStyleNames() {
|
|
|
460950
461498
|
return STYLES.map((s) => `"${s.name}"`).join(", ");
|
|
460951
461499
|
}
|
|
460952
461500
|
|
|
460953
|
-
// ../cli/src/
|
|
460954
|
-
|
|
460955
|
-
import { mkdir, readFile, writeFile, access } from "node:fs/promises";
|
|
460956
|
-
import { resolve, basename } from "node:path";
|
|
460957
|
-
var ASPECT_DIMS = {
|
|
460958
|
-
"16:9": { width: 1920, height: 1080 },
|
|
460959
|
-
"9:16": { width: 1080, height: 1920 },
|
|
460960
|
-
"1:1": { width: 1080, height: 1080 },
|
|
460961
|
-
"4:5": { width: 1080, height: 1350 }
|
|
460962
|
-
};
|
|
460963
|
-
function aspectToDims(aspect) {
|
|
460964
|
-
return ASPECT_DIMS[aspect];
|
|
460965
|
-
}
|
|
460966
|
-
function defaultVibeProjectConfig(name) {
|
|
460967
|
-
return {
|
|
460968
|
-
name,
|
|
460969
|
-
aspect: "16:9",
|
|
460970
|
-
defaultSceneDuration: 5,
|
|
460971
|
-
providers: { image: null, tts: null, transcribe: null },
|
|
460972
|
-
budget: { maxUsd: 0 }
|
|
460973
|
-
};
|
|
460974
|
-
}
|
|
460975
|
-
function buildHyperframesConfig() {
|
|
460976
|
-
return {
|
|
460977
|
-
$schema: "https://hyperframes.heygen.com/schema/hyperframes.json",
|
|
460978
|
-
registry: "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry",
|
|
460979
|
-
paths: {
|
|
460980
|
-
blocks: "compositions",
|
|
460981
|
-
components: "compositions/components",
|
|
460982
|
-
assets: "assets"
|
|
460983
|
-
}
|
|
460984
|
-
};
|
|
460985
|
-
}
|
|
460986
|
-
function buildHyperframesMeta(name, now = /* @__PURE__ */ new Date()) {
|
|
460987
|
-
return { id: name, name, createdAt: now.toISOString() };
|
|
460988
|
-
}
|
|
460989
|
-
function mergeHyperframesConfig(existing, defaults) {
|
|
460990
|
-
const out = { ...defaults, ...existing };
|
|
460991
|
-
if (existing.paths || defaults.paths) {
|
|
460992
|
-
out.paths = { ...defaults.paths ?? {}, ...existing.paths ?? {} };
|
|
460993
|
-
}
|
|
460994
|
-
return out;
|
|
460995
|
-
}
|
|
460996
|
-
function buildEmptyRootHtml(opts) {
|
|
460997
|
-
const { width, height } = ASPECT_DIMS[opts.aspect];
|
|
460998
|
-
return `<!doctype html>
|
|
460999
|
-
<html lang="en">
|
|
461000
|
-
<head>
|
|
461001
|
-
<meta charset="UTF-8" />
|
|
461002
|
-
<meta name="viewport" content="width=${width}, height=${height}" />
|
|
461003
|
-
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
|
461004
|
-
<style>
|
|
461005
|
-
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
461006
|
-
html, body {
|
|
461007
|
-
margin: 0;
|
|
461008
|
-
width: ${width}px;
|
|
461009
|
-
height: ${height}px;
|
|
461010
|
-
overflow: hidden;
|
|
461011
|
-
background: #000;
|
|
461012
|
-
}
|
|
461013
|
-
</style>
|
|
461014
|
-
</head>
|
|
461015
|
-
<body>
|
|
461016
|
-
<div
|
|
461017
|
-
id="root"
|
|
461018
|
-
data-composition-id="main"
|
|
461019
|
-
data-start="0"
|
|
461020
|
-
data-duration="${opts.duration}"
|
|
461021
|
-
data-width="${width}"
|
|
461022
|
-
data-height="${height}"
|
|
461023
|
-
>
|
|
461024
|
-
<!-- Scenes added via \`vibe scene add\` are inserted here. -->
|
|
461025
|
-
<!-- Each scene reference: data-composition-id, data-composition-src, data-start, data-duration, data-track-index. -->
|
|
461026
|
-
<!-- See compositions/*.html for sub-composition contents. -->
|
|
461027
|
-
|
|
461028
|
-
</div>
|
|
461029
|
-
|
|
461030
|
-
<script>
|
|
461031
|
-
window.__timelines = window.__timelines || {};
|
|
461032
|
-
window.__timelines["main"] = gsap.timeline({ paused: true });
|
|
461033
|
-
</script>
|
|
461034
|
-
</body>
|
|
461035
|
-
</html>
|
|
461036
|
-
`;
|
|
461037
|
-
}
|
|
461038
|
-
function buildDesignMd(opts) {
|
|
461039
|
-
const { name, style } = opts;
|
|
461040
|
-
const intro = style ? `Visual identity for **${name}**, scaffolded from the **${style.name}** style (after ${style.designer}). Customise freely \u2014 this file is the single source of truth for every scene's palette, typography, and motion.` : `Visual identity for **${name}**. Fill the sections below before authoring any scene HTML or generating any backdrop. Pick a named style with \`vibe scene styles\` if you want a credible starting point.`;
|
|
461041
|
-
const moodLine = style ? `**Mood:** ${style.mood} \xB7 **Best for:** ${style.bestFor}` : `**Mood:** _(one line \u2014 what should the viewer FEEL?)_`;
|
|
461042
|
-
const palette = style ? `${style.palette.map((c) => `- \`${c}\``).join("\n")}
|
|
461043
|
-
|
|
461044
|
-
${style.paletteNotes}` : `- _hex_ \u2014 primary
|
|
461045
|
-
- _hex_ \u2014 accent
|
|
461046
|
-
|
|
461047
|
-
_2\u20133 colours max. Declare explicit hex values; never name colours abstractly._`;
|
|
461048
|
-
const typography = style ? style.typography : `_One family, two weights. State the role of each (headline / label / body)._`;
|
|
461049
|
-
const composition = style ? style.composition : `_Grid? Centered? Layered? How does negative space behave?_`;
|
|
461050
|
-
const motion = style ? `${style.motion}
|
|
461051
|
-
|
|
461052
|
-
**GSAP signature:** ${style.gsapSignature}` : `_How fast? Snappy or fluid? Overshoot or precision?_
|
|
461053
|
-
|
|
461054
|
-
**GSAP signature:** _e.g. \`expo.out\`, \`sine.inOut\`, \`back.out(1.8)\`_`;
|
|
461055
|
-
const transition = style ? style.transition : `_Which Hyperframes shader matches the energy? (Cinematic Zoom, Cross-Warp Morph, Glitch, Domain Warp, \u2026)_`;
|
|
461056
|
-
const avoid = style ? style.avoid.map((a) => `- ${a}`).join("\n") : `- _anti-pattern 1_
|
|
461057
|
-
- _anti-pattern 2_
|
|
461058
|
-
- _anti-pattern 3_`;
|
|
461059
|
-
return `# ${name} \u2014 Design
|
|
461060
|
-
|
|
461061
|
-
> **Hard-gate.** This file defines the visual identity of every scene.
|
|
461062
|
-
> Author it before generating any HTML, backdrop image, or motion.
|
|
461063
|
-
> The Hyperframes \`hyperframes\` skill enforces this: scenes that
|
|
461064
|
-
> contradict DESIGN.md are rejected.
|
|
461065
|
-
|
|
461066
|
-
${intro}
|
|
461067
|
-
|
|
461068
|
-
## Style
|
|
461069
|
-
|
|
461070
|
-
${moodLine}
|
|
461071
|
-
|
|
461072
|
-
## Palette
|
|
461073
|
-
|
|
461074
|
-
${palette}
|
|
461075
|
-
|
|
461076
|
-
## Typography
|
|
461077
|
-
|
|
461078
|
-
${typography}
|
|
461079
|
-
|
|
461080
|
-
## Composition
|
|
461081
|
-
|
|
461082
|
-
${composition}
|
|
461083
|
-
|
|
461084
|
-
## Motion
|
|
461085
|
-
|
|
461086
|
-
${motion}
|
|
461087
|
-
|
|
461088
|
-
## Transition
|
|
461089
|
-
|
|
461090
|
-
${transition}
|
|
461091
|
-
|
|
461092
|
-
## What NOT to do
|
|
461093
|
-
|
|
461094
|
-
${avoid}
|
|
461095
|
-
|
|
461096
|
-
---
|
|
461097
|
-
|
|
461098
|
-
_Browse other named styles: \`vibe scene styles\`_
|
|
461099
|
-
${style ? `_This file was seeded by \`vibe scene init --visual-style "${style.name}"\`._` : `_Seed this file from a named style: \`vibe scene init <dir> --visual-style "<name>"\`._`}
|
|
461100
|
-
`;
|
|
461101
|
-
}
|
|
461102
|
-
function buildProjectClaudeMd(name) {
|
|
461103
|
-
return `# ${name} \u2014 Scene Authoring Project
|
|
461104
|
-
|
|
461105
|
-
This project is **bilingual**: it works with both VibeFrame (\`vibe\`) and
|
|
461106
|
-
HeyGen Hyperframes (\`hyperframes\`). You can run either CLI inside this
|
|
461107
|
-
directory.
|
|
461108
|
-
|
|
461109
|
-
## Visual identity hard-gate
|
|
461110
|
-
|
|
461111
|
-
**Author \`DESIGN.md\` before any scene HTML.** It defines palette,
|
|
461112
|
-
typography, motion, and transition rules. Both the agent-driven path and
|
|
461113
|
-
the fallback emit reference it; scenes that contradict DESIGN.md are
|
|
461114
|
-
rejected by the Hyperframes \`hyperframes\` skill.
|
|
461115
|
-
|
|
461116
|
-
Browse named styles: \`vibe scene styles\`. Re-seed from one with
|
|
461117
|
-
\`vibe scene init . --visual-style "Swiss Pulse"\` (idempotent).
|
|
461118
|
-
|
|
461119
|
-
## Skills \u2014 USE THESE FIRST
|
|
461120
|
-
|
|
461121
|
-
**Always invoke the relevant skill before authoring scenes.** Skills encode
|
|
461122
|
-
framework-specific patterns (GSAP timeline registration, data-attribute
|
|
461123
|
-
semantics, VibeFrame pipeline conventions) that are NOT in generic web docs.
|
|
461124
|
-
|
|
461125
|
-
| Skill | Command | When to use |
|
|
461126
|
-
| ----------------- | ---------------- | ------------------------------------------------------------------------------------- |
|
|
461127
|
-
| **hyperframes** | \`/hyperframes\` | Cinematic-quality composition \u2014 DESIGN.md hard-gate, named styles, motion principles |
|
|
461128
|
-
| **vibe-scene** | \`/vibe-scene\` | VibeFrame's authoring loop, AI assets, lint feedback, pipeline integration |
|
|
461129
|
-
| **gsap** | \`/gsap\` | GSAP tweens, timelines, easing |
|
|
461130
|
-
|
|
461131
|
-
Install the Hyperframes skills once per machine:
|
|
461132
|
-
|
|
461133
|
-
\`\`\`bash
|
|
461134
|
-
npx skills add heygen-com/hyperframes
|
|
461135
|
-
\`\`\`
|
|
461136
|
-
|
|
461137
|
-
Restart your agent session (or reload the skill list) after installing.
|
|
461138
|
-
If skills aren't available, follow the **Key Rules** below \u2014 they cover
|
|
461139
|
-
the framework-level minimum, not the cinematic craft layer.
|
|
461140
|
-
|
|
461141
|
-
## Project structure
|
|
461142
|
-
|
|
461143
|
-
- \`DESIGN.md\` \u2014 visual identity contract (palette, type, motion, transitions)
|
|
461144
|
-
- \`index.html\` \u2014 root composition (timeline)
|
|
461145
|
-
- \`compositions/scene-*.html\` \u2014 per-scene HTML authored by you or the agent
|
|
461146
|
-
- \`assets/\` \u2014 shared media (narration audio, images, video)
|
|
461147
|
-
- \`transcript.json\` \u2014 Whisper word-level transcript (if narration exists)
|
|
461148
|
-
- \`hyperframes.json\` \u2014 HF registry config (speak to both toolchains)
|
|
461149
|
-
- \`vibe.project.yaml\` \u2014 VibeFrame config (providers, budget)
|
|
461150
|
-
- \`renders/\` \u2014 output MP4s
|
|
461151
|
-
|
|
461152
|
-
## Commands
|
|
461153
|
-
|
|
461154
|
-
\`\`\`bash
|
|
461155
|
-
vibe scene add <name> --narration "..." --visuals "..." # Author a new scene via AI
|
|
461156
|
-
vibe scene lint # Validate scenes (in-process HF linter)
|
|
461157
|
-
vibe scene render # Render to MP4
|
|
461158
|
-
|
|
461159
|
-
# Hyperframes CLI (if installed \u2014 works in this project too)
|
|
461160
|
-
npx hyperframes preview
|
|
461161
|
-
npx hyperframes render
|
|
461162
|
-
\`\`\`
|
|
461163
|
-
|
|
461164
|
-
## Key Rules (for hand-authored scene HTML)
|
|
461165
|
-
|
|
461166
|
-
1. Every timed element needs \`data-start\`, \`data-duration\`, and \`data-track-index\`.
|
|
461167
|
-
2. Elements with timing **MUST** have \`class="clip"\` \u2014 the framework uses this for visibility control.
|
|
461168
|
-
3. Timelines must be paused and registered on \`window.__timelines\`:
|
|
461169
|
-
\`\`\`js
|
|
461170
|
-
window.__timelines = window.__timelines || {};
|
|
461171
|
-
window.__timelines["composition-id"] = gsap.timeline({ paused: true });
|
|
461172
|
-
\`\`\`
|
|
461173
|
-
4. Videos use \`muted\` with a separate \`<audio>\` element for the audio track.
|
|
461174
|
-
5. Sub-compositions use \`data-composition-src="compositions/file.html"\`.
|
|
461175
|
-
6. Only deterministic logic \u2014 no \`Date.now()\`, \`Math.random()\`, or network fetches.
|
|
461176
|
-
|
|
461177
|
-
## Linting \u2014 run after changes
|
|
461178
|
-
|
|
461179
|
-
\`\`\`bash
|
|
461180
|
-
vibe scene lint # preferred \u2014 in-process, no network
|
|
461181
|
-
vibe scene lint --fix # auto-fix mechanical issues
|
|
461182
|
-
vibe scene lint --json # structured output for agent loops
|
|
461183
|
-
\`\`\`
|
|
461184
|
-
`;
|
|
461185
|
-
}
|
|
461186
|
-
function buildSceneGitignore() {
|
|
461187
|
-
return `# VibeFrame caches
|
|
461188
|
-
.vibeframe/cache/
|
|
461189
|
-
.vibeframe/checkpoints/
|
|
461190
|
-
|
|
461191
|
-
# Render outputs
|
|
461192
|
-
renders/*.mp4
|
|
461193
|
-
tmp/
|
|
461194
|
-
|
|
461195
|
-
# OS / editor
|
|
461196
|
-
.DS_Store
|
|
461197
|
-
*.log
|
|
461198
|
-
`;
|
|
461199
|
-
}
|
|
461200
|
-
async function pathExists(p) {
|
|
461201
|
-
try {
|
|
461202
|
-
await access(p);
|
|
461203
|
-
return true;
|
|
461204
|
-
} catch {
|
|
461205
|
-
return false;
|
|
461206
|
-
}
|
|
461207
|
-
}
|
|
461208
|
-
async function scaffoldSceneProject(opts) {
|
|
461209
|
-
const dir = resolve(opts.dir);
|
|
461210
|
-
const name = opts.name ?? basename(dir);
|
|
461211
|
-
const aspect = opts.aspect ?? "16:9";
|
|
461212
|
-
const duration = opts.duration ?? 10;
|
|
461213
|
-
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
461214
|
-
await mkdir(dir, { recursive: true });
|
|
461215
|
-
await mkdir(resolve(dir, "compositions"), { recursive: true });
|
|
461216
|
-
await mkdir(resolve(dir, "assets"), { recursive: true });
|
|
461217
|
-
const created = [];
|
|
461218
|
-
const skipped2 = [];
|
|
461219
|
-
const merged = [];
|
|
461220
|
-
const hfPath = resolve(dir, "hyperframes.json");
|
|
461221
|
-
const hfDefaults = buildHyperframesConfig();
|
|
461222
|
-
if (await pathExists(hfPath)) {
|
|
461223
|
-
const existingRaw = await readFile(hfPath, "utf-8");
|
|
461224
|
-
const existing = JSON.parse(existingRaw);
|
|
461225
|
-
const mergedConfig = mergeHyperframesConfig(existing, hfDefaults);
|
|
461226
|
-
await writeFile(hfPath, JSON.stringify(mergedConfig, null, 2) + "\n", "utf-8");
|
|
461227
|
-
merged.push(hfPath);
|
|
461228
|
-
} else {
|
|
461229
|
-
await writeFile(hfPath, JSON.stringify(hfDefaults, null, 2) + "\n", "utf-8");
|
|
461230
|
-
created.push(hfPath);
|
|
461231
|
-
}
|
|
461232
|
-
const metaPath = resolve(dir, "meta.json");
|
|
461233
|
-
if (await pathExists(metaPath)) {
|
|
461234
|
-
skipped2.push(metaPath);
|
|
461235
|
-
} else {
|
|
461236
|
-
await writeFile(metaPath, JSON.stringify(buildHyperframesMeta(name, now), null, 2) + "\n", "utf-8");
|
|
461237
|
-
created.push(metaPath);
|
|
461238
|
-
}
|
|
461239
|
-
const rootPath = resolve(dir, "index.html");
|
|
461240
|
-
if (await pathExists(rootPath)) {
|
|
461241
|
-
skipped2.push(rootPath);
|
|
461242
|
-
} else {
|
|
461243
|
-
await writeFile(rootPath, buildEmptyRootHtml({ aspect, duration }), "utf-8");
|
|
461244
|
-
created.push(rootPath);
|
|
461245
|
-
}
|
|
461246
|
-
const vibePath = resolve(dir, "vibe.project.yaml");
|
|
461247
|
-
if (await pathExists(vibePath)) {
|
|
461248
|
-
skipped2.push(vibePath);
|
|
461249
|
-
} else {
|
|
461250
|
-
const cfg = { ...defaultVibeProjectConfig(name), aspect };
|
|
461251
|
-
await writeFile(vibePath, (0, import_yaml.stringify)(cfg), "utf-8");
|
|
461252
|
-
created.push(vibePath);
|
|
461253
|
-
}
|
|
461254
|
-
const claudePath = resolve(dir, "CLAUDE.md");
|
|
461255
|
-
if (await pathExists(claudePath)) {
|
|
461256
|
-
skipped2.push(claudePath);
|
|
461257
|
-
} else {
|
|
461258
|
-
await writeFile(claudePath, buildProjectClaudeMd(name), "utf-8");
|
|
461259
|
-
created.push(claudePath);
|
|
461260
|
-
}
|
|
461261
|
-
const designPath = resolve(dir, "DESIGN.md");
|
|
461262
|
-
if (await pathExists(designPath)) {
|
|
461263
|
-
skipped2.push(designPath);
|
|
461264
|
-
} else {
|
|
461265
|
-
await writeFile(
|
|
461266
|
-
designPath,
|
|
461267
|
-
buildDesignMd({ name, style: opts.visualStyle }),
|
|
461268
|
-
"utf-8"
|
|
461269
|
-
);
|
|
461270
|
-
created.push(designPath);
|
|
461271
|
-
}
|
|
461272
|
-
const gitignorePath = resolve(dir, ".gitignore");
|
|
461273
|
-
if (await pathExists(gitignorePath)) {
|
|
461274
|
-
skipped2.push(gitignorePath);
|
|
461275
|
-
} else {
|
|
461276
|
-
await writeFile(gitignorePath, buildSceneGitignore(), "utf-8");
|
|
461277
|
-
created.push(gitignorePath);
|
|
461278
|
-
}
|
|
461279
|
-
return { created, skipped: skipped2, merged };
|
|
461280
|
-
}
|
|
461501
|
+
// ../cli/src/tools/manifest/scene.ts
|
|
461502
|
+
init_scene_project();
|
|
461281
461503
|
|
|
461282
461504
|
// ../cli/src/commands/scene.ts
|
|
461283
461505
|
init_esm();
|
|
@@ -461286,6 +461508,7 @@ init_ora();
|
|
|
461286
461508
|
var import_yaml5 = __toESM(require_dist(), 1);
|
|
461287
461509
|
init_dist();
|
|
461288
461510
|
init_tts_resolve();
|
|
461511
|
+
init_scene_project();
|
|
461289
461512
|
import { basename as basename6, resolve as resolve21, relative as relative7, dirname as dirname17 } from "node:path";
|
|
461290
461513
|
import { mkdir as mkdir10, readFile as readFile10, writeFile as writeFile10, access as access4, copyFile as copyFile2 } from "node:fs/promises";
|
|
461291
461514
|
import { existsSync as existsSync28 } from "node:fs";
|
|
@@ -461777,6 +462000,7 @@ function deriveInstallHosts(detected) {
|
|
|
461777
462000
|
// ../cli/src/commands/scene.ts
|
|
461778
462001
|
init_compose_prompts();
|
|
461779
462002
|
var VALID_ASPECTS2 = ["16:9", "9:16", "1:1", "4:5"];
|
|
462003
|
+
var VALID_SCENE_INIT_PROFILES = ["minimal", "agent", "full"];
|
|
461780
462004
|
function validateAspect(value) {
|
|
461781
462005
|
if (!VALID_ASPECTS2.includes(value)) {
|
|
461782
462006
|
exitWithError(usageError(`Invalid aspect ratio: ${value}`, `Valid: ${VALID_ASPECTS2.join(", ")}`));
|
|
@@ -461808,30 +462032,42 @@ function validateVisualStyle(value) {
|
|
|
461808
462032
|
}
|
|
461809
462033
|
return found;
|
|
461810
462034
|
}
|
|
461811
|
-
|
|
462035
|
+
function formatSceneInitProfile(profile) {
|
|
462036
|
+
if (profile === "minimal") return "authoring files only; build will add render scaffold when needed";
|
|
462037
|
+
if (profile === "agent") return "authoring files plus local composition rules for host agents";
|
|
462038
|
+
return "complete authoring, agent, and render scaffold";
|
|
462039
|
+
}
|
|
462040
|
+
var sceneCommand = new Command("scene").description("Lower-level scene authoring (add, lint, styles). For project flow use `vibe init` / `vibe build` / `vibe render`.").addHelpText("after", `
|
|
461812
462041
|
Examples:
|
|
461813
|
-
$ vibe scene init my-video # Scaffold a new project
|
|
461814
|
-
$ vibe scene init my-video -r 9:16 -d 30 # Vertical 30s project
|
|
461815
462042
|
$ vibe scene add intro --style announcement \\
|
|
461816
462043
|
--headline "Welcome to VibeFrame" # Headline-only scene
|
|
461817
462044
|
$ vibe scene add overview --narration "VibeFrame turns scripts into video." \\
|
|
461818
462045
|
--visuals "studio desk, soft lighting" # AI narration + image
|
|
461819
|
-
$ vibe scene lint # Validate every scene against
|
|
462046
|
+
$ vibe scene lint # Validate every scene against composition rules
|
|
461820
462047
|
$ vibe scene lint --fix # Auto-fix mechanical issues (e.g. missing class="clip")
|
|
461821
462048
|
$ vibe scene lint --json # Structured output for agent loops
|
|
461822
|
-
$ vibe scene
|
|
461823
|
-
$ vibe scene render -o demo.mp4 --quality high # Custom output path + quality
|
|
461824
|
-
$ vibe scene render --fps 60 --format webm # 60fps WebM render
|
|
462049
|
+
$ vibe scene styles # Browse seed visual styles for DESIGN.md
|
|
461825
462050
|
|
|
461826
|
-
|
|
462051
|
+
For the project flow (init / build / render), use the top-level commands.
|
|
462052
|
+
The \`scene init\`, \`scene build\`, and \`scene render\` legacy aliases
|
|
462053
|
+
are still callable but hidden from this help \u2014 they will be removed in v1.0.
|
|
461827
462054
|
Run 'vibe schema scene.<command>' for structured parameter info.`);
|
|
461828
|
-
sceneCommand.command("init").description("Scaffold a new scene project (or safely augment an existing
|
|
462055
|
+
sceneCommand.command("init", { hidden: true }).description("Scaffold a new scene project (or safely augment an existing project) [legacy \u2014 prefer `vibe init`]").argument("<dir>", "Project directory (created if it doesn't exist)").option("-n, --name <name>", "Project name (defaults to directory basename)").option("-r, --ratio <ratio>", "Aspect ratio: 16:9, 9:16, 1:1, 4:5", "16:9").option("-d, --duration <sec>", "Default root composition duration (seconds)", "10").option("--visual-style <name>", `Seed DESIGN.md from a named style (browse via \`vibe scene styles\`). E.g. "Swiss Pulse"`).option("--profile <profile>", "Scene profile: minimal (storyboard/design only), agent (recommended), full (render scaffold upfront)", "agent").option("--dry-run", "Preview parameters without writing files").action(async (dir, options) => {
|
|
461829
462056
|
const startedAt = Date.now();
|
|
461830
462057
|
const aspect = validateAspect(options.ratio);
|
|
461831
462058
|
const duration = validateDuration(options.duration);
|
|
461832
462059
|
const name = options.name ?? basename6(dir.replace(/\/+$/, ""));
|
|
461833
462060
|
const visualStyle = options.visualStyle ? validateVisualStyle(options.visualStyle) : void 0;
|
|
462061
|
+
const profile = String(options.profile ?? "agent");
|
|
462062
|
+
if (!isSceneScaffoldProfile(profile)) {
|
|
462063
|
+
exitWithError(usageError(`Invalid --profile: ${profile}`, `Must be one of: ${VALID_SCENE_INIT_PROFILES.join(", ")}`));
|
|
462064
|
+
}
|
|
462065
|
+
const groups = describeSceneScaffold({ dir, profile });
|
|
461834
462066
|
if (options.dryRun) {
|
|
462067
|
+
if (!isJsonMode() && !isQuietMode()) {
|
|
462068
|
+
printSceneInitDryRun({ dir, name, aspect, duration, visualStyleName: visualStyle?.name ?? null, profile, groups });
|
|
462069
|
+
return;
|
|
462070
|
+
}
|
|
461835
462071
|
outputSuccess({
|
|
461836
462072
|
command: "scene init",
|
|
461837
462073
|
startedAt,
|
|
@@ -461842,23 +462078,25 @@ sceneCommand.command("init").description("Scaffold a new scene project (or safel
|
|
|
461842
462078
|
name,
|
|
461843
462079
|
aspect,
|
|
461844
462080
|
duration,
|
|
461845
|
-
visualStyle: visualStyle?.name ?? null
|
|
461846
|
-
|
|
462081
|
+
visualStyle: visualStyle?.name ?? null,
|
|
462082
|
+
profile
|
|
462083
|
+
},
|
|
462084
|
+
groups
|
|
461847
462085
|
}
|
|
461848
462086
|
});
|
|
461849
462087
|
return;
|
|
461850
462088
|
}
|
|
461851
|
-
const spinner2 = isJsonMode() ? null : ora(`Scaffolding scene project at ${dir}...`).start();
|
|
462089
|
+
const spinner2 = isJsonMode() || isQuietMode() ? null : ora(`Scaffolding scene project at ${dir}...`).start();
|
|
461852
462090
|
try {
|
|
461853
|
-
const result = await scaffoldSceneProject({ dir, name, aspect, duration, visualStyle });
|
|
462091
|
+
const result = await scaffoldSceneProject({ dir, name, aspect, duration, visualStyle, profile });
|
|
461854
462092
|
const detectedIds = detectedAgentHosts().map((h) => h.id);
|
|
461855
462093
|
const skillHosts = deriveInstallHosts(detectedIds);
|
|
461856
462094
|
const projectAbs = resolve21(dir);
|
|
461857
|
-
const skillResult = await installHyperframesSkill({
|
|
462095
|
+
const skillResult = profile === "agent" || profile === "full" ? await installHyperframesSkill({
|
|
461858
462096
|
projectDir: projectAbs,
|
|
461859
462097
|
hosts: skillHosts
|
|
461860
|
-
});
|
|
461861
|
-
if (isJsonMode()) {
|
|
462098
|
+
}) : { success: true, files: [], bundleVersion: "not-installed" };
|
|
462099
|
+
if (isJsonMode() || isQuietMode()) {
|
|
461862
462100
|
outputSuccess({
|
|
461863
462101
|
command: "scene init",
|
|
461864
462102
|
startedAt,
|
|
@@ -461868,16 +462106,27 @@ sceneCommand.command("init").description("Scaffold a new scene project (or safel
|
|
|
461868
462106
|
aspect,
|
|
461869
462107
|
duration,
|
|
461870
462108
|
visualStyle: visualStyle?.name ?? null,
|
|
462109
|
+
profile,
|
|
461871
462110
|
created: result.created,
|
|
461872
462111
|
merged: result.merged,
|
|
461873
462112
|
skipped: result.skipped,
|
|
462113
|
+
groups: result.groups,
|
|
461874
462114
|
skillFiles: skillResult.files,
|
|
461875
462115
|
skillBundleVersion: skillResult.bundleVersion
|
|
461876
462116
|
}
|
|
461877
462117
|
});
|
|
461878
462118
|
return;
|
|
461879
462119
|
}
|
|
461880
|
-
spinner2?.succeed(source_default.green(`
|
|
462120
|
+
spinner2?.succeed(source_default.green(`Video project ready: ${dir}`));
|
|
462121
|
+
console.log();
|
|
462122
|
+
console.log(source_default.bold.cyan("Edit first"));
|
|
462123
|
+
console.log(source_default.dim("\u2500".repeat(60)));
|
|
462124
|
+
console.log(` ${source_default.bold("STORYBOARD.md")} ${source_default.dim("# beats: narration, backdrop, minimum duration")}`);
|
|
462125
|
+
console.log(` ${source_default.bold("DESIGN.md")} ${source_default.dim("# palette, typography, motion rules")}`);
|
|
462126
|
+
console.log();
|
|
462127
|
+
console.log(source_default.bold.cyan("Profile"));
|
|
462128
|
+
console.log(source_default.dim("\u2500".repeat(60)));
|
|
462129
|
+
console.log(` ${source_default.bold(profile)} ${source_default.dim(formatSceneInitProfile(profile))}`);
|
|
461881
462130
|
console.log();
|
|
461882
462131
|
console.log(source_default.bold.cyan("Files"));
|
|
461883
462132
|
console.log(source_default.dim("\u2500".repeat(60)));
|
|
@@ -461888,7 +462137,7 @@ sceneCommand.command("init").description("Scaffold a new scene project (or safel
|
|
|
461888
462137
|
const skillSkipped = skillResult.files.filter((f) => f.status === "skipped-exists");
|
|
461889
462138
|
if (skillWritten.length + skillSkipped.length > 0) {
|
|
461890
462139
|
console.log();
|
|
461891
|
-
console.log(source_default.bold.cyan("
|
|
462140
|
+
console.log(source_default.bold.cyan("Composition rules"));
|
|
461892
462141
|
console.log(source_default.dim("\u2500".repeat(60)));
|
|
461893
462142
|
for (const f of skillWritten) console.log(source_default.green(" +"), f.path);
|
|
461894
462143
|
for (const f of skillSkipped) console.log(source_default.dim(" \xB7"), f.path, source_default.dim("(kept existing)"));
|
|
@@ -461902,16 +462151,39 @@ sceneCommand.command("init").description("Scaffold a new scene project (or safel
|
|
|
461902
462151
|
} else {
|
|
461903
462152
|
console.log(` ${source_default.cyan("vibe scene styles")} ${source_default.dim("# pick a named style for DESIGN.md")}`);
|
|
461904
462153
|
}
|
|
461905
|
-
|
|
462154
|
+
if (profile === "agent" || profile === "full") {
|
|
462155
|
+
console.log(` ${source_default.dim("Your agent now has composition rules in")} ${source_default.cyan("SKILL.md")} ${source_default.dim("\u2014 ask it to author scene HTML directly.")}`);
|
|
462156
|
+
} else {
|
|
462157
|
+
console.log(` ${source_default.cyan("vibe scene install-skill")} ${source_default.dim("# add agent authoring rules later")}`);
|
|
462158
|
+
}
|
|
461906
462159
|
console.log(` ${source_default.cyan("vibe scene add")} <name> ${source_default.dim("# fallback: 5-preset emit (no agent)")}`);
|
|
461907
|
-
console.log(` ${source_default.cyan("vibe
|
|
461908
|
-
console.log(` ${source_default.cyan("vibe
|
|
462160
|
+
console.log(` ${source_default.cyan("vibe build")} ${source_default.dim("# build STORYBOARD.md into scenes/assets")}`);
|
|
462161
|
+
console.log(` ${source_default.cyan("vibe render")} ${source_default.dim("# render to video")}`);
|
|
461909
462162
|
} catch (error) {
|
|
461910
462163
|
spinner2?.fail("Failed to scaffold scene project");
|
|
461911
462164
|
const msg = error instanceof Error ? error.message : String(error);
|
|
461912
462165
|
exitWithError(generalError(`Failed to scaffold: ${msg}`));
|
|
461913
462166
|
}
|
|
461914
462167
|
});
|
|
462168
|
+
function printSceneInitDryRun(opts) {
|
|
462169
|
+
console.log();
|
|
462170
|
+
console.log(source_default.bold.cyan("VibeFrame Scene Init - dry run"));
|
|
462171
|
+
console.log(source_default.dim("-".repeat(60)));
|
|
462172
|
+
console.log(` Project: ${source_default.bold(opts.dir)}`);
|
|
462173
|
+
console.log(` Name: ${source_default.bold(opts.name)}`);
|
|
462174
|
+
console.log(` Profile: ${source_default.bold(opts.profile)} ${source_default.dim(formatSceneInitProfile(opts.profile))}`);
|
|
462175
|
+
console.log(` Aspect: ${opts.aspect}`);
|
|
462176
|
+
console.log(` Duration: ${opts.duration}s`);
|
|
462177
|
+
console.log(` Visual style: ${opts.visualStyleName ?? "none"}`);
|
|
462178
|
+
console.log();
|
|
462179
|
+
console.log(source_default.bold.cyan("Files that would be prepared"));
|
|
462180
|
+
console.log(source_default.dim("-".repeat(60)));
|
|
462181
|
+
for (const file of opts.groups.authoring) console.log(` authoring ${file}`);
|
|
462182
|
+
for (const file of opts.groups.agent) console.log(` agent ${file}`);
|
|
462183
|
+
for (const file of opts.groups.render) console.log(` render ${file}`);
|
|
462184
|
+
console.log();
|
|
462185
|
+
console.log(source_default.dim("No files were written."));
|
|
462186
|
+
}
|
|
461915
462187
|
var VALID_INSTALL_SKILL_HOSTS = ["claude-code", "cursor", "auto", "all"];
|
|
461916
462188
|
sceneCommand.command("install-skill").description("Install the Hyperframes skill into a scene project so the host agent can read it (Phase H1)").argument("[project-dir]", "Project directory containing STORYBOARD.md / DESIGN.md", ".").option("--host <id>", `Host layout target: ${VALID_INSTALL_SKILL_HOSTS.join(" | ")}`, "auto").option("--force", "Overwrite existing skill files (default: skip-on-exist)").option("--dry-run", "Preview which files would be written without changing anything").action(async (projectDirArg, options) => {
|
|
461917
462189
|
const startedAt = Date.now();
|
|
@@ -462457,7 +462729,7 @@ async function executeSceneAdd(opts) {
|
|
|
462457
462729
|
transcriptWordCount
|
|
462458
462730
|
};
|
|
462459
462731
|
}
|
|
462460
|
-
sceneCommand.command("lint").description("Validate scene HTML against
|
|
462732
|
+
sceneCommand.command("lint").description("Validate scene HTML against composition rules (in-process, no Chrome required)").argument("[root]", "Root composition file relative to --project", "index.html").option("--project <dir>", "Project directory", ".").option("--fix", 'Apply mechanical auto-fixes (currently: missing class="clip")').action(async (root2, options) => {
|
|
462461
462733
|
const startedAt = Date.now();
|
|
462462
462734
|
const projectDir = resolve21(options.project);
|
|
462463
462735
|
if (!await rootExists(projectDir, root2)) {
|
|
@@ -462551,7 +462823,7 @@ function validateWorkers(value) {
|
|
|
462551
462823
|
}
|
|
462552
462824
|
return n;
|
|
462553
462825
|
}
|
|
462554
|
-
sceneCommand.command("render").description("Render a scene project to MP4/WebM/MOV via the Hyperframes producer (requires Chrome)").argument("[root]", "Root composition file relative to --project", "index.html").option("--project <dir>", "Project directory", ".").option("-o, --out <path>", "Output file (default: renders/<name>-<timestamp>.<format>)").option("--fps <n>", `Frames per second: ${VALID_FPS.join("|")}`, "30").option("--quality <q>", `Quality preset: ${VALID_QUALITIES.join("|")}`, "standard").option("--format <f>", `Output container: ${VALID_FORMATS.join("|")}`, "mp4").option("--workers <n>", "Capture workers (1-16, default 1)", "1").option("--dry-run", "Preview parameters without rendering").action(async (root2, options) => {
|
|
462826
|
+
sceneCommand.command("render", { hidden: true }).description("Render a scene project to MP4/WebM/MOV via the Hyperframes producer (requires Chrome) [legacy \u2014 prefer `vibe render`]").argument("[root]", "Root composition file relative to --project", "index.html").option("--project <dir>", "Project directory", ".").option("-o, --out <path>", "Output file (default: renders/<name>-<timestamp>.<format>)").option("--fps <n>", `Frames per second: ${VALID_FPS.join("|")}`, "30").option("--quality <q>", `Quality preset: ${VALID_QUALITIES.join("|")}`, "standard").option("--format <f>", `Output container: ${VALID_FORMATS.join("|")}`, "mp4").option("--workers <n>", "Capture workers (1-16, default 1)", "1").option("--dry-run", "Preview parameters without rendering").action(async (root2, options) => {
|
|
462555
462827
|
const startedAt = Date.now();
|
|
462556
462828
|
const fps = validateFps(options.fps);
|
|
462557
462829
|
const quality = validateQuality(options.quality);
|
|
@@ -462627,7 +462899,7 @@ sceneCommand.command("render").description("Render a scene project to MP4/WebM/M
|
|
|
462627
462899
|
}
|
|
462628
462900
|
}
|
|
462629
462901
|
});
|
|
462630
|
-
sceneCommand.command("build").description("One-shot: read STORYBOARD.md cues, dispatch TTS + image-gen per beat, compose, render to MP4
|
|
462902
|
+
sceneCommand.command("build", { hidden: true }).description("One-shot: read STORYBOARD.md cues, dispatch TTS + image-gen per beat, compose, render to MP4 [legacy \u2014 prefer `vibe build`]").argument("[project-dir]", "Project directory containing STORYBOARD.md", ".").option("--mode <mode>", "Build mode: agent (host-agent authors HTML) | batch (CLI's internal LLM authors HTML) | auto (agent if any host detected) [Plan H \u2014 Phase 3]", "auto").option("--effort <level>", "Compose effort tier (batch mode only): low|medium|high", "medium").option("--composer <provider>", "LLM that composes scene HTML in batch mode: claude|openai|gemini (default: auto-resolve from available API keys, claude > gemini > openai)").option("--skip-narration", "Don't dispatch TTS even when beats declare narration cues").option("--skip-backdrop", "Don't dispatch image-gen even when beats declare backdrop cues").option("--skip-render", "Compose only \u2014 don't render to MP4").option("--tts <provider>", "TTS provider: auto|elevenlabs|kokoro (overrides frontmatter)").option("--voice <id>", "Voice id (provider-specific \u2014 overrides frontmatter)").option("--image-provider <name>", "Image provider: openai (only one supported in v0.60)").option("--quality <q>", "Image quality: standard|hd", "hd").option("--image-size <s>", "Image size: 1024x1024|1536x1024|1024x1536", "1536x1024").option("--force", "Re-dispatch primitives even when assets already exist").option("--dry-run", "Preview parameters without dispatching").action(async (projectDirArg, options) => {
|
|
462631
462903
|
const startedAt = Date.now();
|
|
462632
462904
|
const projectDir = resolve21(projectDirArg);
|
|
462633
462905
|
if (options.dryRun) {
|
|
@@ -462744,7 +463016,7 @@ sceneCommand.command("build").description("One-shot: read STORYBOARD.md cues, di
|
|
|
462744
463016
|
}
|
|
462745
463017
|
}
|
|
462746
463018
|
console.log();
|
|
462747
|
-
console.log(source_default.dim("Once you've authored each beat's HTML, re-run `vibe
|
|
463019
|
+
console.log(source_default.dim("Once you've authored each beat's HTML, re-run `vibe build` to lint + render."));
|
|
462748
463020
|
console.log(source_default.dim("Or pass `--mode batch` to use the internal LLM compose path instead."));
|
|
462749
463021
|
return;
|
|
462750
463022
|
}
|
|
@@ -462883,7 +463155,7 @@ var sceneInitTool = defineTool({
|
|
|
462883
463155
|
name: "scene_init",
|
|
462884
463156
|
category: "scene",
|
|
462885
463157
|
cost: "free",
|
|
462886
|
-
description: "Scaffold a new
|
|
463158
|
+
description: "Scaffold a new VibeFrame video scene project. Supports minimal, agent, and full profiles; full includes the current HTML render backend metadata. Idempotent: re-running keeps user-authored files and merges backend config instead of overwriting. No API keys required.",
|
|
462887
463159
|
schema: sceneInitSchema,
|
|
462888
463160
|
async execute(args, ctx) {
|
|
462889
463161
|
const dir = resolve23(ctx.workingDirectory, args.dir);
|
|
@@ -465041,38 +465313,44 @@ function findUnresolvedRefs(params, availableStepIds) {
|
|
|
465041
465313
|
|
|
465042
465314
|
// ../cli/src/pipeline/executor.ts
|
|
465043
465315
|
init_output();
|
|
465044
|
-
var
|
|
465045
|
-
"generate-image": "generate image",
|
|
465046
|
-
"generate-video": "generate video",
|
|
465047
|
-
"generate-tts": "generate speech",
|
|
465048
|
-
"generate-sfx": "generate sound-effect",
|
|
465049
|
-
"generate-music": "generate music",
|
|
465050
|
-
"generate-storyboard": "generate storyboard",
|
|
465051
|
-
"generate-motion": "generate motion",
|
|
465052
|
-
"edit-silence-cut": "edit silence-cut",
|
|
465053
|
-
"edit-jump-cut": "edit jump-cut",
|
|
465054
|
-
"edit-caption": "edit caption",
|
|
465055
|
-
"edit-noise-reduce": "edit noise-reduce",
|
|
465056
|
-
"edit-fade": "edit fade",
|
|
465057
|
-
"edit-translate-srt": "edit translate-srt",
|
|
465058
|
-
"edit-text-overlay": "edit text-overlay",
|
|
465059
|
-
"edit-grade": "edit grade",
|
|
465060
|
-
"edit-speed-ramp": "edit speed-ramp",
|
|
465061
|
-
"edit-reframe": "edit reframe",
|
|
465062
|
-
"edit-interpolate": "edit interpolate",
|
|
465063
|
-
"edit-upscale": "edit upscale-video",
|
|
465064
|
-
"edit-image": "edit image",
|
|
465065
|
-
"audio-transcribe": "audio transcribe",
|
|
465066
|
-
"
|
|
465067
|
-
"
|
|
465068
|
-
"
|
|
465069
|
-
"
|
|
465070
|
-
"
|
|
465071
|
-
"
|
|
465072
|
-
"
|
|
465316
|
+
var ACTION_METADATA = {
|
|
465317
|
+
"generate-image": { id: "generate-image", title: "Generate image", category: "generate", command: "generate image", outputs: ["image"] },
|
|
465318
|
+
"generate-video": { id: "generate-video", title: "Generate video", category: "generate", command: "generate video", outputs: ["video"], requiredKeys: ["provider-dependent"] },
|
|
465319
|
+
"generate-tts": { id: "generate-tts", title: "Generate speech", category: "generate", command: "generate speech", outputs: ["audio"] },
|
|
465320
|
+
"generate-sfx": { id: "generate-sfx", title: "Generate sound effect", category: "generate", command: "generate sound-effect", outputs: ["audio"] },
|
|
465321
|
+
"generate-music": { id: "generate-music", title: "Generate music", category: "generate", command: "generate music", outputs: ["audio"] },
|
|
465322
|
+
"generate-storyboard": { id: "generate-storyboard", title: "Generate storyboard", category: "generate", command: "generate storyboard", outputs: ["storyboard"] },
|
|
465323
|
+
"generate-motion": { id: "generate-motion", title: "Generate motion", category: "generate", command: "generate motion", outputs: ["code", "video"] },
|
|
465324
|
+
"edit-silence-cut": { id: "edit-silence-cut", title: "Cut silence", category: "edit", command: "edit silence-cut", outputs: ["video"] },
|
|
465325
|
+
"edit-jump-cut": { id: "edit-jump-cut", title: "Jump cut", category: "edit", command: "edit jump-cut", outputs: ["video"] },
|
|
465326
|
+
"edit-caption": { id: "edit-caption", title: "Caption video", category: "edit", command: "edit caption", outputs: ["video", "srt"] },
|
|
465327
|
+
"edit-noise-reduce": { id: "edit-noise-reduce", title: "Reduce noise", category: "edit", command: "edit noise-reduce", outputs: ["video"] },
|
|
465328
|
+
"edit-fade": { id: "edit-fade", title: "Add fade", category: "edit", command: "edit fade", outputs: ["video"] },
|
|
465329
|
+
"edit-translate-srt": { id: "edit-translate-srt", title: "Translate subtitles", category: "edit", command: "edit translate-srt", outputs: ["srt"] },
|
|
465330
|
+
"edit-text-overlay": { id: "edit-text-overlay", title: "Add text overlay", category: "edit", command: "edit text-overlay", outputs: ["video"] },
|
|
465331
|
+
"edit-grade": { id: "edit-grade", title: "Color grade", category: "edit", command: "edit grade", outputs: ["video"] },
|
|
465332
|
+
"edit-speed-ramp": { id: "edit-speed-ramp", title: "Speed ramp", category: "edit", command: "edit speed-ramp", outputs: ["video"] },
|
|
465333
|
+
"edit-reframe": { id: "edit-reframe", title: "Reframe video", category: "edit", command: "edit reframe", outputs: ["video"] },
|
|
465334
|
+
"edit-interpolate": { id: "edit-interpolate", title: "Interpolate frames", category: "edit", command: "edit interpolate", outputs: ["video"] },
|
|
465335
|
+
"edit-upscale": { id: "edit-upscale", title: "Upscale video", category: "edit", command: "edit upscale-video", outputs: ["video"] },
|
|
465336
|
+
"edit-image": { id: "edit-image", title: "Edit image", category: "edit", command: "edit image", outputs: ["image"] },
|
|
465337
|
+
"audio-transcribe": { id: "audio-transcribe", title: "Transcribe audio", category: "audio", command: "audio transcribe", outputs: ["transcript", "srt"] },
|
|
465338
|
+
"audio-isolate": { id: "audio-isolate", title: "Isolate audio", category: "audio", outputs: ["audio"] },
|
|
465339
|
+
"audio-dub": { id: "audio-dub", title: "Dub audio", category: "audio", outputs: ["audio", "video"] },
|
|
465340
|
+
"audio-duck": { id: "audio-duck", title: "Duck audio", category: "audio", outputs: ["video"] },
|
|
465341
|
+
"detect-scenes": { id: "detect-scenes", title: "Detect scenes", category: "detect", command: "detect scenes", outputs: ["json"] },
|
|
465342
|
+
"detect-silence": { id: "detect-silence", title: "Detect silence", category: "detect", command: "detect silence", outputs: ["json"] },
|
|
465343
|
+
"detect-beats": { id: "detect-beats", title: "Detect beats", category: "detect", command: "detect beats", outputs: ["json"] },
|
|
465344
|
+
"analyze-media": { id: "analyze-media", title: "Analyze media", category: "analyze", command: "analyze media", outputs: ["json"] },
|
|
465345
|
+
"analyze-video": { id: "analyze-video", title: "Analyze video", category: "analyze", command: "analyze video", outputs: ["json"] },
|
|
465346
|
+
"review-video": { id: "review-video", title: "Review video", category: "analyze", command: "analyze review", outputs: ["json"] },
|
|
465347
|
+
"compose-scenes-with-skills": { id: "compose-scenes-with-skills", title: "Compose scenes with skills", category: "scene", command: "compose scenes with skills", outputs: ["html"] },
|
|
465348
|
+
"scene-build": { id: "scene-build", title: "Build scene project", category: "scene", outputs: ["video", "html", "assets"] },
|
|
465349
|
+
"scene-render": { id: "scene-render", title: "Render scene project", category: "scene", outputs: ["video"] },
|
|
465350
|
+
export: { id: "export", title: "Export project", category: "export", outputs: ["video"] }
|
|
465073
465351
|
};
|
|
465074
465352
|
function maxCostFor(action) {
|
|
465075
|
-
const cmd =
|
|
465353
|
+
const cmd = ACTION_METADATA[action]?.command;
|
|
465076
465354
|
if (!cmd) return 0;
|
|
465077
465355
|
return COST_ESTIMATES[cmd]?.max ?? 0;
|
|
465078
465356
|
}
|
|
@@ -465097,7 +465375,15 @@ async function ensureActionsRegistered() {
|
|
|
465097
465375
|
registerAction("generate-video", async (params, outputDir) => {
|
|
465098
465376
|
const { executeVideoGenerate: executeVideoGenerate2 } = await Promise.resolve().then(() => (init_ai_video(), ai_video_exports));
|
|
465099
465377
|
const output3 = getOutput(params, outputDir, "video.mp4");
|
|
465100
|
-
const r = await executeVideoGenerate2({
|
|
465378
|
+
const r = await executeVideoGenerate2({
|
|
465379
|
+
prompt: params.prompt,
|
|
465380
|
+
provider: params.provider,
|
|
465381
|
+
image: params.image,
|
|
465382
|
+
duration: params.duration,
|
|
465383
|
+
ratio: params.ratio,
|
|
465384
|
+
output: output3,
|
|
465385
|
+
wait: true
|
|
465386
|
+
});
|
|
465101
465387
|
return { id: "", action: "generate-video", success: r.success, output: r.outputPath || r.videoUrl, data: { taskId: r.taskId, provider: r.provider, videoUrl: r.videoUrl }, error: r.error };
|
|
465102
465388
|
});
|
|
465103
465389
|
registerAction("generate-tts", async (params, outputDir) => {
|
|
@@ -466333,6 +466619,25 @@ Run 'vibe schema export' for structured parameter info.`).action(async (projectP
|
|
|
466333
466619
|
spinner2.text = `Encoding... ${progress}%`;
|
|
466334
466620
|
});
|
|
466335
466621
|
spinner2.succeed(source_default.green(`Exported: ${outputPath}`));
|
|
466622
|
+
if (isJsonMode()) {
|
|
466623
|
+
outputSuccess({
|
|
466624
|
+
command: "export",
|
|
466625
|
+
startedAt,
|
|
466626
|
+
data: {
|
|
466627
|
+
outputPath,
|
|
466628
|
+
backend: "ffmpeg",
|
|
466629
|
+
format: options.format,
|
|
466630
|
+
preset: options.preset,
|
|
466631
|
+
resolution: presetSettings.resolution,
|
|
466632
|
+
duration: summary.duration,
|
|
466633
|
+
clipCount: summary.clipCount,
|
|
466634
|
+
bitrate: options.bitrate ?? null,
|
|
466635
|
+
fps: options.fps ?? null,
|
|
466636
|
+
codec: options.codec ?? null
|
|
466637
|
+
}
|
|
466638
|
+
});
|
|
466639
|
+
return;
|
|
466640
|
+
}
|
|
466336
466641
|
console.log();
|
|
466337
466642
|
console.log(source_default.dim(" Duration:"), `${summary.duration.toFixed(1)}s`);
|
|
466338
466643
|
console.log(source_default.dim(" Clips:"), summary.clipCount);
|
|
@@ -467808,7 +468113,7 @@ A scene project is a directory that is **bilingual**: it works with both
|
|
|
467808
468113
|
and a paused GSAP timeline. Cheap to edit, cheap to lint, expensive only
|
|
467809
468114
|
at render.
|
|
467810
468115
|
|
|
467811
|
-
\`vibe
|
|
468116
|
+
\`vibe build\` (v0.60+) is the supported one-shot driver from a
|
|
467812
468117
|
written storyboard to an MP4. Plan H (v0.70) added \`--mode agent\` so the
|
|
467813
468118
|
host agent itself authors the per-beat HTML \u2014 no internal LLM call.
|
|
467814
468119
|
|
|
@@ -467816,19 +468121,19 @@ host agent itself authors the per-beat HTML \u2014 no internal LLM call.
|
|
|
467816
468121
|
|
|
467817
468122
|
| Path | Command | When to use |
|
|
467818
468123
|
|---|---|---|
|
|
467819
|
-
| **One-shot (default, v0.60+)** | \`vibe
|
|
467820
|
-
| **High-craft (manual)** | \`DESIGN.md\` +
|
|
468124
|
+
| **One-shot (default, v0.60+)** | \`vibe build [project-dir]\` | STORYBOARD.md has YAML frontmatter + per-beat cues |
|
|
468125
|
+
| **High-craft (manual)** | \`DESIGN.md\` + local composition rules in your agent | Maximum control: hand-author each scene |
|
|
467821
468126
|
| **Quick draft** | \`vibe scene add --style <preset>\` | No agent or no API keys; fast iteration |
|
|
467822
468127
|
|
|
467823
|
-
Recommend \`vibe
|
|
468128
|
+
Recommend \`vibe build\` whenever the user has a STORYBOARD with
|
|
467824
468129
|
narration / backdrop intent.
|
|
467825
468130
|
|
|
467826
468131
|
## High-craft path
|
|
467827
468132
|
|
|
467828
|
-
1. \`vibe
|
|
468133
|
+
1. \`vibe init my-promo --visual-style "Swiss Pulse"\` \u2014 seeds
|
|
467829
468134
|
\`DESIGN.md\` (palette, typography, motion, transitions) plus the
|
|
467830
468135
|
\`vibe.project.yaml\` / \`hyperframes.json\` / \`index.html\` scaffold.
|
|
467831
|
-
In Plan H this **also installs
|
|
468136
|
+
In Plan H this **also installs local composition rules** at the
|
|
467832
468137
|
right place for your host (\`.claude/skills/hyperframes/\` for Claude
|
|
467833
468138
|
Code, \`.cursor/rules/hyperframes.mdc\` for Cursor, universal
|
|
467834
468139
|
\`SKILL.md\` for everyone else).
|
|
@@ -467839,7 +468144,7 @@ narration / backdrop intent.
|
|
|
467839
468144
|
4. Author each scene HTML directly under \`compositions/scene-<id>.html\`
|
|
467840
468145
|
using the rules from steps 2 and 3. The skill enforces the visual
|
|
467841
468146
|
identity contract \u2014 scenes that contradict DESIGN.md fail lint.
|
|
467842
|
-
5. \`vibe scene lint --fix\` for mechanical issues, \`vibe
|
|
468147
|
+
5. \`vibe scene lint --fix\` for mechanical issues, \`vibe render my-promo\`
|
|
467843
468148
|
to MP4.
|
|
467844
468149
|
|
|
467845
468150
|
## Quick-draft path
|
|
@@ -467849,7 +468154,7 @@ vibe scene init my-promo -r 16:9 -d 30
|
|
|
467849
468154
|
vibe scene add intro --style announcement \\
|
|
467850
468155
|
--headline "Ship videos, not clicks"
|
|
467851
468156
|
vibe scene lint
|
|
467852
|
-
vibe
|
|
468157
|
+
vibe render my-promo
|
|
467853
468158
|
\`\`\`
|
|
467854
468159
|
|
|
467855
468160
|
\`vibe scene init\` is **idempotent** \u2014 running it on an existing
|
|
@@ -467861,12 +468166,12 @@ Safe to invoke on user-provided projects.
|
|
|
467861
468166
|
\`\`\`bash
|
|
467862
468167
|
vibe scene init <dir> [-r 16:9|9:16|1:1|4:5] [-d <sec>] [--visual-style "<name>"]
|
|
467863
468168
|
vibe scene styles [<name>] # list / show vendored visual identities
|
|
467864
|
-
vibe scene install-skill [<dir>] [--host all] # retroactive
|
|
468169
|
+
vibe scene install-skill [<dir>] [--host all] # retroactive composition-rules install
|
|
467865
468170
|
vibe scene add <name> --style <preset> [...]
|
|
467866
468171
|
vibe scene compose-prompts [<dir>] [--beat <id>] # H2: emit plan, no LLM call
|
|
467867
468172
|
vibe scene lint [<root>] [--json] [--fix]
|
|
467868
468173
|
vibe scene render [<root>] [--fps 30] [--quality standard] [--format mp4]
|
|
467869
|
-
vibe
|
|
468174
|
+
vibe build [<dir>] [--mode agent|batch|auto] # H3 dispatch
|
|
467870
468175
|
\`\`\`
|
|
467871
468176
|
|
|
467872
468177
|
## Style presets (for \`vibe scene add --style\`)
|
|
@@ -467884,18 +468189,18 @@ from the generated TTS audio.
|
|
|
467884
468189
|
## STORYBOARD-to-MP4 (one command, v0.60+)
|
|
467885
468190
|
|
|
467886
468191
|
\`\`\`bash
|
|
467887
|
-
vibe
|
|
468192
|
+
vibe init my-promo --visual-style "Swiss Pulse" -d 12
|
|
467888
468193
|
# (edit STORYBOARD.md with per-beat YAML cues \u2014 narration, backdrop, duration)
|
|
467889
|
-
vibe
|
|
468194
|
+
vibe build my-promo
|
|
467890
468195
|
\`\`\`
|
|
467891
468196
|
|
|
467892
|
-
\`vibe
|
|
468197
|
+
\`vibe build\` reads the STORYBOARD frontmatter + per-beat cues,
|
|
467893
468198
|
dispatches TTS + image-gen per beat, then either:
|
|
467894
468199
|
|
|
467895
468200
|
- **\`--mode agent\`** (default when an agent host is detected) \u2014 emits a
|
|
467896
468201
|
\`needs-author\` plan via \`vibe scene compose-prompts\`. The host agent
|
|
467897
468202
|
authors each \`compositions/scene-<id>.html\` itself, then re-invoking
|
|
467898
|
-
\`vibe
|
|
468203
|
+
\`vibe build\` proceeds to lint + render.
|
|
467899
468204
|
- **\`--mode batch\`** \u2014 VibeFrame runs an internal LLM (Claude / OpenAI /
|
|
467900
468205
|
Gemini) to compose the HTML, then renders.
|
|
467901
468206
|
|
|
@@ -467918,9 +468223,9 @@ and surface the error to the user.
|
|
|
467918
468223
|
| Task | Tool |
|
|
467919
468224
|
|------|------|
|
|
467920
468225
|
| Generate narration + image, then author scene | \`vibe scene add\` |
|
|
467921
|
-
| Generate a full scenes project from a STORYBOARD | \`vibe
|
|
468226
|
+
| Generate a full scenes project from a STORYBOARD | \`vibe build\` |
|
|
467922
468227
|
| Hand-tweak a single scene's animation | edit \`compositions/<file>.html\` directly |
|
|
467923
|
-
| Render the project | \`vibe
|
|
468228
|
+
| Render the project | \`vibe render\` *or* \`vibe scene render\` for lower-level control |
|
|
467924
468229
|
| Lint | \`vibe scene lint\` *or* \`npx hyperframes lint\` (equivalent) |
|
|
467925
468230
|
|
|
467926
468231
|
The \`vibe\` CLI adds asset generation, AI orchestration, and pipeline
|
|
@@ -468004,8 +468309,8 @@ Checkpoints land next to the YAML: \`pipeline.yaml.checkpoint.json\`.
|
|
|
468004
468309
|
|
|
468005
468310
|
## Authoring tips
|
|
468006
468311
|
|
|
468007
|
-
1. **Start from
|
|
468008
|
-
|
|
468312
|
+
1. **Start from a tiny YAML** \u2014 keep it in your project directory and run
|
|
468313
|
+
\`vibe run pipeline.yaml --dry-run\` before spending provider budget.
|
|
468009
468314
|
2. **Dry-run first** \u2014 you see estimated cost and resolved variable
|
|
468010
468315
|
graph before spending API credits.
|
|
468011
468316
|
3. **Keep step ids short and descriptive** (\`intro\`, \`scene1\`, \`voice\`,
|
|
@@ -468040,18 +468345,18 @@ var META = {
|
|
|
468040
468345
|
title: "Scene authoring with vibe",
|
|
468041
468346
|
summary: "Author per-scene HTML compositions and render to MP4 (BUILD flow)",
|
|
468042
468347
|
steps: [
|
|
468043
|
-
'Run `vibe
|
|
468348
|
+
'Run `vibe init <dir> --visual-style "<style name>"` to scaffold the project + install local composition rules.',
|
|
468044
468349
|
"Edit `STORYBOARD.md` with per-beat YAML cues (narration / backdrop / duration).",
|
|
468045
468350
|
"Read `SKILL.md` for the framework rules and `DESIGN.md` for the visual-identity hard-gate.",
|
|
468046
|
-
"Run `vibe
|
|
468047
|
-
"Run `vibe scene lint --fix` to validate, then `vibe
|
|
468351
|
+
"Run `vibe build <dir>`. With an agent host detected, the CLI emits a `needs-author` plan; the host agent authors each `compositions/scene-<id>.html` and re-invokes to render.",
|
|
468352
|
+
"Run `vibe scene lint --fix` to validate, then `vibe render <dir>` to produce the MP4."
|
|
468048
468353
|
],
|
|
468049
468354
|
relatedCommands: [
|
|
468050
|
-
"vibe
|
|
468355
|
+
"vibe init",
|
|
468051
468356
|
"vibe scene styles",
|
|
468052
468357
|
"vibe scene install-skill",
|
|
468053
468358
|
"vibe scene compose-prompts",
|
|
468054
|
-
"vibe
|
|
468359
|
+
"vibe build",
|
|
468055
468360
|
"vibe scene lint",
|
|
468056
468361
|
"vibe scene render",
|
|
468057
468362
|
"vibe scene add"
|