@vibeframe/cli 0.105.2 → 0.106.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.
@@ -14543,6 +14543,39 @@ var init_whisper = __esm({
14543
14543
  }
14544
14544
  });
14545
14545
 
14546
+ // ../ai-providers/dist/gemini/gemini-models.js
14547
+ function isGeminiTextModelAlias(model) {
14548
+ return Object.prototype.hasOwnProperty.call(GEMINI_TEXT_MODEL_ALIASES, model);
14549
+ }
14550
+ function resolveGeminiTextModel(model) {
14551
+ const trimmed = model?.trim();
14552
+ if (!trimmed)
14553
+ return GEMINI_DEFAULT_TEXT_MODEL;
14554
+ if (isGeminiTextModelAlias(trimmed))
14555
+ return GEMINI_TEXT_MODEL_ALIASES[trimmed];
14556
+ if (trimmed.startsWith("gemini-"))
14557
+ return trimmed;
14558
+ return GEMINI_DEFAULT_TEXT_MODEL;
14559
+ }
14560
+ var GEMINI_DEFAULT_TEXT_MODEL, GEMINI_AGENT_DEFAULT_TEXT_MODEL, GEMINI_TEXT_MODEL_ALIASES, GEMINI_TEXT_MODEL_HELP;
14561
+ var init_gemini_models = __esm({
14562
+ "../ai-providers/dist/gemini/gemini-models.js"() {
14563
+ "use strict";
14564
+ GEMINI_DEFAULT_TEXT_MODEL = "gemini-3.5-flash";
14565
+ GEMINI_AGENT_DEFAULT_TEXT_MODEL = "gemini-2.5-flash";
14566
+ GEMINI_TEXT_MODEL_ALIASES = {
14567
+ flash: GEMINI_DEFAULT_TEXT_MODEL,
14568
+ latest: GEMINI_DEFAULT_TEXT_MODEL,
14569
+ "flash-3.5": GEMINI_DEFAULT_TEXT_MODEL,
14570
+ "flash-3": "gemini-3-flash-preview",
14571
+ "flash-2.5": "gemini-2.5-flash",
14572
+ pro: "gemini-2.5-pro",
14573
+ "pro-3.1": "gemini-3.1-pro-preview"
14574
+ };
14575
+ GEMINI_TEXT_MODEL_HELP = "flash/latest (Gemini 3.5 Flash), flash-3.5, flash-3, flash-2.5, pro, pro-3.1, or a full gemini-* model ID";
14576
+ }
14577
+ });
14578
+
14546
14579
  // ../ai-providers/dist/gemini/gemini-motion.js
