@vibeframe/cli 0.71.0 → 0.72.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1289 -755
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -6557,6 +6557,7 @@ __export(output_exports, {
|
|
|
6557
6557
|
notFoundError: () => notFoundError,
|
|
6558
6558
|
outputError: () => outputError,
|
|
6559
6559
|
outputResult: () => outputResult,
|
|
6560
|
+
outputSuccess: () => outputSuccess,
|
|
6560
6561
|
spinner: () => spinner,
|
|
6561
6562
|
suggestNext: () => suggestNext,
|
|
6562
6563
|
usageError: () => usageError
|
|
@@ -6615,6 +6616,40 @@ function formatCost(min, max, unit) {
|
|
|
6615
6616
|
if (min === max) return `~$${min.toFixed(2)} ${unit}`;
|
|
6616
6617
|
return `~$${min.toFixed(2)}-$${max.toFixed(2)} ${unit}`;
|
|
6617
6618
|
}
|
|
6619
|
+
function lookupCostEstimateUpperBound(command) {
|
|
6620
|
+
return COST_ESTIMATES[command]?.max ?? 0;
|
|
6621
|
+
}
|
|
6622
|
+
function outputSuccess(opts) {
|
|
6623
|
+
const elapsedMs = Math.max(0, Date.now() - opts.startedAt);
|
|
6624
|
+
const costUsd = opts.costUsd ?? (opts.dryRun ? lookupCostEstimateUpperBound(opts.command) : 0);
|
|
6625
|
+
const envelope = {
|
|
6626
|
+
command: opts.command,
|
|
6627
|
+
...opts.dryRun ? { dryRun: true } : {},
|
|
6628
|
+
elapsedMs,
|
|
6629
|
+
costUsd,
|
|
6630
|
+
warnings: opts.warnings ?? [],
|
|
6631
|
+
data: opts.data
|
|
6632
|
+
};
|
|
6633
|
+
if (isJsonMode()) {
|
|
6634
|
+
const fields = process.env.VIBE_OUTPUT_FIELDS;
|
|
6635
|
+
if (fields) {
|
|
6636
|
+
const keys = fields.split(",").map((k) => k.trim());
|
|
6637
|
+
const data = opts.data;
|
|
6638
|
+
const filteredData = {};
|
|
6639
|
+
for (const key of keys) {
|
|
6640
|
+
if (key in data) filteredData[key] = data[key];
|
|
6641
|
+
}
|
|
6642
|
+
envelope.data = filteredData;
|
|
6643
|
+
}
|
|
6644
|
+
console.log(JSON.stringify(envelope, null, 2));
|
|
6645
|
+
return;
|
|
6646
|
+
}
|
|
6647
|
+
if (isQuietMode()) {
|
|
6648
|
+
const data = opts.data;
|
|
6649
|
+
const primary = data.outputPath ?? data.output ?? data.path ?? data.url ?? data.id;
|
|
6650
|
+
if (primary !== void 0) console.log(String(primary));
|
|
6651
|
+
}
|
|
6652
|
+
}
|
|
6618
6653
|
function outputResult(result) {
|
|
6619
6654
|
if (result.dryRun && result.command && typeof result.command === "string") {
|
|
6620
6655
|
const cost = COST_ESTIMATES[result.command];
|
|
@@ -30047,20 +30082,24 @@ Examples:
|
|
|
30047
30082
|
A scene project is bilingual: it works with both \`vibe\` and \`npx hyperframes\`.
|
|
30048
30083
|
Run 'vibe schema scene.<command>' for structured parameter info.`);
|
|
30049
30084
|
sceneCommand.command("init").description("Scaffold a new scene project (or safely augment an existing Hyperframes project)").argument("<dir>", "Project directory (created if it doesn't exist)").option("-n, --name <name>", "Project name (defaults to directory basename)").option("-r, --ratio <ratio>", "Aspect ratio: 16:9, 9:16, 1:1, 4:5", "16:9").option("-d, --duration <sec>", "Default root composition duration (seconds)", "10").option("--visual-style <name>", `Seed DESIGN.md from a named style (browse via \`vibe scene styles\`). E.g. "Swiss Pulse"`).option("--dry-run", "Preview parameters without writing files").action(async (dir, options) => {
|
|
30085
|
+
const startedAt = Date.now();
|
|
30050
30086
|
const aspect = validateAspect(options.ratio);
|
|
30051
30087
|
const duration = validateDuration(options.duration);
|
|
30052
30088
|
const name = options.name ?? basename4(dir.replace(/\/+$/, ""));
|
|
30053
30089
|
const visualStyle = options.visualStyle ? validateVisualStyle(options.visualStyle) : void 0;
|
|
30054
30090
|
if (options.dryRun) {
|
|
30055
|
-
|
|
30056
|
-
dryRun: true,
|
|
30091
|
+
outputSuccess({
|
|
30057
30092
|
command: "scene init",
|
|
30058
|
-
|
|
30059
|
-
|
|
30060
|
-
|
|
30061
|
-
|
|
30062
|
-
|
|
30063
|
-
|
|
30093
|
+
startedAt,
|
|
30094
|
+
dryRun: true,
|
|
30095
|
+
data: {
|
|
30096
|
+
params: {
|
|
30097
|
+
dir,
|
|
30098
|
+
name,
|
|
30099
|
+
aspect,
|
|
30100
|
+
duration,
|
|
30101
|
+
visualStyle: visualStyle?.name ?? null
|
|
30102
|
+
}
|
|
30064
30103
|
}
|
|
30065
30104
|
});
|
|
30066
30105
|
return;
|
|
@@ -30076,19 +30115,21 @@ Run 'vibe schema scene.<command>' for structured parameter info.`);
|
|
|
30076
30115
|
hosts: skillHosts
|
|
30077
30116
|
});
|
|
30078
30117
|
if (isJsonMode()) {
|
|
30079
|
-
|
|
30080
|
-
success: true,
|
|
30118
|
+
outputSuccess({
|
|
30081
30119
|
command: "scene init",
|
|
30082
|
-
|
|
30083
|
-
|
|
30084
|
-
|
|
30085
|
-
|
|
30086
|
-
|
|
30087
|
-
|
|
30088
|
-
|
|
30089
|
-
|
|
30090
|
-
|
|
30091
|
-
|
|
30120
|
+
startedAt,
|
|
30121
|
+
data: {
|
|
30122
|
+
dir,
|
|
30123
|
+
name,
|
|
30124
|
+
aspect,
|
|
30125
|
+
duration,
|
|
30126
|
+
visualStyle: visualStyle?.name ?? null,
|
|
30127
|
+
created: result.created,
|
|
30128
|
+
merged: result.merged,
|
|
30129
|
+
skipped: result.skipped,
|
|
30130
|
+
skillFiles: skillResult.files,
|
|
30131
|
+
skillBundleVersion: skillResult.bundleVersion
|
|
30132
|
+
}
|
|
30092
30133
|
});
|
|
30093
30134
|
return;
|
|
30094
30135
|
}
|
|
@@ -30129,6 +30170,7 @@ Run 'vibe schema scene.<command>' for structured parameter info.`);
|
|
|
30129
30170
|
});
|
|
30130
30171
|
VALID_INSTALL_SKILL_HOSTS = ["claude-code", "cursor", "auto", "all"];
|
|
30131
30172
|
sceneCommand.command("install-skill").description("Install the Hyperframes skill into a scene project so the host agent can read it (Phase H1)").argument("[project-dir]", "Project directory containing STORYBOARD.md / DESIGN.md", ".").option("--host <id>", `Host layout target: ${VALID_INSTALL_SKILL_HOSTS.join(" | ")}`, "auto").option("--force", "Overwrite existing skill files (default: skip-on-exist)").option("--dry-run", "Preview which files would be written without changing anything").action(async (projectDirArg, options) => {
|
|
30173
|
+
const startedAt = Date.now();
|
|
30132
30174
|
const hostFlag = options.host ?? "auto";
|
|
30133
30175
|
if (!VALID_INSTALL_SKILL_HOSTS.includes(hostFlag)) {
|
|
30134
30176
|
exitWithError(usageError(`Invalid --host: ${hostFlag}`, `Valid: ${VALID_INSTALL_SKILL_HOSTS.join(", ")}`));
|
|
@@ -30148,15 +30190,17 @@ Run 'vibe schema scene.<command>' for structured parameter info.`);
|
|
|
30148
30190
|
dryRun: options.dryRun ?? false
|
|
30149
30191
|
});
|
|
30150
30192
|
if (isJsonMode()) {
|
|
30151
|
-
|
|
30152
|
-
success: true,
|
|
30193
|
+
outputSuccess({
|
|
30153
30194
|
command: "scene install-skill",
|
|
30154
|
-
|
|
30155
|
-
|
|
30156
|
-
|
|
30157
|
-
|
|
30158
|
-
|
|
30159
|
-
|
|
30195
|
+
startedAt,
|
|
30196
|
+
dryRun: options.dryRun ?? false,
|
|
30197
|
+
data: {
|
|
30198
|
+
projectDir,
|
|
30199
|
+
host: hostFlag,
|
|
30200
|
+
resolvedHosts: hosts,
|
|
30201
|
+
bundleVersion: result.bundleVersion,
|
|
30202
|
+
files: result.files
|
|
30203
|
+
}
|
|
30160
30204
|
});
|
|
30161
30205
|
return;
|
|
30162
30206
|
}
|
|
@@ -30178,6 +30222,7 @@ Run 'vibe schema scene.<command>' for structured parameter info.`);
|
|
|
30178
30222
|
}
|
|
30179
30223
|
});
|
|
30180
30224
|
sceneCommand.command("compose-prompts").description("Emit the per-beat compose plan for the host agent to author HTML itself (Phase H2 \u2014 no LLM call)").argument("[project-dir]", "Project directory containing STORYBOARD.md / DESIGN.md", ".").option("--beat <id>", "Restrict the plan to a single beat by id (e.g. 'hook', '1')").action(async (projectDirArg, options) => {
|
|
30225
|
+
const startedAt = Date.now();
|
|
30181
30226
|
const projectDir = resolve14(projectDirArg);
|
|
30182
30227
|
const result = await getComposePrompts({
|
|
30183
30228
|
projectDir,
|
|
@@ -30185,18 +30230,21 @@ Run 'vibe schema scene.<command>' for structured parameter info.`);
|
|
|
30185
30230
|
});
|
|
30186
30231
|
if (!result.success) {
|
|
30187
30232
|
if (isJsonMode()) {
|
|
30188
|
-
|
|
30233
|
+
outputSuccess({
|
|
30189
30234
|
command: "scene compose-prompts",
|
|
30190
|
-
|
|
30235
|
+
startedAt,
|
|
30236
|
+
data: { ...result }
|
|
30191
30237
|
});
|
|
30192
|
-
process.
|
|
30238
|
+
process.exitCode = 1;
|
|
30239
|
+
return;
|
|
30193
30240
|
}
|
|
30194
30241
|
exitWithError(generalError(result.error ?? "compose-prompts failed"));
|
|
30195
30242
|
}
|
|
30196
30243
|
if (isJsonMode()) {
|
|
30197
|
-
|
|
30244
|
+
outputSuccess({
|
|
30198
30245
|
command: "scene compose-prompts",
|
|
30199
|
-
|
|
30246
|
+
startedAt,
|
|
30247
|
+
data: { ...result }
|
|
30200
30248
|
});
|
|
30201
30249
|
return;
|
|
30202
30250
|
}
|
|
@@ -30230,20 +30278,23 @@ Run 'vibe schema scene.<command>' for structured parameter info.`);
|
|
|
30230
30278
|
console.log(source_default.dim("Re-run with --json to get the full per-beat userPrompt + cues for direct consumption."));
|
|
30231
30279
|
});
|
|
30232
30280
|
sceneCommand.command("styles").description("List vendored visual styles (or show one) for DESIGN.md seeding").argument("[name]", "Style name to inspect (omit to list all)").action((name) => {
|
|
30281
|
+
const startedAt = Date.now();
|
|
30233
30282
|
if (!name) {
|
|
30234
30283
|
const all = listVisualStyles();
|
|
30235
30284
|
if (isJsonMode()) {
|
|
30236
|
-
|
|
30237
|
-
success: true,
|
|
30285
|
+
outputSuccess({
|
|
30238
30286
|
command: "scene styles",
|
|
30239
|
-
|
|
30240
|
-
|
|
30241
|
-
|
|
30242
|
-
|
|
30243
|
-
|
|
30244
|
-
|
|
30245
|
-
|
|
30246
|
-
|
|
30287
|
+
startedAt,
|
|
30288
|
+
data: {
|
|
30289
|
+
count: all.length,
|
|
30290
|
+
styles: all.map((s) => ({
|
|
30291
|
+
name: s.name,
|
|
30292
|
+
slug: s.slug,
|
|
30293
|
+
designer: s.designer,
|
|
30294
|
+
mood: s.mood,
|
|
30295
|
+
bestFor: s.bestFor
|
|
30296
|
+
}))
|
|
30297
|
+
}
|
|
30247
30298
|
});
|
|
30248
30299
|
return;
|
|
30249
30300
|
}
|
|
@@ -30271,7 +30322,11 @@ Run 'vibe schema scene.<command>' for structured parameter info.`);
|
|
|
30271
30322
|
return;
|
|
30272
30323
|
}
|
|
30273
30324
|
if (isJsonMode()) {
|
|
30274
|
-
|
|
30325
|
+
outputSuccess({
|
|
30326
|
+
command: "scene styles",
|
|
30327
|
+
startedAt,
|
|
30328
|
+
data: { style }
|
|
30329
|
+
});
|
|
30275
30330
|
return;
|
|
30276
30331
|
}
|
|
30277
30332
|
console.log();
|
|
@@ -30293,6 +30348,7 @@ Run 'vibe schema scene.<command>' for structured parameter info.`);
|
|
|
30293
30348
|
console.log(source_default.dim("Seed DESIGN.md:"), source_default.cyan(`vibe scene init <dir> --visual-style "${style.name}"`));
|
|
30294
30349
|
});
|
|
30295
30350
|
sceneCommand.command("add").description("Add a new scene to a project: AI narration + image + per-scene HTML").argument("<name>", "Scene name (slugified into the composition id)").option("--style <preset>", `Style preset: ${SCENE_PRESETS.join(", ")}`, "simple").option("--narration <text>", "Narration text (or path to a .txt file). Drives TTS + scene duration.").option("--narration-file <path>", "Existing narration audio file (.wav/.mp3). Skips TTS \u2014 useful with hyperframes tts, Mac say, or other external tools.").option("-d, --duration <sec>", "Explicit scene duration in seconds (overrides narration audio)").option("--visuals <prompt>", "Image prompt \u2014 generates assets/scene-<id>.png via the configured image provider").option("--headline <text>", "Visible headline (defaults to the humanised scene name)").option("--kicker <text>", "Small label above the headline (explainer / product-shot)").option("--insert-into <path>", "Root composition file to update", "index.html").option("--project <dir>", "Project directory", ".").option("--image-provider <name>", "Image provider: gemini, openai", "gemini").option("--tts <provider>", "TTS provider: auto, elevenlabs, kokoro (default auto \u2014 picks ElevenLabs when key set, else Kokoro local)", "auto").option("--voice <id>", "Voice id (ElevenLabs name/id, or Kokoro id like af_heart, am_michael)").option("--no-audio", "Skip TTS even when --narration is provided (useful for tests/agent dry runs)").option("--no-image", "Skip image generation even when --visuals is provided").option("--no-transcribe", "Skip Whisper word-level transcribe step (no transcript-<id>.json emitted)").option("--transcribe-language <code>", "BCP-47 language code passed to Whisper (e.g. en, ko)").option("--force", "Overwrite an existing compositions/scene-<id>.html").option("--dry-run", "Preview parameters without writing files or calling APIs").action(async (name, options) => {
|
|
30351
|
+
const startedAt = Date.now();
|
|
30296
30352
|
if (options.style) options.style = validatePreset(options.style);
|
|
30297
30353
|
if (options.duration !== void 0) options.duration = validateDuration(options.duration);
|
|
30298
30354
|
let tts;
|
|
@@ -30303,25 +30359,28 @@ Run 'vibe schema scene.<command>' for structured parameter info.`);
|
|
|
30303
30359
|
}
|
|
30304
30360
|
if (options.dryRun) {
|
|
30305
30361
|
const id = slugifySceneName(name);
|
|
30306
|
-
|
|
30307
|
-
dryRun: true,
|
|
30362
|
+
outputSuccess({
|
|
30308
30363
|
command: "scene add",
|
|
30309
|
-
|
|
30310
|
-
|
|
30311
|
-
|
|
30312
|
-
|
|
30313
|
-
|
|
30314
|
-
|
|
30315
|
-
|
|
30316
|
-
|
|
30317
|
-
|
|
30318
|
-
|
|
30319
|
-
|
|
30320
|
-
|
|
30321
|
-
|
|
30322
|
-
|
|
30323
|
-
|
|
30324
|
-
|
|
30364
|
+
startedAt,
|
|
30365
|
+
dryRun: true,
|
|
30366
|
+
data: {
|
|
30367
|
+
params: {
|
|
30368
|
+
name,
|
|
30369
|
+
id,
|
|
30370
|
+
preset: options.style,
|
|
30371
|
+
narration: !!options.narration,
|
|
30372
|
+
visuals: !!options.visuals,
|
|
30373
|
+
duration: options.duration,
|
|
30374
|
+
headline: options.headline,
|
|
30375
|
+
kicker: options.kicker,
|
|
30376
|
+
project: options.project,
|
|
30377
|
+
insertInto: options.insertInto,
|
|
30378
|
+
imageProvider: options.imageProvider,
|
|
30379
|
+
tts,
|
|
30380
|
+
audio: options.audio,
|
|
30381
|
+
// commander sets `audio: false` when --no-audio is passed
|
|
30382
|
+
image: options.image
|
|
30383
|
+
}
|
|
30325
30384
|
}
|
|
30326
30385
|
});
|
|
30327
30386
|
return;
|
|
@@ -30356,9 +30415,10 @@ Run 'vibe schema scene.<command>' for structured parameter info.`);
|
|
|
30356
30415
|
exitWithError(generalError(result.error ?? "Scene add failed"));
|
|
30357
30416
|
}
|
|
30358
30417
|
if (isJsonMode()) {
|
|
30359
|
-
|
|
30418
|
+
outputSuccess({
|
|
30360
30419
|
command: "scene add",
|
|
30361
|
-
|
|
30420
|
+
startedAt,
|
|
30421
|
+
data: { ...result }
|
|
30362
30422
|
});
|
|
30363
30423
|
return;
|
|
30364
30424
|
}
|
|
@@ -30390,6 +30450,7 @@ Run 'vibe schema scene.<command>' for structured parameter info.`);
|
|
|
30390
30450
|
}
|
|
30391
30451
|
});
|
|
30392
30452
|
sceneCommand.command("lint").description("Validate scene HTML against Hyperframes rules (in-process, no Chrome required)").argument("[root]", "Root composition file relative to --project", "index.html").option("--project <dir>", "Project directory", ".").option("--fix", 'Apply mechanical auto-fixes (currently: missing class="clip")').action(async (root, options) => {
|
|
30453
|
+
const startedAt = Date.now();
|
|
30393
30454
|
const projectDir = resolve14(options.project);
|
|
30394
30455
|
if (!await rootExists(projectDir, root)) {
|
|
30395
30456
|
exitWithError(generalError(
|
|
@@ -30407,11 +30468,12 @@ Run 'vibe schema scene.<command>' for structured parameter info.`);
|
|
|
30407
30468
|
exitWithError(generalError(`Lint failed: ${msg}`));
|
|
30408
30469
|
}
|
|
30409
30470
|
if (isJsonMode()) {
|
|
30410
|
-
|
|
30471
|
+
outputSuccess({
|
|
30411
30472
|
command: "scene lint",
|
|
30412
|
-
|
|
30473
|
+
startedAt,
|
|
30474
|
+
data: { ...result }
|
|
30413
30475
|
});
|
|
30414
|
-
if (!result.ok) process.
|
|
30476
|
+
if (!result.ok) process.exitCode = 1;
|
|
30415
30477
|
return;
|
|
30416
30478
|
}
|
|
30417
30479
|
if (result.ok && result.warningCount === 0 && result.infoCount === 0) {
|
|
@@ -30445,29 +30507,33 @@ Run 'vibe schema scene.<command>' for structured parameter info.`);
|
|
|
30445
30507
|
console.log(` ${source_default.green("\u2714")} ${fx.file} ${source_default.dim(fx.codes.join(", "))}`);
|
|
30446
30508
|
}
|
|
30447
30509
|
}
|
|
30448
|
-
if (!result.ok) process.
|
|
30510
|
+
if (!result.ok) process.exitCode = 1;
|
|
30449
30511
|
});
|
|
30450
30512
|
VALID_FPS = [24, 30, 60];
|
|
30451
30513
|
VALID_QUALITIES = ["draft", "standard", "high"];
|
|
30452
30514
|
VALID_FORMATS = ["mp4", "webm", "mov"];
|
|
30453
30515
|
sceneCommand.command("render").description("Render a scene project to MP4/WebM/MOV via the Hyperframes producer (requires Chrome)").argument("[root]", "Root composition file relative to --project", "index.html").option("--project <dir>", "Project directory", ".").option("-o, --out <path>", "Output file (default: renders/<name>-<timestamp>.<format>)").option("--fps <n>", `Frames per second: ${VALID_FPS.join("|")}`, "30").option("--quality <q>", `Quality preset: ${VALID_QUALITIES.join("|")}`, "standard").option("--format <f>", `Output container: ${VALID_FORMATS.join("|")}`, "mp4").option("--workers <n>", "Capture workers (1-16, default 1)", "1").option("--dry-run", "Preview parameters without rendering").action(async (root, options) => {
|
|
30516
|
+
const startedAt = Date.now();
|
|
30454
30517
|
const fps = validateFps(options.fps);
|
|
30455
30518
|
const quality = validateQuality(options.quality);
|
|
30456
30519
|
const format = validateFormat(options.format);
|
|
30457
30520
|
const workers = validateWorkers(options.workers);
|
|
30458
30521
|
const projectDir = resolve14(options.project);
|
|
30459
30522
|
if (options.dryRun) {
|
|
30460
|
-
|
|
30461
|
-
dryRun: true,
|
|
30523
|
+
outputSuccess({
|
|
30462
30524
|
command: "scene render",
|
|
30463
|
-
|
|
30464
|
-
|
|
30465
|
-
|
|
30466
|
-
|
|
30467
|
-
|
|
30468
|
-
|
|
30469
|
-
|
|
30470
|
-
|
|
30525
|
+
startedAt,
|
|
30526
|
+
dryRun: true,
|
|
30527
|
+
data: {
|
|
30528
|
+
params: {
|
|
30529
|
+
projectDir,
|
|
30530
|
+
root,
|
|
30531
|
+
output: options.out,
|
|
30532
|
+
fps,
|
|
30533
|
+
quality,
|
|
30534
|
+
format,
|
|
30535
|
+
workers
|
|
30536
|
+
}
|
|
30471
30537
|
}
|
|
30472
30538
|
});
|
|
30473
30539
|
return;
|
|
@@ -30488,13 +30554,22 @@ Run 'vibe schema scene.<command>' for structured parameter info.`);
|
|
|
30488
30554
|
if (!result.success) {
|
|
30489
30555
|
spinner2?.fail("Render failed");
|
|
30490
30556
|
if (isJsonMode()) {
|
|
30491
|
-
|
|
30492
|
-
|
|
30557
|
+
outputSuccess({
|
|
30558
|
+
command: "scene render",
|
|
30559
|
+
startedAt,
|
|
30560
|
+
data: { ...result }
|
|
30561
|
+
});
|
|
30562
|
+
process.exitCode = 1;
|
|
30563
|
+
return;
|
|
30493
30564
|
}
|
|
30494
30565
|
exitWithError(generalError(result.error ?? "Render failed"));
|
|
30495
30566
|
}
|
|
30496
30567
|
if (isJsonMode()) {
|
|
30497
|
-
|
|
30568
|
+
outputSuccess({
|
|
30569
|
+
command: "scene render",
|
|
30570
|
+
startedAt,
|
|
30571
|
+
data: { ...result }
|
|
30572
|
+
});
|
|
30498
30573
|
return;
|
|
30499
30574
|
}
|
|
30500
30575
|
spinner2?.succeed(source_default.green(`Render complete: ${result.outputPath}`));
|
|
@@ -30514,25 +30589,29 @@ Run 'vibe schema scene.<command>' for structured parameter info.`);
|
|
|
30514
30589
|
}
|
|
30515
30590
|
});
|
|
30516
30591
|
sceneCommand.command("build").description("One-shot: read STORYBOARD.md cues, dispatch TTS + image-gen per beat, compose, render to MP4 (v0.60)").argument("[project-dir]", "Project directory containing STORYBOARD.md", ".").option("--mode <mode>", "Build mode: agent (host-agent authors HTML) | batch (CLI's internal LLM authors HTML) | auto (agent if any host detected) [Plan H \u2014 Phase 3]", "auto").option("--effort <level>", "Compose effort tier (batch mode only): low|medium|high", "medium").option("--composer <provider>", "LLM that composes scene HTML in batch mode: claude|openai|gemini (default: auto-resolve from available API keys, claude > gemini > openai)").option("--skip-narration", "Don't dispatch TTS even when beats declare narration cues").option("--skip-backdrop", "Don't dispatch image-gen even when beats declare backdrop cues").option("--skip-render", "Compose only \u2014 don't render to MP4").option("--tts <provider>", "TTS provider: auto|elevenlabs|kokoro (overrides frontmatter)").option("--voice <id>", "Voice id (provider-specific \u2014 overrides frontmatter)").option("--image-provider <name>", "Image provider: openai (only one supported in v0.60)").option("--quality <q>", "Image quality: standard|hd", "hd").option("--image-size <s>", "Image size: 1024x1024|1536x1024|1024x1536", "1536x1024").option("--force", "Re-dispatch primitives even when assets already exist").option("--dry-run", "Preview parameters without dispatching").action(async (projectDirArg, options) => {
|
|
30592
|
+
const startedAt = Date.now();
|
|
30517
30593
|
const projectDir = resolve14(projectDirArg);
|
|
30518
30594
|
if (options.dryRun) {
|
|
30519
|
-
|
|
30520
|
-
dryRun: true,
|
|
30595
|
+
outputSuccess({
|
|
30521
30596
|
command: "scene build",
|
|
30522
|
-
|
|
30523
|
-
|
|
30524
|
-
|
|
30525
|
-
|
|
30526
|
-
|
|
30527
|
-
|
|
30528
|
-
|
|
30529
|
-
|
|
30530
|
-
|
|
30531
|
-
|
|
30532
|
-
|
|
30533
|
-
|
|
30534
|
-
|
|
30535
|
-
|
|
30597
|
+
startedAt,
|
|
30598
|
+
dryRun: true,
|
|
30599
|
+
data: {
|
|
30600
|
+
params: {
|
|
30601
|
+
projectDir,
|
|
30602
|
+
mode: options.mode,
|
|
30603
|
+
effort: options.effort,
|
|
30604
|
+
composer: options.composer,
|
|
30605
|
+
skipNarration: options.skipNarration ?? false,
|
|
30606
|
+
skipBackdrop: options.skipBackdrop ?? false,
|
|
30607
|
+
skipRender: options.skipRender ?? false,
|
|
30608
|
+
ttsProvider: options.tts,
|
|
30609
|
+
voice: options.voice,
|
|
30610
|
+
imageProvider: options.imageProvider,
|
|
30611
|
+
imageQuality: options.quality,
|
|
30612
|
+
imageSize: options.imageSize,
|
|
30613
|
+
force: options.force ?? false
|
|
30614
|
+
}
|
|
30536
30615
|
}
|
|
30537
30616
|
});
|
|
30538
30617
|
return;
|
|
@@ -30586,13 +30665,22 @@ Run 'vibe schema scene.<command>' for structured parameter info.`);
|
|
|
30586
30665
|
if (!result.success) {
|
|
30587
30666
|
spinner2?.fail(`Build failed: ${result.error}`);
|
|
30588
30667
|
if (isJsonMode()) {
|
|
30589
|
-
|
|
30590
|
-
|
|
30668
|
+
outputSuccess({
|
|
30669
|
+
command: "scene build",
|
|
30670
|
+
startedAt,
|
|
30671
|
+
data: { ...result }
|
|
30672
|
+
});
|
|
30673
|
+
process.exitCode = 1;
|
|
30674
|
+
return;
|
|
30591
30675
|
}
|
|
30592
30676
|
exitWithError(generalError(result.error ?? "Build failed"));
|
|
30593
30677
|
}
|
|
30594
30678
|
if (isJsonMode()) {
|
|
30595
|
-
|
|
30679
|
+
outputSuccess({
|
|
30680
|
+
command: "scene build",
|
|
30681
|
+
startedAt,
|
|
30682
|
+
data: { ...result }
|
|
30683
|
+
});
|
|
30596
30684
|
return;
|
|
30597
30685
|
}
|
|
30598
30686
|
if (result.phase === "needs-author") {
|
|
@@ -31606,27 +31694,31 @@ Use this image analysis to inform the color palette, typography placement, and o
|
|
|
31606
31694
|
}
|
|
31607
31695
|
function registerMotionCommand(aiCommand) {
|
|
31608
31696
|
aiCommand.command("motion").description("Generate motion graphics using Claude + Remotion (render & composite)").argument("<description>", "Natural language description of the motion graphic").option("-k, --api-key <key>", "Anthropic API key (or set ANTHROPIC_API_KEY env)").option("-o, --output <path>", "Output file path", "motion.tsx").option("-d, --duration <sec>", "Duration in seconds", "5").option("-w, --width <px>", "Width in pixels", "1920").option("-h, --height <px>", "Height in pixels", "1080").option("--fps <fps>", "Frame rate", "30").option("-s, --style <style>", "Style preset: minimal, corporate, playful, cinematic").option("--render", "Render the generated code with Remotion (output .webm)").option("--video <path>", "Base video to composite the motion graphic onto").option("--image <path>", "Image to analyze with Gemini \u2014 color/mood fed into Claude prompt").option("--from-tsx <path>", "Refine an existing TSX file instead of generating from scratch").option("-m, --model <alias>", "LLM model: sonnet (default), opus, gemini, gemini-3.1-pro", "sonnet").option("--dry-run", "Preview parameters without executing").action(async (description, options) => {
|
|
31697
|
+
const startedAt = Date.now();
|
|
31609
31698
|
try {
|
|
31610
31699
|
if (options.output) {
|
|
31611
31700
|
validateOutputPath(options.output);
|
|
31612
31701
|
}
|
|
31613
31702
|
if (options.dryRun) {
|
|
31614
|
-
|
|
31703
|
+
outputSuccess({
|
|
31704
|
+
command: "generate motion",
|
|
31705
|
+
startedAt,
|
|
31615
31706
|
dryRun: true,
|
|
31616
|
-
|
|
31617
|
-
|
|
31618
|
-
|
|
31619
|
-
|
|
31620
|
-
|
|
31621
|
-
|
|
31622
|
-
|
|
31623
|
-
|
|
31624
|
-
|
|
31625
|
-
|
|
31626
|
-
|
|
31627
|
-
|
|
31628
|
-
|
|
31629
|
-
|
|
31707
|
+
data: {
|
|
31708
|
+
params: {
|
|
31709
|
+
description: description.slice(0, 200),
|
|
31710
|
+
duration: options.duration,
|
|
31711
|
+
width: options.width,
|
|
31712
|
+
height: options.height,
|
|
31713
|
+
fps: options.fps,
|
|
31714
|
+
style: options.style,
|
|
31715
|
+
render: options.render ?? false,
|
|
31716
|
+
video: options.video,
|
|
31717
|
+
image: options.image,
|
|
31718
|
+
fromTsx: options.fromTsx,
|
|
31719
|
+
model: options.model,
|
|
31720
|
+
output: options.output
|
|
31721
|
+
}
|
|
31630
31722
|
}
|
|
31631
31723
|
});
|
|
31632
31724
|
return;
|
|
@@ -31740,20 +31832,24 @@ async function executeSoundEffect(options) {
|
|
|
31740
31832
|
}
|
|
31741
31833
|
function registerSoundEffectCommand(parent) {
|
|
31742
31834
|
parent.command("sound-effect").description("Generate sound effect using ElevenLabs").argument("<prompt>", "Description of the sound effect").option("-k, --api-key <key>", "ElevenLabs API key (or set ELEVENLABS_API_KEY env)").option("-o, --output <path>", "Output audio file path", "sound-effect.mp3").option("-d, --duration <seconds>", "Duration in seconds (0.5-22, default: auto)").option("--prompt-influence <value>", "Prompt influence (0-1, default: 0.3)").option("--dry-run", "Preview parameters without executing").action(async (prompt3, options) => {
|
|
31835
|
+
const startedAt = Date.now();
|
|
31743
31836
|
try {
|
|
31744
31837
|
rejectControlChars(prompt3);
|
|
31745
31838
|
if (options.output) {
|
|
31746
31839
|
validateOutputPath(options.output);
|
|
31747
31840
|
}
|
|
31748
31841
|
if (options.dryRun) {
|
|
31749
|
-
|
|
31750
|
-
dryRun: true,
|
|
31842
|
+
outputSuccess({
|
|
31751
31843
|
command: "generate sound-effect",
|
|
31752
|
-
|
|
31753
|
-
|
|
31754
|
-
|
|
31755
|
-
|
|
31756
|
-
|
|
31844
|
+
startedAt,
|
|
31845
|
+
dryRun: true,
|
|
31846
|
+
data: {
|
|
31847
|
+
params: {
|
|
31848
|
+
prompt: prompt3,
|
|
31849
|
+
duration: options.duration,
|
|
31850
|
+
promptInfluence: options.promptInfluence,
|
|
31851
|
+
output: options.output
|
|
31852
|
+
}
|
|
31757
31853
|
}
|
|
31758
31854
|
});
|
|
31759
31855
|
return;
|
|
@@ -31780,7 +31876,11 @@ function registerSoundEffectCommand(parent) {
|
|
|
31780
31876
|
await writeFile13(outputPath, result.audioBuffer);
|
|
31781
31877
|
spinner2.succeed(source_default.green("Sound effect generated"));
|
|
31782
31878
|
if (isJsonMode()) {
|
|
31783
|
-
|
|
31879
|
+
outputSuccess({
|
|
31880
|
+
command: "generate sound-effect",
|
|
31881
|
+
startedAt,
|
|
31882
|
+
data: { outputPath }
|
|
31883
|
+
});
|
|
31784
31884
|
return;
|
|
31785
31885
|
}
|
|
31786
31886
|
console.log(source_default.green(`Saved to: ${outputPath}`));
|
|
@@ -31830,6 +31930,7 @@ async function executeMusicStatus(options) {
|
|
|
31830
31930
|
}
|
|
31831
31931
|
function registerMusicStatusCommand(parent) {
|
|
31832
31932
|
parent.command("music-status", { hidden: true }).description("Check music generation status").argument("<task-id>", "Task ID from music generation").option("-k, --api-key <key>", "Replicate API token (or set REPLICATE_API_TOKEN env)").action(async (taskId, options) => {
|
|
31933
|
+
const startedAt = Date.now();
|
|
31833
31934
|
try {
|
|
31834
31935
|
const apiKey = await requireApiKey(
|
|
31835
31936
|
"REPLICATE_API_TOKEN",
|
|
@@ -31841,12 +31942,15 @@ function registerMusicStatusCommand(parent) {
|
|
|
31841
31942
|
const result = await replicate.getMusicStatus(taskId);
|
|
31842
31943
|
if (isJsonMode()) {
|
|
31843
31944
|
const status = result.audioUrl ? "completed" : result.error ? "failed" : "processing";
|
|
31844
|
-
|
|
31845
|
-
|
|
31846
|
-
|
|
31847
|
-
|
|
31848
|
-
|
|
31849
|
-
|
|
31945
|
+
outputSuccess({
|
|
31946
|
+
command: "generate music-status",
|
|
31947
|
+
startedAt,
|
|
31948
|
+
data: {
|
|
31949
|
+
taskId,
|
|
31950
|
+
status,
|
|
31951
|
+
audioUrl: result.audioUrl,
|
|
31952
|
+
error: result.error
|
|
31953
|
+
}
|
|
31850
31954
|
});
|
|
31851
31955
|
return;
|
|
31852
31956
|
}
|
|
@@ -31884,6 +31988,7 @@ var init_music_status = __esm({
|
|
|
31884
31988
|
// src/commands/generate/video-cancel.ts
|
|
31885
31989
|
function registerVideoCancelCommand(parent) {
|
|
31886
31990
|
parent.command("video-cancel", { hidden: true }).description("Cancel video generation (Grok or Runway)").argument("<task-id>", "Task ID to cancel").option("-p, --provider <provider>", "Provider: grok, runway", "grok").option("-k, --api-key <key>", "API key (or set XAI_API_KEY / RUNWAY_API_SECRET env)").action(async (taskId, options) => {
|
|
31991
|
+
const startedAt = Date.now();
|
|
31887
31992
|
try {
|
|
31888
31993
|
const provider = (options.provider || "grok").toLowerCase();
|
|
31889
31994
|
let success = false;
|
|
@@ -31896,7 +32001,11 @@ function registerVideoCancelCommand(parent) {
|
|
|
31896
32001
|
if (success) {
|
|
31897
32002
|
spinner2.succeed(source_default.green("Generation cancelled"));
|
|
31898
32003
|
if (isJsonMode()) {
|
|
31899
|
-
|
|
32004
|
+
outputSuccess({
|
|
32005
|
+
command: "generate video-cancel",
|
|
32006
|
+
startedAt,
|
|
32007
|
+
data: { taskId, provider: "grok", cancelled: true }
|
|
32008
|
+
});
|
|
31900
32009
|
return;
|
|
31901
32010
|
}
|
|
31902
32011
|
} else {
|
|
@@ -31916,7 +32025,11 @@ function registerVideoCancelCommand(parent) {
|
|
|
31916
32025
|
if (success) {
|
|
31917
32026
|
spinner2.succeed(source_default.green("Generation cancelled"));
|
|
31918
32027
|
if (isJsonMode()) {
|
|
31919
|
-
|
|
32028
|
+
outputSuccess({
|
|
32029
|
+
command: "generate video-cancel",
|
|
32030
|
+
startedAt,
|
|
32031
|
+
data: { taskId, provider: "runway", cancelled: true }
|
|
32032
|
+
});
|
|
31920
32033
|
return;
|
|
31921
32034
|
}
|
|
31922
32035
|
} else {
|
|
@@ -31989,16 +32102,18 @@ async function executeBackground(options) {
|
|
|
31989
32102
|
}
|
|
31990
32103
|
function registerBackgroundCommand(parent) {
|
|
31991
32104
|
parent.command("background").description("Generate video background using DALL-E").argument("<description>", "Background description").option("-k, --api-key <key>", "OpenAI API key (or set OPENAI_API_KEY env)").option("-o, --output <path>", "Output file path (downloads image)").option("-a, --aspect <ratio>", "Aspect ratio: 16:9, 9:16, 1:1", "16:9").option("--dry-run", "Preview parameters without executing").action(async (description, options) => {
|
|
32105
|
+
const startedAt = Date.now();
|
|
31992
32106
|
try {
|
|
31993
32107
|
rejectControlChars(description);
|
|
31994
32108
|
if (options.output) {
|
|
31995
32109
|
validateOutputPath(options.output);
|
|
31996
32110
|
}
|
|
31997
32111
|
if (options.dryRun) {
|
|
31998
|
-
|
|
31999
|
-
dryRun: true,
|
|
32112
|
+
outputSuccess({
|
|
32000
32113
|
command: "generate background",
|
|
32001
|
-
|
|
32114
|
+
startedAt,
|
|
32115
|
+
dryRun: true,
|
|
32116
|
+
data: { params: { description, aspect: options.aspect, output: options.output } }
|
|
32002
32117
|
});
|
|
32003
32118
|
return;
|
|
32004
32119
|
}
|
|
@@ -32033,7 +32148,11 @@ function registerBackgroundCommand(parent) {
|
|
|
32033
32148
|
await mkdir9(dirname7(outputPath), { recursive: true });
|
|
32034
32149
|
await writeFile14(outputPath, buffer);
|
|
32035
32150
|
}
|
|
32036
|
-
|
|
32151
|
+
outputSuccess({
|
|
32152
|
+
command: "generate background",
|
|
32153
|
+
startedAt,
|
|
32154
|
+
data: { imageUrl: img.url, outputPath }
|
|
32155
|
+
});
|
|
32037
32156
|
return;
|
|
32038
32157
|
}
|
|
32039
32158
|
console.log();
|
|
@@ -32179,6 +32298,7 @@ async function executeStoryboard(options) {
|
|
|
32179
32298
|
}
|
|
32180
32299
|
function registerStoryboardCommand(parent) {
|
|
32181
32300
|
parent.command("storyboard").description("Generate video storyboard from content using Claude").argument("<content>", "Content to analyze (text or file path)").option("-k, --api-key <key>", "Anthropic API key (or set ANTHROPIC_API_KEY env)").option("-o, --output <path>", "Output JSON file path").option("-d, --duration <sec>", "Target total duration in seconds").option("-f, --file", "Treat content argument as file path").option("-c, --creativity <level>", "Creativity level: low (default, consistent) or high (varied, unexpected)", "low").option("--dry-run", "Preview parameters without executing").action(async (content, options) => {
|
|
32301
|
+
const startedAt = Date.now();
|
|
32182
32302
|
try {
|
|
32183
32303
|
rejectControlChars(content);
|
|
32184
32304
|
if (options.output) {
|
|
@@ -32194,13 +32314,16 @@ function registerStoryboardCommand(parent) {
|
|
|
32194
32314
|
textContent = await readFile14(filePath, "utf-8");
|
|
32195
32315
|
}
|
|
32196
32316
|
if (options.dryRun) {
|
|
32197
|
-
|
|
32198
|
-
dryRun: true,
|
|
32317
|
+
outputSuccess({
|
|
32199
32318
|
command: "generate storyboard",
|
|
32200
|
-
|
|
32201
|
-
|
|
32202
|
-
|
|
32203
|
-
|
|
32319
|
+
startedAt,
|
|
32320
|
+
dryRun: true,
|
|
32321
|
+
data: {
|
|
32322
|
+
params: {
|
|
32323
|
+
content: textContent.substring(0, 200),
|
|
32324
|
+
duration: options.duration,
|
|
32325
|
+
creativity
|
|
32326
|
+
}
|
|
32204
32327
|
}
|
|
32205
32328
|
});
|
|
32206
32329
|
return;
|
|
@@ -32232,16 +32355,23 @@ function registerStoryboardCommand(parent) {
|
|
|
32232
32355
|
const outputPath = resolve19(process.cwd(), options.output);
|
|
32233
32356
|
await writeFile15(outputPath, JSON.stringify(segments, null, 2), "utf-8");
|
|
32234
32357
|
if (isJsonMode()) {
|
|
32235
|
-
|
|
32236
|
-
|
|
32237
|
-
|
|
32238
|
-
|
|
32239
|
-
|
|
32358
|
+
outputSuccess({
|
|
32359
|
+
command: "generate storyboard",
|
|
32360
|
+
startedAt,
|
|
32361
|
+
data: {
|
|
32362
|
+
segmentCount: segments.length,
|
|
32363
|
+
segments,
|
|
32364
|
+
outputPath
|
|
32365
|
+
}
|
|
32240
32366
|
});
|
|
32241
32367
|
return;
|
|
32242
32368
|
}
|
|
32243
32369
|
} else if (isJsonMode()) {
|
|
32244
|
-
|
|
32370
|
+
outputSuccess({
|
|
32371
|
+
command: "generate storyboard",
|
|
32372
|
+
startedAt,
|
|
32373
|
+
data: { segmentCount: segments.length, segments }
|
|
32374
|
+
});
|
|
32245
32375
|
return;
|
|
32246
32376
|
}
|
|
32247
32377
|
console.log();
|
|
@@ -32486,6 +32616,7 @@ async function executeSpeech(options) {
|
|
|
32486
32616
|
}
|
|
32487
32617
|
function registerSpeechCommand(parent) {
|
|
32488
32618
|
parent.command("speech").alias("tts").description("Generate speech from text using ElevenLabs").argument("[text]", "Text to convert to speech (interactive if omitted)").option("-k, --api-key <key>", "ElevenLabs API key (or set ELEVENLABS_API_KEY env)").option("-o, --output <path>", "Output audio file path", "output.mp3").option("-v, --voice <id>", "Voice ID (default: Rachel)", "21m00Tcm4TlvDq8ikWAM").option("--list-voices", "List available voices").option("--fit-duration <seconds>", "Speed up audio to fit target duration (via FFmpeg atempo)", parseFloat).option("--dry-run", "Preview parameters without executing").action(async (text, options) => {
|
|
32619
|
+
const startedAt = Date.now();
|
|
32489
32620
|
try {
|
|
32490
32621
|
if (!text) {
|
|
32491
32622
|
if (hasTTY()) {
|
|
@@ -32507,10 +32638,11 @@ function registerSpeechCommand(parent) {
|
|
|
32507
32638
|
validateOutputPath(options.output);
|
|
32508
32639
|
}
|
|
32509
32640
|
if (options.dryRun) {
|
|
32510
|
-
|
|
32511
|
-
dryRun: true,
|
|
32641
|
+
outputSuccess({
|
|
32512
32642
|
command: "generate speech",
|
|
32513
|
-
|
|
32643
|
+
startedAt,
|
|
32644
|
+
dryRun: true,
|
|
32645
|
+
data: { params: { text, voice: options.voice, output: options.output } }
|
|
32514
32646
|
});
|
|
32515
32647
|
return;
|
|
32516
32648
|
}
|
|
@@ -32599,10 +32731,13 @@ function registerSpeechCommand(parent) {
|
|
|
32599
32731
|
}
|
|
32600
32732
|
}
|
|
32601
32733
|
if (isJsonMode()) {
|
|
32602
|
-
|
|
32603
|
-
|
|
32604
|
-
|
|
32605
|
-
|
|
32734
|
+
outputSuccess({
|
|
32735
|
+
command: "generate speech",
|
|
32736
|
+
startedAt,
|
|
32737
|
+
data: {
|
|
32738
|
+
characterCount: result.characterCount,
|
|
32739
|
+
outputPath
|
|
32740
|
+
}
|
|
32606
32741
|
});
|
|
32607
32742
|
return;
|
|
32608
32743
|
}
|
|
@@ -32691,6 +32826,7 @@ async function executeMusic(options) {
|
|
|
32691
32826
|
}
|
|
32692
32827
|
function registerMusicCommand(parent) {
|
|
32693
32828
|
parent.command("music").description("Generate background music from a text prompt (ElevenLabs or Replicate MusicGen)").argument("<prompt>", "Description of the music to generate").option("-p, --provider <provider>", "Provider: elevenlabs (default, up to 10min), replicate (MusicGen, max 30s)", "elevenlabs").option("-k, --api-key <key>", "API key (or set ELEVENLABS_API_KEY / REPLICATE_API_TOKEN env)").option("-d, --duration <seconds>", "Duration in seconds (elevenlabs: 3-600, replicate: 1-30)", "8").option("--instrumental", "Force instrumental music, no vocals (ElevenLabs only)").option("-m, --melody <file>", "Reference melody audio file for conditioning (Replicate only)").option("--model <model>", "Model variant (Replicate only): large, stereo-large, melody-large, stereo-melody-large", "stereo-large").option("-o, --output <path>", "Output audio file path", "music.mp3").option("--no-wait", "Don't wait for generation to complete (Replicate async mode)").option("--dry-run", "Preview parameters without executing").action(async (prompt3, options) => {
|
|
32829
|
+
const startedAt = Date.now();
|
|
32694
32830
|
try {
|
|
32695
32831
|
rejectControlChars(prompt3);
|
|
32696
32832
|
if (options.output) {
|
|
@@ -32698,16 +32834,19 @@ function registerMusicCommand(parent) {
|
|
|
32698
32834
|
}
|
|
32699
32835
|
const provider = (options.provider || "elevenlabs").toLowerCase();
|
|
32700
32836
|
if (options.dryRun) {
|
|
32701
|
-
|
|
32702
|
-
dryRun: true,
|
|
32837
|
+
outputSuccess({
|
|
32703
32838
|
command: "generate music",
|
|
32704
|
-
|
|
32705
|
-
|
|
32706
|
-
|
|
32707
|
-
|
|
32708
|
-
|
|
32709
|
-
|
|
32710
|
-
|
|
32839
|
+
startedAt,
|
|
32840
|
+
dryRun: true,
|
|
32841
|
+
data: {
|
|
32842
|
+
params: {
|
|
32843
|
+
prompt: prompt3,
|
|
32844
|
+
provider,
|
|
32845
|
+
duration: options.duration,
|
|
32846
|
+
model: options.model,
|
|
32847
|
+
output: options.output,
|
|
32848
|
+
instrumental: options.instrumental
|
|
32849
|
+
}
|
|
32711
32850
|
}
|
|
32712
32851
|
});
|
|
32713
32852
|
return;
|
|
@@ -32734,11 +32873,14 @@ function registerMusicCommand(parent) {
|
|
|
32734
32873
|
await writeFile17(outputPath, result.audioBuffer);
|
|
32735
32874
|
spinner2.succeed(source_default.green("Music generated successfully"));
|
|
32736
32875
|
if (isJsonMode()) {
|
|
32737
|
-
|
|
32738
|
-
|
|
32739
|
-
|
|
32740
|
-
|
|
32741
|
-
|
|
32876
|
+
outputSuccess({
|
|
32877
|
+
command: "generate music",
|
|
32878
|
+
startedAt,
|
|
32879
|
+
data: {
|
|
32880
|
+
provider: "elevenlabs",
|
|
32881
|
+
outputPath,
|
|
32882
|
+
duration
|
|
32883
|
+
}
|
|
32742
32884
|
});
|
|
32743
32885
|
return;
|
|
32744
32886
|
}
|
|
@@ -32806,12 +32948,15 @@ function registerMusicCommand(parent) {
|
|
|
32806
32948
|
await writeFile17(outputPath, audioBuffer);
|
|
32807
32949
|
spinner2.succeed(source_default.green("Music generated successfully"));
|
|
32808
32950
|
if (isJsonMode()) {
|
|
32809
|
-
|
|
32810
|
-
|
|
32811
|
-
|
|
32812
|
-
|
|
32813
|
-
|
|
32814
|
-
|
|
32951
|
+
outputSuccess({
|
|
32952
|
+
command: "generate music",
|
|
32953
|
+
startedAt,
|
|
32954
|
+
data: {
|
|
32955
|
+
provider: "replicate",
|
|
32956
|
+
taskId: result.taskId,
|
|
32957
|
+
audioUrl: finalResult.audioUrl,
|
|
32958
|
+
outputPath
|
|
32959
|
+
}
|
|
32815
32960
|
});
|
|
32816
32961
|
return;
|
|
32817
32962
|
}
|
|
@@ -33111,6 +33256,7 @@ import { existsSync as existsSync15 } from "node:fs";
|
|
|
33111
33256
|
import { writeFile as writeFile19, mkdir as mkdir11 } from "node:fs/promises";
|
|
33112
33257
|
function registerThumbnailCommand(parent) {
|
|
33113
33258
|
parent.command("thumbnail").description("Generate video thumbnail (DALL-E) or extract best frame from video (Gemini)").argument("[description]", "Thumbnail description (for DALL-E generation)").option("-k, --api-key <key>", "API key (OpenAI for generation, Google for best-frame)").option("-o, --output <path>", "Output file path").option("-s, --style <style>", "Platform style: youtube, instagram, tiktok, twitter").option("--best-frame <video>", "Extract best thumbnail frame from video using Gemini AI").option("--prompt <prompt>", "Custom prompt for best-frame analysis").option("--model <model>", "Gemini model: flash, latest, pro (default: flash)", "flash").action(async (description, options) => {
|
|
33259
|
+
const startedAt = Date.now();
|
|
33114
33260
|
try {
|
|
33115
33261
|
if (description) rejectControlChars(description);
|
|
33116
33262
|
if (options.output) {
|
|
@@ -33146,11 +33292,14 @@ function registerThumbnailCommand(parent) {
|
|
|
33146
33292
|
}
|
|
33147
33293
|
spinner3.succeed(source_default.green("Best frame extracted"));
|
|
33148
33294
|
if (isJsonMode()) {
|
|
33149
|
-
|
|
33150
|
-
|
|
33151
|
-
|
|
33152
|
-
|
|
33153
|
-
|
|
33295
|
+
outputSuccess({
|
|
33296
|
+
command: "generate thumbnail",
|
|
33297
|
+
startedAt,
|
|
33298
|
+
data: {
|
|
33299
|
+
timestamp: result2.timestamp,
|
|
33300
|
+
reason: result2.reason,
|
|
33301
|
+
outputPath: result2.outputPath
|
|
33302
|
+
}
|
|
33154
33303
|
});
|
|
33155
33304
|
return;
|
|
33156
33305
|
}
|
|
@@ -33198,7 +33347,11 @@ function registerThumbnailCommand(parent) {
|
|
|
33198
33347
|
await mkdir11(dirname9(outputPath), { recursive: true });
|
|
33199
33348
|
await writeFile19(outputPath, buffer);
|
|
33200
33349
|
}
|
|
33201
|
-
|
|
33350
|
+
outputSuccess({
|
|
33351
|
+
command: "generate thumbnail",
|
|
33352
|
+
startedAt,
|
|
33353
|
+
data: { imageUrl: img.url, outputPath }
|
|
33354
|
+
});
|
|
33202
33355
|
return;
|
|
33203
33356
|
}
|
|
33204
33357
|
console.log();
|
|
@@ -33269,6 +33422,7 @@ function getStatusColor(status) {
|
|
|
33269
33422
|
}
|
|
33270
33423
|
function registerVideoStatusCommand(parent) {
|
|
33271
33424
|
parent.command("video-status", { hidden: true }).description("Check video generation status (Grok, Runway, or Kling)").argument("<task-id>", "Task ID from video generation").option("-p, --provider <provider>", "Provider: grok, runway, kling", "grok").option("-k, --api-key <key>", "API key (or set XAI_API_KEY / RUNWAY_API_SECRET / KLING_API_KEY env)").option("-t, --type <type>", "Task type: text2video or image2video (Kling only)", "text2video").option("-w, --wait", "Wait for completion").option("-o, --output <path>", "Download video when complete").action(async (taskId, options) => {
|
|
33425
|
+
const startedAt = Date.now();
|
|
33272
33426
|
try {
|
|
33273
33427
|
const provider = (options.provider || "grok").toLowerCase();
|
|
33274
33428
|
if (provider === "grok") {
|
|
@@ -33291,14 +33445,17 @@ function registerVideoStatusCommand(parent) {
|
|
|
33291
33445
|
outputPath = resolve24(process.cwd(), options.output);
|
|
33292
33446
|
await writeFile20(outputPath, buffer);
|
|
33293
33447
|
}
|
|
33294
|
-
|
|
33295
|
-
|
|
33296
|
-
|
|
33297
|
-
|
|
33298
|
-
|
|
33299
|
-
|
|
33300
|
-
|
|
33301
|
-
|
|
33448
|
+
outputSuccess({
|
|
33449
|
+
command: "generate video-status",
|
|
33450
|
+
startedAt,
|
|
33451
|
+
data: {
|
|
33452
|
+
taskId,
|
|
33453
|
+
provider: "grok",
|
|
33454
|
+
status: result.status,
|
|
33455
|
+
videoUrl: result.videoUrl,
|
|
33456
|
+
error: result.error,
|
|
33457
|
+
outputPath
|
|
33458
|
+
}
|
|
33302
33459
|
});
|
|
33303
33460
|
return;
|
|
33304
33461
|
}
|
|
@@ -33356,15 +33513,18 @@ function registerVideoStatusCommand(parent) {
|
|
|
33356
33513
|
outputPath = resolve24(process.cwd(), options.output);
|
|
33357
33514
|
await writeFile20(outputPath, buffer);
|
|
33358
33515
|
}
|
|
33359
|
-
|
|
33360
|
-
|
|
33361
|
-
|
|
33362
|
-
|
|
33363
|
-
|
|
33364
|
-
|
|
33365
|
-
|
|
33366
|
-
|
|
33367
|
-
|
|
33516
|
+
outputSuccess({
|
|
33517
|
+
command: "generate video-status",
|
|
33518
|
+
startedAt,
|
|
33519
|
+
data: {
|
|
33520
|
+
taskId,
|
|
33521
|
+
provider: "runway",
|
|
33522
|
+
status: result.status,
|
|
33523
|
+
videoUrl: result.videoUrl,
|
|
33524
|
+
progress: result.progress,
|
|
33525
|
+
error: result.error,
|
|
33526
|
+
outputPath
|
|
33527
|
+
}
|
|
33368
33528
|
});
|
|
33369
33529
|
return;
|
|
33370
33530
|
}
|
|
@@ -33420,15 +33580,18 @@ function registerVideoStatusCommand(parent) {
|
|
|
33420
33580
|
outputPath = resolve24(process.cwd(), options.output);
|
|
33421
33581
|
await writeFile20(outputPath, buffer);
|
|
33422
33582
|
}
|
|
33423
|
-
|
|
33424
|
-
|
|
33425
|
-
|
|
33426
|
-
|
|
33427
|
-
|
|
33428
|
-
|
|
33429
|
-
|
|
33430
|
-
|
|
33431
|
-
|
|
33583
|
+
outputSuccess({
|
|
33584
|
+
command: "generate video-status",
|
|
33585
|
+
startedAt,
|
|
33586
|
+
data: {
|
|
33587
|
+
taskId,
|
|
33588
|
+
provider: "kling",
|
|
33589
|
+
status: result.status,
|
|
33590
|
+
videoUrl: result.videoUrl,
|
|
33591
|
+
duration: result.duration,
|
|
33592
|
+
error: result.error,
|
|
33593
|
+
outputPath
|
|
33594
|
+
}
|
|
33432
33595
|
});
|
|
33433
33596
|
return;
|
|
33434
33597
|
}
|
|
@@ -33492,22 +33655,26 @@ import { resolve as resolve25 } from "node:path";
|
|
|
33492
33655
|
import { writeFile as writeFile21 } from "node:fs/promises";
|
|
33493
33656
|
function registerVideoExtendCommand(parent) {
|
|
33494
33657
|
parent.command("video-extend", { hidden: true }).description("Extend video duration (Kling by video ID, Veo by operation name)").argument("<id>", "Kling video ID or Veo operation name").option("-p, --provider <provider>", "Provider: kling, veo", "kling").option("-k, --api-key <key>", "API key (KLING_API_KEY or GOOGLE_API_KEY)").option("-o, --output <path>", "Output file path").option("--prompt <text>", "Continuation prompt").option("-d, --duration <sec>", "Duration: 5 or 10 (Kling), 4/6/8 (Veo)", "5").option("-n, --negative <prompt>", "Negative prompt (what to avoid, Kling only)").option("--veo-model <model>", "Veo model: 3.0, 3.1, 3.1-fast", "3.1").option("--no-wait", "Start extension and return task ID without waiting").option("--dry-run", "Preview parameters without executing").action(async (id, options) => {
|
|
33658
|
+
const startedAt = Date.now();
|
|
33495
33659
|
try {
|
|
33496
33660
|
const provider = (options.provider || "kling").toLowerCase();
|
|
33497
33661
|
if (options.output) {
|
|
33498
33662
|
validateOutputPath(options.output);
|
|
33499
33663
|
}
|
|
33500
33664
|
if (options.dryRun) {
|
|
33501
|
-
|
|
33502
|
-
dryRun: true,
|
|
33665
|
+
outputSuccess({
|
|
33503
33666
|
command: "generate video-extend",
|
|
33504
|
-
|
|
33505
|
-
|
|
33506
|
-
|
|
33507
|
-
|
|
33508
|
-
|
|
33509
|
-
|
|
33510
|
-
|
|
33667
|
+
startedAt,
|
|
33668
|
+
dryRun: true,
|
|
33669
|
+
data: {
|
|
33670
|
+
params: {
|
|
33671
|
+
id,
|
|
33672
|
+
provider,
|
|
33673
|
+
prompt: options.prompt,
|
|
33674
|
+
duration: options.duration,
|
|
33675
|
+
negative: options.negative,
|
|
33676
|
+
veoModel: options.veoModel
|
|
33677
|
+
}
|
|
33511
33678
|
}
|
|
33512
33679
|
});
|
|
33513
33680
|
return;
|
|
@@ -33564,13 +33731,16 @@ function registerVideoExtendCommand(parent) {
|
|
|
33564
33731
|
outputPath = resolve25(process.cwd(), options.output);
|
|
33565
33732
|
await writeFile21(outputPath, buffer);
|
|
33566
33733
|
}
|
|
33567
|
-
|
|
33568
|
-
|
|
33569
|
-
|
|
33570
|
-
|
|
33571
|
-
|
|
33572
|
-
|
|
33573
|
-
|
|
33734
|
+
outputSuccess({
|
|
33735
|
+
command: "generate video-extend",
|
|
33736
|
+
startedAt,
|
|
33737
|
+
data: {
|
|
33738
|
+
provider: "kling",
|
|
33739
|
+
taskId: result.id,
|
|
33740
|
+
videoUrl: finalResult.videoUrl,
|
|
33741
|
+
duration: finalResult.duration,
|
|
33742
|
+
outputPath
|
|
33743
|
+
}
|
|
33574
33744
|
});
|
|
33575
33745
|
return;
|
|
33576
33746
|
}
|
|
@@ -33650,13 +33820,16 @@ function registerVideoExtendCommand(parent) {
|
|
|
33650
33820
|
outputPath = resolve25(process.cwd(), options.output);
|
|
33651
33821
|
await writeFile21(outputPath, buffer);
|
|
33652
33822
|
}
|
|
33653
|
-
|
|
33654
|
-
|
|
33655
|
-
|
|
33656
|
-
|
|
33657
|
-
|
|
33658
|
-
|
|
33659
|
-
|
|
33823
|
+
outputSuccess({
|
|
33824
|
+
command: "generate video-extend",
|
|
33825
|
+
startedAt,
|
|
33826
|
+
data: {
|
|
33827
|
+
provider: "veo",
|
|
33828
|
+
taskId: result.id,
|
|
33829
|
+
videoUrl: finalResult.videoUrl,
|
|
33830
|
+
duration: finalResult.duration,
|
|
33831
|
+
outputPath
|
|
33832
|
+
}
|
|
33660
33833
|
});
|
|
33661
33834
|
return;
|
|
33662
33835
|
}
|
|
@@ -33771,6 +33944,7 @@ Examples:
|
|
|
33771
33944
|
$ vibe gen img "landscape photo" -o wide.png -r 16:9
|
|
33772
33945
|
$ vibe gen img "portrait" -o portrait.png -p gemini -m pro
|
|
33773
33946
|
$ vibe gen img "product shot" --dry-run --json`).action(async (prompt3, options) => {
|
|
33947
|
+
const startedAt = Date.now();
|
|
33774
33948
|
try {
|
|
33775
33949
|
if (!prompt3) {
|
|
33776
33950
|
if (hasTTY()) {
|
|
@@ -33829,18 +34003,21 @@ Examples:
|
|
|
33829
34003
|
provider = resolved?.name ?? "gemini";
|
|
33830
34004
|
}
|
|
33831
34005
|
if (options.dryRun) {
|
|
33832
|
-
|
|
33833
|
-
dryRun: true,
|
|
34006
|
+
outputSuccess({
|
|
33834
34007
|
command: "generate image",
|
|
33835
|
-
|
|
33836
|
-
|
|
33837
|
-
|
|
33838
|
-
|
|
33839
|
-
|
|
33840
|
-
|
|
33841
|
-
|
|
33842
|
-
|
|
33843
|
-
|
|
34008
|
+
startedAt,
|
|
34009
|
+
dryRun: true,
|
|
34010
|
+
data: {
|
|
34011
|
+
params: {
|
|
34012
|
+
prompt: prompt3,
|
|
34013
|
+
provider,
|
|
34014
|
+
model: options.model,
|
|
34015
|
+
ratio: options.ratio,
|
|
34016
|
+
size: options.size,
|
|
34017
|
+
quality: options.quality,
|
|
34018
|
+
count: options.count,
|
|
34019
|
+
output: options.output
|
|
34020
|
+
}
|
|
33844
34021
|
}
|
|
33845
34022
|
});
|
|
33846
34023
|
return;
|
|
@@ -33878,14 +34055,17 @@ Examples:
|
|
|
33878
34055
|
await mkdir12(dirname10(outputPath), { recursive: true });
|
|
33879
34056
|
await writeFile22(outputPath, buffer);
|
|
33880
34057
|
}
|
|
33881
|
-
|
|
33882
|
-
|
|
33883
|
-
|
|
33884
|
-
|
|
33885
|
-
|
|
33886
|
-
|
|
33887
|
-
|
|
33888
|
-
|
|
34058
|
+
outputSuccess({
|
|
34059
|
+
command: "generate image",
|
|
34060
|
+
startedAt,
|
|
34061
|
+
data: {
|
|
34062
|
+
provider: "openai",
|
|
34063
|
+
images: result.images.map((img) => ({
|
|
34064
|
+
url: img.url,
|
|
34065
|
+
revisedPrompt: img.revisedPrompt
|
|
34066
|
+
})),
|
|
34067
|
+
outputPath
|
|
34068
|
+
}
|
|
33889
34069
|
});
|
|
33890
34070
|
return;
|
|
33891
34071
|
}
|
|
@@ -33997,13 +34177,18 @@ Examples:
|
|
|
33997
34177
|
await mkdir12(dirname10(outputPath), { recursive: true });
|
|
33998
34178
|
await writeFile22(outputPath, buffer);
|
|
33999
34179
|
}
|
|
34000
|
-
|
|
34001
|
-
|
|
34002
|
-
|
|
34003
|
-
|
|
34004
|
-
|
|
34005
|
-
|
|
34006
|
-
|
|
34180
|
+
outputSuccess({
|
|
34181
|
+
command: "generate image",
|
|
34182
|
+
startedAt,
|
|
34183
|
+
warnings: usedLabel.includes("fallback") ? [`Model "${options.model}" failed; fell back to flash`] : [],
|
|
34184
|
+
data: {
|
|
34185
|
+
provider: "gemini",
|
|
34186
|
+
model: usedLabel,
|
|
34187
|
+
images: result.images.map((img) => ({
|
|
34188
|
+
mimeType: img.mimeType
|
|
34189
|
+
})),
|
|
34190
|
+
outputPath
|
|
34191
|
+
}
|
|
34007
34192
|
});
|
|
34008
34193
|
return;
|
|
34009
34194
|
}
|
|
@@ -34087,11 +34272,14 @@ Examples:
|
|
|
34087
34272
|
await mkdir12(dirname10(outputPath), { recursive: true });
|
|
34088
34273
|
await writeFile22(outputPath, buffer);
|
|
34089
34274
|
}
|
|
34090
|
-
|
|
34091
|
-
|
|
34092
|
-
|
|
34093
|
-
|
|
34094
|
-
|
|
34275
|
+
outputSuccess({
|
|
34276
|
+
command: "generate image",
|
|
34277
|
+
startedAt,
|
|
34278
|
+
data: {
|
|
34279
|
+
provider: "grok",
|
|
34280
|
+
images: result.images.map((img) => ({ url: img.url })),
|
|
34281
|
+
outputPath
|
|
34282
|
+
}
|
|
34095
34283
|
});
|
|
34096
34284
|
return;
|
|
34097
34285
|
}
|
|
@@ -34162,11 +34350,14 @@ Examples:
|
|
|
34162
34350
|
proc.on("close", (code) => {
|
|
34163
34351
|
if (code === 0) {
|
|
34164
34352
|
if (isJsonMode()) {
|
|
34165
|
-
|
|
34166
|
-
|
|
34167
|
-
|
|
34168
|
-
|
|
34169
|
-
|
|
34353
|
+
outputSuccess({
|
|
34354
|
+
command: "generate image",
|
|
34355
|
+
startedAt,
|
|
34356
|
+
data: {
|
|
34357
|
+
provider: "runway",
|
|
34358
|
+
images: [{ format: "file" }],
|
|
34359
|
+
outputPath
|
|
34360
|
+
}
|
|
34170
34361
|
});
|
|
34171
34362
|
} else {
|
|
34172
34363
|
spinner2.succeed(source_default.green("Generated image with Runway"));
|
|
@@ -35892,6 +36083,7 @@ Examples:
|
|
|
35892
36083
|
$ vibe gen vid "epic scene" -i frame.png -o out.mp4 -p runway # Image-to-video
|
|
35893
36084
|
$ vibe gen vid "ocean waves" -o waves.mp4 -p veo --resolution 1080p # Veo
|
|
35894
36085
|
$ vibe gen vid "sunset" -o sun.mp4 -d 10 --dry-run --json`).action(async (prompt3, options) => {
|
|
36086
|
+
const startedAt = Date.now();
|
|
35895
36087
|
try {
|
|
35896
36088
|
if (!prompt3) {
|
|
35897
36089
|
if (hasTTY()) {
|
|
@@ -35983,19 +36175,22 @@ Examples:
|
|
|
35983
36175
|
options.ratio = "16:9";
|
|
35984
36176
|
}
|
|
35985
36177
|
if (options.dryRun) {
|
|
35986
|
-
|
|
35987
|
-
dryRun: true,
|
|
36178
|
+
outputSuccess({
|
|
35988
36179
|
command: "generate video",
|
|
35989
|
-
|
|
35990
|
-
|
|
35991
|
-
|
|
35992
|
-
|
|
35993
|
-
|
|
35994
|
-
|
|
35995
|
-
|
|
35996
|
-
|
|
35997
|
-
|
|
35998
|
-
|
|
36180
|
+
startedAt,
|
|
36181
|
+
dryRun: true,
|
|
36182
|
+
data: {
|
|
36183
|
+
params: {
|
|
36184
|
+
prompt: prompt3,
|
|
36185
|
+
provider,
|
|
36186
|
+
duration: options.duration,
|
|
36187
|
+
ratio: options.ratio,
|
|
36188
|
+
image: options.image,
|
|
36189
|
+
mode: options.mode,
|
|
36190
|
+
negative: options.negative,
|
|
36191
|
+
resolution: options.resolution,
|
|
36192
|
+
veoModel: options.veoModel
|
|
36193
|
+
}
|
|
35999
36194
|
}
|
|
36000
36195
|
});
|
|
36001
36196
|
return;
|
|
@@ -36275,13 +36470,16 @@ Examples:
|
|
|
36275
36470
|
outputPath = resolve29(process.cwd(), options.output);
|
|
36276
36471
|
await writeFile25(outputPath, buffer);
|
|
36277
36472
|
}
|
|
36278
|
-
|
|
36279
|
-
|
|
36280
|
-
|
|
36281
|
-
|
|
36282
|
-
|
|
36283
|
-
|
|
36284
|
-
|
|
36473
|
+
outputSuccess({
|
|
36474
|
+
command: "generate video",
|
|
36475
|
+
startedAt,
|
|
36476
|
+
data: {
|
|
36477
|
+
provider,
|
|
36478
|
+
taskId: result?.id,
|
|
36479
|
+
videoUrl: finalResult.videoUrl,
|
|
36480
|
+
duration: finalResult.duration,
|
|
36481
|
+
outputPath
|
|
36482
|
+
}
|
|
36285
36483
|
});
|
|
36286
36484
|
return;
|
|
36287
36485
|
}
|
|
@@ -37714,6 +37912,7 @@ Examples:
|
|
|
37714
37912
|
$ vibe ed sc video.mp4 --dry-run --json
|
|
37715
37913
|
|
|
37716
37914
|
No API key needed (FFmpeg only). Use --use-gemini for smart detection (requires GOOGLE_API_KEY).`).action(async (videoPath, options) => {
|
|
37915
|
+
const startedAt = Date.now();
|
|
37717
37916
|
try {
|
|
37718
37917
|
if (options.output) {
|
|
37719
37918
|
validateOutputPath(options.output);
|
|
@@ -37730,16 +37929,19 @@ No API key needed (FFmpeg only). Use --use-gemini for smart detection (requires
|
|
|
37730
37929
|
const outputPath = options.output || `${name}-cut${ext}`;
|
|
37731
37930
|
const useGemini = options.useGemini || false;
|
|
37732
37931
|
if (options.dryRun) {
|
|
37733
|
-
|
|
37734
|
-
dryRun: true,
|
|
37932
|
+
outputSuccess({
|
|
37735
37933
|
command: "edit silence-cut",
|
|
37736
|
-
|
|
37737
|
-
|
|
37738
|
-
|
|
37739
|
-
|
|
37740
|
-
|
|
37741
|
-
|
|
37742
|
-
|
|
37934
|
+
startedAt,
|
|
37935
|
+
dryRun: true,
|
|
37936
|
+
data: {
|
|
37937
|
+
params: {
|
|
37938
|
+
videoPath: absVideoPath,
|
|
37939
|
+
noiseThreshold: parseFloat(options.noise),
|
|
37940
|
+
minDuration: parseFloat(options.minDuration),
|
|
37941
|
+
padding: parseFloat(options.padding),
|
|
37942
|
+
useGemini,
|
|
37943
|
+
analyzeOnly: options.analyzeOnly || false
|
|
37944
|
+
}
|
|
37743
37945
|
}
|
|
37744
37946
|
});
|
|
37745
37947
|
return;
|
|
@@ -37764,13 +37966,16 @@ No API key needed (FFmpeg only). Use --use-gemini for smart detection (requires
|
|
|
37764
37966
|
}
|
|
37765
37967
|
spinner2.succeed(source_default.green("Silence detection complete"));
|
|
37766
37968
|
if (isJsonMode()) {
|
|
37767
|
-
|
|
37768
|
-
|
|
37769
|
-
|
|
37770
|
-
|
|
37771
|
-
|
|
37772
|
-
|
|
37773
|
-
|
|
37969
|
+
outputSuccess({
|
|
37970
|
+
command: "edit silence-cut",
|
|
37971
|
+
startedAt,
|
|
37972
|
+
data: {
|
|
37973
|
+
method: result.method,
|
|
37974
|
+
totalDuration: result.totalDuration,
|
|
37975
|
+
silentPeriods: result.silentPeriods,
|
|
37976
|
+
silentDuration: result.silentDuration,
|
|
37977
|
+
outputPath: result.outputPath
|
|
37978
|
+
}
|
|
37774
37979
|
});
|
|
37775
37980
|
return;
|
|
37776
37981
|
}
|
|
@@ -37808,6 +38013,7 @@ Examples:
|
|
|
37808
38013
|
$ vibe ed cap video.mp4 --dry-run --json
|
|
37809
38014
|
|
|
37810
38015
|
Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoPath, options) => {
|
|
38016
|
+
const startedAt = Date.now();
|
|
37811
38017
|
try {
|
|
37812
38018
|
if (options.output) {
|
|
37813
38019
|
validateOutputPath(options.output);
|
|
@@ -37820,16 +38026,19 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
37820
38026
|
exitWithError(generalError("FFmpeg not found. Install with: brew install ffmpeg (macOS) or apt install ffmpeg (Linux). Run `vibe doctor` for details."));
|
|
37821
38027
|
}
|
|
37822
38028
|
if (options.dryRun) {
|
|
37823
|
-
|
|
37824
|
-
dryRun: true,
|
|
38029
|
+
outputSuccess({
|
|
37825
38030
|
command: "edit caption",
|
|
37826
|
-
|
|
37827
|
-
|
|
37828
|
-
|
|
37829
|
-
|
|
37830
|
-
|
|
37831
|
-
|
|
37832
|
-
|
|
38031
|
+
startedAt,
|
|
38032
|
+
dryRun: true,
|
|
38033
|
+
data: {
|
|
38034
|
+
params: {
|
|
38035
|
+
videoPath: absVideoPath,
|
|
38036
|
+
style: options.style,
|
|
38037
|
+
fontSize: options.fontSize ? parseInt(options.fontSize) : void 0,
|
|
38038
|
+
fontColor: options.color,
|
|
38039
|
+
language: options.language,
|
|
38040
|
+
position: options.position
|
|
38041
|
+
}
|
|
37833
38042
|
}
|
|
37834
38043
|
});
|
|
37835
38044
|
return;
|
|
@@ -37858,12 +38067,15 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
37858
38067
|
}
|
|
37859
38068
|
spinner2.succeed(source_default.green("Captions applied"));
|
|
37860
38069
|
if (isJsonMode()) {
|
|
37861
|
-
|
|
37862
|
-
|
|
37863
|
-
|
|
37864
|
-
|
|
37865
|
-
|
|
37866
|
-
|
|
38070
|
+
outputSuccess({
|
|
38071
|
+
command: "edit caption",
|
|
38072
|
+
startedAt,
|
|
38073
|
+
data: {
|
|
38074
|
+
segmentCount: result.segmentCount,
|
|
38075
|
+
style: options.style || "bold",
|
|
38076
|
+
outputPath: result.outputPath,
|
|
38077
|
+
srtPath: result.srtPath
|
|
38078
|
+
}
|
|
37867
38079
|
});
|
|
37868
38080
|
return;
|
|
37869
38081
|
}
|
|
@@ -37882,6 +38094,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
37882
38094
|
}
|
|
37883
38095
|
});
|
|
37884
38096
|
aiCommand.command("noise-reduce").description("Remove background noise from audio/video using FFmpeg (no API key needed)").argument("<input>", "Audio or video file path").option("-o, --output <path>", "Output file path (default: <name>-denoised.<ext>)").option("-s, --strength <level>", "Noise reduction strength: low, medium, high (default: medium)", "medium").option("-n, --noise-floor <dB>", "Custom noise floor in dB (overrides strength preset)").option("--dry-run", "Preview parameters without executing").action(async (inputPath, options) => {
|
|
38097
|
+
const startedAt = Date.now();
|
|
37885
38098
|
try {
|
|
37886
38099
|
if (options.output) {
|
|
37887
38100
|
validateOutputPath(options.output);
|
|
@@ -37894,13 +38107,16 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
37894
38107
|
exitWithError(generalError("FFmpeg not found. Install with: brew install ffmpeg (macOS) or apt install ffmpeg (Linux). Run `vibe doctor` for details."));
|
|
37895
38108
|
}
|
|
37896
38109
|
if (options.dryRun) {
|
|
37897
|
-
|
|
37898
|
-
dryRun: true,
|
|
38110
|
+
outputSuccess({
|
|
37899
38111
|
command: "edit noise-reduce",
|
|
37900
|
-
|
|
37901
|
-
|
|
37902
|
-
|
|
37903
|
-
|
|
38112
|
+
startedAt,
|
|
38113
|
+
dryRun: true,
|
|
38114
|
+
data: {
|
|
38115
|
+
params: {
|
|
38116
|
+
inputPath: absInputPath,
|
|
38117
|
+
strength: options.strength,
|
|
38118
|
+
noiseFloor: options.noiseFloor ? parseFloat(options.noiseFloor) : void 0
|
|
38119
|
+
}
|
|
37904
38120
|
}
|
|
37905
38121
|
});
|
|
37906
38122
|
return;
|
|
@@ -37921,11 +38137,14 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
37921
38137
|
}
|
|
37922
38138
|
spinner2.succeed(source_default.green("Noise reduction complete"));
|
|
37923
38139
|
if (isJsonMode()) {
|
|
37924
|
-
|
|
37925
|
-
|
|
37926
|
-
|
|
37927
|
-
|
|
37928
|
-
|
|
38140
|
+
outputSuccess({
|
|
38141
|
+
command: "edit noise-reduce",
|
|
38142
|
+
startedAt,
|
|
38143
|
+
data: {
|
|
38144
|
+
inputDuration: result.inputDuration,
|
|
38145
|
+
strength: options.strength || "medium",
|
|
38146
|
+
outputPath: result.outputPath
|
|
38147
|
+
}
|
|
37929
38148
|
});
|
|
37930
38149
|
return;
|
|
37931
38150
|
}
|
|
@@ -37941,6 +38160,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
37941
38160
|
}
|
|
37942
38161
|
});
|
|
37943
38162
|
aiCommand.command("fade").description("Apply fade in/out effects to video (FFmpeg only, no API key needed)").argument("<video>", "Video file path").option("-o, --output <path>", "Output file path (default: <name>-faded.<ext>)").option("--fade-in <seconds>", "Fade-in duration in seconds (default: 1)", "1").option("--fade-out <seconds>", "Fade-out duration in seconds (default: 1)", "1").option("--audio-only", "Apply fade to audio only (video stream copied)").option("--video-only", "Apply fade to video only (audio stream copied)").option("--dry-run", "Preview parameters without executing").action(async (videoPath, options) => {
|
|
38163
|
+
const startedAt = Date.now();
|
|
37944
38164
|
try {
|
|
37945
38165
|
if (options.output) {
|
|
37946
38166
|
validateOutputPath(options.output);
|
|
@@ -37953,15 +38173,18 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
37953
38173
|
exitWithError(generalError("FFmpeg not found. Install with: brew install ffmpeg (macOS) or apt install ffmpeg (Linux). Run `vibe doctor` for details."));
|
|
37954
38174
|
}
|
|
37955
38175
|
if (options.dryRun) {
|
|
37956
|
-
|
|
37957
|
-
dryRun: true,
|
|
38176
|
+
outputSuccess({
|
|
37958
38177
|
command: "edit fade",
|
|
37959
|
-
|
|
37960
|
-
|
|
37961
|
-
|
|
37962
|
-
|
|
37963
|
-
|
|
37964
|
-
|
|
38178
|
+
startedAt,
|
|
38179
|
+
dryRun: true,
|
|
38180
|
+
data: {
|
|
38181
|
+
params: {
|
|
38182
|
+
videoPath: absVideoPath,
|
|
38183
|
+
fadeIn: parseFloat(options.fadeIn),
|
|
38184
|
+
fadeOut: parseFloat(options.fadeOut),
|
|
38185
|
+
audioOnly: options.audioOnly || false,
|
|
38186
|
+
videoOnly: options.videoOnly || false
|
|
38187
|
+
}
|
|
37965
38188
|
}
|
|
37966
38189
|
});
|
|
37967
38190
|
return;
|
|
@@ -37984,12 +38207,15 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
37984
38207
|
}
|
|
37985
38208
|
spinner2.succeed(source_default.green("Fade effects applied"));
|
|
37986
38209
|
if (isJsonMode()) {
|
|
37987
|
-
|
|
37988
|
-
|
|
37989
|
-
|
|
37990
|
-
|
|
37991
|
-
|
|
37992
|
-
|
|
38210
|
+
outputSuccess({
|
|
38211
|
+
command: "edit fade",
|
|
38212
|
+
startedAt,
|
|
38213
|
+
data: {
|
|
38214
|
+
totalDuration: result.totalDuration,
|
|
38215
|
+
fadeInApplied: result.fadeInApplied,
|
|
38216
|
+
fadeOutApplied: result.fadeOutApplied,
|
|
38217
|
+
outputPath: result.outputPath
|
|
38218
|
+
}
|
|
37993
38219
|
});
|
|
37994
38220
|
return;
|
|
37995
38221
|
}
|
|
@@ -38006,6 +38232,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
38006
38232
|
}
|
|
38007
38233
|
});
|
|
38008
38234
|
aiCommand.command("translate-srt").description("Translate SRT subtitle file to another language (Claude/OpenAI)").argument("<srt>", "SRT file path").option("-t, --target <language>", "Target language (e.g., ko, es, fr, ja, zh)").option("-o, --output <path>", "Output file path (default: <name>-<target>.srt)").option("-p, --provider <provider>", "Translation provider: claude, openai (default: claude)", "claude").option("--source <language>", "Source language (auto-detected if omitted)").option("-k, --api-key <key>", "API key (or set ANTHROPIC_API_KEY / OPENAI_API_KEY env)").option("--dry-run", "Preview parameters without executing").action(async (srtPath, options) => {
|
|
38235
|
+
const startedAt = Date.now();
|
|
38009
38236
|
try {
|
|
38010
38237
|
if (options.output) {
|
|
38011
38238
|
validateOutputPath(options.output);
|
|
@@ -38018,14 +38245,17 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
38018
38245
|
exitWithError(notFoundError(absSrtPath));
|
|
38019
38246
|
}
|
|
38020
38247
|
if (options.dryRun) {
|
|
38021
|
-
|
|
38022
|
-
dryRun: true,
|
|
38248
|
+
outputSuccess({
|
|
38023
38249
|
command: "edit translate-srt",
|
|
38024
|
-
|
|
38025
|
-
|
|
38026
|
-
|
|
38027
|
-
|
|
38028
|
-
|
|
38250
|
+
startedAt,
|
|
38251
|
+
dryRun: true,
|
|
38252
|
+
data: {
|
|
38253
|
+
params: {
|
|
38254
|
+
srtPath: absSrtPath,
|
|
38255
|
+
targetLanguage: options.target,
|
|
38256
|
+
provider: options.provider || "claude",
|
|
38257
|
+
sourceLanguage: options.source
|
|
38258
|
+
}
|
|
38029
38259
|
}
|
|
38030
38260
|
});
|
|
38031
38261
|
return;
|
|
@@ -38055,12 +38285,15 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
38055
38285
|
}
|
|
38056
38286
|
spinner2.succeed(source_default.green("Translation complete"));
|
|
38057
38287
|
if (isJsonMode()) {
|
|
38058
|
-
|
|
38059
|
-
|
|
38060
|
-
|
|
38061
|
-
|
|
38062
|
-
|
|
38063
|
-
|
|
38288
|
+
outputSuccess({
|
|
38289
|
+
command: "edit translate-srt",
|
|
38290
|
+
startedAt,
|
|
38291
|
+
data: {
|
|
38292
|
+
segmentCount: result.segmentCount,
|
|
38293
|
+
sourceLanguage: result.sourceLanguage,
|
|
38294
|
+
targetLanguage: result.targetLanguage,
|
|
38295
|
+
outputPath: result.outputPath
|
|
38296
|
+
}
|
|
38064
38297
|
});
|
|
38065
38298
|
return;
|
|
38066
38299
|
}
|
|
@@ -38077,6 +38310,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
38077
38310
|
}
|
|
38078
38311
|
});
|
|
38079
38312
|
aiCommand.command("jump-cut").description("Remove filler words (um, uh, like, etc.) from video using Whisper word-level timestamps").argument("<video>", "Video file path").option("-o, --output <path>", "Output file path (default: <name>-jumpcut.<ext>)").option("--fillers <words>", "Comma-separated filler words to detect").option("--padding <seconds>", "Padding around cuts in seconds (default: 0.05)", "0.05").option("-l, --language <lang>", "Language code for transcription (e.g., en, ko)").option("--analyze-only", "Only detect fillers, don't cut").option("-k, --api-key <key>", "OpenAI API key (or set OPENAI_API_KEY env)").option("--dry-run", "Preview parameters without executing").action(async (videoPath, options) => {
|
|
38313
|
+
const startedAt = Date.now();
|
|
38080
38314
|
try {
|
|
38081
38315
|
if (options.output) {
|
|
38082
38316
|
validateOutputPath(options.output);
|
|
@@ -38091,15 +38325,18 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
38091
38325
|
}
|
|
38092
38326
|
if (options.dryRun) {
|
|
38093
38327
|
const fillers2 = options.fillers ? options.fillers.split(",").map((f) => f.trim()) : void 0;
|
|
38094
|
-
|
|
38095
|
-
dryRun: true,
|
|
38328
|
+
outputSuccess({
|
|
38096
38329
|
command: "edit jump-cut",
|
|
38097
|
-
|
|
38098
|
-
|
|
38099
|
-
|
|
38100
|
-
|
|
38101
|
-
|
|
38102
|
-
|
|
38330
|
+
startedAt,
|
|
38331
|
+
dryRun: true,
|
|
38332
|
+
data: {
|
|
38333
|
+
params: {
|
|
38334
|
+
videoPath: absVideoPath,
|
|
38335
|
+
fillers: fillers2,
|
|
38336
|
+
padding: parseFloat(options.padding),
|
|
38337
|
+
language: options.language,
|
|
38338
|
+
analyzeOnly: options.analyzeOnly || false
|
|
38339
|
+
}
|
|
38103
38340
|
}
|
|
38104
38341
|
});
|
|
38105
38342
|
return;
|
|
@@ -38128,13 +38365,16 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
38128
38365
|
}
|
|
38129
38366
|
spinner2.succeed(source_default.green("Filler detection complete"));
|
|
38130
38367
|
if (isJsonMode()) {
|
|
38131
|
-
|
|
38132
|
-
|
|
38133
|
-
|
|
38134
|
-
|
|
38135
|
-
|
|
38136
|
-
|
|
38137
|
-
|
|
38368
|
+
outputSuccess({
|
|
38369
|
+
command: "edit jump-cut",
|
|
38370
|
+
startedAt,
|
|
38371
|
+
data: {
|
|
38372
|
+
totalDuration: result.totalDuration,
|
|
38373
|
+
fillerCount: result.fillerCount,
|
|
38374
|
+
fillerDuration: result.fillerDuration,
|
|
38375
|
+
fillers: result.fillers,
|
|
38376
|
+
outputPath: result.outputPath
|
|
38377
|
+
}
|
|
38138
38378
|
});
|
|
38139
38379
|
return;
|
|
38140
38380
|
}
|
|
@@ -38936,6 +39176,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
|
|
|
38936
39176
|
registerEditCommands(editCommand);
|
|
38937
39177
|
registerFillGapsCommand(editCommand);
|
|
38938
39178
|
editCommand.command("grade").description("Apply AI-generated color grading (Claude + FFmpeg)").argument("<video>", "Video file path").option("-s, --style <prompt>", "Style description (e.g., 'cinematic warm')").option("--preset <name>", "Built-in preset: film-noir, vintage, cinematic-warm, cool-tones, high-contrast, pastel, cyberpunk, horror").option("-o, --output <path>", "Output video file path").option("--analyze-only", "Show filter without applying").option("-k, --api-key <key>", "Anthropic API key (or set ANTHROPIC_API_KEY env)").option("--dry-run", "Preview parameters without executing").action(async (videoPath, options) => {
|
|
39179
|
+
const startedAt = Date.now();
|
|
38939
39180
|
try {
|
|
38940
39181
|
if (options.style) rejectControlChars(options.style);
|
|
38941
39182
|
if (options.output) {
|
|
@@ -38951,13 +39192,16 @@ Run 'vibe schema edit.<command>' for structured parameter info.
|
|
|
38951
39192
|
exitWithError(notFoundError("FFmpeg not found. Install with: brew install ffmpeg"));
|
|
38952
39193
|
}
|
|
38953
39194
|
if (options.dryRun) {
|
|
38954
|
-
|
|
38955
|
-
dryRun: true,
|
|
39195
|
+
outputSuccess({
|
|
38956
39196
|
command: "edit grade",
|
|
38957
|
-
|
|
38958
|
-
|
|
38959
|
-
|
|
38960
|
-
|
|
39197
|
+
startedAt,
|
|
39198
|
+
dryRun: true,
|
|
39199
|
+
data: {
|
|
39200
|
+
params: {
|
|
39201
|
+
videoPath: resolve33(process.cwd(), videoPath),
|
|
39202
|
+
style: options.style || options.preset,
|
|
39203
|
+
analyzeOnly: options.analyzeOnly || false
|
|
39204
|
+
}
|
|
38961
39205
|
}
|
|
38962
39206
|
});
|
|
38963
39207
|
return;
|
|
@@ -38983,12 +39227,15 @@ Run 'vibe schema edit.<command>' for structured parameter info.
|
|
|
38983
39227
|
if (isJsonMode()) {
|
|
38984
39228
|
const absPath2 = resolve33(process.cwd(), videoPath);
|
|
38985
39229
|
const gradeOutputPath = options.output ? resolve33(process.cwd(), options.output) : absPath2.replace(/(\.[^.]+)$/, "-graded$1");
|
|
38986
|
-
|
|
38987
|
-
|
|
38988
|
-
|
|
38989
|
-
|
|
38990
|
-
|
|
38991
|
-
|
|
39230
|
+
outputSuccess({
|
|
39231
|
+
command: "edit grade",
|
|
39232
|
+
startedAt,
|
|
39233
|
+
data: {
|
|
39234
|
+
style: options.preset || options.style,
|
|
39235
|
+
description: gradeResult.description,
|
|
39236
|
+
ffmpegFilter: gradeResult.ffmpegFilter,
|
|
39237
|
+
outputPath: options.analyzeOnly ? void 0 : gradeOutputPath
|
|
39238
|
+
}
|
|
38992
39239
|
});
|
|
38993
39240
|
return;
|
|
38994
39241
|
}
|
|
@@ -39017,6 +39264,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
|
|
|
39017
39264
|
}
|
|
39018
39265
|
});
|
|
39019
39266
|
editCommand.command("text-overlay").description("Apply text overlays to video (FFmpeg drawtext)").argument("<video>", "Video file path").option("-t, --text <texts...>", "Text lines to overlay (repeat for multiple)").option("-s, --style <style>", "Overlay style: lower-third, center-bold, subtitle, minimal", "lower-third").option("--font-size <size>", "Font size in pixels (auto-calculated if omitted)").option("--font-color <color>", "Font color (default: white)", "white").option("--fade <seconds>", "Fade in/out duration in seconds", "0.3").option("--start <seconds>", "Start time in seconds", "0").option("--end <seconds>", "End time in seconds (default: video duration)").option("-o, --output <path>", "Output video file path").option("--dry-run", "Preview parameters without executing").action(async (videoPath, options) => {
|
|
39267
|
+
const startedAt = Date.now();
|
|
39020
39268
|
try {
|
|
39021
39269
|
if (!options.text || options.text.length === 0) {
|
|
39022
39270
|
exitWithError(usageError("At least one --text option is required", 'Example: vibe edit text-overlay video.mp4 -t "NEXUS AI" --style center-bold'));
|
|
@@ -39029,18 +39277,21 @@ Run 'vibe schema edit.<command>' for structured parameter info.
|
|
|
39029
39277
|
exitWithError(generalError("FFmpeg not found", "Install with: brew install ffmpeg (macOS) or apt install ffmpeg (Linux)"));
|
|
39030
39278
|
}
|
|
39031
39279
|
if (options.dryRun) {
|
|
39032
|
-
|
|
39033
|
-
dryRun: true,
|
|
39280
|
+
outputSuccess({
|
|
39034
39281
|
command: "edit text-overlay",
|
|
39035
|
-
|
|
39036
|
-
|
|
39037
|
-
|
|
39038
|
-
|
|
39039
|
-
|
|
39040
|
-
|
|
39041
|
-
|
|
39042
|
-
|
|
39043
|
-
|
|
39282
|
+
startedAt,
|
|
39283
|
+
dryRun: true,
|
|
39284
|
+
data: {
|
|
39285
|
+
params: {
|
|
39286
|
+
videoPath: resolve33(process.cwd(), videoPath),
|
|
39287
|
+
texts: options.text,
|
|
39288
|
+
style: options.style,
|
|
39289
|
+
fontSize: options.fontSize ? parseInt(options.fontSize) : void 0,
|
|
39290
|
+
fontColor: options.fontColor,
|
|
39291
|
+
fade: parseFloat(options.fade),
|
|
39292
|
+
start: parseFloat(options.start),
|
|
39293
|
+
end: options.end ? parseFloat(options.end) : void 0
|
|
39294
|
+
}
|
|
39044
39295
|
}
|
|
39045
39296
|
});
|
|
39046
39297
|
return;
|
|
@@ -39065,11 +39316,14 @@ Run 'vibe schema edit.<command>' for structured parameter info.
|
|
|
39065
39316
|
}
|
|
39066
39317
|
spinner2.succeed(source_default.green("Text overlays applied"));
|
|
39067
39318
|
if (isJsonMode()) {
|
|
39068
|
-
|
|
39069
|
-
|
|
39070
|
-
|
|
39071
|
-
|
|
39072
|
-
|
|
39319
|
+
outputSuccess({
|
|
39320
|
+
command: "edit text-overlay",
|
|
39321
|
+
startedAt,
|
|
39322
|
+
data: {
|
|
39323
|
+
style: options.style,
|
|
39324
|
+
texts: options.text,
|
|
39325
|
+
outputPath: result.outputPath
|
|
39326
|
+
}
|
|
39073
39327
|
});
|
|
39074
39328
|
return;
|
|
39075
39329
|
}
|
|
@@ -39086,6 +39340,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
|
|
|
39086
39340
|
}
|
|
39087
39341
|
});
|
|
39088
39342
|
editCommand.command("speed-ramp").description("Apply content-aware speed ramping (Whisper + Claude + FFmpeg)").argument("<video>", "Video file path").option("-o, --output <path>", "Output video file path").option("-s, --style <style>", "Style: dramatic, smooth, action", "dramatic").option("--min-speed <factor>", "Minimum speed factor", "0.25").option("--max-speed <factor>", "Maximum speed factor", "4.0").option("--analyze-only", "Show keyframes without applying").option("-l, --language <lang>", "Language code for transcription").option("-k, --api-key <key>", "Anthropic API key (or set ANTHROPIC_API_KEY env)").option("--dry-run", "Preview parameters without executing").action(async (videoPath, options) => {
|
|
39343
|
+
const startedAt = Date.now();
|
|
39089
39344
|
try {
|
|
39090
39345
|
if (options.output) {
|
|
39091
39346
|
validateOutputPath(options.output);
|
|
@@ -39094,15 +39349,18 @@ Run 'vibe schema edit.<command>' for structured parameter info.
|
|
|
39094
39349
|
exitWithError(generalError("FFmpeg not found", "Install with: brew install ffmpeg (macOS) or apt install ffmpeg (Linux)"));
|
|
39095
39350
|
}
|
|
39096
39351
|
if (options.dryRun) {
|
|
39097
|
-
|
|
39098
|
-
dryRun: true,
|
|
39352
|
+
outputSuccess({
|
|
39099
39353
|
command: "edit speed-ramp",
|
|
39100
|
-
|
|
39101
|
-
|
|
39102
|
-
|
|
39103
|
-
|
|
39104
|
-
|
|
39105
|
-
|
|
39354
|
+
startedAt,
|
|
39355
|
+
dryRun: true,
|
|
39356
|
+
data: {
|
|
39357
|
+
params: {
|
|
39358
|
+
videoPath: resolve33(process.cwd(), videoPath),
|
|
39359
|
+
style: options.style,
|
|
39360
|
+
minSpeed: parseFloat(options.minSpeed),
|
|
39361
|
+
maxSpeed: parseFloat(options.maxSpeed),
|
|
39362
|
+
analyzeOnly: options.analyzeOnly || false
|
|
39363
|
+
}
|
|
39106
39364
|
}
|
|
39107
39365
|
});
|
|
39108
39366
|
return;
|
|
@@ -39155,11 +39413,14 @@ Run 'vibe schema edit.<command>' for structured parameter info.
|
|
|
39155
39413
|
if (isJsonMode()) {
|
|
39156
39414
|
const avgSpeed2 = speedResult.keyframes.reduce((sum, kf) => sum + kf.speed, 0) / speedResult.keyframes.length;
|
|
39157
39415
|
const speedRampOutputPath = options.output ? resolve33(process.cwd(), options.output) : absPath.replace(/(\.[^.]+)$/, "-ramped$1");
|
|
39158
|
-
|
|
39159
|
-
|
|
39160
|
-
|
|
39161
|
-
|
|
39162
|
-
|
|
39416
|
+
outputSuccess({
|
|
39417
|
+
command: "edit speed-ramp",
|
|
39418
|
+
startedAt,
|
|
39419
|
+
data: {
|
|
39420
|
+
keyframes: speedResult.keyframes,
|
|
39421
|
+
avgSpeed: avgSpeed2,
|
|
39422
|
+
outputPath: options.analyzeOnly ? void 0 : speedRampOutputPath
|
|
39423
|
+
}
|
|
39163
39424
|
});
|
|
39164
39425
|
return;
|
|
39165
39426
|
}
|
|
@@ -39199,6 +39460,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
|
|
|
39199
39460
|
}
|
|
39200
39461
|
});
|
|
39201
39462
|
editCommand.command("reframe").description("Auto-reframe video to different aspect ratio (Claude Vision + FFmpeg)").argument("<video>", "Video file path").option("-a, --aspect <ratio>", "Target aspect ratio: 9:16, 1:1, 4:5", "9:16").option("-f, --focus <mode>", "Focus mode: auto, face, center, action", "auto").option("-o, --output <path>", "Output video file path").option("--analyze-only", "Show crop regions without applying").option("--keyframes <path>", "Export keyframes to JSON file").option("-k, --api-key <key>", "Anthropic API key (or set ANTHROPIC_API_KEY env)").option("--dry-run", "Preview parameters without executing").action(async (videoPath, options) => {
|
|
39463
|
+
const startedAt = Date.now();
|
|
39202
39464
|
try {
|
|
39203
39465
|
if (options.output) {
|
|
39204
39466
|
validateOutputPath(options.output);
|
|
@@ -39207,14 +39469,17 @@ Run 'vibe schema edit.<command>' for structured parameter info.
|
|
|
39207
39469
|
exitWithError(generalError("FFmpeg not found", "Install with: brew install ffmpeg (macOS) or apt install ffmpeg (Linux)"));
|
|
39208
39470
|
}
|
|
39209
39471
|
if (options.dryRun) {
|
|
39210
|
-
|
|
39211
|
-
dryRun: true,
|
|
39472
|
+
outputSuccess({
|
|
39212
39473
|
command: "edit reframe",
|
|
39213
|
-
|
|
39214
|
-
|
|
39215
|
-
|
|
39216
|
-
|
|
39217
|
-
|
|
39474
|
+
startedAt,
|
|
39475
|
+
dryRun: true,
|
|
39476
|
+
data: {
|
|
39477
|
+
params: {
|
|
39478
|
+
videoPath: resolve33(process.cwd(), videoPath),
|
|
39479
|
+
aspect: options.aspect,
|
|
39480
|
+
focus: options.focus,
|
|
39481
|
+
analyzeOnly: options.analyzeOnly || false
|
|
39482
|
+
}
|
|
39218
39483
|
}
|
|
39219
39484
|
});
|
|
39220
39485
|
return;
|
|
@@ -39282,13 +39547,16 @@ Run 'vibe schema edit.<command>' for structured parameter info.
|
|
|
39282
39547
|
spinner2.succeed(source_default.green(`Analyzed ${cropKeyframes.length} keyframes`));
|
|
39283
39548
|
if (isJsonMode()) {
|
|
39284
39549
|
const reframeOutputPath = options.output ? resolve33(process.cwd(), options.output) : absPath.replace(/(\.[^.]+)$/, `-${options.aspect.replace(":", "x")}$1`);
|
|
39285
|
-
|
|
39286
|
-
|
|
39287
|
-
|
|
39288
|
-
|
|
39289
|
-
|
|
39290
|
-
|
|
39291
|
-
|
|
39550
|
+
outputSuccess({
|
|
39551
|
+
command: "edit reframe",
|
|
39552
|
+
startedAt,
|
|
39553
|
+
data: {
|
|
39554
|
+
sourceWidth,
|
|
39555
|
+
sourceHeight,
|
|
39556
|
+
aspect: options.aspect,
|
|
39557
|
+
cropKeyframes,
|
|
39558
|
+
outputPath: options.analyzeOnly ? void 0 : reframeOutputPath
|
|
39559
|
+
}
|
|
39292
39560
|
});
|
|
39293
39561
|
return;
|
|
39294
39562
|
}
|
|
@@ -39339,6 +39607,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
|
|
|
39339
39607
|
}
|
|
39340
39608
|
});
|
|
39341
39609
|
editCommand.command("image").description("Edit image(s) using AI (Gemini/OpenAI/Grok)").argument("<images...>", "Input image file(s) followed by edit prompt").option("-p, --provider <provider>", "Provider: gemini (default), openai, grok", "gemini").option("-k, --api-key <key>", "API key (or set env variable)").option("-o, --output <path>", "Output file path", "edited.png").option("-m, --model <model>", "Model: flash/3.1-flash/latest/pro (Gemini only)", "flash").option("-r, --ratio <ratio>", "Output aspect ratio").option("-s, --size <resolution>", "Resolution: 1K, 2K, 4K (Gemini Pro only)").option("--dry-run", "Preview parameters without executing").action(async (args, options) => {
|
|
39610
|
+
const startedAt = Date.now();
|
|
39342
39611
|
try {
|
|
39343
39612
|
if (args.length < 2) {
|
|
39344
39613
|
exitWithError(usageError("Need at least one image and a prompt"));
|
|
@@ -39354,16 +39623,19 @@ Run 'vibe schema edit.<command>' for structured parameter info.
|
|
|
39354
39623
|
exitWithError(usageError("Grok supports only 1 input image for editing.", "Use -p gemini (up to 14 images) or -p openai (up to 16 images) for multi-image editing."));
|
|
39355
39624
|
}
|
|
39356
39625
|
if (options.dryRun) {
|
|
39357
|
-
|
|
39358
|
-
dryRun: true,
|
|
39626
|
+
outputSuccess({
|
|
39359
39627
|
command: "edit image",
|
|
39360
|
-
|
|
39361
|
-
|
|
39362
|
-
|
|
39363
|
-
|
|
39364
|
-
|
|
39365
|
-
|
|
39366
|
-
|
|
39628
|
+
startedAt,
|
|
39629
|
+
dryRun: true,
|
|
39630
|
+
data: {
|
|
39631
|
+
params: {
|
|
39632
|
+
imagePaths: imagePaths.map((p) => resolve33(process.cwd(), p)),
|
|
39633
|
+
prompt: prompt3,
|
|
39634
|
+
provider,
|
|
39635
|
+
model: options.model,
|
|
39636
|
+
ratio: options.ratio,
|
|
39637
|
+
size: options.size
|
|
39638
|
+
}
|
|
39367
39639
|
}
|
|
39368
39640
|
});
|
|
39369
39641
|
return;
|
|
@@ -39441,11 +39713,14 @@ Run 'vibe schema edit.<command>' for structured parameter info.
|
|
|
39441
39713
|
};
|
|
39442
39714
|
const resultModel = result.model;
|
|
39443
39715
|
if (isJsonMode()) {
|
|
39444
|
-
|
|
39445
|
-
|
|
39446
|
-
|
|
39447
|
-
|
|
39448
|
-
|
|
39716
|
+
outputSuccess({
|
|
39717
|
+
command: "edit image",
|
|
39718
|
+
startedAt,
|
|
39719
|
+
data: {
|
|
39720
|
+
provider,
|
|
39721
|
+
model: resultModel || options.model,
|
|
39722
|
+
outputPath
|
|
39723
|
+
}
|
|
39449
39724
|
});
|
|
39450
39725
|
await saveImage();
|
|
39451
39726
|
return;
|
|
@@ -39461,6 +39736,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
|
|
|
39461
39736
|
}
|
|
39462
39737
|
});
|
|
39463
39738
|
editCommand.command("interpolate").description("Create slow motion with frame interpolation (FFmpeg)").argument("<video>", "Video file path").option("-o, --output <path>", "Output file path").option("-f, --factor <number>", "Slow motion factor: 2, 4, or 8", "2").option("--fps <number>", "Target output FPS").option("--quality <mode>", "Quality: fast or quality", "quality").option("--dry-run", "Preview parameters without executing").action(async (videoPath, options) => {
|
|
39739
|
+
const startedAt = Date.now();
|
|
39464
39740
|
try {
|
|
39465
39741
|
if (options.output) {
|
|
39466
39742
|
validateOutputPath(options.output);
|
|
@@ -39471,14 +39747,17 @@ Run 'vibe schema edit.<command>' for structured parameter info.
|
|
|
39471
39747
|
exitWithError(usageError("Factor must be 2, 4, or 8"));
|
|
39472
39748
|
}
|
|
39473
39749
|
if (options.dryRun) {
|
|
39474
|
-
|
|
39475
|
-
dryRun: true,
|
|
39750
|
+
outputSuccess({
|
|
39476
39751
|
command: "edit interpolate",
|
|
39477
|
-
|
|
39478
|
-
|
|
39479
|
-
|
|
39480
|
-
|
|
39481
|
-
|
|
39752
|
+
startedAt,
|
|
39753
|
+
dryRun: true,
|
|
39754
|
+
data: {
|
|
39755
|
+
params: {
|
|
39756
|
+
videoPath: absPath,
|
|
39757
|
+
factor,
|
|
39758
|
+
fps: options.fps ? parseInt(options.fps) : void 0,
|
|
39759
|
+
quality: options.quality
|
|
39760
|
+
}
|
|
39482
39761
|
}
|
|
39483
39762
|
});
|
|
39484
39763
|
return;
|
|
@@ -39505,12 +39784,15 @@ Run 'vibe schema edit.<command>' for structured parameter info.
|
|
|
39505
39784
|
await execSafe("ffmpeg", ["-i", absPath, "-filter:v", `minterpolate='${mi}:fps=${targetFps}',setpts=${factor}*PTS`, "-an", outputPath, "-y"], { timeout: 6e5 });
|
|
39506
39785
|
spinner2.succeed(source_default.green(`Created ${factor}x slow motion`));
|
|
39507
39786
|
if (isJsonMode()) {
|
|
39508
|
-
|
|
39509
|
-
|
|
39510
|
-
|
|
39511
|
-
|
|
39512
|
-
|
|
39513
|
-
|
|
39787
|
+
outputSuccess({
|
|
39788
|
+
command: "edit interpolate",
|
|
39789
|
+
startedAt,
|
|
39790
|
+
data: {
|
|
39791
|
+
originalFps,
|
|
39792
|
+
targetFps,
|
|
39793
|
+
factor,
|
|
39794
|
+
outputPath
|
|
39795
|
+
}
|
|
39514
39796
|
});
|
|
39515
39797
|
return;
|
|
39516
39798
|
}
|
|
@@ -39536,6 +39818,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
|
|
|
39536
39818
|
}
|
|
39537
39819
|
});
|
|
39538
39820
|
editCommand.command("upscale-video").description("Upscale video resolution using AI or FFmpeg").argument("<video>", "Video file path").option("-o, --output <path>", "Output file path").option("-s, --scale <factor>", "Scale factor: 2 or 4", "2").option("-m, --model <model>", "Model: real-esrgan, topaz", "real-esrgan").option("--ffmpeg", "Use FFmpeg lanczos (free, no API)").option("-k, --api-key <key>", "Replicate API token (or set REPLICATE_API_TOKEN env)").option("--no-wait", "Start processing and return task ID without waiting").option("--dry-run", "Preview parameters without executing").action(async (videoPath, options) => {
|
|
39821
|
+
const startedAt = Date.now();
|
|
39539
39822
|
try {
|
|
39540
39823
|
if (options.output) {
|
|
39541
39824
|
validateOutputPath(options.output);
|
|
@@ -39546,14 +39829,17 @@ Run 'vibe schema edit.<command>' for structured parameter info.
|
|
|
39546
39829
|
exitWithError(usageError("Scale must be 2 or 4"));
|
|
39547
39830
|
}
|
|
39548
39831
|
if (options.dryRun) {
|
|
39549
|
-
|
|
39550
|
-
dryRun: true,
|
|
39832
|
+
outputSuccess({
|
|
39551
39833
|
command: "edit upscale-video",
|
|
39552
|
-
|
|
39553
|
-
|
|
39554
|
-
|
|
39555
|
-
|
|
39556
|
-
|
|
39834
|
+
startedAt,
|
|
39835
|
+
dryRun: true,
|
|
39836
|
+
data: {
|
|
39837
|
+
params: {
|
|
39838
|
+
videoPath: absPath,
|
|
39839
|
+
scale,
|
|
39840
|
+
model: options.model,
|
|
39841
|
+
ffmpeg: options.ffmpeg || false
|
|
39842
|
+
}
|
|
39557
39843
|
}
|
|
39558
39844
|
});
|
|
39559
39845
|
return;
|
|
@@ -39579,10 +39865,13 @@ Run 'vibe schema edit.<command>' for structured parameter info.
|
|
|
39579
39865
|
await execSafe("ffmpeg", ["-i", absPath, "-vf", `scale=${newWidth}:${newHeight}:flags=lanczos`, "-c:a", "copy", outputPath, "-y"]);
|
|
39580
39866
|
spinner3.succeed(source_default.green(`Upscaled to ${newWidth}x${newHeight}`));
|
|
39581
39867
|
if (isJsonMode()) {
|
|
39582
|
-
|
|
39583
|
-
|
|
39584
|
-
|
|
39585
|
-
|
|
39868
|
+
outputSuccess({
|
|
39869
|
+
command: "edit upscale-video",
|
|
39870
|
+
startedAt,
|
|
39871
|
+
data: {
|
|
39872
|
+
dimensions: `${newWidth}x${newHeight}`,
|
|
39873
|
+
outputPath
|
|
39874
|
+
}
|
|
39586
39875
|
});
|
|
39587
39876
|
return;
|
|
39588
39877
|
}
|
|
@@ -40022,21 +40311,25 @@ Score each category 1-10. For fixable issues, provide an FFmpeg filter in autoFi
|
|
|
40022
40311
|
}
|
|
40023
40312
|
function registerReviewCommand(aiCommand) {
|
|
40024
40313
|
aiCommand.command("review").description("Review video quality using Gemini AI and optionally auto-fix issues").argument("<video>", "Video file path").option("-s, --storyboard <path>", "Storyboard JSON file for context").option("--auto-apply", "Automatically apply fixable corrections").option("--verify", "Run verification pass after applying fixes").option("-m, --model <model>", "Gemini model: flash (default), flash-2.5, pro", "flash").option("-o, --output <path>", "Output video file path (for auto-apply)").option("--dry-run", "Preview parameters without executing").action(async (videoPath, options) => {
|
|
40314
|
+
const startedAt = Date.now();
|
|
40025
40315
|
try {
|
|
40026
40316
|
if (options.output) {
|
|
40027
40317
|
validateOutputPath(options.output);
|
|
40028
40318
|
}
|
|
40029
40319
|
if (options.dryRun) {
|
|
40030
|
-
|
|
40031
|
-
dryRun: true,
|
|
40320
|
+
outputSuccess({
|
|
40032
40321
|
command: "ai review",
|
|
40033
|
-
|
|
40034
|
-
|
|
40035
|
-
|
|
40036
|
-
|
|
40037
|
-
|
|
40038
|
-
|
|
40039
|
-
|
|
40322
|
+
startedAt,
|
|
40323
|
+
dryRun: true,
|
|
40324
|
+
data: {
|
|
40325
|
+
params: {
|
|
40326
|
+
videoPath,
|
|
40327
|
+
storyboard: options.storyboard,
|
|
40328
|
+
autoApply: options.autoApply ?? false,
|
|
40329
|
+
verify: options.verify ?? false,
|
|
40330
|
+
model: options.model,
|
|
40331
|
+
output: options.output
|
|
40332
|
+
}
|
|
40040
40333
|
}
|
|
40041
40334
|
});
|
|
40042
40335
|
return;
|
|
@@ -40570,6 +40863,7 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
|
|
|
40570
40863
|
}
|
|
40571
40864
|
function registerHighlightsCommands(aiCommand) {
|
|
40572
40865
|
aiCommand.command("highlights").description("Extract highlights from long-form video/audio content").argument("<media>", "Video or audio file path").option("-o, --output <path>", "Output JSON file with highlights").option("--project <path>", "Create project with highlight clips").option("-d, --duration <seconds>", "Target highlight reel duration", "60").option("-n, --count <number>", "Maximum number of highlights").option("-t, --threshold <value>", "Confidence threshold (0-1)", "0.7").option("--criteria <type>", "Selection criteria: emotional | informative | funny | all", "all").option("-l, --language <lang>", "Language code for transcription (e.g., en, ko)").option("--use-gemini", "Use Gemini Video Understanding for enhanced visual+audio analysis").option("--low-res", "Use low resolution mode for longer videos (Gemini only)").option("--dry-run", "Preview parameters without executing").action(async (mediaPath, options) => {
|
|
40866
|
+
const startedAt = Date.now();
|
|
40573
40867
|
try {
|
|
40574
40868
|
if (options.output) {
|
|
40575
40869
|
validateOutputPath(options.output);
|
|
@@ -40579,20 +40873,23 @@ function registerHighlightsCommands(aiCommand) {
|
|
|
40579
40873
|
exitWithError(notFoundError(absPath));
|
|
40580
40874
|
}
|
|
40581
40875
|
if (options.dryRun) {
|
|
40582
|
-
|
|
40583
|
-
dryRun: true,
|
|
40876
|
+
outputSuccess({
|
|
40584
40877
|
command: "ai highlights",
|
|
40585
|
-
|
|
40586
|
-
|
|
40587
|
-
|
|
40588
|
-
|
|
40589
|
-
|
|
40590
|
-
|
|
40591
|
-
|
|
40592
|
-
|
|
40593
|
-
|
|
40594
|
-
|
|
40595
|
-
|
|
40878
|
+
startedAt,
|
|
40879
|
+
dryRun: true,
|
|
40880
|
+
data: {
|
|
40881
|
+
params: {
|
|
40882
|
+
mediaPath,
|
|
40883
|
+
output: options.output,
|
|
40884
|
+
project: options.project,
|
|
40885
|
+
duration: options.duration,
|
|
40886
|
+
count: options.count,
|
|
40887
|
+
threshold: options.threshold,
|
|
40888
|
+
criteria: options.criteria,
|
|
40889
|
+
language: options.language,
|
|
40890
|
+
useGemini: options.useGemini ?? false,
|
|
40891
|
+
lowRes: options.lowRes ?? false
|
|
40892
|
+
}
|
|
40596
40893
|
}
|
|
40597
40894
|
});
|
|
40598
40895
|
return;
|
|
@@ -40785,13 +41082,13 @@ Analyze both what is SHOWN (visual cues, actions, expressions) and what is SAID
|
|
|
40785
41082
|
});
|
|
40786
41083
|
if (allHighlights.length === 0) {
|
|
40787
41084
|
analyzeSpinner.warn(source_default.yellow("No highlights detected in the content"));
|
|
40788
|
-
|
|
41085
|
+
return;
|
|
40789
41086
|
}
|
|
40790
41087
|
analyzeSpinner.succeed(source_default.green(`Found ${allHighlights.length} potential highlights`));
|
|
40791
41088
|
}
|
|
40792
41089
|
if (allHighlights.length === 0) {
|
|
40793
41090
|
console.log(source_default.yellow("No highlights detected in the content"));
|
|
40794
|
-
|
|
41091
|
+
return;
|
|
40795
41092
|
}
|
|
40796
41093
|
const filterSpinner = ora("\u{1F4CA} Filtering and ranking...").start();
|
|
40797
41094
|
const threshold = parseFloat(options.threshold);
|
|
@@ -40867,6 +41164,7 @@ Analyze both what is SHOWN (visual cues, actions, expressions) and what is SAID
|
|
|
40867
41164
|
}
|
|
40868
41165
|
});
|
|
40869
41166
|
aiCommand.command("auto-shorts").alias("shorts").description("Auto-generate shorts from long-form video").argument("<video>", "Video file path").option("-o, --output <path>", "Output file (single) or directory (multiple)").option("-d, --duration <seconds>", "Target duration in seconds (15-60)", "60").option("-n, --count <number>", "Number of shorts to generate", "1").option("-a, --aspect <ratio>", "Aspect ratio: 9:16, 1:1", "9:16").option("--output-dir <dir>", "Output directory for multiple shorts").option("--add-captions", "Add auto-generated captions").option("--caption-style <style>", "Caption style: minimal, bold, animated", "bold").option("--analyze-only", "Show segments without generating").option("-l, --language <lang>", "Language code for transcription").option("--use-gemini", "Use Gemini Video Understanding for enhanced visual+audio analysis").option("--low-res", "Use low resolution mode for longer videos (Gemini only)").option("--dry-run", "Preview parameters without executing").action(async (videoPath, options) => {
|
|
41167
|
+
const startedAt = Date.now();
|
|
40870
41168
|
try {
|
|
40871
41169
|
if (options.output) {
|
|
40872
41170
|
validateOutputPath(options.output);
|
|
@@ -40876,22 +41174,25 @@ Analyze both what is SHOWN (visual cues, actions, expressions) and what is SAID
|
|
|
40876
41174
|
exitWithError(notFoundError(absPath));
|
|
40877
41175
|
}
|
|
40878
41176
|
if (options.dryRun) {
|
|
40879
|
-
|
|
40880
|
-
dryRun: true,
|
|
41177
|
+
outputSuccess({
|
|
40881
41178
|
command: "ai auto-shorts",
|
|
40882
|
-
|
|
40883
|
-
|
|
40884
|
-
|
|
40885
|
-
|
|
40886
|
-
|
|
40887
|
-
|
|
40888
|
-
|
|
40889
|
-
|
|
40890
|
-
|
|
40891
|
-
|
|
40892
|
-
|
|
40893
|
-
|
|
40894
|
-
|
|
41179
|
+
startedAt,
|
|
41180
|
+
dryRun: true,
|
|
41181
|
+
data: {
|
|
41182
|
+
params: {
|
|
41183
|
+
videoPath,
|
|
41184
|
+
output: options.output,
|
|
41185
|
+
duration: options.duration,
|
|
41186
|
+
count: options.count,
|
|
41187
|
+
aspect: options.aspect,
|
|
41188
|
+
outputDir: options.outputDir,
|
|
41189
|
+
addCaptions: options.addCaptions ?? false,
|
|
41190
|
+
captionStyle: options.captionStyle,
|
|
41191
|
+
analyzeOnly: options.analyzeOnly ?? false,
|
|
41192
|
+
language: options.language,
|
|
41193
|
+
useGemini: options.useGemini ?? false,
|
|
41194
|
+
lowRes: options.lowRes ?? false
|
|
41195
|
+
}
|
|
40895
41196
|
}
|
|
40896
41197
|
});
|
|
40897
41198
|
return;
|
|
@@ -51779,13 +52080,13 @@ function getPresetSettings(preset, aspectRatio) {
|
|
|
51779
52080
|
}
|
|
51780
52081
|
return settings;
|
|
51781
52082
|
}
|
|
51782
|
-
async function runHyperframesExport(projectPath, options, spinner2) {
|
|
52083
|
+
async function runHyperframesExport(projectPath, options, spinner2, startedAt) {
|
|
51783
52084
|
spinner2.text = "Loading project...";
|
|
51784
52085
|
const { readFile: readFile42 } = await import("node:fs/promises");
|
|
51785
52086
|
const { resolve: resolve62, basename: basename22 } = await import("node:path");
|
|
51786
52087
|
const { Project: Project2 } = await Promise.resolve().then(() => (init_engine(), engine_exports));
|
|
51787
52088
|
const { createHyperframesBackend: createHyperframesBackend2 } = await Promise.resolve().then(() => (init_hyperframes(), hyperframes_exports));
|
|
51788
|
-
const { exitWithError: exitWithError2, generalError: generalError2,
|
|
52089
|
+
const { exitWithError: exitWithError2, generalError: generalError2, outputSuccess: outputSuccess2 } = await Promise.resolve().then(() => (init_output(), output_exports));
|
|
51789
52090
|
const chalk2 = (await Promise.resolve().then(() => (init_source(), source_exports))).default;
|
|
51790
52091
|
const filePath = resolve62(process.cwd(), projectPath);
|
|
51791
52092
|
const content = await readFile42(filePath, "utf-8");
|
|
@@ -51811,10 +52112,10 @@ async function runHyperframesExport(projectPath, options, spinner2) {
|
|
|
51811
52112
|
return;
|
|
51812
52113
|
}
|
|
51813
52114
|
spinner2.succeed(chalk2.green(`Exported: ${result.outputPath}`));
|
|
51814
|
-
|
|
51815
|
-
success: true,
|
|
52115
|
+
outputSuccess2({
|
|
51816
52116
|
command: "export",
|
|
51817
|
-
|
|
52117
|
+
startedAt,
|
|
52118
|
+
data: {
|
|
51818
52119
|
outputPath: result.outputPath,
|
|
51819
52120
|
backend: "hyperframes",
|
|
51820
52121
|
durationMs: result.durationMs,
|
|
@@ -51858,6 +52159,7 @@ Cost: Free (no API keys needed). Requires FFmpeg.
|
|
|
51858
52159
|
GIF format: 15fps, no audio, looping. Good for previews and sharing.
|
|
51859
52160
|
Custom flags (--bitrate, --fps, --resolution, --codec) override preset values.
|
|
51860
52161
|
Run 'vibe schema export' for structured parameter info.`).action(async (projectPath, options) => {
|
|
52162
|
+
const startedAt = Date.now();
|
|
51861
52163
|
const spinner2 = ora("Checking FFmpeg...").start();
|
|
51862
52164
|
try {
|
|
51863
52165
|
if (options.output) {
|
|
@@ -51875,27 +52177,30 @@ Run 'vibe schema export' for structured parameter info.`).action(async (projectP
|
|
|
51875
52177
|
exitWithError(usageError(overrideError));
|
|
51876
52178
|
}
|
|
51877
52179
|
if (options.dryRun) {
|
|
51878
|
-
|
|
51879
|
-
dryRun: true,
|
|
52180
|
+
outputSuccess({
|
|
51880
52181
|
command: "export",
|
|
51881
|
-
|
|
51882
|
-
|
|
51883
|
-
|
|
51884
|
-
|
|
51885
|
-
|
|
51886
|
-
|
|
51887
|
-
|
|
51888
|
-
|
|
51889
|
-
|
|
51890
|
-
|
|
51891
|
-
|
|
51892
|
-
|
|
52182
|
+
startedAt,
|
|
52183
|
+
dryRun: true,
|
|
52184
|
+
data: {
|
|
52185
|
+
params: {
|
|
52186
|
+
project: projectPath,
|
|
52187
|
+
output: options.output || null,
|
|
52188
|
+
format: options.format,
|
|
52189
|
+
preset: options.preset,
|
|
52190
|
+
overwrite: options.overwrite,
|
|
52191
|
+
gapFill: options.gapFill,
|
|
52192
|
+
backend: options.backend,
|
|
52193
|
+
bitrate: options.bitrate ?? null,
|
|
52194
|
+
fps: customOverrides.fps ?? null,
|
|
52195
|
+
resolution: options.resolution ?? null,
|
|
52196
|
+
codec: options.codec ?? null
|
|
52197
|
+
}
|
|
51893
52198
|
}
|
|
51894
52199
|
});
|
|
51895
52200
|
return;
|
|
51896
52201
|
}
|
|
51897
52202
|
if (options.backend === "hyperframes") {
|
|
51898
|
-
await runHyperframesExport(projectPath, options, spinner2);
|
|
52203
|
+
await runHyperframesExport(projectPath, options, spinner2, startedAt);
|
|
51899
52204
|
return;
|
|
51900
52205
|
}
|
|
51901
52206
|
const ffmpegPath = await findFFmpeg();
|
|
@@ -52145,20 +52450,24 @@ var init_detect = __esm({
|
|
|
52145
52450
|
init_validate();
|
|
52146
52451
|
detectCommand = new Command("detect").description("Auto-detect scenes, beats, and silences in media");
|
|
52147
52452
|
detectCommand.command("scenes").description("Detect scene changes in video").argument("<video>", "Video file path").option("-t, --threshold <value>", "Scene change threshold (0-1)", "0.3").option("-o, --output <path>", "Output JSON file with timestamps").option("-p, --project <path>", "Add scenes as clips to project").option("--dry-run", "Preview parameters without executing").action(async (videoPath, options) => {
|
|
52453
|
+
const startedAt = Date.now();
|
|
52148
52454
|
const spinner2 = ora("Detecting scenes...").start();
|
|
52149
52455
|
try {
|
|
52150
52456
|
if (options.output) {
|
|
52151
52457
|
validateOutputPath(options.output);
|
|
52152
52458
|
}
|
|
52153
52459
|
if (options.dryRun) {
|
|
52154
|
-
|
|
52155
|
-
dryRun: true,
|
|
52460
|
+
outputSuccess({
|
|
52156
52461
|
command: "detect scenes",
|
|
52157
|
-
|
|
52158
|
-
|
|
52159
|
-
|
|
52160
|
-
|
|
52161
|
-
|
|
52462
|
+
startedAt,
|
|
52463
|
+
dryRun: true,
|
|
52464
|
+
data: {
|
|
52465
|
+
params: {
|
|
52466
|
+
video: videoPath,
|
|
52467
|
+
threshold: options.threshold,
|
|
52468
|
+
output: options.output || null,
|
|
52469
|
+
project: options.project || null
|
|
52470
|
+
}
|
|
52162
52471
|
}
|
|
52163
52472
|
});
|
|
52164
52473
|
return;
|
|
@@ -52260,20 +52569,24 @@ var init_detect = __esm({
|
|
|
52260
52569
|
}
|
|
52261
52570
|
});
|
|
52262
52571
|
detectCommand.command("silence").description("Detect silence in audio/video").argument("<media>", "Media file path").option("-n, --noise <dB>", "Noise threshold in dB", "-30").option("-d, --duration <sec>", "Minimum silence duration", "0.5").option("-o, --output <path>", "Output JSON file with timestamps").option("--dry-run", "Preview parameters without executing").action(async (mediaPath, options) => {
|
|
52572
|
+
const startedAt = Date.now();
|
|
52263
52573
|
const spinner2 = ora("Detecting silence...").start();
|
|
52264
52574
|
try {
|
|
52265
52575
|
if (options.output) {
|
|
52266
52576
|
validateOutputPath(options.output);
|
|
52267
52577
|
}
|
|
52268
52578
|
if (options.dryRun) {
|
|
52269
|
-
|
|
52270
|
-
dryRun: true,
|
|
52579
|
+
outputSuccess({
|
|
52271
52580
|
command: "detect silence",
|
|
52272
|
-
|
|
52273
|
-
|
|
52274
|
-
|
|
52275
|
-
|
|
52276
|
-
|
|
52581
|
+
startedAt,
|
|
52582
|
+
dryRun: true,
|
|
52583
|
+
data: {
|
|
52584
|
+
params: {
|
|
52585
|
+
media: mediaPath,
|
|
52586
|
+
noise: options.noise,
|
|
52587
|
+
duration: options.duration,
|
|
52588
|
+
output: options.output || null
|
|
52589
|
+
}
|
|
52277
52590
|
}
|
|
52278
52591
|
});
|
|
52279
52592
|
return;
|
|
@@ -52342,18 +52655,22 @@ var init_detect = __esm({
|
|
|
52342
52655
|
}
|
|
52343
52656
|
});
|
|
52344
52657
|
detectCommand.command("beats").description("Detect beats in audio (for music sync)").argument("<audio>", "Audio file path").option("-o, --output <path>", "Output JSON file with timestamps").option("--dry-run", "Preview parameters without executing").action(async (audioPath, options) => {
|
|
52658
|
+
const startedAt = Date.now();
|
|
52345
52659
|
const spinner2 = ora("Detecting beats...").start();
|
|
52346
52660
|
try {
|
|
52347
52661
|
if (options.output) {
|
|
52348
52662
|
validateOutputPath(options.output);
|
|
52349
52663
|
}
|
|
52350
52664
|
if (options.dryRun) {
|
|
52351
|
-
|
|
52352
|
-
dryRun: true,
|
|
52665
|
+
outputSuccess({
|
|
52353
52666
|
command: "detect beats",
|
|
52354
|
-
|
|
52355
|
-
|
|
52356
|
-
|
|
52667
|
+
startedAt,
|
|
52668
|
+
dryRun: true,
|
|
52669
|
+
data: {
|
|
52670
|
+
params: {
|
|
52671
|
+
audio: audioPath,
|
|
52672
|
+
output: options.output || null
|
|
52673
|
+
}
|
|
52357
52674
|
}
|
|
52358
52675
|
});
|
|
52359
52676
|
return;
|
|
@@ -61989,7 +62306,7 @@ init_source();
|
|
|
61989
62306
|
// package.json
|
|
61990
62307
|
var package_default = {
|
|
61991
62308
|
name: "@vibeframe/cli",
|
|
61992
|
-
version: "0.
|
|
62309
|
+
version: "0.72.0",
|
|
61993
62310
|
description: "VibeFrame CLI - natural language video editing from the terminal",
|
|
61994
62311
|
private: false,
|
|
61995
62312
|
license: "MIT",
|
|
@@ -62221,20 +62538,24 @@ Examples:
|
|
|
62221
62538
|
Cost: Free (no API keys needed). Projects are saved as .vibe.json files.
|
|
62222
62539
|
Run 'vibe schema project.<command>' for structured parameter info.`);
|
|
62223
62540
|
projectCommand.command("create").description("Create a new project").argument("<name>", "Project name or path (e.g., 'my-project' or 'output/my-project')").option("-o, --output <path>", "Output file path (overrides name-based path)").option("-r, --ratio <ratio>", "Aspect ratio (16:9, 9:16, 1:1, 4:5)", "16:9").option("-f, --fps <fps>", "Frame rate", "30").option("--dry-run", "Preview parameters without executing").action(async (name, options) => {
|
|
62541
|
+
const startedAt = Date.now();
|
|
62224
62542
|
const spinner2 = ora("Creating project...").start();
|
|
62225
62543
|
try {
|
|
62226
62544
|
if (options.output) {
|
|
62227
62545
|
validateOutputPath(options.output);
|
|
62228
62546
|
}
|
|
62229
62547
|
if (options.dryRun) {
|
|
62230
|
-
|
|
62231
|
-
dryRun: true,
|
|
62548
|
+
outputSuccess({
|
|
62232
62549
|
command: "project create",
|
|
62233
|
-
|
|
62234
|
-
|
|
62235
|
-
|
|
62236
|
-
|
|
62237
|
-
|
|
62550
|
+
startedAt,
|
|
62551
|
+
dryRun: true,
|
|
62552
|
+
data: {
|
|
62553
|
+
params: {
|
|
62554
|
+
name,
|
|
62555
|
+
output: options.output || null,
|
|
62556
|
+
aspectRatio: options.ratio,
|
|
62557
|
+
frameRate: options.fps
|
|
62558
|
+
}
|
|
62238
62559
|
}
|
|
62239
62560
|
});
|
|
62240
62561
|
return;
|
|
@@ -62299,17 +62620,21 @@ projectCommand.command("info").description("Show project information").argument(
|
|
|
62299
62620
|
}
|
|
62300
62621
|
});
|
|
62301
62622
|
projectCommand.command("set").description("Update project settings").argument("<file>", "Project file path").option("-n, --name <name>", "Project name").option("-r, --ratio <ratio>", "Aspect ratio (16:9, 9:16, 1:1, 4:5)").option("-f, --fps <fps>", "Frame rate").option("--dry-run", "Preview parameters without executing").action(async (file, options) => {
|
|
62623
|
+
const startedAt = Date.now();
|
|
62302
62624
|
const spinner2 = ora("Updating project...").start();
|
|
62303
62625
|
try {
|
|
62304
62626
|
if (options.dryRun) {
|
|
62305
|
-
|
|
62306
|
-
dryRun: true,
|
|
62627
|
+
outputSuccess({
|
|
62307
62628
|
command: "project set",
|
|
62308
|
-
|
|
62309
|
-
|
|
62310
|
-
|
|
62311
|
-
|
|
62312
|
-
|
|
62629
|
+
startedAt,
|
|
62630
|
+
dryRun: true,
|
|
62631
|
+
data: {
|
|
62632
|
+
params: {
|
|
62633
|
+
file,
|
|
62634
|
+
name: options.name || null,
|
|
62635
|
+
ratio: options.ratio || null,
|
|
62636
|
+
fps: options.fps || null
|
|
62637
|
+
}
|
|
62313
62638
|
}
|
|
62314
62639
|
});
|
|
62315
62640
|
return;
|
|
@@ -62360,18 +62685,22 @@ Typical workflow: create project \u2192 add-source \u2192 add-clip \u2192 trim/s
|
|
|
62360
62685
|
Cost: Free (no API keys needed).
|
|
62361
62686
|
Run 'vibe schema timeline.<command>' for structured parameter info.`);
|
|
62362
62687
|
timelineCommand.command("add-source").description("Add a media source to the project").argument("<project>", "Project file path").argument("<media>", "Media file path").option("-n, --name <name>", "Source name (defaults to filename)").option("-t, --type <type>", "Media type (video, audio, image, lottie)").option("-d, --duration <seconds>", "Duration in seconds (required for images)").option("--dry-run", "Preview parameters without executing").action(async (projectPath, mediaPath, options) => {
|
|
62688
|
+
const startedAt = Date.now();
|
|
62363
62689
|
const spinner2 = ora("Adding source...").start();
|
|
62364
62690
|
try {
|
|
62365
62691
|
if (options.dryRun) {
|
|
62366
|
-
|
|
62367
|
-
dryRun: true,
|
|
62692
|
+
outputSuccess({
|
|
62368
62693
|
command: "timeline add-source",
|
|
62369
|
-
|
|
62370
|
-
|
|
62371
|
-
|
|
62372
|
-
|
|
62373
|
-
|
|
62374
|
-
|
|
62694
|
+
startedAt,
|
|
62695
|
+
dryRun: true,
|
|
62696
|
+
data: {
|
|
62697
|
+
params: {
|
|
62698
|
+
project: projectPath,
|
|
62699
|
+
media: mediaPath,
|
|
62700
|
+
name: options.name || null,
|
|
62701
|
+
type: options.type || null,
|
|
62702
|
+
duration: options.duration || null
|
|
62703
|
+
}
|
|
62375
62704
|
}
|
|
62376
62705
|
});
|
|
62377
62706
|
return;
|
|
@@ -62404,21 +62733,25 @@ timelineCommand.command("add-source").description("Add a media source to the pro
|
|
|
62404
62733
|
}
|
|
62405
62734
|
});
|
|
62406
62735
|
timelineCommand.command("add-clip").description("Add a clip to the timeline").argument("<project>", "Project file path").argument("<source-id>", "Source ID to use").option("-t, --track <id>", "Track ID (defaults to first matching track)").option("-s, --start <seconds>", "Start time in timeline", "0").option("-d, --duration <seconds>", "Clip duration (defaults to source duration)").option("--offset <seconds>", "Source start offset", "0").option("--dry-run", "Preview parameters without executing").action(async (projectPath, sourceId, options) => {
|
|
62736
|
+
const startedAt = Date.now();
|
|
62407
62737
|
const spinner2 = ora("Adding clip...").start();
|
|
62408
62738
|
try {
|
|
62409
62739
|
validateResourceId(sourceId);
|
|
62410
62740
|
if (options.track) validateResourceId(options.track);
|
|
62411
62741
|
if (options.dryRun) {
|
|
62412
|
-
|
|
62413
|
-
dryRun: true,
|
|
62742
|
+
outputSuccess({
|
|
62414
62743
|
command: "timeline add-clip",
|
|
62415
|
-
|
|
62416
|
-
|
|
62417
|
-
|
|
62418
|
-
|
|
62419
|
-
|
|
62420
|
-
|
|
62421
|
-
|
|
62744
|
+
startedAt,
|
|
62745
|
+
dryRun: true,
|
|
62746
|
+
data: {
|
|
62747
|
+
params: {
|
|
62748
|
+
project: projectPath,
|
|
62749
|
+
sourceId,
|
|
62750
|
+
track: options.track || null,
|
|
62751
|
+
start: options.start,
|
|
62752
|
+
duration: options.duration || null,
|
|
62753
|
+
offset: options.offset
|
|
62754
|
+
}
|
|
62422
62755
|
}
|
|
62423
62756
|
});
|
|
62424
62757
|
return;
|
|
@@ -62467,16 +62800,20 @@ timelineCommand.command("add-clip").description("Add a clip to the timeline").ar
|
|
|
62467
62800
|
}
|
|
62468
62801
|
});
|
|
62469
62802
|
timelineCommand.command("add-track").description("Add a new track").argument("<project>", "Project file path").argument("<type>", "Track type (video, audio)").option("-n, --name <name>", "Track name").option("--dry-run", "Preview parameters without executing").action(async (projectPath, type, options) => {
|
|
62803
|
+
const startedAt = Date.now();
|
|
62470
62804
|
const spinner2 = ora("Adding track...").start();
|
|
62471
62805
|
try {
|
|
62472
62806
|
if (options.dryRun) {
|
|
62473
|
-
|
|
62474
|
-
dryRun: true,
|
|
62807
|
+
outputSuccess({
|
|
62475
62808
|
command: "timeline add-track",
|
|
62476
|
-
|
|
62477
|
-
|
|
62478
|
-
|
|
62479
|
-
|
|
62809
|
+
startedAt,
|
|
62810
|
+
dryRun: true,
|
|
62811
|
+
data: {
|
|
62812
|
+
params: {
|
|
62813
|
+
project: projectPath,
|
|
62814
|
+
type,
|
|
62815
|
+
name: options.name || null
|
|
62816
|
+
}
|
|
62480
62817
|
}
|
|
62481
62818
|
});
|
|
62482
62819
|
return;
|
|
@@ -62508,20 +62845,24 @@ timelineCommand.command("add-track").description("Add a new track").argument("<p
|
|
|
62508
62845
|
}
|
|
62509
62846
|
});
|
|
62510
62847
|
timelineCommand.command("add-effect").description("Add an effect to a clip").argument("<project>", "Project file path").argument("<clip-id>", "Clip ID").argument("<effect-type>", "Effect type (fadeIn, fadeOut, blur, brightness, contrast, saturation, speed, volume)").option("-s, --start <seconds>", "Effect start time (relative to clip)", "0").option("-d, --duration <seconds>", "Effect duration (defaults to clip duration)").option("-p, --params <json>", "Effect parameters as JSON", "{}").option("--dry-run", "Preview parameters without executing").action(async (projectPath, clipId, effectType, options) => {
|
|
62848
|
+
const startedAt = Date.now();
|
|
62511
62849
|
const spinner2 = ora("Adding effect...").start();
|
|
62512
62850
|
try {
|
|
62513
62851
|
validateResourceId(clipId);
|
|
62514
62852
|
if (options.dryRun) {
|
|
62515
|
-
|
|
62516
|
-
dryRun: true,
|
|
62853
|
+
outputSuccess({
|
|
62517
62854
|
command: "timeline add-effect",
|
|
62518
|
-
|
|
62519
|
-
|
|
62520
|
-
|
|
62521
|
-
|
|
62522
|
-
|
|
62523
|
-
|
|
62524
|
-
|
|
62855
|
+
startedAt,
|
|
62856
|
+
dryRun: true,
|
|
62857
|
+
data: {
|
|
62858
|
+
params: {
|
|
62859
|
+
project: projectPath,
|
|
62860
|
+
clipId,
|
|
62861
|
+
effectType,
|
|
62862
|
+
start: options.start,
|
|
62863
|
+
duration: options.duration || null,
|
|
62864
|
+
params: options.params
|
|
62865
|
+
}
|
|
62525
62866
|
}
|
|
62526
62867
|
});
|
|
62527
62868
|
return;
|
|
@@ -62561,18 +62902,22 @@ timelineCommand.command("add-effect").description("Add an effect to a clip").arg
|
|
|
62561
62902
|
}
|
|
62562
62903
|
});
|
|
62563
62904
|
timelineCommand.command("trim").description("Trim a clip").argument("<project>", "Project file path").argument("<clip-id>", "Clip ID").option("--start <seconds>", "New start time").option("--duration <seconds>", "New duration").option("--dry-run", "Preview parameters without executing").action(async (projectPath, clipId, options) => {
|
|
62905
|
+
const startedAt = Date.now();
|
|
62564
62906
|
const spinner2 = ora("Trimming clip...").start();
|
|
62565
62907
|
try {
|
|
62566
62908
|
validateResourceId(clipId);
|
|
62567
62909
|
if (options.dryRun) {
|
|
62568
|
-
|
|
62569
|
-
dryRun: true,
|
|
62910
|
+
outputSuccess({
|
|
62570
62911
|
command: "timeline trim",
|
|
62571
|
-
|
|
62572
|
-
|
|
62573
|
-
|
|
62574
|
-
|
|
62575
|
-
|
|
62912
|
+
startedAt,
|
|
62913
|
+
dryRun: true,
|
|
62914
|
+
data: {
|
|
62915
|
+
params: {
|
|
62916
|
+
project: projectPath,
|
|
62917
|
+
clipId,
|
|
62918
|
+
start: options.start || null,
|
|
62919
|
+
duration: options.duration || null
|
|
62920
|
+
}
|
|
62576
62921
|
}
|
|
62577
62922
|
});
|
|
62578
62923
|
return;
|
|
@@ -62665,17 +63010,21 @@ timelineCommand.command("list").description("List timeline contents").argument("
|
|
|
62665
63010
|
}
|
|
62666
63011
|
});
|
|
62667
63012
|
timelineCommand.command("split").description("Split a clip at a specific time").argument("<project>", "Project file path").argument("<clip-id>", "Clip ID to split").option("-t, --time <seconds>", "Split time relative to clip start", "0").option("--dry-run", "Preview parameters without executing").action(async (projectPath, clipId, options) => {
|
|
63013
|
+
const startedAt = Date.now();
|
|
62668
63014
|
const spinner2 = ora("Splitting clip...").start();
|
|
62669
63015
|
try {
|
|
62670
63016
|
validateResourceId(clipId);
|
|
62671
63017
|
if (options.dryRun) {
|
|
62672
|
-
|
|
62673
|
-
dryRun: true,
|
|
63018
|
+
outputSuccess({
|
|
62674
63019
|
command: "timeline split",
|
|
62675
|
-
|
|
62676
|
-
|
|
62677
|
-
|
|
62678
|
-
|
|
63020
|
+
startedAt,
|
|
63021
|
+
dryRun: true,
|
|
63022
|
+
data: {
|
|
63023
|
+
params: {
|
|
63024
|
+
project: projectPath,
|
|
63025
|
+
clipId,
|
|
63026
|
+
time: options.time
|
|
63027
|
+
}
|
|
62679
63028
|
}
|
|
62680
63029
|
});
|
|
62681
63030
|
return;
|
|
@@ -62712,17 +63061,21 @@ timelineCommand.command("split").description("Split a clip at a specific time").
|
|
|
62712
63061
|
}
|
|
62713
63062
|
});
|
|
62714
63063
|
timelineCommand.command("duplicate").description("Duplicate a clip").argument("<project>", "Project file path").argument("<clip-id>", "Clip ID to duplicate").option("-t, --time <seconds>", "Start time for duplicate (default: after original)").option("--dry-run", "Preview parameters without executing").action(async (projectPath, clipId, options) => {
|
|
63064
|
+
const startedAt = Date.now();
|
|
62715
63065
|
const spinner2 = ora("Duplicating clip...").start();
|
|
62716
63066
|
try {
|
|
62717
63067
|
validateResourceId(clipId);
|
|
62718
63068
|
if (options.dryRun) {
|
|
62719
|
-
|
|
62720
|
-
dryRun: true,
|
|
63069
|
+
outputSuccess({
|
|
62721
63070
|
command: "timeline duplicate",
|
|
62722
|
-
|
|
62723
|
-
|
|
62724
|
-
|
|
62725
|
-
|
|
63071
|
+
startedAt,
|
|
63072
|
+
dryRun: true,
|
|
63073
|
+
data: {
|
|
63074
|
+
params: {
|
|
63075
|
+
project: projectPath,
|
|
63076
|
+
clipId,
|
|
63077
|
+
time: options.time || null
|
|
63078
|
+
}
|
|
62726
63079
|
}
|
|
62727
63080
|
});
|
|
62728
63081
|
return;
|
|
@@ -62754,16 +63107,20 @@ timelineCommand.command("duplicate").description("Duplicate a clip").argument("<
|
|
|
62754
63107
|
}
|
|
62755
63108
|
});
|
|
62756
63109
|
timelineCommand.command("delete").description("Delete a clip from the timeline").argument("<project>", "Project file path").argument("<clip-id>", "Clip ID to delete").option("--dry-run", "Preview parameters without executing").action(async (projectPath, clipId, options) => {
|
|
63110
|
+
const startedAt = Date.now();
|
|
62757
63111
|
const spinner2 = ora("Deleting clip...").start();
|
|
62758
63112
|
try {
|
|
62759
63113
|
validateResourceId(clipId);
|
|
62760
63114
|
if (options.dryRun) {
|
|
62761
|
-
|
|
62762
|
-
dryRun: true,
|
|
63115
|
+
outputSuccess({
|
|
62763
63116
|
command: "timeline delete",
|
|
62764
|
-
|
|
62765
|
-
|
|
62766
|
-
|
|
63117
|
+
startedAt,
|
|
63118
|
+
dryRun: true,
|
|
63119
|
+
data: {
|
|
63120
|
+
params: {
|
|
63121
|
+
project: projectPath,
|
|
63122
|
+
clipId
|
|
63123
|
+
}
|
|
62767
63124
|
}
|
|
62768
63125
|
});
|
|
62769
63126
|
return;
|
|
@@ -62791,19 +63148,23 @@ timelineCommand.command("delete").description("Delete a clip from the timeline")
|
|
|
62791
63148
|
}
|
|
62792
63149
|
});
|
|
62793
63150
|
timelineCommand.command("move").description("Move a clip to a new position").argument("<project>", "Project file path").argument("<clip-id>", "Clip ID to move").option("-t, --time <seconds>", "New start time").option("--track <track-id>", "Move to different track").option("--dry-run", "Preview parameters without executing").action(async (projectPath, clipId, options) => {
|
|
63151
|
+
const startedAt = Date.now();
|
|
62794
63152
|
const spinner2 = ora("Moving clip...").start();
|
|
62795
63153
|
try {
|
|
62796
63154
|
validateResourceId(clipId);
|
|
62797
63155
|
if (options.track) validateResourceId(options.track);
|
|
62798
63156
|
if (options.dryRun) {
|
|
62799
|
-
|
|
62800
|
-
dryRun: true,
|
|
63157
|
+
outputSuccess({
|
|
62801
63158
|
command: "timeline move",
|
|
62802
|
-
|
|
62803
|
-
|
|
62804
|
-
|
|
62805
|
-
|
|
62806
|
-
|
|
63159
|
+
startedAt,
|
|
63160
|
+
dryRun: true,
|
|
63161
|
+
data: {
|
|
63162
|
+
params: {
|
|
63163
|
+
project: projectPath,
|
|
63164
|
+
clipId,
|
|
63165
|
+
time: options.time || null,
|
|
63166
|
+
track: options.track || null
|
|
63167
|
+
}
|
|
62807
63168
|
}
|
|
62808
63169
|
});
|
|
62809
63170
|
return;
|
|
@@ -62886,6 +63247,7 @@ Run 'vibe schema analyze.<command>' for structured parameter info.
|
|
|
62886
63247
|
`
|
|
62887
63248
|
);
|
|
62888
63249
|
analyzeCommand.command("media").description("Analyze any media: images, videos, or YouTube URLs using Gemini").argument("<source>", "Image/video file path, image URL, or YouTube URL").argument("<prompt>", "Analysis prompt (e.g., 'Describe this image', 'Summarize this video')").option("-k, --api-key <key>", "Google API key (or set GOOGLE_API_KEY env)").option("-m, --model <model>", "Model: flash (default), flash-2.5, pro", "flash").option("--fps <number>", "Frames per second for video (default: 1)").option("--start <seconds>", "Start offset in seconds (video only)").option("--end <seconds>", "End offset in seconds (video only)").option("--low-res", "Use low resolution mode (fewer tokens)").option("-v, --verbose", "Show token usage").option("--fields <fields>", "Comma-separated fields to include in output (e.g., response,model)").action(async (source, prompt3, options) => {
|
|
63250
|
+
const startedAt = Date.now();
|
|
62889
63251
|
try {
|
|
62890
63252
|
rejectControlChars(prompt3);
|
|
62891
63253
|
if (options.apiKey) {
|
|
@@ -62910,15 +63272,17 @@ analyzeCommand.command("media").description("Analyze any media: images, videos,
|
|
|
62910
63272
|
spinner2.succeed(source_default.green("Analysis complete"));
|
|
62911
63273
|
const response = sanitizeLLMResponse(result.response || "");
|
|
62912
63274
|
if (isJsonMode()) {
|
|
62913
|
-
|
|
63275
|
+
const data = { response, sourceType: result.sourceType, model: result.model };
|
|
62914
63276
|
if (result.totalTokens) {
|
|
62915
|
-
|
|
62916
|
-
|
|
62917
|
-
|
|
62918
|
-
|
|
62919
|
-
|
|
62920
|
-
|
|
62921
|
-
|
|
63277
|
+
data.promptTokens = result.promptTokens;
|
|
63278
|
+
data.responseTokens = result.responseTokens;
|
|
63279
|
+
data.totalTokens = result.totalTokens;
|
|
63280
|
+
}
|
|
63281
|
+
outputSuccess({
|
|
63282
|
+
command: "analyze media",
|
|
63283
|
+
startedAt,
|
|
63284
|
+
data
|
|
63285
|
+
});
|
|
62922
63286
|
return;
|
|
62923
63287
|
}
|
|
62924
63288
|
console.log();
|
|
@@ -62941,6 +63305,7 @@ analyzeCommand.command("media").description("Analyze any media: images, videos,
|
|
|
62941
63305
|
}
|
|
62942
63306
|
});
|
|
62943
63307
|
analyzeCommand.command("video").description("Analyze video using Gemini (summarize, Q&A, extract info)").argument("<source>", "Video file path or YouTube URL").argument("<prompt>", "Analysis prompt (e.g., 'Summarize this video')").option("-k, --api-key <key>", "Google API key (or set GOOGLE_API_KEY env)").option("-m, --model <model>", "Model: flash (default), flash-2.5, pro", "flash").option("--fps <number>", "Frames per second (default: 1, higher for action)").option("--start <seconds>", "Start offset in seconds (for clipping)").option("--end <seconds>", "End offset in seconds (for clipping)").option("--low-res", "Use low resolution mode (fewer tokens, longer videos)").option("-v, --verbose", "Show token usage").option("--fields <fields>", "Comma-separated fields to include in output (e.g., response,model)").action(async (source, prompt3, options) => {
|
|
63308
|
+
const startedAt = Date.now();
|
|
62944
63309
|
try {
|
|
62945
63310
|
rejectControlChars(prompt3);
|
|
62946
63311
|
if (options.apiKey) {
|
|
@@ -62965,15 +63330,17 @@ analyzeCommand.command("video").description("Analyze video using Gemini (summari
|
|
|
62965
63330
|
spinner2.succeed(source_default.green("Video analyzed"));
|
|
62966
63331
|
const response = sanitizeLLMResponse(result.response || "");
|
|
62967
63332
|
if (isJsonMode()) {
|
|
62968
|
-
|
|
63333
|
+
const data = { response, model: result.model };
|
|
62969
63334
|
if (result.totalTokens) {
|
|
62970
|
-
|
|
62971
|
-
|
|
62972
|
-
|
|
62973
|
-
|
|
62974
|
-
|
|
62975
|
-
|
|
62976
|
-
|
|
63335
|
+
data.promptTokens = result.promptTokens;
|
|
63336
|
+
data.responseTokens = result.responseTokens;
|
|
63337
|
+
data.totalTokens = result.totalTokens;
|
|
63338
|
+
}
|
|
63339
|
+
outputSuccess({
|
|
63340
|
+
command: "analyze video",
|
|
63341
|
+
startedAt,
|
|
63342
|
+
data
|
|
63343
|
+
});
|
|
62977
63344
|
return;
|
|
62978
63345
|
}
|
|
62979
63346
|
console.log();
|
|
@@ -62996,6 +63363,7 @@ analyzeCommand.command("video").description("Analyze video using Gemini (summari
|
|
|
62996
63363
|
});
|
|
62997
63364
|
registerReviewCommand(analyzeCommand);
|
|
62998
63365
|
analyzeCommand.command("suggest").description("Get AI edit suggestions using Gemini").argument("<project>", "Project file path").argument("<instruction>", "Natural language instruction").option("-k, --api-key <key>", "Google API key (or set GOOGLE_API_KEY env)").option("--apply", "Apply the first suggestion automatically").action(async (projectPath, instruction, options) => {
|
|
63366
|
+
const startedAt = Date.now();
|
|
62999
63367
|
try {
|
|
63000
63368
|
rejectControlChars(instruction);
|
|
63001
63369
|
const apiKey = await requireApiKey("GOOGLE_API_KEY", "Google", options.apiKey);
|
|
@@ -63011,7 +63379,13 @@ analyzeCommand.command("suggest").description("Get AI edit suggestions using Gem
|
|
|
63011
63379
|
const suggestions = await gemini.autoEdit(clips, instruction);
|
|
63012
63380
|
spinner2.succeed(source_default.green(`Found ${suggestions.length} suggestion(s)`));
|
|
63013
63381
|
if (isJsonMode()) {
|
|
63014
|
-
|
|
63382
|
+
outputSuccess({
|
|
63383
|
+
command: "analyze suggest",
|
|
63384
|
+
startedAt,
|
|
63385
|
+
data: {
|
|
63386
|
+
suggestions: suggestions.map((s) => ({ type: s.type, description: s.description, confidence: s.confidence, clipIds: s.clipIds, params: s.params }))
|
|
63387
|
+
}
|
|
63388
|
+
});
|
|
63015
63389
|
return;
|
|
63016
63390
|
}
|
|
63017
63391
|
console.log();
|
|
@@ -63080,6 +63454,7 @@ Run 'vibe schema audio.<command>' for structured parameter info.
|
|
|
63080
63454
|
`
|
|
63081
63455
|
);
|
|
63082
63456
|
audioCommand.command("transcribe").description("Transcribe audio using Whisper").argument("<audio>", "Audio file path").option("-k, --api-key <key>", "OpenAI API key (or set OPENAI_API_KEY env)").option("-l, --language <lang>", "Language code (e.g., en, ko)").option("-o, --output <path>", "Output file path").option("-f, --format <format>", "Output format: json, srt, vtt (auto-detected from extension)").action(async (audioPath, options) => {
|
|
63457
|
+
const startedAt = Date.now();
|
|
63083
63458
|
try {
|
|
63084
63459
|
if (options.output) {
|
|
63085
63460
|
validateOutputPath(options.output);
|
|
@@ -63100,7 +63475,16 @@ audioCommand.command("transcribe").description("Transcribe audio using Whisper")
|
|
|
63100
63475
|
}
|
|
63101
63476
|
spinner2.succeed(source_default.green("Transcription complete"));
|
|
63102
63477
|
if (isJsonMode()) {
|
|
63103
|
-
|
|
63478
|
+
outputSuccess({
|
|
63479
|
+
command: "audio transcribe",
|
|
63480
|
+
startedAt,
|
|
63481
|
+
data: {
|
|
63482
|
+
fullText: result.fullText,
|
|
63483
|
+
segments: result.segments,
|
|
63484
|
+
language: result.detectedLanguage,
|
|
63485
|
+
outputPath: options.output ? resolve37(process.cwd(), options.output) : void 0
|
|
63486
|
+
}
|
|
63487
|
+
});
|
|
63104
63488
|
return;
|
|
63105
63489
|
}
|
|
63106
63490
|
console.log();
|
|
@@ -63130,6 +63514,7 @@ audioCommand.command("transcribe").description("Transcribe audio using Whisper")
|
|
|
63130
63514
|
}
|
|
63131
63515
|
});
|
|
63132
63516
|
audioCommand.command("voices").description("List available ElevenLabs voices").option("-k, --api-key <key>", "ElevenLabs API key (or set ELEVENLABS_API_KEY env)").action(async (options) => {
|
|
63517
|
+
const startedAt = Date.now();
|
|
63133
63518
|
try {
|
|
63134
63519
|
const apiKey = await requireApiKey("ELEVENLABS_API_KEY", "ElevenLabs", options.apiKey);
|
|
63135
63520
|
const spinner2 = ora("Fetching voices...").start();
|
|
@@ -63138,7 +63523,13 @@ audioCommand.command("voices").description("List available ElevenLabs voices").o
|
|
|
63138
63523
|
const voices = await elevenlabs.getVoices();
|
|
63139
63524
|
spinner2.succeed(source_default.green(`Found ${voices.length} voices`));
|
|
63140
63525
|
if (isJsonMode()) {
|
|
63141
|
-
|
|
63526
|
+
outputSuccess({
|
|
63527
|
+
command: "audio voices",
|
|
63528
|
+
startedAt,
|
|
63529
|
+
data: {
|
|
63530
|
+
voices: voices.map((v) => ({ name: v.name, voiceId: v.voice_id, category: v.category, labels: v.labels }))
|
|
63531
|
+
}
|
|
63532
|
+
});
|
|
63142
63533
|
return;
|
|
63143
63534
|
}
|
|
63144
63535
|
console.log();
|
|
@@ -63156,9 +63547,15 @@ audioCommand.command("voices").description("List available ElevenLabs voices").o
|
|
|
63156
63547
|
}
|
|
63157
63548
|
});
|
|
63158
63549
|
audioCommand.command("isolate").description("Isolate vocals from audio using ElevenLabs").argument("<audio>", "Input audio file path").option("-k, --api-key <key>", "ElevenLabs API key (or set ELEVENLABS_API_KEY env)").option("-o, --output <path>", "Output audio file path", "vocals.mp3").option("--dry-run", "Preview parameters without executing").action(async (audioPath, options) => {
|
|
63550
|
+
const startedAt = Date.now();
|
|
63159
63551
|
try {
|
|
63160
63552
|
if (options.dryRun) {
|
|
63161
|
-
|
|
63553
|
+
outputSuccess({
|
|
63554
|
+
command: "audio isolate",
|
|
63555
|
+
startedAt,
|
|
63556
|
+
dryRun: true,
|
|
63557
|
+
data: { params: { audioPath } }
|
|
63558
|
+
});
|
|
63162
63559
|
return;
|
|
63163
63560
|
}
|
|
63164
63561
|
if (options.output) {
|
|
@@ -63180,7 +63577,11 @@ audioCommand.command("isolate").description("Isolate vocals from audio using Ele
|
|
|
63180
63577
|
await writeFile32(outputPath, result.audioBuffer);
|
|
63181
63578
|
spinner2.succeed(source_default.green("Vocals isolated"));
|
|
63182
63579
|
if (isJsonMode()) {
|
|
63183
|
-
|
|
63580
|
+
outputSuccess({
|
|
63581
|
+
command: "audio isolate",
|
|
63582
|
+
startedAt,
|
|
63583
|
+
data: { outputPath }
|
|
63584
|
+
});
|
|
63184
63585
|
return;
|
|
63185
63586
|
}
|
|
63186
63587
|
console.log(source_default.green(`Saved to: ${outputPath}`));
|
|
@@ -63191,9 +63592,15 @@ audioCommand.command("isolate").description("Isolate vocals from audio using Ele
|
|
|
63191
63592
|
}
|
|
63192
63593
|
});
|
|
63193
63594
|
audioCommand.command("voice-clone").description("Clone a voice from audio samples using ElevenLabs").argument("[samples...]", "Audio sample files (1-25 files)").option("-k, --api-key <key>", "ElevenLabs API key (or set ELEVENLABS_API_KEY env)").option("-n, --name <name>", "Voice name (required)").option("-d, --description <desc>", "Voice description").option("--labels <json>", `Labels as JSON (e.g., '{"accent": "american"}')`).option("--remove-noise", "Remove background noise from samples").option("--list", "List all available voices").option("--dry-run", "Preview parameters without executing").action(async (samples, options) => {
|
|
63595
|
+
const startedAt = Date.now();
|
|
63194
63596
|
try {
|
|
63195
63597
|
if (options.dryRun) {
|
|
63196
|
-
|
|
63598
|
+
outputSuccess({
|
|
63599
|
+
command: "audio voice-clone",
|
|
63600
|
+
startedAt,
|
|
63601
|
+
dryRun: true,
|
|
63602
|
+
data: { params: { samples: samples?.length, name: options.name } }
|
|
63603
|
+
});
|
|
63197
63604
|
return;
|
|
63198
63605
|
}
|
|
63199
63606
|
const apiKey = await requireApiKey("ELEVENLABS_API_KEY", "ElevenLabs", options.apiKey);
|
|
@@ -63249,7 +63656,11 @@ audioCommand.command("voice-clone").description("Clone a voice from audio sample
|
|
|
63249
63656
|
}
|
|
63250
63657
|
spinner2.succeed(source_default.green("Voice cloned successfully"));
|
|
63251
63658
|
if (isJsonMode()) {
|
|
63252
|
-
|
|
63659
|
+
outputSuccess({
|
|
63660
|
+
command: "audio voice-clone",
|
|
63661
|
+
startedAt,
|
|
63662
|
+
data: { name: options.name, voiceId: result.voiceId }
|
|
63663
|
+
});
|
|
63253
63664
|
return;
|
|
63254
63665
|
}
|
|
63255
63666
|
console.log();
|
|
@@ -63267,9 +63678,15 @@ audioCommand.command("voice-clone").description("Clone a voice from audio sample
|
|
|
63267
63678
|
}
|
|
63268
63679
|
});
|
|
63269
63680
|
audioCommand.command("dub").description("Dub audio/video to another language (transcribe, translate, TTS)").argument("<media>", "Input media file (video or audio)").option("-l, --language <lang>", "Target language code (e.g., es, ko, ja) (required)").option("--source <lang>", "Source language code (default: auto-detect)").option("-v, --voice <id>", "ElevenLabs voice ID for output").option("--analyze-only", "Only analyze and show timing, don't generate audio").option("-o, --output <path>", "Output file path").option("--dry-run", "Preview parameters without executing").action(async (mediaPath, options) => {
|
|
63681
|
+
const startedAt = Date.now();
|
|
63270
63682
|
try {
|
|
63271
63683
|
if (options.dryRun) {
|
|
63272
|
-
|
|
63684
|
+
outputSuccess({
|
|
63685
|
+
command: "audio dub",
|
|
63686
|
+
startedAt,
|
|
63687
|
+
dryRun: true,
|
|
63688
|
+
data: { params: { mediaPath, targetLanguage: options.language, sourceLanguage: options.source, voice: options.voice } }
|
|
63689
|
+
});
|
|
63273
63690
|
return;
|
|
63274
63691
|
}
|
|
63275
63692
|
if (options.output) {
|
|
@@ -63429,7 +63846,16 @@ ${segmentTexts}`,
|
|
|
63429
63846
|
await writeFile32(finalOutputPath, combinedBuffer);
|
|
63430
63847
|
spinner2.succeed(source_default.green("Dubbing complete"));
|
|
63431
63848
|
if (isJsonMode()) {
|
|
63432
|
-
|
|
63849
|
+
outputSuccess({
|
|
63850
|
+
command: "audio dub",
|
|
63851
|
+
startedAt,
|
|
63852
|
+
data: {
|
|
63853
|
+
sourceLanguage: transcriptResult.detectedLanguage || options.source || "auto",
|
|
63854
|
+
targetLanguage: options.language,
|
|
63855
|
+
segmentCount: translatedSegments.length,
|
|
63856
|
+
outputPath: finalOutputPath
|
|
63857
|
+
}
|
|
63858
|
+
});
|
|
63433
63859
|
return;
|
|
63434
63860
|
}
|
|
63435
63861
|
console.log();
|
|
@@ -63448,13 +63874,19 @@ ${segmentTexts}`,
|
|
|
63448
63874
|
}
|
|
63449
63875
|
});
|
|
63450
63876
|
audioCommand.command("duck").description("Auto-duck background music when voice is present (FFmpeg)").argument("<music>", "Background music file path").option("-v, --voice <path>", "Voice/narration track (required)").option("-o, --output <path>", "Output audio file path").option("-t, --threshold <dB>", "Sidechain threshold in dB", "-30").option("-r, --ratio <ratio>", "Compression ratio", "3").option("-a, --attack <ms>", "Attack time in ms", "20").option("-l, --release <ms>", "Release time in ms", "200").option("--dry-run", "Preview parameters without executing").action(async (musicPath, options) => {
|
|
63877
|
+
const startedAt = Date.now();
|
|
63451
63878
|
try {
|
|
63452
63879
|
if (options.dryRun) {
|
|
63453
63880
|
const threshold = parseFloat(options.threshold);
|
|
63454
63881
|
const ratio2 = parseFloat(options.ratio);
|
|
63455
63882
|
const attack2 = parseFloat(options.attack);
|
|
63456
63883
|
const release2 = parseFloat(options.release);
|
|
63457
|
-
|
|
63884
|
+
outputSuccess({
|
|
63885
|
+
command: "audio duck",
|
|
63886
|
+
startedAt,
|
|
63887
|
+
dryRun: true,
|
|
63888
|
+
data: { params: { musicPath, voicePath: options.voice, threshold, ratio: ratio2, attack: attack2, release: release2 } }
|
|
63889
|
+
});
|
|
63458
63890
|
return;
|
|
63459
63891
|
}
|
|
63460
63892
|
if (options.output) {
|
|
@@ -63479,7 +63911,11 @@ audioCommand.command("duck").description("Auto-duck background music when voice
|
|
|
63479
63911
|
await execSafe("ffmpeg", ["-i", absMusic, "-i", absVoice, "-filter_complex", filterComplex, "-map", "[out]", outputPath, "-y"]);
|
|
63480
63912
|
spinner2.succeed(source_default.green("Audio ducking complete"));
|
|
63481
63913
|
if (isJsonMode()) {
|
|
63482
|
-
|
|
63914
|
+
outputSuccess({
|
|
63915
|
+
command: "audio duck",
|
|
63916
|
+
startedAt,
|
|
63917
|
+
data: { musicPath: absMusic, voicePath: options.voice, threshold: thresholdDb, ratio, outputPath }
|
|
63918
|
+
});
|
|
63483
63919
|
return;
|
|
63484
63920
|
}
|
|
63485
63921
|
console.log();
|
|
@@ -63517,6 +63953,7 @@ import { resolve as resolve38 } from "node:path";
|
|
|
63517
63953
|
import { existsSync as existsSync29 } from "node:fs";
|
|
63518
63954
|
function registerScriptPipelineCommands(aiCommand) {
|
|
63519
63955
|
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("-g, --generator <engine>", "Video generator: grok | kling | runway | veo", "grok").option("-i, --image-provider <provider>", "Image provider: gemini | openai | grok", "gemini").option("-v, --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) => {
|
|
63956
|
+
const startedAt = Date.now();
|
|
63520
63957
|
try {
|
|
63521
63958
|
const outputDir = resolve38(process.cwd(), projectDir);
|
|
63522
63959
|
const projectPath = resolve38(outputDir, "project.vibe.json");
|
|
@@ -63535,20 +63972,23 @@ function registerScriptPipelineCommands(aiCommand) {
|
|
|
63535
63972
|
exitWithError(usageError("Scene number must be a positive integer (1-based)", "e.g., --scene 3 or --scene 3,4,5"));
|
|
63536
63973
|
}
|
|
63537
63974
|
if (options.dryRun) {
|
|
63538
|
-
|
|
63539
|
-
dryRun: true,
|
|
63975
|
+
outputSuccess({
|
|
63540
63976
|
command: "pipeline regenerate-scene",
|
|
63541
|
-
|
|
63542
|
-
|
|
63543
|
-
|
|
63544
|
-
|
|
63545
|
-
|
|
63546
|
-
|
|
63547
|
-
|
|
63548
|
-
|
|
63549
|
-
|
|
63550
|
-
|
|
63551
|
-
|
|
63977
|
+
startedAt,
|
|
63978
|
+
dryRun: true,
|
|
63979
|
+
data: {
|
|
63980
|
+
params: {
|
|
63981
|
+
projectDir,
|
|
63982
|
+
scene: sceneNums,
|
|
63983
|
+
videoOnly: options.videoOnly ?? false,
|
|
63984
|
+
narrationOnly: options.narrationOnly ?? false,
|
|
63985
|
+
imageOnly: options.imageOnly ?? false,
|
|
63986
|
+
generator: options.generator,
|
|
63987
|
+
imageProvider: options.imageProvider,
|
|
63988
|
+
aspectRatio: options.aspectRatio,
|
|
63989
|
+
retries: options.retries,
|
|
63990
|
+
referenceScene: options.referenceScene
|
|
63991
|
+
}
|
|
63552
63992
|
}
|
|
63553
63993
|
});
|
|
63554
63994
|
return;
|
|
@@ -63743,6 +64183,7 @@ Styles:
|
|
|
63743
64183
|
Required API Key: OPENAI_API_KEY (Whisper transcription)
|
|
63744
64184
|
`
|
|
63745
64185
|
).action(async (videoPath, options) => {
|
|
64186
|
+
const startedAt = Date.now();
|
|
63746
64187
|
try {
|
|
63747
64188
|
const absVideoPath = resolve41(process.cwd(), videoPath);
|
|
63748
64189
|
if (!existsSync32(absVideoPath)) {
|
|
@@ -63753,20 +64194,23 @@ Required API Key: OPENAI_API_KEY (Whisper transcription)
|
|
|
63753
64194
|
}
|
|
63754
64195
|
const outputFile = options.output || videoPath.replace(/(\.\w+)$/, "-captioned$1");
|
|
63755
64196
|
if (options.dryRun) {
|
|
63756
|
-
|
|
63757
|
-
dryRun: true,
|
|
64197
|
+
outputSuccess({
|
|
63758
64198
|
command: "pipeline animated-caption",
|
|
63759
|
-
|
|
63760
|
-
|
|
63761
|
-
|
|
63762
|
-
|
|
63763
|
-
|
|
63764
|
-
|
|
63765
|
-
|
|
63766
|
-
|
|
63767
|
-
|
|
63768
|
-
|
|
63769
|
-
|
|
64199
|
+
startedAt,
|
|
64200
|
+
dryRun: true,
|
|
64201
|
+
data: {
|
|
64202
|
+
params: {
|
|
64203
|
+
videoPath: absVideoPath,
|
|
64204
|
+
outputPath: outputFile,
|
|
64205
|
+
style: options.style,
|
|
64206
|
+
highlightColor: options.highlightColor,
|
|
64207
|
+
fontSize: options.fontSize ? parseInt(options.fontSize) : "auto",
|
|
64208
|
+
position: options.position,
|
|
64209
|
+
wordsPerGroup: options.wordsPerGroup ? parseInt(options.wordsPerGroup) : "auto",
|
|
64210
|
+
maxChars: options.maxChars ? parseInt(options.maxChars) : "auto",
|
|
64211
|
+
language: options.language || "auto",
|
|
64212
|
+
fast: !!options.fast
|
|
64213
|
+
}
|
|
63770
64214
|
}
|
|
63771
64215
|
});
|
|
63772
64216
|
return;
|
|
@@ -63797,13 +64241,16 @@ Required API Key: OPENAI_API_KEY (Whisper transcription)
|
|
|
63797
64241
|
}
|
|
63798
64242
|
spinner2.succeed(source_default.green("Animated captions applied successfully"));
|
|
63799
64243
|
if (isJsonMode()) {
|
|
63800
|
-
|
|
63801
|
-
|
|
63802
|
-
|
|
63803
|
-
|
|
63804
|
-
|
|
63805
|
-
|
|
63806
|
-
|
|
64244
|
+
outputSuccess({
|
|
64245
|
+
command: "pipeline animated-caption",
|
|
64246
|
+
startedAt,
|
|
64247
|
+
data: {
|
|
64248
|
+
outputPath: result.outputPath,
|
|
64249
|
+
wordCount: result.wordCount,
|
|
64250
|
+
groupCount: result.groupCount,
|
|
64251
|
+
style: result.style,
|
|
64252
|
+
tier: result.tier
|
|
64253
|
+
}
|
|
63807
64254
|
});
|
|
63808
64255
|
return;
|
|
63809
64256
|
}
|
|
@@ -68071,18 +68518,22 @@ function isMediaFile(filePath) {
|
|
|
68071
68518
|
return mediaExts.includes(ext);
|
|
68072
68519
|
}
|
|
68073
68520
|
batchCommand.command("import").description("Import multiple media files from a directory").argument("<project>", "Project file path").argument("<directory>", "Directory containing media files").option("-r, --recursive", "Search subdirectories", false).option("-d, --duration <seconds>", "Default duration for images", "5").option("--filter <pattern>", "Filter files by extension (e.g., '.mp4,.mov')").option("--dry-run", "Preview parameters without executing").action(async (projectPath, directory, options) => {
|
|
68521
|
+
const startedAt = Date.now();
|
|
68074
68522
|
const spinner2 = ora("Scanning directory...").start();
|
|
68075
68523
|
try {
|
|
68076
68524
|
if (options.dryRun) {
|
|
68077
|
-
|
|
68078
|
-
dryRun: true,
|
|
68525
|
+
outputSuccess({
|
|
68079
68526
|
command: "batch import",
|
|
68080
|
-
|
|
68081
|
-
|
|
68082
|
-
|
|
68083
|
-
|
|
68084
|
-
|
|
68085
|
-
|
|
68527
|
+
startedAt,
|
|
68528
|
+
dryRun: true,
|
|
68529
|
+
data: {
|
|
68530
|
+
params: {
|
|
68531
|
+
project: projectPath,
|
|
68532
|
+
directory,
|
|
68533
|
+
recursive: options.recursive,
|
|
68534
|
+
duration: options.duration,
|
|
68535
|
+
filter: options.filter || null
|
|
68536
|
+
}
|
|
68086
68537
|
}
|
|
68087
68538
|
});
|
|
68088
68539
|
return;
|
|
@@ -68144,19 +68595,23 @@ batchCommand.command("import").description("Import multiple media files from a d
|
|
|
68144
68595
|
}
|
|
68145
68596
|
});
|
|
68146
68597
|
batchCommand.command("concat").description("Concatenate multiple sources into sequential clips").argument("<project>", "Project file path").argument("[source-ids...]", "Source IDs to concatenate (or --all)").option("--all", "Concatenate all sources in order", false).option("--track <track-id>", "Track to place clips on").option("--start <seconds>", "Starting time", "0").option("--gap <seconds>", "Gap between clips", "0").option("--dry-run", "Preview parameters without executing").action(async (projectPath, sourceIds, options) => {
|
|
68598
|
+
const startedAt = Date.now();
|
|
68147
68599
|
const spinner2 = ora("Creating clips...").start();
|
|
68148
68600
|
try {
|
|
68149
68601
|
if (options.dryRun) {
|
|
68150
|
-
|
|
68151
|
-
dryRun: true,
|
|
68602
|
+
outputSuccess({
|
|
68152
68603
|
command: "batch concat",
|
|
68153
|
-
|
|
68154
|
-
|
|
68155
|
-
|
|
68156
|
-
|
|
68157
|
-
|
|
68158
|
-
|
|
68159
|
-
|
|
68604
|
+
startedAt,
|
|
68605
|
+
dryRun: true,
|
|
68606
|
+
data: {
|
|
68607
|
+
params: {
|
|
68608
|
+
project: projectPath,
|
|
68609
|
+
sourceIds,
|
|
68610
|
+
all: options.all,
|
|
68611
|
+
track: options.track || null,
|
|
68612
|
+
start: options.start,
|
|
68613
|
+
gap: options.gap
|
|
68614
|
+
}
|
|
68160
68615
|
}
|
|
68161
68616
|
});
|
|
68162
68617
|
return;
|
|
@@ -68218,20 +68673,24 @@ batchCommand.command("concat").description("Concatenate multiple sources into se
|
|
|
68218
68673
|
});
|
|
68219
68674
|
batchCommand.command("apply-effect").description("Apply an effect to multiple clips").argument("<project>", "Project file path").argument("<effect-type>", "Effect type (fadeIn, fadeOut, blur, etc.)").argument("[clip-ids...]", "Clip IDs to apply effect to (or --all)").option("--all", "Apply to all clips", false).option("-d, --duration <seconds>", "Effect duration", "1").option("-s, --start <seconds>", "Effect start time (relative to clip)", "0").option("--intensity <value>", "Effect intensity (0-1)", "1").option("--dry-run", "Preview parameters without executing").action(
|
|
68220
68675
|
async (projectPath, effectType, clipIds, options) => {
|
|
68676
|
+
const startedAt = Date.now();
|
|
68221
68677
|
const spinner2 = ora("Applying effects...").start();
|
|
68222
68678
|
try {
|
|
68223
68679
|
if (options.dryRun) {
|
|
68224
|
-
|
|
68225
|
-
dryRun: true,
|
|
68680
|
+
outputSuccess({
|
|
68226
68681
|
command: "batch apply-effect",
|
|
68227
|
-
|
|
68228
|
-
|
|
68229
|
-
|
|
68230
|
-
|
|
68231
|
-
|
|
68232
|
-
|
|
68233
|
-
|
|
68234
|
-
|
|
68682
|
+
startedAt,
|
|
68683
|
+
dryRun: true,
|
|
68684
|
+
data: {
|
|
68685
|
+
params: {
|
|
68686
|
+
project: projectPath,
|
|
68687
|
+
effectType,
|
|
68688
|
+
clipIds,
|
|
68689
|
+
all: options.all,
|
|
68690
|
+
duration: options.duration,
|
|
68691
|
+
start: options.start,
|
|
68692
|
+
intensity: options.intensity
|
|
68693
|
+
}
|
|
68235
68694
|
}
|
|
68236
68695
|
});
|
|
68237
68696
|
return;
|
|
@@ -68285,17 +68744,21 @@ batchCommand.command("apply-effect").description("Apply an effect to multiple cl
|
|
|
68285
68744
|
}
|
|
68286
68745
|
);
|
|
68287
68746
|
batchCommand.command("remove-clips").description("Remove multiple clips from the timeline").argument("<project>", "Project file path").argument("[clip-ids...]", "Clip IDs to remove").option("--all", "Remove all clips", false).option("--track <track-id>", "Remove clips from specific track only").option("--dry-run", "Preview parameters without executing").action(async (projectPath, clipIds, options) => {
|
|
68747
|
+
const startedAt = Date.now();
|
|
68288
68748
|
const spinner2 = ora("Removing clips...").start();
|
|
68289
68749
|
try {
|
|
68290
68750
|
if (options.dryRun) {
|
|
68291
|
-
|
|
68292
|
-
dryRun: true,
|
|
68751
|
+
outputSuccess({
|
|
68293
68752
|
command: "batch remove-clips",
|
|
68294
|
-
|
|
68295
|
-
|
|
68296
|
-
|
|
68297
|
-
|
|
68298
|
-
|
|
68753
|
+
startedAt,
|
|
68754
|
+
dryRun: true,
|
|
68755
|
+
data: {
|
|
68756
|
+
params: {
|
|
68757
|
+
project: projectPath,
|
|
68758
|
+
clipIds,
|
|
68759
|
+
all: options.all,
|
|
68760
|
+
track: options.track || null
|
|
68761
|
+
}
|
|
68299
68762
|
}
|
|
68300
68763
|
});
|
|
68301
68764
|
return;
|
|
@@ -68819,6 +69282,27 @@ subcommands for video generation, editing, analysis, and pipeline
|
|
|
68819
69282
|
orchestration. Cost tiers below \u2014 **always confirm with the user before
|
|
68820
69283
|
running High / Very High tier commands**.
|
|
68821
69284
|
|
|
69285
|
+
## Agent host coverage
|
|
69286
|
+
|
|
69287
|
+
VibeFrame's CLI works with any bash-capable AI coding agent. Six host
|
|
69288
|
+
families have first-class auto-detection and scaffolds today:
|
|
69289
|
+
|
|
69290
|
+
- **Claude Code** \u2014 \`CLAUDE.md\` (this file's import wrapper) +
|
|
69291
|
+
\`AGENTS.md\` + \`/vibe-*\` slash commands.
|
|
69292
|
+
- **OpenAI Codex** \u2014 reads \`AGENTS.md\` natively (agents.md spec).
|
|
69293
|
+
- **Cursor** \u2014 reads \`AGENTS.md\` plus \`.cursor/rules/\` for project
|
|
69294
|
+
rules; supports MCP via \`.cursor/mcp.json\`.
|
|
69295
|
+
- **Aider** \u2014 reads \`AGENTS.md\` (binary-only detection by VibeFrame).
|
|
69296
|
+
- **Gemini CLI** \u2014 primary context file is \`GEMINI.md\`; also reads
|
|
69297
|
+
\`AGENTS.md\` if present.
|
|
69298
|
+
- **OpenCode** \u2014 reads \`AGENTS.md\` (officially supported per
|
|
69299
|
+
\`opencode.ai/docs/rules\`); supports MCP via \`.opencode/mcp.json\`.
|
|
69300
|
+
|
|
69301
|
+
Run \`vibe doctor\` to see which host(s) the CLI detects in your
|
|
69302
|
+
environment. Anyone running another bash-capable agent (Continue,
|
|
69303
|
+
Sourcegraph Cody, etc.) still gets this \`AGENTS.md\` as the universal
|
|
69304
|
+
fallback.
|
|
69305
|
+
|
|
68822
69306
|
## Self-discovery
|
|
68823
69307
|
|
|
68824
69308
|
Don't memorise the command surface \u2014 read it from the CLI:
|
|
@@ -68828,6 +69312,8 @@ vibe --help # command groups overview
|
|
|
68828
69312
|
vibe schema --list # full machine-readable catalog (80+)
|
|
68829
69313
|
vibe schema generate.video # JSON Schema for any single command
|
|
68830
69314
|
vibe doctor # available providers + system health
|
|
69315
|
+
vibe walkthrough scene # step-by-step authoring guide (scene)
|
|
69316
|
+
vibe walkthrough pipeline # step-by-step authoring guide (pipeline)
|
|
68831
69317
|
\`\`\`
|
|
68832
69318
|
|
|
68833
69319
|
## Two flows \u2014 pick by intent
|
|
@@ -68957,7 +69443,7 @@ var CLAUDE_MD = `@AGENTS.md
|
|
|
68957
69443
|
|
|
68958
69444
|
This file imports \`AGENTS.md\` (above) \u2014 that's the canonical source.
|
|
68959
69445
|
Add anything Claude-Code-specific *below*; everything generic should
|
|
68960
|
-
stay in AGENTS.md so other agent hosts (Codex, Cursor, Aider) see it
|
|
69446
|
+
stay in AGENTS.md so other agent hosts (Codex, Cursor, Aider, Gemini CLI, OpenCode) see it
|
|
68961
69447
|
too.
|
|
68962
69448
|
|
|
68963
69449
|
## Skills
|
|
@@ -68980,6 +69466,20 @@ curl -fsSL https://raw.githubusercontent.com/vericontext/vibeframe/main/scripts/
|
|
|
68980
69466
|
<!-- Add Claude-specific notes here: which sub-agents to prefer, custom
|
|
68981
69467
|
slash commands you've added, conversational style preferences, etc. -->
|
|
68982
69468
|
`;
|
|
69469
|
+
var GEMINI_MD = `@AGENTS.md
|
|
69470
|
+
|
|
69471
|
+
# Gemini CLI overrides
|
|
69472
|
+
|
|
69473
|
+
This project's canonical agent guidance lives in \`AGENTS.md\` \u2014 read
|
|
69474
|
+
it first. The \`@AGENTS.md\` line above is the import marker; if
|
|
69475
|
+
Gemini CLI doesn't inline imported files in your version, open
|
|
69476
|
+
\`AGENTS.md\` directly. Both files are kept in sync by \`vibe init\`.
|
|
69477
|
+
|
|
69478
|
+
## Project-specific guidance
|
|
69479
|
+
|
|
69480
|
+
<!-- Add Gemini-CLI-specific notes here: any conventions specific to
|
|
69481
|
+
how you drive vibe from Gemini CLI (preferred models, tone, etc.). -->
|
|
69482
|
+
`;
|
|
68983
69483
|
function renderEnvExample(opts = {}) {
|
|
68984
69484
|
const fallback2 = opts.withLocalFallbackHeader === false ? "" : `# \u2500\u2500 Local fallbacks (no key needed) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
68985
69485
|
# Kokoro TTS (local, ~330MB on first call) \u2192 works without ELEVENLABS_API_KEY
|
|
@@ -69040,6 +69540,7 @@ defaults:
|
|
|
69040
69540
|
init_output();
|
|
69041
69541
|
var VALID_AGENTS = ["claude-code", "codex", "cursor", "aider", "gemini-cli", "opencode", "all", "auto"];
|
|
69042
69542
|
var initCommand = new Command("init").description("Scaffold project-scope agent files (AGENTS.md / CLAUDE.md / .env.example / .gitignore)").argument("[project-dir]", "Project directory (defaults to cwd)", ".").option("--agent <id>", `Agent target: ${VALID_AGENTS.join(" | ")}`, "auto").option("--force", "Overwrite existing files instead of skipping").option("--dry-run", "Print the file list without writing anything").action(async (projectDirArg, options) => {
|
|
69543
|
+
const startedAt = Date.now();
|
|
69043
69544
|
const agent = options.agent;
|
|
69044
69545
|
if (!VALID_AGENTS.includes(agent)) {
|
|
69045
69546
|
exitWithError(usageError(`Invalid --agent: ${agent}`, `Must be one of: ${VALID_AGENTS.join(", ")}`));
|
|
@@ -69048,6 +69549,7 @@ var initCommand = new Command("init").description("Scaffold project-scope agent
|
|
|
69048
69549
|
const projectName = basename19(projectDir);
|
|
69049
69550
|
const targetHosts = resolveTargets(agent);
|
|
69050
69551
|
const wantsClaude = targetHosts.includes("claude-code");
|
|
69552
|
+
const wantsGemini = targetHosts.includes("gemini-cli");
|
|
69051
69553
|
const wantsCrossTool = targetHosts.some((h) => h !== "claude-code");
|
|
69052
69554
|
const actions = [];
|
|
69053
69555
|
if (!existsSync34(projectDir)) {
|
|
@@ -69073,6 +69575,14 @@ var initCommand = new Command("init").description("Scaffold project-scope agent
|
|
|
69073
69575
|
options.dryRun
|
|
69074
69576
|
));
|
|
69075
69577
|
}
|
|
69578
|
+
if (wantsGemini) {
|
|
69579
|
+
actions.push(await writeIfMissing(
|
|
69580
|
+
resolve48(projectDir, "GEMINI.md"),
|
|
69581
|
+
GEMINI_MD,
|
|
69582
|
+
options.force,
|
|
69583
|
+
options.dryRun
|
|
69584
|
+
));
|
|
69585
|
+
}
|
|
69076
69586
|
actions.push(await writeIfMissing(
|
|
69077
69587
|
resolve48(projectDir, ".env.example"),
|
|
69078
69588
|
renderEnvExample(),
|
|
@@ -69091,13 +69601,16 @@ var initCommand = new Command("init").description("Scaffold project-scope agent
|
|
|
69091
69601
|
options.dryRun
|
|
69092
69602
|
));
|
|
69093
69603
|
if (isJsonMode()) {
|
|
69094
|
-
|
|
69604
|
+
outputSuccess({
|
|
69095
69605
|
command: "init",
|
|
69096
|
-
|
|
69097
|
-
|
|
69098
|
-
|
|
69099
|
-
|
|
69100
|
-
|
|
69606
|
+
startedAt,
|
|
69607
|
+
...options.dryRun ? { dryRun: true } : {},
|
|
69608
|
+
data: {
|
|
69609
|
+
projectDir,
|
|
69610
|
+
agent,
|
|
69611
|
+
targetHosts,
|
|
69612
|
+
actions
|
|
69613
|
+
}
|
|
69101
69614
|
});
|
|
69102
69615
|
return;
|
|
69103
69616
|
}
|
|
@@ -69221,14 +69734,15 @@ Examples:
|
|
|
69221
69734
|
$ vibe doctor --json Machine-readable output
|
|
69222
69735
|
`
|
|
69223
69736
|
).action(async (options) => {
|
|
69737
|
+
const startedAt = Date.now();
|
|
69224
69738
|
if (options.json) process.env.VIBE_JSON_OUTPUT = "1";
|
|
69225
69739
|
const isJson = process.env.VIBE_JSON_OUTPUT === "1";
|
|
69226
69740
|
const results = await runDiagnostics();
|
|
69227
69741
|
if (isJson) {
|
|
69228
|
-
|
|
69229
|
-
success: true,
|
|
69742
|
+
outputSuccess({
|
|
69230
69743
|
command: "doctor",
|
|
69231
|
-
|
|
69744
|
+
startedAt,
|
|
69745
|
+
data: { ...results }
|
|
69232
69746
|
});
|
|
69233
69747
|
return;
|
|
69234
69748
|
}
|
|
@@ -69600,6 +70114,7 @@ async function generateTestVideo(outputPath) {
|
|
|
69600
70114
|
}
|
|
69601
70115
|
}
|
|
69602
70116
|
var demoCommand = new Command("demo").description("Run sample edits on a test video (no API keys needed)").option("--keep", "Keep demo output files after completion").option("--json", "Output results as JSON").action(async (options) => {
|
|
70117
|
+
const startedAt = Date.now();
|
|
69603
70118
|
if (options.json) process.env.VIBE_JSON_OUTPUT = "1";
|
|
69604
70119
|
const isJson = isJsonMode() || !process.stdin.isTTY;
|
|
69605
70120
|
if (!commandExists("ffmpeg")) {
|
|
@@ -69699,11 +70214,16 @@ var demoCommand = new Command("demo").description("Run sample edits on a test vi
|
|
|
69699
70214
|
const passed = results.filter((r) => r.success).length;
|
|
69700
70215
|
const total = results.length;
|
|
69701
70216
|
if (isJson) {
|
|
69702
|
-
|
|
69703
|
-
success: passed === total,
|
|
70217
|
+
outputSuccess({
|
|
69704
70218
|
command: "demo",
|
|
69705
|
-
|
|
69706
|
-
|
|
70219
|
+
startedAt,
|
|
70220
|
+
data: {
|
|
70221
|
+
allPassed: passed === total,
|
|
70222
|
+
passed,
|
|
70223
|
+
total,
|
|
70224
|
+
steps: results,
|
|
70225
|
+
demoDir: DEMO_DIR
|
|
70226
|
+
}
|
|
69707
70227
|
});
|
|
69708
70228
|
} else {
|
|
69709
70229
|
console.log();
|
|
@@ -69816,6 +70336,7 @@ Variable references: $step_id.output, $step_id.data.field, \${ENV_VAR}
|
|
|
69816
70336
|
Cost: Depends on steps. Use --dry-run to preview before executing.
|
|
69817
70337
|
Run 'vibe schema run' for structured parameter info.
|
|
69818
70338
|
`).action(async (pipelinePath, options) => {
|
|
70339
|
+
const startedAt = Date.now();
|
|
69819
70340
|
loadEnv();
|
|
69820
70341
|
const isJson = options.json || process.env.VIBE_JSON_OUTPUT === "1";
|
|
69821
70342
|
const cliBudget = {};
|
|
@@ -69848,13 +70369,16 @@ Run 'vibe schema run' for structured parameter info.
|
|
|
69848
70369
|
budget: hasBudgetFlag ? cliBudget : void 0
|
|
69849
70370
|
});
|
|
69850
70371
|
if (isJson) {
|
|
69851
|
-
|
|
69852
|
-
dryRun: true,
|
|
70372
|
+
outputSuccess({
|
|
69853
70373
|
command: "run",
|
|
69854
|
-
|
|
69855
|
-
|
|
69856
|
-
|
|
69857
|
-
|
|
70374
|
+
startedAt,
|
|
70375
|
+
dryRun: true,
|
|
70376
|
+
data: {
|
|
70377
|
+
name: manifest2.name,
|
|
70378
|
+
steps: result2.steps.map((s) => ({ id: s.id, action: s.action, estimatedCost: s.data?.estimatedCost })),
|
|
70379
|
+
totalSteps: result2.totalSteps,
|
|
70380
|
+
budget: result2.budget
|
|
70381
|
+
}
|
|
69858
70382
|
});
|
|
69859
70383
|
} else {
|
|
69860
70384
|
console.log(source_default.bold(" Execution Plan:"));
|
|
@@ -69886,24 +70410,27 @@ Run 'vibe schema run' for structured parameter info.
|
|
|
69886
70410
|
});
|
|
69887
70411
|
spinner2?.stop();
|
|
69888
70412
|
if (isJson) {
|
|
69889
|
-
|
|
69890
|
-
|
|
69891
|
-
|
|
69892
|
-
|
|
69893
|
-
|
|
69894
|
-
|
|
69895
|
-
|
|
69896
|
-
|
|
69897
|
-
|
|
69898
|
-
|
|
69899
|
-
|
|
69900
|
-
|
|
69901
|
-
|
|
69902
|
-
|
|
69903
|
-
|
|
69904
|
-
|
|
69905
|
-
|
|
69906
|
-
|
|
70413
|
+
outputSuccess({
|
|
70414
|
+
command: "run",
|
|
70415
|
+
startedAt,
|
|
70416
|
+
data: {
|
|
70417
|
+
name: result.name,
|
|
70418
|
+
error: result.error,
|
|
70419
|
+
completedSteps: result.completedSteps,
|
|
70420
|
+
totalSteps: result.totalSteps,
|
|
70421
|
+
totalDuration: result.totalDuration,
|
|
70422
|
+
outputDir: result.outputDir,
|
|
70423
|
+
budget: result.budget,
|
|
70424
|
+
steps: result.steps.map((s) => ({
|
|
70425
|
+
id: s.id,
|
|
70426
|
+
action: s.action,
|
|
70427
|
+
success: s.success,
|
|
70428
|
+
output: s.output,
|
|
70429
|
+
duration: s.duration,
|
|
70430
|
+
error: s.error,
|
|
70431
|
+
data: s.data
|
|
70432
|
+
}))
|
|
70433
|
+
}
|
|
69907
70434
|
});
|
|
69908
70435
|
} else {
|
|
69909
70436
|
console.log();
|
|
@@ -70694,13 +71221,17 @@ init_source();
|
|
|
70694
71221
|
init_walkthroughs();
|
|
70695
71222
|
init_output();
|
|
70696
71223
|
var walkthroughCommand = new Command("walkthrough").description("Step-by-step authoring guide for a vibe workflow (universal /vibe-* slash-command equivalent)").argument("[topic]", `Walkthrough topic: ${WALKTHROUGH_TOPICS.join(" | ")}. Omit to list all.`).option("--list", "List available walkthroughs and exit").action(async (topicArg, options) => {
|
|
71224
|
+
const startedAt = Date.now();
|
|
70697
71225
|
if (!topicArg || options.list) {
|
|
70698
71226
|
const topics = listWalkthroughs();
|
|
70699
71227
|
if (isJsonMode()) {
|
|
70700
|
-
|
|
71228
|
+
outputSuccess({
|
|
70701
71229
|
command: "walkthrough",
|
|
70702
|
-
|
|
70703
|
-
|
|
71230
|
+
startedAt,
|
|
71231
|
+
data: {
|
|
71232
|
+
action: "list",
|
|
71233
|
+
topics
|
|
71234
|
+
}
|
|
70704
71235
|
});
|
|
70705
71236
|
return;
|
|
70706
71237
|
}
|
|
@@ -70725,10 +71256,13 @@ var walkthroughCommand = new Command("walkthrough").description("Step-by-step au
|
|
|
70725
71256
|
const topic = topicArg;
|
|
70726
71257
|
const result = loadWalkthrough(topic);
|
|
70727
71258
|
if (isJsonMode()) {
|
|
70728
|
-
|
|
71259
|
+
outputSuccess({
|
|
70729
71260
|
command: "walkthrough",
|
|
70730
|
-
|
|
70731
|
-
|
|
71261
|
+
startedAt,
|
|
71262
|
+
data: {
|
|
71263
|
+
action: "show",
|
|
71264
|
+
...result
|
|
71265
|
+
}
|
|
70732
71266
|
});
|
|
70733
71267
|
return;
|
|
70734
71268
|
}
|