@vibeframe/cli 0.85.0 → 0.86.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +116 -33
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -52677,14 +52677,14 @@ __export(ai_audio_exports, {
52677
52677
  });
52678
52678
  import { resolve as resolve54, dirname as dirname19, basename as basename18, extname as extname12 } from "node:path";
52679
52679
  import { readFile as readFile36, writeFile as writeFile40 } from "node:fs/promises";
52680
- import { existsSync as existsSync37 } from "node:fs";
52680
+ import { existsSync as existsSync38 } from "node:fs";
52681
52681
  async function executeTranscribe(options) {
52682
52682
  const { audioPath, language, output, format, apiKey } = options;
52683
52683
  try {
52684
52684
  const key = apiKey || process.env.OPENAI_API_KEY;
52685
52685
  if (!key) return { success: false, error: "OPENAI_API_KEY required" };
52686
52686
  const absPath = resolve54(process.cwd(), audioPath);
52687
- if (!existsSync37(absPath)) return { success: false, error: `File not found: ${absPath}` };
52687
+ if (!existsSync38(absPath)) return { success: false, error: `File not found: ${absPath}` };
52688
52688
  const whisper = new WhisperProvider();
52689
52689
  await whisper.initialize({ apiKey: key });
52690
52690
  const audioBuffer = await readFile36(absPath);
@@ -52717,7 +52717,7 @@ async function executeIsolate(options) {
52717
52717
  const key = apiKey || process.env.ELEVENLABS_API_KEY;
52718
52718
  if (!key) return { success: false, error: "ELEVENLABS_API_KEY required" };
52719
52719
  const absPath = resolve54(process.cwd(), audioPath);
52720
- if (!existsSync37(absPath)) return { success: false, error: `File not found: ${absPath}` };
52720
+ if (!existsSync38(absPath)) return { success: false, error: `File not found: ${absPath}` };
52721
52721
  const audioBuffer = await readFile36(absPath);
52722
52722
  const elevenlabs = new ElevenLabsProvider();
52723
52723
  await elevenlabs.initialize({ apiKey: key });
@@ -52743,7 +52743,7 @@ async function executeVoiceClone(options) {
52743
52743
  const audioBuffers = [];
52744
52744
  for (const samplePath of samplePaths) {
52745
52745
  const absPath = resolve54(process.cwd(), samplePath);
52746
- if (!existsSync37(absPath)) return { success: false, error: `File not found: ${samplePath}` };
52746
+ if (!existsSync38(absPath)) return { success: false, error: `File not found: ${samplePath}` };
52747
52747
  const buffer = await readFile36(absPath);
52748
52748
  audioBuffers.push(buffer);
52749
52749
  }
@@ -52765,7 +52765,7 @@ async function executeDub(options) {
52765
52765
  const { mediaPath, language, source, voice, analyzeOnly, output } = options;
52766
52766
  try {
52767
52767
  const absPath = resolve54(process.cwd(), mediaPath);
52768
- if (!existsSync37(absPath)) return { success: false, error: `File not found: ${absPath}` };
52768
+ if (!existsSync38(absPath)) return { success: false, error: `File not found: ${absPath}` };
52769
52769
  const openaiKey = process.env.OPENAI_API_KEY;
52770
52770
  const anthropicKey = process.env.ANTHROPIC_API_KEY;
52771
52771
  const elevenlabsKey = process.env.ELEVENLABS_API_KEY;
@@ -52893,8 +52893,8 @@ async function executeDuck(options) {
52893
52893
  if (!commandExists("ffmpeg")) return { success: false, error: "FFmpeg not found" };
52894
52894
  const absMusicPath = resolve54(process.cwd(), musicPath);
52895
52895
  const absVoicePath = resolve54(process.cwd(), voicePath);
52896
- if (!existsSync37(absMusicPath)) return { success: false, error: `Music file not found: ${absMusicPath}` };
52897
- if (!existsSync37(absVoicePath)) return { success: false, error: `Voice file not found: ${absVoicePath}` };
52896
+ if (!existsSync38(absMusicPath)) return { success: false, error: `Music file not found: ${absMusicPath}` };
52897
+ if (!existsSync38(absVoicePath)) return { success: false, error: `Voice file not found: ${absVoicePath}` };
52898
52898
  const defaultOutput = resolve54(dirname19(absMusicPath), `${basename18(absMusicPath, extname12(absMusicPath))}-ducked${extname12(absMusicPath)}`);
52899
52899
  const outputPath = resolve54(process.cwd(), output || defaultOutput);
52900
52900
  const filter = `[1:a]asplit=2[sc][mix];[0:a][sc]sidechaincompress=threshold=${threshold}dB:ratio=${ratio}:attack=${attack}:release=${release}[ducked];[ducked][mix]amix=inputs=2:duration=longest`;
@@ -52908,7 +52908,7 @@ async function executeDuck(options) {
52908
52908
  "-y",
52909
52909
  outputPath
52910
52910
  ], { timeout: 12e4, maxBuffer: 50 * 1024 * 1024 });
52911
- if (!existsSync37(outputPath)) return { success: false, error: "FFmpeg failed to create output" };
52911
+ if (!existsSync38(outputPath)) return { success: false, error: "FFmpeg failed to create output" };
52912
52912
  return { success: true, outputPath };
52913
52913
  } catch (error) {
52914
52914
  return { success: false, error: `Audio ducking failed: ${error instanceof Error ? error.message : String(error)}` };
@@ -52926,7 +52926,7 @@ var init_ai_audio = __esm({
52926
52926
  // src/pipeline/executor.ts
52927
52927
  import { resolve as resolve55 } from "node:path";
52928
52928
  import { readFile as readFile37, writeFile as writeFile41, mkdir as mkdir22 } from "node:fs/promises";
52929
- import { existsSync as existsSync38 } from "node:fs";
52929
+ import { existsSync as existsSync39 } from "node:fs";
52930
52930
  function maxCostFor(action) {
52931
52931
  const cmd = ACTION_METADATA[action]?.command;
52932
52932
  if (!cmd) return 0;
@@ -53143,7 +53143,7 @@ async function ensureActionsRegistered() {
53143
53143
  }
53144
53144
  async function loadPipeline(filePath) {
53145
53145
  const absPath = resolve55(process.cwd(), filePath);
53146
- if (!existsSync38(absPath)) {
53146
+ if (!existsSync39(absPath)) {
53147
53147
  throw new Error(`Pipeline file not found: ${absPath}`);
53148
53148
  }
53149
53149
  const content = await readFile37(absPath, "utf-8");
@@ -53168,7 +53168,7 @@ async function executePipeline(manifest2, options = {}) {
53168
53168
  const startTime = Date.now();
53169
53169
  if (options.resume) {
53170
53170
  const checkpointPath = resolve55(outputDir, CHECKPOINT_FILE);
53171
- if (existsSync38(checkpointPath)) {
53171
+ if (existsSync39(checkpointPath)) {
53172
53172
  const checkpointContent = await readFile37(checkpointPath, "utf-8");
53173
53173
  const checkpoint = (0, import_yaml8.parse)(checkpointContent);
53174
53174
  for (const cs of checkpoint.completedSteps) {
@@ -59771,7 +59771,7 @@ var init_generate2 = __esm({
59771
59771
  // src/tools/manifest/remix.ts
59772
59772
  import { writeFile as writeFile43 } from "node:fs/promises";
59773
59773
  import { tmpdir as tmpdir3 } from "node:os";
59774
- import { join as join13 } from "node:path";
59774
+ import { join as join14 } from "node:path";
59775
59775
  var pipelineHighlightsTool, pipelineAutoShortsTool, pipelineRunTool, pipelineRegenerateSceneTool, remixTools;
59776
59776
  var init_remix = __esm({
59777
59777
  "src/tools/manifest/remix.ts"() {
@@ -59879,7 +59879,7 @@ var init_remix = __esm({
59879
59879
  let resolvedPath = args.pipelinePath;
59880
59880
  let tempPath;
59881
59881
  if (args.pipelineYaml) {
59882
- tempPath = join13(tmpdir3(), `vibe-mcp-pipeline-${Date.now()}.yaml`);
59882
+ tempPath = join14(tmpdir3(), `vibe-mcp-pipeline-${Date.now()}.yaml`);
59883
59883
  await writeFile43(tempPath, args.pipelineYaml, "utf-8");
59884
59884
  resolvedPath = tempPath;
59885
59885
  }
@@ -60636,7 +60636,7 @@ __export(project_builder_exports, {
60636
60636
  resolveSourceUrl: () => resolveSourceUrl
60637
60637
  });
60638
60638
  import { mkdtemp, mkdir as mkdir24, copyFile as copyFile3, writeFile as writeFile47, rm as rm4, readdir as readdir3 } from "node:fs/promises";
60639
- import { existsSync as existsSync39 } from "node:fs";
60639
+ import { existsSync as existsSync40 } from "node:fs";
60640
60640
  import { tmpdir as tmpdir4 } from "node:os";
60641
60641
  import { createRequire } from "node:module";
60642
60642
  import * as path from "node:path";
@@ -60648,7 +60648,7 @@ async function buildTempProject(state, projectFileDir) {
60648
60648
  for (const source of state.sources) {
60649
60649
  if (copied.has(source.id)) continue;
60650
60650
  const resolved = resolveSourceUrl(source.url, projectFileDir);
60651
- if (!existsSync39(resolved)) {
60651
+ if (!existsSync40(resolved)) {
60652
60652
  throw new Error(`Source file not found: ${resolved} (from source id=${source.id})`);
60653
60653
  }
60654
60654
  const dest = path.join(assetsDir, path.basename(resolved));
@@ -61677,7 +61677,7 @@ var init_export2 = __esm({
61677
61677
 
61678
61678
  // src/tools/manifest/agent-only.ts
61679
61679
  import { readFile as readFile41, writeFile as writeFile48, readdir as readdir4, stat as stat6, access as access9, unlink as unlink4 } from "node:fs/promises";
61680
- import { resolve as resolve63, join as join15, basename as basename22, extname as extname13 } from "node:path";
61680
+ import { resolve as resolve63, join as join16, basename as basename22, extname as extname13 } from "node:path";
61681
61681
  function matchPattern(filename, pattern) {
61682
61682
  const regex2 = new RegExp(
61683
61683
  "^" + pattern.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".") + "$",
@@ -61760,7 +61760,7 @@ var init_agent_only = __esm({
61760
61760
  const results = [];
61761
61761
  for (const entry of entries) {
61762
61762
  if (args.pattern && !matchPattern(entry.name, args.pattern)) continue;
61763
- const fullPath = join15(absPath, entry.name);
61763
+ const fullPath = join16(absPath, entry.name);
61764
61764
  const stats = await stat6(fullPath);
61765
61765
  if (entry.isDirectory()) {
61766
61766
  results.push(`[DIR] ${entry.name}/`);
@@ -61865,7 +61865,7 @@ var init_agent_only = __esm({
61865
61865
  const scanDir = async (dir) => {
61866
61866
  const entries = await readdir4(dir, { withFileTypes: true });
61867
61867
  for (const entry of entries) {
61868
- const entryPath = join15(dir, entry.name);
61868
+ const entryPath = join16(dir, entry.name);
61869
61869
  if (entry.isDirectory() && recursive) {
61870
61870
  await scanDir(entryPath);
61871
61871
  } else if (entry.isFile()) {
@@ -62837,7 +62837,7 @@ init_source();
62837
62837
  // package.json
62838
62838
  var package_default = {
62839
62839
  name: "@vibeframe/cli",
62840
- version: "0.85.0",
62840
+ version: "0.86.0",
62841
62841
  description: "VibeFrame CLI - natural language video editing from the terminal",
62842
62842
  private: false,
62843
62843
  license: "MIT",
@@ -70960,8 +70960,10 @@ init_esm();
70960
70960
  init_source();
70961
70961
  init_ora();
70962
70962
  init_scene_build();
70963
+ init_storyboard_parse();
70963
70964
  init_output();
70964
- import { resolve as resolve48 } from "node:path";
70965
+ import { resolve as resolve48, join as join13 } from "node:path";
70966
+ import { readFileSync as readFileSync2, existsSync as existsSync35 } from "node:fs";
70965
70967
  var VALID_MODES = ["agent", "batch", "auto"];
70966
70968
  var buildCommand = new Command("build").description("Build a VibeFrame video project from STORYBOARD.md").argument("[project-dir]", "Video project directory", ".").option("--mode <mode>", "Build mode: agent|batch|auto", "auto").option("--effort <level>", "Compose effort tier (batch mode only): low|medium|high", "medium").option("--composer <provider>", "Batch composer: claude|openai|gemini").option("--skip-narration", "Don't dispatch TTS even when beats declare narration cues").option("--skip-backdrop", "Don't dispatch image-gen even when beats declare backdrop cues").option("--skip-render", "Compose only \u2014 don't render to MP4").option("--tts <provider>", "TTS provider: auto|elevenlabs|kokoro").option("--voice <id>", "Voice id").option("--image-provider <name>", "Image provider: openai").option("--quality <q>", "Image quality: standard|hd", "hd").option("--image-size <s>", "Image size: 1024x1024|1536x1024|1024x1536", "1536x1024").option("--force", "Re-dispatch primitives even when assets already exist").option("--dry-run", "Preview parameters without dispatching").addHelpText("after", `
70967
70969
  Examples:
@@ -71055,9 +71057,53 @@ function printBuildDryRun(projectDirArg, params) {
71055
71057
  console.log(` Image: ${source_default.bold(String(params.imageProvider ?? "openai"))} ${source_default.dim(`${params.imageQuality} ${params.imageSize}`)}${params.skipBackdrop ? source_default.dim(" (skipped)") : ""}`);
71056
71058
  console.log(` Render: ${source_default.bold(params.skipRender ? "skip" : "yes")}`);
71057
71059
  console.log(` Regenerate: ${source_default.bold(params.force ? "yes" : "cache when possible")}`);
71060
+ const estimate = estimateBuildCost(params);
71061
+ if (estimate) {
71062
+ console.log();
71063
+ console.log(source_default.bold.cyan("Estimated cost (tier-derived, conservative)"));
71064
+ console.log(source_default.dim("-".repeat(60)));
71065
+ console.log(` Beats: ${source_default.bold(estimate.beats)}`);
71066
+ if (!params.skipNarration) {
71067
+ console.log(` Narration: ${source_default.cyan(`$${estimate.narrationUsd.toFixed(2)}`)} ${source_default.dim(`(${estimate.beats} \xD7 low)`)}`);
71068
+ }
71069
+ if (!params.skipBackdrop) {
71070
+ console.log(` Backdrops: ${source_default.yellow(`$${estimate.backdropUsd.toFixed(2)}`)} ${source_default.dim(`(${estimate.beats} \xD7 high)`)}`);
71071
+ }
71072
+ if (params.mode !== "agent") {
71073
+ console.log(` Compose (LLM): ${source_default.cyan(`$${estimate.composeUsd.toFixed(2)}`)} ${source_default.dim(`(${estimate.beats} \xD7 ~$0.06 batch mode)`)}`);
71074
+ } else {
71075
+ console.log(` Compose: ${source_default.dim(`$0.00 (agent mode \u2014 host LLM does composition)`)}`);
71076
+ }
71077
+ console.log(` ${source_default.bold("Total:")} ${source_default.bold(`$${estimate.totalUsd.toFixed(2)}`)}`);
71078
+ }
71058
71079
  console.log();
71059
71080
  console.log(source_default.dim("No assets, provider calls, or video files were created."));
71060
71081
  }
71082
+ function estimateBuildCost(params) {
71083
+ const storyboardPath = join13(params.projectDir, "STORYBOARD.md");
71084
+ if (!existsSync35(storyboardPath)) return null;
71085
+ let beats = 0;
71086
+ try {
71087
+ const md = readFileSync2(storyboardPath, "utf-8");
71088
+ beats = parseStoryboard(md).beats.length;
71089
+ } catch {
71090
+ return null;
71091
+ }
71092
+ if (beats === 0) return null;
71093
+ const TTS_PER_BEAT = 0.05;
71094
+ const IMAGE_PER_BEAT = 3;
71095
+ const COMPOSE_PER_BEAT_BATCH = 0.06;
71096
+ const narrationUsd = params.skipNarration ? 0 : beats * TTS_PER_BEAT;
71097
+ const backdropUsd = params.skipBackdrop ? 0 : beats * IMAGE_PER_BEAT;
71098
+ const composeUsd = params.mode === "agent" ? 0 : beats * COMPOSE_PER_BEAT_BATCH;
71099
+ return {
71100
+ beats,
71101
+ narrationUsd,
71102
+ backdropUsd,
71103
+ composeUsd,
71104
+ totalUsd: narrationUsd + backdropUsd + composeUsd
71105
+ };
71106
+ }
71061
71107
  function printNeedsAuthor(result) {
71062
71108
  console.log(source_default.cyan("Agent authoring required"));
71063
71109
  console.log();
@@ -71266,10 +71312,11 @@ init_exec_safe();
71266
71312
  init_api_key();
71267
71313
  init_agent_host_detect();
71268
71314
  init_composer_resolve();
71315
+ init_cost_tier();
71269
71316
  init_scene_build();
71270
71317
  init_output();
71271
71318
  import { access as access7 } from "node:fs/promises";
71272
- import { existsSync as existsSync35 } from "node:fs";
71319
+ import { existsSync as existsSync36 } from "node:fs";
71273
71320
  import { resolve as resolve50 } from "node:path";
71274
71321
  var COMMAND_KEY_MAP = getCommandKeyMap();
71275
71322
  var FREE_COMMANDS = [
@@ -71307,7 +71354,10 @@ Examples:
71307
71354
  });
71308
71355
  return;
71309
71356
  }
71310
- printReport(results, { verbose: Boolean(options.verbose) });
71357
+ printReport(results, {
71358
+ verbose: Boolean(options.verbose),
71359
+ program: doctorCommand.parent
71360
+ });
71311
71361
  if (options.testKeys) {
71312
71362
  await runLiveKeyTests(results);
71313
71363
  }
@@ -71388,20 +71438,20 @@ async function runDiagnostics() {
71388
71438
  installed = false;
71389
71439
  }
71390
71440
  } else if (toolName === "chrome") {
71391
- const { existsSync: existsSync40 } = await import("node:fs");
71441
+ const { existsSync: existsSync41 } = await import("node:fs");
71392
71442
  const { homedir: homedir6 } = await import("node:os");
71393
- const { join: join16 } = await import("node:path");
71443
+ const { join: join17 } = await import("node:path");
71394
71444
  const chromePaths = [
71395
71445
  process.env.HYPERFRAMES_CHROME_PATH,
71396
71446
  process.env.CHROME_PATH,
71397
- join16(homedir6(), ".cache", "puppeteer", "chrome-headless-shell", "mac_arm-147.0.7727.56", "chrome-headless-shell-mac_arm", "chrome-headless-shell"),
71447
+ join17(homedir6(), ".cache", "puppeteer", "chrome-headless-shell", "mac_arm-147.0.7727.56", "chrome-headless-shell-mac_arm", "chrome-headless-shell"),
71398
71448
  "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
71399
71449
  "/Applications/Chromium.app/Contents/MacOS/Chromium",
71400
71450
  "/usr/bin/google-chrome",
71401
71451
  "/usr/bin/chromium",
71402
71452
  "/usr/bin/chromium-browser"
71403
71453
  ];
71404
- installed = chromePaths.some((p) => p && existsSync40(p));
71454
+ installed = chromePaths.some((p) => p && existsSync41(p));
71405
71455
  } else {
71406
71456
  installed = commandExists(toolName);
71407
71457
  }
@@ -71432,7 +71482,7 @@ async function runDiagnostics() {
71432
71482
  const cwd = process.cwd();
71433
71483
  const projectFiles = ["AGENTS.md", "CLAUDE.md", "vibe.project.yaml"].map((rel) => ({
71434
71484
  path: rel,
71435
- exists: existsSync35(resolve50(cwd, rel))
71485
+ exists: existsSync36(resolve50(cwd, rel))
71436
71486
  }));
71437
71487
  const projectInitialized = projectFiles.some((f) => f.exists);
71438
71488
  const hosts = detectAgentHosts();
@@ -71447,8 +71497,8 @@ async function runDiagnostics() {
71447
71497
  } catch (err) {
71448
71498
  if (!(err instanceof ComposerResolveError)) throw err;
71449
71499
  }
71450
- const sceneProjectInCwd = existsSync35(resolve50(cwd, "STORYBOARD.md"));
71451
- const skillInstalled = existsSync35(resolve50(cwd, "SKILL.md"));
71500
+ const sceneProjectInCwd = existsSync36(resolve50(cwd, "STORYBOARD.md"));
71501
+ const skillInstalled = existsSync36(resolve50(cwd, "SKILL.md"));
71452
71502
  return {
71453
71503
  system: {
71454
71504
  node: { version: nodeVersion, ok: true },
@@ -71474,7 +71524,7 @@ async function runDiagnostics() {
71474
71524
  };
71475
71525
  }
71476
71526
  function printReport(results, opts = { verbose: false }) {
71477
- const { verbose } = opts;
71527
+ const { verbose, program: program3 } = opts;
71478
71528
  console.log();
71479
71529
  console.log(source_default.bold(" System"));
71480
71530
  const nodeIcon = results.system.node.ok ? source_default.green("OK") : source_default.red("MISSING");
@@ -71611,12 +71661,40 @@ function printReport(results, opts = { verbose: false }) {
71611
71661
  console.log(
71612
71662
  source_default.dim(` Full command catalog: vibe schema --list`)
71613
71663
  );
71664
+ if (program3) {
71665
+ const tierCounts = countCostTiers(program3);
71666
+ const total = tierCounts.free + tierCounts.low + tierCounts.high + tierCounts["very-high"];
71667
+ if (total > 0) {
71668
+ const parts = [
71669
+ `${TIER_COLOR.free(`${tierCounts.free} free`)}`,
71670
+ `${TIER_COLOR.low(`${tierCounts.low} low`)}`,
71671
+ `${TIER_COLOR.high(`${tierCounts.high} high`)}`,
71672
+ `${TIER_COLOR["very-high"](`${tierCounts["very-high"]} very-high`)}`
71673
+ ];
71674
+ console.log(source_default.dim(` Cost mix:`) + ` ${parts.join(source_default.dim(", "))}`);
71675
+ }
71676
+ }
71614
71677
  const nextStep = pickNextStep(results, missing.length > 0);
71615
71678
  if (nextStep) {
71616
71679
  console.log(source_default.dim(` ${nextStep}`));
71617
71680
  }
71618
71681
  console.log();
71619
71682
  }
71683
+ function countCostTiers(program3) {
71684
+ const counts = {
71685
+ "free": 0,
71686
+ "low": 0,
71687
+ "high": 0,
71688
+ "very-high": 0
71689
+ };
71690
+ const walk = (cmd) => {
71691
+ const tier = getCostTier(cmd);
71692
+ if (tier) counts[tier]++;
71693
+ for (const child of cmd.commands) walk(child);
71694
+ };
71695
+ for (const top of program3.commands) walk(top);
71696
+ return counts;
71697
+ }
71620
71698
  async function runLiveKeyTests(results) {
71621
71699
  console.log();
71622
71700
  console.log(source_default.bold(" Live key tests"));
@@ -71669,7 +71747,7 @@ init_detect();
71669
71747
  init_ai_edit();
71670
71748
  init_output();
71671
71749
  import { resolve as resolve51 } from "node:path";
71672
- import { existsSync as existsSync36 } from "node:fs";
71750
+ import { existsSync as existsSync37 } from "node:fs";
71673
71751
  import { mkdir as mkdir21, rm as rm3 } from "node:fs/promises";
71674
71752
  var DEMO_DIR = resolve51(process.cwd(), ".vibeframe-demo");
71675
71753
  async function generateTestVideo(outputPath) {
@@ -71700,7 +71778,7 @@ async function generateTestVideo(outputPath) {
71700
71778
  "-y",
71701
71779
  outputPath
71702
71780
  ], { timeout: 3e4, maxBuffer: 50 * 1024 * 1024 });
71703
- return existsSync36(outputPath);
71781
+ return existsSync37(outputPath);
71704
71782
  } catch {
71705
71783
  try {
71706
71784
  await execSafe("ffmpeg", [
@@ -71726,7 +71804,7 @@ async function generateTestVideo(outputPath) {
71726
71804
  "-y",
71727
71805
  outputPath
71728
71806
  ], { timeout: 3e4, maxBuffer: 50 * 1024 * 1024 });
71729
- return existsSync36(outputPath);
71807
+ return existsSync37(outputPath);
71730
71808
  } catch {
71731
71809
  return false;
71732
71810
  }
@@ -72836,6 +72914,11 @@ async function startAgent(options = {}) {
72836
72914
  }
72837
72915
  console.log();
72838
72916
  console.log(source_default.dim(`Total: ${tools.length} tools`));
72917
+ const spent = agent.getCumulativeUsd();
72918
+ if (spent > 0 || budgetUsd !== void 0) {
72919
+ const budgetStr = budgetUsd !== void 0 ? ` / $${budgetUsd.toFixed(2)} cap` : "";
72920
+ console.log(source_default.dim(`Spent (tier-estimated): $${spent.toFixed(2)}${budgetStr}`));
72921
+ }
72839
72922
  console.log();
72840
72923
  rl.prompt();
72841
72924
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibeframe/cli",
3
- "version": "0.85.0",
3
+ "version": "0.86.0",
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.85.0",
187
- "@vibeframe/core": "0.85.0"
186
+ "@vibeframe/ai-providers": "0.86.0",
187
+ "@vibeframe/core": "0.86.0"
188
188
  },
189
189
  "engines": {
190
190
  "node": ">=20"