@vibeframe/mcp-server 0.77.0 → 0.79.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 +75 -55
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -452403,10 +452403,10 @@ var init_validate = __esm({
452403
452403
  import { resolve as resolve27, extname as extname7, basename as basename8 } from "node:path";
452404
452404
  import { existsSync as existsSync39 } from "node:fs";
452405
452405
  function registerEditCommands(aiCommand) {
452406
- aiCommand.command("silence-cut").alias("sc").description("Remove silent segments from video (FFmpeg default, or Gemini for smart detection)").argument("<video>", "Video file path").option("-o, --output <path>", "Output file path (default: <name>-cut.<ext>)").option("-n, --noise <dB>", "Silence threshold in dB (default: -30)", "-30").option("-d, --min-duration <seconds>", "Minimum silence duration to cut (default: 0.5)", "0.5").option("--padding <seconds>", "Padding around non-silent segments (default: 0.1)", "0.1").option("--analyze-only", "Only detect silence, don't cut").option("--use-gemini", "Use Gemini Video Understanding for context-aware silence detection").option("-m, --model <model>", "Gemini model (default: flash)").option("--low-res", "Low resolution mode for longer videos (Gemini only)").option("-k, --api-key <key>", "Google API key override (or set GOOGLE_API_KEY env)").option("--dry-run", "Preview parameters without executing").addHelpText("after", `
452406
+ aiCommand.command("silence-cut").alias("sc").description("Remove silent segments from video (FFmpeg default, or Gemini for smart detection)").argument("<video>", "Video file path").option("-o, --output <path>", "Output file path (default: <name>-cut.<ext>)").option("--noise <dB>", "Silence threshold in dB (default: -30)", "-30").option("-d, --min-duration <seconds>", "Minimum silence duration to cut (default: 0.5)", "0.5").option("--padding <seconds>", "Padding around non-silent segments (default: 0.1)", "0.1").option("--analyze-only", "Only detect silence, don't cut").option("--use-gemini", "Use Gemini Video Understanding for context-aware silence detection").option("-m, --model <model>", "Gemini model (default: flash)").option("--low-res", "Low resolution mode for longer videos (Gemini only)").option("-k, --api-key <key>", "Google API key override (or set GOOGLE_API_KEY env)").option("--dry-run", "Preview parameters without executing").addHelpText("after", `
452407
452407
  Examples:
452408
452408
  $ vibe edit silence-cut interview.mp4 -o clean.mp4
452409
- $ vibe ed sc podcast.mp4 -o trimmed.mp4 -n -25 -d 1.0
452409
+ $ vibe ed sc podcast.mp4 -o trimmed.mp4 --noise -25 --min-duration 1.0
452410
452410
  $ vibe ed sc video.mp4 --use-gemini -o smart-cut.mp4 # AI-powered detection
452411
452411
  $ vibe ed sc video.mp4 --analyze-only # Detect only, no cut
452412
452412
  $ vibe ed sc video.mp4 --dry-run --json
@@ -452504,12 +452504,12 @@ No API key needed (FFmpeg only). Use --use-gemini for smart detection (requires
452504
452504
  exitWithError(apiError(`Silence cut failed: ${error instanceof Error ? error.message : String(error)}`, true));
452505
452505
  }
452506
452506
  });
452507
- aiCommand.command("caption").alias("cap").description("Transcribe and burn styled captions onto video (Whisper + FFmpeg)").argument("<video>", "Video file path").option("-o, --output <path>", "Output file path (default: <name>-captioned.<ext>)").option("-s, --style <style>", "Caption style: minimal, bold, outline, karaoke (default: bold)", "bold").option("--font-size <pixels>", "Override auto-calculated font size").option("--color <color>", "Font color (default: white)", "white").option("-l, --language <lang>", "Language code for transcription (e.g., en, ko)").option("--position <pos>", "Caption position: top, center, bottom (default: bottom)", "bottom").option("-k, --api-key <key>", "OpenAI API key (or set OPENAI_API_KEY env)").option("--dry-run", "Preview parameters without executing").addHelpText("after", `
452507
+ aiCommand.command("caption").alias("cap").description("Transcribe and burn styled captions onto video (Whisper + FFmpeg)").argument("<video>", "Video file path").option("-o, --output <path>", "Output file path (default: <name>-captioned.<ext>)").option("--style <style>", "Caption style: minimal, bold, outline, karaoke (default: bold)", "bold").option("--font-size <pixels>", "Override auto-calculated font size").option("--color <color>", "Font color (default: white)", "white").option("-l, --language <lang>", "Language code for transcription (e.g., en, ko)").option("--position <pos>", "Caption position: top, center, bottom (default: bottom)", "bottom").option("-k, --api-key <key>", "OpenAI API key (or set OPENAI_API_KEY env)").option("--dry-run", "Preview parameters without executing").addHelpText("after", `
452508
452508
  Examples:
452509
452509
  $ vibe edit caption video.mp4 -o captioned.mp4
452510
- $ vibe ed cap video.mp4 -o out.mp4 -s bold --position top
452510
+ $ vibe ed cap video.mp4 -o out.mp4 --style bold --position top
452511
452511
  $ vibe ed cap video.mp4 -o out.mp4 -l ko # Korean transcription
452512
- $ vibe ed cap video.mp4 -o out.mp4 -s karaoke # Karaoke-style
452512
+ $ vibe ed cap video.mp4 -o out.mp4 --style karaoke # Karaoke-style
452513
452513
  $ vibe ed cap video.mp4 --dry-run --json
452514
452514
 
452515
452515
  Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoPath, options) => {
@@ -452593,7 +452593,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
452593
452593
  exitWithError(apiError(`Caption failed: ${error instanceof Error ? error.message : String(error)}`, true));
452594
452594
  }
452595
452595
  });
