@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.
Files changed (2) hide show
  1. package/dist/index.js +465 -350
  2. 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, platform, clips) {
17267
- const emptyResult = { platform: platform.id, segments: [], totalDuration: 0 };
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 ${platform.name}.
17271
+ const systemPrompt = `You are an expert video editor optimizing content for ${platform2.name}.
17271
17272
 
17272
17273
  Platform requirements:
17273
- - Aspect ratio: ${platform.aspectRatio}
17274
- - Maximum duration: ${platform.maxDuration}s
17275
- - Ideal duration: ${platform.idealDuration.min}-${platform.idealDuration.max}s
17276
- - Needs strong hook: ${platform.features.hook ? "Yes" : "No"}
17277
- - Needs captions: ${platform.features.captions ? "Yes" : "No"}
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": "${platform.id}",
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 ${platform.name}:
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, platform, clips) {
18001
+ async suggestPlatformCuts(transcriptSegments, viralAnalysis, platform2, clips) {
18001
18002
  if (!this.api)
18002
- return { platform: platform.id, segments: [], totalDuration: 0 };
18003
- return suggestPlatformCuts(this.api, transcriptSegments, viralAnalysis, platform, clips);
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: spawn2 } = await import("child_process");
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 = spawn2("python3", args, {
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 platform = process.platform;
36910
- if (platform === "darwin") {
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 (platform === "linux") {
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 (platform === "win32") {
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 platform = process.platform;
37074
+ const platform2 = process.platform;
36980
37075
  let hint = "";
36981
- if (platform === "darwin") {
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 (platform === "linux") {
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 = spawn(ffmpegPath, args, {
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.0",
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(source_default.cyan(` ${keyDef.name.padEnd(14)} ${source_default.dim(keyDef.envVar)}: `));
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(source_default.cyan(` ${keyDef.name.padEnd(14)} ${source_default.dim(keyDef.envVar)}: `));
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
- const newKey = await promptHidden(source_default.cyan(" New key: "));
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
- const newKey = await promptHidden(source_default.cyan(` ${source_default.dim("\u25CB")} ${p.name.padEnd(12)} ${source_default.dim(p.desc)}: `));
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
- console.log(source_default.dim(" vibe walkthrough scene 5-step authoring guide (storyboard \u2192 MP4)"));
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 platform = process.platform;
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[platform] ? { fix: info.fix[platform] } : {}
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.0",
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.0",
187
- "@vibeframe/core": "0.80.0"
186
+ "@vibeframe/ai-providers": "0.80.1",
187
+ "@vibeframe/core": "0.80.1"
188
188
  },
189
189
  "engines": {
190
190
  "node": ">=20"