@vibeframe/cli 0.102.0 → 0.103.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTEXT.md +4 -4
- package/dist/index.js +293 -104
- 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
|
}
|
|
@@ -32763,6 +32811,19 @@ var init_open_url = __esm({
|
|
|
32763
32811
|
// src/utils/tty.ts
|
|
32764
32812
|
import { createInterface as createInterface2 } from "node:readline";
|
|
32765
32813
|
import { ReadStream } from "node:tty";
|
|
32814
|
+
function stripAnsi2(value) {
|
|
32815
|
+
return value.replace(ANSI_RE, "");
|
|
32816
|
+
}
|
|
32817
|
+
function terminalColumns() {
|
|
32818
|
+
return process.stdout.columns && process.stdout.columns > 0 ? process.stdout.columns : 80;
|
|
32819
|
+
}
|
|
32820
|
+
function fitOptionToLine(value, prefixColumns) {
|
|
32821
|
+
const maxColumns = Math.max(8, terminalColumns() - prefixColumns - 1);
|
|
32822
|
+
const visible = stripAnsi2(value);
|
|
32823
|
+
if (visible.length <= maxColumns) return value;
|
|
32824
|
+
if (maxColumns <= ELLIPSIS.length) return ELLIPSIS.slice(0, maxColumns);
|
|
32825
|
+
return `${visible.slice(0, maxColumns - ELLIPSIS.length)}${ELLIPSIS}`;
|
|
32826
|
+
}
|
|
32766
32827
|
function splitTTYInput(chunk) {
|
|
32767
32828
|
const tokens = [];
|
|
32768
32829
|
for (let i = 0; i < chunk.length; i++) {
|
|
@@ -32883,7 +32944,8 @@ async function promptSelect(_question, options, defaultIndex = 0, opts = {}) {
|
|
|
32883
32944
|
}
|
|
32884
32945
|
for (let i = 0; i < options.length; i++) {
|
|
32885
32946
|
const marker = i === selected ? "\x1B[36m\u276F\x1B[0m" : " ";
|
|
32886
|
-
const
|
|
32947
|
+
const optionText = fitOptionToLine(options[i], 5);
|
|
32948
|
+
const text = i === selected ? `\x1B[1m${optionText}\x1B[0m` : optionText;
|
|
32887
32949
|
process.stdout.write(`\x1B[2K ${marker} ${text}
|
|
32888
32950
|
`);
|
|
32889
32951
|
}
|
|
@@ -32962,7 +33024,8 @@ async function promptConfirm(question, defaultYes = true) {
|
|
|
32962
33024
|
}
|
|
32963
33025
|
for (let i = 0; i < options.length; i++) {
|
|
32964
33026
|
const marker = i === selected ? "\x1B[36m\u276F\x1B[0m" : " ";
|
|
32965
|
-
const
|
|
33027
|
+
const optionText = fitOptionToLine(options[i], 5);
|
|
33028
|
+
const text = i === selected ? `\x1B[1m${optionText}\x1B[0m` : optionText;
|
|
32966
33029
|
process.stdout.write(`\x1B[2K ${marker} ${text}
|
|
32967
33030
|
`);
|
|
32968
33031
|
}
|
|
@@ -33030,7 +33093,8 @@ async function promptMultiSelect(_question, options, defaultSelected = [], opts
|
|
|
33030
33093
|
for (let i = 0; i < options.length; i++) {
|
|
33031
33094
|
const pointer = i === cursor ? "\x1B[36m\u276F\x1B[0m" : " ";
|
|
33032
33095
|
const box = selected[i] ? "\x1B[36m[x]\x1B[0m" : "[ ]";
|
|
33033
|
-
const
|
|
33096
|
+
const optionText = fitOptionToLine(options[i], 9);
|
|
33097
|
+
const text = i === cursor ? `\x1B[1m${optionText}\x1B[0m` : optionText;
|
|
33034
33098
|
process.stdout.write(`\x1B[2K ${pointer} ${box} ${text}
|
|
33035
33099
|
`);
|
|
33036
33100
|
}
|
|
@@ -33122,12 +33186,14 @@ async function promptMultiSelect(_question, options, defaultSelected = [], opts
|
|
|
33122
33186
|
}
|
|
33123
33187
|
return [...picked].sort((a, b) => a - b);
|
|
33124
33188
|
}
|
|
33125
|
-
var ttyStream;
|
|
33189
|
+
var ttyStream, ANSI_RE, ELLIPSIS;
|
|
33126
33190
|
var init_tty = __esm({
|
|
33127
33191
|
"src/utils/tty.ts"() {
|
|
33128
33192
|
"use strict";
|
|
33129
33193
|
init_open_url();
|
|
33130
33194
|
ttyStream = null;
|
|
33195
|
+
ANSI_RE = new RegExp(`${String.fromCharCode(27)}\\[[0-?]*[ -/]*[@-~]`, "g");
|
|
33196
|
+
ELLIPSIS = "...";
|
|
33131
33197
|
}
|
|
33132
33198
|
});
|
|
33133
33199
|
|
|
@@ -34891,6 +34957,7 @@ function isActiveStatus(status) {
|
|
|
34891
34957
|
}
|
|
34892
34958
|
function providerStatusCommand(record) {
|
|
34893
34959
|
if (record.jobType === "generate-video") {
|
|
34960
|
+
if (record.provider !== "runway" && record.provider !== "kling") return void 0;
|
|
34894
34961
|
const type = record.provider === "kling" && record.providerTaskType ? ` --type ${record.providerTaskType}` : "";
|
|
34895
34962
|
return `vibe generate video-status ${record.providerTaskId} -p ${record.provider}${type} --json`;
|
|
34896
34963
|
}
|
|
@@ -58194,9 +58261,22 @@ function backdropCacheDescriptor(opts) {
|
|
|
58194
58261
|
provider: opts.provider,
|
|
58195
58262
|
quality: opts.quality,
|
|
58196
58263
|
size: opts.size,
|
|
58264
|
+
ratio: opts.ratio,
|
|
58197
58265
|
ext: "png"
|
|
58198
58266
|
});
|
|
58199
58267
|
}
|
|
58268
|
+
function imageRatioForSize(size) {
|
|
58269
|
+
switch (size) {
|
|
58270
|
+
case "1024x1024":
|
|
58271
|
+
return "1:1";
|
|
58272
|
+
case "1024x1536":
|
|
58273
|
+
return "2:3";
|
|
58274
|
+
case "1536x1024":
|
|
58275
|
+
return "3:2";
|
|
58276
|
+
default:
|
|
58277
|
+
return "16:9";
|
|
58278
|
+
}
|
|
58279
|
+
}
|
|
58200
58280
|
function videoCacheDescriptor(opts) {
|
|
58201
58281
|
return cacheAssetDescriptor("video", {
|
|
58202
58282
|
beatId: opts.beatId,
|
|
@@ -58274,6 +58354,7 @@ async function createBuildPlan(opts) {
|
|
|
58274
58354
|
}
|
|
58275
58355
|
const storyboardMd = await readFile40(storyboardPath2, "utf-8");
|
|
58276
58356
|
const validation = validateStoryboardMarkdown(storyboardMd);
|
|
58357
|
+
const validationIssues = [...validation.issues];
|
|
58277
58358
|
const parsed = parseStoryboard(storyboardMd);
|
|
58278
58359
|
let sourceBeats = parsed.beats;
|
|
58279
58360
|
if (opts.beat) {
|
|
@@ -58296,6 +58377,7 @@ async function createBuildPlan(opts) {
|
|
|
58296
58377
|
const mode = opts.mode ?? config2.config.build.mode;
|
|
58297
58378
|
const imageQuality = opts.imageQuality ?? config2.config.build.imageQuality ?? "hd";
|
|
58298
58379
|
const imageSize2 = opts.imageSize ?? config2.config.build.imageSize ?? "1536x1024";
|
|
58380
|
+
const imageRatio = imageRatioForSize(imageSize2);
|
|
58299
58381
|
const needsComposer = includeCompose && mode !== "agent" && sourceBeats.some((beat) => !existsSync44(join20(projectDir, `compositions/scene-${beat.id}.html`)));
|
|
58300
58382
|
const resolved = await resolvePlanProviders({
|
|
58301
58383
|
projectDir,
|
|
@@ -58345,7 +58427,8 @@ async function createBuildPlan(opts) {
|
|
|
58345
58427
|
cue: backdropPrompt,
|
|
58346
58428
|
provider: resolved.image.resolved,
|
|
58347
58429
|
quality: imageQuality,
|
|
58348
|
-
size: imageSize2
|
|
58430
|
+
size: imageSize2,
|
|
58431
|
+
ratio: imageRatio
|
|
58349
58432
|
}) : null;
|
|
58350
58433
|
const videoCache = videoPrompt && !videoReference ? videoCacheDescriptor({
|
|
58351
58434
|
beatId: beat.id,
|
|
@@ -58439,7 +58522,8 @@ async function createBuildPlan(opts) {
|
|
|
58439
58522
|
}
|
|
58440
58523
|
}
|
|
58441
58524
|
if (narration?.willGenerate) noteProviderNeed(resolved.narration, "Narration");
|
|
58442
|
-
if (backdrop?.willGenerate
|
|
58525
|
+
if (backdrop?.willGenerate && isSupportedBuildImageProvider(resolved.image.resolved))
|
|
58526
|
+
noteProviderNeed(resolved.image, "Backdrop generation");
|
|
58443
58527
|
if (video?.willGenerate) noteProviderNeed(resolved.video, "Video generation");
|
|
58444
58528
|
if (music?.willGenerate) noteProviderNeed(resolved.music, "Music generation");
|
|
58445
58529
|
if (!compositionExists) missing.add("compositions");
|
|
@@ -58461,23 +58545,35 @@ async function createBuildPlan(opts) {
|
|
|
58461
58545
|
};
|
|
58462
58546
|
});
|
|
58463
58547
|
providerResolution.push(...providerResolutionsForPlan(resolved, beats, opts, includeAssets));
|
|
58548
|
+
if (includeAssets && !opts.skipBackdrop && !isSupportedBuildImageProvider(resolved.image.resolved)) {
|
|
58549
|
+
const unsupportedBackdropBeats = beats.filter(
|
|
58550
|
+
(beat) => isBuildGeneratedBackdropPlan(beat.assets.backdrop)
|
|
58551
|
+
);
|
|
58552
|
+
for (const beat of unsupportedBackdropBeats) {
|
|
58553
|
+
validationIssues.push({
|
|
58554
|
+
severity: "error",
|
|
58555
|
+
code: "UNSUPPORTED_BUILD_IMAGE_PROVIDER",
|
|
58556
|
+
beatId: beat.id,
|
|
58557
|
+
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.`
|
|
58558
|
+
});
|
|
58559
|
+
}
|
|
58560
|
+
if (unsupportedBackdropBeats.length > 0) {
|
|
58561
|
+
const beatFlag = opts.beat ? ` --beat ${opts.beat}` : "";
|
|
58562
|
+
retryWith.push(
|
|
58563
|
+
`vibe build ${projectDir}${beatFlag} --stage assets --image-provider openai --json`
|
|
58564
|
+
);
|
|
58565
|
+
}
|
|
58566
|
+
}
|
|
58567
|
+
const validationOk = !validationIssues.some((issue) => issue.severity === "error");
|
|
58464
58568
|
if (!existsSync44(join20(projectDir, config2.config.composition.entry))) {
|
|
58465
58569
|
missing.add("root-composition");
|
|
58466
|
-
if (
|
|
58570
|
+
if (validationOk) retryWith.push(`vibe build ${projectDir} --stage sync --json`);
|
|
58467
58571
|
}
|
|
58468
58572
|
if (config2.legacy) {
|
|
58469
58573
|
warnings.push(
|
|
58470
58574
|
`Using legacy ${config2.source}; write ${projectDir}/vibe.config.json to use the TO-BE project contract.`
|
|
58471
58575
|
);
|
|
58472
58576
|
}
|
|
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
58577
|
if (!validation.ok) {
|
|
58482
58578
|
retryWith.push(
|
|
58483
58579
|
`vibe storyboard validate ${projectDir} --json`,
|
|
@@ -58488,7 +58584,7 @@ async function createBuildPlan(opts) {
|
|
|
58488
58584
|
projectDir,
|
|
58489
58585
|
config: config2,
|
|
58490
58586
|
stage,
|
|
58491
|
-
status:
|
|
58587
|
+
status: validationOk ? "ready" : "invalid",
|
|
58492
58588
|
currentStage: stage,
|
|
58493
58589
|
mode,
|
|
58494
58590
|
beat: opts.beat ?? null,
|
|
@@ -58500,8 +58596,8 @@ async function createBuildPlan(opts) {
|
|
|
58500
58596
|
warnings,
|
|
58501
58597
|
retryWith,
|
|
58502
58598
|
validation: {
|
|
58503
|
-
ok:
|
|
58504
|
-
issues:
|
|
58599
|
+
ok: validationOk,
|
|
58600
|
+
issues: validationIssues
|
|
58505
58601
|
}
|
|
58506
58602
|
});
|
|
58507
58603
|
}
|
|
@@ -58528,6 +58624,15 @@ function finalizeBuildPlan(plan) {
|
|
|
58528
58624
|
retryWith: unique3(plan.retryWith)
|
|
58529
58625
|
};
|
|
58530
58626
|
}
|
|
58627
|
+
function isBuildGeneratedBackdropPlan(asset) {
|
|
58628
|
+
if (!asset || asset.kind !== "backdrop") return false;
|
|
58629
|
+
return !["referenced-asset", "invalid-reference", "stage-skipped"].includes(asset.reason);
|
|
58630
|
+
}
|
|
58631
|
+
function isSupportedBuildImageProvider(provider) {
|
|
58632
|
+
return SUPPORTED_BUILD_IMAGE_PROVIDERS.includes(
|
|
58633
|
+
provider
|
|
58634
|
+
);
|
|
58635
|
+
}
|
|
58531
58636
|
function nextCommandsForPlan(plan) {
|
|
58532
58637
|
if (plan.status === "invalid") return unique3(plan.retryWith);
|
|
58533
58638
|
const commands = [];
|
|
@@ -58827,7 +58932,7 @@ function loadPlanEnv(projectDir) {
|
|
|
58827
58932
|
dir = dirname19(dir);
|
|
58828
58933
|
}
|
|
58829
58934
|
}
|
|
58830
|
-
var import_dotenv3, BACKDROP_COST_USD, VIDEO_COST_USD, MUSIC_COST_USD, ELEVENLABS_NARRATION_COST_USD, COMPOSE_COST_USD;
|
|
58935
|
+
var import_dotenv3, BACKDROP_COST_USD, VIDEO_COST_USD, MUSIC_COST_USD, ELEVENLABS_NARRATION_COST_USD, COMPOSE_COST_USD, SUPPORTED_BUILD_IMAGE_PROVIDERS;
|
|
58831
58936
|
var init_build_plan = __esm({
|
|
58832
58937
|
"src/commands/_shared/build-plan.ts"() {
|
|
58833
58938
|
"use strict";
|
|
@@ -58846,6 +58951,7 @@ var init_build_plan = __esm({
|
|
|
58846
58951
|
MUSIC_COST_USD = 0.5;
|
|
58847
58952
|
ELEVENLABS_NARRATION_COST_USD = 0.05;
|
|
58848
58953
|
COMPOSE_COST_USD = 0.06;
|
|
58954
|
+
SUPPORTED_BUILD_IMAGE_PROVIDERS = ["openai", "gemini", "grok"];
|
|
58849
58955
|
}
|
|
58850
58956
|
});
|
|
58851
58957
|
|
|
@@ -60252,19 +60358,18 @@ async function dispatchBackdrop(beat, ctx) {
|
|
|
60252
60358
|
if (reference) return referencePrimitiveOutcome("backdrop", beat, ctx, reference);
|
|
60253
60359
|
const prompt3 = stringOrUndefined4(beat.cues?.backdrop);
|
|
60254
60360
|
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
60361
|
const rel = `assets/backdrop-${beat.id}.png`;
|
|
60261
60362
|
const abs = join22(ctx.projectDir, rel);
|
|
60363
|
+
const size = ctx.imageSize ?? "1536x1024";
|
|
60364
|
+
const ratio = imageRatioForSize(size);
|
|
60365
|
+
const metadataOptions = { quality: ctx.imageQuality, size, ratio };
|
|
60262
60366
|
const cache = backdropCacheDescriptor({
|
|
60263
60367
|
beatId: beat.id,
|
|
60264
60368
|
cue: prompt3,
|
|
60265
60369
|
provider: ctx.imageProvider,
|
|
60266
60370
|
quality: ctx.imageQuality,
|
|
60267
|
-
size
|
|
60371
|
+
size,
|
|
60372
|
+
ratio
|
|
60268
60373
|
});
|
|
60269
60374
|
const metadataPath = assetMetadataPath("backdrop", beat.id);
|
|
60270
60375
|
if (existsSync46(abs) && !ctx.force) {
|
|
@@ -60275,7 +60380,7 @@ async function dispatchBackdrop(beat, ctx) {
|
|
|
60275
60380
|
beatId: beat.id,
|
|
60276
60381
|
cue: prompt3,
|
|
60277
60382
|
provider: ctx.imageProvider,
|
|
60278
|
-
options:
|
|
60383
|
+
options: metadataOptions,
|
|
60279
60384
|
cacheKey: cache.key
|
|
60280
60385
|
})) {
|
|
60281
60386
|
ctx.onProgress({ type: "backdrop-cached", beatId: beat.id, path: rel });
|
|
@@ -60300,7 +60405,7 @@ async function dispatchBackdrop(beat, ctx) {
|
|
|
60300
60405
|
beatId: beat.id,
|
|
60301
60406
|
cue: prompt3,
|
|
60302
60407
|
provider: ctx.imageProvider,
|
|
60303
|
-
options:
|
|
60408
|
+
options: metadataOptions,
|
|
60304
60409
|
cacheKey: cache.key,
|
|
60305
60410
|
canonicalPath: rel,
|
|
60306
60411
|
cachePath: cache.path
|
|
@@ -60316,27 +60421,14 @@ async function dispatchBackdrop(beat, ctx) {
|
|
|
60316
60421
|
freshness: "fresh"
|
|
60317
60422
|
};
|
|
60318
60423
|
}
|
|
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";
|
|
60424
|
+
const generated = await generateBackdropImage(prompt3, ctx, ratio);
|
|
60425
|
+
if (!generated.success) {
|
|
60426
|
+
const error = generated.error;
|
|
60335
60427
|
ctx.onProgress({ type: "backdrop-failed", beatId: beat.id, error });
|
|
60336
60428
|
return { status: "failed", error };
|
|
60337
60429
|
}
|
|
60338
60430
|
await mkdir24(dirname22(abs), { recursive: true });
|
|
60339
|
-
const buffer =
|
|
60431
|
+
const buffer = generated.buffer;
|
|
60340
60432
|
await writeFile48(abs, buffer);
|
|
60341
60433
|
await mkdir24(dirname22(cacheAbs), { recursive: true });
|
|
60342
60434
|
await writeFile48(cacheAbs, buffer);
|
|
@@ -60345,8 +60437,8 @@ async function dispatchBackdrop(beat, ctx) {
|
|
|
60345
60437
|
kind: "backdrop",
|
|
60346
60438
|
beatId: beat.id,
|
|
60347
60439
|
cue: prompt3,
|
|
60348
|
-
provider:
|
|
60349
|
-
options:
|
|
60440
|
+
provider: ctx.imageProvider,
|
|
60441
|
+
options: metadataOptions,
|
|
60350
60442
|
cacheKey: cache.key,
|
|
60351
60443
|
canonicalPath: rel,
|
|
60352
60444
|
cachePath: cache.path
|
|
@@ -60355,18 +60447,75 @@ async function dispatchBackdrop(beat, ctx) {
|
|
|
60355
60447
|
type: "backdrop-generated",
|
|
60356
60448
|
beatId: beat.id,
|
|
60357
60449
|
path: rel,
|
|
60358
|
-
provider:
|
|
60450
|
+
provider: ctx.imageProvider
|
|
60359
60451
|
});
|
|
60360
60452
|
return {
|
|
60361
60453
|
status: "generated",
|
|
60362
60454
|
path: rel,
|
|
60363
|
-
provider:
|
|
60455
|
+
provider: ctx.imageProvider,
|
|
60364
60456
|
cachePath: cache.path,
|
|
60365
60457
|
cacheKey: cache.key,
|
|
60366
60458
|
metadataPath,
|
|
60367
60459
|
freshness: "fresh"
|
|
60368
60460
|
};
|
|
60369
60461
|
}
|
|
60462
|
+
async function generateBackdropImage(prompt3, ctx, ratio) {
|
|
60463
|
+
loadSceneBuildEnv(ctx.projectDir);
|
|
60464
|
+
const keyInfo = imageProviderKeyInfo(ctx.imageProvider);
|
|
60465
|
+
const apiKey = await getApiKeyFromConfig(keyInfo.configKey, { cwd: ctx.projectDir }) ?? process.env[keyInfo.envVar] ?? "";
|
|
60466
|
+
if (!apiKey) {
|
|
60467
|
+
return {
|
|
60468
|
+
success: false,
|
|
60469
|
+
error: `${keyInfo.envVar} not set \u2014 cannot dispatch backdrop with ${ctx.imageProvider}`
|
|
60470
|
+
};
|
|
60471
|
+
}
|
|
60472
|
+
if (ctx.imageProvider === "openai") {
|
|
60473
|
+
const provider2 = new OpenAIImageProvider();
|
|
60474
|
+
await provider2.initialize({ apiKey });
|
|
60475
|
+
const result2 = await provider2.generateImage(prompt3, {
|
|
60476
|
+
model: "gpt-image-2",
|
|
60477
|
+
size: ctx.imageSize,
|
|
60478
|
+
quality: ctx.imageQuality
|
|
60479
|
+
});
|
|
60480
|
+
return imageBufferFromResult(result2);
|
|
60481
|
+
}
|
|
60482
|
+
if (ctx.imageProvider === "gemini") {
|
|
60483
|
+
const provider2 = new GeminiProvider();
|
|
60484
|
+
await provider2.initialize({ apiKey });
|
|
60485
|
+
const result2 = await provider2.generateImage(prompt3, {
|
|
60486
|
+
model: "flash",
|
|
60487
|
+
aspectRatio: ratio
|
|
60488
|
+
});
|
|
60489
|
+
return imageBufferFromResult(result2);
|
|
60490
|
+
}
|
|
60491
|
+
const provider = new GrokProvider();
|
|
60492
|
+
await provider.initialize({ apiKey });
|
|
60493
|
+
const result = await provider.generateImage(prompt3, {
|
|
60494
|
+
aspectRatio: ratio,
|
|
60495
|
+
n: 1
|
|
60496
|
+
});
|
|
60497
|
+
return imageBufferFromResult(result);
|
|
60498
|
+
}
|
|
60499
|
+
async function imageBufferFromResult(result) {
|
|
60500
|
+
const image = result.images?.[0];
|
|
60501
|
+
if (!result.success || !image) {
|
|
60502
|
+
return { success: false, error: result.error ?? "no image data returned" };
|
|
60503
|
+
}
|
|
60504
|
+
if (image.base64) return { success: true, buffer: Buffer.from(image.base64, "base64") };
|
|
60505
|
+
if (image.url) {
|
|
60506
|
+
const response = await fetch(image.url);
|
|
60507
|
+
if (!response.ok) {
|
|
60508
|
+
return { success: false, error: `failed to download image: HTTP ${response.status}` };
|
|
60509
|
+
}
|
|
60510
|
+
return { success: true, buffer: Buffer.from(await response.arrayBuffer()) };
|
|
60511
|
+
}
|
|
60512
|
+
return { success: false, error: "no image data returned" };
|
|
60513
|
+
}
|
|
60514
|
+
function imageProviderKeyInfo(provider) {
|
|
60515
|
+
if (provider === "gemini") return { configKey: "google", envVar: "GOOGLE_API_KEY" };
|
|
60516
|
+
if (provider === "grok") return { configKey: "xai", envVar: "XAI_API_KEY" };
|
|
60517
|
+
return { configKey: "openai", envVar: "OPENAI_API_KEY" };
|
|
60518
|
+
}
|
|
60370
60519
|
async function dispatchVideo(beat, ctx) {
|
|
60371
60520
|
const reference = assetReferenceForBeat(ctx.projectDir, "video", beat);
|
|
60372
60521
|
if (reference) return referencePrimitiveOutcome("video", beat, ctx, reference);
|
|
@@ -61265,14 +61414,14 @@ __export(ai_audio_exports, {
|
|
|
61265
61414
|
});
|
|
61266
61415
|
import { resolve as resolve69, dirname as dirname24, basename as basename22, extname as extname15 } from "node:path";
|
|
61267
61416
|
import { readFile as readFile45, writeFile as writeFile49 } from "node:fs/promises";
|
|
61268
|
-
import { existsSync as
|
|
61417
|
+
import { existsSync as existsSync51 } from "node:fs";
|
|
61269
61418
|
async function executeTranscribe(options) {
|
|
61270
61419
|
const { audioPath, language, output, format, apiKey } = options;
|
|
61271
61420
|
try {
|
|
61272
61421
|
const key = apiKey || process.env.OPENAI_API_KEY;
|
|
61273
61422
|
if (!key) return { success: false, error: "OPENAI_API_KEY required" };
|
|
61274
61423
|
const absPath = resolve69(process.cwd(), audioPath);
|
|
61275
|
-
if (!
|
|
61424
|
+
if (!existsSync51(absPath)) return { success: false, error: `File not found: ${absPath}` };
|
|
61276
61425
|
const whisper = new WhisperProvider();
|
|
61277
61426
|
await whisper.initialize({ apiKey: key });
|
|
61278
61427
|
const audioBuffer = await readFile45(absPath);
|
|
@@ -61305,7 +61454,7 @@ async function executeIsolate(options) {
|
|
|
61305
61454
|
const key = apiKey || process.env.ELEVENLABS_API_KEY;
|
|
61306
61455
|
if (!key) return { success: false, error: "ELEVENLABS_API_KEY required" };
|
|
61307
61456
|
const absPath = resolve69(process.cwd(), audioPath);
|
|
61308
|
-
if (!
|
|
61457
|
+
if (!existsSync51(absPath)) return { success: false, error: `File not found: ${absPath}` };
|
|
61309
61458
|
const audioBuffer = await readFile45(absPath);
|
|
61310
61459
|
const elevenlabs = new ElevenLabsProvider();
|
|
61311
61460
|
await elevenlabs.initialize({ apiKey: key });
|
|
@@ -61331,7 +61480,7 @@ async function executeVoiceClone(options) {
|
|
|
61331
61480
|
const audioBuffers = [];
|
|
61332
61481
|
for (const samplePath of samplePaths) {
|
|
61333
61482
|
const absPath = resolve69(process.cwd(), samplePath);
|
|
61334
|
-
if (!
|
|
61483
|
+
if (!existsSync51(absPath)) return { success: false, error: `File not found: ${samplePath}` };
|
|
61335
61484
|
const buffer = await readFile45(absPath);
|
|
61336
61485
|
audioBuffers.push(buffer);
|
|
61337
61486
|
}
|
|
@@ -61353,7 +61502,7 @@ async function executeDub(options) {
|
|
|
61353
61502
|
const { mediaPath, language, source, voice, analyzeOnly, output } = options;
|
|
61354
61503
|
try {
|
|
61355
61504
|
const absPath = resolve69(process.cwd(), mediaPath);
|
|
61356
|
-
if (!
|
|
61505
|
+
if (!existsSync51(absPath)) return { success: false, error: `File not found: ${absPath}` };
|
|
61357
61506
|
const openaiKey = process.env.OPENAI_API_KEY;
|
|
61358
61507
|
const anthropicKey = process.env.ANTHROPIC_API_KEY;
|
|
61359
61508
|
const elevenlabsKey = process.env.ELEVENLABS_API_KEY;
|
|
@@ -61481,8 +61630,8 @@ async function executeDuck(options) {
|
|
|
61481
61630
|
if (!commandExists("ffmpeg")) return { success: false, error: "FFmpeg not found" };
|
|
61482
61631
|
const absMusicPath = resolve69(process.cwd(), musicPath);
|
|
61483
61632
|
const absVoicePath = resolve69(process.cwd(), voicePath);
|
|
61484
|
-
if (!
|
|
61485
|
-
if (!
|
|
61633
|
+
if (!existsSync51(absMusicPath)) return { success: false, error: `Music file not found: ${absMusicPath}` };
|
|
61634
|
+
if (!existsSync51(absVoicePath)) return { success: false, error: `Voice file not found: ${absVoicePath}` };
|
|
61486
61635
|
const defaultOutput = resolve69(dirname24(absMusicPath), `${basename22(absMusicPath, extname15(absMusicPath))}-ducked${extname15(absMusicPath)}`);
|
|
61487
61636
|
const outputPath = resolve69(process.cwd(), output || defaultOutput);
|
|
61488
61637
|
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 +61645,7 @@ async function executeDuck(options) {
|
|
|
61496
61645
|
"-y",
|
|
61497
61646
|
outputPath
|
|
61498
61647
|
], { timeout: 12e4, maxBuffer: 50 * 1024 * 1024 });
|
|
61499
|
-
if (!
|
|
61648
|
+
if (!existsSync51(outputPath)) return { success: false, error: "FFmpeg failed to create output" };
|
|
61500
61649
|
return { success: true, outputPath };
|
|
61501
61650
|
} catch (error) {
|
|
61502
61651
|
return { success: false, error: `Audio ducking failed: ${error instanceof Error ? error.message : String(error)}` };
|
|
@@ -61514,7 +61663,7 @@ var init_ai_audio = __esm({
|
|
|
61514
61663
|
// src/pipeline/executor.ts
|
|
61515
61664
|
import { resolve as resolve70 } from "node:path";
|
|
61516
61665
|
import { readFile as readFile46, writeFile as writeFile50, mkdir as mkdir26 } from "node:fs/promises";
|
|
61517
|
-
import { existsSync as
|
|
61666
|
+
import { existsSync as existsSync52 } from "node:fs";
|
|
61518
61667
|
function maxCostFor(action) {
|
|
61519
61668
|
const cmd = ACTION_METADATA[action]?.command;
|
|
61520
61669
|
if (!cmd) return 0;
|
|
@@ -61829,7 +61978,7 @@ async function ensureActionsRegistered() {
|
|
|
61829
61978
|
}
|
|
61830
61979
|
async function loadPipeline(filePath) {
|
|
61831
61980
|
const absPath = resolve70(process.cwd(), filePath);
|
|
61832
|
-
if (!
|
|
61981
|
+
if (!existsSync52(absPath)) {
|
|
61833
61982
|
throw new Error(`Pipeline file not found: ${absPath}`);
|
|
61834
61983
|
}
|
|
61835
61984
|
const content = await readFile46(absPath, "utf-8");
|
|
@@ -61854,7 +62003,7 @@ async function executePipeline(manifest2, options = {}) {
|
|
|
61854
62003
|
const startTime = Date.now();
|
|
61855
62004
|
if (options.resume) {
|
|
61856
62005
|
const checkpointPath = resolve70(outputDir, CHECKPOINT_FILE);
|
|
61857
|
-
if (
|
|
62006
|
+
if (existsSync52(checkpointPath)) {
|
|
61858
62007
|
const checkpointContent = await readFile46(checkpointPath, "utf-8");
|
|
61859
62008
|
const checkpoint = (0, import_yaml8.parse)(checkpointContent);
|
|
61860
62009
|
for (const cs of checkpoint.completedSteps) {
|
|
@@ -67398,7 +67547,10 @@ async function executeSuggestEdit(options) {
|
|
|
67398
67547
|
}
|
|
67399
67548
|
return { success: true, suggestions };
|
|
67400
67549
|
} catch (error) {
|
|
67401
|
-
return {
|
|
67550
|
+
return {
|
|
67551
|
+
success: false,
|
|
67552
|
+
error: `Suggest failed: ${error instanceof Error ? error.message : String(error)}`
|
|
67553
|
+
};
|
|
67402
67554
|
}
|
|
67403
67555
|
}
|
|
67404
67556
|
var init_ai_suggest_edit = __esm({
|
|
@@ -68078,12 +68230,12 @@ var init_generate2 = __esm({
|
|
|
68078
68230
|
});
|
|
68079
68231
|
|
|
68080
68232
|
// src/tools/manifest/storyboard.ts
|
|
68081
|
-
import { existsSync as
|
|
68233
|
+
import { existsSync as existsSync53 } from "node:fs";
|
|
68082
68234
|
import { readFile as readFile48, writeFile as writeFile52 } from "node:fs/promises";
|
|
68083
68235
|
import { resolve as resolve74 } from "node:path";
|
|
68084
68236
|
async function readStoryboard2(projectDir) {
|
|
68085
68237
|
const path2 = resolve74(projectDir, "STORYBOARD.md");
|
|
68086
|
-
if (!
|
|
68238
|
+
if (!existsSync53(path2)) return null;
|
|
68087
68239
|
return { path: path2, markdown: await readFile48(path2, "utf-8") };
|
|
68088
68240
|
}
|
|
68089
68241
|
var projectDirSchema, storyboardListTool, storyboardValidateTool, storyboardGetTool, storyboardSetTool, storyboardMoveTool, storyboardReviseTool, planTool, storyboardTools;
|
|
@@ -71173,7 +71325,7 @@ init_source();
|
|
|
71173
71325
|
// package.json
|
|
71174
71326
|
var package_default = {
|
|
71175
71327
|
name: "@vibeframe/cli",
|
|
71176
|
-
version: "0.
|
|
71328
|
+
version: "0.103.1",
|
|
71177
71329
|
description: "VibeFrame CLI - natural language video editing from the terminal",
|
|
71178
71330
|
private: false,
|
|
71179
71331
|
license: "MIT",
|
|
@@ -71620,7 +71772,9 @@ init_project_resolver();
|
|
|
71620
71772
|
import { readFile as readFile12, writeFile as writeFile12 } from "node:fs/promises";
|
|
71621
71773
|
import { existsSync as existsSync10 } from "node:fs";
|
|
71622
71774
|
import { resolve as resolve16, basename as basename3, extname } from "node:path";
|
|
71623
|
-
var timelineCommand = new Command("timeline").description("Low-level timeline JSON commands").addHelpText(
|
|
71775
|
+
var timelineCommand = new Command("timeline").description("Low-level timeline JSON commands").addHelpText(
|
|
71776
|
+
"after",
|
|
71777
|
+
`
|
|
71624
71778
|
Examples:
|
|
71625
71779
|
$ vibe timeline create my-video # my-video/timeline.json
|
|
71626
71780
|
$ vibe timeline add-source my-video video.mp4 # Returns source ID
|
|
@@ -71632,7 +71786,8 @@ Examples:
|
|
|
71632
71786
|
|
|
71633
71787
|
Typical workflow: create \u2192 add-source \u2192 add-clip \u2192 trim-clip/split-clip \u2192 export
|
|
71634
71788
|
Cost: Free (no API keys needed).
|
|
71635
|
-
Run 'vibe schema timeline.<command>' for structured parameter info.`
|
|
71789
|
+
Run 'vibe schema timeline.<command>' for structured parameter info.`
|
|
71790
|
+
);
|
|
71636
71791
|
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
71792
|
await executeTimelineCreate(name, options, "timeline create", Date.now());
|
|
71638
71793
|
});
|
|
@@ -71843,7 +71998,10 @@ timelineCommand.command("add-track").description("Add a new track").argument("<p
|
|
|
71843
71998
|
exitWithError(generalError(`Failed to add track: ${msg}`));
|
|
71844
71999
|
}
|
|
71845
72000
|
});
|
|
71846
|
-
timelineCommand.command("add-effect").description("Add an effect to a clip").argument("<project>", "Timeline file or directory").argument("<clip-id>", "Clip ID").argument(
|
|
72001
|
+
timelineCommand.command("add-effect").description("Add an effect to a clip").argument("<project>", "Timeline file or directory").argument("<clip-id>", "Clip ID").argument(
|
|
72002
|
+
"<effect-type>",
|
|
72003
|
+
"Effect type (fadeIn, fadeOut, blur, brightness, contrast, saturation, speed, volume)"
|
|
72004
|
+
).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
72005
|
const startedAt = Date.now();
|
|
71848
72006
|
const spinner2 = ora("Adding effect...").start();
|
|
71849
72007
|
try {
|
|
@@ -72067,7 +72225,9 @@ timelineCommand.command("list").description("List timeline contents").argument("
|
|
|
72067
72225
|
for (const clip of clips) {
|
|
72068
72226
|
const source = project.getSource(clip.sourceId);
|
|
72069
72227
|
console.log(` ${source_default.yellow(clip.id)}`);
|
|
72070
|
-
console.log(
|
|
72228
|
+
console.log(
|
|
72229
|
+
` ${source?.name || "unknown"} @ ${clip.startTime}s (${clip.duration}s)`
|
|
72230
|
+
);
|
|
72071
72231
|
if (clip.effects.length > 0) {
|
|
72072
72232
|
console.log(` Effects: ${clip.effects.map((e) => e.type).join(", ")}`);
|
|
72073
72233
|
}
|
|
@@ -72327,10 +72487,10 @@ function detectMediaType(path2) {
|
|
|
72327
72487
|
return "video";
|
|
72328
72488
|
}
|
|
72329
72489
|
applyTiers(timelineCommand, {
|
|
72330
|
-
|
|
72331
|
-
|
|
72332
|
-
|
|
72333
|
-
|
|
72490
|
+
create: "free",
|
|
72491
|
+
info: "free",
|
|
72492
|
+
set: "free",
|
|
72493
|
+
list: "free",
|
|
72334
72494
|
"add-source": "free",
|
|
72335
72495
|
"add-clip": "free",
|
|
72336
72496
|
"add-track": "free",
|
|
@@ -79933,7 +80093,7 @@ starting from a media file and wants it transformed, it's REMIX.
|
|
|
79933
80093
|
| Add static captions | \`vibe edit caption in.mp4 -o out.mp4\` |
|
|
79934
80094
|
| Translate audio (transcribe \u2192 TTS) | \`vibe audio dub file.mp4 -t ko\` |
|
|
79935
80095
|
| Transcribe (Whisper, word-level) | \`vibe audio transcribe file.mp4 --granularity word\` |
|
|
79936
|
-
| Inspect
|
|
80096
|
+
| Inspect media with Gemini | \`vibe inspect media file.mp4 "summarise" --json\` |
|
|
79937
80097
|
|
|
79938
80098
|
### Compose pipelines (Video as Code)
|
|
79939
80099
|
|
|
@@ -79946,10 +80106,10 @@ starting from a media file and wants it transformed, it's REMIX.
|
|
|
79946
80106
|
|
|
79947
80107
|
| Tier | Examples | Approx. per call |
|
|
79948
80108
|
|---|---|---|
|
|
79949
|
-
| **Free** | \`detect *\`, \`
|
|
79950
|
-
| **Low** | \`
|
|
79951
|
-
| **High** | \`generate
|
|
79952
|
-
| **Very High** | \`remix
|
|
80109
|
+
| **Free** | \`schema/context/doctor\`, \`detect *\`, \`status *\`, \`plan\`, \`storyboard validate\`, \`inspect project/render --cheap\`, deterministic edits | $0 |
|
|
80110
|
+
| **Low** | \`generate narration/sound-effect/music\`, \`audio transcribe\`, \`inspect media\`, optional AI review | $0.01\u2013$0.10 |
|
|
80111
|
+
| **High** | \`generate image/motion\`, \`edit image/reframe/grade/speed-ramp\` | $1\u2013$5 |
|
|
80112
|
+
| **Very High** | \`generate video\`, \`edit fill-gaps\`, \`remix highlights/auto-shorts\`, \`build\` with generated assets | $5\u2013$50+ |
|
|
79953
80113
|
|
|
79954
80114
|
## Agent invariants
|
|
79955
80115
|
|
|
@@ -80122,7 +80282,7 @@ defaults:
|
|
|
80122
80282
|
# Optional \u2014 uncomment to set per-primitive provider preferences.
|
|
80123
80283
|
# providers:
|
|
80124
80284
|
# tts: elevenlabs # auto | elevenlabs | kokoro
|
|
80125
|
-
# image: openai # openai | gemini
|
|
80285
|
+
# image: openai # openai | gemini | grok
|
|
80126
80286
|
# music: elevenlabs
|
|
80127
80287
|
|
|
80128
80288
|
# Optional \u2014 cap total spend per pipeline run.
|
|
@@ -80735,11 +80895,12 @@ import { resolve as resolve58 } from "node:path";
|
|
|
80735
80895
|
var VALID_STAGES = ["assets", "compose", "sync", "render", "all"];
|
|
80736
80896
|
var VALID_MODES = ["agent", "batch", "auto"];
|
|
80737
80897
|
var VALID_TTS_PROVIDERS = ["auto", "elevenlabs", "kokoro"];
|
|
80898
|
+
var VALID_IMAGE_PROVIDERS = ["openai", "gemini", "grok"];
|
|
80738
80899
|
var VALID_VIDEO_PROVIDERS = ["seedance", "grok", "kling", "runway", "veo"];
|
|
80739
80900
|
var VALID_MUSIC_PROVIDERS = ["elevenlabs", "replicate"];
|
|
80740
80901
|
var VALID_COMPOSERS = ["claude", "openai", "gemini"];
|
|
80741
80902
|
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>",
|
|
80903
|
+
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
80904
|
const startedAt = Date.now();
|
|
80744
80905
|
const stage = String(options.stage ?? "all");
|
|
80745
80906
|
if (!VALID_STAGES.includes(stage)) {
|
|
@@ -80762,6 +80923,11 @@ var planCommand = new Command("plan").description("Read STORYBOARD.md and show b
|
|
|
80762
80923
|
VALID_MUSIC_PROVIDERS,
|
|
80763
80924
|
"music"
|
|
80764
80925
|
);
|
|
80926
|
+
const imageProvider = parseOptionalProvider(
|
|
80927
|
+
options.imageProvider,
|
|
80928
|
+
VALID_IMAGE_PROVIDERS,
|
|
80929
|
+
"image"
|
|
80930
|
+
);
|
|
80765
80931
|
const composer = parseOptionalProvider(options.composer, VALID_COMPOSERS, "composer");
|
|
80766
80932
|
const imageQuality = parseOptionalProvider(options.quality, VALID_IMAGE_QUALITIES, "quality");
|
|
80767
80933
|
const maxCost = options.maxCost !== void 0 ? Number.parseFloat(String(options.maxCost)) : void 0;
|
|
@@ -80785,7 +80951,7 @@ var planCommand = new Command("plan").description("Read STORYBOARD.md and show b
|
|
|
80785
80951
|
skipMusic: options.skipMusic,
|
|
80786
80952
|
ttsProvider,
|
|
80787
80953
|
voice: options.voice,
|
|
80788
|
-
imageProvider
|
|
80954
|
+
imageProvider,
|
|
80789
80955
|
imageQuality,
|
|
80790
80956
|
imageSize: options.imageSize,
|
|
80791
80957
|
videoProvider,
|
|
@@ -80922,9 +81088,10 @@ import { resolve as resolve63, join as join23 } from "node:path";
|
|
|
80922
81088
|
import { readFileSync as readFileSync3, existsSync as existsSync47 } from "node:fs";
|
|
80923
81089
|
var VALID_MODES2 = ["agent", "batch", "auto"];
|
|
80924
81090
|
var VALID_STAGES2 = ["assets", "compose", "sync", "render", "all"];
|
|
81091
|
+
var VALID_IMAGE_PROVIDERS2 = ["openai", "gemini", "grok"];
|
|
80925
81092
|
var VALID_VIDEO_PROVIDERS2 = ["seedance", "grok", "kling", "runway", "veo"];
|
|
80926
81093
|
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>",
|
|
81094
|
+
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
81095
|
"after",
|
|
80929
81096
|
`
|
|
80930
81097
|
Examples:
|
|
@@ -80967,6 +81134,11 @@ Advanced equivalent: \`vibe scene build\`.`
|
|
|
80967
81134
|
VALID_MUSIC_PROVIDERS2,
|
|
80968
81135
|
"music"
|
|
80969
81136
|
);
|
|
81137
|
+
const imageProvider = parseOptionalProvider2(
|
|
81138
|
+
options.imageProvider,
|
|
81139
|
+
VALID_IMAGE_PROVIDERS2,
|
|
81140
|
+
"image"
|
|
81141
|
+
);
|
|
80970
81142
|
const params = {
|
|
80971
81143
|
projectDir,
|
|
80972
81144
|
stage,
|
|
@@ -80982,7 +81154,7 @@ Advanced equivalent: \`vibe scene build\`.`
|
|
|
80982
81154
|
skipRender: options.skipRender ?? false,
|
|
80983
81155
|
ttsProvider: options.tts,
|
|
80984
81156
|
voice: options.voice,
|
|
80985
|
-
imageProvider
|
|
81157
|
+
imageProvider,
|
|
80986
81158
|
videoProvider,
|
|
80987
81159
|
musicProvider,
|
|
80988
81160
|
imageQuality: options.quality,
|
|
@@ -81001,7 +81173,7 @@ Advanced equivalent: \`vibe scene build\`.`
|
|
|
81001
81173
|
skipMusic: options.skipMusic,
|
|
81002
81174
|
ttsProvider: options.tts,
|
|
81003
81175
|
voice: options.voice,
|
|
81004
|
-
imageProvider
|
|
81176
|
+
imageProvider,
|
|
81005
81177
|
imageQuality: options.quality,
|
|
81006
81178
|
imageSize: options.imageSize,
|
|
81007
81179
|
videoProvider,
|
|
@@ -81092,7 +81264,7 @@ Advanced equivalent: \`vibe scene build\`.`
|
|
|
81092
81264
|
skipRender: options.skipRender,
|
|
81093
81265
|
ttsProvider: options.tts,
|
|
81094
81266
|
voice: options.voice,
|
|
81095
|
-
imageProvider
|
|
81267
|
+
imageProvider,
|
|
81096
81268
|
videoProvider,
|
|
81097
81269
|
musicProvider,
|
|
81098
81270
|
imageQuality: options.quality,
|
|
@@ -81813,7 +81985,7 @@ async function runDiagnostics() {
|
|
|
81813
81985
|
installed = false;
|
|
81814
81986
|
}
|
|
81815
81987
|
} else if (toolName === "chrome") {
|
|
81816
|
-
const { existsSync:
|
|
81988
|
+
const { existsSync: existsSync54 } = await import("node:fs");
|
|
81817
81989
|
const { homedir: homedir7 } = await import("node:os");
|
|
81818
81990
|
const { join: join26 } = await import("node:path");
|
|
81819
81991
|
const chromePaths = [
|
|
@@ -81826,7 +81998,7 @@ async function runDiagnostics() {
|
|
|
81826
81998
|
"/usr/bin/chromium",
|
|
81827
81999
|
"/usr/bin/chromium-browser"
|
|
81828
82000
|
];
|
|
81829
|
-
installed = chromePaths.some((p) => p &&
|
|
82001
|
+
installed = chromePaths.some((p) => p && existsSync54(p));
|
|
81830
82002
|
} else {
|
|
81831
82003
|
installed = commandExists(toolName);
|
|
81832
82004
|
}
|
|
@@ -81842,28 +82014,33 @@ async function runDiagnostics() {
|
|
|
81842
82014
|
configExists = true;
|
|
81843
82015
|
} catch {
|
|
81844
82016
|
}
|
|
82017
|
+
const cwd = process.cwd();
|
|
82018
|
+
const activeConfig = await loadConfig({ cwd });
|
|
81845
82019
|
const providers = {};
|
|
81846
82020
|
let readyCount = FREE_COMMANDS.length;
|
|
81847
82021
|
let totalCount = FREE_COMMANDS.length;
|
|
81848
82022
|
for (const [envVar, commands] of Object.entries(COMMAND_KEY_MAP)) {
|
|
82023
|
+
const providerName = Object.entries(PROVIDER_ENV_VARS).find(([, v]) => v === envVar)?.[0] ?? envVar;
|
|
82024
|
+
const configuredInConfig = Boolean(
|
|
82025
|
+
activeConfig?.providers[providerName]
|
|
82026
|
+
);
|
|
81849
82027
|
const configured = Boolean(
|
|
81850
|
-
process.env[envVar] || PROVIDER_ENV_ALIASES[envVar]?.map((alias) => process.env[alias]).find(Boolean)
|
|
82028
|
+
process.env[envVar] || PROVIDER_ENV_ALIASES[envVar]?.map((alias) => process.env[alias]).find(Boolean) || configuredInConfig
|
|
81851
82029
|
);
|
|
81852
|
-
const providerName = Object.entries(PROVIDER_ENV_VARS).find(([, v]) => v === envVar)?.[0] ?? envVar;
|
|
81853
82030
|
providers[providerName] = { envVar, configured, commands };
|
|
81854
82031
|
totalCount += commands.length;
|
|
81855
82032
|
if (configured) {
|
|
81856
82033
|
readyCount += commands.length;
|
|
81857
82034
|
}
|
|
81858
82035
|
}
|
|
81859
|
-
const cwd = process.cwd();
|
|
81860
82036
|
const projectFiles = ["AGENTS.md", "CLAUDE.md", "vibe.project.yaml"].map((rel) => ({
|
|
81861
82037
|
path: rel,
|
|
81862
82038
|
exists: existsSync48(resolve66(cwd, rel))
|
|
81863
82039
|
}));
|
|
81864
82040
|
const projectInitialized = projectFiles.some((f) => f.exists);
|
|
81865
|
-
const
|
|
81866
|
-
const
|
|
82041
|
+
const nearestProjectConfigPath = await findProjectConfigPath(cwd);
|
|
82042
|
+
const projectConfigPath = nearestProjectConfigPath ?? getProjectConfigPath(cwd);
|
|
82043
|
+
const projectConfigExists = Boolean(nearestProjectConfigPath);
|
|
81867
82044
|
const activeScope = await getActiveScope(cwd);
|
|
81868
82045
|
const hosts = detectAgentHosts();
|
|
81869
82046
|
const detectedNames = hosts.filter((h) => h.detected).map((h) => h.label);
|
|
@@ -82136,8 +82313,9 @@ async function runLiveKeyTests(results) {
|
|
|
82136
82313
|
return;
|
|
82137
82314
|
}
|
|
82138
82315
|
const { testKey: testKey2 } = await Promise.resolve().then(() => (init_key_live_test(), key_live_test_exports));
|
|
82316
|
+
const activeConfig = await loadConfig({ cwd: process.cwd() });
|
|
82139
82317
|
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);
|
|
82318
|
+
const value = process.env[info.envVar] || PROVIDER_ENV_ALIASES[info.envVar]?.map((alias) => process.env[alias]).find(Boolean) || activeConfig?.providers[name];
|
|
82141
82319
|
if (!value) continue;
|
|
82142
82320
|
process.stdout.write(` ${name.padEnd(12)} `);
|
|
82143
82321
|
const result = await testKey2(name, value);
|
|
@@ -82378,11 +82556,21 @@ var demoCommand = new Command("demo").description("Run sample edits on a test vi
|
|
|
82378
82556
|
|
|
82379
82557
|
// src/commands/context.ts
|
|
82380
82558
|
init_esm();
|
|
82559
|
+
import { existsSync as existsSync50 } from "node:fs";
|
|
82381
82560
|
import { readFile as readFile44 } from "node:fs/promises";
|
|
82382
82561
|
import { resolve as resolve68, dirname as dirname23 } from "node:path";
|
|
82383
82562
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
82384
82563
|
var __filename = fileURLToPath2(import.meta.url);
|
|
82385
82564
|
var __dirname = dirname23(__filename);
|
|
82565
|
+
function resolveContextPath(baseDir = __dirname) {
|
|
82566
|
+
const candidates = [
|
|
82567
|
+
// Bundled package layout: packages/cli/dist/index.js -> packages/cli/CONTEXT.md
|
|
82568
|
+
resolve68(baseDir, "../CONTEXT.md"),
|
|
82569
|
+
// Source/dev layout: packages/cli/src/commands/context.ts -> packages/cli/CONTEXT.md
|
|
82570
|
+
resolve68(baseDir, "../../CONTEXT.md")
|
|
82571
|
+
];
|
|
82572
|
+
return candidates.find((candidate) => existsSync50(candidate)) ?? null;
|
|
82573
|
+
}
|
|
82386
82574
|
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
82575
|
const options = {
|
|
82388
82576
|
json: cmd.parent?.opts()?.json || cmd.opts()?.json || cmd.opts()?.format === "json",
|
|
@@ -82468,8 +82656,9 @@ var contextCommand = new Command("context").description("Print CLI context/guide
|
|
|
82468
82656
|
console.log(JSON.stringify(contract, null, 2));
|
|
82469
82657
|
return;
|
|
82470
82658
|
}
|
|
82471
|
-
const contextPath = resolve68(__dirname, "../CONTEXT.md");
|
|
82472
82659
|
try {
|
|
82660
|
+
const contextPath = resolveContextPath();
|
|
82661
|
+
if (!contextPath) throw new Error("CONTEXT.md not found");
|
|
82473
82662
|
const content = await readFile44(contextPath, "utf-8");
|
|
82474
82663
|
console.log(
|
|
82475
82664
|
content.replace(
|
|
@@ -82561,7 +82750,7 @@ Use 'vibe schema <command> --json' to get parameter schemas.
|
|
|
82561
82750
|
Use 'vibe doctor --json' to check configured API keys.
|
|
82562
82751
|
Use '--dry-run --json' before any mutating/costly operation.
|
|
82563
82752
|
|
|
82564
|
-
Cost tiers: Free (detect
|
|
82753
|
+
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
82754
|
|
|
82566
82755
|
Group \u2192 MCP tool name: '<group>_<leaf>' (snake_case). Bare top-level (init/build/render/run) maps to bare MCP names.
|
|
82567
82756
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibeframe/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.103.1",
|
|
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.1",
|
|
187
|
+
"@vibeframe/core": "0.103.1"
|
|
188
188
|
},
|
|
189
189
|
"engines": {
|
|
190
190
|
"node": ">=20"
|