@vibeframe/cli 0.102.0 → 0.103.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/CONTEXT.md +4 -4
- package/dist/index.js +271 -100
- package/package.json +3 -3
package/CONTEXT.md
CHANGED
|
@@ -40,10 +40,10 @@ vibe guide <topic> --json # Step-by-step guides (motion | scene | p
|
|
|
40
40
|
|
|
41
41
|
| Tier | Commands | Per-call cost |
|
|
42
42
|
| --------- | ------------------------------------------------------------------------------------------------------------------------------- | ------------- |
|
|
43
|
-
| Free | `detect *`, `
|
|
44
|
-
| Low | `
|
|
45
|
-
| High | `generate
|
|
46
|
-
| Very High | `remix
|
|
43
|
+
| Free | `schema/context/doctor`, `detect *`, `status *`, `plan`, `storyboard validate`, `inspect project/render --cheap`, deterministic edits | $0 |
|
|
44
|
+
| Low | `generate narration/sound-effect/music`, `audio transcribe/list-voices`, `inspect media`, optional AI review | $0.01–0.10 |
|
|
45
|
+
| High | `generate image/motion`, `edit image/reframe/grade/speed-ramp` | $1–5 |
|
|
46
|
+
| Very High | `generate video`, `edit fill-gaps`, `remix highlights/auto-shorts`, `build` with generated assets | $5–50+ |
|
|
47
47
|
|
|
48
48
|
> Rule: **confirm with the user before any High / Very-High call**.
|
|
49
49
|
|
package/dist/index.js
CHANGED
|
@@ -25732,6 +25732,7 @@ __export(config_exports, {
|
|
|
25732
25732
|
USER_CONFIG_DIR: () => USER_CONFIG_DIR,
|
|
25733
25733
|
USER_CONFIG_PATH: () => USER_CONFIG_PATH,
|
|
25734
25734
|
createDefaultConfig: () => createDefaultConfig,
|
|
25735
|
+
findProjectConfigPath: () => findProjectConfigPath,
|
|
25735
25736
|
getActiveScope: () => getActiveScope,
|
|
25736
25737
|
getApiKeyFromConfig: () => getApiKeyFromConfig,
|
|
25737
25738
|
getConfigDir: () => getConfigDir,
|
|
@@ -26461,9 +26462,14 @@ End on the product name, offer, or command. Avoid adding a new idea in the
|
|
|
26461
26462
|
final beat.
|
|
26462
26463
|
`;
|
|
26463
26464
|
}
|
|
26464
|
-
function
|
|
26465
|
+
function buildProjectAgentsMd(name) {
|
|
26465
26466
|
return `# ${name} \u2014 Scene Authoring Project
|
|
26466
26467
|
|
|
26468
|
+
This is the canonical cross-agent guidance file for this scene project.
|
|
26469
|
+
Claude Code imports it through \`CLAUDE.md\`; Codex, Cursor, Aider,
|
|
26470
|
+
Gemini CLI, OpenCode, and other bash-capable agents should read it
|
|
26471
|
+
directly.
|
|
26472
|
+
|
|
26467
26473
|
This project is **bilingual**: it works with both VibeFrame (\`vibe\`) and
|
|
26468
26474
|
HeyGen Hyperframes (\`hyperframes\`). You can run either CLI inside this
|
|
26469
26475
|
directory.
|
|
@@ -26503,6 +26509,8 @@ Browse named styles: \`vibe scene list-styles\`. Re-seed from one with
|
|
|
26503
26509
|
|
|
26504
26510
|
## Skills \u2014 USE THESE FIRST
|
|
26505
26511
|
|
|
26512
|
+
@SKILL.md
|
|
26513
|
+
|
|
26506
26514
|
**Always invoke the relevant skill before authoring scenes.** Skills encode
|
|
26507
26515
|
framework-specific patterns (GSAP timeline registration, data-attribute
|
|
26508
26516
|
semantics, VibeFrame pipeline conventions) that are NOT in generic web docs.
|
|
@@ -26571,6 +26579,17 @@ vibe scene lint --json # structured output for agent loops
|
|
|
26571
26579
|
\`\`\`
|
|
26572
26580
|
`;
|
|
26573
26581
|
}
|
|
26582
|
+
function buildProjectClaudeMd(name) {
|
|
26583
|
+
return `@AGENTS.md
|
|
26584
|
+
|
|
26585
|
+
# ${name} \u2014 Claude Code Overrides
|
|
26586
|
+
|
|
26587
|
+
This file imports \`AGENTS.md\`; keep cross-agent VibeFrame and
|
|
26588
|
+
Hyperframes instructions there so Codex, Cursor, Aider, Gemini CLI, and
|
|
26589
|
+
OpenCode see the same project rules. Add Claude-Code-specific notes below
|
|
26590
|
+
only when this project needs them.
|
|
26591
|
+
`;
|
|
26592
|
+
}
|
|
26574
26593
|
function buildSceneGitignore() {
|
|
26575
26594
|
return `# VibeFrame \u2014 caches, checkpoints, and project-scope config.yaml (may contain API keys)
|
|
26576
26595
|
.vibeframe/
|
|
@@ -26613,6 +26632,7 @@ function describeSceneScaffold(opts) {
|
|
|
26613
26632
|
}
|
|
26614
26633
|
if (profile === "agent" || profile === "full") {
|
|
26615
26634
|
groups.agent = [
|
|
26635
|
+
resolve7(dir, "AGENTS.md"),
|
|
26616
26636
|
resolve7(dir, "SKILL.md"),
|
|
26617
26637
|
resolve7(dir, "references"),
|
|
26618
26638
|
resolve7(dir, "CLAUDE.md")
|
|
@@ -26692,9 +26712,24 @@ async function scaffoldSceneProject(opts) {
|
|
|
26692
26712
|
created.push(vibePath);
|
|
26693
26713
|
}
|
|
26694
26714
|
if (profile === "agent" || profile === "full") {
|
|
26715
|
+
const agentsPath = resolve7(dir, "AGENTS.md");
|
|
26716
|
+
if (await pathExists(agentsPath)) {
|
|
26717
|
+
skipped2.push(agentsPath);
|
|
26718
|
+
} else {
|
|
26719
|
+
await writeFile4(agentsPath, buildProjectAgentsMd(name), "utf-8");
|
|
26720
|
+
created.push(agentsPath);
|
|
26721
|
+
}
|
|
26695
26722
|
const claudePath = resolve7(dir, "CLAUDE.md");
|
|
26696
26723
|
if (await pathExists(claudePath)) {
|
|
26697
|
-
|
|
26724
|
+
const existing = await readFile5(claudePath, "utf-8");
|
|
26725
|
+
if (existing.includes("@AGENTS.md")) {
|
|
26726
|
+
skipped2.push(claudePath);
|
|
26727
|
+
} else {
|
|
26728
|
+
await writeFile4(claudePath, `@AGENTS.md
|
|
26729
|
+
|
|
26730
|
+
${existing}`, "utf-8");
|
|
26731
|
+
merged.push(claudePath);
|
|
26732
|
+
}
|
|
26698
26733
|
} else {
|
|
26699
26734
|
await writeFile4(claudePath, buildProjectClaudeMd(name), "utf-8");
|
|
26700
26735
|
created.push(claudePath);
|
|
@@ -32543,22 +32578,35 @@ function applySuggestion(project, suggestion) {
|
|
|
32543
32578
|
if (clipIds.length === 0) return false;
|
|
32544
32579
|
const clipId = clipIds[0];
|
|
32545
32580
|
switch (type) {
|
|
32546
|
-
case "trim":
|
|
32547
|
-
|
|
32548
|
-
|
|
32581
|
+
case "trim": {
|
|
32582
|
+
const newDuration = params.newDuration;
|
|
32583
|
+
if (typeof newDuration === "number") {
|
|
32584
|
+
return project.trimClipEnd(clipId, newDuration);
|
|
32549
32585
|
}
|
|
32550
32586
|
break;
|
|
32551
|
-
|
|
32552
|
-
|
|
32587
|
+
}
|
|
32588
|
+
case "add-effect": {
|
|
32589
|
+
const effectType = params.effectType;
|
|
32590
|
+
if (typeof effectType === "string") {
|
|
32591
|
+
const startTime = typeof params.startTime === "number" ? params.startTime : 0;
|
|
32592
|
+
const duration = typeof params.duration === "number" ? params.duration : 1;
|
|
32593
|
+
const rawEffectParams = params.effectParams && typeof params.effectParams === "object" ? params.effectParams : {};
|
|
32594
|
+
const effectParams = {};
|
|
32595
|
+
for (const [k, v] of Object.entries(rawEffectParams)) {
|
|
32596
|
+
if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
|
|
32597
|
+
effectParams[k] = v;
|
|
32598
|
+
}
|
|
32599
|
+
}
|
|
32553
32600
|
const effect = project.addEffect(clipId, {
|
|
32554
|
-
type:
|
|
32555
|
-
startTime
|
|
32556
|
-
duration
|
|
32557
|
-
params:
|
|
32601
|
+
type: effectType,
|
|
32602
|
+
startTime,
|
|
32603
|
+
duration,
|
|
32604
|
+
params: effectParams
|
|
32558
32605
|
});
|
|
32559
32606
|
return effect !== null;
|
|
32560
32607
|
}
|
|
32561
32608
|
break;
|
|
32609
|
+
}
|
|
32562
32610
|
case "delete":
|
|
32563
32611
|
return project.removeClip(clipId);
|
|
32564
32612
|
}
|
|
@@ -34891,6 +34939,7 @@ function isActiveStatus(status) {
|
|
|
34891
34939
|
}
|
|
34892
34940
|
function providerStatusCommand(record) {
|
|
34893
34941
|
if (record.jobType === "generate-video") {
|
|
34942
|
+
if (record.provider !== "runway" && record.provider !== "kling") return void 0;
|
|
34894
34943
|
const type = record.provider === "kling" && record.providerTaskType ? ` --type ${record.providerTaskType}` : "";
|
|
34895
34944
|
return `vibe generate video-status ${record.providerTaskId} -p ${record.provider}${type} --json`;
|
|
34896
34945
|
}
|
|
@@ -58194,9 +58243,22 @@ function backdropCacheDescriptor(opts) {
|
|
|
58194
58243
|
provider: opts.provider,
|
|
58195
58244
|
quality: opts.quality,
|
|
58196
58245
|
size: opts.size,
|
|
58246
|
+
ratio: opts.ratio,
|
|
58197
58247
|
ext: "png"
|
|
58198
58248
|
});
|
|
58199
58249
|
}
|
|
58250
|
+
function imageRatioForSize(size) {
|
|
58251
|
+
switch (size) {
|
|
58252
|
+
case "1024x1024":
|
|
58253
|
+
return "1:1";
|
|
58254
|
+
case "1024x1536":
|
|
58255
|
+
return "2:3";
|
|
58256
|
+
case "1536x1024":
|
|
58257
|
+
return "3:2";
|
|
58258
|
+
default:
|
|
58259
|
+
return "16:9";
|
|
58260
|
+
}
|
|
58261
|
+
}
|
|
58200
58262
|
function videoCacheDescriptor(opts) {
|
|
58201
58263
|
return cacheAssetDescriptor("video", {
|
|
58202
58264
|
beatId: opts.beatId,
|
|
@@ -58274,6 +58336,7 @@ async function createBuildPlan(opts) {
|
|
|
58274
58336
|
}
|
|
58275
58337
|
const storyboardMd = await readFile40(storyboardPath2, "utf-8");
|
|
58276
58338
|
const validation = validateStoryboardMarkdown(storyboardMd);
|
|
58339
|
+
const validationIssues = [...validation.issues];
|
|
58277
58340
|
const parsed = parseStoryboard(storyboardMd);
|
|
58278
58341
|
let sourceBeats = parsed.beats;
|
|
58279
58342
|
if (opts.beat) {
|
|
@@ -58296,6 +58359,7 @@ async function createBuildPlan(opts) {
|
|
|
58296
58359
|
const mode = opts.mode ?? config2.config.build.mode;
|
|
58297
58360
|
const imageQuality = opts.imageQuality ?? config2.config.build.imageQuality ?? "hd";
|
|
58298
58361
|
const imageSize2 = opts.imageSize ?? config2.config.build.imageSize ?? "1536x1024";
|
|
58362
|
+
const imageRatio = imageRatioForSize(imageSize2);
|
|
58299
58363
|
const needsComposer = includeCompose && mode !== "agent" && sourceBeats.some((beat) => !existsSync44(join20(projectDir, `compositions/scene-${beat.id}.html`)));
|
|
58300
58364
|
const resolved = await resolvePlanProviders({
|
|
58301
58365
|
projectDir,
|
|
@@ -58345,7 +58409,8 @@ async function createBuildPlan(opts) {
|
|
|
58345
58409
|
cue: backdropPrompt,
|
|
58346
58410
|
provider: resolved.image.resolved,
|
|
58347
58411
|
quality: imageQuality,
|
|
58348
|
-
size: imageSize2
|
|
58412
|
+
size: imageSize2,
|
|
58413
|
+
ratio: imageRatio
|
|
58349
58414
|
}) : null;
|
|
58350
58415
|
const videoCache = videoPrompt && !videoReference ? videoCacheDescriptor({
|
|
58351
58416
|
beatId: beat.id,
|
|
@@ -58439,7 +58504,8 @@ async function createBuildPlan(opts) {
|
|
|
58439
58504
|
}
|
|
58440
58505
|
}
|
|
58441
58506
|
if (narration?.willGenerate) noteProviderNeed(resolved.narration, "Narration");
|
|
58442
|
-
if (backdrop?.willGenerate
|
|
58507
|
+
if (backdrop?.willGenerate && isSupportedBuildImageProvider(resolved.image.resolved))
|
|
58508
|
+
noteProviderNeed(resolved.image, "Backdrop generation");
|
|
58443
58509
|
if (video?.willGenerate) noteProviderNeed(resolved.video, "Video generation");
|
|
58444
58510
|
if (music?.willGenerate) noteProviderNeed(resolved.music, "Music generation");
|
|
58445
58511
|
if (!compositionExists) missing.add("compositions");
|
|
@@ -58461,23 +58527,35 @@ async function createBuildPlan(opts) {
|
|
|
58461
58527
|
};
|
|
58462
58528
|
});
|
|
58463
58529
|
providerResolution.push(...providerResolutionsForPlan(resolved, beats, opts, includeAssets));
|
|
58530
|
+
if (includeAssets && !opts.skipBackdrop && !isSupportedBuildImageProvider(resolved.image.resolved)) {
|
|
58531
|
+
const unsupportedBackdropBeats = beats.filter(
|
|
58532
|
+
(beat) => isBuildGeneratedBackdropPlan(beat.assets.backdrop)
|
|
58533
|
+
);
|
|
58534
|
+
for (const beat of unsupportedBackdropBeats) {
|
|
58535
|
+
validationIssues.push({
|
|
58536
|
+
severity: "error",
|
|
58537
|
+
code: "UNSUPPORTED_BUILD_IMAGE_PROVIDER",
|
|
58538
|
+
beatId: beat.id,
|
|
58539
|
+
message: `Build backdrop generation supports openai, gemini, and grok. Beat "${beat.id}" resolves image provider "${resolved.image.resolved}"; rerun with one of those or replace the backdrop cue with a project asset path.`
|
|
58540
|
+
});
|
|
58541
|
+
}
|
|
58542
|
+
if (unsupportedBackdropBeats.length > 0) {
|
|
58543
|
+
const beatFlag = opts.beat ? ` --beat ${opts.beat}` : "";
|
|
58544
|
+
retryWith.push(
|
|
58545
|
+
`vibe build ${projectDir}${beatFlag} --stage assets --image-provider openai --json`
|
|
58546
|
+
);
|
|
58547
|
+
}
|
|
58548
|
+
}
|
|
58549
|
+
const validationOk = !validationIssues.some((issue) => issue.severity === "error");
|
|
58464
58550
|
if (!existsSync44(join20(projectDir, config2.config.composition.entry))) {
|
|
58465
58551
|
missing.add("root-composition");
|
|
58466
|
-
if (
|
|
58552
|
+
if (validationOk) retryWith.push(`vibe build ${projectDir} --stage sync --json`);
|
|
58467
58553
|
}
|
|
58468
58554
|
if (config2.legacy) {
|
|
58469
58555
|
warnings.push(
|
|
58470
58556
|
`Using legacy ${config2.source}; write ${projectDir}/vibe.config.json to use the TO-BE project contract.`
|
|
58471
58557
|
);
|
|
58472
58558
|
}
|
|
58473
|
-
if (resolved.image.resolved !== "openai" && beats.some((beat) => {
|
|
58474
|
-
const backdrop = beat.assets.backdrop;
|
|
58475
|
-
return backdrop && !["referenced-asset", "invalid-reference", "stage-skipped"].includes(backdrop.reason);
|
|
58476
|
-
}) && !opts.skipBackdrop) {
|
|
58477
|
-
warnings.push(
|
|
58478
|
-
`Image provider "${resolved.image.resolved}" is not supported by build assets yet; use --image-provider openai.`
|
|
58479
|
-
);
|
|
58480
|
-
}
|
|
58481
58559
|
if (!validation.ok) {
|
|
58482
58560
|
retryWith.push(
|
|
58483
58561
|
`vibe storyboard validate ${projectDir} --json`,
|
|
@@ -58488,7 +58566,7 @@ async function createBuildPlan(opts) {
|
|
|
58488
58566
|
projectDir,
|
|
58489
58567
|
config: config2,
|
|
58490
58568
|
stage,
|
|
58491
|
-
status:
|
|
58569
|
+
status: validationOk ? "ready" : "invalid",
|
|
58492
58570
|
currentStage: stage,
|
|
58493
58571
|
mode,
|
|
58494
58572
|
beat: opts.beat ?? null,
|
|
@@ -58500,8 +58578,8 @@ async function createBuildPlan(opts) {
|
|
|
58500
58578
|
warnings,
|
|
58501
58579
|
retryWith,
|
|
58502
58580
|
validation: {
|
|
58503
|
-
ok:
|
|
58504
|
-
issues:
|
|
58581
|
+
ok: validationOk,
|
|
58582
|
+
issues: validationIssues
|
|
58505
58583
|
}
|
|
58506
58584
|
});
|
|
58507
58585
|
}
|
|
@@ -58528,6 +58606,15 @@ function finalizeBuildPlan(plan) {
|
|
|
58528
58606
|
retryWith: unique3(plan.retryWith)
|
|
58529
58607
|
};
|
|
58530
58608
|
}
|
|
58609
|
+
function isBuildGeneratedBackdropPlan(asset) {
|
|
58610
|
+
if (!asset || asset.kind !== "backdrop") return false;
|
|
58611
|
+
return !["referenced-asset", "invalid-reference", "stage-skipped"].includes(asset.reason);
|
|
58612
|
+
}
|
|
58613
|
+
function isSupportedBuildImageProvider(provider) {
|
|
58614
|
+
return SUPPORTED_BUILD_IMAGE_PROVIDERS.includes(
|
|
58615
|
+
provider
|
|
58616
|
+
);
|
|
58617
|
+
}
|
|
58531
58618
|
function nextCommandsForPlan(plan) {
|
|
58532
58619
|
if (plan.status === "invalid") return unique3(plan.retryWith);
|
|
58533
58620
|
const commands = [];
|
|
@@ -58827,7 +58914,7 @@ function loadPlanEnv(projectDir) {
|
|
|
58827
58914
|
dir = dirname19(dir);
|
|
58828
58915
|
}
|
|
58829
58916
|
}
|
|
58830
|
-
var import_dotenv3, BACKDROP_COST_USD, VIDEO_COST_USD, MUSIC_COST_USD, ELEVENLABS_NARRATION_COST_USD, COMPOSE_COST_USD;
|
|
58917
|
+
var import_dotenv3, BACKDROP_COST_USD, VIDEO_COST_USD, MUSIC_COST_USD, ELEVENLABS_NARRATION_COST_USD, COMPOSE_COST_USD, SUPPORTED_BUILD_IMAGE_PROVIDERS;
|
|
58831
58918
|
var init_build_plan = __esm({
|
|
58832
58919
|
"src/commands/_shared/build-plan.ts"() {
|
|
58833
58920
|
"use strict";
|
|
@@ -58846,6 +58933,7 @@ var init_build_plan = __esm({
|
|
|
58846
58933
|
MUSIC_COST_USD = 0.5;
|
|
58847
58934
|
ELEVENLABS_NARRATION_COST_USD = 0.05;
|
|
58848
58935
|
COMPOSE_COST_USD = 0.06;
|
|
58936
|
+
SUPPORTED_BUILD_IMAGE_PROVIDERS = ["openai", "gemini", "grok"];
|
|
58849
58937
|
}
|
|
58850
58938
|
});
|
|
58851
58939
|
|
|
@@ -60252,19 +60340,18 @@ async function dispatchBackdrop(beat, ctx) {
|
|
|
60252
60340
|
if (reference) return referencePrimitiveOutcome("backdrop", beat, ctx, reference);
|
|
60253
60341
|
const prompt3 = stringOrUndefined4(beat.cues?.backdrop);
|
|
60254
60342
|
if (!prompt3) return { status: "no-cue" };
|
|
60255
|
-
if (ctx.imageProvider !== "openai") {
|
|
60256
|
-
const error = `image provider "${ctx.imageProvider}" not yet supported (use openai)`;
|
|
60257
|
-
ctx.onProgress({ type: "backdrop-failed", beatId: beat.id, error });
|
|
60258
|
-
return { status: "failed", error };
|
|
60259
|
-
}
|
|
60260
60343
|
const rel = `assets/backdrop-${beat.id}.png`;
|
|
60261
60344
|
const abs = join22(ctx.projectDir, rel);
|
|
60345
|
+
const size = ctx.imageSize ?? "1536x1024";
|
|
60346
|
+
const ratio = imageRatioForSize(size);
|
|
60347
|
+
const metadataOptions = { quality: ctx.imageQuality, size, ratio };
|
|
60262
60348
|
const cache = backdropCacheDescriptor({
|
|
60263
60349
|
beatId: beat.id,
|
|
60264
60350
|
cue: prompt3,
|
|
60265
60351
|
provider: ctx.imageProvider,
|
|
60266
60352
|
quality: ctx.imageQuality,
|
|
60267
|
-
size
|
|
60353
|
+
size,
|
|
60354
|
+
ratio
|
|
60268
60355
|
});
|
|
60269
60356
|
const metadataPath = assetMetadataPath("backdrop", beat.id);
|
|
60270
60357
|
if (existsSync46(abs) && !ctx.force) {
|
|
@@ -60275,7 +60362,7 @@ async function dispatchBackdrop(beat, ctx) {
|
|
|
60275
60362
|
beatId: beat.id,
|
|
60276
60363
|
cue: prompt3,
|
|
60277
60364
|
provider: ctx.imageProvider,
|
|
60278
|
-
options:
|
|
60365
|
+
options: metadataOptions,
|
|
60279
60366
|
cacheKey: cache.key
|
|
60280
60367
|
})) {
|
|
60281
60368
|
ctx.onProgress({ type: "backdrop-cached", beatId: beat.id, path: rel });
|
|
@@ -60300,7 +60387,7 @@ async function dispatchBackdrop(beat, ctx) {
|
|
|
60300
60387
|
beatId: beat.id,
|
|
60301
60388
|
cue: prompt3,
|
|
60302
60389
|
provider: ctx.imageProvider,
|
|
60303
|
-
options:
|
|
60390
|
+
options: metadataOptions,
|
|
60304
60391
|
cacheKey: cache.key,
|
|
60305
60392
|
canonicalPath: rel,
|
|
60306
60393
|
cachePath: cache.path
|
|
@@ -60316,27 +60403,14 @@ async function dispatchBackdrop(beat, ctx) {
|
|
|
60316
60403
|
freshness: "fresh"
|
|
60317
60404
|
};
|
|
60318
60405
|
}
|
|
60319
|
-
|
|
60320
|
-
|
|
60321
|
-
|
|
60322
|
-
const error = "OPENAI_API_KEY not set \u2014 cannot dispatch backdrop";
|
|
60323
|
-
ctx.onProgress({ type: "backdrop-failed", beatId: beat.id, error });
|
|
60324
|
-
return { status: "failed", error };
|
|
60325
|
-
}
|
|
60326
|
-
const provider = new OpenAIImageProvider();
|
|
60327
|
-
await provider.initialize({ apiKey });
|
|
60328
|
-
const result = await provider.generateImage(prompt3, {
|
|
60329
|
-
model: "gpt-image-2",
|
|
60330
|
-
size: ctx.imageSize,
|
|
60331
|
-
quality: ctx.imageQuality
|
|
60332
|
-
});
|
|
60333
|
-
if (!result.success || !result.images?.[0]?.base64) {
|
|
60334
|
-
const error = result.error ?? "no image data returned";
|
|
60406
|
+
const generated = await generateBackdropImage(prompt3, ctx, ratio);
|
|
60407
|
+
if (!generated.success) {
|
|
60408
|
+
const error = generated.error;
|
|
60335
60409
|
ctx.onProgress({ type: "backdrop-failed", beatId: beat.id, error });
|
|
60336
60410
|
return { status: "failed", error };
|
|
60337
60411
|
}
|
|
60338
60412
|
await mkdir24(dirname22(abs), { recursive: true });
|
|
60339
|
-
const buffer =
|
|
60413
|
+
const buffer = generated.buffer;
|
|
60340
60414
|
await writeFile48(abs, buffer);
|
|
60341
60415
|
await mkdir24(dirname22(cacheAbs), { recursive: true });
|
|
60342
60416
|
await writeFile48(cacheAbs, buffer);
|
|
@@ -60345,8 +60419,8 @@ async function dispatchBackdrop(beat, ctx) {
|
|
|
60345
60419
|
kind: "backdrop",
|
|
60346
60420
|
beatId: beat.id,
|
|
60347
60421
|
cue: prompt3,
|
|
60348
|
-
provider:
|
|
60349
|
-
options:
|
|
60422
|
+
provider: ctx.imageProvider,
|
|
60423
|
+
options: metadataOptions,
|
|
60350
60424
|
cacheKey: cache.key,
|
|
60351
60425
|
canonicalPath: rel,
|
|
60352
60426
|
cachePath: cache.path
|
|
@@ -60355,18 +60429,75 @@ async function dispatchBackdrop(beat, ctx) {
|
|
|
60355
60429
|
type: "backdrop-generated",
|
|
60356
60430
|
beatId: beat.id,
|
|
60357
60431
|
path: rel,
|
|
60358
|
-
provider:
|
|
60432
|
+
provider: ctx.imageProvider
|
|
60359
60433
|
});
|
|
60360
60434
|
return {
|
|
60361
60435
|
status: "generated",
|
|
60362
60436
|
path: rel,
|
|
60363
|
-
provider:
|
|
60437
|
+
provider: ctx.imageProvider,
|
|
60364
60438
|
cachePath: cache.path,
|
|
60365
60439
|
cacheKey: cache.key,
|
|
60366
60440
|
metadataPath,
|
|
60367
60441
|
freshness: "fresh"
|
|
60368
60442
|
};
|
|
60369
60443
|
}
|
|
60444
|
+
async function generateBackdropImage(prompt3, ctx, ratio) {
|
|
60445
|
+
loadSceneBuildEnv(ctx.projectDir);
|
|
60446
|
+
const keyInfo = imageProviderKeyInfo(ctx.imageProvider);
|
|
60447
|
+
const apiKey = await getApiKeyFromConfig(keyInfo.configKey, { cwd: ctx.projectDir }) ?? process.env[keyInfo.envVar] ?? "";
|
|
60448
|
+
if (!apiKey) {
|
|
60449
|
+
return {
|
|
60450
|
+
success: false,
|
|
60451
|
+
error: `${keyInfo.envVar} not set \u2014 cannot dispatch backdrop with ${ctx.imageProvider}`
|
|
60452
|
+
};
|
|
60453
|
+
}
|
|
60454
|
+
if (ctx.imageProvider === "openai") {
|
|
60455
|
+
const provider2 = new OpenAIImageProvider();
|
|
60456
|
+
await provider2.initialize({ apiKey });
|
|
60457
|
+
const result2 = await provider2.generateImage(prompt3, {
|
|
60458
|
+
model: "gpt-image-2",
|
|
60459
|
+
size: ctx.imageSize,
|
|
60460
|
+
quality: ctx.imageQuality
|
|
60461
|
+
});
|
|
60462
|
+
return imageBufferFromResult(result2);
|
|
60463
|
+
}
|
|
60464
|
+
if (ctx.imageProvider === "gemini") {
|
|
60465
|
+
const provider2 = new GeminiProvider();
|
|
60466
|
+
await provider2.initialize({ apiKey });
|
|
60467
|
+
const result2 = await provider2.generateImage(prompt3, {
|
|
60468
|
+
model: "flash",
|
|
60469
|
+
aspectRatio: ratio
|
|
60470
|
+
});
|
|
60471
|
+
return imageBufferFromResult(result2);
|
|
60472
|
+
}
|
|
60473
|
+
const provider = new GrokProvider();
|
|
60474
|
+
await provider.initialize({ apiKey });
|
|
60475
|
+
const result = await provider.generateImage(prompt3, {
|
|
60476
|
+
aspectRatio: ratio,
|
|
60477
|
+
n: 1
|
|
60478
|
+
});
|
|
60479
|
+
return imageBufferFromResult(result);
|
|
60480
|
+
}
|
|
60481
|
+
async function imageBufferFromResult(result) {
|
|
60482
|
+
const image = result.images?.[0];
|
|
60483
|
+
if (!result.success || !image) {
|
|
60484
|
+
return { success: false, error: result.error ?? "no image data returned" };
|
|
60485
|
+
}
|
|
60486
|
+
if (image.base64) return { success: true, buffer: Buffer.from(image.base64, "base64") };
|
|
60487
|
+
if (image.url) {
|
|
60488
|
+
const response = await fetch(image.url);
|
|
60489
|
+
if (!response.ok) {
|
|
60490
|
+
return { success: false, error: `failed to download image: HTTP ${response.status}` };
|
|
60491
|
+
}
|
|
60492
|
+
return { success: true, buffer: Buffer.from(await response.arrayBuffer()) };
|
|
60493
|
+
}
|
|
60494
|
+
return { success: false, error: "no image data returned" };
|
|
60495
|
+
}
|
|
60496
|
+
function imageProviderKeyInfo(provider) {
|
|
60497
|
+
if (provider === "gemini") return { configKey: "google", envVar: "GOOGLE_API_KEY" };
|
|
60498
|
+
if (provider === "grok") return { configKey: "xai", envVar: "XAI_API_KEY" };
|
|
60499
|
+
return { configKey: "openai", envVar: "OPENAI_API_KEY" };
|
|
60500
|
+
}
|
|
60370
60501
|
async function dispatchVideo(beat, ctx) {
|
|
60371
60502
|
const reference = assetReferenceForBeat(ctx.projectDir, "video", beat);
|
|
60372
60503
|
if (reference) return referencePrimitiveOutcome("video", beat, ctx, reference);
|
|
@@ -61265,14 +61396,14 @@ __export(ai_audio_exports, {
|
|
|
61265
61396
|
});
|
|
61266
61397
|
import { resolve as resolve69, dirname as dirname24, basename as basename22, extname as extname15 } from "node:path";
|
|
61267
61398
|
import { readFile as readFile45, writeFile as writeFile49 } from "node:fs/promises";
|
|
61268
|
-
import { existsSync as
|
|
61399
|
+
import { existsSync as existsSync51 } from "node:fs";
|
|
61269
61400
|
async function executeTranscribe(options) {
|
|
61270
61401
|
const { audioPath, language, output, format, apiKey } = options;
|
|
61271
61402
|
try {
|
|
61272
61403
|
const key = apiKey || process.env.OPENAI_API_KEY;
|
|
61273
61404
|
if (!key) return { success: false, error: "OPENAI_API_KEY required" };
|
|
61274
61405
|
const absPath = resolve69(process.cwd(), audioPath);
|
|
61275
|
-
if (!
|
|
61406
|
+
if (!existsSync51(absPath)) return { success: false, error: `File not found: ${absPath}` };
|
|
61276
61407
|
const whisper = new WhisperProvider();
|
|
61277
61408
|
await whisper.initialize({ apiKey: key });
|
|
61278
61409
|
const audioBuffer = await readFile45(absPath);
|
|
@@ -61305,7 +61436,7 @@ async function executeIsolate(options) {
|
|
|
61305
61436
|
const key = apiKey || process.env.ELEVENLABS_API_KEY;
|
|
61306
61437
|
if (!key) return { success: false, error: "ELEVENLABS_API_KEY required" };
|
|
61307
61438
|
const absPath = resolve69(process.cwd(), audioPath);
|
|
61308
|
-
if (!
|
|
61439
|
+
if (!existsSync51(absPath)) return { success: false, error: `File not found: ${absPath}` };
|
|
61309
61440
|
const audioBuffer = await readFile45(absPath);
|
|
61310
61441
|
const elevenlabs = new ElevenLabsProvider();
|
|
61311
61442
|
await elevenlabs.initialize({ apiKey: key });
|
|
@@ -61331,7 +61462,7 @@ async function executeVoiceClone(options) {
|
|
|
61331
61462
|
const audioBuffers = [];
|
|
61332
61463
|
for (const samplePath of samplePaths) {
|
|
61333
61464
|
const absPath = resolve69(process.cwd(), samplePath);
|
|
61334
|
-
if (!
|
|
61465
|
+
if (!existsSync51(absPath)) return { success: false, error: `File not found: ${samplePath}` };
|
|
61335
61466
|
const buffer = await readFile45(absPath);
|
|
61336
61467
|
audioBuffers.push(buffer);
|
|
61337
61468
|
}
|
|
@@ -61353,7 +61484,7 @@ async function executeDub(options) {
|
|
|
61353
61484
|
const { mediaPath, language, source, voice, analyzeOnly, output } = options;
|
|
61354
61485
|
try {
|
|
61355
61486
|
const absPath = resolve69(process.cwd(), mediaPath);
|
|
61356
|
-
if (!
|
|
61487
|
+
if (!existsSync51(absPath)) return { success: false, error: `File not found: ${absPath}` };
|
|
61357
61488
|
const openaiKey = process.env.OPENAI_API_KEY;
|
|
61358
61489
|
const anthropicKey = process.env.ANTHROPIC_API_KEY;
|
|
61359
61490
|
const elevenlabsKey = process.env.ELEVENLABS_API_KEY;
|
|
@@ -61481,8 +61612,8 @@ async function executeDuck(options) {
|
|
|
61481
61612
|
if (!commandExists("ffmpeg")) return { success: false, error: "FFmpeg not found" };
|
|
61482
61613
|
const absMusicPath = resolve69(process.cwd(), musicPath);
|
|
61483
61614
|
const absVoicePath = resolve69(process.cwd(), voicePath);
|
|
61484
|
-
if (!
|
|
61485
|
-
if (!
|
|
61615
|
+
if (!existsSync51(absMusicPath)) return { success: false, error: `Music file not found: ${absMusicPath}` };
|
|
61616
|
+
if (!existsSync51(absVoicePath)) return { success: false, error: `Voice file not found: ${absVoicePath}` };
|
|
61486
61617
|
const defaultOutput = resolve69(dirname24(absMusicPath), `${basename22(absMusicPath, extname15(absMusicPath))}-ducked${extname15(absMusicPath)}`);
|
|
61487
61618
|
const outputPath = resolve69(process.cwd(), output || defaultOutput);
|
|
61488
61619
|
const filter = `[1:a]asplit=2[sc][mix];[0:a][sc]sidechaincompress=threshold=${threshold}dB:ratio=${ratio}:attack=${attack}:release=${release}[ducked];[ducked][mix]amix=inputs=2:duration=longest`;
|
|
@@ -61496,7 +61627,7 @@ async function executeDuck(options) {
|
|
|
61496
61627
|
"-y",
|
|
61497
61628
|
outputPath
|
|
61498
61629
|
], { timeout: 12e4, maxBuffer: 50 * 1024 * 1024 });
|
|
61499
|
-
if (!
|
|
61630
|
+
if (!existsSync51(outputPath)) return { success: false, error: "FFmpeg failed to create output" };
|
|
61500
61631
|
return { success: true, outputPath };
|
|
61501
61632
|
} catch (error) {
|
|
61502
61633
|
return { success: false, error: `Audio ducking failed: ${error instanceof Error ? error.message : String(error)}` };
|
|
@@ -61514,7 +61645,7 @@ var init_ai_audio = __esm({
|
|
|
61514
61645
|
// src/pipeline/executor.ts
|
|
61515
61646
|
import { resolve as resolve70 } from "node:path";
|
|
61516
61647
|
import { readFile as readFile46, writeFile as writeFile50, mkdir as mkdir26 } from "node:fs/promises";
|
|
61517
|
-
import { existsSync as
|
|
61648
|
+
import { existsSync as existsSync52 } from "node:fs";
|
|
61518
61649
|
function maxCostFor(action) {
|
|
61519
61650
|
const cmd = ACTION_METADATA[action]?.command;
|
|
61520
61651
|
if (!cmd) return 0;
|
|
@@ -61829,7 +61960,7 @@ async function ensureActionsRegistered() {
|
|
|
61829
61960
|
}
|
|
61830
61961
|
async function loadPipeline(filePath) {
|
|
61831
61962
|
const absPath = resolve70(process.cwd(), filePath);
|
|
61832
|
-
if (!
|
|
61963
|
+
if (!existsSync52(absPath)) {
|
|
61833
61964
|
throw new Error(`Pipeline file not found: ${absPath}`);
|
|
61834
61965
|
}
|
|
61835
61966
|
const content = await readFile46(absPath, "utf-8");
|
|
@@ -61854,7 +61985,7 @@ async function executePipeline(manifest2, options = {}) {
|
|
|
61854
61985
|
const startTime = Date.now();
|
|
61855
61986
|
if (options.resume) {
|
|
61856
61987
|
const checkpointPath = resolve70(outputDir, CHECKPOINT_FILE);
|
|
61857
|
-
if (
|
|
61988
|
+
if (existsSync52(checkpointPath)) {
|
|
61858
61989
|
const checkpointContent = await readFile46(checkpointPath, "utf-8");
|
|
61859
61990
|
const checkpoint = (0, import_yaml8.parse)(checkpointContent);
|
|
61860
61991
|
for (const cs of checkpoint.completedSteps) {
|
|
@@ -67398,7 +67529,10 @@ async function executeSuggestEdit(options) {
|
|
|
67398
67529
|
}
|
|
67399
67530
|
return { success: true, suggestions };
|
|
67400
67531
|
} catch (error) {
|
|
67401
|
-
return {
|
|
67532
|
+
return {
|
|
67533
|
+
success: false,
|
|
67534
|
+
error: `Suggest failed: ${error instanceof Error ? error.message : String(error)}`
|
|
67535
|
+
};
|
|
67402
67536
|
}
|
|
67403
67537
|
}
|
|
67404
67538
|
var init_ai_suggest_edit = __esm({
|
|
@@ -68078,12 +68212,12 @@ var init_generate2 = __esm({
|
|
|
68078
68212
|
});
|
|
68079
68213
|
|
|
68080
68214
|
// src/tools/manifest/storyboard.ts
|
|
68081
|
-
import { existsSync as
|
|
68215
|
+
import { existsSync as existsSync53 } from "node:fs";
|
|
68082
68216
|
import { readFile as readFile48, writeFile as writeFile52 } from "node:fs/promises";
|
|
68083
68217
|
import { resolve as resolve74 } from "node:path";
|
|
68084
68218
|
async function readStoryboard2(projectDir) {
|
|
68085
68219
|
const path2 = resolve74(projectDir, "STORYBOARD.md");
|
|
68086
|
-
if (!
|
|
68220
|
+
if (!existsSync53(path2)) return null;
|
|
68087
68221
|
return { path: path2, markdown: await readFile48(path2, "utf-8") };
|
|
68088
68222
|
}
|
|
68089
68223
|
var projectDirSchema, storyboardListTool, storyboardValidateTool, storyboardGetTool, storyboardSetTool, storyboardMoveTool, storyboardReviseTool, planTool, storyboardTools;
|
|
@@ -71173,7 +71307,7 @@ init_source();
|
|
|
71173
71307
|
// package.json
|
|
71174
71308
|
var package_default = {
|
|
71175
71309
|
name: "@vibeframe/cli",
|
|
71176
|
-
version: "0.
|
|
71310
|
+
version: "0.103.0",
|
|
71177
71311
|
description: "VibeFrame CLI - natural language video editing from the terminal",
|
|
71178
71312
|
private: false,
|
|
71179
71313
|
license: "MIT",
|
|
@@ -71620,7 +71754,9 @@ init_project_resolver();
|
|
|
71620
71754
|
import { readFile as readFile12, writeFile as writeFile12 } from "node:fs/promises";
|
|
71621
71755
|
import { existsSync as existsSync10 } from "node:fs";
|
|
71622
71756
|
import { resolve as resolve16, basename as basename3, extname } from "node:path";
|
|
71623
|
-
var timelineCommand = new Command("timeline").description("Low-level timeline JSON commands").addHelpText(
|
|
71757
|
+
var timelineCommand = new Command("timeline").description("Low-level timeline JSON commands").addHelpText(
|
|
71758
|
+
"after",
|
|
71759
|
+
`
|
|
71624
71760
|
Examples:
|
|
71625
71761
|
$ vibe timeline create my-video # my-video/timeline.json
|
|
71626
71762
|
$ vibe timeline add-source my-video video.mp4 # Returns source ID
|
|
@@ -71632,7 +71768,8 @@ Examples:
|
|
|
71632
71768
|
|
|
71633
71769
|
Typical workflow: create \u2192 add-source \u2192 add-clip \u2192 trim-clip/split-clip \u2192 export
|
|
71634
71770
|
Cost: Free (no API keys needed).
|
|
71635
|
-
Run 'vibe schema timeline.<command>' for structured parameter info.`
|
|
71771
|
+
Run 'vibe schema timeline.<command>' for structured parameter info.`
|
|
71772
|
+
);
|
|
71636
71773
|
timelineCommand.command("create").description("Create a low-level timeline JSON file").argument("<name>", "Timeline name or path (e.g., 'my-video' or 'output/my-video')").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("--fps <fps>", "Frame rate", "30").option("--dry-run", "Preview parameters without executing").action(async (name, options) => {
|
|
71637
71774
|
await executeTimelineCreate(name, options, "timeline create", Date.now());
|
|
71638
71775
|
});
|
|
@@ -71843,7 +71980,10 @@ timelineCommand.command("add-track").description("Add a new track").argument("<p
|
|
|
71843
71980
|
exitWithError(generalError(`Failed to add track: ${msg}`));
|
|
71844
71981
|
}
|
|
71845
71982
|
});
|
|
71846
|
-
timelineCommand.command("add-effect").description("Add an effect to a clip").argument("<project>", "Timeline file or directory").argument("<clip-id>", "Clip ID").argument(
|
|
71983
|
+
timelineCommand.command("add-effect").description("Add an effect to a clip").argument("<project>", "Timeline file or directory").argument("<clip-id>", "Clip ID").argument(
|
|
71984
|
+
"<effect-type>",
|
|
71985
|
+
"Effect type (fadeIn, fadeOut, blur, brightness, contrast, saturation, speed, volume)"
|
|
71986
|
+
).option("--start <seconds>", "Effect start time (relative to clip)", "0").option("-d, --duration <seconds>", "Effect duration (defaults to clip duration)").option("--params <json>", "Effect parameters as JSON", "{}").option("--dry-run", "Preview parameters without executing").action(async (projectPath, clipId, effectType, options) => {
|
|
71847
71987
|
const startedAt = Date.now();
|
|
71848
71988
|
const spinner2 = ora("Adding effect...").start();
|
|
71849
71989
|
try {
|
|
@@ -72067,7 +72207,9 @@ timelineCommand.command("list").description("List timeline contents").argument("
|
|
|
72067
72207
|
for (const clip of clips) {
|
|
72068
72208
|
const source = project.getSource(clip.sourceId);
|
|
72069
72209
|
console.log(` ${source_default.yellow(clip.id)}`);
|
|
72070
|
-
console.log(
|
|
72210
|
+
console.log(
|
|
72211
|
+
` ${source?.name || "unknown"} @ ${clip.startTime}s (${clip.duration}s)`
|
|
72212
|
+
);
|
|
72071
72213
|
if (clip.effects.length > 0) {
|
|
72072
72214
|
console.log(` Effects: ${clip.effects.map((e) => e.type).join(", ")}`);
|
|
72073
72215
|
}
|
|
@@ -72327,10 +72469,10 @@ function detectMediaType(path2) {
|
|
|
72327
72469
|
return "video";
|
|
72328
72470
|
}
|
|
72329
72471
|
applyTiers(timelineCommand, {
|
|
72330
|
-
|
|
72331
|
-
|
|
72332
|
-
|
|
72333
|
-
|
|
72472
|
+
create: "free",
|
|
72473
|
+
info: "free",
|
|
72474
|
+
set: "free",
|
|
72475
|
+
list: "free",
|
|
72334
72476
|
"add-source": "free",
|
|
72335
72477
|
"add-clip": "free",
|
|
72336
72478
|
"add-track": "free",
|
|
@@ -79933,7 +80075,7 @@ starting from a media file and wants it transformed, it's REMIX.
|
|
|
79933
80075
|
| Add static captions | \`vibe edit caption in.mp4 -o out.mp4\` |
|
|
79934
80076
|
| Translate audio (transcribe \u2192 TTS) | \`vibe audio dub file.mp4 -t ko\` |
|
|
79935
80077
|
| Transcribe (Whisper, word-level) | \`vibe audio transcribe file.mp4 --granularity word\` |
|
|
79936
|
-
| Inspect
|
|
80078
|
+
| Inspect media with Gemini | \`vibe inspect media file.mp4 "summarise" --json\` |
|
|
79937
80079
|
|
|
79938
80080
|
### Compose pipelines (Video as Code)
|
|
79939
80081
|
|
|
@@ -79946,10 +80088,10 @@ starting from a media file and wants it transformed, it's REMIX.
|
|
|
79946
80088
|
|
|
79947
80089
|
| Tier | Examples | Approx. per call |
|
|
79948
80090
|
|---|---|---|
|
|
79949
|
-
| **Free** | \`detect *\`, \`
|
|
79950
|
-
| **Low** | \`
|
|
79951
|
-
| **High** | \`generate
|
|
79952
|
-
| **Very High** | \`remix
|
|
80091
|
+
| **Free** | \`schema/context/doctor\`, \`detect *\`, \`status *\`, \`plan\`, \`storyboard validate\`, \`inspect project/render --cheap\`, deterministic edits | $0 |
|
|
80092
|
+
| **Low** | \`generate narration/sound-effect/music\`, \`audio transcribe\`, \`inspect media\`, optional AI review | $0.01\u2013$0.10 |
|
|
80093
|
+
| **High** | \`generate image/motion\`, \`edit image/reframe/grade/speed-ramp\` | $1\u2013$5 |
|
|
80094
|
+
| **Very High** | \`generate video\`, \`edit fill-gaps\`, \`remix highlights/auto-shorts\`, \`build\` with generated assets | $5\u2013$50+ |
|
|
79953
80095
|
|
|
79954
80096
|
## Agent invariants
|
|
79955
80097
|
|
|
@@ -80122,7 +80264,7 @@ defaults:
|
|
|
80122
80264
|
# Optional \u2014 uncomment to set per-primitive provider preferences.
|
|
80123
80265
|
# providers:
|
|
80124
80266
|
# tts: elevenlabs # auto | elevenlabs | kokoro
|
|
80125
|
-
# image: openai # openai | gemini
|
|
80267
|
+
# image: openai # openai | gemini | grok
|
|
80126
80268
|
# music: elevenlabs
|
|
80127
80269
|
|
|
80128
80270
|
# Optional \u2014 cap total spend per pipeline run.
|
|
@@ -80735,11 +80877,12 @@ import { resolve as resolve58 } from "node:path";
|
|
|
80735
80877
|
var VALID_STAGES = ["assets", "compose", "sync", "render", "all"];
|
|
80736
80878
|
var VALID_MODES = ["agent", "batch", "auto"];
|
|
80737
80879
|
var VALID_TTS_PROVIDERS = ["auto", "elevenlabs", "kokoro"];
|
|
80880
|
+
var VALID_IMAGE_PROVIDERS = ["openai", "gemini", "grok"];
|
|
80738
80881
|
var VALID_VIDEO_PROVIDERS = ["seedance", "grok", "kling", "runway", "veo"];
|
|
80739
80882
|
var VALID_MUSIC_PROVIDERS = ["elevenlabs", "replicate"];
|
|
80740
80883
|
var VALID_COMPOSERS = ["claude", "openai", "gemini"];
|
|
80741
80884
|
var VALID_IMAGE_QUALITIES = ["standard", "hd"];
|
|
80742
|
-
var planCommand = new Command("plan").description("Read STORYBOARD.md and show build plan, costs, missing cues, and provider needs").argument("[project-dir]", "Video project directory", ".").option("--stage <stage>", `Stage to plan: ${VALID_STAGES.join("|")}`, "all").option("--beat <id>", "Restrict the plan to one beat").option("--mode <mode>", "Build mode: agent|batch|auto", "auto").option("--skip-narration", "Don't include narration generation in the plan").option("--skip-backdrop", "Don't include backdrop image generation in the plan").option("--skip-video", "Don't include video generation in the plan").option("--skip-music", "Don't include music generation in the plan").option("--tts <provider>", "TTS provider: auto|elevenlabs|kokoro").option("--voice <id>", "Voice id").option("--image-provider <name>",
|
|
80885
|
+
var planCommand = new Command("plan").description("Read STORYBOARD.md and show build plan, costs, missing cues, and provider needs").argument("[project-dir]", "Video project directory", ".").option("--stage <stage>", `Stage to plan: ${VALID_STAGES.join("|")}`, "all").option("--beat <id>", "Restrict the plan to one beat").option("--mode <mode>", "Build mode: agent|batch|auto", "auto").option("--skip-narration", "Don't include narration generation in the plan").option("--skip-backdrop", "Don't include backdrop image generation in the plan").option("--skip-video", "Don't include video generation in the plan").option("--skip-music", "Don't include music generation in the plan").option("--tts <provider>", "TTS provider: auto|elevenlabs|kokoro").option("--voice <id>", "Voice id").option("--image-provider <name>", `Image provider: ${VALID_IMAGE_PROVIDERS.join("|")}`).option("--video-provider <name>", `Video provider: ${VALID_VIDEO_PROVIDERS.join("|")}`).option("--music-provider <name>", `Music provider: ${VALID_MUSIC_PROVIDERS.join("|")}`).option("--quality <q>", "Image quality: standard|hd").option("--image-size <s>", "Image size: 1024x1024|1536x1024|1024x1536").option("--composer <provider>", "Batch composer: claude|openai|gemini").option("--force", "Plan regeneration even when outputs already exist").option("--max-cost <usd>", "Fail if estimated cost exceeds this USD cap").action(async (projectDirArg, options) => {
|
|
80743
80886
|
const startedAt = Date.now();
|
|
80744
80887
|
const stage = String(options.stage ?? "all");
|
|
80745
80888
|
if (!VALID_STAGES.includes(stage)) {
|
|
@@ -80762,6 +80905,11 @@ var planCommand = new Command("plan").description("Read STORYBOARD.md and show b
|
|
|
80762
80905
|
VALID_MUSIC_PROVIDERS,
|
|
80763
80906
|
"music"
|
|
80764
80907
|
);
|
|
80908
|
+
const imageProvider = parseOptionalProvider(
|
|
80909
|
+
options.imageProvider,
|
|
80910
|
+
VALID_IMAGE_PROVIDERS,
|
|
80911
|
+
"image"
|
|
80912
|
+
);
|
|
80765
80913
|
const composer = parseOptionalProvider(options.composer, VALID_COMPOSERS, "composer");
|
|
80766
80914
|
const imageQuality = parseOptionalProvider(options.quality, VALID_IMAGE_QUALITIES, "quality");
|
|
80767
80915
|
const maxCost = options.maxCost !== void 0 ? Number.parseFloat(String(options.maxCost)) : void 0;
|
|
@@ -80785,7 +80933,7 @@ var planCommand = new Command("plan").description("Read STORYBOARD.md and show b
|
|
|
80785
80933
|
skipMusic: options.skipMusic,
|
|
80786
80934
|
ttsProvider,
|
|
80787
80935
|
voice: options.voice,
|
|
80788
|
-
imageProvider
|
|
80936
|
+
imageProvider,
|
|
80789
80937
|
imageQuality,
|
|
80790
80938
|
imageSize: options.imageSize,
|
|
80791
80939
|
videoProvider,
|
|
@@ -80922,9 +81070,10 @@ import { resolve as resolve63, join as join23 } from "node:path";
|
|
|
80922
81070
|
import { readFileSync as readFileSync3, existsSync as existsSync47 } from "node:fs";
|
|
80923
81071
|
var VALID_MODES2 = ["agent", "batch", "auto"];
|
|
80924
81072
|
var VALID_STAGES2 = ["assets", "compose", "sync", "render", "all"];
|
|
81073
|
+
var VALID_IMAGE_PROVIDERS2 = ["openai", "gemini", "grok"];
|
|
80925
81074
|
var VALID_VIDEO_PROVIDERS2 = ["seedance", "grok", "kling", "runway", "veo"];
|
|
80926
81075
|
var VALID_MUSIC_PROVIDERS2 = ["elevenlabs", "replicate"];
|
|
80927
|
-
var buildCommand = new Command("build").description("Build a VibeFrame video project from STORYBOARD.md").argument("[project-dir]", "Video project directory", ".").option("--stage <stage>", `Build stage: ${VALID_STAGES2.join("|")}`, "all").option("--beat <id>", "Restrict asset/compose work to one beat id").option("--mode <mode>", "Build mode: agent|batch|auto", "auto").option("--effort <level>", "Compose effort tier (batch mode only): low|medium|high", "medium").option("--composer <provider>", "Batch composer: claude|openai|gemini").option("--max-cost <usd>", "Fail before provider spend when estimated cost exceeds this USD cap").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-video", "Don't dispatch video generation even when beats declare video cues").option("--skip-music", "Don't dispatch music generation even when beats declare music cues").option("--skip-render", "Compose only \u2014 don't render to MP4").option("--tts <provider>", "TTS provider: auto|elevenlabs|kokoro").option("--voice <id>", "Voice id").option("--image-provider <name>",
|
|
81076
|
+
var buildCommand = new Command("build").description("Build a VibeFrame video project from STORYBOARD.md").argument("[project-dir]", "Video project directory", ".").option("--stage <stage>", `Build stage: ${VALID_STAGES2.join("|")}`, "all").option("--beat <id>", "Restrict asset/compose work to one beat id").option("--mode <mode>", "Build mode: agent|batch|auto", "auto").option("--effort <level>", "Compose effort tier (batch mode only): low|medium|high", "medium").option("--composer <provider>", "Batch composer: claude|openai|gemini").option("--max-cost <usd>", "Fail before provider spend when estimated cost exceeds this USD cap").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-video", "Don't dispatch video generation even when beats declare video cues").option("--skip-music", "Don't dispatch music generation even when beats declare music cues").option("--skip-render", "Compose only \u2014 don't render to MP4").option("--tts <provider>", "TTS provider: auto|elevenlabs|kokoro").option("--voice <id>", "Voice id").option("--image-provider <name>", `Image provider: ${VALID_IMAGE_PROVIDERS2.join("|")}`).option("--video-provider <name>", `Video provider: ${VALID_VIDEO_PROVIDERS2.join("|")}`).option("--music-provider <name>", `Music provider: ${VALID_MUSIC_PROVIDERS2.join("|")}`).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").addHelpText(
|
|
80928
81077
|
"after",
|
|
80929
81078
|
`
|
|
80930
81079
|
Examples:
|
|
@@ -80967,6 +81116,11 @@ Advanced equivalent: \`vibe scene build\`.`
|
|
|
80967
81116
|
VALID_MUSIC_PROVIDERS2,
|
|
80968
81117
|
"music"
|
|
80969
81118
|
);
|
|
81119
|
+
const imageProvider = parseOptionalProvider2(
|
|
81120
|
+
options.imageProvider,
|
|
81121
|
+
VALID_IMAGE_PROVIDERS2,
|
|
81122
|
+
"image"
|
|
81123
|
+
);
|
|
80970
81124
|
const params = {
|
|
80971
81125
|
projectDir,
|
|
80972
81126
|
stage,
|
|
@@ -80982,7 +81136,7 @@ Advanced equivalent: \`vibe scene build\`.`
|
|
|
80982
81136
|
skipRender: options.skipRender ?? false,
|
|
80983
81137
|
ttsProvider: options.tts,
|
|
80984
81138
|
voice: options.voice,
|
|
80985
|
-
imageProvider
|
|
81139
|
+
imageProvider,
|
|
80986
81140
|
videoProvider,
|
|
80987
81141
|
musicProvider,
|
|
80988
81142
|
imageQuality: options.quality,
|
|
@@ -81001,7 +81155,7 @@ Advanced equivalent: \`vibe scene build\`.`
|
|
|
81001
81155
|
skipMusic: options.skipMusic,
|
|
81002
81156
|
ttsProvider: options.tts,
|
|
81003
81157
|
voice: options.voice,
|
|
81004
|
-
imageProvider
|
|
81158
|
+
imageProvider,
|
|
81005
81159
|
imageQuality: options.quality,
|
|
81006
81160
|
imageSize: options.imageSize,
|
|
81007
81161
|
videoProvider,
|
|
@@ -81092,7 +81246,7 @@ Advanced equivalent: \`vibe scene build\`.`
|
|
|
81092
81246
|
skipRender: options.skipRender,
|
|
81093
81247
|
ttsProvider: options.tts,
|
|
81094
81248
|
voice: options.voice,
|
|
81095
|
-
imageProvider
|
|
81249
|
+
imageProvider,
|
|
81096
81250
|
videoProvider,
|
|
81097
81251
|
musicProvider,
|
|
81098
81252
|
imageQuality: options.quality,
|
|
@@ -81813,7 +81967,7 @@ async function runDiagnostics() {
|
|
|
81813
81967
|
installed = false;
|
|
81814
81968
|
}
|
|
81815
81969
|
} else if (toolName === "chrome") {
|
|
81816
|
-
const { existsSync:
|
|
81970
|
+
const { existsSync: existsSync54 } = await import("node:fs");
|
|
81817
81971
|
const { homedir: homedir7 } = await import("node:os");
|
|
81818
81972
|
const { join: join26 } = await import("node:path");
|
|
81819
81973
|
const chromePaths = [
|
|
@@ -81826,7 +81980,7 @@ async function runDiagnostics() {
|
|
|
81826
81980
|
"/usr/bin/chromium",
|
|
81827
81981
|
"/usr/bin/chromium-browser"
|
|
81828
81982
|
];
|
|
81829
|
-
installed = chromePaths.some((p) => p &&
|
|
81983
|
+
installed = chromePaths.some((p) => p && existsSync54(p));
|
|
81830
81984
|
} else {
|
|
81831
81985
|
installed = commandExists(toolName);
|
|
81832
81986
|
}
|
|
@@ -81842,28 +81996,33 @@ async function runDiagnostics() {
|
|
|
81842
81996
|
configExists = true;
|
|
81843
81997
|
} catch {
|
|
81844
81998
|
}
|
|
81999
|
+
const cwd = process.cwd();
|
|
82000
|
+
const activeConfig = await loadConfig({ cwd });
|
|
81845
82001
|
const providers = {};
|
|
81846
82002
|
let readyCount = FREE_COMMANDS.length;
|
|
81847
82003
|
let totalCount = FREE_COMMANDS.length;
|
|
81848
82004
|
for (const [envVar, commands] of Object.entries(COMMAND_KEY_MAP)) {
|
|
82005
|
+
const providerName = Object.entries(PROVIDER_ENV_VARS).find(([, v]) => v === envVar)?.[0] ?? envVar;
|
|
82006
|
+
const configuredInConfig = Boolean(
|
|
82007
|
+
activeConfig?.providers[providerName]
|
|
82008
|
+
);
|
|
81849
82009
|
const configured = Boolean(
|
|
81850
|
-
process.env[envVar] || PROVIDER_ENV_ALIASES[envVar]?.map((alias) => process.env[alias]).find(Boolean)
|
|
82010
|
+
process.env[envVar] || PROVIDER_ENV_ALIASES[envVar]?.map((alias) => process.env[alias]).find(Boolean) || configuredInConfig
|
|
81851
82011
|
);
|
|
81852
|
-
const providerName = Object.entries(PROVIDER_ENV_VARS).find(([, v]) => v === envVar)?.[0] ?? envVar;
|
|
81853
82012
|
providers[providerName] = { envVar, configured, commands };
|
|
81854
82013
|
totalCount += commands.length;
|
|
81855
82014
|
if (configured) {
|
|
81856
82015
|
readyCount += commands.length;
|
|
81857
82016
|
}
|
|
81858
82017
|
}
|
|
81859
|
-
const cwd = process.cwd();
|
|
81860
82018
|
const projectFiles = ["AGENTS.md", "CLAUDE.md", "vibe.project.yaml"].map((rel) => ({
|
|
81861
82019
|
path: rel,
|
|
81862
82020
|
exists: existsSync48(resolve66(cwd, rel))
|
|
81863
82021
|
}));
|
|
81864
82022
|
const projectInitialized = projectFiles.some((f) => f.exists);
|
|
81865
|
-
const
|
|
81866
|
-
const
|
|
82023
|
+
const nearestProjectConfigPath = await findProjectConfigPath(cwd);
|
|
82024
|
+
const projectConfigPath = nearestProjectConfigPath ?? getProjectConfigPath(cwd);
|
|
82025
|
+
const projectConfigExists = Boolean(nearestProjectConfigPath);
|
|
81867
82026
|
const activeScope = await getActiveScope(cwd);
|
|
81868
82027
|
const hosts = detectAgentHosts();
|
|
81869
82028
|
const detectedNames = hosts.filter((h) => h.detected).map((h) => h.label);
|
|
@@ -82136,8 +82295,9 @@ async function runLiveKeyTests(results) {
|
|
|
82136
82295
|
return;
|
|
82137
82296
|
}
|
|
82138
82297
|
const { testKey: testKey2 } = await Promise.resolve().then(() => (init_key_live_test(), key_live_test_exports));
|
|
82298
|
+
const activeConfig = await loadConfig({ cwd: process.cwd() });
|
|
82139
82299
|
for (const [name, info] of configured) {
|
|
82140
|
-
const value = process.env[info.envVar] || PROVIDER_ENV_ALIASES[info.envVar]?.map((alias) => process.env[alias]).find(Boolean);
|
|
82300
|
+
const value = process.env[info.envVar] || PROVIDER_ENV_ALIASES[info.envVar]?.map((alias) => process.env[alias]).find(Boolean) || activeConfig?.providers[name];
|
|
82141
82301
|
if (!value) continue;
|
|
82142
82302
|
process.stdout.write(` ${name.padEnd(12)} `);
|
|
82143
82303
|
const result = await testKey2(name, value);
|
|
@@ -82378,11 +82538,21 @@ var demoCommand = new Command("demo").description("Run sample edits on a test vi
|
|
|
82378
82538
|
|
|
82379
82539
|
// src/commands/context.ts
|
|
82380
82540
|
init_esm();
|
|
82541
|
+
import { existsSync as existsSync50 } from "node:fs";
|
|
82381
82542
|
import { readFile as readFile44 } from "node:fs/promises";
|
|
82382
82543
|
import { resolve as resolve68, dirname as dirname23 } from "node:path";
|
|
82383
82544
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
82384
82545
|
var __filename = fileURLToPath2(import.meta.url);
|
|
82385
82546
|
var __dirname = dirname23(__filename);
|
|
82547
|
+
function resolveContextPath(baseDir = __dirname) {
|
|
82548
|
+
const candidates = [
|
|
82549
|
+
// Bundled package layout: packages/cli/dist/index.js -> packages/cli/CONTEXT.md
|
|
82550
|
+
resolve68(baseDir, "../CONTEXT.md"),
|
|
82551
|
+
// Source/dev layout: packages/cli/src/commands/context.ts -> packages/cli/CONTEXT.md
|
|
82552
|
+
resolve68(baseDir, "../../CONTEXT.md")
|
|
82553
|
+
];
|
|
82554
|
+
return candidates.find((candidate) => existsSync50(candidate)) ?? null;
|
|
82555
|
+
}
|
|
82386
82556
|
var contextCommand = new Command("context").description("Print CLI context/guidelines for AI agent integration").option("--format <format>", "Output format: markdown | json", "markdown").action(async (_options, cmd) => {
|
|
82387
82557
|
const options = {
|
|
82388
82558
|
json: cmd.parent?.opts()?.json || cmd.opts()?.json || cmd.opts()?.format === "json",
|
|
@@ -82468,8 +82638,9 @@ var contextCommand = new Command("context").description("Print CLI context/guide
|
|
|
82468
82638
|
console.log(JSON.stringify(contract, null, 2));
|
|
82469
82639
|
return;
|
|
82470
82640
|
}
|
|
82471
|
-
const contextPath = resolve68(__dirname, "../CONTEXT.md");
|
|
82472
82641
|
try {
|
|
82642
|
+
const contextPath = resolveContextPath();
|
|
82643
|
+
if (!contextPath) throw new Error("CONTEXT.md not found");
|
|
82473
82644
|
const content = await readFile44(contextPath, "utf-8");
|
|
82474
82645
|
console.log(
|
|
82475
82646
|
content.replace(
|
|
@@ -82561,7 +82732,7 @@ Use 'vibe schema <command> --json' to get parameter schemas.
|
|
|
82561
82732
|
Use 'vibe doctor --json' to check configured API keys.
|
|
82562
82733
|
Use '--dry-run --json' before any mutating/costly operation.
|
|
82563
82734
|
|
|
82564
|
-
Cost tiers: Free (detect
|
|
82735
|
+
Cost tiers: Free (schema/context/doctor/detect/status/plan/storyboard validate/inspect project/render --cheap, deterministic edits) | Low (generate narration/sound-effect/music, audio transcribe, inspect media, optional AI review) | High (generate image/motion, edit image/reframe/grade/speed-ramp) | Very High (generate video, edit fill-gaps, remix highlights/auto-shorts, build with generated assets)
|
|
82565
82736
|
|
|
82566
82737
|
Group \u2192 MCP tool name: '<group>_<leaf>' (snake_case). Bare top-level (init/build/render/run) maps to bare MCP names.
|
|
82567
82738
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibeframe/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.103.0",
|
|
4
4
|
"description": "VibeFrame CLI - natural language video editing from the terminal",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
@@ -183,8 +183,8 @@
|
|
|
183
183
|
"tsx": "^4.21.0",
|
|
184
184
|
"typescript": "^5.3.3",
|
|
185
185
|
"vitest": "^1.2.2",
|
|
186
|
-
"@vibeframe/ai-providers": "0.
|
|
187
|
-
"@vibeframe/core": "0.
|
|
186
|
+
"@vibeframe/ai-providers": "0.103.0",
|
|
187
|
+
"@vibeframe/core": "0.103.0"
|
|
188
188
|
},
|
|
189
189
|
"engines": {
|
|
190
190
|
"node": ">=20"
|