@vibeframe/mcp-server 0.48.5 → 0.49.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +400 -70
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -6751,14 +6751,14 @@ var init_output = __esm({
6751
6751
  return ExitCode2;
6752
6752
  })(ExitCode || {});
6753
6753
  PROVIDER_ERROR_HINTS = [
6754
+ // Billing (must precede the 429 rate-limit pattern)
6755
+ { pattern: /402|payment.*required|billing|INSUFFICIENT_BALANCE|insufficient.*(credit|funds|balance)|balance.*(not.*enough|insufficient)|credits?.*exhausted|account.*balance/i, suggestion: "Account balance or credits exhausted. Top up at the provider dashboard, or try -p <other-provider>.", retryable: false },
6754
6756
  // Rate limits / quota
6755
6757
  { pattern: /429|rate.?limit|too many requests/i, suggestion: "Rate limited. Wait 30-60 seconds and retry, or check your plan's rate limits.", retryable: true },
6756
6758
  { pattern: /RESOURCE_EXHAUSTED|quota.*exceeded|requests.*per.*(minute|day)/i, suggestion: "Quota exceeded. Wait for the quota window to reset, or upgrade your plan. Consider -p <other-provider> to use a different provider.", retryable: true },
6757
6759
  // Auth
6758
6760
  { pattern: /401|unauthorized|(invalid|incorrect).*api.?key|invalid_api_key|authentication.*(failed|error)|missing.*api.?key|did not start with 'key_'/i, suggestion: "API key is invalid or expired. Run 'vibe setup' to update, or check the key at the provider's dashboard.", retryable: false },
6759
6761
  { pattern: /403|forbidden|permission.*denied/i, suggestion: "Access denied. Your API key may lack required permissions, or the feature requires a paid plan.", retryable: false },
6760
- // Billing
6761
- { pattern: /402|payment.*required|billing|INSUFFICIENT_BALANCE|insufficient.*(credit|funds|balance)|credits?.*exhausted/i, suggestion: "Account balance or credits exhausted. Top up at the provider dashboard, or try -p <other-provider>.", retryable: false },
6762
6762
  // Server
6763
6763
  { pattern: /500|internal.*error|server.*error/i, suggestion: "Provider server error. Retry in a few minutes.", retryable: true },
6764
6764
  { pattern: /503|service.*unavailable|overloaded|overloaded_error/i, suggestion: "Provider is temporarily overloaded. Retry in 1-2 minutes, or switch provider with -p.", retryable: true },
@@ -12835,7 +12835,7 @@ var require_previous_map = __commonJS({
12835
12835
  "../../node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/previous-map.js"(exports, module) {
12836
12836
  "use strict";
12837
12837
  var { existsSync: existsSync35, readFileSync: readFileSync11 } = __require("fs");
12838
- var { dirname: dirname20, join: join23 } = __require("path");
12838
+ var { dirname: dirname21, join: join23 } = __require("path");
12839
12839
  var { SourceMapConsumer, SourceMapGenerator } = require_source_map();
12840
12840
  function fromBase64(str) {
12841
12841
  if (Buffer) {
@@ -12854,7 +12854,7 @@ var require_previous_map = __commonJS({
12854
12854
  if (!this.mapFile && opts.from) {
12855
12855
  this.mapFile = opts.from;
12856
12856
  }
12857
- if (this.mapFile) this.root = dirname20(this.mapFile);
12857
+ if (this.mapFile) this.root = dirname21(this.mapFile);
12858
12858
  if (text) this.text = text;
12859
12859
  }
12860
12860
  consumer() {
@@ -12896,7 +12896,7 @@ var require_previous_map = __commonJS({
12896
12896
  }
12897
12897
  }
12898
12898
  loadFile(path15) {
12899
- this.root = dirname20(path15);
12899
+ this.root = dirname21(path15);
12900
12900
  if (existsSync35(path15)) {
12901
12901
  this.mapFile = path15;
12902
12902
  return readFileSync11(path15, "utf-8").toString().trim();
@@ -12933,7 +12933,7 @@ var require_previous_map = __commonJS({
12933
12933
  return this.decodeInline(this.annotation);
12934
12934
  } else if (this.annotation) {
12935
12935
  let map3 = this.annotation;
12936
- if (file) map3 = join23(dirname20(file), map3);
12936
+ if (file) map3 = join23(dirname21(file), map3);
12937
12937
  return this.loadFile(map3);
12938
12938
  }
12939
12939
  }
@@ -13369,12 +13369,12 @@ var require_fromJSON = __commonJS({
13369
13369
  var require_map_generator = __commonJS({
13370
13370
  "../../node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/map-generator.js"(exports, module) {
13371
13371
  "use strict";
13372
- var { dirname: dirname20, relative: relative4, resolve: resolve35, sep } = __require("path");
13372
+ var { dirname: dirname21, relative: relative4, resolve: resolve35, sep } = __require("path");
13373
13373
  var { SourceMapConsumer, SourceMapGenerator } = require_source_map();
13374
13374
  var { pathToFileURL } = __require("url");
13375
13375
  var Input2 = require_input();
13376
13376
  var sourceMapAvailable = Boolean(SourceMapConsumer && SourceMapGenerator);
13377
- var pathAvailable = Boolean(dirname20 && resolve35 && relative4 && sep);
13377
+ var pathAvailable = Boolean(dirname21 && resolve35 && relative4 && sep);
13378
13378
  var MapGenerator = class {
13379
13379
  constructor(stringify5, root2, opts, cssString) {
13380
13380
  this.stringify = stringify5;
@@ -13406,7 +13406,7 @@ var require_map_generator = __commonJS({
13406
13406
  applyPrevMaps() {
13407
13407
  for (let prev of this.previous()) {
13408
13408
  let from3 = this.toUrl(this.path(prev.file));
13409
- let root2 = prev.root || dirname20(prev.file);
13409
+ let root2 = prev.root || dirname21(prev.file);
13410
13410
  let map3;
13411
13411
  if (this.mapOpts.sourcesContent === false) {
13412
13412
  map3 = new SourceMapConsumer(prev.text);
@@ -13593,9 +13593,9 @@ var require_map_generator = __commonJS({
13593
13593
  if (/^\w+:\/\//.test(file)) return file;
13594
13594
  let cached = this.memoizedPaths.get(file);
13595
13595
  if (cached) return cached;
13596
- let from3 = this.opts.to ? dirname20(this.opts.to) : ".";
13596
+ let from3 = this.opts.to ? dirname21(this.opts.to) : ".";
13597
13597
  if (typeof this.mapOpts.annotation === "string") {
13598
- from3 = dirname20(resolve35(from3, this.mapOpts.annotation));
13598
+ from3 = dirname21(resolve35(from3, this.mapOpts.annotation));
13599
13599
  }
13600
13600
  let path15 = relative4(from3, file);
13601
13601
  this.memoizedPaths.set(file, path15);
@@ -443791,7 +443791,7 @@ var init_ai_helpers = __esm({
443791
443791
 
443792
443792
  // ../cli/src/commands/ai-script-pipeline.ts
443793
443793
  import { readFile as readFile9, writeFile as writeFile10, mkdir as mkdir9, unlink as unlink4, rename as rename4 } from "node:fs/promises";
443794
- import { resolve as resolve20, basename as basename7, extname as extname7 } from "node:path";
443794
+ import { resolve as resolve20, basename as basename7, dirname as dirname14, extname as extname7 } from "node:path";
443795
443795
  import { existsSync as existsSync25 } from "node:fs";
443796
443796
  function sleep(ms) {
443797
443797
  return new Promise((resolve35) => setTimeout(resolve35, ms));
@@ -443819,6 +443819,73 @@ async function uploadToImgbb(imageBuffer, apiKey) {
443819
443819
  return { success: false, error: String(err) };
443820
443820
  }
443821
443821
  }
443822
+ async function extendVideoToTarget(videoPath, targetDuration, outputDir, sceneLabel, options) {
443823
+ const actualDuration = await getVideoDuration(videoPath);
443824
+ if (actualDuration >= targetDuration - 0.1) return;
443825
+ const ratio = targetDuration / actualDuration;
443826
+ const extendedPath = resolve20(outputDir, `${basename7(videoPath, ".mp4")}-extended.mp4`);
443827
+ if (ratio > 1.4 && options?.kling && options?.videoId) {
443828
+ try {
443829
+ options.onProgress?.(`${sceneLabel}: Extending via Kling API...`);
443830
+ const extendResult = await options.kling.extendVideo(options.videoId, {
443831
+ duration: "5"
443832
+ });
443833
+ if (extendResult.status !== "failed" && extendResult.id) {
443834
+ const waitResult = await options.kling.waitForExtendCompletion(
443835
+ extendResult.id,
443836
+ (status) => {
443837
+ options.onProgress?.(`${sceneLabel}: extend ${status.status}...`);
443838
+ },
443839
+ 6e5
443840
+ );
443841
+ if (waitResult.status === "completed" && waitResult.videoUrl) {
443842
+ const extendedVideoPath = resolve20(outputDir, `${basename7(videoPath, ".mp4")}-kling-ext.mp4`);
443843
+ const buffer = await downloadVideo(waitResult.videoUrl);
443844
+ await writeFile10(extendedVideoPath, buffer);
443845
+ const concatPath = resolve20(outputDir, `${basename7(videoPath, ".mp4")}-concat.mp4`);
443846
+ const listPath = resolve20(outputDir, `${basename7(videoPath, ".mp4")}-concat.txt`);
443847
+ await writeFile10(listPath, `file '${videoPath}'
443848
+ file '${extendedVideoPath}'`, "utf-8");
443849
+ await execSafe("ffmpeg", ["-y", "-f", "concat", "-safe", "0", "-i", listPath, "-c", "copy", concatPath]);
443850
+ const concatDuration = await getVideoDuration(concatPath);
443851
+ if (concatDuration > targetDuration + 0.5) {
443852
+ await execSafe("ffmpeg", ["-y", "-i", concatPath, "-t", targetDuration.toFixed(2), "-c", "copy", extendedPath]);
443853
+ await unlink4(concatPath);
443854
+ } else {
443855
+ await rename4(concatPath, extendedPath);
443856
+ }
443857
+ await unlink4(extendedVideoPath).catch(() => {
443858
+ });
443859
+ await unlink4(listPath).catch(() => {
443860
+ });
443861
+ await unlink4(videoPath);
443862
+ await rename4(extendedPath, videoPath);
443863
+ return;
443864
+ }
443865
+ }
443866
+ options.onProgress?.(`${sceneLabel}: Kling extend failed, using FFmpeg fallback...`);
443867
+ } catch {
443868
+ options.onProgress?.(`${sceneLabel}: Kling extend error, using FFmpeg fallback...`);
443869
+ }
443870
+ }
443871
+ await extendVideoNaturally(videoPath, targetDuration, extendedPath);
443872
+ await unlink4(videoPath);
443873
+ await rename4(extendedPath, videoPath);
443874
+ }
443875
+ function logSceneFailure(provider, sceneLabel, err) {
443876
+ let msg;
443877
+ if (err instanceof Error) {
443878
+ msg = err.message;
443879
+ } else if (typeof err === "string") {
443880
+ msg = err;
443881
+ } else if (err && typeof err === "object" && "error" in err && typeof err.error === "string") {
443882
+ msg = err.error;
443883
+ } else {
443884
+ msg = String(err);
443885
+ }
443886
+ console.error(source_default.dim(`
443887
+ [${provider} ${sceneLabel}: ${msg}]`));
443888
+ }
443822
443889
  async function generateVideoWithRetryGrok(grok, segment, options, maxRetries, onProgress) {
443823
443890
  const prompt3 = segment.visualStyle ? `${segment.visuals}. Style: ${segment.visualStyle}` : segment.visuals;
443824
443891
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
@@ -443832,9 +443899,13 @@ async function generateVideoWithRetryGrok(grok, segment, options, maxRetries, on
443832
443899
  if (result.status !== "failed" && result.id) {
443833
443900
  return { requestId: result.id };
443834
443901
  }
443902
+ const providerErr = result.error || "Grok returned failed status";
443835
443903
  if (attempt < maxRetries) {
443836
- onProgress?.(`\u26A0 Retry ${attempt + 1}/${maxRetries}...`);
443904
+ onProgress?.(`\u26A0 ${providerErr.slice(0, 50)}... retry ${attempt + 1}/${maxRetries}`);
443837
443905
  await sleep(RETRY_DELAY_MS);
443906
+ } else {
443907
+ console.error(source_default.dim(`
443908
+ [Grok error: ${providerErr}]`));
443838
443909
  }
443839
443910
  } catch (err) {
443840
443911
  const errMsg = err instanceof Error ? err.message : String(err);
@@ -443868,9 +443939,13 @@ async function generateVideoWithRetryKling(kling, segment, options, maxRetries,
443868
443939
  type: options.referenceImage ? "image2video" : "text2video"
443869
443940
  };
443870
443941
  }
443942
+ const providerErr = result.error || "Kling returned failed status";
443871
443943
  if (attempt < maxRetries) {
443872
- onProgress?.(`\u26A0 Retry ${attempt + 1}/${maxRetries}...`);
443944
+ onProgress?.(`\u26A0 ${providerErr.slice(0, 50)}... retry ${attempt + 1}/${maxRetries}`);
443873
443945
  await sleep(RETRY_DELAY_MS);
443946
+ } else {
443947
+ console.error(source_default.dim(`
443948
+ [Kling error: ${providerErr}]`));
443874
443949
  }
443875
443950
  } catch (err) {
443876
443951
  const errMsg = err instanceof Error ? err.message : String(err);
@@ -443897,9 +443972,13 @@ async function generateVideoWithRetryRunway(runway, segment, referenceImage, opt
443897
443972
  if (result.status !== "failed" && result.id) {
443898
443973
  return { taskId: result.id };
443899
443974
  }
443975
+ const providerErr = result.error || "Runway returned failed status";
443900
443976
  if (attempt < maxRetries) {
443901
- onProgress?.(`\u26A0 Retry ${attempt + 1}/${maxRetries}...`);
443977
+ onProgress?.(`\u26A0 ${providerErr.slice(0, 50)}... retry ${attempt + 1}/${maxRetries}`);
443902
443978
  await sleep(RETRY_DELAY_MS);
443979
+ } else {
443980
+ console.error(source_default.dim(`
443981
+ [Runway error: ${providerErr}]`));
443903
443982
  }
443904
443983
  } catch (err) {
443905
443984
  const errMsg = err instanceof Error ? err.message : String(err);
@@ -443928,9 +444007,13 @@ async function generateVideoWithRetryVeo(gemini, segment, options, maxRetries, o
443928
444007
  if (result.status !== "failed" && result.id) {
443929
444008
  return { operationName: result.id };
443930
444009
  }
444010
+ const providerErr = result.error || "Veo returned failed status";
443931
444011
  if (attempt < maxRetries) {
443932
- onProgress?.(`\u26A0 Retry ${attempt + 1}/${maxRetries}...`);
444012
+ onProgress?.(`\u26A0 ${providerErr.slice(0, 50)}... retry ${attempt + 1}/${maxRetries}`);
443933
444013
  await sleep(RETRY_DELAY_MS);
444014
+ } else {
444015
+ console.error(source_default.dim(`
444016
+ [Veo error: ${providerErr}]`));
443934
444017
  }
443935
444018
  } catch (err) {
443936
444019
  const errMsg = err instanceof Error ? err.message : String(err);
@@ -444063,13 +444146,42 @@ async function executeScriptToVideo(options) {
444063
444146
  });
444064
444147
  continue;
444065
444148
  }
444066
- const ttsResult = await elevenlabs.textToSpeech(narrationText, {
444149
+ const wordCount = narrationText.split(/\s+/).filter(Boolean).length;
444150
+ const maxWords = segment.duration > 5 ? 24 : 12;
444151
+ if (wordCount > maxWords * 1.3) {
444152
+ options.onProgress?.(
444153
+ `\u26A0 Scene ${i + 1} narration has ${wordCount} words (target ~${maxWords} for ${segment.duration}s); speech may rush.`
444154
+ );
444155
+ }
444156
+ options.onProgress?.(`Scene ${i + 1}/${segments.length}: generating narration...`);
444157
+ let ttsResult = await elevenlabs.textToSpeech(narrationText, {
444067
444158
  voiceId: options.voice
444068
444159
  });
444069
444160
  if (ttsResult.success && ttsResult.audioBuffer) {
444070
444161
  const audioPath = resolve20(absOutputDir, `narration-${i + 1}.mp3`);
444071
444162
  await writeFile10(audioPath, ttsResult.audioBuffer);
444072
- const actualDuration = await getAudioDuration(audioPath);
444163
+ let actualDuration = await getAudioDuration(audioPath);
444164
+ const videoBracket = segment.duration > 5 ? 10 : 5;
444165
+ const overageRatio = actualDuration / videoBracket;
444166
+ if (overageRatio > 1 && overageRatio <= 1.35) {
444167
+ const adjustedSpeed = Math.min(1.35, parseFloat(overageRatio.toFixed(2)));
444168
+ options.onProgress?.(
444169
+ `Scene ${i + 1}: adjusting narration speed to ${adjustedSpeed}x...`
444170
+ );
444171
+ const speedResult = await elevenlabs.textToSpeech(narrationText, {
444172
+ voiceId: options.voice,
444173
+ speed: adjustedSpeed
444174
+ });
444175
+ if (speedResult.success && speedResult.audioBuffer) {
444176
+ await writeFile10(audioPath, speedResult.audioBuffer);
444177
+ actualDuration = await getAudioDuration(audioPath);
444178
+ ttsResult = speedResult;
444179
+ }
444180
+ } else if (overageRatio > 1.35) {
444181
+ options.onProgress?.(
444182
+ `\u26A0 Scene ${i + 1} narration is ${((overageRatio - 1) * 100).toFixed(0)}% over target (${actualDuration.toFixed(1)}s vs ${videoBracket}s bracket)`
444183
+ );
444184
+ }
444073
444185
  segment.duration = actualDuration;
444074
444186
  result.narrations.push(audioPath);
444075
444187
  result.narrationEntries.push({
@@ -444120,6 +444232,7 @@ async function executeScriptToVideo(options) {
444120
444232
  for (let i = 0; i < segments.length; i++) {
444121
444233
  const segment = segments[i];
444122
444234
  const imagePrompt = segment.visualStyle ? `${segment.visuals}. Style: ${segment.visualStyle}` : segment.visuals;
444235
+ options.onProgress?.(`Scene ${i + 1}/${segments.length}: generating image...`);
444123
444236
  try {
444124
444237
  let imageBuffer;
444125
444238
  let imageUrl;
@@ -444191,6 +444304,7 @@ async function executeScriptToVideo(options) {
444191
444304
  }
444192
444305
  const segment = segments[i];
444193
444306
  const videoDuration = Math.min(15, Math.max(1, segment.duration));
444307
+ options.onProgress?.(`Scene ${i + 1}/${segments.length}: generating video (grok)...`);
444194
444308
  const imageBuffer = await readFile9(imagePaths[i]);
444195
444309
  const ext = extname7(imagePaths[i]).toLowerCase().slice(1);
444196
444310
  const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : "image/png";
@@ -444219,10 +444333,12 @@ async function executeScriptToVideo(options) {
444219
444333
  videoPaths.push(videoPath);
444220
444334
  result.videos.push(videoPath);
444221
444335
  } else {
444336
+ logSceneFailure("Grok", `scene ${i + 1}`, waitResult);
444222
444337
  videoPaths.push("");
444223
444338
  result.failedScenes.push(i + 1);
444224
444339
  }
444225
- } catch {
444340
+ } catch (err) {
444341
+ logSceneFailure("Grok", `scene ${i + 1}`, err);
444226
444342
  videoPaths.push("");
444227
444343
  result.failedScenes.push(i + 1);
444228
444344
  }
@@ -444237,6 +444353,24 @@ async function executeScriptToVideo(options) {
444237
444353
  if (!kling.isConfigured()) {
444238
444354
  return { success: false, outputDir: absOutputDir, scenes: segments.length, error: "Invalid Kling API key format. Use ACCESS_KEY:SECRET_KEY" };
444239
444355
  }
444356
+ const imgbbApiKey = await getApiKeyFromConfig("imgbb") || process.env.IMGBB_API_KEY;
444357
+ const imageUrls = new Array(segments.length);
444358
+ if (imgbbApiKey) {
444359
+ options.onProgress?.("Uploading scene images for Kling image-to-video...");
444360
+ for (let i = 0; i < imagePaths.length; i++) {
444361
+ if (imagePaths[i]) {
444362
+ try {
444363
+ const imageBuffer = await readFile9(imagePaths[i]);
444364
+ const uploadResult = await uploadToImgbb(imageBuffer, imgbbApiKey);
444365
+ if (uploadResult.success && uploadResult.url) {
444366
+ imageUrls[i] = uploadResult.url;
444367
+ }
444368
+ } catch {
444369
+ imageUrls[i] = void 0;
444370
+ }
444371
+ }
444372
+ }
444373
+ }
444240
444374
  for (let i = 0; i < segments.length; i++) {
444241
444375
  if (!imagePaths[i]) {
444242
444376
  videoPaths.push("");
@@ -444244,10 +444378,15 @@ async function executeScriptToVideo(options) {
444244
444378
  }
444245
444379
  const segment = segments[i];
444246
444380
  const videoDuration = segment.duration > 5 ? 10 : 5;
444381
+ options.onProgress?.(`Scene ${i + 1}/${segments.length}: generating video (kling)...`);
444247
444382
  const taskResult = await generateVideoWithRetryKling(
444248
444383
  kling,
444249
444384
  segment,
444250
- { duration: videoDuration, aspectRatio: options.aspectRatio || "16:9" },
444385
+ {
444386
+ duration: videoDuration,
444387
+ aspectRatio: options.aspectRatio || "16:9",
444388
+ referenceImage: imageUrls[i]
444389
+ },
444251
444390
  maxRetries
444252
444391
  );
444253
444392
  if (taskResult) {
@@ -444257,21 +444396,26 @@ async function executeScriptToVideo(options) {
444257
444396
  const videoPath = resolve20(absOutputDir, `scene-${i + 1}.mp4`);
444258
444397
  const buffer = await downloadVideo(waitResult.videoUrl, videoApiKey);
444259
444398
  await writeFile10(videoPath, buffer);
444260
- const targetDuration = segment.duration;
444261
- const actualVideoDuration = await getVideoDuration(videoPath);
444262
- if (actualVideoDuration < targetDuration - 0.1) {
444263
- const extendedPath = resolve20(absOutputDir, `scene-${i + 1}-extended.mp4`);
444264
- await extendVideoNaturally(videoPath, targetDuration, extendedPath);
444265
- await unlink4(videoPath);
444266
- await rename4(extendedPath, videoPath);
444267
- }
444399
+ await extendVideoToTarget(
444400
+ videoPath,
444401
+ segment.duration,
444402
+ absOutputDir,
444403
+ `Scene ${i + 1}`,
444404
+ {
444405
+ kling,
444406
+ videoId: waitResult.videoId,
444407
+ onProgress: options.onProgress
444408
+ }
444409
+ );
444268
444410
  videoPaths.push(videoPath);
444269
444411
  result.videos.push(videoPath);
444270
444412
  } else {
444413
+ logSceneFailure("Kling", `scene ${i + 1}`, waitResult);
444271
444414
  videoPaths.push("");
444272
444415
  result.failedScenes.push(i + 1);
444273
444416
  }
444274
- } catch {
444417
+ } catch (err) {
444418
+ logSceneFailure("Kling", `scene ${i + 1}`, err);
444275
444419
  videoPaths.push("");
444276
444420
  result.failedScenes.push(i + 1);
444277
444421
  }
@@ -444290,6 +444434,7 @@ async function executeScriptToVideo(options) {
444290
444434
  }
444291
444435
  const segment = segments[i];
444292
444436
  const veoDuration = segment.duration > 6 ? 8 : segment.duration > 4 ? 6 : 4;
444437
+ options.onProgress?.(`Scene ${i + 1}/${segments.length}: generating video (veo)...`);
444293
444438
  const taskResult = await generateVideoWithRetryVeo(
444294
444439
  veo,
444295
444440
  segment,
@@ -444314,10 +444459,12 @@ async function executeScriptToVideo(options) {
444314
444459
  videoPaths.push(videoPath);
444315
444460
  result.videos.push(videoPath);
444316
444461
  } else {
444462
+ logSceneFailure("Veo", `scene ${i + 1}`, waitResult);
444317
444463
  videoPaths.push("");
444318
444464
  result.failedScenes.push(i + 1);
444319
444465
  }
444320
- } catch {
444466
+ } catch (err) {
444467
+ logSceneFailure("Veo", `scene ${i + 1}`, err);
444321
444468
  videoPaths.push("");
444322
444469
  result.failedScenes.push(i + 1);
444323
444470
  }
@@ -444341,6 +444488,7 @@ async function executeScriptToVideo(options) {
444341
444488
  const referenceImage = `data:${mimeType};base64,${imageBuffer.toString("base64")}`;
444342
444489
  const videoDuration = segment.duration > 5 ? 10 : 5;
444343
444490
  const aspectRatio = options.aspectRatio === "1:1" ? "16:9" : options.aspectRatio || "16:9";
444491
+ options.onProgress?.(`Scene ${i + 1}/${segments.length}: generating video (runway)...`);
444344
444492
  const taskResult = await generateVideoWithRetryRunway(
444345
444493
  runway,
444346
444494
  segment,
@@ -444366,10 +444514,12 @@ async function executeScriptToVideo(options) {
444366
444514
  videoPaths.push(videoPath);
444367
444515
  result.videos.push(videoPath);
444368
444516
  } else {
444517
+ logSceneFailure("Runway", `scene ${i + 1}`, waitResult);
444369
444518
  videoPaths.push("");
444370
444519
  result.failedScenes.push(i + 1);
444371
444520
  }
444372
- } catch {
444521
+ } catch (err) {
444522
+ logSceneFailure("Runway", `scene ${i + 1}`, err);
444373
444523
  videoPaths.push("");
444374
444524
  result.failedScenes.push(i + 1);
444375
444525
  }
@@ -444474,7 +444624,11 @@ async function executeScriptToVideo(options) {
444474
444624
  });
444475
444625
  currentTime += actualDuration;
444476
444626
  }
444477
- const projectPath = resolve20(absOutputDir, "project.vibe.json");
444627
+ const projectPath = options.projectFilePath ? resolve20(process.cwd(), options.projectFilePath) : resolve20(absOutputDir, "project.vibe.json");
444628
+ const projectParentDir = dirname14(projectPath);
444629
+ if (!existsSync25(projectParentDir)) {
444630
+ await mkdir9(projectParentDir, { recursive: true });
444631
+ }
444478
444632
  await writeFile10(projectPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
444479
444633
  result.projectPath = projectPath;
444480
444634
  result.totalDuration = currentTime;
@@ -444536,6 +444690,8 @@ async function executeRegenerateScene(options) {
444536
444690
  }
444537
444691
  }
444538
444692
  const regenerateVideo = options.videoOnly || !options.narrationOnly && !options.imageOnly;
444693
+ const regenerateNarration = options.narrationOnly || !options.videoOnly && !options.imageOnly;
444694
+ const regenerateImage = options.imageOnly || !options.videoOnly && !options.narrationOnly;
444539
444695
  let videoApiKey;
444540
444696
  if (regenerateVideo) {
444541
444697
  const generatorKeyMap = {
@@ -444554,11 +444710,163 @@ async function executeRegenerateScene(options) {
444554
444710
  return { ...result, error: `${genInfo.name} API key required. Run 'vibe setup' or set ${genInfo.envVar} in .env` };
444555
444711
  }
444556
444712
  }
444713
+ let imageApiKey;
444714
+ if (regenerateImage) {
444715
+ const imageProvider = options.imageProvider || "openai";
444716
+ const imageKeyMap = {
444717
+ openai: { envVar: "OPENAI_API_KEY", name: "OpenAI" },
444718
+ gemini: { envVar: "GOOGLE_API_KEY", name: "Google" },
444719
+ grok: { envVar: "XAI_API_KEY", name: "xAI" }
444720
+ };
444721
+ const info = imageKeyMap[imageProvider];
444722
+ if (!info) {
444723
+ return { ...result, error: `Invalid imageProvider: ${imageProvider}` };
444724
+ }
444725
+ imageApiKey = await getApiKey(info.envVar, info.name) ?? void 0;
444726
+ if (!imageApiKey) {
444727
+ return { ...result, error: `${info.name} API key required. Run 'vibe setup' or set ${info.envVar} in .env` };
444728
+ }
444729
+ }
444730
+ let elevenlabsApiKey;
444731
+ if (regenerateNarration) {
444732
+ elevenlabsApiKey = await getApiKey("ELEVENLABS_API_KEY", "ElevenLabs") ?? void 0;
444733
+ if (!elevenlabsApiKey) {
444734
+ return { ...result, error: "ElevenLabs API key required. Run 'vibe setup' or set ELEVENLABS_API_KEY in .env" };
444735
+ }
444736
+ }
444737
+ let storyboardMutated = false;
444557
444738
  for (const sceneNum of options.scenes) {
444558
444739
  const segment = segments[sceneNum - 1];
444740
+ const narrationPath = resolve20(outputDir, `narration-${sceneNum}.mp3`);
444559
444741
  const imagePath = resolve20(outputDir, `scene-${sceneNum}.png`);
444560
444742
  const videoPath = resolve20(outputDir, `scene-${sceneNum}.mp4`);
444743
+ let sceneFailed = false;
444744
+ if (regenerateNarration && elevenlabsApiKey) {
444745
+ options.onProgress?.(`Scene ${sceneNum}: regenerating narration...`);
444746
+ const elevenlabs = new ElevenLabsProvider();
444747
+ await elevenlabs.initialize({ apiKey: elevenlabsApiKey });
444748
+ const narrationText = segment.narration || segment.description;
444749
+ const ttsResult = await elevenlabs.textToSpeech(narrationText, {
444750
+ voiceId: options.voice
444751
+ });
444752
+ if (ttsResult.success && ttsResult.audioBuffer) {
444753
+ await writeFile10(narrationPath, ttsResult.audioBuffer);
444754
+ segment.duration = await getAudioDuration(narrationPath);
444755
+ storyboardMutated = true;
444756
+ } else {
444757
+ sceneFailed = true;
444758
+ }
444759
+ }
444760
+ if (!sceneFailed && regenerateImage && imageApiKey) {
444761
+ options.onProgress?.(`Scene ${sceneNum}: regenerating image...`);
444762
+ const imageProvider = options.imageProvider || "openai";
444763
+ const characterDesc = segment.characterDescription || segments[0]?.characterDescription;
444764
+ let imagePrompt = segment.visualStyle ? `${segment.visuals}. Style: ${segment.visualStyle}` : segment.visuals;
444765
+ if (characterDesc) {
444766
+ imagePrompt = `${imagePrompt}
444767
+
444768
+ IMPORTANT - Character appearance must match exactly: ${characterDesc}`;
444769
+ }
444770
+ let referenceImageBuffer;
444771
+ const refSceneNum = options.referenceScene;
444772
+ if (refSceneNum && refSceneNum >= 1 && refSceneNum <= segments.length && refSceneNum !== sceneNum) {
444773
+ const refImagePath = resolve20(outputDir, `scene-${refSceneNum}.png`);
444774
+ if (existsSync25(refImagePath)) {
444775
+ referenceImageBuffer = await readFile9(refImagePath);
444776
+ }
444777
+ } else if (!refSceneNum) {
444778
+ for (let i = 1; i <= segments.length; i++) {
444779
+ if (i !== sceneNum) {
444780
+ const otherImagePath = resolve20(outputDir, `scene-${i}.png`);
444781
+ if (existsSync25(otherImagePath)) {
444782
+ referenceImageBuffer = await readFile9(otherImagePath);
444783
+ break;
444784
+ }
444785
+ }
444786
+ }
444787
+ }
444788
+ const dalleImageSizes = {
444789
+ "16:9": "1536x1024",
444790
+ "9:16": "1024x1536",
444791
+ "1:1": "1024x1024"
444792
+ };
444793
+ let imageBuffer;
444794
+ let imageUrl;
444795
+ if (imageProvider === "openai") {
444796
+ const openaiImage = new OpenAIImageProvider();
444797
+ await openaiImage.initialize({ apiKey: imageApiKey });
444798
+ const imageResult = await openaiImage.generateImage(imagePrompt, {
444799
+ size: dalleImageSizes[options.aspectRatio || "16:9"] || "1536x1024",
444800
+ quality: "standard"
444801
+ });
444802
+ if (imageResult.success && imageResult.images?.[0]) {
444803
+ const img = imageResult.images[0];
444804
+ if (img.base64) imageBuffer = Buffer.from(img.base64, "base64");
444805
+ else if (img.url) imageUrl = img.url;
444806
+ }
444807
+ } else if (imageProvider === "gemini") {
444808
+ const gemini = new GeminiProvider();
444809
+ await gemini.initialize({ apiKey: imageApiKey });
444810
+ if (referenceImageBuffer) {
444811
+ const simplifiedVisuals = segment.visuals.split(/[,.]/).find(
444812
+ (part) => part.includes("standing") || part.includes("sitting") || part.includes("walking") || part.includes("lying") || part.includes("reaching") || part.includes("looking") || part.includes("working") || part.includes("coding") || part.includes("typing")
444813
+ ) || segment.visuals.split(".")[0];
444814
+ const editPrompt = `Generate a new image showing the SAME SINGLE person from the reference image in a new scene.
444815
+
444816
+ REFERENCE: Look at the person in the reference image - their face, hair, build, and overall appearance.
444817
+
444818
+ NEW SCENE: ${simplifiedVisuals}
444819
+
444820
+ CRITICAL RULES:
444821
+ 1. Show ONLY ONE person - the exact same individual from the reference image
444822
+ 2. The person must have the IDENTICAL face, hair style, and body type
444823
+ 3. Do NOT show multiple people or duplicate the character
444824
+ 4. Create a single moment in time, one pose, one action
444825
+ 5. Match the art style and quality of the reference image
444826
+
444827
+ Generate the single-person scene image now.`;
444828
+ const imageResult = await gemini.editImage([referenceImageBuffer], editPrompt, {
444829
+ aspectRatio: options.aspectRatio || "16:9"
444830
+ });
444831
+ if (imageResult.success && imageResult.images?.[0]?.base64) {
444832
+ imageBuffer = Buffer.from(imageResult.images[0].base64, "base64");
444833
+ }
444834
+ } else {
444835
+ const imageResult = await gemini.generateImage(imagePrompt, {
444836
+ aspectRatio: options.aspectRatio || "16:9"
444837
+ });
444838
+ if (imageResult.success && imageResult.images?.[0]?.base64) {
444839
+ imageBuffer = Buffer.from(imageResult.images[0].base64, "base64");
444840
+ }
444841
+ }
444842
+ } else if (imageProvider === "grok") {
444843
+ const grok = new GrokProvider();
444844
+ await grok.initialize({ apiKey: imageApiKey });
444845
+ const imageResult = await grok.generateImage(imagePrompt, {
444846
+ aspectRatio: options.aspectRatio || "16:9"
444847
+ });
444848
+ if (imageResult.success && imageResult.images?.[0]) {
444849
+ const img = imageResult.images[0];
444850
+ if (img.base64) imageBuffer = Buffer.from(img.base64, "base64");
444851
+ else if (img.url) imageUrl = img.url;
444852
+ }
444853
+ }
444854
+ if (imageBuffer) {
444855
+ await writeFile10(imagePath, imageBuffer);
444856
+ } else if (imageUrl) {
444857
+ const response = await fetch(imageUrl);
444858
+ const buffer = Buffer.from(await response.arrayBuffer());
444859
+ await writeFile10(imagePath, buffer);
444860
+ } else {
444861
+ sceneFailed = true;
444862
+ }
444863
+ }
444864
+ if (sceneFailed) {
444865
+ result.failedScenes.push(sceneNum);
444866
+ continue;
444867
+ }
444561
444868
  if (regenerateVideo && videoApiKey) {
444869
+ options.onProgress?.(`Scene ${sceneNum}: regenerating video (${options.generator || "grok"})...`);
444562
444870
  if (!existsSync25(imagePath)) {
444563
444871
  result.failedScenes.push(sceneNum);
444564
444872
  continue;
@@ -444599,9 +444907,11 @@ async function executeRegenerateScene(options) {
444599
444907
  }
444600
444908
  result.regeneratedScenes.push(sceneNum);
444601
444909
  } else {
444910
+ logSceneFailure("Grok", `scene ${sceneNum}`, waitResult);
444602
444911
  result.failedScenes.push(sceneNum);
444603
444912
  }
444604
- } catch {
444913
+ } catch (err) {
444914
+ logSceneFailure("Grok", `scene ${sceneNum}`, err);
444605
444915
  result.failedScenes.push(sceneNum);
444606
444916
  }
444607
444917
  } else {
@@ -444640,9 +444950,11 @@ async function executeRegenerateScene(options) {
444640
444950
  }
444641
444951
  result.regeneratedScenes.push(sceneNum);
444642
444952
  } else {
444953
+ logSceneFailure("Veo", `scene ${sceneNum}`, waitResult);
444643
444954
  result.failedScenes.push(sceneNum);
444644
444955
  }
444645
- } catch {
444956
+ } catch (err) {
444957
+ logSceneFailure("Veo", `scene ${sceneNum}`, err);
444646
444958
  result.failedScenes.push(sceneNum);
444647
444959
  }
444648
444960
  } else {
@@ -444679,19 +444991,24 @@ async function executeRegenerateScene(options) {
444679
444991
  if (waitResult.status === "completed" && waitResult.videoUrl) {
444680
444992
  const buffer = await downloadVideo(waitResult.videoUrl, videoApiKey);
444681
444993
  await writeFile10(videoPath, buffer);
444682
- const targetDuration = segment.duration;
444683
- const actualVideoDuration = await getVideoDuration(videoPath);
444684
- if (actualVideoDuration < targetDuration - 0.1) {
444685
- const extendedPath = resolve20(outputDir, `scene-${sceneNum}-extended.mp4`);
444686
- await extendVideoNaturally(videoPath, targetDuration, extendedPath);
444687
- await unlink4(videoPath);
444688
- await rename4(extendedPath, videoPath);
444689
- }
444994
+ await extendVideoToTarget(
444995
+ videoPath,
444996
+ segment.duration,
444997
+ outputDir,
444998
+ `Scene ${sceneNum}`,
444999
+ {
445000
+ kling,
445001
+ videoId: waitResult.videoId,
445002
+ onProgress: options.onProgress
445003
+ }
445004
+ );
444690
445005
  result.regeneratedScenes.push(sceneNum);
444691
445006
  } else {
445007
+ logSceneFailure("Kling", `scene ${sceneNum}`, waitResult);
444692
445008
  result.failedScenes.push(sceneNum);
444693
445009
  }
444694
- } catch {
445010
+ } catch (err) {
445011
+ logSceneFailure("Kling", `scene ${sceneNum}`, err);
444695
445012
  result.failedScenes.push(sceneNum);
444696
445013
  }
444697
445014
  } else {
@@ -444727,16 +445044,29 @@ async function executeRegenerateScene(options) {
444727
445044
  }
444728
445045
  result.regeneratedScenes.push(sceneNum);
444729
445046
  } else {
445047
+ logSceneFailure("Runway", `scene ${sceneNum}`, waitResult);
444730
445048
  result.failedScenes.push(sceneNum);
444731
445049
  }
444732
- } catch {
445050
+ } catch (err) {
445051
+ logSceneFailure("Runway", `scene ${sceneNum}`, err);
444733
445052
  result.failedScenes.push(sceneNum);
444734
445053
  }
444735
445054
  } else {
444736
445055
  result.failedScenes.push(sceneNum);
444737
445056
  }
444738
445057
  }
445058
+ } else if (!sceneFailed) {
445059
+ result.regeneratedScenes.push(sceneNum);
445060
+ }
445061
+ }
445062
+ if (storyboardMutated) {
445063
+ let currentTime = 0;
445064
+ for (const segment of segments) {
445065
+ segment.startTime = currentTime;
445066
+ currentTime += segment.duration;
444739
445067
  }
445068
+ const serialized = storyboardPath.endsWith(".yaml") ? (0, import_yaml2.stringify)({ scenes: segments }, { indent: 2 }) : JSON.stringify(segments, null, 2);
445069
+ await writeFile10(storyboardPath, serialized, "utf-8");
444740
445070
  }
444741
445071
  result.success = result.failedScenes.length === 0;
444742
445072
  return result;
@@ -447094,7 +447424,7 @@ __export(generate_exports, {
447094
447424
  executeStoryboard: () => executeStoryboard,
447095
447425
  generateCommand: () => generateCommand
447096
447426
  });
447097
- import { resolve as resolve25, dirname as dirname15, basename as basename9, extname as extname9 } from "node:path";
447427
+ import { resolve as resolve25, dirname as dirname16, basename as basename9, extname as extname9 } from "node:path";
447098
447428
  import { fileURLToPath as fileURLToPath5 } from "node:url";
447099
447429
  import { readFile as readFile13, writeFile as writeFile14, mkdir as mkdir11 } from "node:fs/promises";
447100
447430
  import { existsSync as existsSync28 } from "node:fs";
@@ -447360,7 +447690,7 @@ Examples:
447360
447690
  } else {
447361
447691
  throw new Error("No image data available");
447362
447692
  }
447363
- await mkdir11(dirname15(outputPath), { recursive: true });
447693
+ await mkdir11(dirname16(outputPath), { recursive: true });
447364
447694
  await writeFile14(outputPath, buffer);
447365
447695
  }
447366
447696
  outputResult({ success: true, provider: "openai", images: result.images.map((img) => ({ url: img.url, revisedPrompt: img.revisedPrompt })), outputPath });
@@ -447396,7 +447726,7 @@ Examples:
447396
447726
  throw new Error("No image data available");
447397
447727
  }
447398
447728
  const outputPath = resolve25(process.cwd(), options.output);
447399
- await mkdir11(dirname15(outputPath), { recursive: true });
447729
+ await mkdir11(dirname16(outputPath), { recursive: true });
447400
447730
  await writeFile14(outputPath, buffer);
447401
447731
  saveSpinner.succeed(source_default.green(`Saved to: ${outputPath}`));
447402
447732
  } catch (err) {
@@ -447446,7 +447776,7 @@ Examples:
447446
447776
  if (outputPath && result.images.length > 0) {
447447
447777
  const img = result.images[0];
447448
447778
  const buffer = Buffer.from(img.base64, "base64");
447449
- await mkdir11(dirname15(outputPath), { recursive: true });
447779
+ await mkdir11(dirname16(outputPath), { recursive: true });
447450
447780
  await writeFile14(outputPath, buffer);
447451
447781
  }
447452
447782
  outputResult({ success: true, provider: "gemini", images: result.images.map((img) => ({ mimeType: img.mimeType })), outputPath });
@@ -447467,7 +447797,7 @@ Examples:
447467
447797
  const img = result.images[0];
447468
447798
  const buffer = Buffer.from(img.base64, "base64");
447469
447799
  const outputPath = resolve25(process.cwd(), options.output);
447470
- await mkdir11(dirname15(outputPath), { recursive: true });
447800
+ await mkdir11(dirname16(outputPath), { recursive: true });
447471
447801
  await writeFile14(outputPath, buffer);
447472
447802
  saveSpinner.succeed(source_default.green(`Saved to: ${outputPath}`));
447473
447803
  } catch (err) {
@@ -447506,7 +447836,7 @@ Examples:
447506
447836
  } else {
447507
447837
  throw new Error("No image data available");
447508
447838
  }
447509
- await mkdir11(dirname15(outputPath), { recursive: true });
447839
+ await mkdir11(dirname16(outputPath), { recursive: true });
447510
447840
  await writeFile14(outputPath, buffer);
447511
447841
  }
447512
447842
  outputResult({ success: true, provider: "grok", images: result.images.map((img) => ({ url: img.url })), outputPath });
@@ -447539,7 +447869,7 @@ Examples:
447539
447869
  throw new Error("No image data available");
447540
447870
  }
447541
447871
  const outputPath = resolve25(process.cwd(), options.output);
447542
- await mkdir11(dirname15(outputPath), { recursive: true });
447872
+ await mkdir11(dirname16(outputPath), { recursive: true });
447543
447873
  await writeFile14(outputPath, buffer);
447544
447874
  saveSpinner.succeed(source_default.green(`Saved to: ${outputPath}`));
447545
447875
  } catch (err) {
@@ -447549,7 +447879,7 @@ Examples:
447549
447879
  } else if (provider === "runway") {
447550
447880
  const { spawn: spawn10 } = await import("child_process");
447551
447881
  const __filename2 = fileURLToPath5(import.meta.url);
447552
- const __dirname3 = dirname15(__filename2);
447882
+ const __dirname3 = dirname16(__filename2);
447553
447883
  const scriptPath = resolve25(__dirname3, "../../../../.claude/skills/runway-video/scripts/image.py");
447554
447884
  if (!options.output) {
447555
447885
  spinner2.fail("Output path required for Runway");
@@ -448344,7 +448674,7 @@ Examples:
448344
448674
  throw new Error("No image data available");
448345
448675
  }
448346
448676
  outputPath = resolve25(process.cwd(), options.output);
448347
- await mkdir11(dirname15(outputPath), { recursive: true });
448677
+ await mkdir11(dirname16(outputPath), { recursive: true });
448348
448678
  await writeFile14(outputPath, buffer);
448349
448679
  }
448350
448680
  outputResult({ success: true, imageUrl: img.url, outputPath });
@@ -448371,7 +448701,7 @@ Examples:
448371
448701
  throw new Error("No image data available");
448372
448702
  }
448373
448703
  const outputPath = resolve25(process.cwd(), options.output);
448374
- await mkdir11(dirname15(outputPath), { recursive: true });
448704
+ await mkdir11(dirname16(outputPath), { recursive: true });
448375
448705
  await writeFile14(outputPath, buffer);
448376
448706
  saveSpinner.succeed(source_default.green(`Saved to: ${outputPath}`));
448377
448707
  } catch (err) {
@@ -448417,7 +448747,7 @@ Examples:
448417
448747
  throw new Error("No image data available");
448418
448748
  }
448419
448749
  outputPath = resolve25(process.cwd(), options.output);
448420
- await mkdir11(dirname15(outputPath), { recursive: true });
448750
+ await mkdir11(dirname16(outputPath), { recursive: true });
448421
448751
  await writeFile14(outputPath, buffer);
448422
448752
  }
448423
448753
  outputResult({ success: true, imageUrl: img.url, outputPath });
@@ -448444,7 +448774,7 @@ Examples:
448444
448774
  throw new Error("No image data available");
448445
448775
  }
448446
448776
  const outputPath = resolve25(process.cwd(), options.output);
448447
- await mkdir11(dirname15(outputPath), { recursive: true });
448777
+ await mkdir11(dirname16(outputPath), { recursive: true });
448448
448778
  await writeFile14(outputPath, buffer);
448449
448779
  saveSpinner.succeed(source_default.green(`Saved to: ${outputPath}`));
448450
448780
  } catch (err) {
@@ -449316,7 +449646,7 @@ var init_ai_edit_cli = __esm({
449316
449646
 
449317
449647
  // ../cli/src/commands/ai-fill-gaps.ts
449318
449648
  import { readFile as readFile14, writeFile as writeFile15, mkdir as mkdir12 } from "node:fs/promises";
449319
- import { resolve as resolve27, dirname as dirname16 } from "node:path";
449649
+ import { resolve as resolve27, dirname as dirname17 } from "node:path";
449320
449650
  import { existsSync as existsSync30 } from "node:fs";
449321
449651
  function detectVideoGaps(videoClips, totalDuration) {
449322
449652
  const gaps = [];
@@ -449460,7 +449790,7 @@ function registerFillGapsCommand(aiCommand) {
449460
449790
  if (!kling.isConfigured()) {
449461
449791
  exitWithError(authError("KLING_API_KEY", "Kling"));
449462
449792
  }
449463
- const projectDir = dirname16(filePath);
449793
+ const projectDir = dirname17(filePath);
449464
449794
  const footageDir = options.dir ? resolve27(process.cwd(), options.dir) : resolve27(projectDir, "footage");
449465
449795
  if (!existsSync30(footageDir)) {
449466
449796
  await mkdir12(footageDir, { recursive: true });
@@ -449709,7 +450039,7 @@ __export(edit_cmd_exports, {
449709
450039
  executeSpeedRamp: () => executeSpeedRamp,
449710
450040
  executeUpscale: () => executeUpscale
449711
450041
  });
449712
- import { resolve as resolve28, dirname as dirname17 } from "node:path";
450042
+ import { resolve as resolve28, dirname as dirname18 } from "node:path";
449713
450043
  import { readFile as readFile15, writeFile as writeFile16, mkdir as mkdir13 } from "node:fs/promises";
449714
450044
  async function executeGrade(options) {
449715
450045
  const { videoPath, style, preset, output: output3, analyzeOnly, apiKey } = options;
@@ -450421,7 +450751,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
450421
450751
  const img = result.images[0];
450422
450752
  const outputPath = resolve28(process.cwd(), options.output);
450423
450753
  const saveImage = async () => {
450424
- await mkdir13(dirname17(outputPath), { recursive: true });
450754
+ await mkdir13(dirname18(outputPath), { recursive: true });
450425
450755
  if (img.base64) {
450426
450756
  const buffer = Buffer.from(img.base64, "base64");
450427
450757
  await writeFile16(outputPath, buffer);
@@ -450612,7 +450942,7 @@ __export(ai_audio_exports, {
450612
450942
  executeVoiceClone: () => executeVoiceClone,
450613
450943
  registerAudioCommands: () => registerAudioCommands
450614
450944
  });
450615
- import { resolve as resolve29, dirname as dirname18, basename as basename11, extname as extname11 } from "node:path";
450945
+ import { resolve as resolve29, dirname as dirname19, basename as basename11, extname as extname11 } from "node:path";
450616
450946
  import { readFile as readFile16, writeFile as writeFile17 } from "node:fs/promises";
450617
450947
  import { existsSync as existsSync31 } from "node:fs";
450618
450948
  function _registerAudioCommands(aiCommand) {
@@ -451068,7 +451398,7 @@ function _registerAudioCommands(aiCommand) {
451068
451398
  const isVideo = [".mp4", ".mov", ".avi", ".mkv", ".webm"].includes(ext);
451069
451399
  let audioPath = absPath;
451070
451400
  if (isVideo) {
451071
- const tempAudioPath = resolve29(dirname18(absPath), `temp-audio-${Date.now()}.mp3`);
451401
+ const tempAudioPath = resolve29(dirname19(absPath), `temp-audio-${Date.now()}.mp3`);
451072
451402
  try {
451073
451403
  execSafeSync("ffmpeg", ["-i", absPath, "-vn", "-acodec", "mp3", "-y", tempAudioPath]);
451074
451404
  audioPath = tempAudioPath;
@@ -451200,7 +451530,7 @@ ${segmentTexts}`,
451200
451530
  const combinedBuffer = Buffer.concat(dubbedAudioBuffers.map((a) => a.buffer));
451201
451531
  const outputExt = isVideo ? ".mp3" : extname11(absPath);
451202
451532
  const defaultOutputPath = resolve29(
451203
- dirname18(absPath),
451533
+ dirname19(absPath),
451204
451534
  `${basename11(absPath, extname11(absPath))}-${options.language}${outputExt}`
451205
451535
  );
451206
451536
  const finalOutputPath = resolve29(process.cwd(), options.output || defaultOutputPath);
@@ -451363,7 +451693,7 @@ async function executeDub(options) {
451363
451693
  const isVideo = [".mp4", ".mov", ".avi", ".mkv", ".webm"].includes(ext);
451364
451694
  let audioPath = absPath;
451365
451695
  if (isVideo) {
451366
- const tempAudioPath = resolve29(dirname18(absPath), `temp-audio-${Date.now()}.mp3`);
451696
+ const tempAudioPath = resolve29(dirname19(absPath), `temp-audio-${Date.now()}.mp3`);
451367
451697
  execSafeSync("ffmpeg", ["-i", absPath, "-vn", "-acodec", "mp3", "-y", tempAudioPath]);
451368
451698
  audioPath = tempAudioPath;
451369
451699
  }
@@ -451445,7 +451775,7 @@ ${segmentTexts}`,
451445
451775
  }
451446
451776
  const combinedBuffer = Buffer.concat(dubbedBuffers);
451447
451777
  const outputExt = isVideo ? ".mp3" : extname11(absPath);
451448
- const defaultOutputPath = resolve29(dirname18(absPath), `${basename11(absPath, extname11(absPath))}-${language}${outputExt}`);
451778
+ const defaultOutputPath = resolve29(dirname19(absPath), `${basename11(absPath, extname11(absPath))}-${language}${outputExt}`);
451449
451779
  const finalOutputPath = resolve29(process.cwd(), output3 || defaultOutputPath);
451450
451780
  await writeFile17(finalOutputPath, combinedBuffer);
451451
451781
  if (isVideo && audioPath !== absPath) {
@@ -451482,7 +451812,7 @@ async function executeDuck(options) {
451482
451812
  const absVoicePath = resolve29(process.cwd(), voicePath);
451483
451813
  if (!existsSync31(absMusicPath)) return { success: false, error: `Music file not found: ${absMusicPath}` };
451484
451814
  if (!existsSync31(absVoicePath)) return { success: false, error: `Voice file not found: ${absVoicePath}` };
451485
- const defaultOutput = resolve29(dirname18(absMusicPath), `${basename11(absMusicPath, extname11(absMusicPath))}-ducked${extname11(absMusicPath)}`);
451815
+ const defaultOutput = resolve29(dirname19(absMusicPath), `${basename11(absMusicPath, extname11(absMusicPath))}-ducked${extname11(absMusicPath)}`);
451486
451816
  const outputPath = resolve29(process.cwd(), output3 || defaultOutput);
451487
451817
  const filter4 = `[1:a]asplit=2[sc][mix];[0:a][sc]sidechaincompress=threshold=${threshold}dB:ratio=${ratio}:attack=${attack}:release=${release}[ducked];[ducked][mix]amix=inputs=2:duration=longest`;
451488
451818
  await execSafe("ffmpeg", [
@@ -453637,7 +453967,7 @@ import { join as join21 } from "node:path";
453637
453967
 
453638
453968
  // ../cli/src/commands/ai-highlights.ts
453639
453969
  import { readFile as readFile10, writeFile as writeFile11, mkdir as mkdir10 } from "node:fs/promises";
453640
- import { resolve as resolve21, dirname as dirname14, basename as basename8, extname as extname8 } from "node:path";
453970
+ import { resolve as resolve21, dirname as dirname15, basename as basename8, extname as extname8 } from "node:path";
453641
453971
  import { existsSync as existsSync26 } from "node:fs";
453642
453972
  init_dist2();
453643
453973
  init_engine();
@@ -453999,7 +454329,7 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
453999
454329
  }))
454000
454330
  };
454001
454331
  }
454002
- const outputDir = options.outputDir ? resolve21(process.cwd(), options.outputDir) : dirname14(absPath);
454332
+ const outputDir = options.outputDir ? resolve21(process.cwd(), options.outputDir) : dirname15(absPath);
454003
454333
  if (options.outputDir && !existsSync26(outputDir)) {
454004
454334
  await mkdir10(outputDir, { recursive: true });
454005
454335
  }
@@ -454785,7 +455115,7 @@ init_ai_edit();
454785
455115
  init_api_key();
454786
455116
  init_exec_safe();
454787
455117
  init_remotion();
454788
- import { resolve as resolve33, dirname as dirname19, basename as basename13 } from "node:path";
455118
+ import { resolve as resolve33, dirname as dirname20, basename as basename13 } from "node:path";
454789
455119
  import { writeFile as writeFile21, mkdir as mkdir15, rm as rm5 } from "node:fs/promises";
454790
455120
  import { existsSync as existsSync34 } from "node:fs";
454791
455121
  import { tmpdir as tmpdir6 } from "node:os";
@@ -454967,7 +455297,7 @@ async function executeAnimatedCaption(options) {
454967
455297
  }
454968
455298
  const groups = groupWords(transcript.words, { wordsPerGroup, maxChars });
454969
455299
  const absOutputPath = resolve33(process.cwd(), outputPath);
454970
- const outDir = dirname19(absOutputPath);
455300
+ const outDir = dirname20(absOutputPath);
454971
455301
  if (!existsSync34(outDir)) {
454972
455302
  await mkdir15(outDir, { recursive: true });
454973
455303
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibeframe/mcp-server",
3
- "version": "0.48.5",
3
+ "version": "0.49.1",
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.48.5",
61
- "@vibeframe/core": "0.48.5"
60
+ "@vibeframe/cli": "0.49.1",
61
+ "@vibeframe/core": "0.49.1"
62
62
  },
63
63
  "engines": {
64
64
  "node": ">=20"