@vibeframe/cli 0.78.0 → 0.79.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.
- package/dist/index.js +207 -146
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -29771,7 +29771,7 @@ __export(remotion_exports, {
|
|
|
29771
29771
|
wrapComponentWithVideo: () => wrapComponentWithVideo
|
|
29772
29772
|
});
|
|
29773
29773
|
import { writeFile as writeFile10, mkdir as mkdir6, rm, copyFile as copyFile2 } from "node:fs/promises";
|
|
29774
|
-
import { existsSync as
|
|
29774
|
+
import { existsSync as existsSync9 } from "node:fs";
|
|
29775
29775
|
import { join as join7 } from "node:path";
|
|
29776
29776
|
import { tmpdir } from "node:os";
|
|
29777
29777
|
import { createHash as createHash3 } from "node:crypto";
|
|
@@ -29846,7 +29846,7 @@ registerRoot(Root);
|
|
|
29846
29846
|
const depsHash = createHash3("md5").update(packageJsonStr).digest("hex").slice(0, 12);
|
|
29847
29847
|
const cacheMarker = join7(REMOTION_CACHE_DIR, `.deps-${depsHash}`);
|
|
29848
29848
|
const cachedModules = join7(REMOTION_CACHE_DIR, "node_modules");
|
|
29849
|
-
if (
|
|
29849
|
+
if (existsSync9(cacheMarker) && existsSync9(cachedModules)) {
|
|
29850
29850
|
const { symlink } = await import("node:fs/promises");
|
|
29851
29851
|
try {
|
|
29852
29852
|
await symlink(cachedModules, join7(dir, "node_modules"), "dir");
|
|
@@ -29858,7 +29858,7 @@ registerRoot(Root);
|
|
|
29858
29858
|
try {
|
|
29859
29859
|
await mkdir6(REMOTION_CACHE_DIR, { recursive: true });
|
|
29860
29860
|
await writeFile10(join7(REMOTION_CACHE_DIR, "package.json"), packageJsonStr);
|
|
29861
|
-
if (
|
|
29861
|
+
if (existsSync9(join7(dir, "node_modules"))) {
|
|
29862
29862
|
const { rename: rename5, symlink } = await import("node:fs/promises");
|
|
29863
29863
|
await rename5(join7(dir, "node_modules"), cachedModules).catch(() => {
|
|
29864
29864
|
});
|
|
@@ -30526,7 +30526,7 @@ __export(ai_motion_exports, {
|
|
|
30526
30526
|
registerMotionCommand: () => registerMotionCommand
|
|
30527
30527
|
});
|
|
30528
30528
|
import { resolve as resolve12 } from "node:path";
|
|
30529
|
-
import { existsSync as
|
|
30529
|
+
import { existsSync as existsSync10 } from "node:fs";
|
|
30530
30530
|
import { readFile as readFile10, writeFile as writeFile11 } from "node:fs/promises";
|
|
30531
30531
|
async function executeMotion(options) {
|
|
30532
30532
|
const modelAlias = options.model || "sonnet";
|
|
@@ -30574,7 +30574,7 @@ Use this image analysis to inform the color palette, typography placement, and o
|
|
|
30574
30574
|
let result;
|
|
30575
30575
|
if (options.fromTsx) {
|
|
30576
30576
|
const tsxPath = resolve12(process.cwd(), options.fromTsx);
|
|
30577
|
-
if (!
|
|
30577
|
+
if (!existsSync10(tsxPath)) {
|
|
30578
30578
|
return { success: false, error: `TSX file not found: ${tsxPath}` };
|
|
30579
30579
|
}
|
|
30580
30580
|
const existingCode = await readFile10(tsxPath, "utf-8");
|
|
@@ -30709,6 +30709,33 @@ function registerMotionCommand(aiCommand) {
|
|
|
30709
30709
|
if (options.output) {
|
|
30710
30710
|
validateOutputPath(options.output);
|
|
30711
30711
|
}
|
|
30712
|
+
if (options.duration !== void 0) {
|
|
30713
|
+
const d = parseFloat(options.duration);
|
|
30714
|
+
if (!Number.isFinite(d) || d <= 0 || d > 60) {
|
|
30715
|
+
exitWithError(usageError(
|
|
30716
|
+
`Invalid --duration: ${options.duration}`,
|
|
30717
|
+
"Must be a positive number \u2264 60 seconds."
|
|
30718
|
+
));
|
|
30719
|
+
}
|
|
30720
|
+
}
|
|
30721
|
+
if (options.width !== void 0) {
|
|
30722
|
+
const w = parseInt(options.width, 10);
|
|
30723
|
+
if (!Number.isFinite(w) || w < 16 || w > 7680) {
|
|
30724
|
+
exitWithError(usageError(
|
|
30725
|
+
`Invalid --width: ${options.width}`,
|
|
30726
|
+
"Must be an integer between 16 and 7680."
|
|
30727
|
+
));
|
|
30728
|
+
}
|
|
30729
|
+
}
|
|
30730
|
+
if (options.height !== void 0) {
|
|
30731
|
+
const h = parseInt(options.height, 10);
|
|
30732
|
+
if (!Number.isFinite(h) || h < 16 || h > 4320) {
|
|
30733
|
+
exitWithError(usageError(
|
|
30734
|
+
`Invalid --height: ${options.height}`,
|
|
30735
|
+
"Must be an integer between 16 and 4320."
|
|
30736
|
+
));
|
|
30737
|
+
}
|
|
30738
|
+
}
|
|
30712
30739
|
if (options.dryRun) {
|
|
30713
30740
|
outputSuccess({
|
|
30714
30741
|
command: "generate motion",
|
|
@@ -31778,7 +31805,7 @@ var init_speech = __esm({
|
|
|
31778
31805
|
// src/commands/generate/music.ts
|
|
31779
31806
|
import { resolve as resolve17 } from "node:path";
|
|
31780
31807
|
import { writeFile as writeFile16 } from "node:fs/promises";
|
|
31781
|
-
import { existsSync as
|
|
31808
|
+
import { existsSync as existsSync11 } from "node:fs";
|
|
31782
31809
|
async function executeMusic(options) {
|
|
31783
31810
|
try {
|
|
31784
31811
|
const provider = options.provider || "elevenlabs";
|
|
@@ -31835,13 +31862,22 @@ async function executeMusic(options) {
|
|
|
31835
31862
|
}
|
|
31836
31863
|
}
|
|
31837
31864
|
function registerMusicCommand(parent) {
|
|
31838
|
-
parent.command("music").description("Generate background music from a text prompt (ElevenLabs or Replicate MusicGen)").argument("<prompt>", "Description of the music to generate").option("-p, --provider <provider>", "Provider: elevenlabs (default, up to 10min), replicate (MusicGen, max 30s)", "elevenlabs").option("-k, --api-key <key>", "API key (or set ELEVENLABS_API_KEY / REPLICATE_API_TOKEN env)").option("-d, --duration <seconds>", "Duration in seconds (elevenlabs: 3-600, replicate: 1-30)", "8").option("--instrumental", "Force instrumental music, no vocals (ElevenLabs only)").option("--melody <file>", "Reference melody audio file for conditioning (Replicate only)").option("--model <model>", "Model variant (Replicate only): large, stereo-large, melody-large, stereo-melody-large", "stereo-large").option("-o, --output <path>", "Output audio file path", "music.mp3").option("--no-wait", "Don't wait for generation to complete (Replicate async mode)").option("--dry-run", "Preview parameters without executing").action(async (prompt3, options) => {
|
|
31865
|
+
parent.command("music").description("Generate background music from a text prompt (ElevenLabs or Replicate MusicGen)").argument("<prompt>", "Description of the music to generate").option("-p, --provider <provider>", "Provider: elevenlabs (default, up to 10min), replicate (MusicGen, max 30s)", "elevenlabs").option("-k, --api-key <key>", "API key (or set ELEVENLABS_API_KEY / REPLICATE_API_TOKEN env)").option("-d, --duration <seconds>", "Duration in seconds (elevenlabs: 3-600, replicate: 1-30)", "8").option("--instrumental", "Force instrumental music, no vocals (ElevenLabs only)").option("--melody <file>", "Reference melody audio file for conditioning (Replicate only)").option("-m, --model <model>", "Model variant (Replicate only): large, stereo-large, melody-large, stereo-melody-large", "stereo-large").option("-o, --output <path>", "Output audio file path", "music.mp3").option("--no-wait", "Don't wait for generation to complete (Replicate async mode)").option("--dry-run", "Preview parameters without executing").action(async (prompt3, options) => {
|
|
31839
31866
|
const startedAt = Date.now();
|
|
31840
31867
|
try {
|
|
31841
31868
|
rejectControlChars(prompt3);
|
|
31842
31869
|
if (options.output) {
|
|
31843
31870
|
validateOutputPath(options.output);
|
|
31844
31871
|
}
|
|
31872
|
+
if (options.duration !== void 0) {
|
|
31873
|
+
const d = parseFloat(options.duration);
|
|
31874
|
+
if (!Number.isFinite(d) || d <= 0 || d > 600) {
|
|
31875
|
+
exitWithError(usageError(
|
|
31876
|
+
`Invalid --duration: ${options.duration}`,
|
|
31877
|
+
"Must be a positive number \u2264 600s (ElevenLabs 3-600, Replicate 1-30)."
|
|
31878
|
+
));
|
|
31879
|
+
}
|
|
31880
|
+
}
|
|
31845
31881
|
const provider = (options.provider || "elevenlabs").toLowerCase();
|
|
31846
31882
|
if (options.dryRun) {
|
|
31847
31883
|
outputSuccess({
|
|
@@ -31913,7 +31949,7 @@ function registerMusicCommand(parent) {
|
|
|
31913
31949
|
if (options.melody) {
|
|
31914
31950
|
spinner2.text = "Uploading melody reference...";
|
|
31915
31951
|
const absPath = resolve17(process.cwd(), options.melody);
|
|
31916
|
-
if (!
|
|
31952
|
+
if (!existsSync11(absPath)) {
|
|
31917
31953
|
spinner2.fail(`Melody file not found: ${options.melody}`);
|
|
31918
31954
|
exitWithError(notFoundError(options.melody));
|
|
31919
31955
|
}
|
|
@@ -32004,7 +32040,7 @@ __export(ai_image_exports, {
|
|
|
32004
32040
|
});
|
|
32005
32041
|
import { resolve as resolve18, dirname as dirname5 } from "node:path";
|
|
32006
32042
|
import { readFile as readFile12, writeFile as writeFile17, mkdir as mkdir8 } from "node:fs/promises";
|
|
32007
|
-
import { existsSync as
|
|
32043
|
+
import { existsSync as existsSync12 } from "node:fs";
|
|
32008
32044
|
async function executeImageGenerate(options) {
|
|
32009
32045
|
const {
|
|
32010
32046
|
prompt: prompt3,
|
|
@@ -32148,7 +32184,7 @@ async function executeGeminiEdit(options) {
|
|
|
32148
32184
|
const imageBuffers = [];
|
|
32149
32185
|
for (const imagePath of imagePaths) {
|
|
32150
32186
|
const absPath = resolve18(process.cwd(), imagePath);
|
|
32151
|
-
if (!
|
|
32187
|
+
if (!existsSync12(absPath)) {
|
|
32152
32188
|
return { success: false, error: `Image not found: ${absPath}` };
|
|
32153
32189
|
}
|
|
32154
32190
|
const buffer = await readFile12(absPath);
|
|
@@ -32196,7 +32232,7 @@ async function executeThumbnailBestFrame(options) {
|
|
|
32196
32232
|
model = "flash",
|
|
32197
32233
|
apiKey
|
|
32198
32234
|
} = options;
|
|
32199
|
-
if (!
|
|
32235
|
+
if (!existsSync12(videoPath)) {
|
|
32200
32236
|
return { success: false, error: `Video not found: ${videoPath}` };
|
|
32201
32237
|
}
|
|
32202
32238
|
if (!commandExists("ffmpeg")) {
|
|
@@ -32236,7 +32272,7 @@ async function executeThumbnailBestFrame(options) {
|
|
|
32236
32272
|
reason = reasonMatch[1];
|
|
32237
32273
|
}
|
|
32238
32274
|
await execSafe("ffmpeg", ["-ss", String(timestamp), "-i", videoPath, "-frames:v", "1", "-q:v", "2", outputPath, "-y"], { timeout: 6e4, maxBuffer: 50 * 1024 * 1024 });
|
|
32239
|
-
if (!
|
|
32275
|
+
if (!existsSync12(outputPath)) {
|
|
32240
32276
|
return { success: false, error: "FFmpeg failed to extract frame" };
|
|
32241
32277
|
}
|
|
32242
32278
|
return {
|
|
@@ -32262,7 +32298,7 @@ var init_ai_image = __esm({
|
|
|
32262
32298
|
|
|
32263
32299
|
// src/commands/generate/thumbnail.ts
|
|
32264
32300
|
import { resolve as resolve19, dirname as dirname6, basename as basename3, extname as extname2 } from "node:path";
|
|
32265
|
-
import { existsSync as
|
|
32301
|
+
import { existsSync as existsSync13 } from "node:fs";
|
|
32266
32302
|
import { writeFile as writeFile18, mkdir as mkdir9 } from "node:fs/promises";
|
|
32267
32303
|
function registerThumbnailCommand(parent) {
|
|
32268
32304
|
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) => {
|
|
@@ -32274,7 +32310,7 @@ function registerThumbnailCommand(parent) {
|
|
|
32274
32310
|
}
|
|
32275
32311
|
if (options.bestFrame) {
|
|
32276
32312
|
const absVideoPath = resolve19(process.cwd(), options.bestFrame);
|
|
32277
|
-
if (!
|
|
32313
|
+
if (!existsSync13(absVideoPath)) {
|
|
32278
32314
|
exitWithError(notFoundError(absVideoPath));
|
|
32279
32315
|
}
|
|
32280
32316
|
if (!commandExists("ffmpeg")) {
|
|
@@ -32975,6 +33011,15 @@ Examples:
|
|
|
32975
33011
|
if (options.output) {
|
|
32976
33012
|
validateOutputPath(options.output);
|
|
32977
33013
|
}
|
|
33014
|
+
if (options.count !== void 0) {
|
|
33015
|
+
const n = parseInt(options.count, 10);
|
|
33016
|
+
if (!Number.isFinite(n) || n < 1 || n > 10) {
|
|
33017
|
+
exitWithError(usageError(
|
|
33018
|
+
`Invalid --count: ${options.count}`,
|
|
33019
|
+
"Must be an integer between 1 and 10."
|
|
33020
|
+
));
|
|
33021
|
+
}
|
|
33022
|
+
}
|
|
32978
33023
|
const imageRegistry = getProvidersFor("image");
|
|
32979
33024
|
const validProviders = [...imageRegistry.map((p) => p.name), "runway"];
|
|
32980
33025
|
const providerEnvMap = Object.fromEntries(
|
|
@@ -34654,7 +34699,7 @@ var init_video_providers = __esm({
|
|
|
34654
34699
|
// src/commands/ai-script-pipeline.ts
|
|
34655
34700
|
import { readFile as readFile13, writeFile as writeFile23, unlink as unlink2, rename as rename2 } from "node:fs/promises";
|
|
34656
34701
|
import { resolve as resolve24, extname as extname3 } from "node:path";
|
|
34657
|
-
import { existsSync as
|
|
34702
|
+
import { existsSync as existsSync14 } from "node:fs";
|
|
34658
34703
|
async function executeRegenerateScene(options) {
|
|
34659
34704
|
const result = {
|
|
34660
34705
|
success: false,
|
|
@@ -34663,12 +34708,12 @@ async function executeRegenerateScene(options) {
|
|
|
34663
34708
|
};
|
|
34664
34709
|
try {
|
|
34665
34710
|
const outputDir = resolve24(process.cwd(), options.projectDir);
|
|
34666
|
-
if (!
|
|
34711
|
+
if (!existsSync14(outputDir)) {
|
|
34667
34712
|
return { ...result, error: `Project directory not found: ${outputDir}` };
|
|
34668
34713
|
}
|
|
34669
34714
|
const yamlPath = resolve24(outputDir, "storyboard.yaml");
|
|
34670
34715
|
const jsonPath = resolve24(outputDir, "storyboard.json");
|
|
34671
|
-
const storyboardPath =
|
|
34716
|
+
const storyboardPath = existsSync14(yamlPath) ? yamlPath : existsSync14(jsonPath) ? jsonPath : null;
|
|
34672
34717
|
if (!storyboardPath) {
|
|
34673
34718
|
return { ...result, error: `Storyboard not found in: ${outputDir} (expected storyboard.yaml or storyboard.json)` };
|
|
34674
34719
|
}
|
|
@@ -34761,14 +34806,14 @@ IMPORTANT - Character appearance must match exactly: ${characterDesc}`;
|
|
|
34761
34806
|
const refSceneNum = options.referenceScene;
|
|
34762
34807
|
if (refSceneNum && refSceneNum >= 1 && refSceneNum <= segments.length && refSceneNum !== sceneNum) {
|
|
34763
34808
|
const refImagePath = resolve24(outputDir, `scene-${refSceneNum}.png`);
|
|
34764
|
-
if (
|
|
34809
|
+
if (existsSync14(refImagePath)) {
|
|
34765
34810
|
referenceImageBuffer = await readFile13(refImagePath);
|
|
34766
34811
|
}
|
|
34767
34812
|
} else if (!refSceneNum) {
|
|
34768
34813
|
for (let i = 1; i <= segments.length; i++) {
|
|
34769
34814
|
if (i !== sceneNum) {
|
|
34770
34815
|
const otherImagePath = resolve24(outputDir, `scene-${i}.png`);
|
|
34771
|
-
if (
|
|
34816
|
+
if (existsSync14(otherImagePath)) {
|
|
34772
34817
|
referenceImageBuffer = await readFile13(otherImagePath);
|
|
34773
34818
|
break;
|
|
34774
34819
|
}
|
|
@@ -34857,7 +34902,7 @@ Generate the single-person scene image now.`;
|
|
|
34857
34902
|
}
|
|
34858
34903
|
if (regenerateVideo && videoApiKey) {
|
|
34859
34904
|
options.onProgress?.(`Scene ${sceneNum}: regenerating video (${options.generator || "grok"})...`);
|
|
34860
|
-
if (!
|
|
34905
|
+
if (!existsSync14(imagePath)) {
|
|
34861
34906
|
result.failedScenes.push(sceneNum);
|
|
34862
34907
|
continue;
|
|
34863
34908
|
}
|
|
@@ -35119,6 +35164,15 @@ Examples:
|
|
|
35119
35164
|
if (options.output) {
|
|
35120
35165
|
validateOutputPath(options.output);
|
|
35121
35166
|
}
|
|
35167
|
+
if (options.duration !== void 0) {
|
|
35168
|
+
const d = parseFloat(options.duration);
|
|
35169
|
+
if (!Number.isFinite(d) || d <= 0 || d > 60) {
|
|
35170
|
+
exitWithError(usageError(
|
|
35171
|
+
`Invalid --duration: ${options.duration}`,
|
|
35172
|
+
"Must be a positive number \u2264 60 seconds."
|
|
35173
|
+
));
|
|
35174
|
+
}
|
|
35175
|
+
}
|
|
35122
35176
|
const validProviders = ["runway", "kling", "veo", "grok", "seedance", "fal"];
|
|
35123
35177
|
const videoEnvMap = {
|
|
35124
35178
|
grok: "XAI_API_KEY",
|
|
@@ -35631,7 +35685,7 @@ Run 'vibe schema generate.<command>' for structured parameter info.
|
|
|
35631
35685
|
});
|
|
35632
35686
|
|
|
35633
35687
|
// src/commands/_shared/edit/silence-cut.ts
|
|
35634
|
-
import { existsSync as
|
|
35688
|
+
import { existsSync as existsSync15 } from "node:fs";
|
|
35635
35689
|
import { readFile as readFile15 } from "node:fs/promises";
|
|
35636
35690
|
async function detectSilencePeriods(videoPath, noiseThreshold, minDuration) {
|
|
35637
35691
|
const totalDuration = await getVideoDuration(videoPath);
|
|
@@ -35763,7 +35817,7 @@ async function executeSilenceCut(options) {
|
|
|
35763
35817
|
analyzeOnly = false,
|
|
35764
35818
|
useGemini = false
|
|
35765
35819
|
} = options;
|
|
35766
|
-
if (!
|
|
35820
|
+
if (!existsSync15(videoPath)) {
|
|
35767
35821
|
return { success: false, error: `Video not found: ${videoPath}` };
|
|
35768
35822
|
}
|
|
35769
35823
|
if (!commandExists("ffmpeg")) {
|
|
@@ -35871,7 +35925,7 @@ var init_silence_cut = __esm({
|
|
|
35871
35925
|
});
|
|
35872
35926
|
|
|
35873
35927
|
// src/commands/_shared/edit/jump-cut.ts
|
|
35874
|
-
import { existsSync as
|
|
35928
|
+
import { existsSync as existsSync16 } from "node:fs";
|
|
35875
35929
|
import { readFile as readFile16, writeFile as writeFile25, mkdir as mkdir11 } from "node:fs/promises";
|
|
35876
35930
|
import { join as join8 } from "node:path";
|
|
35877
35931
|
async function transcribeWithWords(audioPath, apiKey, language) {
|
|
@@ -35934,7 +35988,7 @@ async function executeJumpCut(options) {
|
|
|
35934
35988
|
analyzeOnly = false,
|
|
35935
35989
|
apiKey
|
|
35936
35990
|
} = options;
|
|
35937
|
-
if (!
|
|
35991
|
+
if (!existsSync16(videoPath)) {
|
|
35938
35992
|
return { success: false, error: `Video not found: ${videoPath}` };
|
|
35939
35993
|
}
|
|
35940
35994
|
if (!commandExists("ffmpeg")) {
|
|
@@ -36220,7 +36274,7 @@ var init_helpers = __esm({
|
|
|
36220
36274
|
});
|
|
36221
36275
|
|
|
36222
36276
|
// src/commands/_shared/edit/caption.ts
|
|
36223
|
-
import { existsSync as
|
|
36277
|
+
import { existsSync as existsSync17 } from "node:fs";
|
|
36224
36278
|
import { readFile as readFile17, writeFile as writeFile26, mkdir as mkdir12 } from "node:fs/promises";
|
|
36225
36279
|
import { dirname as dirname8, basename as basename5, extname as extname4, join as join9 } from "node:path";
|
|
36226
36280
|
function getCaptionForceStyle(style, fontSize, fontColor, position) {
|
|
@@ -36250,7 +36304,7 @@ async function executeCaption(options) {
|
|
|
36250
36304
|
position = "bottom",
|
|
36251
36305
|
apiKey
|
|
36252
36306
|
} = options;
|
|
36253
|
-
if (!
|
|
36307
|
+
if (!existsSync17(videoPath)) {
|
|
36254
36308
|
return { success: false, error: `Video not found: ${videoPath}` };
|
|
36255
36309
|
}
|
|
36256
36310
|
if (!commandExists("ffmpeg")) {
|
|
@@ -36428,10 +36482,10 @@ var init_caption = __esm({
|
|
|
36428
36482
|
});
|
|
36429
36483
|
|
|
36430
36484
|
// src/commands/_shared/edit/noise-reduce.ts
|
|
36431
|
-
import { existsSync as
|
|
36485
|
+
import { existsSync as existsSync18 } from "node:fs";
|
|
36432
36486
|
async function executeNoiseReduce(options) {
|
|
36433
36487
|
const { inputPath, outputPath, strength = "medium", noiseFloor } = options;
|
|
36434
|
-
if (!
|
|
36488
|
+
if (!existsSync18(inputPath)) {
|
|
36435
36489
|
return { success: false, error: `File not found: ${inputPath}` };
|
|
36436
36490
|
}
|
|
36437
36491
|
if (!commandExists("ffmpeg")) {
|
|
@@ -36488,7 +36542,7 @@ var init_noise_reduce = __esm({
|
|
|
36488
36542
|
});
|
|
36489
36543
|
|
|
36490
36544
|
// src/commands/_shared/edit/fade.ts
|
|
36491
|
-
import { existsSync as
|
|
36545
|
+
import { existsSync as existsSync19 } from "node:fs";
|
|
36492
36546
|
async function executeFade(options) {
|
|
36493
36547
|
const {
|
|
36494
36548
|
videoPath,
|
|
@@ -36498,7 +36552,7 @@ async function executeFade(options) {
|
|
|
36498
36552
|
audioOnly = false,
|
|
36499
36553
|
videoOnly = false
|
|
36500
36554
|
} = options;
|
|
36501
|
-
if (!
|
|
36555
|
+
if (!existsSync19(videoPath)) {
|
|
36502
36556
|
return { success: false, error: `Video not found: ${videoPath}` };
|
|
36503
36557
|
}
|
|
36504
36558
|
if (!commandExists("ffmpeg")) {
|
|
@@ -36565,7 +36619,7 @@ var init_fade = __esm({
|
|
|
36565
36619
|
});
|
|
36566
36620
|
|
|
36567
36621
|
// src/commands/_shared/edit/translate-srt.ts
|
|
36568
|
-
import { existsSync as
|
|
36622
|
+
import { existsSync as existsSync20 } from "node:fs";
|
|
36569
36623
|
import { readFile as readFile18, writeFile as writeFile27 } from "node:fs/promises";
|
|
36570
36624
|
async function executeTranslateSrt(options) {
|
|
36571
36625
|
const {
|
|
@@ -36576,7 +36630,7 @@ async function executeTranslateSrt(options) {
|
|
|
36576
36630
|
sourceLanguage,
|
|
36577
36631
|
apiKey
|
|
36578
36632
|
} = options;
|
|
36579
|
-
if (!
|
|
36633
|
+
if (!existsSync20(srtPath)) {
|
|
36580
36634
|
return { success: false, error: `SRT file not found: ${srtPath}` };
|
|
36581
36635
|
}
|
|
36582
36636
|
try {
|
|
@@ -36693,7 +36747,7 @@ var init_translate_srt = __esm({
|
|
|
36693
36747
|
});
|
|
36694
36748
|
|
|
36695
36749
|
// src/commands/_shared/edit/text-overlay.ts
|
|
36696
|
-
import { existsSync as
|
|
36750
|
+
import { existsSync as existsSync21 } from "node:fs";
|
|
36697
36751
|
import { resolve as resolve26 } from "node:path";
|
|
36698
36752
|
function detectSystemFont() {
|
|
36699
36753
|
const platform = process.platform;
|
|
@@ -36704,7 +36758,7 @@ function detectSystemFont() {
|
|
|
36704
36758
|
"/Library/Fonts/Arial.ttf"
|
|
36705
36759
|
];
|
|
36706
36760
|
for (const f of candidates) {
|
|
36707
|
-
if (
|
|
36761
|
+
if (existsSync21(f)) return f;
|
|
36708
36762
|
}
|
|
36709
36763
|
} else if (platform === "linux") {
|
|
36710
36764
|
const candidates = [
|
|
@@ -36713,7 +36767,7 @@ function detectSystemFont() {
|
|
|
36713
36767
|
"/usr/share/fonts/TTF/DejaVuSans-Bold.ttf"
|
|
36714
36768
|
];
|
|
36715
36769
|
for (const f of candidates) {
|
|
36716
|
-
if (
|
|
36770
|
+
if (existsSync21(f)) return f;
|
|
36717
36771
|
}
|
|
36718
36772
|
} else if (platform === "win32") {
|
|
36719
36773
|
const candidates = [
|
|
@@ -36721,7 +36775,7 @@ function detectSystemFont() {
|
|
|
36721
36775
|
"C:\\Windows\\Fonts\\segoeui.ttf"
|
|
36722
36776
|
];
|
|
36723
36777
|
for (const f of candidates) {
|
|
36724
|
-
if (
|
|
36778
|
+
if (existsSync21(f)) return f;
|
|
36725
36779
|
}
|
|
36726
36780
|
}
|
|
36727
36781
|
return null;
|
|
@@ -36742,7 +36796,7 @@ async function applyTextOverlays(options) {
|
|
|
36742
36796
|
}
|
|
36743
36797
|
const absVideoPath = resolve26(process.cwd(), videoPath);
|
|
36744
36798
|
const absOutputPath = resolve26(process.cwd(), outputPath);
|
|
36745
|
-
if (!
|
|
36799
|
+
if (!existsSync21(absVideoPath)) {
|
|
36746
36800
|
return { success: false, error: `Video not found: ${absVideoPath}` };
|
|
36747
36801
|
}
|
|
36748
36802
|
if (!commandExists("ffmpeg")) {
|
|
@@ -36923,12 +36977,12 @@ var init_ai_edit = __esm({
|
|
|
36923
36977
|
|
|
36924
36978
|
// src/commands/ai-edit-cli.ts
|
|
36925
36979
|
import { resolve as resolve27, extname as extname5, basename as basename6 } from "node:path";
|
|
36926
|
-
import { existsSync as
|
|
36980
|
+
import { existsSync as existsSync22 } from "node:fs";
|
|
36927
36981
|
function registerEditCommands(aiCommand) {
|
|
36928
36982
|
aiCommand.command("silence-cut").alias("sc").description("Remove silent segments from video (FFmpeg default, or Gemini for smart detection)").argument("<video>", "Video file path").option("-o, --output <path>", "Output file path (default: <name>-cut.<ext>)").option("--noise <dB>", "Silence threshold in dB (default: -30)", "-30").option("-d, --min-duration <seconds>", "Minimum silence duration to cut (default: 0.5)", "0.5").option("--padding <seconds>", "Padding around non-silent segments (default: 0.1)", "0.1").option("--analyze-only", "Only detect silence, don't cut").option("--use-gemini", "Use Gemini Video Understanding for context-aware silence detection").option("-m, --model <model>", "Gemini model (default: flash)").option("--low-res", "Low resolution mode for longer videos (Gemini only)").option("-k, --api-key <key>", "Google API key override (or set GOOGLE_API_KEY env)").option("--dry-run", "Preview parameters without executing").addHelpText("after", `
|
|
36929
36983
|
Examples:
|
|
36930
36984
|
$ vibe edit silence-cut interview.mp4 -o clean.mp4
|
|
36931
|
-
$ vibe ed sc podcast.mp4 -o trimmed.mp4
|
|
36985
|
+
$ vibe ed sc podcast.mp4 -o trimmed.mp4 --noise -25 --min-duration 1.0
|
|
36932
36986
|
$ vibe ed sc video.mp4 --use-gemini -o smart-cut.mp4 # AI-powered detection
|
|
36933
36987
|
$ vibe ed sc video.mp4 --analyze-only # Detect only, no cut
|
|
36934
36988
|
$ vibe ed sc video.mp4 --dry-run --json
|
|
@@ -36940,7 +36994,7 @@ No API key needed (FFmpeg only). Use --use-gemini for smart detection (requires
|
|
|
36940
36994
|
validateOutputPath(options.output);
|
|
36941
36995
|
}
|
|
36942
36996
|
const absVideoPath = resolve27(process.cwd(), videoPath);
|
|
36943
|
-
if (!
|
|
36997
|
+
if (!existsSync22(absVideoPath)) {
|
|
36944
36998
|
exitWithError(notFoundError(absVideoPath));
|
|
36945
36999
|
}
|
|
36946
37000
|
if (!commandExists("ffmpeg")) {
|
|
@@ -37029,9 +37083,9 @@ No API key needed (FFmpeg only). Use --use-gemini for smart detection (requires
|
|
|
37029
37083
|
aiCommand.command("caption").alias("cap").description("Transcribe and burn styled captions onto video (Whisper + FFmpeg)").argument("<video>", "Video file path").option("-o, --output <path>", "Output file path (default: <name>-captioned.<ext>)").option("--style <style>", "Caption style: minimal, bold, outline, karaoke (default: bold)", "bold").option("--font-size <pixels>", "Override auto-calculated font size").option("--color <color>", "Font color (default: white)", "white").option("-l, --language <lang>", "Language code for transcription (e.g., en, ko)").option("--position <pos>", "Caption position: top, center, bottom (default: bottom)", "bottom").option("-k, --api-key <key>", "OpenAI API key (or set OPENAI_API_KEY env)").option("--dry-run", "Preview parameters without executing").addHelpText("after", `
|
|
37030
37084
|
Examples:
|
|
37031
37085
|
$ vibe edit caption video.mp4 -o captioned.mp4
|
|
37032
|
-
$ vibe ed cap video.mp4 -o out.mp4
|
|
37086
|
+
$ vibe ed cap video.mp4 -o out.mp4 --style bold --position top
|
|
37033
37087
|
$ vibe ed cap video.mp4 -o out.mp4 -l ko # Korean transcription
|
|
37034
|
-
$ vibe ed cap video.mp4 -o out.mp4
|
|
37088
|
+
$ vibe ed cap video.mp4 -o out.mp4 --style karaoke # Karaoke-style
|
|
37035
37089
|
$ vibe ed cap video.mp4 --dry-run --json
|
|
37036
37090
|
|
|
37037
37091
|
Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoPath, options) => {
|
|
@@ -37041,7 +37095,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
37041
37095
|
validateOutputPath(options.output);
|
|
37042
37096
|
}
|
|
37043
37097
|
const absVideoPath = resolve27(process.cwd(), videoPath);
|
|
37044
|
-
if (!
|
|
37098
|
+
if (!existsSync22(absVideoPath)) {
|
|
37045
37099
|
exitWithError(notFoundError(absVideoPath));
|
|
37046
37100
|
}
|
|
37047
37101
|
if (!commandExists("ffmpeg")) {
|
|
@@ -37122,7 +37176,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
37122
37176
|
validateOutputPath(options.output);
|
|
37123
37177
|
}
|
|
37124
37178
|
const absInputPath = resolve27(process.cwd(), inputPath);
|
|
37125
|
-
if (!
|
|
37179
|
+
if (!existsSync22(absInputPath)) {
|
|
37126
37180
|
exitWithError(notFoundError(absInputPath));
|
|
37127
37181
|
}
|
|
37128
37182
|
if (!commandExists("ffmpeg")) {
|
|
@@ -37188,7 +37242,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
37188
37242
|
validateOutputPath(options.output);
|
|
37189
37243
|
}
|
|
37190
37244
|
const absVideoPath = resolve27(process.cwd(), videoPath);
|
|
37191
|
-
if (!
|
|
37245
|
+
if (!existsSync22(absVideoPath)) {
|
|
37192
37246
|
exitWithError(notFoundError(absVideoPath));
|
|
37193
37247
|
}
|
|
37194
37248
|
if (!commandExists("ffmpeg")) {
|
|
@@ -37263,7 +37317,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
37263
37317
|
exitWithError(usageError("Target language required. Use -t or --target"));
|
|
37264
37318
|
}
|
|
37265
37319
|
const absSrtPath = resolve27(process.cwd(), srtPath);
|
|
37266
|
-
if (!
|
|
37320
|
+
if (!existsSync22(absSrtPath)) {
|
|
37267
37321
|
exitWithError(notFoundError(absSrtPath));
|
|
37268
37322
|
}
|
|
37269
37323
|
if (options.dryRun) {
|
|
@@ -37339,7 +37393,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
37339
37393
|
}
|
|
37340
37394
|
if (options.fillers) rejectControlChars(options.fillers);
|
|
37341
37395
|
const absVideoPath = resolve27(process.cwd(), videoPath);
|
|
37342
|
-
if (!
|
|
37396
|
+
if (!existsSync22(absVideoPath)) {
|
|
37343
37397
|
exitWithError(notFoundError(absVideoPath));
|
|
37344
37398
|
}
|
|
37345
37399
|
if (!commandExists("ffmpeg")) {
|
|
@@ -37439,7 +37493,7 @@ var init_ai_edit_cli = __esm({
|
|
|
37439
37493
|
});
|
|
37440
37494
|
|
|
37441
37495
|
// src/commands/_shared/execute-fill-gaps.ts
|
|
37442
|
-
import { existsSync as
|
|
37496
|
+
import { existsSync as existsSync23 } from "node:fs";
|
|
37443
37497
|
import { readFile as readFile19, writeFile as writeFile28, mkdir as mkdir13, rename as renameFs } from "node:fs/promises";
|
|
37444
37498
|
import { resolve as resolve28, dirname as dirname9 } from "node:path";
|
|
37445
37499
|
function detectVideoGaps(videoClips, totalDuration) {
|
|
@@ -37522,7 +37576,7 @@ async function executeFillGaps(options) {
|
|
|
37522
37576
|
try {
|
|
37523
37577
|
onProgress("Loading project...");
|
|
37524
37578
|
const filePath = resolve28(process.cwd(), options.projectPath);
|
|
37525
|
-
if (!
|
|
37579
|
+
if (!existsSync23(filePath)) {
|
|
37526
37580
|
return {
|
|
37527
37581
|
success: false,
|
|
37528
37582
|
error: `Project file not found: ${filePath}`,
|
|
@@ -37657,7 +37711,7 @@ async function executeFillGaps(options) {
|
|
|
37657
37711
|
}
|
|
37658
37712
|
const projectDir = dirname9(filePath);
|
|
37659
37713
|
const footageDir = options.dir ? resolve28(process.cwd(), options.dir) : resolve28(projectDir, "footage");
|
|
37660
|
-
if (!
|
|
37714
|
+
if (!existsSync23(footageDir)) {
|
|
37661
37715
|
await mkdir13(footageDir, { recursive: true });
|
|
37662
37716
|
}
|
|
37663
37717
|
humanLines.push("Generating AI Videos");
|
|
@@ -38178,12 +38232,12 @@ var init_edit_cmd = __esm({
|
|
|
38178
38232
|
`
|
|
38179
38233
|
Examples:
|
|
38180
38234
|
$ vibe edit silence-cut interview.mp4 -o clean.mp4
|
|
38181
|
-
$ vibe edit caption video.mp4 -o captioned.mp4
|
|
38235
|
+
$ vibe edit caption video.mp4 -o captioned.mp4 --style bold
|
|
38182
38236
|
$ vibe edit grade video.mp4 -o graded.mp4 --preset cinematic-warm
|
|
38183
38237
|
$ vibe edit reframe landscape.mp4 -o vertical.mp4 -a 9:16
|
|
38184
38238
|
$ vibe edit image photo.png "add sunset background" -o edited.png
|
|
38185
|
-
$ vibe edit text-overlay video.mp4
|
|
38186
|
-
$ vibe edit noise-reduce noisy.mp4 -o clean.mp4
|
|
38239
|
+
$ vibe edit text-overlay video.mp4 --text "Title" --style center-bold -o out.mp4
|
|
38240
|
+
$ vibe edit noise-reduce noisy.mp4 -o clean.mp4 --strength high
|
|
38187
38241
|
$ vibe edit fade video.mp4 -o faded.mp4 --fade-in 1 --fade-out 1
|
|
38188
38242
|
|
|
38189
38243
|
API Keys (varies by subcommand):
|
|
@@ -38930,7 +38984,7 @@ __export(ai_analyze_exports, {
|
|
|
38930
38984
|
});
|
|
38931
38985
|
import { readFile as readFile21 } from "node:fs/promises";
|
|
38932
38986
|
import { extname as extname6, resolve as resolve30 } from "node:path";
|
|
38933
|
-
import { existsSync as
|
|
38987
|
+
import { existsSync as existsSync24 } from "node:fs";
|
|
38934
38988
|
async function executeGeminiVideo(options) {
|
|
38935
38989
|
try {
|
|
38936
38990
|
const apiKey = await getApiKey("GOOGLE_API_KEY", "Google");
|
|
@@ -38949,7 +39003,7 @@ async function executeGeminiVideo(options) {
|
|
|
38949
39003
|
videoData = options.source;
|
|
38950
39004
|
} else {
|
|
38951
39005
|
const absPath = resolve30(process.cwd(), options.source);
|
|
38952
|
-
if (!
|
|
39006
|
+
if (!existsSync24(absPath)) {
|
|
38953
39007
|
return { success: false, error: `File not found: ${absPath}` };
|
|
38954
39008
|
}
|
|
38955
39009
|
videoData = await readFile21(absPath);
|
|
@@ -39022,7 +39076,7 @@ async function executeAnalyze(options) {
|
|
|
39022
39076
|
imageBuffer = Buffer.from(await response.arrayBuffer());
|
|
39023
39077
|
} else {
|
|
39024
39078
|
const absPath = resolve30(process.cwd(), source);
|
|
39025
|
-
if (!
|
|
39079
|
+
if (!existsSync24(absPath)) {
|
|
39026
39080
|
return { success: false, error: `File not found: ${absPath}` };
|
|
39027
39081
|
}
|
|
39028
39082
|
imageBuffer = await readFile21(absPath);
|
|
@@ -39057,7 +39111,7 @@ async function executeAnalyze(options) {
|
|
|
39057
39111
|
videoData = Buffer.from(await response.arrayBuffer());
|
|
39058
39112
|
} else {
|
|
39059
39113
|
const absPath = resolve30(process.cwd(), source);
|
|
39060
|
-
if (!
|
|
39114
|
+
if (!existsSync24(absPath)) {
|
|
39061
39115
|
return { success: false, error: `File not found: ${absPath}` };
|
|
39062
39116
|
}
|
|
39063
39117
|
videoData = await readFile21(absPath);
|
|
@@ -39196,7 +39250,7 @@ __export(ai_review_exports, {
|
|
|
39196
39250
|
});
|
|
39197
39251
|
import { readFile as readFile22, rename as rename3 } from "node:fs/promises";
|
|
39198
39252
|
import { resolve as resolve31 } from "node:path";
|
|
39199
|
-
import { existsSync as
|
|
39253
|
+
import { existsSync as existsSync25 } from "node:fs";
|
|
39200
39254
|
function parseReviewFeedback(response) {
|
|
39201
39255
|
let cleaned = response.trim();
|
|
39202
39256
|
if (cleaned.startsWith("```json")) {
|
|
@@ -39221,7 +39275,7 @@ function parseReviewFeedback(response) {
|
|
|
39221
39275
|
async function executeReview(options) {
|
|
39222
39276
|
const { videoPath, storyboardPath, autoApply = false, verify = false, model = "flash" } = options;
|
|
39223
39277
|
const absVideoPath = resolve31(process.cwd(), videoPath);
|
|
39224
|
-
if (!
|
|
39278
|
+
if (!existsSync25(absVideoPath)) {
|
|
39225
39279
|
return { success: false, error: `Video not found: ${absVideoPath}` };
|
|
39226
39280
|
}
|
|
39227
39281
|
const apiKey = process.env.GOOGLE_API_KEY || await getApiKey("GOOGLE_API_KEY", "Google");
|
|
@@ -39231,7 +39285,7 @@ async function executeReview(options) {
|
|
|
39231
39285
|
let storyboardContext = "";
|
|
39232
39286
|
if (storyboardPath) {
|
|
39233
39287
|
const absStoryboardPath = resolve31(process.cwd(), storyboardPath);
|
|
39234
|
-
if (
|
|
39288
|
+
if (existsSync25(absStoryboardPath)) {
|
|
39235
39289
|
const content = await readFile22(absStoryboardPath, "utf-8");
|
|
39236
39290
|
storyboardContext = `
|
|
39237
39291
|
|
|
@@ -39437,7 +39491,7 @@ var init_ai_review = __esm({
|
|
|
39437
39491
|
// src/commands/ai-highlights.ts
|
|
39438
39492
|
import { readFile as readFile26, writeFile as writeFile33, mkdir as mkdir15 } from "node:fs/promises";
|
|
39439
39493
|
import { resolve as resolve35, dirname as dirname12, basename as basename8, extname as extname8 } from "node:path";
|
|
39440
|
-
import { existsSync as
|
|
39494
|
+
import { existsSync as existsSync28 } from "node:fs";
|
|
39441
39495
|
function filterHighlights(highlights, options) {
|
|
39442
39496
|
let filtered = highlights.filter((h) => h.confidence >= options.threshold);
|
|
39443
39497
|
filtered.sort((a, b) => b.confidence - a.confidence);
|
|
@@ -39477,7 +39531,7 @@ function truncate(text, maxLength) {
|
|
|
39477
39531
|
async function executeHighlights(options) {
|
|
39478
39532
|
try {
|
|
39479
39533
|
const absPath = resolve35(process.cwd(), options.media);
|
|
39480
|
-
if (!
|
|
39534
|
+
if (!existsSync28(absPath)) {
|
|
39481
39535
|
return { success: false, highlights: [], totalDuration: 0, totalHighlightDuration: 0, error: `File not found: ${absPath}` };
|
|
39482
39536
|
}
|
|
39483
39537
|
const ext = extname8(absPath).toLowerCase();
|
|
@@ -39593,7 +39647,7 @@ Analyze both what is SHOWN (visual cues, actions, expressions) and what is SAID
|
|
|
39593
39647
|
const audioBuffer = await readFile26(audioPath);
|
|
39594
39648
|
const audioBlob = new Blob([audioBuffer]);
|
|
39595
39649
|
const transcriptResult = await whisper.transcribe(audioBlob, options.language);
|
|
39596
|
-
if (tempAudioPath &&
|
|
39650
|
+
if (tempAudioPath && existsSync28(tempAudioPath)) {
|
|
39597
39651
|
const { unlink: unlinkFile } = await import("node:fs/promises");
|
|
39598
39652
|
await unlinkFile(tempAudioPath).catch(() => {
|
|
39599
39653
|
});
|
|
@@ -39680,7 +39734,7 @@ async function executeAutoShorts(options) {
|
|
|
39680
39734
|
return { success: false, shorts: [], error: "FFmpeg not found" };
|
|
39681
39735
|
}
|
|
39682
39736
|
const absPath = resolve35(process.cwd(), options.video);
|
|
39683
|
-
if (!
|
|
39737
|
+
if (!existsSync28(absPath)) {
|
|
39684
39738
|
return { success: false, shorts: [], error: `File not found: ${absPath}` };
|
|
39685
39739
|
}
|
|
39686
39740
|
const targetDuration = options.duration ?? 60;
|
|
@@ -39808,7 +39862,7 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
|
|
|
39808
39862
|
};
|
|
39809
39863
|
}
|
|
39810
39864
|
const outputDir = options.outputDir ? resolve35(process.cwd(), options.outputDir) : dirname12(absPath);
|
|
39811
|
-
if (options.outputDir && !
|
|
39865
|
+
if (options.outputDir && !existsSync28(outputDir)) {
|
|
39812
39866
|
await mkdir15(outputDir, { recursive: true });
|
|
39813
39867
|
}
|
|
39814
39868
|
const result = {
|
|
@@ -39891,7 +39945,7 @@ function registerHighlightsCommands(aiCommand) {
|
|
|
39891
39945
|
validateOutputPath(options.output);
|
|
39892
39946
|
}
|
|
39893
39947
|
const absPath = resolve35(process.cwd(), mediaPath);
|
|
39894
|
-
if (!
|
|
39948
|
+
if (!existsSync28(absPath)) {
|
|
39895
39949
|
exitWithError(notFoundError(absPath));
|
|
39896
39950
|
}
|
|
39897
39951
|
if (options.dryRun) {
|
|
@@ -40078,7 +40132,7 @@ Analyze both what is SHOWN (visual cues, actions, expressions) and what is SAID
|
|
|
40078
40132
|
const transcriptResult = await whisper.transcribe(audioBlob, options.language);
|
|
40079
40133
|
if (transcriptResult.status === "failed" || !transcriptResult.segments) {
|
|
40080
40134
|
transcribeSpinner.fail("Transcription failed");
|
|
40081
|
-
if (tempAudioPath &&
|
|
40135
|
+
if (tempAudioPath && existsSync28(tempAudioPath)) {
|
|
40082
40136
|
const { unlink: unlinkFile } = await import("node:fs/promises");
|
|
40083
40137
|
await unlinkFile(tempAudioPath).catch(() => {
|
|
40084
40138
|
});
|
|
@@ -40086,7 +40140,7 @@ Analyze both what is SHOWN (visual cues, actions, expressions) and what is SAID
|
|
|
40086
40140
|
exitWithError(apiError(`Transcription failed: ${transcriptResult.error}`, true));
|
|
40087
40141
|
}
|
|
40088
40142
|
transcribeSpinner.succeed(source_default.green(`Transcribed ${transcriptResult.segments.length} segments`));
|
|
40089
|
-
if (tempAudioPath &&
|
|
40143
|
+
if (tempAudioPath && existsSync28(tempAudioPath)) {
|
|
40090
40144
|
const { unlink: unlinkFile } = await import("node:fs/promises");
|
|
40091
40145
|
await unlinkFile(tempAudioPath).catch(() => {
|
|
40092
40146
|
});
|
|
@@ -40192,7 +40246,7 @@ Analyze both what is SHOWN (visual cues, actions, expressions) and what is SAID
|
|
|
40192
40246
|
validateOutputPath(options.output);
|
|
40193
40247
|
}
|
|
40194
40248
|
const absPath = resolve35(process.cwd(), videoPath);
|
|
40195
|
-
if (!
|
|
40249
|
+
if (!existsSync28(absPath)) {
|
|
40196
40250
|
exitWithError(notFoundError(absPath));
|
|
40197
40251
|
}
|
|
40198
40252
|
if (options.dryRun) {
|
|
@@ -40385,7 +40439,7 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
|
|
|
40385
40439
|
return;
|
|
40386
40440
|
}
|
|
40387
40441
|
const outputDir = options.outputDir ? resolve35(process.cwd(), options.outputDir) : dirname12(absPath);
|
|
40388
|
-
if (options.outputDir && !
|
|
40442
|
+
if (options.outputDir && !existsSync28(outputDir)) {
|
|
40389
40443
|
await mkdir15(outputDir, { recursive: true });
|
|
40390
40444
|
}
|
|
40391
40445
|
for (let i = 0; i < selectedHighlights.length; i++) {
|
|
@@ -40402,7 +40456,7 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
|
|
|
40402
40456
|
outputPath = resolve35(outputDir, `${baseName}-short-${i + 1}.mp4`);
|
|
40403
40457
|
}
|
|
40404
40458
|
const parentDir = dirname12(outputPath);
|
|
40405
|
-
if (!
|
|
40459
|
+
if (!existsSync28(parentDir)) {
|
|
40406
40460
|
await mkdir15(parentDir, { recursive: true });
|
|
40407
40461
|
}
|
|
40408
40462
|
const { stdout: probeOut } = await execSafe("ffprobe", [
|
|
@@ -40477,7 +40531,7 @@ var init_ai_highlights = __esm({
|
|
|
40477
40531
|
// src/commands/ai-animated-caption.ts
|
|
40478
40532
|
import { resolve as resolve36, dirname as dirname13, basename as basename9 } from "node:path";
|
|
40479
40533
|
import { writeFile as writeFile34, mkdir as mkdir16, rm as rm2 } from "node:fs/promises";
|
|
40480
|
-
import { existsSync as
|
|
40534
|
+
import { existsSync as existsSync29 } from "node:fs";
|
|
40481
40535
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
40482
40536
|
function groupWords(words, options = {}) {
|
|
40483
40537
|
if (words.length === 0) return [];
|
|
@@ -40654,7 +40708,7 @@ async function executeAnimatedCaption(options) {
|
|
|
40654
40708
|
const groups = groupWords(transcript.words, { wordsPerGroup, maxChars });
|
|
40655
40709
|
const absOutputPath = resolve36(process.cwd(), outputPath);
|
|
40656
40710
|
const outDir = dirname13(absOutputPath);
|
|
40657
|
-
if (!
|
|
40711
|
+
if (!existsSync29(outDir)) {
|
|
40658
40712
|
await mkdir16(outDir, { recursive: true });
|
|
40659
40713
|
}
|
|
40660
40714
|
if (tier === "ass") {
|
|
@@ -50603,7 +50657,7 @@ var init_detect = __esm({
|
|
|
50603
50657
|
});
|
|
50604
50658
|
|
|
50605
50659
|
// src/pipeline/renderers/chrome.ts
|
|
50606
|
-
import { existsSync as
|
|
50660
|
+
import { existsSync as existsSync32 } from "node:fs";
|
|
50607
50661
|
import { homedir as homedir5 } from "node:os";
|
|
50608
50662
|
import { join as join11 } from "node:path";
|
|
50609
50663
|
function findChrome() {
|
|
@@ -50629,7 +50683,7 @@ function findChrome() {
|
|
|
50629
50683
|
"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"
|
|
50630
50684
|
];
|
|
50631
50685
|
for (const c of candidates) {
|
|
50632
|
-
if (c &&
|
|
50686
|
+
if (c && existsSync32(c)) return c;
|
|
50633
50687
|
}
|
|
50634
50688
|
return void 0;
|
|
50635
50689
|
}
|
|
@@ -50884,7 +50938,7 @@ __export(scene_render_exports, {
|
|
|
50884
50938
|
qualityToCrf: () => qualityToCrf
|
|
50885
50939
|
});
|
|
50886
50940
|
import { mkdir as mkdir18, readFile as readFile32, stat as stat4 } from "node:fs/promises";
|
|
50887
|
-
import { existsSync as
|
|
50941
|
+
import { existsSync as existsSync33 } from "node:fs";
|
|
50888
50942
|
import { resolve as resolve45, relative as relative6, dirname as dirname16, basename as basename16 } from "node:path";
|
|
50889
50943
|
import {
|
|
50890
50944
|
createRenderJob,
|
|
@@ -50902,7 +50956,7 @@ function defaultOutputPath(opts) {
|
|
|
50902
50956
|
}
|
|
50903
50957
|
async function readProjectName(projectDir) {
|
|
50904
50958
|
const cfgPath = resolve45(projectDir, "vibe.project.yaml");
|
|
50905
|
-
if (!
|
|
50959
|
+
if (!existsSync33(cfgPath)) return void 0;
|
|
50906
50960
|
try {
|
|
50907
50961
|
const raw = await (await import("node:fs/promises")).readFile(cfgPath, "utf-8");
|
|
50908
50962
|
const parsed = (0, import_yaml7.parse)(raw);
|
|
@@ -51032,7 +51086,7 @@ __export(scene_build_exports, {
|
|
|
51032
51086
|
executeSceneBuild: () => executeSceneBuild,
|
|
51033
51087
|
resolveSceneBuildMode: () => resolveSceneBuildMode
|
|
51034
51088
|
});
|
|
51035
|
-
import { existsSync as
|
|
51089
|
+
import { existsSync as existsSync34 } from "node:fs";
|
|
51036
51090
|
import { mkdir as mkdir19, readFile as readFile33, writeFile as writeFile38 } from "node:fs/promises";
|
|
51037
51091
|
import { dirname as dirname17, join as join12, resolve as resolve46 } from "node:path";
|
|
51038
51092
|
async function executeSceneBuild(opts) {
|
|
@@ -51042,7 +51096,7 @@ async function executeSceneBuild(opts) {
|
|
|
51042
51096
|
});
|
|
51043
51097
|
const mode = resolveSceneBuildMode(opts);
|
|
51044
51098
|
const storyboardPath = join12(projectDir, "STORYBOARD.md");
|
|
51045
|
-
if (!
|
|
51099
|
+
if (!existsSync34(storyboardPath)) {
|
|
51046
51100
|
return failBeforePrimitives(
|
|
51047
51101
|
`STORYBOARD.md not found at ${storyboardPath}. Run \`vibe scene init <dir>\` to create a starter, or add STORYBOARD.md with per-beat cues.`,
|
|
51048
51102
|
startedAt
|
|
@@ -51078,7 +51132,7 @@ async function executeSceneBuild(opts) {
|
|
|
51078
51132
|
if (mode === "agent") {
|
|
51079
51133
|
const compositionsDir = join12(projectDir, "compositions");
|
|
51080
51134
|
const missingBeats = parsed.beats.filter(
|
|
51081
|
-
(b) => !
|
|
51135
|
+
(b) => !existsSync34(join12(compositionsDir, `scene-${b.id}.html`))
|
|
51082
51136
|
);
|
|
51083
51137
|
if (missingBeats.length > 0) {
|
|
51084
51138
|
const plan = await getComposePrompts({ projectDir });
|
|
@@ -51126,7 +51180,7 @@ async function executeSceneBuild(opts) {
|
|
|
51126
51180
|
}
|
|
51127
51181
|
composeData = composeResult.data;
|
|
51128
51182
|
}
|
|
51129
|
-
if (!
|
|
51183
|
+
if (!existsSync34(join12(projectDir, "index.html"))) {
|
|
51130
51184
|
await scaffoldSceneProject({
|
|
51131
51185
|
dir: projectDir,
|
|
51132
51186
|
name: projectDir.split(/[\\/]/).filter(Boolean).pop(),
|
|
@@ -51186,7 +51240,7 @@ async function dispatchNarration(beat, ctx) {
|
|
|
51186
51240
|
if (!text) return { status: "no-cue" };
|
|
51187
51241
|
for (const ext of ["mp3", "wav"]) {
|
|
51188
51242
|
const rel2 = `assets/narration-${beat.id}.${ext}`;
|
|
51189
|
-
if (
|
|
51243
|
+
if (existsSync34(join12(ctx.projectDir, rel2)) && !ctx.force) {
|
|
51190
51244
|
ctx.onProgress({ type: "narration-cached", beatId: beat.id, path: rel2 });
|
|
51191
51245
|
return { status: "cached", path: rel2 };
|
|
51192
51246
|
}
|
|
@@ -51227,7 +51281,7 @@ async function dispatchBackdrop(beat, ctx) {
|
|
|
51227
51281
|
}
|
|
51228
51282
|
const rel = `assets/backdrop-${beat.id}.png`;
|
|
51229
51283
|
const abs = join12(ctx.projectDir, rel);
|
|
51230
|
-
if (
|
|
51284
|
+
if (existsSync34(abs) && !ctx.force) {
|
|
51231
51285
|
ctx.onProgress({ type: "backdrop-cached", beatId: beat.id, path: rel });
|
|
51232
51286
|
return { status: "cached", path: rel };
|
|
51233
51287
|
}
|
|
@@ -51265,7 +51319,7 @@ function loadSceneBuildEnv(projectDir) {
|
|
|
51265
51319
|
(0, import_dotenv3.config)({ path: resolve46(process.cwd(), ".env"), quiet: true });
|
|
51266
51320
|
let dir = process.cwd();
|
|
51267
51321
|
while (dir !== dirname17(dir)) {
|
|
51268
|
-
if (
|
|
51322
|
+
if (existsSync34(join12(dir, "pnpm-workspace.yaml"))) {
|
|
51269
51323
|
(0, import_dotenv3.config)({ path: join12(dir, ".env"), quiet: true });
|
|
51270
51324
|
return;
|
|
51271
51325
|
}
|
|
@@ -51296,7 +51350,7 @@ function resolveSceneBuildMode(opts) {
|
|
|
51296
51350
|
}
|
|
51297
51351
|
async function syncRootClipReferences(beats, projectDir, outcomes) {
|
|
51298
51352
|
const rootPath = join12(projectDir, "index.html");
|
|
51299
|
-
if (!
|
|
51353
|
+
if (!existsSync34(rootPath)) return;
|
|
51300
51354
|
const html = await readFile33(rootPath, "utf-8");
|
|
51301
51355
|
let cursor = 0;
|
|
51302
51356
|
const clipLines = [];
|
|
@@ -51747,14 +51801,14 @@ __export(ai_audio_exports, {
|
|
|
51747
51801
|
});
|
|
51748
51802
|
import { resolve as resolve53, dirname as dirname19, basename as basename17, extname as extname12 } from "node:path";
|
|
51749
51803
|
import { readFile as readFile36, writeFile as writeFile40 } from "node:fs/promises";
|
|
51750
|
-
import { existsSync as
|
|
51804
|
+
import { existsSync as existsSync37 } from "node:fs";
|
|
51751
51805
|
async function executeTranscribe(options) {
|
|
51752
51806
|
const { audioPath, language, output, format, apiKey } = options;
|
|
51753
51807
|
try {
|
|
51754
51808
|
const key = apiKey || process.env.OPENAI_API_KEY;
|
|
51755
51809
|
if (!key) return { success: false, error: "OPENAI_API_KEY required" };
|
|
51756
51810
|
const absPath = resolve53(process.cwd(), audioPath);
|
|
51757
|
-
if (!
|
|
51811
|
+
if (!existsSync37(absPath)) return { success: false, error: `File not found: ${absPath}` };
|
|
51758
51812
|
const whisper = new WhisperProvider();
|
|
51759
51813
|
await whisper.initialize({ apiKey: key });
|
|
51760
51814
|
const audioBuffer = await readFile36(absPath);
|
|
@@ -51787,7 +51841,7 @@ async function executeIsolate(options) {
|
|
|
51787
51841
|
const key = apiKey || process.env.ELEVENLABS_API_KEY;
|
|
51788
51842
|
if (!key) return { success: false, error: "ELEVENLABS_API_KEY required" };
|
|
51789
51843
|
const absPath = resolve53(process.cwd(), audioPath);
|
|
51790
|
-
if (!
|
|
51844
|
+
if (!existsSync37(absPath)) return { success: false, error: `File not found: ${absPath}` };
|
|
51791
51845
|
const audioBuffer = await readFile36(absPath);
|
|
51792
51846
|
const elevenlabs = new ElevenLabsProvider();
|
|
51793
51847
|
await elevenlabs.initialize({ apiKey: key });
|
|
@@ -51813,7 +51867,7 @@ async function executeVoiceClone(options) {
|
|
|
51813
51867
|
const audioBuffers = [];
|
|
51814
51868
|
for (const samplePath of samplePaths) {
|
|
51815
51869
|
const absPath = resolve53(process.cwd(), samplePath);
|
|
51816
|
-
if (!
|
|
51870
|
+
if (!existsSync37(absPath)) return { success: false, error: `File not found: ${samplePath}` };
|
|
51817
51871
|
const buffer = await readFile36(absPath);
|
|
51818
51872
|
audioBuffers.push(buffer);
|
|
51819
51873
|
}
|
|
@@ -51835,7 +51889,7 @@ async function executeDub(options) {
|
|
|
51835
51889
|
const { mediaPath, language, source, voice, analyzeOnly, output } = options;
|
|
51836
51890
|
try {
|
|
51837
51891
|
const absPath = resolve53(process.cwd(), mediaPath);
|
|
51838
|
-
if (!
|
|
51892
|
+
if (!existsSync37(absPath)) return { success: false, error: `File not found: ${absPath}` };
|
|
51839
51893
|
const openaiKey = process.env.OPENAI_API_KEY;
|
|
51840
51894
|
const anthropicKey = process.env.ANTHROPIC_API_KEY;
|
|
51841
51895
|
const elevenlabsKey = process.env.ELEVENLABS_API_KEY;
|
|
@@ -51963,8 +52017,8 @@ async function executeDuck(options) {
|
|
|
51963
52017
|
if (!commandExists("ffmpeg")) return { success: false, error: "FFmpeg not found" };
|
|
51964
52018
|
const absMusicPath = resolve53(process.cwd(), musicPath);
|
|
51965
52019
|
const absVoicePath = resolve53(process.cwd(), voicePath);
|
|
51966
|
-
if (!
|
|
51967
|
-
if (!
|
|
52020
|
+
if (!existsSync37(absMusicPath)) return { success: false, error: `Music file not found: ${absMusicPath}` };
|
|
52021
|
+
if (!existsSync37(absVoicePath)) return { success: false, error: `Voice file not found: ${absVoicePath}` };
|
|
51968
52022
|
const defaultOutput = resolve53(dirname19(absMusicPath), `${basename17(absMusicPath, extname12(absMusicPath))}-ducked${extname12(absMusicPath)}`);
|
|
51969
52023
|
const outputPath = resolve53(process.cwd(), output || defaultOutput);
|
|
51970
52024
|
const filter = `[1:a]asplit=2[sc][mix];[0:a][sc]sidechaincompress=threshold=${threshold}dB:ratio=${ratio}:attack=${attack}:release=${release}[ducked];[ducked][mix]amix=inputs=2:duration=longest`;
|
|
@@ -51978,7 +52032,7 @@ async function executeDuck(options) {
|
|
|
51978
52032
|
"-y",
|
|
51979
52033
|
outputPath
|
|
51980
52034
|
], { timeout: 12e4, maxBuffer: 50 * 1024 * 1024 });
|
|
51981
|
-
if (!
|
|
52035
|
+
if (!existsSync37(outputPath)) return { success: false, error: "FFmpeg failed to create output" };
|
|
51982
52036
|
return { success: true, outputPath };
|
|
51983
52037
|
} catch (error) {
|
|
51984
52038
|
return { success: false, error: `Audio ducking failed: ${error instanceof Error ? error.message : String(error)}` };
|
|
@@ -51996,7 +52050,7 @@ var init_ai_audio = __esm({
|
|
|
51996
52050
|
// src/pipeline/executor.ts
|
|
51997
52051
|
import { resolve as resolve54 } from "node:path";
|
|
51998
52052
|
import { readFile as readFile37, writeFile as writeFile41, mkdir as mkdir21 } from "node:fs/promises";
|
|
51999
|
-
import { existsSync as
|
|
52053
|
+
import { existsSync as existsSync38 } from "node:fs";
|
|
52000
52054
|
function maxCostFor(action) {
|
|
52001
52055
|
const cmd = ACTION_METADATA[action]?.command;
|
|
52002
52056
|
if (!cmd) return 0;
|
|
@@ -52213,7 +52267,7 @@ async function ensureActionsRegistered() {
|
|
|
52213
52267
|
}
|
|
52214
52268
|
async function loadPipeline(filePath) {
|
|
52215
52269
|
const absPath = resolve54(process.cwd(), filePath);
|
|
52216
|
-
if (!
|
|
52270
|
+
if (!existsSync38(absPath)) {
|
|
52217
52271
|
throw new Error(`Pipeline file not found: ${absPath}`);
|
|
52218
52272
|
}
|
|
52219
52273
|
const content = await readFile37(absPath, "utf-8");
|
|
@@ -52238,7 +52292,7 @@ async function executePipeline(manifest2, options = {}) {
|
|
|
52238
52292
|
const startTime = Date.now();
|
|
52239
52293
|
if (options.resume) {
|
|
52240
52294
|
const checkpointPath = resolve54(outputDir, CHECKPOINT_FILE);
|
|
52241
|
-
if (
|
|
52295
|
+
if (existsSync38(checkpointPath)) {
|
|
52242
52296
|
const checkpointContent = await readFile37(checkpointPath, "utf-8");
|
|
52243
52297
|
const checkpoint = (0, import_yaml8.parse)(checkpointContent);
|
|
52244
52298
|
for (const cs of checkpoint.completedSteps) {
|
|
@@ -59644,7 +59698,7 @@ __export(project_builder_exports, {
|
|
|
59644
59698
|
resolveSourceUrl: () => resolveSourceUrl
|
|
59645
59699
|
});
|
|
59646
59700
|
import { mkdtemp, mkdir as mkdir22, copyFile as copyFile3, writeFile as writeFile46, rm as rm4, readdir as readdir3 } from "node:fs/promises";
|
|
59647
|
-
import { existsSync as
|
|
59701
|
+
import { existsSync as existsSync39 } from "node:fs";
|
|
59648
59702
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
59649
59703
|
import { createRequire } from "node:module";
|
|
59650
59704
|
import * as path from "node:path";
|
|
@@ -59656,7 +59710,7 @@ async function buildTempProject(state, projectFileDir) {
|
|
|
59656
59710
|
for (const source of state.sources) {
|
|
59657
59711
|
if (copied.has(source.id)) continue;
|
|
59658
59712
|
const resolved = resolveSourceUrl(source.url, projectFileDir);
|
|
59659
|
-
if (!
|
|
59713
|
+
if (!existsSync39(resolved)) {
|
|
59660
59714
|
throw new Error(`Source file not found: ${resolved} (from source id=${source.id})`);
|
|
59661
59715
|
}
|
|
59662
59716
|
const dest = path.join(assetsDir, path.basename(resolved));
|
|
@@ -60494,11 +60548,11 @@ var init_export = __esm({
|
|
|
60494
60548
|
).option("--overwrite", "Overwrite output file if exists", false).option("--gap-fill <strategy>", "Gap filling strategy (black, extend)", "extend").option("--backend <name>", "Render backend: ffmpeg (default) | hyperframes (experimental)", "ffmpeg").option("--bitrate <value>", "Video bitrate (e.g. 5000k, 8M) \u2014 overrides preset").option("--fps <number>", "Frames per second (e.g. 24, 30, 60) \u2014 overrides preset").option("--resolution <WxH>", "Output resolution (e.g. 1920x1080) \u2014 overrides preset").option("--codec <codec>", "Video codec: h264 (default) | h265 | vp9 \u2014 overrides preset").option("--dry-run", "Preview parameters without executing").addHelpText("after", `
|
|
60495
60549
|
Examples:
|
|
60496
60550
|
$ vibe export project.vibe.json -o output.mp4
|
|
60497
|
-
$ vibe export project.vibe.json -o output.mp4
|
|
60498
|
-
$ vibe export project.vibe.json -o output.webm
|
|
60499
|
-
$ vibe export project.vibe.json -o output.gif
|
|
60551
|
+
$ vibe export project.vibe.json -o output.mp4 --preset high --overwrite
|
|
60552
|
+
$ vibe export project.vibe.json -o output.webm --format webm
|
|
60553
|
+
$ vibe export project.vibe.json -o output.gif --format gif
|
|
60500
60554
|
$ vibe export project.vibe.json -o out.mp4 --bitrate 5000k --fps 24 --codec h265
|
|
60501
|
-
$ vibe export project.vibe.json -o out.mp4
|
|
60555
|
+
$ vibe export project.vibe.json -o out.mp4 --preset high --fps 60
|
|
60502
60556
|
|
|
60503
60557
|
Cost: Free (no API keys needed). Requires FFmpeg.
|
|
60504
60558
|
GIF format: 15fps, no audio, looping. Good for previews and sharing.
|
|
@@ -61634,28 +61688,31 @@ narration / backdrop intent.
|
|
|
61634
61688
|
## Quick-draft path
|
|
61635
61689
|
|
|
61636
61690
|
\`\`\`bash
|
|
61637
|
-
vibe
|
|
61691
|
+
vibe init my-promo -r 16:9 -d 30
|
|
61638
61692
|
vibe scene add intro --style announcement \\
|
|
61639
61693
|
--headline "Ship videos, not clicks"
|
|
61640
61694
|
vibe scene lint
|
|
61641
61695
|
vibe render my-promo
|
|
61642
61696
|
\`\`\`
|
|
61643
61697
|
|
|
61644
|
-
\`vibe
|
|
61645
|
-
|
|
61646
|
-
|
|
61698
|
+
\`vibe init\` is **idempotent** \u2014 running it on an existing Hyperframes
|
|
61699
|
+
directory merges \`hyperframes.json\` instead of clobbering it. Safe to
|
|
61700
|
+
invoke on user-provided projects.
|
|
61647
61701
|
|
|
61648
61702
|
## Subcommands
|
|
61649
61703
|
|
|
61650
61704
|
\`\`\`bash
|
|
61651
|
-
|
|
61705
|
+
# Project flow (top-level \u2014 preferred entry points)
|
|
61706
|
+
vibe init <dir> [-r 16:9|9:16|1:1|4:5] [-d <sec>] [--visual-style "<name>"]
|
|
61707
|
+
vibe build [<dir>] [--mode agent|batch|auto] # H3 dispatch
|
|
61708
|
+
vibe render [<dir>] [--fps 30] [--quality standard] [--format mp4]
|
|
61709
|
+
|
|
61710
|
+
# Lower-level scene authoring
|
|
61652
61711
|
vibe scene list-styles [<name>] # list / show vendored visual identities
|
|
61653
61712
|
vibe scene install-skill [<dir>] [--host all] # retroactive composition-rules install
|
|
61654
61713
|
vibe scene add <name> --style <preset> [...]
|
|
61655
61714
|
vibe scene compose-prompts [<dir>] [--beat <id>] # H2: emit plan, no LLM call
|
|
61656
61715
|
vibe scene lint [<root>] [--json] [--fix]
|
|
61657
|
-
vibe scene render [<root>] [--fps 30] [--quality standard] [--format mp4]
|
|
61658
|
-
vibe build [<dir>] [--mode agent|batch|auto] # H3 dispatch
|
|
61659
61716
|
\`\`\`
|
|
61660
61717
|
|
|
61661
61718
|
## Style presets (for \`vibe scene add --style\`)
|
|
@@ -61709,7 +61766,7 @@ and surface the error to the user.
|
|
|
61709
61766
|
| Generate narration + image, then author scene | \`vibe scene add\` |
|
|
61710
61767
|
| Generate a full scenes project from a STORYBOARD | \`vibe build\` |
|
|
61711
61768
|
| Hand-tweak a single scene's animation | edit \`compositions/<file>.html\` directly |
|
|
61712
|
-
| Render the project | \`vibe render\`
|
|
61769
|
+
| Render the project | \`vibe render\` (one canonical entry point) |
|
|
61713
61770
|
| Lint | \`vibe scene lint\` *or* \`npx hyperframes lint\` (equivalent) |
|
|
61714
61771
|
|
|
61715
61772
|
The \`vibe\` CLI adds asset generation, AI orchestration, and pipeline
|
|
@@ -61837,12 +61894,12 @@ overlay, etc.) \u2014 useful at the tail of a pipeline.
|
|
|
61837
61894
|
],
|
|
61838
61895
|
relatedCommands: [
|
|
61839
61896
|
"vibe init",
|
|
61897
|
+
"vibe build",
|
|
61898
|
+
"vibe render",
|
|
61840
61899
|
"vibe scene list-styles",
|
|
61841
61900
|
"vibe scene install-skill",
|
|
61842
61901
|
"vibe scene compose-prompts",
|
|
61843
|
-
"vibe build",
|
|
61844
61902
|
"vibe scene lint",
|
|
61845
|
-
"vibe scene render",
|
|
61846
61903
|
"vibe scene add"
|
|
61847
61904
|
]
|
|
61848
61905
|
},
|
|
@@ -62166,7 +62223,7 @@ init_source();
|
|
|
62166
62223
|
// package.json
|
|
62167
62224
|
var package_default = {
|
|
62168
62225
|
name: "@vibeframe/cli",
|
|
62169
|
-
version: "0.
|
|
62226
|
+
version: "0.79.1",
|
|
62170
62227
|
description: "VibeFrame CLI - natural language video editing from the terminal",
|
|
62171
62228
|
private: false,
|
|
62172
62229
|
license: "MIT",
|
|
@@ -62578,6 +62635,7 @@ init_engine();
|
|
|
62578
62635
|
init_validate();
|
|
62579
62636
|
init_output();
|
|
62580
62637
|
import { readFile as readFile9, writeFile as writeFile9 } from "node:fs/promises";
|
|
62638
|
+
import { existsSync as existsSync8 } from "node:fs";
|
|
62581
62639
|
import { resolve as resolve11, basename as basename2, extname } from "node:path";
|
|
62582
62640
|
var timelineCommand = new Command("timeline").description("Timeline editing commands").addHelpText("after", `
|
|
62583
62641
|
Examples:
|
|
@@ -62928,6 +62986,9 @@ timelineCommand.command("list").description("List timeline contents").argument("
|
|
|
62928
62986
|
const startedAt = Date.now();
|
|
62929
62987
|
try {
|
|
62930
62988
|
const filePath = resolve11(process.cwd(), projectPath);
|
|
62989
|
+
if (!existsSync8(filePath)) {
|
|
62990
|
+
exitWithError(notFoundError(projectPath));
|
|
62991
|
+
}
|
|
62931
62992
|
const content = await readFile9(filePath, "utf-8");
|
|
62932
62993
|
const data = JSON.parse(content);
|
|
62933
62994
|
const project = Project.fromJSON(data);
|
|
@@ -63548,16 +63609,16 @@ init_output();
|
|
|
63548
63609
|
init_validate();
|
|
63549
63610
|
import { resolve as resolve33, dirname as dirname11, basename as basename7, extname as extname7 } from "node:path";
|
|
63550
63611
|
import { readFile as readFile24, writeFile as writeFile31 } from "node:fs/promises";
|
|
63551
|
-
import { existsSync as
|
|
63612
|
+
import { existsSync as existsSync26 } from "node:fs";
|
|
63552
63613
|
var audioCommand = new Command("audio").alias("au").description("Audio operations (transcribe, TTS, voice clone, ducking)").addHelpText(
|
|
63553
63614
|
"after",
|
|
63554
63615
|
`
|
|
63555
63616
|
Examples:
|
|
63556
|
-
$ vibe audio transcribe interview.mp3 -o transcript.srt
|
|
63617
|
+
$ vibe audio transcribe interview.mp3 -o transcript.srt --format srt
|
|
63557
63618
|
$ vibe audio transcribe video.mp4 -l ko # Specify language
|
|
63558
63619
|
$ vibe audio list-voices # List available voices
|
|
63559
63620
|
$ vibe audio isolate song.mp3 -o vocals.mp3
|
|
63560
|
-
$ vibe audio clone-voice sample.mp3
|
|
63621
|
+
$ vibe audio clone-voice sample.mp3 --name "my-voice"
|
|
63561
63622
|
$ vibe audio dub video.mp4 -l ko -o dubbed.mp4
|
|
63562
63623
|
$ vibe audio duck music.mp3 --voice narration.mp3 -o ducked.mp3
|
|
63563
63624
|
|
|
@@ -63752,7 +63813,7 @@ audioCommand.command("clone-voice").description("Clone a voice from audio sample
|
|
|
63752
63813
|
const audioBuffers = [];
|
|
63753
63814
|
for (const samplePath of samples) {
|
|
63754
63815
|
const absPath = resolve33(process.cwd(), samplePath);
|
|
63755
|
-
if (!
|
|
63816
|
+
if (!existsSync26(absPath)) {
|
|
63756
63817
|
spinner2.fail(`File not found: ${samplePath}`);
|
|
63757
63818
|
exitWithError(notFoundError(samplePath));
|
|
63758
63819
|
}
|
|
@@ -63813,7 +63874,7 @@ audioCommand.command("dub").description("Dub audio/video to another language (tr
|
|
|
63813
63874
|
exitWithError(usageError("Target language is required. Use -l or --language"));
|
|
63814
63875
|
}
|
|
63815
63876
|
const absPath = resolve33(process.cwd(), mediaPath);
|
|
63816
|
-
if (!
|
|
63877
|
+
if (!existsSync26(absPath)) {
|
|
63817
63878
|
exitWithError(notFoundError(mediaPath));
|
|
63818
63879
|
}
|
|
63819
63880
|
const openaiKey = await requireApiKey("OPENAI_API_KEY", "OpenAI");
|
|
@@ -64056,7 +64117,7 @@ init_esm();
|
|
|
64056
64117
|
init_source();
|
|
64057
64118
|
init_ora();
|
|
64058
64119
|
import { resolve as resolve37, basename as basename10 } from "node:path";
|
|
64059
|
-
import { existsSync as
|
|
64120
|
+
import { existsSync as existsSync30 } from "node:fs";
|
|
64060
64121
|
|
|
64061
64122
|
// src/commands/ai-script-pipeline-cli.ts
|
|
64062
64123
|
init_source();
|
|
@@ -64067,19 +64128,19 @@ init_ai_script_pipeline();
|
|
|
64067
64128
|
init_output();
|
|
64068
64129
|
import { readFile as readFile25, writeFile as writeFile32 } from "node:fs/promises";
|
|
64069
64130
|
import { resolve as resolve34 } from "node:path";
|
|
64070
|
-
import { existsSync as
|
|
64131
|
+
import { existsSync as existsSync27 } from "node:fs";
|
|
64071
64132
|
function registerScriptPipelineCommands(aiCommand) {
|
|
64072
64133
|
aiCommand.command("regenerate-scene").description("Regenerate a specific scene in a script-to-video output directory").argument("<project-dir>", "Path to the script-to-video output directory").requiredOption("--scene <numbers>", "Scene number(s) to regenerate (1-based), e.g., 3 or 3,4,5").option("--video-only", "Only regenerate video").option("--narration-only", "Only regenerate narration").option("--image-only", "Only regenerate image").option("--generator <engine>", "Video generator: grok | kling | runway | veo", "grok").option("-i, --image-provider <provider>", "Image provider: gemini | openai | grok", "gemini").option("--voice <id>", "ElevenLabs voice ID for narration").option("-a, --aspect-ratio <ratio>", "Aspect ratio: 16:9 | 9:16 | 1:1", "16:9").option("--retries <count>", "Number of retries for video generation failures", String(DEFAULT_VIDEO_RETRIES)).option("--reference-scene <num>", "Use another scene's image as reference for character consistency").option("--dry-run", "Preview parameters without executing").action(async (projectDir, options) => {
|
|
64073
64134
|
const startedAt = Date.now();
|
|
64074
64135
|
try {
|
|
64075
64136
|
const outputDir = resolve34(process.cwd(), projectDir);
|
|
64076
64137
|
const projectPath = resolve34(outputDir, "project.vibe.json");
|
|
64077
|
-
if (!
|
|
64138
|
+
if (!existsSync27(outputDir)) {
|
|
64078
64139
|
exitWithError(notFoundError(outputDir));
|
|
64079
64140
|
}
|
|
64080
64141
|
const yamlPath = resolve34(outputDir, "storyboard.yaml");
|
|
64081
64142
|
const jsonPath = resolve34(outputDir, "storyboard.json");
|
|
64082
|
-
const storyboardPath =
|
|
64143
|
+
const storyboardPath = existsSync27(yamlPath) ? yamlPath : existsSync27(jsonPath) ? jsonPath : null;
|
|
64083
64144
|
const storyboardIsYaml = storyboardPath === yamlPath;
|
|
64084
64145
|
if (!storyboardPath) {
|
|
64085
64146
|
exitWithError(notFoundError(`${outputDir}/storyboard.{yaml,json}`));
|
|
@@ -64177,7 +64238,7 @@ function registerScriptPipelineCommands(aiCommand) {
|
|
|
64177
64238
|
exitWithError(apiError(result.error || "Scene regeneration failed", true));
|
|
64178
64239
|
}
|
|
64179
64240
|
spinner2.succeed(source_default.green(`Regenerated ${result.regeneratedScenes.length} scene(s)`));
|
|
64180
|
-
if (
|
|
64241
|
+
if (existsSync27(projectPath) && result.regeneratedScenes.length > 0) {
|
|
64181
64242
|
const updateSpinner = ora("\u{1F4E6} Updating project file...").start();
|
|
64182
64243
|
try {
|
|
64183
64244
|
const updatedContent = await readFile25(storyboardPath, "utf-8");
|
|
@@ -64191,7 +64252,7 @@ function registerScriptPipelineCommands(aiCommand) {
|
|
|
64191
64252
|
const imagePath = resolve34(outputDir, `scene-${sceneNum}.png`);
|
|
64192
64253
|
const videoSource = projectData.state.sources.find((s) => s.name === `Scene ${sceneNum}`);
|
|
64193
64254
|
if (videoSource) {
|
|
64194
|
-
const hasVideo =
|
|
64255
|
+
const hasVideo = existsSync27(videoPath);
|
|
64195
64256
|
videoSource.url = hasVideo ? videoPath : imagePath;
|
|
64196
64257
|
videoSource.type = hasVideo ? "video" : "image";
|
|
64197
64258
|
videoSource.duration = segment.duration;
|
|
@@ -64255,9 +64316,9 @@ Two flows \u2014 pick by intent:
|
|
|
64255
64316
|
|
|
64256
64317
|
Examples (REMIX):
|
|
64257
64318
|
$ vibe remix highlights long-video.mp4 -o highlights.json -d 60
|
|
64258
|
-
$ vibe remix auto-shorts long-video.mp4 -o shorts/
|
|
64259
|
-
$ vibe remix animated-caption video.mp4 -o captioned.mp4
|
|
64260
|
-
$ vibe remix animated-caption video.mp4 -o out.mp4
|
|
64319
|
+
$ vibe remix auto-shorts long-video.mp4 -o shorts/ --count 3 --add-captions
|
|
64320
|
+
$ vibe remix animated-caption video.mp4 -o captioned.mp4 --style highlight
|
|
64321
|
+
$ vibe remix animated-caption video.mp4 -o out.mp4 --style karaoke-sweep --fast
|
|
64261
64322
|
|
|
64262
64323
|
Provider API Keys:
|
|
64263
64324
|
highlights: GOOGLE_API_KEY (Gemini analysis)
|
|
@@ -64282,8 +64343,8 @@ pipelineCommand.command("animated-caption").description("Add animated captions w
|
|
|
64282
64343
|
`
|
|
64283
64344
|
Examples:
|
|
64284
64345
|
$ vibe remix animated-caption video.mp4 -o captioned.mp4
|
|
64285
|
-
$ vibe remix animated-caption video.mp4 -o out.mp4
|
|
64286
|
-
$ vibe remix animated-caption video.mp4 -o out.mp4
|
|
64346
|
+
$ vibe remix animated-caption video.mp4 -o out.mp4 --style bounce
|
|
64347
|
+
$ vibe remix animated-caption video.mp4 -o out.mp4 --style karaoke-sweep --fast
|
|
64287
64348
|
|
|
64288
64349
|
Styles:
|
|
64289
64350
|
highlight (default) TikTok-style background highlight on active word (Remotion)
|
|
@@ -64299,7 +64360,7 @@ Required API Key: OPENAI_API_KEY (Whisper transcription)
|
|
|
64299
64360
|
const startedAt = Date.now();
|
|
64300
64361
|
try {
|
|
64301
64362
|
const absVideoPath = resolve37(process.cwd(), videoPath);
|
|
64302
|
-
if (!
|
|
64363
|
+
if (!existsSync30(absVideoPath)) {
|
|
64303
64364
|
exitWithError(notFoundError(absVideoPath));
|
|
64304
64365
|
}
|
|
64305
64366
|
if (!ANIMATED_CAPTION_STYLES.includes(options.style)) {
|
|
@@ -69400,7 +69461,7 @@ init_agent_host_detect();
|
|
|
69400
69461
|
init_scene_project();
|
|
69401
69462
|
init_visual_styles();
|
|
69402
69463
|
init_install_skill();
|
|
69403
|
-
import { existsSync as
|
|
69464
|
+
import { existsSync as existsSync31 } from "node:fs";
|
|
69404
69465
|
import { mkdir as mkdir17, readFile as readFile30, writeFile as writeFile37 } from "node:fs/promises";
|
|
69405
69466
|
import { basename as basename14, dirname as dirname14, resolve as resolve42 } from "node:path";
|
|
69406
69467
|
|
|
@@ -69691,7 +69752,7 @@ var initCommand = new Command("init").description("Scaffold a VibeFrame project
|
|
|
69691
69752
|
const wantsGemini = targetHosts.includes("gemini-cli");
|
|
69692
69753
|
const wantsCrossTool = targetHosts.some((h) => h !== "claude-code");
|
|
69693
69754
|
const actions = [];
|
|
69694
|
-
if (!
|
|
69755
|
+
if (!existsSync31(projectDir)) {
|
|
69695
69756
|
if (options.dryRun) {
|
|
69696
69757
|
actions.push({ path: projectDir, status: "would-write", reason: "(would create directory)" });
|
|
69697
69758
|
} else {
|
|
@@ -69937,7 +69998,7 @@ function resolveTargets(agent) {
|
|
|
69937
69998
|
return [agent];
|
|
69938
69999
|
}
|
|
69939
70000
|
async function writeIfMissing(absPath, content, force, dryRun) {
|
|
69940
|
-
const exists =
|
|
70001
|
+
const exists = existsSync31(absPath);
|
|
69941
70002
|
if (exists && !force) {
|
|
69942
70003
|
return { path: absPath, status: "skipped-exists", reason: "(use --force to overwrite)" };
|
|
69943
70004
|
}
|
|
@@ -69950,7 +70011,7 @@ async function writeIfMissing(absPath, content, force, dryRun) {
|
|
|
69950
70011
|
}
|
|
69951
70012
|
async function mergeGitignore(absPath, additions, dryRun) {
|
|
69952
70013
|
const marker = "# VibeFrame";
|
|
69953
|
-
if (!
|
|
70014
|
+
if (!existsSync31(absPath)) {
|
|
69954
70015
|
if (dryRun) return { path: absPath, status: "would-write" };
|
|
69955
70016
|
await writeFile37(absPath, additions.trimStart(), "utf-8");
|
|
69956
70017
|
return { path: absPath, status: "wrote" };
|
|
@@ -70293,7 +70354,7 @@ init_composer_resolve();
|
|
|
70293
70354
|
init_scene_build();
|
|
70294
70355
|
init_output();
|
|
70295
70356
|
import { access as access6 } from "node:fs/promises";
|
|
70296
|
-
import { existsSync as
|
|
70357
|
+
import { existsSync as existsSync35 } from "node:fs";
|
|
70297
70358
|
import { resolve as resolve49 } from "node:path";
|
|
70298
70359
|
var COMMAND_KEY_MAP = getCommandKeyMap();
|
|
70299
70360
|
var FREE_COMMANDS = [
|
|
@@ -70335,21 +70396,21 @@ var REQUIRED_FFMPEG_FILTERS = {
|
|
|
70335
70396
|
drawtext: {
|
|
70336
70397
|
commands: ["edit text-overlay", "edit caption"],
|
|
70337
70398
|
fix: {
|
|
70338
|
-
darwin: "brew
|
|
70399
|
+
darwin: "brew tap homebrew-ffmpeg/ffmpeg && brew install homebrew-ffmpeg/ffmpeg/ffmpeg --with-freetype (Homebrew core ffmpeg lacks libfreetype)",
|
|
70339
70400
|
linux: "sudo apt install ffmpeg (or rebuild with --enable-libfreetype)"
|
|
70340
70401
|
}
|
|
70341
70402
|
},
|
|
70342
70403
|
subtitles: {
|
|
70343
70404
|
commands: ["edit caption"],
|
|
70344
70405
|
fix: {
|
|
70345
|
-
darwin: "brew
|
|
70406
|
+
darwin: "brew tap homebrew-ffmpeg/ffmpeg && brew install homebrew-ffmpeg/ffmpeg/ffmpeg --with-libass (Homebrew core ffmpeg lacks libass)",
|
|
70346
70407
|
linux: "sudo apt install ffmpeg (or rebuild with --enable-libass)"
|
|
70347
70408
|
}
|
|
70348
70409
|
},
|
|
70349
70410
|
afftdn: {
|
|
70350
70411
|
commands: ["edit noise-reduce"],
|
|
70351
70412
|
fix: {
|
|
70352
|
-
darwin: "brew
|
|
70413
|
+
darwin: "brew install ffmpeg (afftdn ships in the default Homebrew formula; a reinstall is rarely needed)",
|
|
70353
70414
|
linux: "sudo apt install ffmpeg (or rebuild with --enable-libfftw3)"
|
|
70354
70415
|
}
|
|
70355
70416
|
}
|
|
@@ -70407,7 +70468,7 @@ async function runDiagnostics() {
|
|
|
70407
70468
|
installed = false;
|
|
70408
70469
|
}
|
|
70409
70470
|
} else if (toolName === "chrome") {
|
|
70410
|
-
const { existsSync:
|
|
70471
|
+
const { existsSync: existsSync40 } = await import("node:fs");
|
|
70411
70472
|
const { homedir: homedir6 } = await import("node:os");
|
|
70412
70473
|
const { join: join16 } = await import("node:path");
|
|
70413
70474
|
const chromePaths = [
|
|
@@ -70420,7 +70481,7 @@ async function runDiagnostics() {
|
|
|
70420
70481
|
"/usr/bin/chromium",
|
|
70421
70482
|
"/usr/bin/chromium-browser"
|
|
70422
70483
|
];
|
|
70423
|
-
installed = chromePaths.some((p) => p &&
|
|
70484
|
+
installed = chromePaths.some((p) => p && existsSync40(p));
|
|
70424
70485
|
} else {
|
|
70425
70486
|
installed = commandExists(toolName);
|
|
70426
70487
|
}
|
|
@@ -70451,7 +70512,7 @@ async function runDiagnostics() {
|
|
|
70451
70512
|
const cwd = process.cwd();
|
|
70452
70513
|
const projectFiles = ["AGENTS.md", "CLAUDE.md", "vibe.project.yaml"].map((rel) => ({
|
|
70453
70514
|
path: rel,
|
|
70454
|
-
exists:
|
|
70515
|
+
exists: existsSync35(resolve49(cwd, rel))
|
|
70455
70516
|
}));
|
|
70456
70517
|
const projectInitialized = projectFiles.some((f) => f.exists);
|
|
70457
70518
|
const hosts = detectAgentHosts();
|
|
@@ -70466,8 +70527,8 @@ async function runDiagnostics() {
|
|
|
70466
70527
|
} catch (err) {
|
|
70467
70528
|
if (!(err instanceof ComposerResolveError)) throw err;
|
|
70468
70529
|
}
|
|
70469
|
-
const sceneProjectInCwd =
|
|
70470
|
-
const skillInstalled =
|
|
70530
|
+
const sceneProjectInCwd = existsSync35(resolve49(cwd, "STORYBOARD.md"));
|
|
70531
|
+
const skillInstalled = existsSync35(resolve49(cwd, "SKILL.md"));
|
|
70471
70532
|
return {
|
|
70472
70533
|
system: {
|
|
70473
70534
|
node: { version: nodeVersion, ok: true },
|
|
@@ -70633,7 +70694,7 @@ init_detect();
|
|
|
70633
70694
|
init_ai_edit();
|
|
70634
70695
|
init_output();
|
|
70635
70696
|
import { resolve as resolve50 } from "node:path";
|
|
70636
|
-
import { existsSync as
|
|
70697
|
+
import { existsSync as existsSync36 } from "node:fs";
|
|
70637
70698
|
import { mkdir as mkdir20, rm as rm3 } from "node:fs/promises";
|
|
70638
70699
|
var DEMO_DIR = resolve50(process.cwd(), ".vibeframe-demo");
|
|
70639
70700
|
async function generateTestVideo(outputPath) {
|
|
@@ -70664,7 +70725,7 @@ async function generateTestVideo(outputPath) {
|
|
|
70664
70725
|
"-y",
|
|
70665
70726
|
outputPath
|
|
70666
70727
|
], { timeout: 3e4, maxBuffer: 50 * 1024 * 1024 });
|
|
70667
|
-
return
|
|
70728
|
+
return existsSync36(outputPath);
|
|
70668
70729
|
} catch {
|
|
70669
70730
|
try {
|
|
70670
70731
|
await execSafe("ffmpeg", [
|
|
@@ -70690,7 +70751,7 @@ async function generateTestVideo(outputPath) {
|
|
|
70690
70751
|
"-y",
|
|
70691
70752
|
outputPath
|
|
70692
70753
|
], { timeout: 3e4, maxBuffer: 50 * 1024 * 1024 });
|
|
70693
|
-
return
|
|
70754
|
+
return existsSync36(outputPath);
|
|
70694
70755
|
} catch {
|
|
70695
70756
|
return false;
|
|
70696
70757
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibeframe/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.79.1",
|
|
4
4
|
"description": "VibeFrame CLI - natural language video editing from the terminal",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
@@ -182,8 +182,8 @@
|
|
|
182
182
|
"tsx": "^4.21.0",
|
|
183
183
|
"typescript": "^5.3.3",
|
|
184
184
|
"vitest": "^1.2.2",
|
|
185
|
-
"@vibeframe/
|
|
186
|
-
"@vibeframe/
|
|
185
|
+
"@vibeframe/ai-providers": "0.79.1",
|
|
186
|
+
"@vibeframe/core": "0.79.1"
|
|
187
187
|
},
|
|
188
188
|
"engines": {
|
|
189
189
|
"node": ">=20"
|