@vibeframe/cli 0.105.2 → 0.106.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/_shared/edit/silence-cut.d.ts +1 -1
- package/dist/commands/_shared/render-inspect.d.ts +1 -1
- package/dist/commands/_shared/scene-html-emit.d.ts +24 -0
- package/dist/commands/ai-analyze.d.ts +2 -2
- package/dist/commands/ai-motion.d.ts +1 -1
- package/dist/commands/ai-review.d.ts +1 -1
- package/dist/commands/scene.d.ts +4 -1
- package/dist/index.js +267 -73
- package/dist/tools/manifest/edit.d.ts +11 -11
- package/dist/tools/manifest/generate.d.ts +6 -3
- package/dist/tools/manifest/index.js +249 -65
- package/dist/tools/manifest/inspect.d.ts +12 -12
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -7299,6 +7299,39 @@ var init_whisper = __esm({
|
|
|
7299
7299
|
}
|
|
7300
7300
|
});
|
|
7301
7301
|
|
|
7302
|
+
// ../ai-providers/dist/gemini/gemini-models.js
|
|
7303
|
+
function isGeminiTextModelAlias(model) {
|
|
7304
|
+
return Object.prototype.hasOwnProperty.call(GEMINI_TEXT_MODEL_ALIASES, model);
|
|
7305
|
+
}
|
|
7306
|
+
function resolveGeminiTextModel(model) {
|
|
7307
|
+
const trimmed = model?.trim();
|
|
7308
|
+
if (!trimmed)
|
|
7309
|
+
return GEMINI_DEFAULT_TEXT_MODEL;
|
|
7310
|
+
if (isGeminiTextModelAlias(trimmed))
|
|
7311
|
+
return GEMINI_TEXT_MODEL_ALIASES[trimmed];
|
|
7312
|
+
if (trimmed.startsWith("gemini-"))
|
|
7313
|
+
return trimmed;
|
|
7314
|
+
return GEMINI_DEFAULT_TEXT_MODEL;
|
|
7315
|
+
}
|
|
7316
|
+
var GEMINI_DEFAULT_TEXT_MODEL, GEMINI_AGENT_DEFAULT_TEXT_MODEL, GEMINI_TEXT_MODEL_ALIASES, GEMINI_TEXT_MODEL_HELP;
|
|
7317
|
+
var init_gemini_models = __esm({
|
|
7318
|
+
"../ai-providers/dist/gemini/gemini-models.js"() {
|
|
7319
|
+
"use strict";
|
|
7320
|
+
GEMINI_DEFAULT_TEXT_MODEL = "gemini-3.5-flash";
|
|
7321
|
+
GEMINI_AGENT_DEFAULT_TEXT_MODEL = "gemini-2.5-flash";
|
|
7322
|
+
GEMINI_TEXT_MODEL_ALIASES = {
|
|
7323
|
+
flash: GEMINI_DEFAULT_TEXT_MODEL,
|
|
7324
|
+
latest: GEMINI_DEFAULT_TEXT_MODEL,
|
|
7325
|
+
"flash-3.5": GEMINI_DEFAULT_TEXT_MODEL,
|
|
7326
|
+
"flash-3": "gemini-3-flash-preview",
|
|
7327
|
+
"flash-2.5": "gemini-2.5-flash",
|
|
7328
|
+
pro: "gemini-2.5-pro",
|
|
7329
|
+
"pro-3.1": "gemini-3.1-pro-preview"
|
|
7330
|
+
};
|
|
7331
|
+
GEMINI_TEXT_MODEL_HELP = "flash/latest (Gemini 3.5 Flash), flash-3.5, flash-3, flash-2.5, pro, pro-3.1, or a full gemini-* model ID";
|
|
7332
|
+
}
|
|
7333
|
+
});
|
|
7334
|
+
|
|
7302
7335
|
// ../ai-providers/dist/gemini/gemini-motion.js
|
|
7303
7336
|
function buildMotionSystemPrompt(width, height, fps, duration, durationInFrames, stylePreset) {
|
|
7304
7337
|
return `You are a world-class broadcast motion graphics designer (like Apple Keynote, Netflix intros, ESPN graphics). Create STUNNING, jaw-dropping overlays that make viewers go "wow".
|
|
@@ -7452,7 +7485,7 @@ async function generateMotion(api, description, options = {}) {
|
|
|
7452
7485
|
const duration = options.duration || 5;
|
|
7453
7486
|
const durationInFrames = Math.round(duration * fps);
|
|
7454
7487
|
const stylePreset = options.style || "modern and clean";
|
|
7455
|
-
const modelId = options.model
|
|
7488
|
+
const modelId = resolveGeminiTextModel(options.model);
|
|
7456
7489
|
let systemPrompt = buildMotionSystemPrompt(width, height, fps, duration, durationInFrames, stylePreset);
|
|
7457
7490
|
if (options.videoContext) {
|
|
7458
7491
|
const sourceLabel = options.sourceType === "image" ? "IMAGE" : "VIDEO";
|
|
@@ -7533,7 +7566,7 @@ async function refineMotion(api, existingCode, instructions, options = {}) {
|
|
|
7533
7566
|
const fps = options.fps || 30;
|
|
7534
7567
|
const duration = options.duration || 5;
|
|
7535
7568
|
const durationInFrames = Math.round(duration * fps);
|
|
7536
|
-
const modelId = options.model
|
|
7569
|
+
const modelId = resolveGeminiTextModel(options.model);
|
|
7537
7570
|
const systemPrompt = `You are a world-class broadcast motion graphics designer. Modify the provided Remotion component based on instructions.
|
|
7538
7571
|
|
|
7539
7572
|
CANVAS: ${width}\xD7${height}px | ${fps}fps | ${durationInFrames} frames (${duration}s)
|
|
@@ -7628,9 +7661,18 @@ var GEMINI_MOTION_MODELS, STYLE_USER_INSTRUCTIONS;
|
|
|
7628
7661
|
var init_gemini_motion = __esm({
|
|
7629
7662
|
"../ai-providers/dist/gemini/gemini-motion.js"() {
|
|
7630
7663
|
"use strict";
|
|
7664
|
+
init_gemini_models();
|
|
7631
7665
|
GEMINI_MOTION_MODELS = {
|
|
7666
|
+
gemini: GEMINI_DEFAULT_TEXT_MODEL,
|
|
7667
|
+
flash: GEMINI_DEFAULT_TEXT_MODEL,
|
|
7668
|
+
latest: GEMINI_DEFAULT_TEXT_MODEL,
|
|
7669
|
+
"flash-3.5": GEMINI_DEFAULT_TEXT_MODEL,
|
|
7670
|
+
"flash-3": "gemini-3-flash-preview",
|
|
7671
|
+
"flash-2.5": "gemini-2.5-flash",
|
|
7632
7672
|
pro: "gemini-2.5-pro",
|
|
7633
|
-
"
|
|
7673
|
+
"2.5-pro": "gemini-2.5-pro",
|
|
7674
|
+
"3.1-pro": "gemini-3.1-pro-preview",
|
|
7675
|
+
"pro-3.1": "gemini-3.1-pro-preview"
|
|
7634
7676
|
};
|
|
7635
7677
|
STYLE_USER_INSTRUCTIONS = {
|
|
7636
7678
|
minimal: "CENTER-ALIGNED pure typography. Thin lines + large title + subtitle. NO glass panels, NO bokeh, NO dark bars. Breathing pulse during hold.",
|
|
@@ -7753,7 +7795,7 @@ async function analyzeContent(api, content, targetDuration, options) {
|
|
|
7753
7795
|
maxOutputTokens: 4096
|
|
7754
7796
|
}
|
|
7755
7797
|
};
|
|
7756
|
-
const response = await fetch(`${api.baseUrl}/models
|
|
7798
|
+
const response = await fetch(`${api.baseUrl}/models/${GEMINI_DEFAULT_TEXT_MODEL}:generateContent?key=${api.apiKey}`, {
|
|
7757
7799
|
method: "POST",
|
|
7758
7800
|
headers: { "Content-Type": "application/json" },
|
|
7759
7801
|
body: JSON.stringify(payload)
|
|
@@ -7786,6 +7828,7 @@ var init_gemini_storyboard = __esm({
|
|
|
7786
7828
|
"../ai-providers/dist/gemini/gemini-storyboard.js"() {
|
|
7787
7829
|
"use strict";
|
|
7788
7830
|
init_storyboard_prompt();
|
|
7831
|
+
init_gemini_models();
|
|
7789
7832
|
}
|
|
7790
7833
|
});
|
|
7791
7834
|
|
|
@@ -7824,6 +7867,7 @@ var init_GeminiProvider = __esm({
|
|
|
7824
7867
|
"../ai-providers/dist/gemini/GeminiProvider.js"() {
|
|
7825
7868
|
"use strict";
|
|
7826
7869
|
init_gemini_motion();
|
|
7870
|
+
init_gemini_models();
|
|
7827
7871
|
init_gemini_storyboard();
|
|
7828
7872
|
init_http();
|
|
7829
7873
|
MODEL_MAP = {
|
|
@@ -8332,7 +8376,7 @@ var init_GeminiProvider = __esm({
|
|
|
8332
8376
|
error: "Google API key not configured"
|
|
8333
8377
|
};
|
|
8334
8378
|
}
|
|
8335
|
-
const modelId = options.model
|
|
8379
|
+
const modelId = resolveGeminiTextModel(options.model);
|
|
8336
8380
|
try {
|
|
8337
8381
|
let videoPart;
|
|
8338
8382
|
if (typeof videoData === "string") {
|
|
@@ -8439,7 +8483,7 @@ var init_GeminiProvider = __esm({
|
|
|
8439
8483
|
error: "Google API key not configured"
|
|
8440
8484
|
};
|
|
8441
8485
|
}
|
|
8442
|
-
const modelId = options.model
|
|
8486
|
+
const modelId = resolveGeminiTextModel(options.model);
|
|
8443
8487
|
try {
|
|
8444
8488
|
const buffers = Array.isArray(imageData) ? imageData : [imageData];
|
|
8445
8489
|
const imageParts = buffers.map((buf) => ({
|
|
@@ -8541,7 +8585,7 @@ Example response:
|
|
|
8541
8585
|
[{"type":"trim","description":"Trim intro to 3 seconds","clipIds":["clip-1"],"params":{"newDuration":3},"confidence":0.9}]
|
|
8542
8586
|
|
|
8543
8587
|
Respond with ONLY the JSON array, no other text.`;
|
|
8544
|
-
const response = await fetch(`${this.baseUrl}/models
|
|
8588
|
+
const response = await fetch(`${this.baseUrl}/models/${GEMINI_DEFAULT_TEXT_MODEL}:generateContent?key=${this.apiKey}`, {
|
|
8545
8589
|
method: "POST",
|
|
8546
8590
|
headers: {
|
|
8547
8591
|
"Content-Type": "application/json"
|
|
@@ -8663,6 +8707,7 @@ var init_gemini = __esm({
|
|
|
8663
8707
|
"use strict";
|
|
8664
8708
|
init_GeminiProvider();
|
|
8665
8709
|
init_gemini_motion();
|
|
8710
|
+
init_gemini_models();
|
|
8666
8711
|
init_define_provider();
|
|
8667
8712
|
defineProvider({
|
|
8668
8713
|
id: "gemini",
|
|
@@ -18026,6 +18071,10 @@ __export(dist_exports2, {
|
|
|
18026
18071
|
ClaudeProvider: () => ClaudeProvider,
|
|
18027
18072
|
ElevenLabsProvider: () => ElevenLabsProvider,
|
|
18028
18073
|
FalProvider: () => FalProvider,
|
|
18074
|
+
GEMINI_AGENT_DEFAULT_TEXT_MODEL: () => GEMINI_AGENT_DEFAULT_TEXT_MODEL,
|
|
18075
|
+
GEMINI_DEFAULT_TEXT_MODEL: () => GEMINI_DEFAULT_TEXT_MODEL,
|
|
18076
|
+
GEMINI_TEXT_MODEL_ALIASES: () => GEMINI_TEXT_MODEL_ALIASES,
|
|
18077
|
+
GEMINI_TEXT_MODEL_HELP: () => GEMINI_TEXT_MODEL_HELP,
|
|
18029
18078
|
GeminiProvider: () => GeminiProvider,
|
|
18030
18079
|
GrokProvider: () => GrokProvider,
|
|
18031
18080
|
KNOWN_VOICES: () => KNOWN_VOICES,
|
|
@@ -18056,6 +18105,7 @@ __export(dist_exports2, {
|
|
|
18056
18105
|
getProvidersFor: () => getProvidersFor,
|
|
18057
18106
|
getSetupProviders: () => getSetupProviders,
|
|
18058
18107
|
grokProvider: () => grokProvider,
|
|
18108
|
+
isGeminiTextModelAlias: () => isGeminiTextModelAlias,
|
|
18059
18109
|
klingProvider: () => klingProvider,
|
|
18060
18110
|
kokoroProvider: () => kokoroProvider,
|
|
18061
18111
|
ollamaProvider: () => ollamaProvider,
|
|
@@ -18063,6 +18113,7 @@ __export(dist_exports2, {
|
|
|
18063
18113
|
openaiProvider: () => openaiProvider,
|
|
18064
18114
|
providerRegistry: () => providerRegistry,
|
|
18065
18115
|
replicateProvider: () => replicateProvider,
|
|
18116
|
+
resolveGeminiTextModel: () => resolveGeminiTextModel,
|
|
18066
18117
|
resolveVoiceId: () => resolveVoiceId,
|
|
18067
18118
|
runwayProvider: () => runwayProvider,
|
|
18068
18119
|
whisperProvider: () => whisperProvider
|
|
@@ -27471,6 +27522,52 @@ function buildPreset(input) {
|
|
|
27471
27522
|
}
|
|
27472
27523
|
}
|
|
27473
27524
|
}
|
|
27525
|
+
function lottieOverlayStyle(input) {
|
|
27526
|
+
const pos = input.position ?? "full";
|
|
27527
|
+
const rawScale = input.scale ?? (pos === "full" ? 1 : 0.25);
|
|
27528
|
+
const scale = Math.max(0.01, Math.min(2, rawScale));
|
|
27529
|
+
const opacity = Math.max(0, Math.min(1, input.opacity ?? 1));
|
|
27530
|
+
const base = [
|
|
27531
|
+
"position:absolute",
|
|
27532
|
+
"pointer-events:none",
|
|
27533
|
+
`opacity:${opacity}`
|
|
27534
|
+
];
|
|
27535
|
+
if (pos === "full") {
|
|
27536
|
+
base.push("inset:0", "width:100%", "height:100%");
|
|
27537
|
+
} else {
|
|
27538
|
+
const pct = scale * 100;
|
|
27539
|
+
base.push(`width:${pct}%`, `height:${pct}%`);
|
|
27540
|
+
switch (pos) {
|
|
27541
|
+
case "center":
|
|
27542
|
+
base.push("top:50%", "left:50%", "transform:translate(-50%,-50%)");
|
|
27543
|
+
break;
|
|
27544
|
+
case "top-left":
|
|
27545
|
+
base.push("top:4%", "left:4%");
|
|
27546
|
+
break;
|
|
27547
|
+
case "top-right":
|
|
27548
|
+
base.push("top:4%", "right:4%");
|
|
27549
|
+
break;
|
|
27550
|
+
case "bottom-left":
|
|
27551
|
+
base.push("bottom:4%", "left:4%");
|
|
27552
|
+
break;
|
|
27553
|
+
case "bottom-right":
|
|
27554
|
+
base.push("bottom:4%", "right:4%");
|
|
27555
|
+
break;
|
|
27556
|
+
}
|
|
27557
|
+
}
|
|
27558
|
+
return base.join(";");
|
|
27559
|
+
}
|
|
27560
|
+
function buildLottieOverlay(input, sceneId) {
|
|
27561
|
+
const loop = input.loop ?? true ? " loop" : "";
|
|
27562
|
+
const style = lottieOverlayStyle(input);
|
|
27563
|
+
const overlayId = `lottie-overlay-${sceneId}`;
|
|
27564
|
+
const markup = `<dotlottie-wc id="${overlayId}" src="${esc(input.src)}" autoplay${loop} style="${style}"></dotlottie-wc>`;
|
|
27565
|
+
const script = `<script type="module">
|
|
27566
|
+
import { setWasmUrl } from "${DOTLOTTIE_WC_CDN}";
|
|
27567
|
+
setWasmUrl("${DOTLOTTIE_WASM_CDN}");
|
|
27568
|
+
</script>`;
|
|
27569
|
+
return { markup, script };
|
|
27570
|
+
}
|
|
27474
27571
|
function emitSceneHtml(input) {
|
|
27475
27572
|
if (input.duration <= 0) {
|
|
27476
27573
|
throw new Error(`Scene duration must be > 0, got ${input.duration}`);
|
|
@@ -27491,15 +27588,20 @@ function emitSceneHtml(input) {
|
|
|
27491
27588
|
data-volume="1"
|
|
27492
27589
|
></audio>
|
|
27493
27590
|
` : "";
|
|
27591
|
+
const lottieLayer = input.lottie ? buildLottieOverlay(input.lottie, id) : null;
|
|
27592
|
+
const lottieMarkup = lottieLayer ? `
|
|
27593
|
+
${lottieLayer.markup}` : "";
|
|
27594
|
+
const lottieScript = lottieLayer ? `
|
|
27595
|
+
${lottieLayer.script}` : "";
|
|
27494
27596
|
return `<template id="scene-${id}-template">
|
|
27495
27597
|
<div data-composition-id="${id}" data-start="0" data-duration="${dur}" data-width="${input.width}" data-height="${input.height}">
|
|
27496
27598
|
<style>
|
|
27497
27599
|
${parts.css}
|
|
27498
27600
|
</style>
|
|
27499
27601
|
|
|
27500
|
-
${parts.body}
|
|
27602
|
+
${parts.body}${lottieMarkup}
|
|
27501
27603
|
${audioBlock}
|
|
27502
|
-
<script src="${GSAP_CDN}"></script
|
|
27604
|
+
<script src="${GSAP_CDN}"></script>${lottieScript}
|
|
27503
27605
|
<script>
|
|
27504
27606
|
window.__timelines = window.__timelines || {};
|
|
27505
27607
|
const tl = gsap.timeline({ paused: true });
|
|
@@ -27561,7 +27663,7 @@ function readRootDims(rootHtml) {
|
|
|
27561
27663
|
if (!widthMatch || !heightMatch) return null;
|
|
27562
27664
|
return { width: parseInt(widthMatch[1], 10), height: parseInt(heightMatch[1], 10) };
|
|
27563
27665
|
}
|
|
27564
|
-
var SCENE_PRESETS, GSAP_CDN, SCENE_OVERLAP_SECONDS;
|
|
27666
|
+
var SCENE_PRESETS, LOTTIE_POSITIONS, GSAP_CDN, SCENE_OVERLAP_SECONDS, DOTLOTTIE_WC_VERSION, DOTLOTTIE_WEB_VERSION, DOTLOTTIE_WC_CDN, DOTLOTTIE_WASM_CDN;
|
|
27565
27667
|
var init_scene_html_emit = __esm({
|
|
27566
27668
|
"src/commands/_shared/scene-html-emit.ts"() {
|
|
27567
27669
|
"use strict";
|
|
@@ -27572,8 +27674,20 @@ var init_scene_html_emit = __esm({
|
|
|
27572
27674
|
"kinetic-type",
|
|
27573
27675
|
"product-shot"
|
|
27574
27676
|
];
|
|
27677
|
+
LOTTIE_POSITIONS = [
|
|
27678
|
+
"full",
|
|
27679
|
+
"center",
|
|
27680
|
+
"top-left",
|
|
27681
|
+
"top-right",
|
|
27682
|
+
"bottom-left",
|
|
27683
|
+
"bottom-right"
|
|
27684
|
+
];
|
|
27575
27685
|
GSAP_CDN = "https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js";
|
|
27576
27686
|
SCENE_OVERLAP_SECONDS = 0.4;
|
|
27687
|
+
DOTLOTTIE_WC_VERSION = "0.9.12";
|
|
27688
|
+
DOTLOTTIE_WEB_VERSION = "0.71.0";
|
|
27689
|
+
DOTLOTTIE_WC_CDN = `https://cdn.jsdelivr.net/npm/@lottiefiles/dotlottie-wc@${DOTLOTTIE_WC_VERSION}/dist/index.js`;
|
|
27690
|
+
DOTLOTTIE_WASM_CDN = `https://cdn.jsdelivr.net/npm/@lottiefiles/dotlottie-web@${DOTLOTTIE_WEB_VERSION}/dist/dotlottie-player.wasm`;
|
|
27577
27691
|
}
|
|
27578
27692
|
});
|
|
27579
27693
|
|
|
@@ -29607,6 +29721,7 @@ var MODEL_SETTINGS_BY_PROVIDER, ComposeBeatError, defaultCallLLM, CONFIG_PROVIDE
|
|
|
29607
29721
|
var init_compose_scenes_skills = __esm({
|
|
29608
29722
|
"src/commands/_shared/compose-scenes-skills.ts"() {
|
|
29609
29723
|
"use strict";
|
|
29724
|
+
init_dist2();
|
|
29610
29725
|
init_config();
|
|
29611
29726
|
init_bundle();
|
|
29612
29727
|
init_scene_lint();
|
|
@@ -29628,12 +29743,12 @@ var init_compose_scenes_skills = __esm({
|
|
|
29628
29743
|
medium: { model: "gpt-5", maxTokens: 6e3, costPerMTokIn: 1.25, costPerMTokOut: 10 },
|
|
29629
29744
|
high: { model: "gpt-5", maxTokens: 8e3, costPerMTokIn: 1.25, costPerMTokOut: 10 }
|
|
29630
29745
|
},
|
|
29631
|
-
// Google Gemini
|
|
29632
|
-
//
|
|
29746
|
+
// Google Gemini 3.5 Flash — current one-shot Gemini default for
|
|
29747
|
+
// analysis/composition. Keep agent loops on their explicit 2.5 Flash default.
|
|
29633
29748
|
gemini: {
|
|
29634
|
-
low: { model:
|
|
29635
|
-
medium: { model:
|
|
29636
|
-
high: { model:
|
|
29749
|
+
low: { model: GEMINI_DEFAULT_TEXT_MODEL, maxTokens: 4e3, costPerMTokIn: 1.5, costPerMTokOut: 9 },
|
|
29750
|
+
medium: { model: GEMINI_DEFAULT_TEXT_MODEL, maxTokens: 6e3, costPerMTokIn: 1.5, costPerMTokOut: 9 },
|
|
29751
|
+
high: { model: GEMINI_DEFAULT_TEXT_MODEL, maxTokens: 8e3, costPerMTokIn: 1.5, costPerMTokOut: 9 }
|
|
29637
29752
|
}
|
|
29638
29753
|
};
|
|
29639
29754
|
ComposeBeatError = class extends Error {
|
|
@@ -30579,6 +30694,30 @@ async function executeSceneAdd(opts) {
|
|
|
30579
30694
|
await writeFile11(imageAbsPath, buffer);
|
|
30580
30695
|
}
|
|
30581
30696
|
}
|
|
30697
|
+
let lottieRelPath;
|
|
30698
|
+
let lottieAbsPath;
|
|
30699
|
+
let lottieInput;
|
|
30700
|
+
if (opts.lottie) {
|
|
30701
|
+
const sourceAbs = resolve15(opts.lottie.src);
|
|
30702
|
+
if (!await pathExists2(sourceAbs)) {
|
|
30703
|
+
return errResult(`Lottie file not found: ${sourceAbs}`);
|
|
30704
|
+
}
|
|
30705
|
+
const ext = (sourceAbs.match(/\.([a-z0-9]+)$/i)?.[1] ?? "json").toLowerCase();
|
|
30706
|
+
if (ext !== "json" && ext !== "lottie") {
|
|
30707
|
+
return errResult(`Unsupported Lottie file extension: .${ext}. Use .json or .lottie.`);
|
|
30708
|
+
}
|
|
30709
|
+
lottieRelPath = `assets/lottie-${id}.${ext}`;
|
|
30710
|
+
lottieAbsPath = resolve15(projectDir, lottieRelPath);
|
|
30711
|
+
await mkdir6(dirname3(lottieAbsPath), { recursive: true });
|
|
30712
|
+
await copyFile(sourceAbs, lottieAbsPath);
|
|
30713
|
+
lottieInput = {
|
|
30714
|
+
src: lottieRelPath,
|
|
30715
|
+
position: opts.lottie.position,
|
|
30716
|
+
scale: opts.lottie.scale,
|
|
30717
|
+
opacity: opts.lottie.opacity,
|
|
30718
|
+
loop: opts.lottie.loop
|
|
30719
|
+
};
|
|
30720
|
+
}
|
|
30582
30721
|
const cfg = await loadVibeProjectConfig(projectDir);
|
|
30583
30722
|
const fallback2 = cfg?.defaultSceneDuration ?? 5;
|
|
30584
30723
|
const NARRATION_TAIL_BUFFER = 0.5;
|
|
@@ -30607,7 +30746,8 @@ async function executeSceneAdd(opts) {
|
|
|
30607
30746
|
kicker: opts.kicker,
|
|
30608
30747
|
imagePath: imageRelPath,
|
|
30609
30748
|
audioPath: audioRelPath,
|
|
30610
|
-
transcript: transcriptWords
|
|
30749
|
+
transcript: transcriptWords,
|
|
30750
|
+
lottie: lottieInput
|
|
30611
30751
|
});
|
|
30612
30752
|
await mkdir6(dirname3(scenePath), { recursive: true });
|
|
30613
30753
|
await writeFile11(scenePath, sceneHtml, "utf-8");
|
|
@@ -30628,6 +30768,7 @@ async function executeSceneAdd(opts) {
|
|
|
30628
30768
|
rootPath: relative6(process.cwd(), rootPath) || rootPath,
|
|
30629
30769
|
audioPath: audioAbsPath ? relative6(process.cwd(), audioAbsPath) || audioAbsPath : void 0,
|
|
30630
30770
|
imagePath: imageAbsPath ? relative6(process.cwd(), imageAbsPath) || imageAbsPath : void 0,
|
|
30771
|
+
lottiePath: lottieAbsPath ? relative6(process.cwd(), lottieAbsPath) || lottieAbsPath : void 0,
|
|
30631
30772
|
transcriptPath: transcriptAbsPath ? relative6(process.cwd(), transcriptAbsPath) || transcriptAbsPath : void 0,
|
|
30632
30773
|
transcriptWordCount
|
|
30633
30774
|
};
|
|
@@ -30905,9 +31046,40 @@ Run 'vibe schema scene.<command>' for structured parameter info.`
|
|
|
30905
31046
|
).option("--no-image", "Skip image generation even when --visuals is provided").option(
|
|
30906
31047
|
"--no-transcribe",
|
|
30907
31048
|
"Skip Whisper word-level transcribe step (no transcript-<id>.json emitted)"
|
|
30908
|
-
).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("--
|
|
31049
|
+
).option("--transcribe-language <code>", "BCP-47 language code passed to Whisper (e.g. en, ko)").option("--force", "Overwrite an existing compositions/scene-<id>.html").option("--lottie <path>", "Lottie animation file (.json/.lottie) to overlay on the scene").option(
|
|
31050
|
+
"--lottie-position <position>",
|
|
31051
|
+
`Lottie position: ${LOTTIE_POSITIONS.join(", ")}`,
|
|
31052
|
+
"full"
|
|
31053
|
+
).option("--lottie-scale <number>", "Lottie overlay scale (0.01-2)").option("--lottie-opacity <number>", "Lottie overlay opacity (0-1)", "1").option("--lottie-no-loop", "Do not loop the Lottie animation").option("--dry-run", "Preview parameters without writing files or calling APIs").action(async (name, options) => {
|
|
30909
31054
|
const startedAt = Date.now();
|
|
30910
31055
|
if (options.style) options.style = validatePreset(options.style);
|
|
31056
|
+
if (options.lottiePosition) {
|
|
31057
|
+
if (!LOTTIE_POSITIONS.includes(options.lottiePosition)) {
|
|
31058
|
+
exitWithError(
|
|
31059
|
+
usageError(
|
|
31060
|
+
`Invalid --lottie-position "${options.lottiePosition}". Valid: ${LOTTIE_POSITIONS.join(", ")}`
|
|
31061
|
+
)
|
|
31062
|
+
);
|
|
31063
|
+
}
|
|
31064
|
+
}
|
|
31065
|
+
let lottieScale;
|
|
31066
|
+
if (options.lottieScale !== void 0) {
|
|
31067
|
+
lottieScale = Number(options.lottieScale);
|
|
31068
|
+
if (!Number.isFinite(lottieScale) || lottieScale < 0.01 || lottieScale > 2) {
|
|
31069
|
+
exitWithError(
|
|
31070
|
+
usageError(`Invalid --lottie-scale "${options.lottieScale}". Must be between 0.01 and 2.`)
|
|
31071
|
+
);
|
|
31072
|
+
}
|
|
31073
|
+
}
|
|
31074
|
+
let lottieOpacity = 1;
|
|
31075
|
+
if (options.lottieOpacity !== void 0) {
|
|
31076
|
+
lottieOpacity = Number(options.lottieOpacity);
|
|
31077
|
+
if (!Number.isFinite(lottieOpacity) || lottieOpacity < 0 || lottieOpacity > 1) {
|
|
31078
|
+
exitWithError(
|
|
31079
|
+
usageError(`Invalid --lottie-opacity "${options.lottieOpacity}". Must be between 0 and 1.`)
|
|
31080
|
+
);
|
|
31081
|
+
}
|
|
31082
|
+
}
|
|
30911
31083
|
if (options.duration !== void 0) options.duration = validateDuration(options.duration);
|
|
30912
31084
|
let tts;
|
|
30913
31085
|
try {
|
|
@@ -30936,7 +31108,11 @@ Run 'vibe schema scene.<command>' for structured parameter info.`
|
|
|
30936
31108
|
imageProvider: options.imageProvider,
|
|
30937
31109
|
tts,
|
|
30938
31110
|
audio: options.audio,
|
|
30939
|
-
|
|
31111
|
+
lottie: options.lottie ?? null,
|
|
31112
|
+
lottiePosition: options.lottiePosition,
|
|
31113
|
+
lottieScale: lottieScale ?? null,
|
|
31114
|
+
lottieOpacity: options.lottieOpacity !== void 0 ? lottieOpacity : null,
|
|
31115
|
+
lottieLoop: !options.lottieNoLoop,
|
|
30940
31116
|
image: options.image
|
|
30941
31117
|
}
|
|
30942
31118
|
}
|
|
@@ -30964,6 +31140,13 @@ Run 'vibe schema scene.<command>' for structured parameter info.`
|
|
|
30964
31140
|
skipTranscribe: options.transcribe === false,
|
|
30965
31141
|
transcribeLanguage: options.transcribeLanguage,
|
|
30966
31142
|
force: !!options.force,
|
|
31143
|
+
lottie: options.lottie ? {
|
|
31144
|
+
src: options.lottie,
|
|
31145
|
+
position: options.lottiePosition ?? "full",
|
|
31146
|
+
scale: lottieScale,
|
|
31147
|
+
opacity: options.lottieOpacity !== void 0 ? lottieOpacity : 1,
|
|
31148
|
+
loop: !options.lottieNoLoop
|
|
31149
|
+
} : void 0,
|
|
30967
31150
|
onProgress: (msg) => {
|
|
30968
31151
|
if (spinner2) spinner2.text = msg;
|
|
30969
31152
|
}
|
|
@@ -32028,7 +32211,7 @@ Use this image analysis to inform the color palette, typography placement, and o
|
|
|
32028
32211
|
6. Recommended animation timing and entrance/exit style
|
|
32029
32212
|
7. Any moments where overlays should pause, fade, or stay minimal`;
|
|
32030
32213
|
const analysisResult = await gemini.analyzeVideo(videoBuffer, analysisPrompt, {
|
|
32031
|
-
model:
|
|
32214
|
+
model: GEMINI_DEFAULT_TEXT_MODEL,
|
|
32032
32215
|
fps: 1,
|
|
32033
32216
|
lowResolution: true
|
|
32034
32217
|
});
|
|
@@ -32197,7 +32380,7 @@ function registerMotionCommand(aiCommand) {
|
|
|
32197
32380
|
"auto"
|
|
32198
32381
|
).option("--understanding-prompt <text>", "Custom prompt for --video understanding").option("--from-tsx <path>", "Refine an existing TSX file instead of generating from scratch").option(
|
|
32199
32382
|
"-m, --model <alias>",
|
|
32200
|
-
"LLM model: sonnet (default), opus, gemini, gemini-3.1-pro",
|
|
32383
|
+
"LLM model: sonnet (default), opus, gemini, gemini-2.5-pro, gemini-3.1-pro",
|
|
32201
32384
|
"sonnet"
|
|
32202
32385
|
).option("--dry-run", "Preview parameters without executing").action(async (description, options) => {
|
|
32203
32386
|
const startedAt = Date.now();
|
|
@@ -32341,7 +32524,8 @@ var init_ai_motion = __esm({
|
|
|
32341
32524
|
sonnet: { provider: "claude", modelId: "claude-sonnet-4-6" },
|
|
32342
32525
|
opus: { provider: "claude", modelId: "claude-opus-4-7" },
|
|
32343
32526
|
"opus-4-6": { provider: "claude", modelId: "claude-opus-4-6" },
|
|
32344
|
-
gemini: { provider: "gemini", modelId:
|
|
32527
|
+
gemini: { provider: "gemini", modelId: GEMINI_DEFAULT_TEXT_MODEL },
|
|
32528
|
+
"gemini-2.5-pro": { provider: "gemini", modelId: "gemini-2.5-pro" },
|
|
32345
32529
|
"gemini-3.1-pro": { provider: "gemini", modelId: "gemini-3.1-pro-preview" }
|
|
32346
32530
|
};
|
|
32347
32531
|
}
|
|
@@ -35768,13 +35952,7 @@ async function executeThumbnailBestFrame(options) {
|
|
|
35768
35952
|
await gemini.initialize({ apiKey: googleKey });
|
|
35769
35953
|
const videoData = await readFile17(videoPath);
|
|
35770
35954
|
const analysisPrompt = prompt3 || 'Analyze this video and find the single best frame for a thumbnail. Look for frames that are visually striking, well-composed, emotionally engaging, and representative of the video content. Avoid blurry frames, transitions, or dark scenes. Return ONLY a JSON object: {"timestamp": <seconds as number>, "reason": "<brief explanation>"}';
|
|
35771
|
-
const
|
|
35772
|
-
flash: "gemini-3-flash-preview",
|
|
35773
|
-
latest: "gemini-2.5-flash",
|
|
35774
|
-
"flash-2.5": "gemini-2.5-flash",
|
|
35775
|
-
pro: "gemini-2.5-pro"
|
|
35776
|
-
};
|
|
35777
|
-
const modelId = modelMap[model] || "gemini-3-flash-preview";
|
|
35955
|
+
const modelId = resolveGeminiTextModel(model);
|
|
35778
35956
|
const result = await gemini.analyzeVideo(videoData, analysisPrompt, {
|
|
35779
35957
|
model: modelId,
|
|
35780
35958
|
fps: 1
|
|
@@ -35822,7 +36000,7 @@ import { resolve as resolve27, dirname as dirname8, basename as basename5, extna
|
|
|
35822
36000
|
import { existsSync as existsSync17 } from "node:fs";
|
|
35823
36001
|
import { writeFile as writeFile25, mkdir as mkdir12 } from "node:fs/promises";
|
|
35824
36002
|
function registerThumbnailCommand(parent) {
|
|
35825
|
-
parent.command("thumbnail").description("Generate video thumbnail (DALL-E) or extract best frame from video (Gemini)").argument("[description]", "Thumbnail description (for DALL-E generation)").option("-k, --api-key <key>", "API key (OpenAI for generation, Google for best-frame)").option("-o, --output <path>", "Output file path").option("--style <style>", "Platform style: youtube, instagram, tiktok, twitter").option("--best-frame <video>", "Extract best thumbnail frame from video using Gemini AI").option("--prompt <prompt>", "Custom prompt for best-frame analysis").option("--model <model>",
|
|
36003
|
+
parent.command("thumbnail").description("Generate video thumbnail (DALL-E) or extract best frame from video (Gemini)").argument("[description]", "Thumbnail description (for DALL-E generation)").option("-k, --api-key <key>", "API key (OpenAI for generation, Google for best-frame)").option("-o, --output <path>", "Output file path").option("--style <style>", "Platform style: youtube, instagram, tiktok, twitter").option("--best-frame <video>", "Extract best thumbnail frame from video using Gemini AI").option("--prompt <prompt>", "Custom prompt for best-frame analysis").option("--model <model>", `Gemini model: ${GEMINI_TEXT_MODEL_HELP}`, "flash").action(async (description, options) => {
|
|
35826
36004
|
const startedAt = Date.now();
|
|
35827
36005
|
try {
|
|
35828
36006
|
if (description) rejectControlChars(description);
|
|
@@ -37977,7 +38155,7 @@ function registerVideoCommand(parent) {
|
|
|
37977
38155
|
"--seedance-model <model>",
|
|
37978
38156
|
"Seedance variant: quality or fast (fal.ai only)",
|
|
37979
38157
|
"quality"
|
|
37980
|
-
).option("--negative <prompt>", "Negative prompt - what to avoid (Kling/Veo)").option("--resolution <res>", "Video resolution: 720p, 1080p, 4k
|
|
38158
|
+
).option("--negative <prompt>", "Negative prompt - what to avoid (Kling/Veo)").option("--resolution <res>", "Video resolution: 480p, 720p, 1080p, or 4k depending on provider").option("--last-frame <path>", "Last frame image for frame interpolation (Veo) or Seedance end frame").option("--end-image <path>", "Ending frame image for Seedance image-to-video").option(
|
|
37981
38159
|
"--ref-images <paths...>",
|
|
37982
38160
|
"Reference images for Seedance reference-to-video or Veo character consistency"
|
|
37983
38161
|
).option("--ref-videos <paths...>", "Reference videos for Seedance reference-to-video").option("--ref-audio <paths...>", "Reference audio for Seedance reference-to-video").option("--no-generate-audio", "Disable native audio when the provider supports it").option("--person <mode>", "Person generation: allow_all, allow_adult (Veo only)").option("--veo-model <model>", "Veo model: 3.0, 3.1, 3.1-fast (default: 3.1-fast)", "3.1-fast").option(
|
|
@@ -38438,6 +38616,30 @@ Examples:
|
|
|
38438
38616
|
exitWithError(apiError(message, true));
|
|
38439
38617
|
}
|
|
38440
38618
|
}
|
|
38619
|
+
let seedanceEndImage;
|
|
38620
|
+
const seedanceEndImagePath = options.endImage ?? options.lastFrame;
|
|
38621
|
+
if (seedanceEndImagePath && seedanceReferences.length === 0) {
|
|
38622
|
+
try {
|
|
38623
|
+
const absEndImagePath = resolve31(process.cwd(), seedanceEndImagePath);
|
|
38624
|
+
const endImageBuffer = await readFile18(absEndImagePath);
|
|
38625
|
+
const ext = seedanceEndImagePath.toLowerCase().split(".").pop();
|
|
38626
|
+
const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : `image/${ext || "png"}`;
|
|
38627
|
+
const uploadHost = await resolveUploadHost();
|
|
38628
|
+
spinner2.text = `Uploading end image via ${uploadHost.provider} for Seedance...`;
|
|
38629
|
+
const upload = await uploadHost.uploadImage(endImageBuffer, {
|
|
38630
|
+
filename: seedanceEndImagePath,
|
|
38631
|
+
mimeType
|
|
38632
|
+
});
|
|
38633
|
+
seedanceEndImage = upload.url;
|
|
38634
|
+
} catch (err) {
|
|
38635
|
+
spinner2.fail("End image upload failed");
|
|
38636
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
38637
|
+
if (message.includes("IMGBB_API_KEY")) {
|
|
38638
|
+
exitWithError(authError("IMGBB_API_KEY", "ImgBB"));
|
|
38639
|
+
}
|
|
38640
|
+
exitWithError(apiError(message, true));
|
|
38641
|
+
}
|
|
38642
|
+
}
|
|
38441
38643
|
spinner2.text = "Generating video with fal.ai Seedance 2.0 (this may take 1-3 minutes)...";
|
|
38442
38644
|
const seedanceModel = String(options.seedanceModel ?? "quality").toLowerCase();
|
|
38443
38645
|
const falModel = seedanceModel === "fast" || seedanceModel === "seedance-2.0-fast" ? "seedance-2.0-fast" : "seedance-2.0";
|
|
@@ -38450,7 +38652,8 @@ Examples:
|
|
|
38450
38652
|
negativePrompt: options.negative,
|
|
38451
38653
|
model: falModel,
|
|
38452
38654
|
resolution: options.resolution,
|
|
38453
|
-
generateAudio: options.generateAudio
|
|
38655
|
+
generateAudio: options.generateAudio,
|
|
38656
|
+
lastFrame: seedanceEndImage
|
|
38454
38657
|
});
|
|
38455
38658
|
finalResult = result;
|
|
38456
38659
|
}
|
|
@@ -38760,12 +38963,7 @@ async function detectSilencePeriodsWithGemini(videoPath, minDuration, options) {
|
|
|
38760
38963
|
const gemini = new GeminiProvider();
|
|
38761
38964
|
await gemini.initialize({ apiKey: geminiApiKey });
|
|
38762
38965
|
const videoBuffer = await readFile19(videoPath);
|
|
38763
|
-
const
|
|
38764
|
-
flash: "gemini-3-flash-preview",
|
|
38765
|
-
"flash-2.5": "gemini-2.5-flash",
|
|
38766
|
-
pro: "gemini-2.5-pro"
|
|
38767
|
-
};
|
|
38768
|
-
const modelId = options.model ? modelMap[options.model] || modelMap.flash : void 0;
|
|
38966
|
+
const modelId = options.model ? resolveGeminiTextModel(options.model) : void 0;
|
|
38769
38967
|
const prompt3 = `Analyze this video and identify all silent or dead segments where there is NO meaningful content.
|
|
38770
38968
|
|
|
38771
38969
|
Detect these as silent/dead segments:
|
|
@@ -41671,7 +41869,7 @@ async function executeMotionOverlay(options) {
|
|
|
41671
41869
|
};
|
|
41672
41870
|
}
|
|
41673
41871
|
function registerMotionOverlayCommand(parent) {
|
|
41674
|
-
parent.command("motion-overlay").description("Apply designed motion graphics overlays to an existing video").argument("<video>", "Video file path").argument("[description]", "Motion overlay description (omit when using --asset)").option("--asset <path>", "User-provided .json/.lottie animation to overlay").option("-o, --output <path>", "Output video file path").option("-d, --duration <sec>", "Overlay/render duration in seconds").option("--start <sec>", "Overlay start time in seconds", "0").option("--style <style>", "Style preset for generated overlays: minimal, corporate, playful, cinematic").option("-m, --model <alias>", "LLM model for generated overlays: sonnet, opus, gemini, gemini-3.1-pro", "sonnet").option("--understand <mode>", "Analyze video before generated overlay: auto, off, required", "auto").option("--understanding-prompt <text>", "Custom prompt for video understanding").option("--position <position>", "Lottie position: full, center, top-left, top-right, bottom-left, bottom-right", "full").option("--scale <number>", "Lottie overlay scale (0.01-2)").option("--opacity <number>", "Lottie overlay opacity (0-1)", "1").option("--loop", "Loop Lottie overlay", true).option("--no-loop", "Do not loop Lottie overlay").option("--dry-run", "Preview parameters without executing").action(async (videoPath, description, options) => {
|
|
41872
|
+
parent.command("motion-overlay").description("Apply designed motion graphics overlays to an existing video").argument("<video>", "Video file path").argument("[description]", "Motion overlay description (omit when using --asset)").option("--asset <path>", "User-provided .json/.lottie animation to overlay").option("-o, --output <path>", "Output video file path").option("-d, --duration <sec>", "Overlay/render duration in seconds").option("--start <sec>", "Overlay start time in seconds", "0").option("--style <style>", "Style preset for generated overlays: minimal, corporate, playful, cinematic").option("-m, --model <alias>", "LLM model for generated overlays: sonnet, opus, gemini, gemini-2.5-pro, gemini-3.1-pro", "sonnet").option("--understand <mode>", "Analyze video before generated overlay: auto, off, required", "auto").option("--understanding-prompt <text>", "Custom prompt for video understanding").option("--position <position>", "Lottie position: full, center, top-left, top-right, bottom-left, bottom-right", "full").option("--scale <number>", "Lottie overlay scale (0.01-2)").option("--opacity <number>", "Lottie overlay opacity (0-1)", "1").option("--loop", "Loop Lottie overlay", true).option("--no-loop", "Do not loop Lottie overlay").option("--dry-run", "Preview parameters without executing").action(async (videoPath, description, options) => {
|
|
41675
41873
|
const startedAt = Date.now();
|
|
41676
41874
|
try {
|
|
41677
41875
|
if (options.output) validateOutputPath(options.output);
|
|
@@ -42761,12 +42959,7 @@ async function executeGeminiVideo(options) {
|
|
|
42761
42959
|
return { success: false, error: "Google API key required. Run 'vibe setup' or set GOOGLE_API_KEY in .env" };
|
|
42762
42960
|
}
|
|
42763
42961
|
const isYouTube = options.source.includes("youtube.com") || options.source.includes("youtu.be");
|
|
42764
|
-
const
|
|
42765
|
-
flash: "gemini-3-flash-preview",
|
|
42766
|
-
"flash-2.5": "gemini-2.5-flash",
|
|
42767
|
-
pro: "gemini-2.5-pro"
|
|
42768
|
-
};
|
|
42769
|
-
const modelId = modelMap[options.model || "flash"] || modelMap.flash;
|
|
42962
|
+
const modelId = resolveGeminiTextModel(options.model);
|
|
42770
42963
|
let videoData;
|
|
42771
42964
|
if (isYouTube) {
|
|
42772
42965
|
videoData = options.source;
|
|
@@ -42827,12 +43020,7 @@ async function executeAnalyze(options) {
|
|
|
42827
43020
|
error: "Cannot detect source type. Supported: images (.png/.jpg/.webp/.gif), videos (.mp4/.mov/.webm), YouTube URLs, image URLs."
|
|
42828
43021
|
};
|
|
42829
43022
|
}
|
|
42830
|
-
const
|
|
42831
|
-
flash: "gemini-3-flash-preview",
|
|
42832
|
-
"flash-2.5": "gemini-2.5-flash",
|
|
42833
|
-
pro: "gemini-2.5-pro"
|
|
42834
|
-
};
|
|
42835
|
-
const modelId = modelMap[options.model || "flash"] || modelMap.flash;
|
|
43023
|
+
const modelId = resolveGeminiTextModel(options.model);
|
|
42836
43024
|
const gemini = new GeminiProvider();
|
|
42837
43025
|
await gemini.initialize({ apiKey });
|
|
42838
43026
|
if (isImage) {
|
|
@@ -42912,7 +43100,7 @@ async function executeAnalyze(options) {
|
|
|
42912
43100
|
}
|
|
42913
43101
|
}
|
|
42914
43102
|
function registerAnalyzeCommands(aiCommand) {
|
|
42915
|
-
aiCommand.command("gemini-video").description("Analyze video using Gemini (summarize, Q&A, extract info)").argument("<source>", "Video file path or YouTube URL").argument("<prompt>", "Analysis prompt (e.g., 'Summarize this video')").option("-k, --api-key <key>", "Google API key (or set GOOGLE_API_KEY env)").option("-m, --model <model>",
|
|
43103
|
+
aiCommand.command("gemini-video").description("Analyze video using Gemini (summarize, Q&A, extract info)").argument("<source>", "Video file path or YouTube URL").argument("<prompt>", "Analysis prompt (e.g., 'Summarize this video')").option("-k, --api-key <key>", "Google API key (or set GOOGLE_API_KEY env)").option("-m, --model <model>", `Model: ${GEMINI_TEXT_MODEL_HELP}`, "flash").option("--fps <number>", "Frames per second (default: 1, higher for action)").option("--start <seconds>", "Start offset in seconds (for clipping)").option("--end <seconds>", "End offset in seconds (for clipping)").option("--low-res", "Use low resolution mode (fewer tokens, longer videos)").option("-v, --verbose", "Show token usage").action(async (source, prompt3, options) => {
|
|
42916
43104
|
try {
|
|
42917
43105
|
if (options.apiKey) {
|
|
42918
43106
|
process.env.GOOGLE_API_KEY = options.apiKey;
|
|
@@ -42955,7 +43143,7 @@ function registerAnalyzeCommands(aiCommand) {
|
|
|
42955
43143
|
exitWithError(apiError(`Video analysis failed: ${error instanceof Error ? error.message : String(error)}`, true));
|
|
42956
43144
|
}
|
|
42957
43145
|
});
|
|
42958
|
-
aiCommand.command("analyze").description("Analyze any media: images, videos, or YouTube URLs using Gemini").argument("<source>", "Image/video file path, image URL, or YouTube URL").argument("<prompt>", "Analysis prompt (e.g., 'Describe this image', 'Summarize this video')").option("-k, --api-key <key>", "Google API key (or set GOOGLE_API_KEY env)").option("-m, --model <model>",
|
|
43146
|
+
aiCommand.command("analyze").description("Analyze any media: images, videos, or YouTube URLs using Gemini").argument("<source>", "Image/video file path, image URL, or YouTube URL").argument("<prompt>", "Analysis prompt (e.g., 'Describe this image', 'Summarize this video')").option("-k, --api-key <key>", "Google API key (or set GOOGLE_API_KEY env)").option("-m, --model <model>", `Model: ${GEMINI_TEXT_MODEL_HELP}`, "flash").option("--fps <number>", "Frames per second for video (default: 1)").option("--start <seconds>", "Start offset in seconds (video only)").option("--end <seconds>", "End offset in seconds (video only)").option("--low-res", "Use low resolution mode (fewer tokens)").option("-v, --verbose", "Show token usage").action(async (source, prompt3, options) => {
|
|
42959
43147
|
try {
|
|
42960
43148
|
if (options.apiKey) {
|
|
42961
43149
|
process.env.GOOGLE_API_KEY = options.apiKey;
|
|
@@ -43076,12 +43264,7 @@ ${content}`;
|
|
|
43076
43264
|
|
|
43077
43265
|
Project context for beat-aware review:
|
|
43078
43266
|
${projectContext}` : "";
|
|
43079
|
-
const
|
|
43080
|
-
flash: "gemini-3-flash-preview",
|
|
43081
|
-
"flash-2.5": "gemini-2.5-flash",
|
|
43082
|
-
pro: "gemini-2.5-pro"
|
|
43083
|
-
};
|
|
43084
|
-
const modelId = modelMap[model] || modelMap.flash;
|
|
43267
|
+
const modelId = resolveGeminiTextModel(model);
|
|
43085
43268
|
const reviewPrompt = `You are a professional video editor reviewing this video for quality. Analyze the video and return a JSON review with the following structure. Return ONLY valid JSON, no extra text.
|
|
43086
43269
|
|
|
43087
43270
|
{
|
|
@@ -43188,7 +43371,7 @@ Score each category 1-10. Prefer beatIssues when you can map a problem to a stor
|
|
|
43188
43371
|
return result;
|
|
43189
43372
|
}
|
|
43190
43373
|
function registerReviewCommand(aiCommand) {
|
|
43191
|
-
aiCommand.command("review", { hidden: true }).description("Review video quality using Gemini AI and optionally auto-fix issues").argument("<source>", "Video file path").option("--storyboard <path>", "Storyboard JSON file for context").option("--auto-apply", "Automatically apply fixable corrections").option("--verify", "Run verification pass after applying fixes").option("-m, --model <model>",
|
|
43374
|
+
aiCommand.command("review", { hidden: true }).description("Review video quality using Gemini AI and optionally auto-fix issues").argument("<source>", "Video file path").option("--storyboard <path>", "Storyboard JSON file for context").option("--auto-apply", "Automatically apply fixable corrections").option("--verify", "Run verification pass after applying fixes").option("-m, --model <model>", `Gemini model: ${GEMINI_TEXT_MODEL_HELP}`, "flash").option("-o, --output <path>", "Output video file path (for auto-apply)").option("--dry-run", "Preview parameters without executing").action(async (videoPath, options) => {
|
|
43192
43375
|
const startedAt = Date.now();
|
|
43193
43376
|
try {
|
|
43194
43377
|
if (options.output) {
|
|
@@ -67614,7 +67797,7 @@ var init_edit = __esm({
|
|
|
67614
67797
|
duration: external_exports.number().optional().describe("Overlay/render duration in seconds"),
|
|
67615
67798
|
start: external_exports.number().optional().describe("Overlay start time in seconds"),
|
|
67616
67799
|
style: external_exports.string().optional().describe("Style preset for generated overlays"),
|
|
67617
|
-
model: external_exports.enum(["sonnet", "opus", "gemini", "gemini-3.1-pro"]).optional().describe("LLM model for generated overlays"),
|
|
67800
|
+
model: external_exports.enum(["sonnet", "opus", "gemini", "gemini-2.5-pro", "gemini-3.1-pro"]).optional().describe("LLM model for generated overlays"),
|
|
67618
67801
|
understand: external_exports.enum(["auto", "off", "required"]).optional().describe("Analyze video before generated overlay (default: auto)"),
|
|
67619
67802
|
understandingPrompt: external_exports.string().optional().describe("Custom prompt for video understanding"),
|
|
67620
67803
|
position: external_exports.enum(["full", "center", "top-left", "top-right", "bottom-left", "bottom-right"]).optional().describe("Lottie overlay position"),
|
|
@@ -67990,7 +68173,7 @@ var init_inspect = __esm({
|
|
|
67990
68173
|
outputPath: external_exports.string().optional().describe("Optional review report path. Defaults to <project>/review-report.json."),
|
|
67991
68174
|
report: external_exports.boolean().optional().describe("Write review-report.json. Default true."),
|
|
67992
68175
|
ai: external_exports.boolean().optional().describe("Also run Gemini video review and merge findings into review-report.json. Default false."),
|
|
67993
|
-
model: external_exports.enum(["flash", "flash-2.5", "pro"]).optional().describe("Gemini model variant for ai review. Default flash."),
|
|
68176
|
+
model: external_exports.enum(["flash", "latest", "flash-3", "flash-2.5", "pro", "pro-3.1"]).optional().describe("Gemini model variant for ai review. Default flash."),
|
|
67994
68177
|
dryRun: external_exports.boolean().optional().describe("Preview resolved inputs without probing video or calling Gemini.")
|
|
67995
68178
|
}),
|
|
67996
68179
|
async execute(args, ctx) {
|
|
@@ -68042,7 +68225,7 @@ var init_inspect = __esm({
|
|
|
68042
68225
|
schema: external_exports.object({
|
|
68043
68226
|
source: external_exports.string().describe("Path to image/video or YouTube URL"),
|
|
68044
68227
|
prompt: external_exports.string().describe("Analysis prompt (e.g., 'Describe the scene', 'Count people')"),
|
|
68045
|
-
model: external_exports.enum(["flash", "flash-2.5", "pro"]).optional().describe("Gemini model variant (default: flash)"),
|
|
68228
|
+
model: external_exports.enum(["flash", "latest", "flash-3", "flash-2.5", "pro", "pro-3.1"]).optional().describe("Gemini model variant (default: flash)"),
|
|
68046
68229
|
fps: external_exports.number().optional().describe("Frames per second for video sampling (default: 1)"),
|
|
68047
68230
|
start: external_exports.number().optional().describe("Start time in seconds for video analysis"),
|
|
68048
68231
|
end: external_exports.number().optional().describe("End time in seconds for video analysis"),
|
|
@@ -68070,7 +68253,7 @@ var init_inspect = __esm({
|
|
|
68070
68253
|
schema: external_exports.object({
|
|
68071
68254
|
source: external_exports.string().describe("Path to video file"),
|
|
68072
68255
|
prompt: external_exports.string().describe("Analysis prompt"),
|
|
68073
|
-
model: external_exports.enum(["flash", "flash-2.5", "pro"]).optional().describe("Gemini model variant (default: flash)"),
|
|
68256
|
+
model: external_exports.enum(["flash", "latest", "flash-3", "flash-2.5", "pro", "pro-3.1"]).optional().describe("Gemini model variant (default: flash)"),
|
|
68074
68257
|
fps: external_exports.number().optional().describe("Frames per second for sampling"),
|
|
68075
68258
|
start: external_exports.number().optional().describe("Start time in seconds"),
|
|
68076
68259
|
end: external_exports.number().optional().describe("End time in seconds"),
|
|
@@ -68100,7 +68283,7 @@ var init_inspect = __esm({
|
|
|
68100
68283
|
storyboardPath: external_exports.string().optional().describe("Path to storyboard.json for intent comparison"),
|
|
68101
68284
|
autoApply: external_exports.boolean().optional().describe("Automatically apply suggested fixes (default: false)"),
|
|
68102
68285
|
verify: external_exports.boolean().optional().describe("Re-review after applying fixes (default: false)"),
|
|
68103
|
-
model: external_exports.enum(["flash", "flash-2.5", "pro"]).optional().describe("Gemini model variant (default: flash)"),
|
|
68286
|
+
model: external_exports.enum(["flash", "latest", "flash-3", "flash-2.5", "pro", "pro-3.1"]).optional().describe("Gemini model variant (default: flash)"),
|
|
68104
68287
|
outputPath: external_exports.string().optional().describe("Output path for fixed video")
|
|
68105
68288
|
}),
|
|
68106
68289
|
async execute(args) {
|
|
@@ -68192,7 +68375,7 @@ var init_generate2 = __esm({
|
|
|
68192
68375
|
"Analyze the base video with Gemini before generating motion graphics: auto, off, or required (default: auto)"
|
|
68193
68376
|
),
|
|
68194
68377
|
understandingPrompt: external_exports.string().optional().describe("Custom prompt for video understanding when --video is provided"),
|
|
68195
|
-
model: external_exports.enum(["sonnet", "opus", "gemini", "gemini-3.1-pro"]).optional().describe("LLM model for code generation (default: sonnet)"),
|
|
68378
|
+
model: external_exports.enum(["sonnet", "opus", "gemini", "gemini-2.5-pro", "gemini-3.1-pro"]).optional().describe("LLM model for code generation (default: sonnet)"),
|
|
68196
68379
|
output: external_exports.string().optional().describe("Output path (TSX if code-only, MP4 if rendered)")
|
|
68197
68380
|
}),
|
|
68198
68381
|
async execute(args) {
|
|
@@ -68457,6 +68640,7 @@ var init_generate2 = __esm({
|
|
|
68457
68640
|
"Video provider (default: seedance when FAL_API_KEY is configured, otherwise first configured provider)"
|
|
68458
68641
|
),
|
|
68459
68642
|
image: external_exports.string().optional().describe("Reference image path for image-to-video"),
|
|
68643
|
+
endImage: external_exports.string().optional().describe("Ending frame image path for Seedance image-to-video"),
|
|
68460
68644
|
refImages: external_exports.array(external_exports.string()).optional().describe("Reference images for Seedance reference-to-video"),
|
|
68461
68645
|
refVideos: external_exports.array(external_exports.string()).optional().describe("Reference videos for Seedance reference-to-video"),
|
|
68462
68646
|
refAudio: external_exports.array(external_exports.string()).optional().describe("Reference audio files for Seedance reference-to-video"),
|
|
@@ -68464,7 +68648,7 @@ var init_generate2 = __esm({
|
|
|
68464
68648
|
ratio: external_exports.string().optional().describe("Aspect ratio: 16:9, 9:16, 1:1 (default: 16:9)"),
|
|
68465
68649
|
mode: external_exports.string().optional().describe("Kling mode: std or pro"),
|
|
68466
68650
|
negative: external_exports.string().optional().describe("Negative prompt (Seedance/Kling/Veo)"),
|
|
68467
|
-
resolution: external_exports.string().optional().describe("Resolution: 720p, 1080p, 4k
|
|
68651
|
+
resolution: external_exports.string().optional().describe("Resolution: 480p, 720p, 1080p, or 4k depending on provider"),
|
|
68468
68652
|
veoModel: external_exports.string().optional().describe("Veo model: 3.0, 3.1, 3.1-fast"),
|
|
68469
68653
|
runwayModel: external_exports.string().optional().describe("Runway model: gen4.5, gen4_turbo"),
|
|
68470
68654
|
seedanceModel: external_exports.string().optional().describe("Seedance variant: quality or fast (fal.ai only)"),
|
|
@@ -71702,7 +71886,7 @@ init_source();
|
|
|
71702
71886
|
// package.json
|
|
71703
71887
|
var package_default = {
|
|
71704
71888
|
name: "@vibeframe/cli",
|
|
71705
|
-
version: "0.
|
|
71889
|
+
version: "0.106.0",
|
|
71706
71890
|
description: "VibeFrame CLI - natural language video editing from the terminal",
|
|
71707
71891
|
private: false,
|
|
71708
71892
|
license: "MIT",
|
|
@@ -72801,7 +72985,14 @@ init_scene_inspect();
|
|
|
72801
72985
|
init_render_inspect();
|
|
72802
72986
|
import { readFile as readFile29, writeFile as writeFile36 } from "node:fs/promises";
|
|
72803
72987
|
import { resolve as resolve42 } from "node:path";
|
|
72804
|
-
var VALID_RENDER_AI_MODELS = [
|
|
72988
|
+
var VALID_RENDER_AI_MODELS = [
|
|
72989
|
+
"flash",
|
|
72990
|
+
"latest",
|
|
72991
|
+
"flash-3",
|
|
72992
|
+
"flash-2.5",
|
|
72993
|
+
"pro",
|
|
72994
|
+
"pro-3.1"
|
|
72995
|
+
];
|
|
72805
72996
|
var inspectCommand = new Command("inspect").description("Inspect projects, renders, and media").addHelpText(
|
|
72806
72997
|
"after",
|
|
72807
72998
|
`
|
|
@@ -72866,7 +73057,7 @@ inspectCommand.command("project").description(
|
|
|
72866
73057
|
}
|
|
72867
73058
|
});
|
|
72868
73059
|
applyTier(inspectCommand.commands[inspectCommand.commands.length - 1], "free");
|
|
72869
|
-
inspectCommand.command("render").description("Inspect a rendered project video with local checks and optional Gemini review").argument("[project-dir]", "VibeFrame project directory", ".").option("--cheap", "Run local checks only (default; no AI/API calls)").option("--ai", "Also run Gemini video review and merge findings into review-report.json").option("-m, --model <model>",
|
|
73060
|
+
inspectCommand.command("render").description("Inspect a rendered project video with local checks and optional Gemini review").argument("[project-dir]", "VibeFrame project directory", ".").option("--cheap", "Run local checks only (default; no AI/API calls)").option("--ai", "Also run Gemini video review and merge findings into review-report.json").option("-m, --model <model>", `Gemini model for --ai: ${GEMINI_TEXT_MODEL_HELP}`, "flash").option("--beat <id>", "Inspect a render for one storyboard beat").option(
|
|
72870
73061
|
"--video <path>",
|
|
72871
73062
|
"Rendered video path. Defaults to build-report outputPath or latest renders/* video."
|
|
72872
73063
|
).option(
|
|
@@ -72931,7 +73122,7 @@ inspectCommand.command("render").description("Inspect a rendered project video w
|
|
|
72931
73122
|
}
|
|
72932
73123
|
});
|
|
72933
73124
|
applyTier(inspectCommand.commands[inspectCommand.commands.length - 1], "low");
|
|
72934
|
-
inspectCommand.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>",
|
|
73125
|
+
inspectCommand.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: ${GEMINI_TEXT_MODEL_HELP}`, "flash").option("--fps <number>", "Frames per second for video (default: 1)").option("--start <seconds>", "Start offset in seconds (video only)").option("--end <seconds>", "End offset in seconds (video only)").option("--low-res", "Use low resolution mode (fewer tokens)").option("--verbose", "Show token usage").option("--fields <fields>", "Comma-separated fields to include in output (e.g., response,model)").option("--dry-run", "Preview parameters without executing").action(async (source, prompt3, options) => {
|
|
72935
73126
|
const startedAt = Date.now();
|
|
72936
73127
|
try {
|
|
72937
73128
|
rejectControlChars(prompt3);
|
|
@@ -73034,14 +73225,17 @@ function printInspectSummary(title, status, score, issues, reportPath) {
|
|
|
73034
73225
|
}
|
|
73035
73226
|
}
|
|
73036
73227
|
function parseRenderAiModel(value) {
|
|
73037
|
-
if (
|
|
73228
|
+
if (isGeminiTextModelAlias(value) || value.startsWith("gemini-")) {
|
|
73038
73229
|
return value;
|
|
73039
73230
|
}
|
|
73040
73231
|
exitWithError(
|
|
73041
|
-
usageError(
|
|
73232
|
+
usageError(
|
|
73233
|
+
`Invalid --model: ${value}`,
|
|
73234
|
+
`Must be one of: ${VALID_RENDER_AI_MODELS.join(", ")}, or a full gemini-* model ID`
|
|
73235
|
+
)
|
|
73042
73236
|
);
|
|
73043
73237
|
}
|
|
73044
|
-
inspectCommand.command("video", { hidden: true }).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>",
|
|
73238
|
+
inspectCommand.command("video", { hidden: true }).description("Analyze video using Gemini (summarize, Q&A, extract info)").argument("<source>", "Video file path or YouTube URL").argument("<prompt>", "Analysis prompt (e.g., 'Summarize this video')").option("-k, --api-key <key>", "Google API key (or set GOOGLE_API_KEY env)").option("-m, --model <model>", `Model: ${GEMINI_TEXT_MODEL_HELP}`, "flash").option("--fps <number>", "Frames per second (default: 1, higher for action)").option("--start <seconds>", "Start offset in seconds (for clipping)").option("--end <seconds>", "End offset in seconds (for clipping)").option("--low-res", "Use low resolution mode (fewer tokens, longer videos)").option("--verbose", "Show token usage").option("--fields <fields>", "Comma-separated fields to include in output (e.g., response,model)").option("--dry-run", "Preview parameters without executing").action(async (source, prompt3, options) => {
|
|
73045
73239
|
const startedAt = Date.now();
|
|
73046
73240
|
try {
|
|
73047
73241
|
rejectControlChars(prompt3);
|