452596
- aiCommand.command("noise-reduce").description("Remove background noise from audio/video using FFmpeg (no API key needed)").argument("<input>", "Audio or video file path").option("-o, --output <path>", "Output file path (default: <name>-denoised.<ext>)").option("-s, --strength <level>", "Noise reduction strength: low, medium, high (default: medium)", "medium").option("-n, --noise-floor <dB>", "Custom noise floor in dB (overrides strength preset)").option("--dry-run", "Preview parameters without executing").action(async (inputPath, options) => {
452596
+ aiCommand.command("noise-reduce").description("Remove background noise from audio/video using FFmpeg (no API key needed)").argument("<input>", "Audio or video file path").option("-o, --output <path>", "Output file path (default: <name>-denoised.<ext>)").option("--strength <level>", "Noise reduction strength: low, medium, high (default: medium)", "medium").option("--noise-floor <dB>", "Custom noise floor in dB (overrides strength preset)").option("--dry-run", "Preview parameters without executing").action(async (inputPath, options) => {
452597
452597
  const startedAt = Date.now();
452598
452598
  try {
452599
452599
  if (options.output) {
@@ -452731,7 +452731,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
452731
452731
  exitWithError(generalError(`Fade failed: ${error instanceof Error ? error.message : String(error)}`));
452732
452732
  }
452733
452733
  });
452734
- aiCommand.command("translate-srt").description("Translate SRT subtitle file to another language (Claude/OpenAI)").argument("<srt>", "SRT file path").option("-t, --target <language>", "Target language (e.g., ko, es, fr, ja, zh)").option("-o, --output <path>", "Output file path (default: <name>-<target>.srt)").option("-p, --provider <provider>", "Translation provider: claude, openai (default: claude)", "claude").option("--source <language>", "Source language (auto-detected if omitted)").option("-k, --api-key <key>", "API key (or set ANTHROPIC_API_KEY / OPENAI_API_KEY env)").option("--dry-run", "Preview parameters without executing").action(async (srtPath, options) => {
452734
+ aiCommand.command("translate-srt").description("Translate SRT subtitle file to another language (Claude or OpenAI)").argument("<srt>", "SRT file path").option("--target <language>", "Target language (e.g., ko, es, fr, ja, zh)").option("-o, --output <path>", "Output file path (default: <name>-<target>.srt)").option("-p, --provider <provider>", "Translation provider: claude, openai (default: claude)", "claude").option("--source <language>", "Source language (auto-detected if omitted)").option("-k, --api-key <key>", "API key (or set ANTHROPIC_API_KEY / OPENAI_API_KEY env)").option("--dry-run", "Preview parameters without executing").action(async (srtPath, options) => {
452735
452735
  const startedAt = Date.now();
452736
452736
  try {
452737
452737
  if (options.output) {
@@ -453740,7 +453740,7 @@ var init_execute_fill_gaps = __esm({
453740
453740
 
453741
453741
  // ../cli/src/commands/ai-fill-gaps.ts
453742
453742
  function registerFillGapsCommand(aiCommand) {
453743
- aiCommand.command("fill-gaps").description("Fill timeline gaps with AI-generated video (Kling image-to-video)").argument("<project>", "Project file path").option("-p, --provider <provider>", "AI provider (kling)", "kling").option("-o, --output <path>", "Output project path (default: overwrite)").option("-d, --dir <path>", "Directory to save generated videos").option("--prompt <text>", "Custom prompt for video generation").option("--dry-run", "Show gaps without generating").option("-m, --mode <mode>", "Generation mode: std or pro (Kling)", "std").option("-r, --ratio <ratio>", "Aspect ratio: 16:9, 9:16, or 1:1", "16:9").action(async (projectPath, options) => {
453743
+ aiCommand.command("fill-gaps").description("Fill timeline gaps with AI-generated video (Kling image-to-video)").argument("<project>", "Project file path").option("-p, --provider <provider>", "AI provider (kling)", "kling").option("-o, --output <path>", "Output project path (default: overwrite)").option("-d, --dir <path>", "Directory to save generated videos").option("--prompt <text>", "Custom prompt for video generation").option("--dry-run", "Show gaps without generating").option("--mode <mode>", "Generation mode: std or pro (Kling)", "std").option("-r, --ratio <ratio>", "Aspect ratio: 16:9, 9:16, or 1:1", "16:9").action(async (projectPath, options) => {
453744
453744
  try {
453745
453745
  if (options.output) {
453746
453746
  validateOutputPath(options.output);
@@ -453999,12 +453999,12 @@ var init_edit_cmd = __esm({
453999
453999
  `
454000
454000
  Examples:
454001
454001
  $ vibe edit silence-cut interview.mp4 -o clean.mp4
454002
- $ vibe edit caption video.mp4 -o captioned.mp4 -s bold
454002
+ $ vibe edit caption video.mp4 -o captioned.mp4 --style bold
454003
454003
  $ vibe edit grade video.mp4 -o graded.mp4 --preset cinematic-warm
454004
454004
  $ vibe edit reframe landscape.mp4 -o vertical.mp4 -a 9:16
454005
454005
  $ vibe edit image photo.png "add sunset background" -o edited.png
454006
- $ vibe edit text-overlay video.mp4 -t "Title" -s center-bold -o out.mp4
454007
- $ vibe edit noise-reduce noisy.mp4 -o clean.mp4 -s high
454006
+ $ vibe edit text-overlay video.mp4 --text "Title" --style center-bold -o out.mp4
454007
+ $ vibe edit noise-reduce noisy.mp4 -o clean.mp4 --strength high
454008
454008
  $ vibe edit fade video.mp4 -o faded.mp4 --fade-in 1 --fade-out 1
454009
454009
 
454010
454010
  API Keys (varies by subcommand):
@@ -454018,7 +454018,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
454018
454018
  );
454019
454019
  registerEditCommands(editCommand);
454020
454020
  registerFillGapsCommand(editCommand);
454021
- editCommand.command("grade").description("Apply AI-generated color grading (Claude + FFmpeg)").argument("<video>", "Video file path").option("-s, --style <prompt>", "Style description (e.g., 'cinematic warm')").option("--preset <name>", "Built-in preset: film-noir, vintage, cinematic-warm, cool-tones, high-contrast, pastel, cyberpunk, horror").option("-o, --output <path>", "Output video file path").option("--analyze-only", "Show filter without applying").option("-k, --api-key <key>", "Anthropic API key (or set ANTHROPIC_API_KEY env)").option("--dry-run", "Preview parameters without executing").action(async (videoPath, options) => {
454021
+ editCommand.command("grade").description("Apply AI-generated color grading (Claude + FFmpeg)").argument("<video>", "Video file path").option("--style <prompt>", "Style description (e.g., 'cinematic warm')").option("--preset <name>", "Built-in preset: film-noir, vintage, cinematic-warm, cool-tones, high-contrast, pastel, cyberpunk, horror").option("-o, --output <path>", "Output video file path").option("--analyze-only", "Show filter without applying").option("-k, --api-key <key>", "Anthropic API key (or set ANTHROPIC_API_KEY env)").option("--dry-run", "Preview parameters without executing").action(async (videoPath, options) => {
454022
454022
  const startedAt = Date.now();
454023
454023
  try {
454024
454024
  if (options.style) rejectControlChars(options.style);
@@ -454106,7 +454106,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
454106
454106
  exitWithError(apiError(`Color grading failed: ${error.message}`));
454107
454107
  }
454108
454108
  });
454109
- editCommand.command("text-overlay").description("Apply text overlays to video (FFmpeg drawtext)").argument("<video>", "Video file path").option("-t, --text <texts...>", "Text lines to overlay (repeat for multiple)").option("-s, --style <style>", "Overlay style: lower-third, center-bold, subtitle, minimal", "lower-third").option("--font-size <size>", "Font size in pixels (auto-calculated if omitted)").option("--font-color <color>", "Font color (default: white)", "white").option("--fade <seconds>", "Fade in/out duration in seconds", "0.3").option("--start <seconds>", "Start time in seconds", "0").option("--end <seconds>", "End time in seconds (default: video duration)").option("-o, --output <path>", "Output video file path").option("--dry-run", "Preview parameters without executing").action(async (videoPath, options) => {
454109
+ editCommand.command("text-overlay").description("Apply text overlays to video (FFmpeg drawtext)").argument("<video>", "Video file path").option("--text <texts...>", "Text lines to overlay (repeat for multiple)").option("--style <style>", "Overlay style: lower-third, center-bold, subtitle, minimal", "lower-third").option("--font-size <size>", "Font size in pixels (auto-calculated if omitted)").option("--font-color <color>", "Font color (default: white)", "white").option("--fade <seconds>", "Fade in/out duration in seconds", "0.3").option("--start <seconds>", "Start time in seconds", "0").option("--end <seconds>", "End time in seconds (default: video duration)").option("-o, --output <path>", "Output video file path").option("--dry-run", "Preview parameters without executing").action(async (videoPath, options) => {
454110
454110
  const startedAt = Date.now();
454111
454111
  try {
454112
454112
  if (!options.text || options.text.length === 0) {
@@ -454182,7 +454182,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
454182
454182
  exitWithError(generalError(`Text overlay failed: ${msg}`));
454183
454183
  }
454184
454184
  });
454185
- editCommand.command("speed-ramp").description("Apply content-aware speed ramping (Whisper + Claude + FFmpeg)").argument("<video>", "Video file path").option("-o, --output <path>", "Output video file path").option("-s, --style <style>", "Style: dramatic, smooth, action", "dramatic").option("--min-speed <factor>", "Minimum speed factor", "0.25").option("--max-speed <factor>", "Maximum speed factor", "4.0").option("--analyze-only", "Show keyframes without applying").option("-l, --language <lang>", "Language code for transcription").option("-k, --api-key <key>", "Anthropic API key (or set ANTHROPIC_API_KEY env)").option("--dry-run", "Preview parameters without executing").action(async (videoPath, options) => {
454185
+ editCommand.command("speed-ramp").description("Apply content-aware speed ramping (Whisper + Claude + FFmpeg)").argument("<video>", "Video file path").option("-o, --output <path>", "Output video file path").option("--style <style>", "Style: dramatic, smooth, action", "dramatic").option("--min-speed <factor>", "Minimum speed factor", "0.25").option("--max-speed <factor>", "Maximum speed factor", "4.0").option("--analyze-only", "Show keyframes without applying").option("-l, --language <lang>", "Language code for transcription").option("-k, --api-key <key>", "Anthropic API key (or set ANTHROPIC_API_KEY env)").option("--dry-run", "Preview parameters without executing").action(async (videoPath, options) => {
454186
454186
  const startedAt = Date.now();
454187
454187
  try {
454188
454188
  if (options.output) {
@@ -454302,7 +454302,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
454302
454302
  exitWithError(generalError(`Speed ramping failed: ${msg}`));
454303
454303
  }
454304
454304
  });
454305
- editCommand.command("reframe").description("Auto-reframe video to different aspect ratio (Claude Vision + FFmpeg)").argument("<video>", "Video file path").option("-a, --aspect <ratio>", "Target aspect ratio: 9:16, 1:1, 4:5", "9:16").option("-f, --focus <mode>", "Focus mode: auto, face, center, action", "auto").option("-o, --output <path>", "Output video file path").option("--analyze-only", "Show crop regions without applying").option("--keyframes <path>", "Export keyframes to JSON file").option("-k, --api-key <key>", "Anthropic API key (or set ANTHROPIC_API_KEY env)").option("--dry-run", "Preview parameters without executing").action(async (videoPath, options) => {
454305
+ editCommand.command("reframe").description("Auto-reframe video to different aspect ratio (Claude Vision + FFmpeg)").argument("<video>", "Video file path").option("-a, --aspect <ratio>", "Target aspect ratio: 9:16, 1:1, 4:5", "9:16").option("--focus <mode>", "Focus mode: auto, face, center, action", "auto").option("-o, --output <path>", "Output video file path").option("--analyze-only", "Show crop regions without applying").option("--keyframes <path>", "Export keyframes to JSON file").option("-k, --api-key <key>", "Anthropic API key (or set ANTHROPIC_API_KEY env)").option("--dry-run", "Preview parameters without executing").action(async (videoPath, options) => {
454306
454306
  const startedAt = Date.now();
454307
454307
  try {
454308
454308
  if (options.output) {
@@ -454449,7 +454449,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
454449
454449
  exitWithError(generalError(`Reframe failed: ${msg}`));
454450
454450
  }
454451
454451
  });
454452
- editCommand.command("image").description("Edit image(s) using AI (Gemini/OpenAI/Grok)").argument("<images...>", "Input image file(s) followed by edit prompt").option("-p, --provider <provider>", "Provider: gemini (default), openai, grok", "gemini").option("-k, --api-key <key>", "API key (or set env variable)").option("-o, --output <path>", "Output file path", "edited.png").option("-m, --model <model>", "Model: flash/3.1-flash/latest/pro (Gemini only)", "flash").option("-r, --ratio <ratio>", "Output aspect ratio").option("-s, --size <resolution>", "Resolution: 1K, 2K, 4K (Gemini Pro only)").option("--dry-run", "Preview parameters without executing").action(async (args, options) => {
454452
+ editCommand.command("image").description("Edit image(s) using AI (Gemini, OpenAI, or Grok)").argument("<images...>", "Input image file(s) followed by edit prompt").option("-p, --provider <provider>", "Provider: gemini (default), openai, grok", "gemini").option("-k, --api-key <key>", "API key (or set env variable)").option("-o, --output <path>", "Output file path", "edited.png").option("-m, --model <model>", "Model: flash/3.1-flash/latest/pro (Gemini only)", "flash").option("-r, --ratio <ratio>", "Output aspect ratio").option("--size <resolution>", "Resolution: 1K, 2K, 4K (Gemini Pro only)").option("--dry-run", "Preview parameters without executing").action(async (args, options) => {
454453
454453
  const startedAt = Date.now();
454454
454454
  try {
454455
454455
  if (args.length < 2) {
@@ -454578,7 +454578,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
454578
454578
  exitWithError(apiError(`Image editing failed: ${msg}`, true));
454579
454579
  }
454580
454580
  });
454581
- editCommand.command("interpolate").description("Create slow motion with frame interpolation (FFmpeg)").argument("<video>", "Video file path").option("-o, --output <path>", "Output file path").option("-f, --factor <number>", "Slow motion factor: 2, 4, or 8", "2").option("--fps <number>", "Target output FPS").option("--quality <mode>", "Quality: fast or quality", "quality").option("--dry-run", "Preview parameters without executing").action(async (videoPath, options) => {
454581
+ editCommand.command("interpolate").description("Create slow motion with frame interpolation (FFmpeg)").argument("<video>", "Video file path").option("-o, --output <path>", "Output file path").option("--factor <number>", "Slow motion factor: 2, 4, or 8", "2").option("--fps <number>", "Target output FPS").option("--mode <mode>", "Speed/quality tradeoff: fast or quality", "quality").option("--dry-run", "Preview parameters without executing").action(async (videoPath, options) => {
454582
454582
  const startedAt = Date.now();
454583
454583
  try {
454584
454584
  if (options.output) {
@@ -454599,7 +454599,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
454599
454599
  videoPath: absPath,
454600
454600
  factor,
454601
454601
  fps: options.fps ? parseInt(options.fps) : void 0,
454602
- quality: options.quality
454602
+ mode: options.mode
454603
454603
  }
454604
454604
  }
454605
454605
  });
@@ -454622,7 +454622,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
454622
454622
  const [num, den] = fpsOut.trim().split("/").map(Number);
454623
454623
  const originalFps = num / (den || 1);
454624
454624
  const targetFps = options.fps ? parseInt(options.fps) : originalFps * factor;
454625
- const mi = options.quality === "fast" ? "mi_mode=mci" : "mi_mode=mci:mc_mode=aobmc:me_mode=bidir:vsbmc=1";
454625
+ const mi = options.mode === "fast" ? "mi_mode=mci" : "mi_mode=mci:mc_mode=aobmc:me_mode=bidir:vsbmc=1";
454626
454626
  spinner2.text = `Interpolating frames (${originalFps.toFixed(1)} \u2192 ${targetFps}fps)...`;
454627
454627
  await execSafe("ffmpeg", ["-i", absPath, "-filter:v", `minterpolate='${mi}:fps=${targetFps}',setpts=${factor}*PTS`, "-an", outputPath, "-y"], { timeout: 6e5 });
454628
454628
  spinner2.succeed(source_default.green(`Created ${factor}x slow motion`));
@@ -454660,7 +454660,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
454660
454660
  exitWithError(generalError(`Frame interpolation failed: ${msg}`));
454661
454661
  }
454662
454662
  });
454663
- editCommand.command("upscale").description("Upscale video resolution using AI or FFmpeg").argument("<video>", "Video file path").option("-o, --output <path>", "Output file path").option("-s, --scale <factor>", "Scale factor: 2 or 4", "2").option("-m, --model <model>", "Model: real-esrgan, topaz", "real-esrgan").option("--ffmpeg", "Use FFmpeg lanczos (free, no API)").option("-k, --api-key <key>", "Replicate API token (or set REPLICATE_API_TOKEN env)").option("--no-wait", "Start processing and return task ID without waiting").option("--dry-run", "Preview parameters without executing").action(async (videoPath, options) => {
454663
+ editCommand.command("upscale").description("Upscale video resolution using AI or FFmpeg").argument("<video>", "Video file path").option("-o, --output <path>", "Output file path").option("--scale <factor>", "Scale factor: 2 or 4", "2").option("-m, --model <model>", "Model: real-esrgan, topaz", "real-esrgan").option("--ffmpeg", "Use FFmpeg lanczos (free, no API)").option("-k, --api-key <key>", "Replicate API token (or set REPLICATE_API_TOKEN env)").option("--no-wait", "Start processing and return task ID without waiting").option("--dry-run", "Preview parameters without executing").action(async (videoPath, options) => {
454664
454664
  const startedAt = Date.now();
454665
454665
  try {
454666
454666
  if (options.output) {
@@ -455418,7 +455418,7 @@ Score each category 1-10. For fixable issues, provide an FFmpeg filter in autoFi
455418
455418
  return result;
455419
455419
  }
455420
455420
  function registerReviewCommand(aiCommand) {
455421
- aiCommand.command("review").description("Review video quality using Gemini AI and optionally auto-fix issues").argument("<video>", "Video file path").option("-s, --storyboard <path>", "Storyboard JSON file for context").option("--auto-apply", "Automatically apply fixable corrections").option("--verify", "Run verification pass after applying fixes").option("-m, --model <model>", "Gemini model: flash (default), flash-2.5, pro", "flash").option("-o, --output <path>", "Output video file path (for auto-apply)").option("--dry-run", "Preview parameters without executing").action(async (videoPath, options) => {
455421
+ aiCommand.command("review").description("Review video quality using Gemini AI and optionally auto-fix issues").argument("<source>", "Video file path").option("--storyboard <path>", "Storyboard JSON file for context").option("--auto-apply", "Automatically apply fixable corrections").option("--verify", "Run verification pass after applying fixes").option("-m, --model <model>", "Gemini model: flash (default), flash-2.5, pro", "flash").option("-o, --output <path>", "Output video file path (for auto-apply)").option("--dry-run", "Preview parameters without executing").action(async (videoPath, options) => {
455422
455422
  const startedAt = Date.now();
455423
455423
  try {
455424
455424
  if (options.output) {
@@ -455426,7 +455426,7 @@ function registerReviewCommand(aiCommand) {
455426
455426
  }
455427
455427
  if (options.dryRun) {
455428
455428
  outputSuccess({
455429
- command: "ai review",
455429
+ command: "inspect review",
455430
455430
  startedAt,
455431
455431
  dryRun: true,
455432
455432
  data: {
@@ -455704,7 +455704,7 @@ Use this image analysis to inform the color palette, typography placement, and o
455704
455704
  return { success: true, codePath, componentName: component.name, renderedPath: renderResult.outputPath };
455705
455705
  }
455706
455706
  function registerMotionCommand(aiCommand) {
455707
- aiCommand.command("motion").description("Generate motion graphics using Claude + Remotion (render & composite)").argument("<description>", "Natural language description of the motion graphic").option("-k, --api-key <key>", "Anthropic API key (or set ANTHROPIC_API_KEY env)").option("-o, --output <path>", "Output file path", "motion.tsx").option("-d, --duration <sec>", "Duration in seconds", "5").option("-w, --width <px>", "Width in pixels", "1920").option("-h, --height <px>", "Height in pixels", "1080").option("--fps <fps>", "Frame rate", "30").option("-s, --style <style>", "Style preset: minimal, corporate, playful, cinematic").option("--render", "Render the generated code with Remotion (output .webm)").option("--video <path>", "Base video to composite the motion graphic onto").option("--image <path>", "Image to analyze with Gemini \u2014 color/mood fed into Claude prompt").option("--from-tsx <path>", "Refine an existing TSX file instead of generating from scratch").option("-m, --model <alias>", "LLM model: sonnet (default), opus, gemini, gemini-3.1-pro", "sonnet").option("--dry-run", "Preview parameters without executing").action(async (description, options) => {
455707
+ aiCommand.command("motion").description("Generate motion graphics using Claude + Remotion (render & composite)").argument("<description>", "Natural language description of the motion graphic").option("-k, --api-key <key>", "Anthropic API key (or set ANTHROPIC_API_KEY env)").option("-o, --output <path>", "Output file path", "motion.tsx").option("-d, --duration <sec>", "Duration in seconds", "5").option("--width <px>", "Width in pixels", "1920").option("--height <px>", "Height in pixels", "1080").option("--fps <fps>", "Frame rate", "30").option("--style <style>", "Style preset: minimal, corporate, playful, cinematic").option("--render", "Render the generated code with Remotion (output .webm)").option("--video <path>", "Base video to composite the motion graphic onto").option("--image <path>", "Image to analyze with Gemini \u2014 color/mood fed into Claude prompt").option("--from-tsx <path>", "Refine an existing TSX file instead of generating from scratch").option("-m, --model <alias>", "LLM model: sonnet (default), opus, gemini, gemini-3.1-pro", "sonnet").option("--dry-run", "Preview parameters without executing").action(async (description, options) => {
455708
455708
  const startedAt = Date.now();
455709
455709
  try {
455710
455710
  if (options.output) {
@@ -456259,7 +456259,7 @@ async function executeStoryboard(options) {
456259
456259
  }
456260
456260
  }
456261
456261
  function registerStoryboardCommand(parent) {
456262
- parent.command("storyboard").description("Generate video storyboard from content using Claude").argument("<content>", "Content to analyze (text or file path)").option("-k, --api-key <key>", "Anthropic API key (or set ANTHROPIC_API_KEY env)").option("-o, --output <path>", "Output JSON file path").option("-d, --duration <sec>", "Target total duration in seconds").option("-f, --file", "Treat content argument as file path").option("-c, --creativity <level>", "Creativity level: low (default, consistent) or high (varied, unexpected)", "low").option("--dry-run", "Preview parameters without executing").action(async (content, options) => {
456262
+ parent.command("storyboard").description("Generate video storyboard from content using Claude").argument("<content>", "Content to analyze (text or file path)").option("-k, --api-key <key>", "Anthropic API key (or set ANTHROPIC_API_KEY env)").option("-o, --output <path>", "Output JSON file path").option("-d, --duration <sec>", "Target total duration in seconds").option("--file", "Treat content argument as file path").option("--creativity <level>", "Creativity level: low (default, consistent) or high (varied, unexpected)", "low").option("--dry-run", "Preview parameters without executing").action(async (content, options) => {
456263
456263
  const startedAt = Date.now();
456264
456264
  try {
456265
456265
  rejectControlChars(content);
@@ -456464,7 +456464,7 @@ async function executeSpeech(options) {
456464
456464
  }
456465
456465
  }
456466
456466
  function registerSpeechCommand(parent) {
456467
- parent.command("speech").alias("tts").description("Generate speech from text using ElevenLabs").argument("[text]", "Text to convert to speech (interactive if omitted)").option("-k, --api-key <key>", "ElevenLabs API key (or set ELEVENLABS_API_KEY env)").option("-o, --output <path>", "Output audio file path", "output.mp3").option("-v, --voice <id>", "Voice ID (default: Rachel)", "21m00Tcm4TlvDq8ikWAM").option("--list-voices", "List available voices").option("--fit-duration <seconds>", "Speed up audio to fit target duration (via FFmpeg atempo)", parseFloat).option("--dry-run", "Preview parameters without executing").action(async (text, options) => {
456467
+ parent.command("speech").alias("tts").description("Generate speech from text using ElevenLabs").argument("[text]", "Text to convert to speech (interactive if omitted)").option("-k, --api-key <key>", "ElevenLabs API key (or set ELEVENLABS_API_KEY env)").option("-o, --output <path>", "Output audio file path", "output.mp3").option("--voice <id>", "Voice ID (default: Rachel)", "21m00Tcm4TlvDq8ikWAM").option("--list-voices", "List available voices").option("--fit-duration <seconds>", "Speed up audio to fit target duration (via FFmpeg atempo)", parseFloat).option("--dry-run", "Preview parameters without executing").action(async (text, options) => {
456468
456468
  const startedAt = Date.now();
456469
456469
  try {
456470
456470
  if (!text) {
@@ -456674,7 +456674,7 @@ async function executeMusic(options) {
456674
456674
  }
456675
456675
  }
456676
456676
  function registerMusicCommand(parent) {
456677
- parent.command("music").description("Generate background music from a text prompt (ElevenLabs or Replicate MusicGen)").argument("<prompt>", "Description of the music to generate").option("-p, --provider <provider>", "Provider: elevenlabs (default, up to 10min), replicate (MusicGen, max 30s)", "elevenlabs").option("-k, --api-key <key>", "API key (or set ELEVENLABS_API_KEY / REPLICATE_API_TOKEN env)").option("-d, --duration <seconds>", "Duration in seconds (elevenlabs: 3-600, replicate: 1-30)", "8").option("--instrumental", "Force instrumental music, no vocals (ElevenLabs only)").option("-m, --melody <file>", "Reference melody audio file for conditioning (Replicate only)").option("--model <model>", "Model variant (Replicate only): large, stereo-large, melody-large, stereo-melody-large", "stereo-large").option("-o, --output <path>", "Output audio file path", "music.mp3").option("--no-wait", "Don't wait for generation to complete (Replicate async mode)").option("--dry-run", "Preview parameters without executing").action(async (prompt3, options) => {
456677
+ parent.command("music").description("Generate background music from a text prompt (ElevenLabs or Replicate MusicGen)").argument("<prompt>", "Description of the music to generate").option("-p, --provider <provider>", "Provider: elevenlabs (default, up to 10min), replicate (MusicGen, max 30s)", "elevenlabs").option("-k, --api-key <key>", "API key (or set ELEVENLABS_API_KEY / REPLICATE_API_TOKEN env)").option("-d, --duration <seconds>", "Duration in seconds (elevenlabs: 3-600, replicate: 1-30)", "8").option("--instrumental", "Force instrumental music, no vocals (ElevenLabs only)").option("--melody <file>", "Reference melody audio file for conditioning (Replicate only)").option("-m, --model <model>", "Model variant (Replicate only): large, stereo-large, melody-large, stereo-melody-large", "stereo-large").option("-o, --output <path>", "Output audio file path", "music.mp3").option("--no-wait", "Don't wait for generation to complete (Replicate async mode)").option("--dry-run", "Preview parameters without executing").action(async (prompt3, options) => {
456678
456678
  const startedAt = Date.now();
456679
456679
  try {
456680
456680
  rejectControlChars(prompt3);
@@ -456839,7 +456839,7 @@ import { resolve as resolve43, dirname as dirname26, basename as basename10, ext
456839
456839
  import { existsSync as existsSync47 } from "node:fs";
456840
456840
  import { writeFile as writeFile27, mkdir as mkdir19 } from "node:fs/promises";
456841
456841
  function registerThumbnailCommand(parent) {
456842
- parent.command("thumbnail").description("Generate video thumbnail (DALL-E) or extract best frame from video (Gemini)").argument("[description]", "Thumbnail description (for DALL-E generation)").option("-k, --api-key <key>", "API key (OpenAI for generation, Google for best-frame)").option("-o, --output <path>", "Output file path").option("-s, --style <style>", "Platform style: youtube, instagram, tiktok, twitter").option("--best-frame <video>", "Extract best thumbnail frame from video using Gemini AI").option("--prompt <prompt>", "Custom prompt for best-frame analysis").option("--model <model>", "Gemini model: flash, latest, pro (default: flash)", "flash").action(async (description, options) => {
456842
+ parent.command("thumbnail").description("Generate video thumbnail (DALL-E) or extract best frame from video (Gemini)").argument("[description]", "Thumbnail description (for DALL-E generation)").option("-k, --api-key <key>", "API key (OpenAI for generation, Google for best-frame)").option("-o, --output <path>", "Output file path").option("--style <style>", "Platform style: youtube, instagram, tiktok, twitter").option("--best-frame <video>", "Extract best thumbnail frame from video using Gemini AI").option("--prompt <prompt>", "Custom prompt for best-frame analysis").option("--model <model>", "Gemini model: flash, latest, pro (default: flash)", "flash").action(async (description, options) => {
456843
456843
  const startedAt = Date.now();
456844
456844
  try {
456845
456845
  if (description) rejectControlChars(description);
@@ -457005,7 +457005,7 @@ function getStatusColor(status) {
457005
457005
  }
457006
457006
  }
457007
457007
  function registerVideoStatusCommand(parent) {
457008
- parent.command("video-status", { hidden: true }).description("Check video generation status (Grok, Runway, or Kling)").argument("<task-id>", "Task ID from video generation").option("-p, --provider <provider>", "Provider: grok, runway, kling", "grok").option("-k, --api-key <key>", "API key (or set XAI_API_KEY / RUNWAY_API_SECRET / KLING_API_KEY env)").option("-t, --type <type>", "Task type: text2video or image2video (Kling only)", "text2video").option("-w, --wait", "Wait for completion").option("-o, --output <path>", "Download video when complete").action(async (taskId, options) => {
457008
+ parent.command("video-status", { hidden: true }).description("Check video generation status (Grok, Runway, or Kling)").argument("<task-id>", "Task ID from video generation").option("-p, --provider <provider>", "Provider: grok, runway, kling", "grok").option("-k, --api-key <key>", "API key (or set XAI_API_KEY / RUNWAY_API_SECRET / KLING_API_KEY env)").option("--type <type>", "Task type: text2video or image2video (Kling only)", "text2video").option("--wait", "Wait for completion").option("-o, --output <path>", "Download video when complete").action(async (taskId, options) => {
457009
457009
  const startedAt = Date.now();
457010
457010
  try {
457011
457011
  const provider = (options.provider || "grok").toLowerCase();
@@ -457238,7 +457238,7 @@ var init_video_status = __esm({
457238
457238
  import { resolve as resolve45 } from "node:path";
457239
457239
  import { writeFile as writeFile29 } from "node:fs/promises";
457240
457240
  function registerVideoExtendCommand(parent) {
457241
- parent.command("video-extend", { hidden: true }).description("Extend video duration (Kling by video ID, Veo by operation name)").argument("<id>", "Kling video ID or Veo operation name").option("-p, --provider <provider>", "Provider: kling, veo", "kling").option("-k, --api-key <key>", "API key (KLING_API_KEY or GOOGLE_API_KEY)").option("-o, --output <path>", "Output file path").option("--prompt <text>", "Continuation prompt").option("-d, --duration <sec>", "Duration: 5 or 10 (Kling), 4/6/8 (Veo)", "5").option("-n, --negative <prompt>", "Negative prompt (what to avoid, Kling only)").option("--veo-model <model>", "Veo model: 3.0, 3.1, 3.1-fast", "3.1").option("--no-wait", "Start extension and return task ID without waiting").option("--dry-run", "Preview parameters without executing").action(async (id, options) => {
457241
+ parent.command("video-extend", { hidden: true }).description("Extend video duration (Kling by video ID, Veo by operation name)").argument("<id>", "Kling video ID or Veo operation name").option("-p, --provider <provider>", "Provider: kling, veo", "kling").option("-k, --api-key <key>", "API key (KLING_API_KEY or GOOGLE_API_KEY)").option("-o, --output <path>", "Output file path").option("--prompt <text>", "Continuation prompt").option("-d, --duration <sec>", "Duration: 5 or 10 (Kling), 4/6/8 (Veo)", "5").option("--negative <prompt>", "Negative prompt (what to avoid, Kling only)").option("--veo-model <model>", "Veo model: 3.0, 3.1, 3.1-fast", "3.1").option("--no-wait", "Start extension and return task ID without waiting").option("--dry-run", "Preview parameters without executing").action(async (id, options) => {
457242
457242
  const startedAt = Date.now();
457243
457243
  try {
457244
457244
  const provider = (options.provider || "kling").toLowerCase();
@@ -457521,7 +457521,7 @@ import { resolve as resolve46, dirname as dirname27 } from "node:path";
457521
457521
  import { fileURLToPath as fileURLToPath4 } from "node:url";
457522
457522
  import { writeFile as writeFile30, mkdir as mkdir20 } from "node:fs/promises";
457523
457523
  function registerImageCommand(parent) {
457524
- parent.command("image").alias("img").description("Generate image using AI (Gemini, OpenAI gpt-image, Grok, or Runway)").argument("[prompt]", "Image description prompt (interactive if omitted)").option("-p, --provider <provider>", "Provider: openai (default when OPENAI_API_KEY set), gemini, grok, runway").option("-k, --api-key <key>", "API key (or set env: OPENAI_API_KEY, GOOGLE_API_KEY)").option("-o, --output <path>", "Output file path (downloads image)").option("-s, --size <size>", "Image size (openai: 1024x1024, 1536x1024, 1024x1536)", "1024x1024").option("-r, --ratio <ratio>", "Aspect ratio (gemini: 1:1, 1:4, 1:8, 4:1, 8:1, 16:9, 9:16, 3:4, 4:3, etc.)", "1:1").option("--quality <quality>", "Quality: standard, hd (openai only)", "standard").option("--style <style>", "Style: vivid, natural (openai only)", "vivid").option("-n, --count <n>", "Number of images to generate", "1").option("-m, --model <model>", "Model. Gemini: flash, 3.1-flash, latest, pro. OpenAI: 1.5 (default), 2 (gpt-image-2)").option("--dry-run", "Preview parameters without executing").addHelpText("after", `
457524
+ parent.command("image").alias("img").description("Generate image using AI (Gemini, OpenAI gpt-image, Grok, or Runway)").argument("[prompt]", "Image description prompt (interactive if omitted)").option("-p, --provider <provider>", "Provider: openai (default when OPENAI_API_KEY set), gemini, grok, runway").option("-k, --api-key <key>", "API key (or set env: OPENAI_API_KEY, GOOGLE_API_KEY)").option("-o, --output <path>", "Output file path (downloads image)").option("--size <size>", "Image size (openai: 1024x1024, 1536x1024, 1024x1536)", "1024x1024").option("-r, --ratio <ratio>", "Aspect ratio (gemini: 1:1, 1:4, 1:8, 4:1, 8:1, 16:9, 9:16, 3:4, 4:3, etc.)", "1:1").option("--quality <quality>", "Quality: standard, hd (openai only)", "standard").option("--style <style>", "Style: vivid, natural (openai only)", "vivid").option("--count <n>", "Number of images to generate", "1").option("-m, --model <model>", "Model. Gemini: flash, 3.1-flash, latest, pro. OpenAI: 1.5 (default), 2 (gpt-image-2)").option("--dry-run", "Preview parameters without executing").addHelpText("after", `
457525
457525
  Examples:
457526
457526
  $ vibe generate image "a sunset over the ocean" -o sunset.png
457527
457527
  $ vibe gen img "logo design" -o logo.png -p openai
@@ -457549,6 +457549,15 @@ Examples:
457549
457549
  if (options.output) {
457550
457550
  validateOutputPath(options.output);
457551
457551
  }
457552
+ if (options.count !== void 0) {
457553
+ const n = parseInt(options.count, 10);
457554
+ if (!Number.isFinite(n) || n < 1 || n > 10) {
457555
+ exitWithError(usageError(
457556
+ `Invalid --count: ${options.count}`,
457557
+ "Must be an integer between 1 and 10."
457558
+ ));
457559
+ }
457560
+ }
457552
457561
  const imageRegistry = getProvidersFor("image");
457553
457562
  const validProviders = [...imageRegistry.map((p) => p.name), "runway"];
457554
457563
  const providerEnvMap = Object.fromEntries(
@@ -459664,7 +459673,7 @@ function registerVideoCommand(parent) {
459664
459673
  "-d, --duration <sec>",
459665
459674
  "Duration in seconds. Seedance accepts 4-15 (`fal` alias supported); Kling accepts 5 or 10; Veo maps to 6 or 8.",
459666
459675
  "5"
459667
- ).option("-r, --ratio <ratio>", "Aspect ratio: 16:9, 9:16, or 1:1 (auto-detected from image if omitted)").option("-s, --seed <number>", "Random seed for reproducibility (Runway only)").option("-m, --mode <mode>", "Generation mode: std or pro (Kling only)", "std").option("--seedance-model <model>", "Seedance variant: quality or fast (fal.ai only)", "quality").option("-n, --negative <prompt>", "Negative prompt - what to avoid (Kling/Veo)").option("--resolution <res>", "Video resolution: 720p, 1080p, 4k (Veo only)").option("--last-frame <path>", "Last frame image for frame interpolation (Veo only)").option("--ref-images <paths...>", "Reference images for character consistency (Veo 3.1 only, max 3)").option("--person <mode>", "Person generation: allow_all, allow_adult (Veo only)").option("--veo-model <model>", "Veo model: 3.0, 3.1, 3.1-fast (default: 3.1-fast)", "3.1-fast").option("--runway-model <model>", "Runway model: gen4.5 (default, text+image-to-video), gen4_turbo (image-to-video only)", "gen4.5").option("--no-wait", "Start generation and return task ID without waiting").option("--dry-run", "Preview parameters without executing").addHelpText("after", `
459676
+ ).option("-r, --ratio <ratio>", "Aspect ratio: 16:9, 9:16, or 1:1 (auto-detected from image if omitted)").option("--seed <number>", "Random seed for reproducibility (Runway only)").option("--mode <mode>", "Generation mode: std or pro (Kling only)", "std").option("--seedance-model <model>", "Seedance variant: quality or fast (fal.ai only)", "quality").option("--negative <prompt>", "Negative prompt - what to avoid (Kling/Veo)").option("--resolution <res>", "Video resolution: 720p, 1080p, 4k (Veo only)").option("--last-frame <path>", "Last frame image for frame interpolation (Veo only)").option("--ref-images <paths...>", "Reference images for character consistency (Veo 3.1 only, max 3)").option("--person <mode>", "Person generation: allow_all, allow_adult (Veo only)").option("--veo-model <model>", "Veo model: 3.0, 3.1, 3.1-fast (default: 3.1-fast)", "3.1-fast").option("--runway-model <model>", "Runway model: gen4.5 (default, text+image-to-video), gen4_turbo (image-to-video only)", "gen4.5").option("--no-wait", "Start generation and return task ID without waiting").option("--dry-run", "Preview parameters without executing").addHelpText("after", `
459668
459677
  Examples:
459669
459678
  $ vibe generate video "dancing cat" -o cat.mp4 # Seedance when FAL_KEY is set
459670
459679
  $ vibe gen vid "cinematic city timelapse" -o city.mp4 -p seedance # Seedance via fal.ai
@@ -459693,6 +459702,15 @@ Examples:
459693
459702
  if (options.output) {
459694
459703
  validateOutputPath(options.output);
459695
459704
  }
459705
+ if (options.duration !== void 0) {
459706
+ const d = parseFloat(options.duration);
459707
+ if (!Number.isFinite(d) || d <= 0 || d > 60) {
459708
+ exitWithError(usageError(
459709
+ `Invalid --duration: ${options.duration}`,
459710
+ "Must be a positive number \u2264 60 seconds."
459711
+ ));
459712
+ }
459713
+ }
459696
459714
  const validProviders = ["runway", "kling", "veo", "grok", "seedance", "fal"];
459697
459715
  const videoEnvMap = {
459698
459716
  grok: "XAI_API_KEY",
@@ -460670,15 +460688,14 @@ var init_detect = __esm({
460670
460688
  "use strict";
460671
460689
  init_esm();
460672
460690
  init_source();
460673
- init_ora();
460674
460691
  init_engine();
460675
460692
  init_exec_safe();
460676
460693
  init_output();
460677
460694
  init_validate();
460678
460695
  detectCommand = new Command("detect").description("Auto-detect scenes, beats, and silences in media");
460679
- detectCommand.command("scenes").description("Detect scene changes in video").argument("<video>", "Video file path").option("-t, --threshold <value>", "Scene change threshold (0-1)", "0.3").option("-o, --output <path>", "Output JSON file with timestamps").option("-p, --project <path>", "Add scenes as clips to project").option("--dry-run", "Preview parameters without executing").action(async (videoPath, options) => {
460696
+ detectCommand.command("scenes").description("Detect scene changes in video").argument("<video>", "Video file path").option("--threshold <value>", "Scene change threshold (0-1)", "0.3").option("-o, --output <path>", "Output JSON file with timestamps").option("--project <path>", "Add scenes as clips to project").option("--dry-run", "Preview parameters without executing").action(async (videoPath, options) => {
460680
460697
  const startedAt = Date.now();
460681
- const spinner2 = ora("Detecting scenes...").start();
460698
+ const spinner2 = spinner("Detecting scenes...").start();
460682
460699
  try {
460683
460700
  if (options.output) {
460684
460701
  validateOutputPath(options.output);
@@ -460795,9 +460812,9 @@ var init_detect = __esm({
460795
460812
  exitWithError(generalError(`Scene detection failed: ${msg}`));
460796
460813
  }
460797
460814
  });
460798
- detectCommand.command("silence").description("Detect silence in audio/video").argument("<media>", "Media file path").option("-n, --noise <dB>", "Noise threshold in dB", "-30").option("-d, --duration <sec>", "Minimum silence duration", "0.5").option("-o, --output <path>", "Output JSON file with timestamps").option("--dry-run", "Preview parameters without executing").action(async (mediaPath, options) => {
460815
+ detectCommand.command("silence").description("Detect silence in audio/video").argument("<media>", "Media file path").option("--noise <dB>", "Noise threshold in dB", "-30").option("-d, --duration <sec>", "Minimum silence duration", "0.5").option("-o, --output <path>", "Output JSON file with timestamps").option("--dry-run", "Preview parameters without executing").action(async (mediaPath, options) => {
460799
460816
  const startedAt = Date.now();
460800
- const spinner2 = ora("Detecting silence...").start();
460817
+ const spinner2 = spinner("Detecting silence...").start();
460801
460818
  try {
460802
460819
  if (options.output) {
460803
460820
  validateOutputPath(options.output);
@@ -460883,7 +460900,7 @@ var init_detect = __esm({
460883
460900
  });
460884
460901
  detectCommand.command("beats").description("Detect beats in audio (for music sync)").argument("<audio>", "Audio file path").option("-o, --output <path>", "Output JSON file with timestamps").option("--dry-run", "Preview parameters without executing").action(async (audioPath, options) => {
460885
460902
  const startedAt = Date.now();
460886
- const spinner2 = ora("Detecting beats...").start();
460903
+ const spinner2 = spinner("Detecting beats...").start();
460887
460904
  try {
460888
460905
  if (options.output) {
460889
460906
  validateOutputPath(options.output);
@@ -462007,7 +462024,7 @@ function validatePreset(value) {
462007
462024
  }
462008
462025
  return value;
462009
462026
  }
462010
- var sceneCommand = new Command("scene").description("Lower-level scene authoring (add, lint, styles). For project flow use `vibe init` / `vibe build` / `vibe render`.").addHelpText("after", `
462027
+ var sceneCommand = new Command("scene").description("Lower-level scene authoring (add, lint, list-styles). For project flow use `vibe init` / `vibe build` / `vibe render`.").addHelpText("after", `
462011
462028
  Examples:
462012
462029
  $ vibe scene add intro --style announcement \\
462013
462030
  --headline "Welcome to VibeFrame" # Headline-only scene
@@ -466061,18 +466078,18 @@ async function runExport(projectPath, outputPath, options = {}) {
466061
466078
  };
466062
466079
  }
466063
466080
  }
466064
- var exportCommand = new Command("export").description("Export project to video file").argument("<project>", "Project file path").option("-o, --output <path>", "Output file path").option("-f, --format <format>", "Output format (mp4, webm, mov, gif)", "mp4").option(
466065
- "-p, --preset <preset>",
466081
+ var exportCommand = new Command("export").description("Export project to video file").argument("<project>", "Project file path").option("-o, --output <path>", "Output file path").option("--format <format>", "Output format (mp4, webm, mov, gif)", "mp4").option(
466082
+ "--preset <preset>",
466066
466083
  "Quality preset (draft, standard, high, ultra)",
466067
466084
  "standard"
466068
- ).option("-y, --overwrite", "Overwrite output file if exists", false).option("-g, --gap-fill <strategy>", "Gap filling strategy (black, extend)", "extend").option("--backend <name>", "Render backend: ffmpeg (default) | hyperframes (experimental)", "ffmpeg").option("--bitrate <value>", "Video bitrate (e.g. 5000k, 8M) \u2014 overrides preset").option("--fps <number>", "Frames per second (e.g. 24, 30, 60) \u2014 overrides preset").option("--resolution <WxH>", "Output resolution (e.g. 1920x1080) \u2014 overrides preset").option("--codec <codec>", "Video codec: h264 (default) | h265 | vp9 \u2014 overrides preset").option("--dry-run", "Preview parameters without executing").addHelpText("after", `
466085
+ ).option("--overwrite", "Overwrite output file if exists", false).option("--gap-fill <strategy>", "Gap filling strategy (black, extend)", "extend").option("--backend <name>", "Render backend: ffmpeg (default) | hyperframes (experimental)", "ffmpeg").option("--bitrate <value>", "Video bitrate (e.g. 5000k, 8M) \u2014 overrides preset").option("--fps <number>", "Frames per second (e.g. 24, 30, 60) \u2014 overrides preset").option("--resolution <WxH>", "Output resolution (e.g. 1920x1080) \u2014 overrides preset").option("--codec <codec>", "Video codec: h264 (default) | h265 | vp9 \u2014 overrides preset").option("--dry-run", "Preview parameters without executing").addHelpText("after", `
466069
466086
  Examples:
466070
466087
  $ vibe export project.vibe.json -o output.mp4
466071
- $ vibe export project.vibe.json -o output.mp4 -p high -y
466072
- $ vibe export project.vibe.json -o output.webm -f webm
466073
- $ vibe export project.vibe.json -o output.gif -f gif
466088
+ $ vibe export project.vibe.json -o output.mp4 --preset high --overwrite
466089
+ $ vibe export project.vibe.json -o output.webm --format webm
466090
+ $ vibe export project.vibe.json -o output.gif --format gif
466074
466091
  $ vibe export project.vibe.json -o out.mp4 --bitrate 5000k --fps 24 --codec h265
466075
- $ vibe export project.vibe.json -o out.mp4 -p high --fps 60
466092
+ $ vibe export project.vibe.json -o out.mp4 --preset high --fps 60
466076
466093
 
466077
466094
  Cost: Free (no API keys needed). Requires FFmpeg.
466078
466095
  GIF format: 15fps, no audio, looping. Good for previews and sharing.
@@ -467713,28 +467730,31 @@ narration / backdrop intent.
467713
467730
  ## Quick-draft path
467714
467731
 
467715
467732
  \`\`\`bash
467716
- vibe scene init my-promo -r 16:9 -d 30
467733
+ vibe init my-promo -r 16:9 -d 30
467717
467734
  vibe scene add intro --style announcement \\
467718
467735
  --headline "Ship videos, not clicks"
467719
467736
  vibe scene lint
467720
467737
  vibe render my-promo
467721
467738
  \`\`\`
467722
467739
 
467723
- \`vibe scene init\` is **idempotent** \u2014 running it on an existing
467724
- Hyperframes directory merges \`hyperframes.json\` instead of clobbering it.
467725
- Safe to invoke on user-provided projects.
467740
+ \`vibe init\` is **idempotent** \u2014 running it on an existing Hyperframes
467741
+ directory merges \`hyperframes.json\` instead of clobbering it. Safe to
467742
+ invoke on user-provided projects.
467726
467743
 
467727
467744
  ## Subcommands
467728
467745
 
467729
467746
  \`\`\`bash
467730
- vibe scene init <dir> [-r 16:9|9:16|1:1|4:5] [-d <sec>] [--visual-style "<name>"]
467747
+ # Project flow (top-level \u2014 preferred entry points)
467748
+ vibe init <dir> [-r 16:9|9:16|1:1|4:5] [-d <sec>] [--visual-style "<name>"]
467749
+ vibe build [<dir>] [--mode agent|batch|auto] # H3 dispatch
467750
+ vibe render [<dir>] [--fps 30] [--quality standard] [--format mp4]
467751
+
467752
+ # Lower-level scene authoring
467731
467753
  vibe scene list-styles [<name>] # list / show vendored visual identities
467732
467754
  vibe scene install-skill [<dir>] [--host all] # retroactive composition-rules install
467733
467755
  vibe scene add <name> --style <preset> [...]
467734
467756
  vibe scene compose-prompts [<dir>] [--beat <id>] # H2: emit plan, no LLM call
467735
467757
  vibe scene lint [<root>] [--json] [--fix]
467736
- vibe scene render [<root>] [--fps 30] [--quality standard] [--format mp4]
467737
- vibe build [<dir>] [--mode agent|batch|auto] # H3 dispatch
467738
467758
  \`\`\`
467739
467759
 
467740
467760
  ## Style presets (for \`vibe scene add --style\`)
@@ -467788,7 +467808,7 @@ and surface the error to the user.
467788
467808
  | Generate narration + image, then author scene | \`vibe scene add\` |
467789
467809
  | Generate a full scenes project from a STORYBOARD | \`vibe build\` |
467790
467810
  | Hand-tweak a single scene's animation | edit \`compositions/<file>.html\` directly |
467791
- | Render the project | \`vibe render\` *or* \`vibe scene render\` for lower-level control |
467811
+ | Render the project | \`vibe render\` (one canonical entry point) |
467792
467812
  | Lint | \`vibe scene lint\` *or* \`npx hyperframes lint\` (equivalent) |
467793
467813
 
467794
467814
  The \`vibe\` CLI adds asset generation, AI orchestration, and pipeline
@@ -467916,12 +467936,12 @@ var META = {
467916
467936
  ],
467917
467937
  relatedCommands: [
467918
467938
  "vibe init",
467939
+ "vibe build",
467940
+ "vibe render",
467919
467941
  "vibe scene list-styles",
467920
467942
  "vibe scene install-skill",
467921
467943
  "vibe scene compose-prompts",
467922
- "vibe build",
467923
467944
  "vibe scene lint",
467924
- "vibe scene render",
467925
467945
  "vibe scene add"
467926
467946
  ]
467927
467947
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibeframe/mcp-server",
3
- "version": "0.77.0",
3
+ "version": "0.79.0",
4
4
  "description": "VibeFrame MCP Server - AI-native video editing via Model Context Protocol",
5
5
  "type": "module",
6
6
  "bin": {
@@ -57,8 +57,8 @@
57
57
  "tsx": "^4.21.0",
58
58
  "typescript": "^5.3.3",
59
59
  "vitest": "^1.2.2",
60
- "@vibeframe/cli": "0.77.0",
61
- "@vibeframe/core": "0.77.0"
60
+ "@vibeframe/cli": "0.79.0",
61
+ "@vibeframe/core": "0.79.0"
62
62
  },
63
63
  "engines": {
64
64
  "node": ">=20"