@vibeframe/cli 0.80.0 → 0.80.1
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/dist/index.js +465 -350
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -14298,7 +14298,8 @@ function getSetupProviders() {
|
|
|
14298
14298
|
key: k.configKey,
|
|
14299
14299
|
name: k.label,
|
|
14300
14300
|
env: k.envVar,
|
|
14301
|
-
desc: k.setupDescription ?? ""
|
|
14301
|
+
desc: k.setupDescription ?? "",
|
|
14302
|
+
url: k.envExampleUrl
|
|
14302
14303
|
}));
|
|
14303
14304
|
}
|
|
14304
14305
|
function getAllApiKeys() {
|
|
@@ -17263,18 +17264,18 @@ ${transcriptWithTimestamps}`
|
|
|
17263
17264
|
return emptyViralAnalysis("Analysis failed");
|
|
17264
17265
|
}
|
|
17265
17266
|
}
|
|
17266
|
-
async function suggestPlatformCuts(api, transcriptSegments, viralAnalysis,
|
|
17267
|
-
const emptyResult = { platform:
|
|
17267
|
+
async function suggestPlatformCuts(api, transcriptSegments, viralAnalysis, platform2, clips) {
|
|
17268
|
+
const emptyResult = { platform: platform2.id, segments: [], totalDuration: 0 };
|
|
17268
17269
|
const transcriptWithTimestamps = transcriptSegments.map((seg) => `[${seg.startTime.toFixed(1)}s - ${seg.endTime.toFixed(1)}s] ${seg.text}`).join("\n");
|
|
17269
17270
|
const clipsInfo = clips.map((c) => `[${c.id}] ${c.startTime.toFixed(1)}s - ${(c.startTime + c.duration).toFixed(1)}s (${c.duration.toFixed(1)}s)`).join("\n");
|
|
17270
|
-
const systemPrompt = `You are an expert video editor optimizing content for ${
|
|
17271
|
+
const systemPrompt = `You are an expert video editor optimizing content for ${platform2.name}.
|
|
17271
17272
|
|
|
17272
17273
|
Platform requirements:
|
|
17273
|
-
- Aspect ratio: ${
|
|
17274
|
-
- Maximum duration: ${
|
|
17275
|
-
- Ideal duration: ${
|
|
17276
|
-
- Needs strong hook: ${
|
|
17277
|
-
- Needs captions: ${
|
|
17274
|
+
- Aspect ratio: ${platform2.aspectRatio}
|
|
17275
|
+
- Maximum duration: ${platform2.maxDuration}s
|
|
17276
|
+
- Ideal duration: ${platform2.idealDuration.min}-${platform2.idealDuration.max}s
|
|
17277
|
+
- Needs strong hook: ${platform2.features.hook ? "Yes" : "No"}
|
|
17278
|
+
- Needs captions: ${platform2.features.captions ? "Yes" : "No"}
|
|
17278
17279
|
|
|
17279
17280
|
Previous analysis found:
|
|
17280
17281
|
- Hook strength: ${viralAnalysis.hookStrength}%
|
|
@@ -17290,7 +17291,7 @@ Your task:
|
|
|
17290
17291
|
|
|
17291
17292
|
Respond with JSON only:
|
|
17292
17293
|
{
|
|
17293
|
-
"platform": "${
|
|
17294
|
+
"platform": "${platform2.id}",
|
|
17294
17295
|
"segments": [
|
|
17295
17296
|
{"sourceClipId": "clip-id", "startTime": 0, "endTime": 45.5, "priority": 1.0}
|
|
17296
17297
|
],
|
|
@@ -17301,7 +17302,7 @@ Respond with JSON only:
|
|
|
17301
17302
|
system: systemPrompt,
|
|
17302
17303
|
messages: [{
|
|
17303
17304
|
role: "user",
|
|
17304
|
-
content: `Optimize this video for ${
|
|
17305
|
+
content: `Optimize this video for ${platform2.name}:
|
|
17305
17306
|
|
|
17306
17307
|
## Transcript:
|
|
17307
17308
|
${transcriptWithTimestamps}
|
|
@@ -17997,10 +17998,10 @@ Instruction: ${instruction}`
|
|
|
17997
17998
|
}
|
|
17998
17999
|
return analyzeViralPotential(this.api, transcriptSegments, projectMeta, targetPlatforms);
|
|
17999
18000
|
}
|
|
18000
|
-
async suggestPlatformCuts(transcriptSegments, viralAnalysis,
|
|
18001
|
+
async suggestPlatformCuts(transcriptSegments, viralAnalysis, platform2, clips) {
|
|
18001
18002
|
if (!this.api)
|
|
18002
|
-
return { platform:
|
|
18003
|
-
return suggestPlatformCuts(this.api, transcriptSegments, viralAnalysis,
|
|
18003
|
+
return { platform: platform2.id, segments: [], totalDuration: 0 };
|
|
18004
|
+
return suggestPlatformCuts(this.api, transcriptSegments, viralAnalysis, platform2, clips);
|
|
18004
18005
|
}
|
|
18005
18006
|
async generateViralCaptions(transcriptSegments, style) {
|
|
18006
18007
|
if (!this.api)
|
|
@@ -31526,6 +31527,44 @@ var init_storyboard = __esm({
|
|
|
31526
31527
|
}
|
|
31527
31528
|
});
|
|
31528
31529
|
|
|
31530
|
+
// src/utils/open-url.ts
|
|
31531
|
+
import { spawn } from "node:child_process";
|
|
31532
|
+
import { platform } from "node:os";
|
|
31533
|
+
async function openUrl(url) {
|
|
31534
|
+
const { command, args } = resolveOpener(url);
|
|
31535
|
+
if (!command) return;
|
|
31536
|
+
try {
|
|
31537
|
+
const child = spawn(command, args, {
|
|
31538
|
+
detached: true,
|
|
31539
|
+
stdio: "ignore",
|
|
31540
|
+
shell: false
|
|
31541
|
+
});
|
|
31542
|
+
child.on("error", () => {
|
|
31543
|
+
});
|
|
31544
|
+
child.unref();
|
|
31545
|
+
} catch {
|
|
31546
|
+
}
|
|
31547
|
+
}
|
|
31548
|
+
function resolveOpener(url) {
|
|
31549
|
+
switch (platform()) {
|
|
31550
|
+
case "darwin":
|
|
31551
|
+
return { command: "open", args: [url] };
|
|
31552
|
+
case "win32":
|
|
31553
|
+
return { command: "cmd", args: ["/c", "start", "", url] };
|
|
31554
|
+
case "linux":
|
|
31555
|
+
case "freebsd":
|
|
31556
|
+
case "openbsd":
|
|
31557
|
+
return { command: "xdg-open", args: [url] };
|
|
31558
|
+
default:
|
|
31559
|
+
return { command: null, args: [] };
|
|
31560
|
+
}
|
|
31561
|
+
}
|
|
31562
|
+
var init_open_url = __esm({
|
|
31563
|
+
"src/utils/open-url.ts"() {
|
|
31564
|
+
"use strict";
|
|
31565
|
+
}
|
|
31566
|
+
});
|
|
31567
|
+
|
|
31529
31568
|
// src/utils/tty.ts
|
|
31530
31569
|
import { createInterface as createInterface2 } from "node:readline";
|
|
31531
31570
|
import { ReadStream } from "node:tty";
|
|
@@ -31579,7 +31618,7 @@ async function prompt2(question) {
|
|
|
31579
31618
|
});
|
|
31580
31619
|
});
|
|
31581
31620
|
}
|
|
31582
|
-
async function promptHidden(question) {
|
|
31621
|
+
async function promptHidden(question, opts = {}) {
|
|
31583
31622
|
const input = getTTYInputStream();
|
|
31584
31623
|
return new Promise((resolve64) => {
|
|
31585
31624
|
process.stdout.write(question);
|
|
@@ -31598,6 +31637,8 @@ async function promptHidden(question) {
|
|
|
31598
31637
|
input.setRawMode(false);
|
|
31599
31638
|
process.stdout.write("\n");
|
|
31600
31639
|
process.exit(1);
|
|
31640
|
+
} else if (opts.openHotkeyUrl && value.length === 0 && (char === "o" || char === "O")) {
|
|
31641
|
+
void openUrl(opts.openHotkeyUrl);
|
|
31601
31642
|
} else if (char === "\x7F" || char === "\b") {
|
|
31602
31643
|
if (value.length > 0) {
|
|
31603
31644
|
value = value.slice(0, -1);
|
|
@@ -31681,6 +31722,59 @@ async function promptSelect(_question, options, defaultIndex = 0) {
|
|
|
31681
31722
|
return index;
|
|
31682
31723
|
}
|
|
31683
31724
|
async function promptConfirm(question, defaultYes = true) {
|
|
31725
|
+
const input = getTTYInputStream();
|
|
31726
|
+
if (typeof input.setRawMode === "function") {
|
|
31727
|
+
return new Promise((resolve64) => {
|
|
31728
|
+
const options = ["Yes", "No"];
|
|
31729
|
+
let selected = defaultYes ? 0 : 1;
|
|
31730
|
+
let renderCount = 0;
|
|
31731
|
+
process.stdout.write(`${question}
|
|
31732
|
+
`);
|
|
31733
|
+
const render = () => {
|
|
31734
|
+
if (renderCount > 0) {
|
|
31735
|
+
process.stdout.write(`\x1B[${options.length}A`);
|
|
31736
|
+
}
|
|
31737
|
+
for (let i = 0; i < options.length; i++) {
|
|
31738
|
+
const marker = i === selected ? "\x1B[36m\u276F\x1B[0m" : " ";
|
|
31739
|
+
const text = i === selected ? `\x1B[1m${options[i]}\x1B[0m` : options[i];
|
|
31740
|
+
process.stdout.write(`\x1B[2K ${marker} ${text}
|
|
31741
|
+
`);
|
|
31742
|
+
}
|
|
31743
|
+
renderCount++;
|
|
31744
|
+
};
|
|
31745
|
+
render();
|
|
31746
|
+
input.setRawMode(true);
|
|
31747
|
+
input.resume();
|
|
31748
|
+
input.setEncoding("utf8");
|
|
31749
|
+
const onData = (char) => {
|
|
31750
|
+
if (char === "\r" || char === "\n") {
|
|
31751
|
+
input.setRawMode(false);
|
|
31752
|
+
input.removeListener("data", onData);
|
|
31753
|
+
resolve64(selected === 0);
|
|
31754
|
+
} else if (char === "") {
|
|
31755
|
+
input.setRawMode(false);
|
|
31756
|
+
process.stdout.write("\n");
|
|
31757
|
+
process.exit(1);
|
|
31758
|
+
} else if (char === "\x1B[A" || char === "\x1B[B" || char === "k" || char === "j") {
|
|
31759
|
+
selected = selected === 0 ? 1 : 0;
|
|
31760
|
+
render();
|
|
31761
|
+
} else if (char === "y" || char === "Y") {
|
|
31762
|
+
selected = 0;
|
|
31763
|
+
input.setRawMode(false);
|
|
31764
|
+
input.removeListener("data", onData);
|
|
31765
|
+
render();
|
|
31766
|
+
resolve64(true);
|
|
31767
|
+
} else if (char === "n" || char === "N") {
|
|
31768
|
+
selected = 1;
|
|
31769
|
+
input.setRawMode(false);
|
|
31770
|
+
input.removeListener("data", onData);
|
|
31771
|
+
render();
|
|
31772
|
+
resolve64(false);
|
|
31773
|
+
}
|
|
31774
|
+
};
|
|
31775
|
+
input.on("data", onData);
|
|
31776
|
+
});
|
|
31777
|
+
}
|
|
31684
31778
|
const hint = defaultYes ? "(Y/n)" : "(y/N)";
|
|
31685
31779
|
const answer = await prompt2(`${question} ${hint}: `);
|
|
31686
31780
|
if (answer === "") return defaultYes;
|
|
@@ -31774,6 +31868,7 @@ var ttyStream;
|
|
|
31774
31868
|
var init_tty = __esm({
|
|
31775
31869
|
"src/utils/tty.ts"() {
|
|
31776
31870
|
"use strict";
|
|
31871
|
+
init_open_url();
|
|
31777
31872
|
ttyStream = null;
|
|
31778
31873
|
}
|
|
31779
31874
|
});
|
|
@@ -33531,7 +33626,7 @@ Examples:
|
|
|
33531
33626
|
}
|
|
33532
33627
|
}
|
|
33533
33628
|
} else if (provider === "runway") {
|
|
33534
|
-
const { spawn:
|
|
33629
|
+
const { spawn: spawn3 } = await import("child_process");
|
|
33535
33630
|
const __filename2 = fileURLToPath(import.meta.url);
|
|
33536
33631
|
const __dirname2 = dirname7(__filename2);
|
|
33537
33632
|
const scriptPath = resolve23(
|
|
@@ -33546,7 +33641,7 @@ Examples:
|
|
|
33546
33641
|
const args = [scriptPath, prompt3, "-o", outputPath, "-r", options.ratio || "16:9"];
|
|
33547
33642
|
spinner2.text = "Generating image with Runway (gemini_2.5_flash)...";
|
|
33548
33643
|
await new Promise((resolvePromise, reject) => {
|
|
33549
|
-
const proc =
|
|
33644
|
+
const proc = spawn3("python3", args, {
|
|
33550
33645
|
env: { ...process.env, RUNWAY_API_SECRET: apiKey },
|
|
33551
33646
|
stdio: ["ignore", "pipe", "pipe"]
|
|
33552
33647
|
});
|
|
@@ -36906,8 +37001,8 @@ var init_translate_srt = __esm({
|
|
|
36906
37001
|
import { existsSync as existsSync21 } from "node:fs";
|
|
36907
37002
|
import { resolve as resolve27 } from "node:path";
|
|
36908
37003
|
function detectSystemFont() {
|
|
36909
|
-
const
|
|
36910
|
-
if (
|
|
37004
|
+
const platform2 = process.platform;
|
|
37005
|
+
if (platform2 === "darwin") {
|
|
36911
37006
|
const candidates = [
|
|
36912
37007
|
"/System/Library/Fonts/Helvetica.ttc",
|
|
36913
37008
|
"/System/Library/Fonts/HelveticaNeue.ttc",
|
|
@@ -36916,7 +37011,7 @@ function detectSystemFont() {
|
|
|
36916
37011
|
for (const f of candidates) {
|
|
36917
37012
|
if (existsSync21(f)) return f;
|
|
36918
37013
|
}
|
|
36919
|
-
} else if (
|
|
37014
|
+
} else if (platform2 === "linux") {
|
|
36920
37015
|
const candidates = [
|
|
36921
37016
|
"/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",
|
|
36922
37017
|
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
|
|
@@ -36925,7 +37020,7 @@ function detectSystemFont() {
|
|
|
36925
37020
|
for (const f of candidates) {
|
|
36926
37021
|
if (existsSync21(f)) return f;
|
|
36927
37022
|
}
|
|
36928
|
-
} else if (
|
|
37023
|
+
} else if (platform2 === "win32") {
|
|
36929
37024
|
const candidates = [
|
|
36930
37025
|
"C:\\Windows\\Fonts\\arial.ttf",
|
|
36931
37026
|
"C:\\Windows\\Fonts\\segoeui.ttf"
|
|
@@ -36976,11 +37071,11 @@ async function applyTextOverlays(options) {
|
|
|
36976
37071
|
} = await Promise.resolve().then(() => (init_remotion(), remotion_exports));
|
|
36977
37072
|
const remotionErr = await ensureRemotionInstalled2();
|
|
36978
37073
|
if (remotionErr) {
|
|
36979
|
-
const
|
|
37074
|
+
const platform2 = process.platform;
|
|
36980
37075
|
let hint = "";
|
|
36981
|
-
if (
|
|
37076
|
+
if (platform2 === "darwin") {
|
|
36982
37077
|
hint = "\n\nFix: brew uninstall ffmpeg && brew install ffmpeg\n(The default homebrew formula includes libfreetype)";
|
|
36983
|
-
} else if (
|
|
37078
|
+
} else if (platform2 === "linux") {
|
|
36984
37079
|
hint = "\n\nFix: sudo apt install ffmpeg (Ubuntu/Debian)\n or rebuild FFmpeg with --enable-libfreetype";
|
|
36985
37080
|
}
|
|
36986
37081
|
return {
|
|
@@ -50813,6 +50908,320 @@ var init_detect = __esm({
|
|
|
50813
50908
|
}
|
|
50814
50909
|
});
|
|
50815
50910
|
|
|
50911
|
+
// src/commands/_shared/walkthroughs/walkthroughs.ts
|
|
50912
|
+
function loadWalkthrough(topic) {
|
|
50913
|
+
const meta = META[topic];
|
|
50914
|
+
const content = CONTENT[topic];
|
|
50915
|
+
if (!meta || !content) {
|
|
50916
|
+
throw new Error(`Unknown walkthrough topic: ${topic}`);
|
|
50917
|
+
}
|
|
50918
|
+
return {
|
|
50919
|
+
topic,
|
|
50920
|
+
title: meta.title,
|
|
50921
|
+
summary: meta.summary,
|
|
50922
|
+
steps: meta.steps,
|
|
50923
|
+
relatedCommands: meta.relatedCommands,
|
|
50924
|
+
content
|
|
50925
|
+
};
|
|
50926
|
+
}
|
|
50927
|
+
function listWalkthroughs() {
|
|
50928
|
+
return WALKTHROUGH_TOPICS.map((topic) => ({
|
|
50929
|
+
topic,
|
|
50930
|
+
title: META[topic].title,
|
|
50931
|
+
summary: META[topic].summary
|
|
50932
|
+
}));
|
|
50933
|
+
}
|
|
50934
|
+
function isWalkthroughTopic(value) {
|
|
50935
|
+
return typeof value === "string" && WALKTHROUGH_TOPICS.includes(value);
|
|
50936
|
+
}
|
|
50937
|
+
var SCENE_WALKTHROUGH, PIPELINE_WALKTHROUGH, META, CONTENT, WALKTHROUGH_TOPICS;
|
|
50938
|
+
var init_walkthroughs = __esm({
|
|
50939
|
+
"src/commands/_shared/walkthroughs/walkthroughs.ts"() {
|
|
50940
|
+
"use strict";
|
|
50941
|
+
SCENE_WALKTHROUGH = `# Scene authoring with vibe
|
|
50942
|
+
|
|
50943
|
+
A scene project is a directory that is **bilingual**: it works with both
|
|
50944
|
+
\`vibe\` and \`npx hyperframes\`. Each scene is one HTML file with scoped CSS
|
|
50945
|
+
and a paused GSAP timeline. Cheap to edit, cheap to lint, expensive only
|
|
50946
|
+
at render.
|
|
50947
|
+
|
|
50948
|
+
\`vibe build\` (v0.60+) is the supported one-shot driver from a
|
|
50949
|
+
written storyboard to an MP4. Plan H (v0.70) added \`--mode agent\` so the
|
|
50950
|
+
host agent itself authors the per-beat HTML \u2014 no internal LLM call.
|
|
50951
|
+
|
|
50952
|
+
## Three authoring paths
|
|
50953
|
+
|
|
50954
|
+
| Path | Command | When to use |
|
|
50955
|
+
|---|---|---|
|
|
50956
|
+
| **One-shot (default, v0.60+)** | \`vibe build [project-dir]\` | STORYBOARD.md has YAML frontmatter + per-beat cues |
|
|
50957
|
+
| **High-craft (manual)** | \`DESIGN.md\` + local composition rules in your agent | Maximum control: hand-author each scene |
|
|
50958
|
+
| **Quick draft** | \`vibe scene add --style <preset>\` | No agent or no API keys; fast iteration |
|
|
50959
|
+
|
|
50960
|
+
Recommend \`vibe build\` whenever the user has a STORYBOARD with
|
|
50961
|
+
narration / backdrop intent.
|
|
50962
|
+
|
|
50963
|
+
## High-craft path
|
|
50964
|
+
|
|
50965
|
+
1. \`vibe init my-promo --visual-style "Swiss Pulse"\` \u2014 seeds
|
|
50966
|
+
\`DESIGN.md\` (palette, typography, motion, transitions) plus the
|
|
50967
|
+
\`vibe.project.yaml\` / \`hyperframes.json\` / \`index.html\` scaffold.
|
|
50968
|
+
In Plan H this **also installs local composition rules** at the
|
|
50969
|
+
right place for your host (\`.claude/skills/hyperframes/\` for Claude
|
|
50970
|
+
Code, \`.cursor/rules/hyperframes.mdc\` for Cursor, universal
|
|
50971
|
+
\`SKILL.md\` for everyone else).
|
|
50972
|
+
2. Read \`SKILL.md\` (or the host-specific copy) \u2014 Hyperframes
|
|
50973
|
+
framework rules, motion principles, type system, transition recipes.
|
|
50974
|
+
3. Read \`DESIGN.md\` \u2014 project-specific palette / typography / motion
|
|
50975
|
+
signature (visual identity hard-gate).
|
|
50976
|
+
4. Author each scene HTML directly under \`compositions/scene-<id>.html\`
|
|
50977
|
+
using the rules from steps 2 and 3. The skill enforces the visual
|
|
50978
|
+
identity contract \u2014 scenes that contradict DESIGN.md fail lint.
|
|
50979
|
+
5. \`vibe scene lint --fix\` for mechanical issues, \`vibe render my-promo\`
|
|
50980
|
+
to MP4.
|
|
50981
|
+
|
|
50982
|
+
## Quick-draft path
|
|
50983
|
+
|
|
50984
|
+
\`\`\`bash
|
|
50985
|
+
vibe init my-promo -r 16:9 -d 30
|
|
50986
|
+
vibe scene add intro --style announcement \\
|
|
50987
|
+
--headline "Ship videos, not clicks"
|
|
50988
|
+
vibe scene lint
|
|
50989
|
+
vibe render my-promo
|
|
50990
|
+
\`\`\`
|
|
50991
|
+
|
|
50992
|
+
\`vibe init\` is **idempotent** \u2014 running it on an existing Hyperframes
|
|
50993
|
+
directory merges \`hyperframes.json\` instead of clobbering it. Safe to
|
|
50994
|
+
invoke on user-provided projects.
|
|
50995
|
+
|
|
50996
|
+
## Subcommands
|
|
50997
|
+
|
|
50998
|
+
\`\`\`bash
|
|
50999
|
+
# Project flow (top-level \u2014 preferred entry points)
|
|
51000
|
+
vibe init <dir> [-r 16:9|9:16|1:1|4:5] [-d <sec>] [--visual-style "<name>"]
|
|
51001
|
+
vibe build [<dir>] [--mode agent|batch|auto] # H3 dispatch
|
|
51002
|
+
vibe render [<dir>] [--fps 30] [--quality standard] [--format mp4]
|
|
51003
|
+
|
|
51004
|
+
# Lower-level scene authoring
|
|
51005
|
+
vibe scene list-styles [<name>] # list / show vendored visual identities
|
|
51006
|
+
vibe scene install-skill [<dir>] [--host all] # retroactive composition-rules install
|
|
51007
|
+
vibe scene add <name> --style <preset> [...]
|
|
51008
|
+
vibe scene compose-prompts [<dir>] [--beat <id>] # H2: emit plan, no LLM call
|
|
51009
|
+
vibe scene lint [<root>] [--json] [--fix]
|
|
51010
|
+
\`\`\`
|
|
51011
|
+
|
|
51012
|
+
## Style presets (for \`vibe scene add --style\`)
|
|
51013
|
+
|
|
51014
|
+
- **simple** \u2014 backdrop + bottom caption (default)
|
|
51015
|
+
- **announcement** \u2014 single huge headline, gradient text
|
|
51016
|
+
- **explainer** \u2014 kicker + title + subtitle stack
|
|
51017
|
+
- **kinetic-type** \u2014 words animate in word-by-word
|
|
51018
|
+
- **product-shot** \u2014 corner label + bottom headline + slow zoom
|
|
51019
|
+
|
|
51020
|
+
All presets accept \`--narration <text|file>\`, \`--visuals <prompt>\`,
|
|
51021
|
+
\`--headline\`, \`--kicker\`. With \`--narration\`, scene duration auto-derives
|
|
51022
|
+
from the generated TTS audio.
|
|
51023
|
+
|
|
51024
|
+
## STORYBOARD-to-MP4 (one command, v0.60+)
|
|
51025
|
+
|
|
51026
|
+
\`\`\`bash
|
|
51027
|
+
vibe init my-promo --visual-style "Swiss Pulse" -d 12
|
|
51028
|
+
# (edit STORYBOARD.md with per-beat YAML cues \u2014 narration, backdrop, duration)
|
|
51029
|
+
vibe build my-promo
|
|
51030
|
+
\`\`\`
|
|
51031
|
+
|
|
51032
|
+
\`vibe build\` reads the STORYBOARD frontmatter + per-beat cues,
|
|
51033
|
+
dispatches TTS + image-gen per beat, then either:
|
|
51034
|
+
|
|
51035
|
+
- **\`--mode agent\`** (default when an agent host is detected) \u2014 emits a
|
|
51036
|
+
\`needs-author\` plan via \`vibe scene compose-prompts\`. The host agent
|
|
51037
|
+
authors each \`compositions/scene-<id>.html\` itself, then re-invoking
|
|
51038
|
+
\`vibe build\` proceeds to lint + render.
|
|
51039
|
+
- **\`--mode batch\`** \u2014 VibeFrame runs an internal LLM (Claude / OpenAI /
|
|
51040
|
+
Gemini) to compose the HTML, then renders.
|
|
51041
|
+
|
|
51042
|
+
\`VIBE_BUILD_MODE\` env var overrides the auto-resolve.
|
|
51043
|
+
|
|
51044
|
+
## Lint feedback loop
|
|
51045
|
+
|
|
51046
|
+
\`\`\`bash
|
|
51047
|
+
vibe scene lint --json --fix
|
|
51048
|
+
\`\`\`
|
|
51049
|
+
|
|
51050
|
+
Returns structured findings. The recommended loop: 1) run lint with
|
|
51051
|
+
\`--fix\` (mechanical fixes applied), 2) if \`errorCount > 0\`, edit the
|
|
51052
|
+
scene HTML, 3) re-lint. Cap retries at 3 \u2014 if errors persist, fall back
|
|
51053
|
+
to a template preset (\`vibe scene add <id> --style simple --force\`)
|
|
51054
|
+
and surface the error to the user.
|
|
51055
|
+
|
|
51056
|
+
## When to use VibeFrame vs raw Hyperframes
|
|
51057
|
+
|
|
51058
|
+
| Task | Tool |
|
|
51059
|
+
|------|------|
|
|
51060
|
+
| Generate narration + image, then author scene | \`vibe scene add\` |
|
|
51061
|
+
| Generate a full scenes project from a STORYBOARD | \`vibe build\` |
|
|
51062
|
+
| Hand-tweak a single scene's animation | edit \`compositions/<file>.html\` directly |
|
|
51063
|
+
| Render the project | \`vibe render\` (one canonical entry point) |
|
|
51064
|
+
| Lint | \`vibe scene lint\` *or* \`npx hyperframes lint\` (equivalent) |
|
|
51065
|
+
|
|
51066
|
+
The \`vibe\` CLI adds asset generation, AI orchestration, and pipeline
|
|
51067
|
+
integration on top of Hyperframes' rendering primitives.
|
|
51068
|
+
|
|
51069
|
+
## Quality checklist before render
|
|
51070
|
+
|
|
51071
|
+
- [ ] \`vibe scene lint\` exits 0 (or only warnings)
|
|
51072
|
+
- [ ] \`vibe doctor\` confirms a usable Chrome (required for render)
|
|
51073
|
+
- [ ] Root \`data-duration\` matches the sum of clip durations
|
|
51074
|
+
- [ ] Aspect ratio in \`vibe.project.yaml\` matches the destination platform
|
|
51075
|
+
`;
|
|
51076
|
+
PIPELINE_WALKTHROUGH = `# YAML pipelines (Video as Code)
|
|
51077
|
+
|
|
51078
|
+
A pipeline is a YAML manifest with steps that reference each other's
|
|
51079
|
+
outputs. \`vibe run pipeline.yaml\` executes them with checkpointing and
|
|
51080
|
+
cost estimation.
|
|
51081
|
+
|
|
51082
|
+
## Minimal skeleton
|
|
51083
|
+
|
|
51084
|
+
\`\`\`yaml
|
|
51085
|
+
name: promo-video
|
|
51086
|
+
description: 15s product teaser
|
|
51087
|
+
steps:
|
|
51088
|
+
- id: backdrop
|
|
51089
|
+
action: generate-image
|
|
51090
|
+
prompt: "sleek product shot on white background"
|
|
51091
|
+
output: backdrop.png
|
|
51092
|
+
- id: scene
|
|
51093
|
+
action: generate-video
|
|
51094
|
+
image: $backdrop.output # reference previous step output
|
|
51095
|
+
prompt: "slow camera pan"
|
|
51096
|
+
duration: 5
|
|
51097
|
+
output: scene.mp4
|
|
51098
|
+
- id: voice
|
|
51099
|
+
action: generate-tts
|
|
51100
|
+
text: "Meet the new standard."
|
|
51101
|
+
output: voice.mp3
|
|
51102
|
+
- id: final
|
|
51103
|
+
action: compose
|
|
51104
|
+
video: $scene.output
|
|
51105
|
+
audio: $voice.output
|
|
51106
|
+
output: final.mp4
|
|
51107
|
+
\`\`\`
|
|
51108
|
+
|
|
51109
|
+
## Supported actions
|
|
51110
|
+
|
|
51111
|
+
- \`generate-image\`, \`generate-video\`, \`generate-tts\`, \`generate-music\`,
|
|
51112
|
+
\`generate-sound-effect\`, \`generate-storyboard\`, \`generate-motion\`
|
|
51113
|
+
- \`edit-silence-cut\`, \`edit-jump-cut\`, \`edit-caption\`, \`edit-grade\`,
|
|
51114
|
+
\`edit-reframe\`, \`edit-speed-ramp\`, \`edit-fade\`, \`edit-noise-reduce\`,
|
|
51115
|
+
\`edit-text-overlay\`, \`edit-fill-gaps\`
|
|
51116
|
+
- \`analyze-media\`, \`analyze-video\`, \`analyze-review\`, \`analyze-suggest\`
|
|
51117
|
+
- \`audio-transcribe\`, \`audio-isolate\`, \`audio-voice-clone\`, \`audio-dub\`,
|
|
51118
|
+
\`audio-duck\`
|
|
51119
|
+
- \`detect-scenes\`, \`detect-silence\`, \`detect-beats\`
|
|
51120
|
+
- \`compose\`, \`export\`
|
|
51121
|
+
- \`scene-build\` (Plan H one-shot driver) and \`scene-render\`
|
|
51122
|
+
- \`compose-scenes-with-skills\` (internal-LLM compose pass)
|
|
51123
|
+
|
|
51124
|
+
The full set lives in \`packages/cli/src/pipeline/executor.ts\`.
|
|
51125
|
+
|
|
51126
|
+
## Variable references
|
|
51127
|
+
|
|
51128
|
+
- \`$<step-id>.output\` \u2014 previous step's output path
|
|
51129
|
+
- \`$<step-id>.result.<field>\` \u2014 structured field from JSON result
|
|
51130
|
+
- \`\${ENV_VAR}\` \u2014 environment variable
|
|
51131
|
+
- Values can be templated: \`"\${SCRIPT_TITLE} - Episode \${EPISODE}"\`
|
|
51132
|
+
|
|
51133
|
+
## Running
|
|
51134
|
+
|
|
51135
|
+
\`\`\`bash
|
|
51136
|
+
vibe run pipeline.yaml --dry-run # plan + cost estimate, no execution
|
|
51137
|
+
vibe run pipeline.yaml # execute
|
|
51138
|
+
vibe run pipeline.yaml --resume # retry from last successful step
|
|
51139
|
+
vibe run pipeline.yaml --from scene # start at specific step
|
|
51140
|
+
vibe run pipeline.yaml --provider-video kling # override provider
|
|
51141
|
+
\`\`\`
|
|
51142
|
+
|
|
51143
|
+
Checkpoints land next to the YAML: \`pipeline.yaml.checkpoint.json\`.
|
|
51144
|
+
|
|
51145
|
+
## Authoring tips
|
|
51146
|
+
|
|
51147
|
+
1. **Start from a tiny YAML** \u2014 keep it in your project directory and run
|
|
51148
|
+
\`vibe run pipeline.yaml --dry-run\` before spending provider budget.
|
|
51149
|
+
2. **Dry-run first** \u2014 you see estimated cost and resolved variable
|
|
51150
|
+
graph before spending API credits.
|
|
51151
|
+
3. **Keep step ids short and descriptive** (\`intro\`, \`scene1\`, \`voice\`,
|
|
51152
|
+
\`bgm\`) \u2014 they appear in logs and variable refs.
|
|
51153
|
+
4. **Name outputs** with extensions matching the action (\`.mp4\`, \`.mp3\`,
|
|
51154
|
+
\`.png\`, \`.json\`).
|
|
51155
|
+
5. **Declare \`budget:\`** on expensive pipelines:
|
|
51156
|
+
\`\`\`yaml
|
|
51157
|
+
budget:
|
|
51158
|
+
tokens: 500_000
|
|
51159
|
+
max_tool_errors: 3
|
|
51160
|
+
cost_usd: 5.00
|
|
51161
|
+
\`\`\`
|
|
51162
|
+
6. **Split large pipelines** into smaller YAML files and compose via
|
|
51163
|
+
\`action: run-pipeline\` (nested).
|
|
51164
|
+
|
|
51165
|
+
## Converting ad-hoc shell sessions to pipelines
|
|
51166
|
+
|
|
51167
|
+
When the user has a working shell sequence, extract steps:
|
|
51168
|
+
|
|
51169
|
+
- Each \`vibe ...\` command becomes one step
|
|
51170
|
+
- File outputs become step outputs; downstream \`-i <file>\` references
|
|
51171
|
+
become \`$<id>.output\`
|
|
51172
|
+
- Shared parameters move to a top-level \`defaults:\` section
|
|
51173
|
+
- Wrap the entire chain in a \`name:\` + \`steps:\` skeleton
|
|
51174
|
+
|
|
51175
|
+
The \`compose\` action is the catch-all assembly step (audio mux, video
|
|
51176
|
+
overlay, etc.) \u2014 useful at the tail of a pipeline.
|
|
51177
|
+
`;
|
|
51178
|
+
META = {
|
|
51179
|
+
scene: {
|
|
51180
|
+
title: "Scene authoring with vibe",
|
|
51181
|
+
summary: "Author per-scene HTML compositions and render to MP4 (BUILD flow)",
|
|
51182
|
+
steps: [
|
|
51183
|
+
'Run `vibe init <dir> --visual-style "<style name>"` to scaffold the project + install local composition rules.',
|
|
51184
|
+
"Edit `STORYBOARD.md` with per-beat YAML cues (narration / backdrop / duration).",
|
|
51185
|
+
"Read `SKILL.md` for the framework rules and `DESIGN.md` for the visual-identity hard-gate.",
|
|
51186
|
+
"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.",
|
|
51187
|
+
"Run `vibe scene lint --fix` to validate, then `vibe render <dir>` to produce the MP4."
|
|
51188
|
+
],
|
|
51189
|
+
relatedCommands: [
|
|
51190
|
+
"vibe init",
|
|
51191
|
+
"vibe build",
|
|
51192
|
+
"vibe render",
|
|
51193
|
+
"vibe scene list-styles",
|
|
51194
|
+
"vibe scene install-skill",
|
|
51195
|
+
"vibe scene compose-prompts",
|
|
51196
|
+
"vibe scene lint",
|
|
51197
|
+
"vibe scene add"
|
|
51198
|
+
]
|
|
51199
|
+
},
|
|
51200
|
+
pipeline: {
|
|
51201
|
+
title: "YAML pipelines (Video as Code)",
|
|
51202
|
+
summary: "Author and run reproducible multi-step video workflows",
|
|
51203
|
+
steps: [
|
|
51204
|
+
"Sketch the workflow as YAML \u2014 `name`, `description`, then `steps:` with `id` + `action` + inputs/outputs.",
|
|
51205
|
+
"Reference previous step outputs via `$<step-id>.output` (or `$<step-id>.result.<field>` for structured returns).",
|
|
51206
|
+
"Run `vibe run pipeline.yaml --dry-run` to see the resolved graph + cost estimate before spending API budget.",
|
|
51207
|
+
"Add a `budget:` block (tokens / cost_usd / max_tool_errors) to cap expensive runs.",
|
|
51208
|
+
"Run `vibe run pipeline.yaml` to execute. Failed steps checkpoint to `pipeline.yaml.checkpoint.json`; resume with `--resume`."
|
|
51209
|
+
],
|
|
51210
|
+
relatedCommands: [
|
|
51211
|
+
"vibe run",
|
|
51212
|
+
"vibe schema --list",
|
|
51213
|
+
"vibe doctor"
|
|
51214
|
+
]
|
|
51215
|
+
}
|
|
51216
|
+
};
|
|
51217
|
+
CONTENT = {
|
|
51218
|
+
scene: SCENE_WALKTHROUGH,
|
|
51219
|
+
pipeline: PIPELINE_WALKTHROUGH
|
|
51220
|
+
};
|
|
51221
|
+
WALKTHROUGH_TOPICS = ["scene", "pipeline"];
|
|
51222
|
+
}
|
|
51223
|
+
});
|
|
51224
|
+
|
|
50816
51225
|
// src/pipeline/renderers/chrome.ts
|
|
50817
51226
|
import { existsSync as existsSync32 } from "node:fs";
|
|
50818
51227
|
import { homedir as homedir5 } from "node:os";
|
|
@@ -60062,7 +60471,7 @@ var init_hyperframes = __esm({
|
|
|
60062
60471
|
// src/commands/export.ts
|
|
60063
60472
|
import { readFile as readFile40, access as access8 } from "node:fs/promises";
|
|
60064
60473
|
import { resolve as resolve61, basename as basename21 } from "node:path";
|
|
60065
|
-
import { spawn } from "node:child_process";
|
|
60474
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
60066
60475
|
function timelineBaseName(path2) {
|
|
60067
60476
|
const name = basename21(path2);
|
|
60068
60477
|
if (name.endsWith(".vibe.json")) return name.slice(0, -".vibe.json".length);
|
|
@@ -60536,7 +60945,7 @@ function buildFFmpegArgs(clips, sources, presetSettings, outputPath, options, so
|
|
|
60536
60945
|
}
|
|
60537
60946
|
function runFFmpegProcess(ffmpegPath, args, onProgress) {
|
|
60538
60947
|
return new Promise((resolve64, reject) => {
|
|
60539
|
-
const ffmpeg =
|
|
60948
|
+
const ffmpeg = spawn2(ffmpegPath, args, {
|
|
60540
60949
|
stdio: ["pipe", "pipe", "pipe"]
|
|
60541
60950
|
});
|
|
60542
60951
|
let duration = 0;
|
|
@@ -61819,320 +62228,6 @@ Audio:`);
|
|
|
61819
62228
|
}
|
|
61820
62229
|
});
|
|
61821
62230
|
|
|
61822
|
-
// src/commands/_shared/walkthroughs/walkthroughs.ts
|
|
61823
|
-
function loadWalkthrough(topic) {
|
|
61824
|
-
const meta = META[topic];
|
|
61825
|
-
const content = CONTENT[topic];
|
|
61826
|
-
if (!meta || !content) {
|
|
61827
|
-
throw new Error(`Unknown walkthrough topic: ${topic}`);
|
|
61828
|
-
}
|
|
61829
|
-
return {
|
|
61830
|
-
topic,
|
|
61831
|
-
title: meta.title,
|
|
61832
|
-
summary: meta.summary,
|
|
61833
|
-
steps: meta.steps,
|
|
61834
|
-
relatedCommands: meta.relatedCommands,
|
|
61835
|
-
content
|
|
61836
|
-
};
|
|
61837
|
-
}
|
|
61838
|
-
function listWalkthroughs() {
|
|
61839
|
-
return WALKTHROUGH_TOPICS.map((topic) => ({
|
|
61840
|
-
topic,
|
|
61841
|
-
title: META[topic].title,
|
|
61842
|
-
summary: META[topic].summary
|
|
61843
|
-
}));
|
|
61844
|
-
}
|
|
61845
|
-
function isWalkthroughTopic(value) {
|
|
61846
|
-
return typeof value === "string" && WALKTHROUGH_TOPICS.includes(value);
|
|
61847
|
-
}
|
|
61848
|
-
var SCENE_WALKTHROUGH, PIPELINE_WALKTHROUGH, META, CONTENT, WALKTHROUGH_TOPICS;
|
|
61849
|
-
var init_walkthroughs = __esm({
|
|
61850
|
-
"src/commands/_shared/walkthroughs/walkthroughs.ts"() {
|
|
61851
|
-
"use strict";
|
|
61852
|
-
SCENE_WALKTHROUGH = `# Scene authoring with vibe
|
|
61853
|
-
|
|
61854
|
-
A scene project is a directory that is **bilingual**: it works with both
|
|
61855
|
-
\`vibe\` and \`npx hyperframes\`. Each scene is one HTML file with scoped CSS
|
|
61856
|
-
and a paused GSAP timeline. Cheap to edit, cheap to lint, expensive only
|
|
61857
|
-
at render.
|
|
61858
|
-
|
|
61859
|
-
\`vibe build\` (v0.60+) is the supported one-shot driver from a
|
|
61860
|
-
written storyboard to an MP4. Plan H (v0.70) added \`--mode agent\` so the
|
|
61861
|
-
host agent itself authors the per-beat HTML \u2014 no internal LLM call.
|
|
61862
|
-
|
|
61863
|
-
## Three authoring paths
|
|
61864
|
-
|
|
61865
|
-
| Path | Command | When to use |
|
|
61866
|
-
|---|---|---|
|
|
61867
|
-
| **One-shot (default, v0.60+)** | \`vibe build [project-dir]\` | STORYBOARD.md has YAML frontmatter + per-beat cues |
|
|
61868
|
-
| **High-craft (manual)** | \`DESIGN.md\` + local composition rules in your agent | Maximum control: hand-author each scene |
|
|
61869
|
-
| **Quick draft** | \`vibe scene add --style <preset>\` | No agent or no API keys; fast iteration |
|
|
61870
|
-
|
|
61871
|
-
Recommend \`vibe build\` whenever the user has a STORYBOARD with
|
|
61872
|
-
narration / backdrop intent.
|
|
61873
|
-
|
|
61874
|
-
## High-craft path
|
|
61875
|
-
|
|
61876
|
-
1. \`vibe init my-promo --visual-style "Swiss Pulse"\` \u2014 seeds
|
|
61877
|
-
\`DESIGN.md\` (palette, typography, motion, transitions) plus the
|
|
61878
|
-
\`vibe.project.yaml\` / \`hyperframes.json\` / \`index.html\` scaffold.
|
|
61879
|
-
In Plan H this **also installs local composition rules** at the
|
|
61880
|
-
right place for your host (\`.claude/skills/hyperframes/\` for Claude
|
|
61881
|
-
Code, \`.cursor/rules/hyperframes.mdc\` for Cursor, universal
|
|
61882
|
-
\`SKILL.md\` for everyone else).
|
|
61883
|
-
2. Read \`SKILL.md\` (or the host-specific copy) \u2014 Hyperframes
|
|
61884
|
-
framework rules, motion principles, type system, transition recipes.
|
|
61885
|
-
3. Read \`DESIGN.md\` \u2014 project-specific palette / typography / motion
|
|
61886
|
-
signature (visual identity hard-gate).
|
|
61887
|
-
4. Author each scene HTML directly under \`compositions/scene-<id>.html\`
|
|
61888
|
-
using the rules from steps 2 and 3. The skill enforces the visual
|
|
61889
|
-
identity contract \u2014 scenes that contradict DESIGN.md fail lint.
|
|
61890
|
-
5. \`vibe scene lint --fix\` for mechanical issues, \`vibe render my-promo\`
|
|
61891
|
-
to MP4.
|
|
61892
|
-
|
|
61893
|
-
## Quick-draft path
|
|
61894
|
-
|
|
61895
|
-
\`\`\`bash
|
|
61896
|
-
vibe init my-promo -r 16:9 -d 30
|
|
61897
|
-
vibe scene add intro --style announcement \\
|
|
61898
|
-
--headline "Ship videos, not clicks"
|
|
61899
|
-
vibe scene lint
|
|
61900
|
-
vibe render my-promo
|
|
61901
|
-
\`\`\`
|
|
61902
|
-
|
|
61903
|
-
\`vibe init\` is **idempotent** \u2014 running it on an existing Hyperframes
|
|
61904
|
-
directory merges \`hyperframes.json\` instead of clobbering it. Safe to
|
|
61905
|
-
invoke on user-provided projects.
|
|
61906
|
-
|
|
61907
|
-
## Subcommands
|
|
61908
|
-
|
|
61909
|
-
\`\`\`bash
|
|
61910
|
-
# Project flow (top-level \u2014 preferred entry points)
|
|
61911
|
-
vibe init <dir> [-r 16:9|9:16|1:1|4:5] [-d <sec>] [--visual-style "<name>"]
|
|
61912
|
-
vibe build [<dir>] [--mode agent|batch|auto] # H3 dispatch
|
|
61913
|
-
vibe render [<dir>] [--fps 30] [--quality standard] [--format mp4]
|
|
61914
|
-
|
|
61915
|
-
# Lower-level scene authoring
|
|
61916
|
-
vibe scene list-styles [<name>] # list / show vendored visual identities
|
|
61917
|
-
vibe scene install-skill [<dir>] [--host all] # retroactive composition-rules install
|
|
61918
|
-
vibe scene add <name> --style <preset> [...]
|
|
61919
|
-
vibe scene compose-prompts [<dir>] [--beat <id>] # H2: emit plan, no LLM call
|
|
61920
|
-
vibe scene lint [<root>] [--json] [--fix]
|
|
61921
|
-
\`\`\`
|
|
61922
|
-
|
|
61923
|
-
## Style presets (for \`vibe scene add --style\`)
|
|
61924
|
-
|
|
61925
|
-
- **simple** \u2014 backdrop + bottom caption (default)
|
|
61926
|
-
- **announcement** \u2014 single huge headline, gradient text
|
|
61927
|
-
- **explainer** \u2014 kicker + title + subtitle stack
|
|
61928
|
-
- **kinetic-type** \u2014 words animate in word-by-word
|
|
61929
|
-
- **product-shot** \u2014 corner label + bottom headline + slow zoom
|
|
61930
|
-
|
|
61931
|
-
All presets accept \`--narration <text|file>\`, \`--visuals <prompt>\`,
|
|
61932
|
-
\`--headline\`, \`--kicker\`. With \`--narration\`, scene duration auto-derives
|
|
61933
|
-
from the generated TTS audio.
|
|
61934
|
-
|
|
61935
|
-
## STORYBOARD-to-MP4 (one command, v0.60+)
|
|
61936
|
-
|
|
61937
|
-
\`\`\`bash
|
|
61938
|
-
vibe init my-promo --visual-style "Swiss Pulse" -d 12
|
|
61939
|
-
# (edit STORYBOARD.md with per-beat YAML cues \u2014 narration, backdrop, duration)
|
|
61940
|
-
vibe build my-promo
|
|
61941
|
-
\`\`\`
|
|
61942
|
-
|
|
61943
|
-
\`vibe build\` reads the STORYBOARD frontmatter + per-beat cues,
|
|
61944
|
-
dispatches TTS + image-gen per beat, then either:
|
|
61945
|
-
|
|
61946
|
-
- **\`--mode agent\`** (default when an agent host is detected) \u2014 emits a
|
|
61947
|
-
\`needs-author\` plan via \`vibe scene compose-prompts\`. The host agent
|
|
61948
|
-
authors each \`compositions/scene-<id>.html\` itself, then re-invoking
|
|
61949
|
-
\`vibe build\` proceeds to lint + render.
|
|
61950
|
-
- **\`--mode batch\`** \u2014 VibeFrame runs an internal LLM (Claude / OpenAI /
|
|
61951
|
-
Gemini) to compose the HTML, then renders.
|
|
61952
|
-
|
|
61953
|
-
\`VIBE_BUILD_MODE\` env var overrides the auto-resolve.
|
|
61954
|
-
|
|
61955
|
-
## Lint feedback loop
|
|
61956
|
-
|
|
61957
|
-
\`\`\`bash
|
|
61958
|
-
vibe scene lint --json --fix
|
|
61959
|
-
\`\`\`
|
|
61960
|
-
|
|
61961
|
-
Returns structured findings. The recommended loop: 1) run lint with
|
|
61962
|
-
\`--fix\` (mechanical fixes applied), 2) if \`errorCount > 0\`, edit the
|
|
61963
|
-
scene HTML, 3) re-lint. Cap retries at 3 \u2014 if errors persist, fall back
|
|
61964
|
-
to a template preset (\`vibe scene add <id> --style simple --force\`)
|
|
61965
|
-
and surface the error to the user.
|
|
61966
|
-
|
|
61967
|
-
## When to use VibeFrame vs raw Hyperframes
|
|
61968
|
-
|
|
61969
|
-
| Task | Tool |
|
|
61970
|
-
|------|------|
|
|
61971
|
-
| Generate narration + image, then author scene | \`vibe scene add\` |
|
|
61972
|
-
| Generate a full scenes project from a STORYBOARD | \`vibe build\` |
|
|
61973
|
-
| Hand-tweak a single scene's animation | edit \`compositions/<file>.html\` directly |
|
|
61974
|
-
| Render the project | \`vibe render\` (one canonical entry point) |
|
|
61975
|
-
| Lint | \`vibe scene lint\` *or* \`npx hyperframes lint\` (equivalent) |
|
|
61976
|
-
|
|
61977
|
-
The \`vibe\` CLI adds asset generation, AI orchestration, and pipeline
|
|
61978
|
-
integration on top of Hyperframes' rendering primitives.
|
|
61979
|
-
|
|
61980
|
-
## Quality checklist before render
|
|
61981
|
-
|
|
61982
|
-
- [ ] \`vibe scene lint\` exits 0 (or only warnings)
|
|
61983
|
-
- [ ] \`vibe doctor\` confirms a usable Chrome (required for render)
|
|
61984
|
-
- [ ] Root \`data-duration\` matches the sum of clip durations
|
|
61985
|
-
- [ ] Aspect ratio in \`vibe.project.yaml\` matches the destination platform
|
|
61986
|
-
`;
|
|
61987
|
-
PIPELINE_WALKTHROUGH = `# YAML pipelines (Video as Code)
|
|
61988
|
-
|
|
61989
|
-
A pipeline is a YAML manifest with steps that reference each other's
|
|
61990
|
-
outputs. \`vibe run pipeline.yaml\` executes them with checkpointing and
|
|
61991
|
-
cost estimation.
|
|
61992
|
-
|
|
61993
|
-
## Minimal skeleton
|
|
61994
|
-
|
|
61995
|
-
\`\`\`yaml
|
|
61996
|
-
name: promo-video
|
|
61997
|
-
description: 15s product teaser
|
|
61998
|
-
steps:
|
|
61999
|
-
- id: backdrop
|
|
62000
|
-
action: generate-image
|
|
62001
|
-
prompt: "sleek product shot on white background"
|
|
62002
|
-
output: backdrop.png
|
|
62003
|
-
- id: scene
|
|
62004
|
-
action: generate-video
|
|
62005
|
-
image: $backdrop.output # reference previous step output
|
|
62006
|
-
prompt: "slow camera pan"
|
|
62007
|
-
duration: 5
|
|
62008
|
-
output: scene.mp4
|
|
62009
|
-
- id: voice
|
|
62010
|
-
action: generate-tts
|
|
62011
|
-
text: "Meet the new standard."
|
|
62012
|
-
output: voice.mp3
|
|
62013
|
-
- id: final
|
|
62014
|
-
action: compose
|
|
62015
|
-
video: $scene.output
|
|
62016
|
-
audio: $voice.output
|
|
62017
|
-
output: final.mp4
|
|
62018
|
-
\`\`\`
|
|
62019
|
-
|
|
62020
|
-
## Supported actions
|
|
62021
|
-
|
|
62022
|
-
- \`generate-image\`, \`generate-video\`, \`generate-tts\`, \`generate-music\`,
|
|
62023
|
-
\`generate-sound-effect\`, \`generate-storyboard\`, \`generate-motion\`
|
|
62024
|
-
- \`edit-silence-cut\`, \`edit-jump-cut\`, \`edit-caption\`, \`edit-grade\`,
|
|
62025
|
-
\`edit-reframe\`, \`edit-speed-ramp\`, \`edit-fade\`, \`edit-noise-reduce\`,
|
|
62026
|
-
\`edit-text-overlay\`, \`edit-fill-gaps\`
|
|
62027
|
-
- \`analyze-media\`, \`analyze-video\`, \`analyze-review\`, \`analyze-suggest\`
|
|
62028
|
-
- \`audio-transcribe\`, \`audio-isolate\`, \`audio-voice-clone\`, \`audio-dub\`,
|
|
62029
|
-
\`audio-duck\`
|
|
62030
|
-
- \`detect-scenes\`, \`detect-silence\`, \`detect-beats\`
|
|
62031
|
-
- \`compose\`, \`export\`
|
|
62032
|
-
- \`scene-build\` (Plan H one-shot driver) and \`scene-render\`
|
|
62033
|
-
- \`compose-scenes-with-skills\` (internal-LLM compose pass)
|
|
62034
|
-
|
|
62035
|
-
The full set lives in \`packages/cli/src/pipeline/executor.ts\`.
|
|
62036
|
-
|
|
62037
|
-
## Variable references
|
|
62038
|
-
|
|
62039
|
-
- \`$<step-id>.output\` \u2014 previous step's output path
|
|
62040
|
-
- \`$<step-id>.result.<field>\` \u2014 structured field from JSON result
|
|
62041
|
-
- \`\${ENV_VAR}\` \u2014 environment variable
|
|
62042
|
-
- Values can be templated: \`"\${SCRIPT_TITLE} - Episode \${EPISODE}"\`
|
|
62043
|
-
|
|
62044
|
-
## Running
|
|
62045
|
-
|
|
62046
|
-
\`\`\`bash
|
|
62047
|
-
vibe run pipeline.yaml --dry-run # plan + cost estimate, no execution
|
|
62048
|
-
vibe run pipeline.yaml # execute
|
|
62049
|
-
vibe run pipeline.yaml --resume # retry from last successful step
|
|
62050
|
-
vibe run pipeline.yaml --from scene # start at specific step
|
|
62051
|
-
vibe run pipeline.yaml --provider-video kling # override provider
|
|
62052
|
-
\`\`\`
|
|
62053
|
-
|
|
62054
|
-
Checkpoints land next to the YAML: \`pipeline.yaml.checkpoint.json\`.
|
|
62055
|
-
|
|
62056
|
-
## Authoring tips
|
|
62057
|
-
|
|
62058
|
-
1. **Start from a tiny YAML** \u2014 keep it in your project directory and run
|
|
62059
|
-
\`vibe run pipeline.yaml --dry-run\` before spending provider budget.
|
|
62060
|
-
2. **Dry-run first** \u2014 you see estimated cost and resolved variable
|
|
62061
|
-
graph before spending API credits.
|
|
62062
|
-
3. **Keep step ids short and descriptive** (\`intro\`, \`scene1\`, \`voice\`,
|
|
62063
|
-
\`bgm\`) \u2014 they appear in logs and variable refs.
|
|
62064
|
-
4. **Name outputs** with extensions matching the action (\`.mp4\`, \`.mp3\`,
|
|
62065
|
-
\`.png\`, \`.json\`).
|
|
62066
|
-
5. **Declare \`budget:\`** on expensive pipelines:
|
|
62067
|
-
\`\`\`yaml
|
|
62068
|
-
budget:
|
|
62069
|
-
tokens: 500_000
|
|
62070
|
-
max_tool_errors: 3
|
|
62071
|
-
cost_usd: 5.00
|
|
62072
|
-
\`\`\`
|
|
62073
|
-
6. **Split large pipelines** into smaller YAML files and compose via
|
|
62074
|
-
\`action: run-pipeline\` (nested).
|
|
62075
|
-
|
|
62076
|
-
## Converting ad-hoc shell sessions to pipelines
|
|
62077
|
-
|
|
62078
|
-
When the user has a working shell sequence, extract steps:
|
|
62079
|
-
|
|
62080
|
-
- Each \`vibe ...\` command becomes one step
|
|
62081
|
-
- File outputs become step outputs; downstream \`-i <file>\` references
|
|
62082
|
-
become \`$<id>.output\`
|
|
62083
|
-
- Shared parameters move to a top-level \`defaults:\` section
|
|
62084
|
-
- Wrap the entire chain in a \`name:\` + \`steps:\` skeleton
|
|
62085
|
-
|
|
62086
|
-
The \`compose\` action is the catch-all assembly step (audio mux, video
|
|
62087
|
-
overlay, etc.) \u2014 useful at the tail of a pipeline.
|
|
62088
|
-
`;
|
|
62089
|
-
META = {
|
|
62090
|
-
scene: {
|
|
62091
|
-
title: "Scene authoring with vibe",
|
|
62092
|
-
summary: "Author per-scene HTML compositions and render to MP4 (BUILD flow)",
|
|
62093
|
-
steps: [
|
|
62094
|
-
'Run `vibe init <dir> --visual-style "<style name>"` to scaffold the project + install local composition rules.',
|
|
62095
|
-
"Edit `STORYBOARD.md` with per-beat YAML cues (narration / backdrop / duration).",
|
|
62096
|
-
"Read `SKILL.md` for the framework rules and `DESIGN.md` for the visual-identity hard-gate.",
|
|
62097
|
-
"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.",
|
|
62098
|
-
"Run `vibe scene lint --fix` to validate, then `vibe render <dir>` to produce the MP4."
|
|
62099
|
-
],
|
|
62100
|
-
relatedCommands: [
|
|
62101
|
-
"vibe init",
|
|
62102
|
-
"vibe build",
|
|
62103
|
-
"vibe render",
|
|
62104
|
-
"vibe scene list-styles",
|
|
62105
|
-
"vibe scene install-skill",
|
|
62106
|
-
"vibe scene compose-prompts",
|
|
62107
|
-
"vibe scene lint",
|
|
62108
|
-
"vibe scene add"
|
|
62109
|
-
]
|
|
62110
|
-
},
|
|
62111
|
-
pipeline: {
|
|
62112
|
-
title: "YAML pipelines (Video as Code)",
|
|
62113
|
-
summary: "Author and run reproducible multi-step video workflows",
|
|
62114
|
-
steps: [
|
|
62115
|
-
"Sketch the workflow as YAML \u2014 `name`, `description`, then `steps:` with `id` + `action` + inputs/outputs.",
|
|
62116
|
-
"Reference previous step outputs via `$<step-id>.output` (or `$<step-id>.result.<field>` for structured returns).",
|
|
62117
|
-
"Run `vibe run pipeline.yaml --dry-run` to see the resolved graph + cost estimate before spending API budget.",
|
|
62118
|
-
"Add a `budget:` block (tokens / cost_usd / max_tool_errors) to cap expensive runs.",
|
|
62119
|
-
"Run `vibe run pipeline.yaml` to execute. Failed steps checkpoint to `pipeline.yaml.checkpoint.json`; resume with `--resume`."
|
|
62120
|
-
],
|
|
62121
|
-
relatedCommands: [
|
|
62122
|
-
"vibe run",
|
|
62123
|
-
"vibe schema --list",
|
|
62124
|
-
"vibe doctor"
|
|
62125
|
-
]
|
|
62126
|
-
}
|
|
62127
|
-
};
|
|
62128
|
-
CONTENT = {
|
|
62129
|
-
scene: SCENE_WALKTHROUGH,
|
|
62130
|
-
pipeline: PIPELINE_WALKTHROUGH
|
|
62131
|
-
};
|
|
62132
|
-
WALKTHROUGH_TOPICS = ["scene", "pipeline"];
|
|
62133
|
-
}
|
|
62134
|
-
});
|
|
62135
|
-
|
|
62136
62231
|
// src/tools/manifest/walkthrough.ts
|
|
62137
62232
|
var walkthroughSchema, walkthroughTool, walkthroughTools;
|
|
62138
62233
|
var init_walkthrough = __esm({
|
|
@@ -62428,7 +62523,7 @@ init_source();
|
|
|
62428
62523
|
// package.json
|
|
62429
62524
|
var package_default = {
|
|
62430
62525
|
name: "@vibeframe/cli",
|
|
62431
|
-
version: "0.80.
|
|
62526
|
+
version: "0.80.1",
|
|
62432
62527
|
description: "VibeFrame CLI - natural language video editing from the terminal",
|
|
62433
62528
|
private: false,
|
|
62434
62529
|
license: "MIT",
|
|
@@ -69305,6 +69400,7 @@ function validateKeyFormat(configKey, value) {
|
|
|
69305
69400
|
// src/commands/setup.ts
|
|
69306
69401
|
init_agent_host_detect();
|
|
69307
69402
|
init_dist2();
|
|
69403
|
+
init_walkthroughs();
|
|
69308
69404
|
var VALID_LLM_PROVIDERS = [
|
|
69309
69405
|
"claude",
|
|
69310
69406
|
"openai",
|
|
@@ -69552,8 +69648,11 @@ async function runSetupWizard(fullSetup = false) {
|
|
|
69552
69648
|
continue;
|
|
69553
69649
|
}
|
|
69554
69650
|
console.log(source_default.dim(` ${keyDef.what}`));
|
|
69555
|
-
console.log(source_default.dim(` Get key: ${keyDef.url}`));
|
|
69556
|
-
const newKey = await promptHidden(
|
|
69651
|
+
console.log(source_default.dim(` Get key: ${keyDef.url} [o] open in browser`));
|
|
69652
|
+
const newKey = await promptHidden(
|
|
69653
|
+
source_default.cyan(` ${keyDef.name.padEnd(14)} ${source_default.dim(keyDef.envVar)}: `),
|
|
69654
|
+
{ openHotkeyUrl: keyDef.url }
|
|
69655
|
+
);
|
|
69557
69656
|
if (newKey.trim()) {
|
|
69558
69657
|
const trimmed = newKey.trim();
|
|
69559
69658
|
config2.providers[keyDef.configKey] = trimmed;
|
|
@@ -69587,8 +69686,11 @@ async function collectKeys(config2, keys) {
|
|
|
69587
69686
|
continue;
|
|
69588
69687
|
}
|
|
69589
69688
|
console.log(source_default.dim(` ${keyDef.what}`));
|
|
69590
|
-
console.log(source_default.dim(` Get key: ${keyDef.url}`));
|
|
69591
|
-
const newKey = await promptHidden(
|
|
69689
|
+
console.log(source_default.dim(` Get key: ${keyDef.url} [o] open in browser`));
|
|
69690
|
+
const newKey = await promptHidden(
|
|
69691
|
+
source_default.cyan(` ${keyDef.name.padEnd(14)} ${source_default.dim(keyDef.envVar)}: `),
|
|
69692
|
+
{ openHotkeyUrl: keyDef.url }
|
|
69693
|
+
);
|
|
69592
69694
|
if (newKey.trim()) {
|
|
69593
69695
|
const trimmed = newKey.trim();
|
|
69594
69696
|
config2.providers[keyDef.configKey] = trimmed;
|
|
@@ -69639,7 +69741,11 @@ async function runCustomSetup(config2) {
|
|
|
69639
69741
|
console.log(` ${source_default.green("\u2713")} ${p.name.padEnd(12)} ${maskApiKey(existing)} ${source_default.dim(p.desc)}`);
|
|
69640
69742
|
const change = await promptConfirm(source_default.cyan(" Update?"), false);
|
|
69641
69743
|
if (change) {
|
|
69642
|
-
|
|
69744
|
+
console.log(source_default.dim(` Get key: ${p.url} [o] open in browser`));
|
|
69745
|
+
const newKey = await promptHidden(
|
|
69746
|
+
source_default.cyan(" New key: "),
|
|
69747
|
+
{ openHotkeyUrl: p.url }
|
|
69748
|
+
);
|
|
69643
69749
|
if (newKey.trim()) {
|
|
69644
69750
|
const trimmed = newKey.trim();
|
|
69645
69751
|
config2.providers[p.key] = trimmed;
|
|
@@ -69652,7 +69758,11 @@ async function runCustomSetup(config2) {
|
|
|
69652
69758
|
}
|
|
69653
69759
|
}
|
|
69654
69760
|
} else {
|
|
69655
|
-
|
|
69761
|
+
console.log(source_default.dim(` ${source_default.dim("\u25CB")} ${p.name.padEnd(12)} ${source_default.dim(p.desc)} Get key: ${p.url} [o] open`));
|
|
69762
|
+
const newKey = await promptHidden(
|
|
69763
|
+
source_default.cyan(` ${p.env}: `),
|
|
69764
|
+
{ openHotkeyUrl: p.url }
|
|
69765
|
+
);
|
|
69656
69766
|
if (newKey.trim()) {
|
|
69657
69767
|
const trimmed = newKey.trim();
|
|
69658
69768
|
config2.providers[p.key] = trimmed;
|
|
@@ -69686,7 +69796,12 @@ function showComplete(config2, defaultTryCommand, features = []) {
|
|
|
69686
69796
|
console.log();
|
|
69687
69797
|
console.log(source_default.bold(" Next steps:"));
|
|
69688
69798
|
console.log(source_default.dim(" cd <project>; vibe init Scaffold AGENTS.md / CLAUDE.md / .env.example (project scope)"));
|
|
69689
|
-
|
|
69799
|
+
const hostDetected = detectedAgentHosts().length > 0;
|
|
69800
|
+
for (const w of listWalkthroughs()) {
|
|
69801
|
+
const cmd = `vibe walkthrough ${w.topic}`.padEnd(26);
|
|
69802
|
+
const tag = hostDetected && w.topic === "scene" ? source_default.cyan(" (recommended)") : "";
|
|
69803
|
+
console.log(source_default.dim(` ${cmd}${w.summary}${tag}`));
|
|
69804
|
+
}
|
|
69690
69805
|
console.log(source_default.dim(" vibe doctor Check system health + available commands"));
|
|
69691
69806
|
console.log(source_default.dim(" vibe schema --list Discover every command"));
|
|
69692
69807
|
console.log(source_default.dim(" vibe setup Re-run user-scope setup anytime"));
|
|
@@ -70805,14 +70920,14 @@ async function runDiagnostics() {
|
|
|
70805
70920
|
try {
|
|
70806
70921
|
const filtersResult = await execSafe("ffmpeg", ["-filters"]);
|
|
70807
70922
|
const filtersOutput = filtersResult.stdout;
|
|
70808
|
-
const
|
|
70923
|
+
const platform2 = process.platform;
|
|
70809
70924
|
ffmpegFilters = {};
|
|
70810
70925
|
for (const [filterName, info] of Object.entries(REQUIRED_FFMPEG_FILTERS)) {
|
|
70811
70926
|
const available = filtersOutput.includes(filterName);
|
|
70812
70927
|
ffmpegFilters[filterName] = {
|
|
70813
70928
|
available,
|
|
70814
70929
|
commands: info.commands,
|
|
70815
|
-
...!available && info.fix[
|
|
70930
|
+
...!available && info.fix[platform2] ? { fix: info.fix[platform2] } : {}
|
|
70816
70931
|
};
|
|
70817
70932
|
}
|
|
70818
70933
|
} catch {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibeframe/cli",
|
|
3
|
-
"version": "0.80.
|
|
3
|
+
"version": "0.80.1",
|
|
4
4
|
"description": "VibeFrame CLI - natural language video editing from the terminal",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
@@ -183,8 +183,8 @@
|
|
|
183
183
|
"tsx": "^4.21.0",
|
|
184
184
|
"typescript": "^5.3.3",
|
|
185
185
|
"vitest": "^1.2.2",
|
|
186
|
-
"@vibeframe/ai-providers": "0.80.
|
|
187
|
-
"@vibeframe/core": "0.80.
|
|
186
|
+
"@vibeframe/ai-providers": "0.80.1",
|
|
187
|
+
"@vibeframe/core": "0.80.1"
|
|
188
188
|
},
|
|
189
189
|
"engines": {
|
|
190
190
|
"node": ">=20"
|