14547
14580
  function buildMotionSystemPrompt(width, height, fps, duration, durationInFrames, stylePreset) {
14548
14581
  return `You are a world-class broadcast motion graphics designer (like Apple Keynote, Netflix intros, ESPN graphics). Create STUNNING, jaw-dropping overlays that make viewers go "wow".
@@ -14696,7 +14729,7 @@ async function generateMotion(api, description, options = {}) {
14696
14729
  const duration = options.duration || 5;
14697
14730
  const durationInFrames = Math.round(duration * fps);
14698
14731
  const stylePreset = options.style || "modern and clean";
14699
- const modelId = options.model || "gemini-2.5-pro";
14732
+ const modelId = resolveGeminiTextModel(options.model);
14700
14733
  let systemPrompt = buildMotionSystemPrompt(width, height, fps, duration, durationInFrames, stylePreset);
14701
14734
  if (options.videoContext) {
14702
14735
  const sourceLabel = options.sourceType === "image" ? "IMAGE" : "VIDEO";
@@ -14777,7 +14810,7 @@ async function refineMotion(api, existingCode, instructions, options = {}) {
14777
14810
  const fps = options.fps || 30;
14778
14811
  const duration = options.duration || 5;
14779
14812
  const durationInFrames = Math.round(duration * fps);
14780
- const modelId = options.model || "gemini-2.5-pro";
14813
+ const modelId = resolveGeminiTextModel(options.model);
14781
14814
  const systemPrompt = `You are a world-class broadcast motion graphics designer. Modify the provided Remotion component based on instructions.
14782
14815
 
14783
14816
  CANVAS: ${width}\xD7${height}px | ${fps}fps | ${durationInFrames} frames (${duration}s)
@@ -14872,9 +14905,18 @@ var GEMINI_MOTION_MODELS, STYLE_USER_INSTRUCTIONS;
14872
14905
  var init_gemini_motion = __esm({
14873
14906
  "../ai-providers/dist/gemini/gemini-motion.js"() {
14874
14907
  "use strict";
14908
+ init_gemini_models();
14875
14909
  GEMINI_MOTION_MODELS = {
14910
+ gemini: GEMINI_DEFAULT_TEXT_MODEL,
14911
+ flash: GEMINI_DEFAULT_TEXT_MODEL,
14912
+ latest: GEMINI_DEFAULT_TEXT_MODEL,
14913
+ "flash-3.5": GEMINI_DEFAULT_TEXT_MODEL,
14914
+ "flash-3": "gemini-3-flash-preview",
14915
+ "flash-2.5": "gemini-2.5-flash",
14876
14916
  pro: "gemini-2.5-pro",
14877
- "3.1-pro": "gemini-3.1-pro-preview"
14917
+ "2.5-pro": "gemini-2.5-pro",
14918
+ "3.1-pro": "gemini-3.1-pro-preview",
14919
+ "pro-3.1": "gemini-3.1-pro-preview"
14878
14920
  };
14879
14921
  STYLE_USER_INSTRUCTIONS = {
14880
14922
  minimal: "CENTER-ALIGNED pure typography. Thin lines + large title + subtitle. NO glass panels, NO bokeh, NO dark bars. Breathing pulse during hold.",
@@ -14997,7 +15039,7 @@ async function analyzeContent(api, content, targetDuration, options) {
14997
15039
  maxOutputTokens: 4096
14998
15040
  }
14999
15041
  };
15000
- const response = await fetch(`${api.baseUrl}/models/gemini-2.5-flash:generateContent?key=${api.apiKey}`, {
15042
+ const response = await fetch(`${api.baseUrl}/models/${GEMINI_DEFAULT_TEXT_MODEL}:generateContent?key=${api.apiKey}`, {
15001
15043
  method: "POST",
15002
15044
  headers: { "Content-Type": "application/json" },
15003
15045
  body: JSON.stringify(payload)
@@ -15030,6 +15072,7 @@ var init_gemini_storyboard = __esm({
15030
15072
  "../ai-providers/dist/gemini/gemini-storyboard.js"() {
15031
15073
  "use strict";
15032
15074
  init_storyboard_prompt();
15075
+ init_gemini_models();
15033
15076
  }
15034
15077
  });
15035
15078
 
@@ -15068,6 +15111,7 @@ var init_GeminiProvider = __esm({
15068
15111
  "../ai-providers/dist/gemini/GeminiProvider.js"() {
15069
15112
  "use strict";
15070
15113
  init_gemini_motion();
15114
+ init_gemini_models();
15071
15115
  init_gemini_storyboard();
15072
15116
  init_http();
15073
15117
  MODEL_MAP = {
@@ -15576,7 +15620,7 @@ var init_GeminiProvider = __esm({
15576
15620
  error: "Google API key not configured"
15577
15621
  };
15578
15622
  }
15579
- const modelId = options.model || "gemini-3-flash-preview";
15623
+ const modelId = resolveGeminiTextModel(options.model);
15580
15624
  try {
15581
15625
  let videoPart;
15582
15626
  if (typeof videoData === "string") {
@@ -15683,7 +15727,7 @@ var init_GeminiProvider = __esm({
15683
15727
  error: "Google API key not configured"
15684
15728
  };
15685
15729
  }
15686
- const modelId = options.model || "gemini-3-flash-preview";
15730
+ const modelId = resolveGeminiTextModel(options.model);
15687
15731
  try {
15688
15732
  const buffers = Array.isArray(imageData) ? imageData : [imageData];
15689
15733
  const imageParts = buffers.map((buf) => ({
@@ -15785,7 +15829,7 @@ Example response:
15785
15829
  [{"type":"trim","description":"Trim intro to 3 seconds","clipIds":["clip-1"],"params":{"newDuration":3},"confidence":0.9}]
15786
15830
 
15787
15831
  Respond with ONLY the JSON array, no other text.`;
15788
- const response = await fetch(`${this.baseUrl}/models/gemini-2.5-flash:generateContent?key=${this.apiKey}`, {
15832
+ const response = await fetch(`${this.baseUrl}/models/${GEMINI_DEFAULT_TEXT_MODEL}:generateContent?key=${this.apiKey}`, {
15789
15833
  method: "POST",
15790
15834
  headers: {
15791
15835
  "Content-Type": "application/json"
@@ -15907,6 +15951,7 @@ var init_gemini = __esm({
15907
15951
  "use strict";
15908
15952
  init_GeminiProvider();
15909
15953
  init_gemini_motion();
15954
+ init_gemini_models();
15910
15955
  init_define_provider();
15911
15956
  defineProvider({
15912
15957
  id: "gemini",
@@ -25270,6 +25315,10 @@ __export(dist_exports2, {
25270
25315
  ClaudeProvider: () => ClaudeProvider,
25271
25316
  ElevenLabsProvider: () => ElevenLabsProvider,
25272
25317
  FalProvider: () => FalProvider,
25318
+ GEMINI_AGENT_DEFAULT_TEXT_MODEL: () => GEMINI_AGENT_DEFAULT_TEXT_MODEL,
25319
+ GEMINI_DEFAULT_TEXT_MODEL: () => GEMINI_DEFAULT_TEXT_MODEL,
25320
+ GEMINI_TEXT_MODEL_ALIASES: () => GEMINI_TEXT_MODEL_ALIASES,
25321
+ GEMINI_TEXT_MODEL_HELP: () => GEMINI_TEXT_MODEL_HELP,
25273
25322
  GeminiProvider: () => GeminiProvider,
25274
25323
  GrokProvider: () => GrokProvider,
25275
25324
  KNOWN_VOICES: () => KNOWN_VOICES,
@@ -25300,6 +25349,7 @@ __export(dist_exports2, {
25300
25349
  getProvidersFor: () => getProvidersFor,
25301
25350
  getSetupProviders: () => getSetupProviders,
25302
25351
  grokProvider: () => grokProvider,
25352
+ isGeminiTextModelAlias: () => isGeminiTextModelAlias,
25303
25353
  klingProvider: () => klingProvider,
25304
25354
  kokoroProvider: () => kokoroProvider,
25305
25355
  ollamaProvider: () => ollamaProvider,
@@ -25307,6 +25357,7 @@ __export(dist_exports2, {
25307
25357
  openaiProvider: () => openaiProvider,
25308
25358
  providerRegistry: () => providerRegistry,
25309
25359
  replicateProvider: () => replicateProvider,
25360
+ resolveGeminiTextModel: () => resolveGeminiTextModel,
25310
25361
  resolveVoiceId: () => resolveVoiceId,
25311
25362
  runwayProvider: () => runwayProvider,
25312
25363
  whisperProvider: () => whisperProvider
@@ -28479,6 +28530,7 @@ var MODEL_SETTINGS_BY_PROVIDER, ComposeBeatError, defaultCallLLM, CONFIG_PROVIDE
28479
28530
  var init_compose_scenes_skills = __esm({
28480
28531
  "src/commands/_shared/compose-scenes-skills.ts"() {
28481
28532
  "use strict";
28533
+ init_dist2();
28482
28534
  init_config();
28483
28535
  init_bundle();
28484
28536
  init_scene_lint();
@@ -28500,12 +28552,12 @@ var init_compose_scenes_skills = __esm({
28500
28552
  medium: { model: "gpt-5", maxTokens: 6e3, costPerMTokIn: 1.25, costPerMTokOut: 10 },
28501
28553
  high: { model: "gpt-5", maxTokens: 8e3, costPerMTokIn: 1.25, costPerMTokOut: 10 }
28502
28554
  },
28503
- // Google Gemini 2.5 Pro — ~2.6× cheaper than Claude per spike, ~20 s/beat.
28504
- // Strong instruction-following on the Hyperframes skill bundle.
28555
+ // Google Gemini 3.5 Flash — current one-shot Gemini default for
28556
+ // analysis/composition. Keep agent loops on their explicit 2.5 Flash default.
28505
28557
  gemini: {
28506
- low: { model: "gemini-2.5-pro", maxTokens: 4e3, costPerMTokIn: 1.25, costPerMTokOut: 5 },
28507
- medium: { model: "gemini-2.5-pro", maxTokens: 6e3, costPerMTokIn: 1.25, costPerMTokOut: 5 },
28508
- high: { model: "gemini-2.5-pro", maxTokens: 8e3, costPerMTokIn: 1.25, costPerMTokOut: 5 }
28558
+ low: { model: GEMINI_DEFAULT_TEXT_MODEL, maxTokens: 4e3, costPerMTokIn: 1.5, costPerMTokOut: 9 },
28559
+ medium: { model: GEMINI_DEFAULT_TEXT_MODEL, maxTokens: 6e3, costPerMTokIn: 1.5, costPerMTokOut: 9 },
28560
+ high: { model: GEMINI_DEFAULT_TEXT_MODEL, maxTokens: 8e3, costPerMTokIn: 1.5, costPerMTokOut: 9 }
28509
28561
  }
28510
28562
  };
28511
28563
  ComposeBeatError = class extends Error {
@@ -35178,12 +35230,7 @@ async function detectSilencePeriodsWithGemini(videoPath, minDuration, options) {
35178
35230
  const gemini = new GeminiProvider();
35179
35231
  await gemini.initialize({ apiKey: geminiApiKey });
35180
35232
  const videoBuffer = await readFile18(videoPath);
35181
- const modelMap = {
35182
- flash: "gemini-3-flash-preview",
35183
- "flash-2.5": "gemini-2.5-flash",
35184
- pro: "gemini-2.5-pro"
35185
- };
35186
- const modelId = options.model ? modelMap[options.model] || modelMap.flash : void 0;
35233
+ const modelId = options.model ? resolveGeminiTextModel(options.model) : void 0;
35187
35234
  const prompt3 = `Analyze this video and identify all silent or dead segments where there is NO meaningful content.
35188
35235
 
35189
35236
  Detect these as silent/dead segments:
@@ -38635,7 +38682,7 @@ Use this image analysis to inform the color palette, typography placement, and o
38635
38682
  6. Recommended animation timing and entrance/exit style
38636
38683
  7. Any moments where overlays should pause, fade, or stay minimal`;
38637
38684
  const analysisResult = await gemini.analyzeVideo(videoBuffer, analysisPrompt, {
38638
- model: "gemini-2.5-flash",
38685
+ model: GEMINI_DEFAULT_TEXT_MODEL,
38639
38686
  fps: 1,
38640
38687
  lowResolution: true
38641
38688
  });
@@ -38804,7 +38851,7 @@ function registerMotionCommand(aiCommand) {
38804
38851
  "auto"
38805
38852
  ).option("--understanding-prompt <text>", "Custom prompt for --video understanding").option("--from-tsx <path>", "Refine an existing TSX file instead of generating from scratch").option(
38806
38853
  "-m, --model <alias>",
38807
- "LLM model: sonnet (default), opus, gemini, gemini-3.1-pro",
38854
+ "LLM model: sonnet (default), opus, gemini, gemini-2.5-pro, gemini-3.1-pro",
38808
38855
  "sonnet"
38809
38856
  ).option("--dry-run", "Preview parameters without executing").action(async (description, options) => {
38810
38857
  const startedAt = Date.now();
@@ -38948,7 +38995,8 @@ var init_ai_motion = __esm({
38948
38995
  sonnet: { provider: "claude", modelId: "claude-sonnet-4-6" },
38949
38996
  opus: { provider: "claude", modelId: "claude-opus-4-7" },
38950
38997
  "opus-4-6": { provider: "claude", modelId: "claude-opus-4-6" },
38951
- gemini: { provider: "gemini", modelId: "gemini-2.5-pro" },
38998
+ gemini: { provider: "gemini", modelId: GEMINI_DEFAULT_TEXT_MODEL },
38999
+ "gemini-2.5-pro": { provider: "gemini", modelId: "gemini-2.5-pro" },
38952
39000
  "gemini-3.1-pro": { provider: "gemini", modelId: "gemini-3.1-pro-preview" }
38953
39001
  };
38954
39002
  }
@@ -39529,7 +39577,7 @@ async function executeMotionOverlay(options) {
39529
39577
  };
39530
39578
  }
39531
39579
  function registerMotionOverlayCommand(parent) {
39532
- parent.command("motion-overlay").description("Apply designed motion graphics overlays to an existing video").argument("<video>", "Video file path").argument("[description]", "Motion overlay description (omit when using --asset)").option("--asset <path>", "User-provided .json/.lottie animation to overlay").option("-o, --output <path>", "Output video file path").option("-d, --duration <sec>", "Overlay/render duration in seconds").option("--start <sec>", "Overlay start time in seconds", "0").option("--style <style>", "Style preset for generated overlays: minimal, corporate, playful, cinematic").option("-m, --model <alias>", "LLM model for generated overlays: sonnet, opus, gemini, gemini-3.1-pro", "sonnet").option("--understand <mode>", "Analyze video before generated overlay: auto, off, required", "auto").option("--understanding-prompt <text>", "Custom prompt for video understanding").option("--position <position>", "Lottie position: full, center, top-left, top-right, bottom-left, bottom-right", "full").option("--scale <number>", "Lottie overlay scale (0.01-2)").option("--opacity <number>", "Lottie overlay opacity (0-1)", "1").option("--loop", "Loop Lottie overlay", true).option("--no-loop", "Do not loop Lottie overlay").option("--dry-run", "Preview parameters without executing").action(async (videoPath, description, options) => {
39580
+ parent.command("motion-overlay").description("Apply designed motion graphics overlays to an existing video").argument("<video>", "Video file path").argument("[description]", "Motion overlay description (omit when using --asset)").option("--asset <path>", "User-provided .json/.lottie animation to overlay").option("-o, --output <path>", "Output video file path").option("-d, --duration <sec>", "Overlay/render duration in seconds").option("--start <sec>", "Overlay start time in seconds", "0").option("--style <style>", "Style preset for generated overlays: minimal, corporate, playful, cinematic").option("-m, --model <alias>", "LLM model for generated overlays: sonnet, opus, gemini, gemini-2.5-pro, gemini-3.1-pro", "sonnet").option("--understand <mode>", "Analyze video before generated overlay: auto, off, required", "auto").option("--understanding-prompt <text>", "Custom prompt for video understanding").option("--position <position>", "Lottie position: full, center, top-left, top-right, bottom-left, bottom-right", "full").option("--scale <number>", "Lottie overlay scale (0.01-2)").option("--opacity <number>", "Lottie overlay opacity (0-1)", "1").option("--loop", "Loop Lottie overlay", true).option("--no-loop", "Do not loop Lottie overlay").option("--dry-run", "Preview parameters without executing").action(async (videoPath, description, options) => {
39533
39581
  const startedAt = Date.now();
39534
39582
  try {
39535
39583
  if (options.output) validateOutputPath(options.output);
@@ -40818,13 +40866,7 @@ async function executeThumbnailBestFrame(options) {
40818
40866
  await gemini.initialize({ apiKey: googleKey });
40819
40867
  const videoData = await readFile25(videoPath);
40820
40868
  const analysisPrompt = prompt3 || 'Analyze this video and find the single best frame for a thumbnail. Look for frames that are visually striking, well-composed, emotionally engaging, and representative of the video content. Avoid blurry frames, transitions, or dark scenes. Return ONLY a JSON object: {"timestamp": <seconds as number>, "reason": "<brief explanation>"}';
40821
- const modelMap = {
40822
- flash: "gemini-3-flash-preview",
40823
- latest: "gemini-2.5-flash",
40824
- "flash-2.5": "gemini-2.5-flash",
40825
- pro: "gemini-2.5-pro"
40826
- };
40827
- const modelId = modelMap[model] || "gemini-3-flash-preview";
40869
+ const modelId = resolveGeminiTextModel(model);
40828
40870
  const result = await gemini.analyzeVideo(videoData, analysisPrompt, {
40829
40871
  model: modelId,
40830
40872
  fps: 1
@@ -40884,12 +40926,7 @@ async function executeGeminiVideo(options) {
40884
40926
  return { success: false, error: "Google API key required. Run 'vibe setup' or set GOOGLE_API_KEY in .env" };
40885
40927
  }
40886
40928
  const isYouTube = options.source.includes("youtube.com") || options.source.includes("youtu.be");
40887
- const modelMap = {
40888
- flash: "gemini-3-flash-preview",
40889
- "flash-2.5": "gemini-2.5-flash",
40890
- pro: "gemini-2.5-pro"
40891
- };
40892
- const modelId = modelMap[options.model || "flash"] || modelMap.flash;
40929
+ const modelId = resolveGeminiTextModel(options.model);
40893
40930
  let videoData;
40894
40931
  if (isYouTube) {
40895
40932
  videoData = options.source;
@@ -40950,12 +40987,7 @@ async function executeAnalyze(options) {
40950
40987
  error: "Cannot detect source type. Supported: images (.png/.jpg/.webp/.gif), videos (.mp4/.mov/.webm), YouTube URLs, image URLs."
40951
40988
  };
40952
40989
  }
40953
- const modelMap = {
40954
- flash: "gemini-3-flash-preview",
40955
- "flash-2.5": "gemini-2.5-flash",
40956
- pro: "gemini-2.5-pro"
40957
- };
40958
- const modelId = modelMap[options.model || "flash"] || modelMap.flash;
40990
+ const modelId = resolveGeminiTextModel(options.model);
40959
40991
  const gemini = new GeminiProvider();
40960
40992
  await gemini.initialize({ apiKey });
40961
40993
  if (isImage) {
@@ -41035,7 +41067,7 @@ async function executeAnalyze(options) {
41035
41067
  }
41036
41068
  }
41037
41069
  function registerAnalyzeCommands(aiCommand) {
41038
- aiCommand.command("gemini-video").description("Analyze video using Gemini (summarize, Q&A, extract info)").argument("<source>", "Video file path or YouTube URL").argument("<prompt>", "Analysis prompt (e.g., 'Summarize this video')").option("-k, --api-key <key>", "Google API key (or set GOOGLE_API_KEY env)").option("-m, --model <model>", "Model: flash (default), flash-2.5, pro", "flash").option("--fps <number>", "Frames per second (default: 1, higher for action)").option("--start <seconds>", "Start offset in seconds (for clipping)").option("--end <seconds>", "End offset in seconds (for clipping)").option("--low-res", "Use low resolution mode (fewer tokens, longer videos)").option("-v, --verbose", "Show token usage").action(async (source, prompt3, options) => {
41070
+ aiCommand.command("gemini-video").description("Analyze video using Gemini (summarize, Q&A, extract info)").argument("<source>", "Video file path or YouTube URL").argument("<prompt>", "Analysis prompt (e.g., 'Summarize this video')").option("-k, --api-key <key>", "Google API key (or set GOOGLE_API_KEY env)").option("-m, --model <model>", `Model: ${GEMINI_TEXT_MODEL_HELP}`, "flash").option("--fps <number>", "Frames per second (default: 1, higher for action)").option("--start <seconds>", "Start offset in seconds (for clipping)").option("--end <seconds>", "End offset in seconds (for clipping)").option("--low-res", "Use low resolution mode (fewer tokens, longer videos)").option("-v, --verbose", "Show token usage").action(async (source, prompt3, options) => {
41039
41071
  try {
41040
41072
  if (options.apiKey) {
41041
41073
  process.env.GOOGLE_API_KEY = options.apiKey;
@@ -41078,7 +41110,7 @@ function registerAnalyzeCommands(aiCommand) {
41078
41110
  exitWithError(apiError(`Video analysis failed: ${error instanceof Error ? error.message : String(error)}`, true));
41079
41111
  }
41080
41112
  });
41081
- aiCommand.command("analyze").description("Analyze any media: images, videos, or YouTube URLs using Gemini").argument("<source>", "Image/video file path, image URL, or YouTube URL").argument("<prompt>", "Analysis prompt (e.g., 'Describe this image', 'Summarize this video')").option("-k, --api-key <key>", "Google API key (or set GOOGLE_API_KEY env)").option("-m, --model <model>", "Model: flash (default), flash-2.5, pro", "flash").option("--fps <number>", "Frames per second for video (default: 1)").option("--start <seconds>", "Start offset in seconds (video only)").option("--end <seconds>", "End offset in seconds (video only)").option("--low-res", "Use low resolution mode (fewer tokens)").option("-v, --verbose", "Show token usage").action(async (source, prompt3, options) => {
41113
+ aiCommand.command("analyze").description("Analyze any media: images, videos, or YouTube URLs using Gemini").argument("<source>", "Image/video file path, image URL, or YouTube URL").argument("<prompt>", "Analysis prompt (e.g., 'Describe this image', 'Summarize this video')").option("-k, --api-key <key>", "Google API key (or set GOOGLE_API_KEY env)").option("-m, --model <model>", `Model: ${GEMINI_TEXT_MODEL_HELP}`, "flash").option("--fps <number>", "Frames per second for video (default: 1)").option("--start <seconds>", "Start offset in seconds (video only)").option("--end <seconds>", "End offset in seconds (video only)").option("--low-res", "Use low resolution mode (fewer tokens)").option("-v, --verbose", "Show token usage").action(async (source, prompt3, options) => {
41082
41114
  try {
41083
41115
  if (options.apiKey) {
41084
41116
  process.env.GOOGLE_API_KEY = options.apiKey;
@@ -41199,12 +41231,7 @@ ${content}`;
41199
41231
 
41200
41232
  Project context for beat-aware review:
41201
41233
  ${projectContext}` : "";
41202
- const modelMap = {
41203
- flash: "gemini-3-flash-preview",
41204
- "flash-2.5": "gemini-2.5-flash",
41205
- pro: "gemini-2.5-pro"
41206
- };
41207
- const modelId = modelMap[model] || modelMap.flash;
41234
+ const modelId = resolveGeminiTextModel(model);
41208
41235
  const reviewPrompt = `You are a professional video editor reviewing this video for quality. Analyze the video and return a JSON review with the following structure. Return ONLY valid JSON, no extra text.
41209
41236
 
41210
41237
  {
@@ -41311,7 +41338,7 @@ Score each category 1-10. Prefer beatIssues when you can map a problem to a stor
41311
41338
  return result;
41312
41339
  }
41313
41340
  function registerReviewCommand(aiCommand) {
41314
- aiCommand.command("review", { hidden: true }).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) => {
41341
+ aiCommand.command("review", { hidden: true }).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: ${GEMINI_TEXT_MODEL_HELP}`, "flash").option("-o, --output <path>", "Output video file path (for auto-apply)").option("--dry-run", "Preview parameters without executing").action(async (videoPath, options) => {
41315
41342
  const startedAt = Date.now();
41316
41343
  try {
41317
41344
  if (options.output) {
@@ -42198,7 +42225,7 @@ import { resolve as resolve46, dirname as dirname19, basename as basename13, ext
42198
42225
  import { existsSync as existsSync38 } from "node:fs";
42199
42226
  import { writeFile as writeFile34, mkdir as mkdir20 } from "node:fs/promises";
42200
42227
  function registerThumbnailCommand(parent) {
42201
- 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) => {
42228
+ 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: ${GEMINI_TEXT_MODEL_HELP}`, "flash").action(async (description, options) => {
42202
42229
  const startedAt = Date.now();
42203
42230
  try {
42204
42231
  if (description) rejectControlChars(description);
@@ -44353,7 +44380,7 @@ function registerVideoCommand(parent) {
44353
44380
  "--seedance-model <model>",
44354
44381
  "Seedance variant: quality or fast (fal.ai only)",
44355
44382
  "quality"
44356
- ).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(
44383
+ ).option("--negative <prompt>", "Negative prompt - what to avoid (Kling/Veo)").option("--resolution <res>", "Video resolution: 480p, 720p, 1080p, or 4k depending on provider").option("--last-frame <path>", "Last frame image for frame interpolation (Veo) or Seedance end frame").option("--end-image <path>", "Ending frame image for Seedance image-to-video").option(
44357
44384
  "--ref-images <paths...>",
44358
44385
  "Reference images for Seedance reference-to-video or Veo character consistency"
44359
44386
  ).option("--ref-videos <paths...>", "Reference videos for Seedance reference-to-video").option("--ref-audio <paths...>", "Reference audio for Seedance reference-to-video").option("--no-generate-audio", "Disable native audio when the provider supports it").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(
@@ -44814,6 +44841,30 @@ Examples:
44814
44841
  exitWithError(apiError(message, true));
44815
44842
  }
44816
44843
  }
44844
+ let seedanceEndImage;
44845
+ const seedanceEndImagePath = options.endImage ?? options.lastFrame;
44846
+ if (seedanceEndImagePath && seedanceReferences.length === 0) {
44847
+ try {
44848
+ const absEndImagePath = resolve50(process.cwd(), seedanceEndImagePath);
44849
+ const endImageBuffer = await readFile32(absEndImagePath);
44850
+ const ext = seedanceEndImagePath.toLowerCase().split(".").pop();
44851
+ const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : `image/${ext || "png"}`;
44852
+ const uploadHost = await resolveUploadHost();
44853
+ spinner2.text = `Uploading end image via ${uploadHost.provider} for Seedance...`;
44854
+ const upload = await uploadHost.uploadImage(endImageBuffer, {
44855
+ filename: seedanceEndImagePath,
44856
+ mimeType
44857
+ });
44858
+ seedanceEndImage = upload.url;
44859
+ } catch (err) {
44860
+ spinner2.fail("End image upload failed");
44861
+ const message = err instanceof Error ? err.message : String(err);
44862
+ if (message.includes("IMGBB_API_KEY")) {
44863
+ exitWithError(authError("IMGBB_API_KEY", "ImgBB"));
44864
+ }
44865
+ exitWithError(apiError(message, true));
44866
+ }
44867
+ }
44817
44868
  spinner2.text = "Generating video with fal.ai Seedance 2.0 (this may take 1-3 minutes)...";
44818
44869
  const seedanceModel = String(options.seedanceModel ?? "quality").toLowerCase();
44819
44870
  const falModel = seedanceModel === "fast" || seedanceModel === "seedance-2.0-fast" ? "seedance-2.0-fast" : "seedance-2.0";
@@ -44826,7 +44877,8 @@ Examples:
44826
44877
  negativePrompt: options.negative,
44827
44878
  model: falModel,
44828
44879
  resolution: options.resolution,
44829
- generateAudio: options.generateAudio
44880
+ generateAudio: options.generateAudio,
44881
+ lastFrame: seedanceEndImage
44830
44882
  });
44831
44883
  finalResult = result;
44832
44884
  }
@@ -50718,6 +50770,14 @@ var SCENE_PRESETS = [
50718
50770
  "kinetic-type",
50719
50771
  "product-shot"
50720
50772
  ];
50773
+ var LOTTIE_POSITIONS = [
50774
+ "full",
50775
+ "center",
50776
+ "top-left",
50777
+ "top-right",
50778
+ "bottom-left",
50779
+ "bottom-right"
50780
+ ];
50721
50781
  var GSAP_CDN = "https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js";
50722
50782
  function esc(text) {
50723
50783
  return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
@@ -50998,6 +51058,56 @@ function buildPreset(input) {
50998
51058
  }
50999
51059
  }
51000
51060
  }
51061
+ var DOTLOTTIE_WC_VERSION = "0.9.12";
51062
+ var DOTLOTTIE_WEB_VERSION = "0.71.0";
51063
+ var DOTLOTTIE_WC_CDN = `https://cdn.jsdelivr.net/npm/@lottiefiles/dotlottie-wc@${DOTLOTTIE_WC_VERSION}/dist/index.js`;
51064
+ var DOTLOTTIE_WASM_CDN = `https://cdn.jsdelivr.net/npm/@lottiefiles/dotlottie-web@${DOTLOTTIE_WEB_VERSION}/dist/dotlottie-player.wasm`;
51065
+ function lottieOverlayStyle(input) {
51066
+ const pos = input.position ?? "full";
51067
+ const rawScale = input.scale ?? (pos === "full" ? 1 : 0.25);
51068
+ const scale = Math.max(0.01, Math.min(2, rawScale));
51069
+ const opacity = Math.max(0, Math.min(1, input.opacity ?? 1));
51070
+ const base = [
51071
+ "position:absolute",
51072
+ "pointer-events:none",
51073
+ `opacity:${opacity}`
51074
+ ];
51075
+ if (pos === "full") {
51076
+ base.push("inset:0", "width:100%", "height:100%");
51077
+ } else {
51078
+ const pct = scale * 100;
51079
+ base.push(`width:${pct}%`, `height:${pct}%`);
51080
+ switch (pos) {
51081
+ case "center":
51082
+ base.push("top:50%", "left:50%", "transform:translate(-50%,-50%)");
51083
+ break;
51084
+ case "top-left":
51085
+ base.push("top:4%", "left:4%");
51086
+ break;
51087
+ case "top-right":
51088
+ base.push("top:4%", "right:4%");
51089
+ break;
51090
+ case "bottom-left":
51091
+ base.push("bottom:4%", "left:4%");
51092
+ break;
51093
+ case "bottom-right":
51094
+ base.push("bottom:4%", "right:4%");
51095
+ break;
51096
+ }
51097
+ }
51098
+ return base.join(";");
51099
+ }
51100
+ function buildLottieOverlay(input, sceneId) {
51101
+ const loop = input.loop ?? true ? " loop" : "";
51102
+ const style = lottieOverlayStyle(input);
51103
+ const overlayId = `lottie-overlay-${sceneId}`;
51104
+ const markup = `<dotlottie-wc id="${overlayId}" src="${esc(input.src)}" autoplay${loop} style="${style}"></dotlottie-wc>`;
51105
+ const script = `<script type="module">
51106
+ import { setWasmUrl } from "${DOTLOTTIE_WC_CDN}";
51107
+ setWasmUrl("${DOTLOTTIE_WASM_CDN}");
51108
+ </script>`;
51109
+ return { markup, script };
51110
+ }
51001
51111
  function emitSceneHtml(input) {
51002
51112
  if (input.duration <= 0) {
51003
51113
  throw new Error(`Scene duration must be > 0, got ${input.duration}`);
@@ -51018,15 +51128,20 @@ function emitSceneHtml(input) {
51018
51128
  data-volume="1"
51019
51129
  ></audio>
51020
51130
  ` : "";
51131
+ const lottieLayer = input.lottie ? buildLottieOverlay(input.lottie, id) : null;
51132
+ const lottieMarkup = lottieLayer ? `
51133
+ ${lottieLayer.markup}` : "";
51134
+ const lottieScript = lottieLayer ? `
51135
+ ${lottieLayer.script}` : "";
51021
51136
  return `<template id="scene-${id}-template">
51022
51137
  <div data-composition-id="${id}" data-start="0" data-duration="${dur}" data-width="${input.width}" data-height="${input.height}">
51023
51138
  <style>
51024
51139
  ${parts.css}
51025
51140
  </style>
51026
51141
 
51027
- ${parts.body}
51142
+ ${parts.body}${lottieMarkup}
51028
51143
  ${audioBlock}
51029
- <script src="${GSAP_CDN}"></script>
51144
+ <script src="${GSAP_CDN}"></script>${lottieScript}
51030
51145
  <script>
51031
51146
  window.__timelines = window.__timelines || {};
51032
51147
  const tl = gsap.timeline({ paused: true });
@@ -51450,9 +51565,40 @@ sceneCommand.command("add").description("Add a new scene to a project: AI narrat
51450
51565
  ).option("--no-image", "Skip image generation even when --visuals is provided").option(
51451
51566
  "--no-transcribe",
51452
51567
  "Skip Whisper word-level transcribe step (no transcript-<id>.json emitted)"
51453
- ).option("--transcribe-language <code>", "BCP-47 language code passed to Whisper (e.g. en, ko)").option("--force", "Overwrite an existing compositions/scene-<id>.html").option("--dry-run", "Preview parameters without writing files or calling APIs").action(async (name, options) => {
51568
+ ).option("--transcribe-language <code>", "BCP-47 language code passed to Whisper (e.g. en, ko)").option("--force", "Overwrite an existing compositions/scene-<id>.html").option("--lottie <path>", "Lottie animation file (.json/.lottie) to overlay on the scene").option(
51569
+ "--lottie-position <position>",
51570
+ `Lottie position: ${LOTTIE_POSITIONS.join(", ")}`,
51571
+ "full"
51572
+ ).option("--lottie-scale <number>", "Lottie overlay scale (0.01-2)").option("--lottie-opacity <number>", "Lottie overlay opacity (0-1)", "1").option("--lottie-no-loop", "Do not loop the Lottie animation").option("--dry-run", "Preview parameters without writing files or calling APIs").action(async (name, options) => {
51454
51573
  const startedAt = Date.now();
51455
51574
  if (options.style) options.style = validatePreset(options.style);
51575
+ if (options.lottiePosition) {
51576
+ if (!LOTTIE_POSITIONS.includes(options.lottiePosition)) {
51577
+ exitWithError(
51578
+ usageError(
51579
+ `Invalid --lottie-position "${options.lottiePosition}". Valid: ${LOTTIE_POSITIONS.join(", ")}`
51580
+ )
51581
+ );
51582
+ }
51583
+ }
51584
+ let lottieScale;
51585
+ if (options.lottieScale !== void 0) {
51586
+ lottieScale = Number(options.lottieScale);
51587
+ if (!Number.isFinite(lottieScale) || lottieScale < 0.01 || lottieScale > 2) {
51588
+ exitWithError(
51589
+ usageError(`Invalid --lottie-scale "${options.lottieScale}". Must be between 0.01 and 2.`)
51590
+ );
51591
+ }
51592
+ }
51593
+ let lottieOpacity = 1;
51594
+ if (options.lottieOpacity !== void 0) {
51595
+ lottieOpacity = Number(options.lottieOpacity);
51596
+ if (!Number.isFinite(lottieOpacity) || lottieOpacity < 0 || lottieOpacity > 1) {
51597
+ exitWithError(
51598
+ usageError(`Invalid --lottie-opacity "${options.lottieOpacity}". Must be between 0 and 1.`)
51599
+ );
51600
+ }
51601
+ }
51456
51602
  if (options.duration !== void 0) options.duration = validateDuration(options.duration);
51457
51603
  let tts;
51458
51604
  try {
@@ -51481,7 +51627,11 @@ sceneCommand.command("add").description("Add a new scene to a project: AI narrat
51481
51627
  imageProvider: options.imageProvider,
51482
51628
  tts,
51483
51629
  audio: options.audio,
51484
- // commander sets `audio: false` when --no-audio is passed
51630
+ lottie: options.lottie ?? null,
51631
+ lottiePosition: options.lottiePosition,
51632
+ lottieScale: lottieScale ?? null,
51633
+ lottieOpacity: options.lottieOpacity !== void 0 ? lottieOpacity : null,
51634
+ lottieLoop: !options.lottieNoLoop,
51485
51635
  image: options.image
51486
51636
  }
51487
51637
  }
@@ -51509,6 +51659,13 @@ sceneCommand.command("add").description("Add a new scene to a project: AI narrat
51509
51659
  skipTranscribe: options.transcribe === false,
51510
51660
  transcribeLanguage: options.transcribeLanguage,
51511
51661
  force: !!options.force,
51662
+ lottie: options.lottie ? {
51663
+ src: options.lottie,
51664
+ position: options.lottiePosition ?? "full",
51665
+ scale: lottieScale,
51666
+ opacity: options.lottieOpacity !== void 0 ? lottieOpacity : 1,
51667
+ loop: !options.lottieNoLoop
51668
+ } : void 0,
51512
51669
  onProgress: (msg) => {
51513
51670
  if (spinner2) spinner2.text = msg;
51514
51671
  }
@@ -51784,6 +51941,30 @@ async function executeSceneAdd(opts) {
51784
51941
  await writeFile10(imageAbsPath, buffer);
51785
51942
  }
51786
51943
  }
51944
+ let lottieRelPath;
51945
+ let lottieAbsPath;
51946
+ let lottieInput;
51947
+ if (opts.lottie) {
51948
+ const sourceAbs = resolve12(opts.lottie.src);
51949
+ if (!await pathExists2(sourceAbs)) {
51950
+ return errResult(`Lottie file not found: ${sourceAbs}`);
51951
+ }
51952
+ const ext = (sourceAbs.match(/\.([a-z0-9]+)$/i)?.[1] ?? "json").toLowerCase();
51953
+ if (ext !== "json" && ext !== "lottie") {
51954
+ return errResult(`Unsupported Lottie file extension: .${ext}. Use .json or .lottie.`);
51955
+ }
51956
+ lottieRelPath = `assets/lottie-${id}.${ext}`;
51957
+ lottieAbsPath = resolve12(projectDir, lottieRelPath);
51958
+ await mkdir5(dirname3(lottieAbsPath), { recursive: true });
51959
+ await copyFile(sourceAbs, lottieAbsPath);
51960
+ lottieInput = {
51961
+ src: lottieRelPath,
51962
+ position: opts.lottie.position,
51963
+ scale: opts.lottie.scale,
51964
+ opacity: opts.lottie.opacity,
51965
+ loop: opts.lottie.loop
51966
+ };
51967
+ }
51787
51968
  const cfg = await loadVibeProjectConfig(projectDir);
51788
51969
  const fallback2 = cfg?.defaultSceneDuration ?? 5;
51789
51970
  const NARRATION_TAIL_BUFFER = 0.5;
@@ -51812,7 +51993,8 @@ async function executeSceneAdd(opts) {
51812
51993
  kicker: opts.kicker,
51813
51994
  imagePath: imageRelPath,
51814
51995
  audioPath: audioRelPath,
51815
- transcript: transcriptWords
51996
+ transcript: transcriptWords,
51997
+ lottie: lottieInput
51816
51998
  });
51817
51999
  await mkdir5(dirname3(scenePath), { recursive: true });
51818
52000
  await writeFile10(scenePath, sceneHtml, "utf-8");
@@ -51833,6 +52015,7 @@ async function executeSceneAdd(opts) {
51833
52015
  rootPath: relative5(process.cwd(), rootPath) || rootPath,
51834
52016
  audioPath: audioAbsPath ? relative5(process.cwd(), audioAbsPath) || audioAbsPath : void 0,
51835
52017
  imagePath: imageAbsPath ? relative5(process.cwd(), imageAbsPath) || imageAbsPath : void 0,
52018
+ lottiePath: lottieAbsPath ? relative5(process.cwd(), lottieAbsPath) || lottieAbsPath : void 0,
51836
52019
  transcriptPath: transcriptAbsPath ? relative5(process.cwd(), transcriptAbsPath) || transcriptAbsPath : void 0,
51837
52020
  transcriptWordCount
51838
52021
  };
@@ -53099,7 +53282,7 @@ var editMotionOverlayTool = defineTool({
53099
53282
  duration: external_exports.number().optional().describe("Overlay/render duration in seconds"),
53100
53283
  start: external_exports.number().optional().describe("Overlay start time in seconds"),
53101
53284
  style: external_exports.string().optional().describe("Style preset for generated overlays"),
53102
- model: external_exports.enum(["sonnet", "opus", "gemini", "gemini-3.1-pro"]).optional().describe("LLM model for generated overlays"),
53285
+ model: external_exports.enum(["sonnet", "opus", "gemini", "gemini-2.5-pro", "gemini-3.1-pro"]).optional().describe("LLM model for generated overlays"),
53103
53286
  understand: external_exports.enum(["auto", "off", "required"]).optional().describe("Analyze video before generated overlay (default: auto)"),
53104
53287
  understandingPrompt: external_exports.string().optional().describe("Custom prompt for video understanding"),
53105
53288
  position: external_exports.enum(["full", "center", "top-left", "top-right", "bottom-left", "bottom-right"]).optional().describe("Lottie overlay position"),
@@ -54833,7 +55016,7 @@ var inspectRenderTool = defineTool({
54833
55016
  outputPath: external_exports.string().optional().describe("Optional review report path. Defaults to <project>/review-report.json."),
54834
55017
  report: external_exports.boolean().optional().describe("Write review-report.json. Default true."),
54835
55018
  ai: external_exports.boolean().optional().describe("Also run Gemini video review and merge findings into review-report.json. Default false."),
54836
- model: external_exports.enum(["flash", "flash-2.5", "pro"]).optional().describe("Gemini model variant for ai review. Default flash."),
55019
+ model: external_exports.enum(["flash", "latest", "flash-3", "flash-2.5", "pro", "pro-3.1"]).optional().describe("Gemini model variant for ai review. Default flash."),
54837
55020
  dryRun: external_exports.boolean().optional().describe("Preview resolved inputs without probing video or calling Gemini.")
54838
55021
  }),
54839
55022
  async execute(args, ctx) {
@@ -54885,7 +55068,7 @@ var analyzeMediaTool = defineTool({
54885
55068
  schema: external_exports.object({
54886
55069
  source: external_exports.string().describe("Path to image/video or YouTube URL"),
54887
55070
  prompt: external_exports.string().describe("Analysis prompt (e.g., 'Describe the scene', 'Count people')"),
54888
- model: external_exports.enum(["flash", "flash-2.5", "pro"]).optional().describe("Gemini model variant (default: flash)"),
55071
+ model: external_exports.enum(["flash", "latest", "flash-3", "flash-2.5", "pro", "pro-3.1"]).optional().describe("Gemini model variant (default: flash)"),
54889
55072
  fps: external_exports.number().optional().describe("Frames per second for video sampling (default: 1)"),
54890
55073
  start: external_exports.number().optional().describe("Start time in seconds for video analysis"),
54891
55074
  end: external_exports.number().optional().describe("End time in seconds for video analysis"),
@@ -54913,7 +55096,7 @@ var analyzeVideoTool = defineTool({
54913
55096
  schema: external_exports.object({
54914
55097
  source: external_exports.string().describe("Path to video file"),
54915
55098
  prompt: external_exports.string().describe("Analysis prompt"),
54916
- model: external_exports.enum(["flash", "flash-2.5", "pro"]).optional().describe("Gemini model variant (default: flash)"),
55099
+ model: external_exports.enum(["flash", "latest", "flash-3", "flash-2.5", "pro", "pro-3.1"]).optional().describe("Gemini model variant (default: flash)"),
54917
55100
  fps: external_exports.number().optional().describe("Frames per second for sampling"),
54918
55101
  start: external_exports.number().optional().describe("Start time in seconds"),
54919
55102
  end: external_exports.number().optional().describe("End time in seconds"),
@@ -54943,7 +55126,7 @@ var analyzeReviewTool = defineTool({
54943
55126
  storyboardPath: external_exports.string().optional().describe("Path to storyboard.json for intent comparison"),
54944
55127
  autoApply: external_exports.boolean().optional().describe("Automatically apply suggested fixes (default: false)"),
54945
55128
  verify: external_exports.boolean().optional().describe("Re-review after applying fixes (default: false)"),
54946
- model: external_exports.enum(["flash", "flash-2.5", "pro"]).optional().describe("Gemini model variant (default: flash)"),
55129
+ model: external_exports.enum(["flash", "latest", "flash-3", "flash-2.5", "pro", "pro-3.1"]).optional().describe("Gemini model variant (default: flash)"),
54947
55130
  outputPath: external_exports.string().optional().describe("Output path for fixed video")
54948
55131
  }),
54949
55132
  async execute(args) {
@@ -55027,7 +55210,7 @@ var generateMotionTool = defineTool({
55027
55210
  "Analyze the base video with Gemini before generating motion graphics: auto, off, or required (default: auto)"
55028
55211
  ),
55029
55212
  understandingPrompt: external_exports.string().optional().describe("Custom prompt for video understanding when --video is provided"),
55030
- model: external_exports.enum(["sonnet", "opus", "gemini", "gemini-3.1-pro"]).optional().describe("LLM model for code generation (default: sonnet)"),
55213
+ model: external_exports.enum(["sonnet", "opus", "gemini", "gemini-2.5-pro", "gemini-3.1-pro"]).optional().describe("LLM model for code generation (default: sonnet)"),
55031
55214
  output: external_exports.string().optional().describe("Output path (TSX if code-only, MP4 if rendered)")
55032
55215
  }),
55033
55216
  async execute(args) {
@@ -55292,6 +55475,7 @@ var generateVideoTool = defineTool({
55292
55475
  "Video provider (default: seedance when FAL_API_KEY is configured, otherwise first configured provider)"
55293
55476
  ),
55294
55477
  image: external_exports.string().optional().describe("Reference image path for image-to-video"),
55478
+ endImage: external_exports.string().optional().describe("Ending frame image path for Seedance image-to-video"),
55295
55479
  refImages: external_exports.array(external_exports.string()).optional().describe("Reference images for Seedance reference-to-video"),
55296
55480
  refVideos: external_exports.array(external_exports.string()).optional().describe("Reference videos for Seedance reference-to-video"),
55297
55481
  refAudio: external_exports.array(external_exports.string()).optional().describe("Reference audio files for Seedance reference-to-video"),
@@ -55299,7 +55483,7 @@ var generateVideoTool = defineTool({
55299
55483
  ratio: external_exports.string().optional().describe("Aspect ratio: 16:9, 9:16, 1:1 (default: 16:9)"),
55300
55484
  mode: external_exports.string().optional().describe("Kling mode: std or pro"),
55301
55485
  negative: external_exports.string().optional().describe("Negative prompt (Seedance/Kling/Veo)"),
55302
- resolution: external_exports.string().optional().describe("Resolution: 720p, 1080p, 4k (Veo only)"),
55486
+ resolution: external_exports.string().optional().describe("Resolution: 480p, 720p, 1080p, or 4k depending on provider"),
55303
55487
  veoModel: external_exports.string().optional().describe("Veo model: 3.0, 3.1, 3.1-fast"),
55304
55488
  runwayModel: external_exports.string().optional().describe("Runway model: gen4.5, gen4_turbo"),
55305
55489
  seedanceModel: external_exports.string().optional().describe("Seedance variant: quality or fast (fal.ai only)"),