@vibeframe/cli 0.113.9 → 0.113.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/_shared/build-plan.d.ts +7 -0
- package/dist/commands/_shared/compose-prompts.d.ts +7 -0
- package/dist/commands/_shared/compose-scenes-skills.d.ts +30 -1
- package/dist/commands/_shared/scene-build.d.ts +20 -0
- package/dist/commands/_shared/transcribe-narration.d.ts +23 -0
- package/dist/commands/_shared/transcribe-narration.test.d.ts +1 -0
- package/dist/index.js +766 -621
- package/dist/tools/manifest/index.js +610 -473
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -11631,15 +11631,15 @@ async function loadKokoroRuntime() {
|
|
|
11631
11631
|
}
|
|
11632
11632
|
async function loadKokoroFromWorkspace(baseDir = process.cwd()) {
|
|
11633
11633
|
try {
|
|
11634
|
-
const { readFile:
|
|
11635
|
-
const { join:
|
|
11634
|
+
const { readFile: readFile57 } = await import("node:fs/promises");
|
|
11635
|
+
const { join: join31, resolve: resolve86 } = await import("node:path");
|
|
11636
11636
|
const { pathToFileURL } = await import("node:url");
|
|
11637
11637
|
const pkgDir = resolve86(baseDir, "node_modules", "kokoro-js");
|
|
11638
|
-
const pkg = JSON.parse(await
|
|
11638
|
+
const pkg = JSON.parse(await readFile57(join31(pkgDir, "package.json"), "utf-8"));
|
|
11639
11639
|
const entry = pkg.exports?.node?.import ?? pkg.exports?.default ?? pkg.module ?? pkg.main;
|
|
11640
11640
|
if (!entry)
|
|
11641
11641
|
return null;
|
|
11642
|
-
const mod = await import(pathToFileURL(
|
|
11642
|
+
const mod = await import(pathToFileURL(join31(pkgDir, entry)).href);
|
|
11643
11643
|
return mod.KokoroTTS ?? null;
|
|
11644
11644
|
} catch {
|
|
11645
11645
|
return null;
|
|
@@ -11650,20 +11650,20 @@ async function loadBundledKokoroRuntime(runtimeDir = process.env.VIBE_KOKORO_RUN
|
|
|
11650
11650
|
if (!base)
|
|
11651
11651
|
return null;
|
|
11652
11652
|
try {
|
|
11653
|
-
const { readFile:
|
|
11654
|
-
const { join:
|
|
11653
|
+
const { readFile: readFile57, writeFile: writeFile61, mkdir: mkdir31 } = await import("node:fs/promises");
|
|
11654
|
+
const { join: join31, resolve: resolve86 } = await import("node:path");
|
|
11655
11655
|
const { pathToFileURL } = await import("node:url");
|
|
11656
11656
|
const { homedir: homedir8 } = await import("node:os");
|
|
11657
11657
|
const transformersDir = resolve86(base, "node_modules", "@huggingface", "transformers");
|
|
11658
11658
|
const ortDistDir = resolve86(base, "node_modules", "onnxruntime-web", "dist");
|
|
11659
|
-
const transformers = await import(pathToFileURL(
|
|
11660
|
-
const cacheRoot =
|
|
11659
|
+
const transformers = await import(pathToFileURL(join31(transformersDir, "dist", "transformers.web.js")).href);
|
|
11660
|
+
const cacheRoot = join31(homedir8(), ".cache", "vibeframe", "models");
|
|
11661
11661
|
transformers.env.allowLocalModels = false;
|
|
11662
11662
|
transformers.env.useCustomCache = true;
|
|
11663
11663
|
transformers.env.customCache = {
|
|
11664
11664
|
async match(key) {
|
|
11665
11665
|
try {
|
|
11666
|
-
return new Response(await
|
|
11666
|
+
return new Response(await readFile57(join31(cacheRoot, encodeURIComponent(String(key)))));
|
|
11667
11667
|
} catch {
|
|
11668
11668
|
return void 0;
|
|
11669
11669
|
}
|
|
@@ -11672,7 +11672,7 @@ async function loadBundledKokoroRuntime(runtimeDir = process.env.VIBE_KOKORO_RUN
|
|
|
11672
11672
|
try {
|
|
11673
11673
|
const buf = Buffer.from(await response.arrayBuffer());
|
|
11674
11674
|
await mkdir31(cacheRoot, { recursive: true });
|
|
11675
|
-
await writeFile61(
|
|
11675
|
+
await writeFile61(join31(cacheRoot, encodeURIComponent(String(key))), buf);
|
|
11676
11676
|
} catch {
|
|
11677
11677
|
}
|
|
11678
11678
|
}
|
|
@@ -29972,10 +29972,57 @@ var init_composer_resolve = __esm({
|
|
|
29972
29972
|
}
|
|
29973
29973
|
});
|
|
29974
29974
|
|
|
29975
|
+
// src/commands/_shared/transcribe-narration.ts
|
|
29976
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
29977
|
+
import { readFile as readFile8 } from "node:fs/promises";
|
|
29978
|
+
import { join as join6 } from "node:path";
|
|
29979
|
+
async function transcribeNarrationWords(audioAbsPath, opts) {
|
|
29980
|
+
try {
|
|
29981
|
+
const whisper = new WhisperProvider();
|
|
29982
|
+
await whisper.initialize({ apiKey: opts.apiKey });
|
|
29983
|
+
const audioBytes = await readFile8(audioAbsPath);
|
|
29984
|
+
const audioBlob = new Blob([new Uint8Array(audioBytes)]);
|
|
29985
|
+
const transcript = await whisper.transcribe(audioBlob, void 0, {
|
|
29986
|
+
granularity: "word",
|
|
29987
|
+
language: opts.language
|
|
29988
|
+
});
|
|
29989
|
+
if (transcript.status === "completed" && transcript.words?.length) {
|
|
29990
|
+
return transcript.words.map((w) => ({ text: w.text, start: w.start, end: w.end }));
|
|
29991
|
+
}
|
|
29992
|
+
return [];
|
|
29993
|
+
} catch {
|
|
29994
|
+
return [];
|
|
29995
|
+
}
|
|
29996
|
+
}
|
|
29997
|
+
function beatTranscriptRelPath(beatId) {
|
|
29998
|
+
return `assets/transcript-${beatId}.json`;
|
|
29999
|
+
}
|
|
30000
|
+
async function readBeatTranscript(projectDir, beatId) {
|
|
30001
|
+
const abs = join6(projectDir, beatTranscriptRelPath(beatId));
|
|
30002
|
+
if (!existsSync7(abs)) return void 0;
|
|
30003
|
+
try {
|
|
30004
|
+
const parsed = JSON.parse(await readFile8(abs, "utf-8"));
|
|
30005
|
+
if (!Array.isArray(parsed)) return void 0;
|
|
30006
|
+
const words = parsed.filter(
|
|
30007
|
+
(w) => typeof w === "object" && w !== null && typeof w.text === "string" && Number.isFinite(w.start) && Number.isFinite(w.end)
|
|
30008
|
+
).map((w) => ({ text: w.text, start: w.start, end: w.end }));
|
|
30009
|
+
return words.length > 0 ? words : void 0;
|
|
30010
|
+
} catch {
|
|
30011
|
+
return void 0;
|
|
30012
|
+
}
|
|
30013
|
+
}
|
|
30014
|
+
var init_transcribe_narration = __esm({
|
|
30015
|
+
"src/commands/_shared/transcribe-narration.ts"() {
|
|
30016
|
+
"use strict";
|
|
30017
|
+
init_dist2();
|
|
30018
|
+
}
|
|
30019
|
+
});
|
|
30020
|
+
|
|
29975
30021
|
// src/commands/_shared/compose-scenes-skills.ts
|
|
29976
30022
|
var compose_scenes_skills_exports = {};
|
|
29977
30023
|
__export(compose_scenes_skills_exports, {
|
|
29978
30024
|
ComposeBeatError: () => ComposeBeatError,
|
|
30025
|
+
TRANSCRIPT_PROMPT_MAX_WORDS: () => TRANSCRIPT_PROMPT_MAX_WORDS,
|
|
29979
30026
|
buildSystemPrompt: () => buildSystemPrompt,
|
|
29980
30027
|
buildUserPrompt: () => buildUserPrompt,
|
|
29981
30028
|
composeBeatHtml: () => composeBeatHtml,
|
|
@@ -29985,15 +30032,16 @@ __export(compose_scenes_skills_exports, {
|
|
|
29985
30032
|
executeComposeScenesWithSkills: () => executeComposeScenesWithSkills,
|
|
29986
30033
|
extractHtml: () => extractHtml,
|
|
29987
30034
|
formatLintFeedback: () => formatLintFeedback,
|
|
30035
|
+
formatTranscriptSection: () => formatTranscriptSection,
|
|
29988
30036
|
lintBeatHtml: () => lintBeatHtml
|
|
29989
30037
|
});
|
|
29990
30038
|
import Anthropic from "@anthropic-ai/sdk";
|
|
29991
30039
|
import OpenAI from "openai";
|
|
29992
30040
|
import { GoogleGenerativeAI } from "@google/generative-ai";
|
|
29993
30041
|
import { createHash as createHash2 } from "node:crypto";
|
|
29994
|
-
import { existsSync as
|
|
29995
|
-
import { mkdir as mkdir5, readFile as
|
|
29996
|
-
import { dirname as dirname3, join as
|
|
30042
|
+
import { existsSync as existsSync8, mkdirSync } from "node:fs";
|
|
30043
|
+
import { mkdir as mkdir5, readFile as readFile9, writeFile as writeFile8 } from "node:fs/promises";
|
|
30044
|
+
import { dirname as dirname3, join as join7, resolve as resolve11 } from "node:path";
|
|
29997
30045
|
import { runHyperframeLint as runHyperframeLint2 } from "@hyperframes/producer";
|
|
29998
30046
|
function buildSystemPrompt(ctx) {
|
|
29999
30047
|
return `You are a Hyperframes composition author. The skill content below
|
|
@@ -30027,6 +30075,46 @@ function formatBeatCues(cues) {
|
|
|
30027
30075
|
}
|
|
30028
30076
|
return lines.join("\n") + "\n";
|
|
30029
30077
|
}
|
|
30078
|
+
function formatTranscriptSection(transcript, maxWords = TRANSCRIPT_PROMPT_MAX_WORDS) {
|
|
30079
|
+
if (!transcript || transcript.length === 0) return "";
|
|
30080
|
+
const round = (n) => Number(Math.max(0, n).toFixed(2));
|
|
30081
|
+
const noAudioRule = "This is for VISUAL sync only \u2014 do NOT add an `<audio>` tag or invent SFX; the narration audio is wired separately by the root timeline. If this beat is visual-only (no on-screen text), you may ignore these timings.";
|
|
30082
|
+
if (transcript.length <= maxWords) {
|
|
30083
|
+
const pairs = transcript.map((w) => `[${round(w.start)}, ${JSON.stringify(w.text)}]`).join(", ");
|
|
30084
|
+
return `
|
|
30085
|
+
|
|
30086
|
+
=== Narration word timings (seconds) ===
|
|
30087
|
+
|
|
30088
|
+
Whisper word-level start times for this beat's narration. If you author captions
|
|
30089
|
+
or kinetic typography, reveal each word at its start time with absolute-time
|
|
30090
|
+
GSAP tweens, e.g.
|
|
30091
|
+
\`tl.fromTo('.caption .word[data-i="0"]', { opacity: 0, y: 10 }, { opacity: 1, y: 0, duration: 0.18 }, ${round(
|
|
30092
|
+
transcript[0].start
|
|
30093
|
+
)});\`
|
|
30094
|
+
${noAudioRule}
|
|
30095
|
+
|
|
30096
|
+
word starts (word-level): ${pairs}`;
|
|
30097
|
+
}
|
|
30098
|
+
const phraseCount = Math.max(1, Math.floor(maxWords / 2));
|
|
30099
|
+
const chunkSize = Math.ceil(transcript.length / phraseCount);
|
|
30100
|
+
const anchors = [];
|
|
30101
|
+
for (let i = 0; i < transcript.length; i += chunkSize) {
|
|
30102
|
+
const chunk = transcript.slice(i, i + chunkSize);
|
|
30103
|
+
const text = chunk.map((w) => w.text).join(" ");
|
|
30104
|
+
anchors.push(`[${round(chunk[0].start)}, ${JSON.stringify(text)}]`);
|
|
30105
|
+
}
|
|
30106
|
+
return `
|
|
30107
|
+
|
|
30108
|
+
=== Narration timings (approximate, phrase-level) ===
|
|
30109
|
+
|
|
30110
|
+
This beat's narration is long (${transcript.length} words), so timings are
|
|
30111
|
+
grouped into phrase-level anchors (start time of each phrase). Use them to PACE
|
|
30112
|
+
caption / kinetic-type reveals across the beat \u2014 they are approximate, not
|
|
30113
|
+
per-word truth.
|
|
30114
|
+
${noAudioRule}
|
|
30115
|
+
|
|
30116
|
+
phrase starts (approximate): ${anchors.join(", ")}`;
|
|
30117
|
+
}
|
|
30030
30118
|
function buildUserPrompt(ctx) {
|
|
30031
30119
|
const compositionId = `scene-${ctx.beat.id}`;
|
|
30032
30120
|
const durLiteral = ctx.finalDurationSec !== void 0 ? String(ctx.finalDurationSec) : void 0;
|
|
@@ -30117,6 +30205,13 @@ ${finalDurationBullet}
|
|
|
30117
30205
|
- If the beat cues include \`narration\`, do not embed an \`<audio>\` tag in the
|
|
30118
30206
|
scene fragment. The root timeline wires \`assets/narration-${ctx.beat.id}.wav\`
|
|
30119
30207
|
or \`assets/narration-${ctx.beat.id}.mp3\` separately.
|
|
30208
|
+
- Do NOT invent sound effects. This scene fragment is visual-only: never add
|
|
30209
|
+
an \`<audio>\` tag, and never imply ambient/foley SFX (keyboard clicks, whoosh,
|
|
30210
|
+
applause, room tone) that the beat did not explicitly request. Audio comes
|
|
30211
|
+
only from the beat's explicit \`narration\` and \`music\` cues, wired by the
|
|
30212
|
+
root timeline. If the beat declares an explicit \`sfx\` cue, treat it as intent
|
|
30213
|
+
for the storyboard author \u2014 still do not embed it here \u2014 and only when it is
|
|
30214
|
+
clearly tied to the beat's narration/motion; ignore orphaned or generic SFX.
|
|
30120
30215
|
- **\`.clip\` elements get visibility control from the framework but NO
|
|
30121
30216
|
sizing.** Always give \`.clip\` explicit fill via CSS:
|
|
30122
30217
|
\`{ position: absolute; inset: 0; }\` (or equivalent
|
|
@@ -30182,7 +30277,7 @@ ${ctx.storyboardGlobal || "(no global direction)"}
|
|
|
30182
30277
|
|
|
30183
30278
|
## ${ctx.beat.heading}
|
|
30184
30279
|
${formatBeatCues(ctx.beat.cues)}${cueDurationNote}
|
|
30185
|
-
${ctx.beat.body}
|
|
30280
|
+
${ctx.beat.body}${formatTranscriptSection(ctx.transcript)}
|
|
30186
30281
|
|
|
30187
30282
|
=== Output format ===
|
|
30188
30283
|
|
|
@@ -30202,7 +30297,7 @@ function computeCacheKey(parts) {
|
|
|
30202
30297
|
return createHash2("sha256").update(parts.provider).update("\u241E").update(parts.model).update("\u241E").update(parts.systemPrompt).update("\u241E").update(parts.userPrompt).digest("hex");
|
|
30203
30298
|
}
|
|
30204
30299
|
function defaultCacheDir() {
|
|
30205
|
-
return
|
|
30300
|
+
return join7(USER_CACHE_DIR, "compose-scenes");
|
|
30206
30301
|
}
|
|
30207
30302
|
function extractHtml(responseText) {
|
|
30208
30303
|
const fenced = responseText.match(/```html\s*\n([\s\S]*?)\n```/);
|
|
@@ -30226,9 +30321,9 @@ async function composeBeatHtml(ctx, overrides) {
|
|
|
30226
30321
|
model: settings.model
|
|
30227
30322
|
});
|
|
30228
30323
|
const cacheDir = ctx.cacheDir ?? defaultCacheDir();
|
|
30229
|
-
const cachePath =
|
|
30230
|
-
if (
|
|
30231
|
-
const html2 = await
|
|
30324
|
+
const cachePath = join7(cacheDir, `${cacheKey}.html`);
|
|
30325
|
+
if (existsSync8(cachePath)) {
|
|
30326
|
+
const html2 = await readFile9(cachePath, "utf-8");
|
|
30232
30327
|
return { html: html2, cached: true, cacheKey, model: settings.model, provider: ctx.provider };
|
|
30233
30328
|
}
|
|
30234
30329
|
if (!ctx.apiKey) {
|
|
@@ -30364,14 +30459,14 @@ async function executeComposeScenesWithSkills(params, outputDir, overrides) {
|
|
|
30364
30459
|
const projectRoot = params.project ? resolve11(outputDir, params.project) : resolve11(outputDir);
|
|
30365
30460
|
const designPath = resolve11(projectRoot, params.design ?? "DESIGN.md");
|
|
30366
30461
|
const storyboardPath2 = resolve11(projectRoot, params.storyboard ?? "STORYBOARD.md");
|
|
30367
|
-
if (!
|
|
30462
|
+
if (!existsSync8(designPath)) {
|
|
30368
30463
|
return { success: false, error: `DESIGN.md not found at ${designPath}` };
|
|
30369
30464
|
}
|
|
30370
|
-
if (!
|
|
30465
|
+
if (!existsSync8(storyboardPath2)) {
|
|
30371
30466
|
return { success: false, error: `STORYBOARD.md not found at ${storyboardPath2}` };
|
|
30372
30467
|
}
|
|
30373
|
-
const designMd = await
|
|
30374
|
-
const storyboardMd = await
|
|
30468
|
+
const designMd = await readFile9(designPath, "utf-8");
|
|
30469
|
+
const storyboardMd = await readFile9(storyboardPath2, "utf-8");
|
|
30375
30470
|
const { global: storyboardGlobal, beats } = parseStoryboard(storyboardMd);
|
|
30376
30471
|
if (beats.length === 0) {
|
|
30377
30472
|
return {
|
|
@@ -30391,7 +30486,7 @@ async function executeComposeScenesWithSkills(params, outputDir, overrides) {
|
|
|
30391
30486
|
provider = params.composer ?? "claude";
|
|
30392
30487
|
apiKey = "";
|
|
30393
30488
|
}
|
|
30394
|
-
const compositionsDir =
|
|
30489
|
+
const compositionsDir = join7(projectRoot, "compositions");
|
|
30395
30490
|
await mkdir5(compositionsDir, { recursive: true });
|
|
30396
30491
|
let finalDurations = /* @__PURE__ */ new Map();
|
|
30397
30492
|
try {
|
|
@@ -30405,6 +30500,7 @@ async function executeComposeScenesWithSkills(params, outputDir, overrides) {
|
|
|
30405
30500
|
async (beat, beatIndex) => {
|
|
30406
30501
|
onProgress({ type: "beat-start", beatId: beat.id, beatIndex, totalBeats });
|
|
30407
30502
|
try {
|
|
30503
|
+
const transcript = await readBeatTranscript(projectRoot, beat.id);
|
|
30408
30504
|
const result = await composeBeatWithRetry(
|
|
30409
30505
|
{
|
|
30410
30506
|
beat,
|
|
@@ -30415,11 +30511,12 @@ async function executeComposeScenesWithSkills(params, outputDir, overrides) {
|
|
|
30415
30511
|
apiKey,
|
|
30416
30512
|
effort: params.effort,
|
|
30417
30513
|
cacheDir: params.cacheDir,
|
|
30418
|
-
finalDurationSec: finalDurations.get(beat.id)
|
|
30514
|
+
finalDurationSec: finalDurations.get(beat.id),
|
|
30515
|
+
transcript
|
|
30419
30516
|
},
|
|
30420
30517
|
overrides
|
|
30421
30518
|
);
|
|
30422
|
-
const compositionPath =
|
|
30519
|
+
const compositionPath = join7(compositionsDir, `scene-${beat.id}.html`);
|
|
30423
30520
|
await mkdir5(dirname3(compositionPath), { recursive: true });
|
|
30424
30521
|
await writeFile8(compositionPath, result.html, "utf-8");
|
|
30425
30522
|
if (result.cached) {
|
|
@@ -30498,7 +30595,7 @@ ${failures.map((f) => ` - ${f.beatId}: ${f.error}`).join("\n")}`,
|
|
|
30498
30595
|
data: aggregateData
|
|
30499
30596
|
};
|
|
30500
30597
|
}
|
|
30501
|
-
var MODEL_SETTINGS_BY_PROVIDER, ComposeBeatError, defaultCallLLM, CONFIG_PROVIDER_BY_COMPOSER;
|
|
30598
|
+
var MODEL_SETTINGS_BY_PROVIDER, TRANSCRIPT_PROMPT_MAX_WORDS, ComposeBeatError, defaultCallLLM, CONFIG_PROVIDER_BY_COMPOSER;
|
|
30502
30599
|
var init_compose_scenes_skills = __esm({
|
|
30503
30600
|
"src/commands/_shared/compose-scenes-skills.ts"() {
|
|
30504
30601
|
"use strict";
|
|
@@ -30510,6 +30607,7 @@ var init_compose_scenes_skills = __esm({
|
|
|
30510
30607
|
init_root_sync();
|
|
30511
30608
|
init_composer_resolve();
|
|
30512
30609
|
init_config();
|
|
30610
|
+
init_transcribe_narration();
|
|
30513
30611
|
MODEL_SETTINGS_BY_PROVIDER = {
|
|
30514
30612
|
// Anthropic Claude Sonnet 4.6 — v0.69 baseline. ~9 s/beat, $3/$15 per MTok.
|
|
30515
30613
|
claude: {
|
|
@@ -30533,6 +30631,7 @@ var init_compose_scenes_skills = __esm({
|
|
|
30533
30631
|
high: { model: GEMINI_DEFAULT_TEXT_MODEL, maxTokens: 8e3, costPerMTokIn: 1.5, costPerMTokOut: 9 }
|
|
30534
30632
|
}
|
|
30535
30633
|
};
|
|
30634
|
+
TRANSCRIPT_PROMPT_MAX_WORDS = 120;
|
|
30536
30635
|
ComposeBeatError = class extends Error {
|
|
30537
30636
|
constructor(code, message, cause) {
|
|
30538
30637
|
super(message);
|
|
@@ -30618,22 +30717,22 @@ var init_compose_scenes_skills = __esm({
|
|
|
30618
30717
|
});
|
|
30619
30718
|
|
|
30620
30719
|
// src/commands/_shared/compose-prompts.ts
|
|
30621
|
-
import { existsSync as
|
|
30622
|
-
import { readFile as
|
|
30623
|
-
import { join as
|
|
30720
|
+
import { existsSync as existsSync9 } from "node:fs";
|
|
30721
|
+
import { readFile as readFile10 } from "node:fs/promises";
|
|
30722
|
+
import { join as join8, relative as relative4, resolve as resolve12 } from "node:path";
|
|
30624
30723
|
async function getComposePrompts(opts) {
|
|
30625
30724
|
const projectDir = resolve12(opts.projectDir);
|
|
30626
|
-
const designPath =
|
|
30627
|
-
const storyboardPath2 =
|
|
30628
|
-
const skillPath =
|
|
30629
|
-
const compositionsDir =
|
|
30725
|
+
const designPath = join8(projectDir, "DESIGN.md");
|
|
30726
|
+
const storyboardPath2 = join8(projectDir, "STORYBOARD.md");
|
|
30727
|
+
const skillPath = join8(projectDir, "SKILL.md");
|
|
30728
|
+
const compositionsDir = join8(projectDir, "compositions");
|
|
30630
30729
|
const warnings = [];
|
|
30631
30730
|
const baseError = (msg) => ({
|
|
30632
30731
|
success: false,
|
|
30633
30732
|
projectDir,
|
|
30634
30733
|
designReference: "DESIGN.md",
|
|
30635
30734
|
storyboardReference: "STORYBOARD.md",
|
|
30636
|
-
skillReference:
|
|
30735
|
+
skillReference: existsSync9(skillPath) ? "SKILL.md" : null,
|
|
30637
30736
|
compositionsDir: "compositions",
|
|
30638
30737
|
beats: [],
|
|
30639
30738
|
instructions: [],
|
|
@@ -30641,18 +30740,18 @@ async function getComposePrompts(opts) {
|
|
|
30641
30740
|
warnings,
|
|
30642
30741
|
error: msg
|
|
30643
30742
|
});
|
|
30644
|
-
if (!
|
|
30743
|
+
if (!existsSync9(designPath)) {
|
|
30645
30744
|
return baseError(`DESIGN.md not found at ${designPath}. Run \`vibe scene init <dir>\` first.`);
|
|
30646
30745
|
}
|
|
30647
|
-
if (!
|
|
30746
|
+
if (!existsSync9(storyboardPath2)) {
|
|
30648
30747
|
return baseError(`STORYBOARD.md not found at ${storyboardPath2}. Run \`vibe scene init <dir>\` to create a starter, or add STORYBOARD.md with per-beat cues.`);
|
|
30649
30748
|
}
|
|
30650
|
-
if (!
|
|
30749
|
+
if (!existsSync9(skillPath)) {
|
|
30651
30750
|
warnings.push(
|
|
30652
30751
|
"SKILL.md not installed \u2014 host agent won't have Hyperframes rules in context. Run `vibe scene install-skill` to install it."
|
|
30653
30752
|
);
|
|
30654
30753
|
}
|
|
30655
|
-
const storyboardMd = await
|
|
30754
|
+
const storyboardMd = await readFile10(storyboardPath2, "utf-8");
|
|
30656
30755
|
const parsed = parseStoryboard(storyboardMd);
|
|
30657
30756
|
if (parsed.beats.length === 0) {
|
|
30658
30757
|
return baseError(`STORYBOARD.md has no \`## Beat \u2026\` headings.`);
|
|
@@ -30674,28 +30773,33 @@ async function getComposePrompts(opts) {
|
|
|
30674
30773
|
finalDurations = await resolveProjectBeatDurations(projectDir);
|
|
30675
30774
|
} catch {
|
|
30676
30775
|
}
|
|
30677
|
-
const result =
|
|
30678
|
-
|
|
30679
|
-
|
|
30680
|
-
|
|
30681
|
-
|
|
30682
|
-
beat
|
|
30683
|
-
|
|
30684
|
-
|
|
30685
|
-
|
|
30686
|
-
|
|
30687
|
-
|
|
30688
|
-
|
|
30689
|
-
|
|
30690
|
-
|
|
30691
|
-
|
|
30692
|
-
|
|
30693
|
-
|
|
30694
|
-
|
|
30695
|
-
|
|
30696
|
-
|
|
30697
|
-
|
|
30698
|
-
|
|
30776
|
+
const result = await Promise.all(
|
|
30777
|
+
beats.map(async (beat) => {
|
|
30778
|
+
const outputPathAbs = join8(compositionsDir, `scene-${beat.id}.html`);
|
|
30779
|
+
const outputPathRel = relative4(projectDir, outputPathAbs);
|
|
30780
|
+
const finalDurationSec = finalDurations.get(beat.id);
|
|
30781
|
+
const transcript = await readBeatTranscript(projectDir, beat.id);
|
|
30782
|
+
const userPrompt = buildUserPrompt({
|
|
30783
|
+
beat,
|
|
30784
|
+
storyboardGlobal: parsed.global,
|
|
30785
|
+
finalDurationSec,
|
|
30786
|
+
transcript
|
|
30787
|
+
});
|
|
30788
|
+
return {
|
|
30789
|
+
id: beat.id,
|
|
30790
|
+
heading: beat.heading,
|
|
30791
|
+
outputPath: outputPathRel,
|
|
30792
|
+
duration: beat.duration,
|
|
30793
|
+
finalDurationSec,
|
|
30794
|
+
cues: beat.cues,
|
|
30795
|
+
body: beat.body,
|
|
30796
|
+
userPrompt,
|
|
30797
|
+
transcriptPath: transcript ? beatTranscriptRelPath(beat.id) : void 0,
|
|
30798
|
+
exists: existsSync9(outputPathAbs)
|
|
30799
|
+
};
|
|
30800
|
+
})
|
|
30801
|
+
);
|
|
30802
|
+
const skillRef = existsSync9(skillPath) ? "SKILL.md" : null;
|
|
30699
30803
|
const instructions = buildInstructions({
|
|
30700
30804
|
skillRef,
|
|
30701
30805
|
beatCount: result.length,
|
|
@@ -30743,13 +30847,14 @@ var init_compose_prompts = __esm({
|
|
|
30743
30847
|
init_compose_scenes_skills();
|
|
30744
30848
|
init_root_sync();
|
|
30745
30849
|
init_bundle();
|
|
30850
|
+
init_transcribe_narration();
|
|
30746
30851
|
}
|
|
30747
30852
|
});
|
|
30748
30853
|
|
|
30749
30854
|
// src/commands/_shared/scene-submit.ts
|
|
30750
|
-
import { existsSync as
|
|
30751
|
-
import { mkdir as mkdir6, readFile as
|
|
30752
|
-
import { join as
|
|
30855
|
+
import { existsSync as existsSync10 } from "node:fs";
|
|
30856
|
+
import { mkdir as mkdir6, readFile as readFile11, writeFile as writeFile9 } from "node:fs/promises";
|
|
30857
|
+
import { join as join9, resolve as resolve13 } from "node:path";
|
|
30753
30858
|
async function executeSceneSubmit(opts) {
|
|
30754
30859
|
const projectDir = resolve13(opts.projectDir);
|
|
30755
30860
|
const beatId = opts.beatId.trim();
|
|
@@ -30763,15 +30868,15 @@ async function executeSceneSubmit(opts) {
|
|
|
30763
30868
|
warnings: [],
|
|
30764
30869
|
retryWith: []
|
|
30765
30870
|
};
|
|
30766
|
-
const storyboardPath2 =
|
|
30767
|
-
if (!
|
|
30871
|
+
const storyboardPath2 = join9(projectDir, "STORYBOARD.md");
|
|
30872
|
+
if (!existsSync10(storyboardPath2)) {
|
|
30768
30873
|
return {
|
|
30769
30874
|
...base,
|
|
30770
30875
|
success: false,
|
|
30771
30876
|
error: `STORYBOARD.md not found at ${storyboardPath2}. Run \`vibe init <dir>\` first.`
|
|
30772
30877
|
};
|
|
30773
30878
|
}
|
|
30774
|
-
const parsed = parseStoryboard(await
|
|
30879
|
+
const parsed = parseStoryboard(await readFile11(storyboardPath2, "utf-8"));
|
|
30775
30880
|
const beat = parsed.beats.find((b) => b.id === beatId);
|
|
30776
30881
|
if (!beat) {
|
|
30777
30882
|
const available = parsed.beats.map((b) => b.id).join(", ") || "(none)";
|
|
@@ -30841,8 +30946,8 @@ async function executeSceneSubmit(opts) {
|
|
|
30841
30946
|
if (opts.validateOnly) {
|
|
30842
30947
|
return { ...base, success: true };
|
|
30843
30948
|
}
|
|
30844
|
-
const absPath =
|
|
30845
|
-
await mkdir6(
|
|
30949
|
+
const absPath = join9(projectDir, scenePath);
|
|
30950
|
+
await mkdir6(join9(projectDir, "compositions"), { recursive: true });
|
|
30846
30951
|
await writeFile9(absPath, html.endsWith("\n") ? html : `${html}
|
|
30847
30952
|
`, "utf-8");
|
|
30848
30953
|
return {
|
|
@@ -30866,9 +30971,9 @@ var init_scene_submit = __esm({
|
|
|
30866
30971
|
});
|
|
30867
30972
|
|
|
30868
30973
|
// src/commands/_shared/sub-comp-duration-sync.ts
|
|
30869
|
-
import { existsSync as
|
|
30870
|
-
import { readFile as
|
|
30871
|
-
import { join as
|
|
30974
|
+
import { existsSync as existsSync11 } from "node:fs";
|
|
30975
|
+
import { readFile as readFile12 } from "node:fs/promises";
|
|
30976
|
+
import { join as join10, resolve as resolve14 } from "node:path";
|
|
30872
30977
|
function syncSubCompositionDurationHtml(html, beatId, newDurationSec, fileRel) {
|
|
30873
30978
|
const compositionId = `scene-${beatId}`;
|
|
30874
30979
|
const file = fileRel ?? `compositions/${compositionId}.html`;
|
|
@@ -30960,15 +31065,15 @@ async function createSubCompDurationSyncPlans(opts) {
|
|
|
30960
31065
|
const results = [];
|
|
30961
31066
|
for (const beat of opts.beats) {
|
|
30962
31067
|
const fileRel = `compositions/scene-${beat.id}.html`;
|
|
30963
|
-
const filePath =
|
|
30964
|
-
if (!
|
|
31068
|
+
const filePath = join10(projectDir, fileRel);
|
|
31069
|
+
if (!existsSync11(filePath)) continue;
|
|
30965
31070
|
const newDurationSec = await resolveSyncedBeatDuration({
|
|
30966
31071
|
projectDir,
|
|
30967
31072
|
beatDuration: beat.duration,
|
|
30968
31073
|
narrationPath: beat.narrationPath,
|
|
30969
31074
|
sceneDurationSec: beat.sceneDurationSec
|
|
30970
31075
|
});
|
|
30971
|
-
const html = await
|
|
31076
|
+
const html = await readFile12(filePath, "utf-8");
|
|
30972
31077
|
const outcome = syncSubCompositionDurationHtml(html, beat.id, newDurationSec, fileRel);
|
|
30973
31078
|
results.push({ ...outcome, file: fileRel, beatId: beat.id });
|
|
30974
31079
|
}
|
|
@@ -30993,7 +31098,7 @@ var init_sub_comp_duration_sync = __esm({
|
|
|
30993
31098
|
|
|
30994
31099
|
// src/commands/_shared/review-report.ts
|
|
30995
31100
|
import { writeFile as writeFile10 } from "node:fs/promises";
|
|
30996
|
-
import { join as
|
|
31101
|
+
import { join as join11, resolve as resolve15 } from "node:path";
|
|
30997
31102
|
function statusFromIssues(issues) {
|
|
30998
31103
|
if (issues.some((issue) => issue.severity === "error")) return "fail";
|
|
30999
31104
|
if (issues.some((issue) => issue.severity === "warning")) return "warn";
|
|
@@ -31171,7 +31276,7 @@ function buildReviewReport(opts) {
|
|
|
31171
31276
|
});
|
|
31172
31277
|
}
|
|
31173
31278
|
function defaultReviewReportPath(projectDir) {
|
|
31174
|
-
return
|
|
31279
|
+
return join11(resolve15(projectDir), "review-report.json");
|
|
31175
31280
|
}
|
|
31176
31281
|
async function writeReviewReport(path2, report) {
|
|
31177
31282
|
await writeFile10(path2, JSON.stringify(report, null, 2) + "\n", "utf-8");
|
|
@@ -31486,7 +31591,7 @@ var init_review_report = __esm({
|
|
|
31486
31591
|
});
|
|
31487
31592
|
|
|
31488
31593
|
// src/commands/_shared/scene-repair.ts
|
|
31489
|
-
import { readFile as
|
|
31594
|
+
import { readFile as readFile13, writeFile as writeFile11 } from "node:fs/promises";
|
|
31490
31595
|
import { relative as relative5, resolve as resolve16 } from "node:path";
|
|
31491
31596
|
import { runHyperframeLint as runHyperframeLint3 } from "@hyperframes/producer";
|
|
31492
31597
|
async function executeSceneRepair(opts) {
|
|
@@ -31532,7 +31637,7 @@ async function executeSceneRepair(opts) {
|
|
|
31532
31637
|
}
|
|
31533
31638
|
for (const target of targets) {
|
|
31534
31639
|
const rel = relative5(projectDir, target.abs) || target.abs;
|
|
31535
|
-
const html = await
|
|
31640
|
+
const html = await readFile13(target.abs, "utf-8");
|
|
31536
31641
|
const findings = lintHtml(html, rel, target.isSub);
|
|
31537
31642
|
const { html: nextHtml, fixedCodes } = applyMechanicalFixes(html, findings);
|
|
31538
31643
|
if (fixedCodes.length > 0) {
|
|
@@ -31905,8 +32010,8 @@ var init_storyboard_edit = __esm({
|
|
|
31905
32010
|
|
|
31906
32011
|
// src/commands/scene.ts
|
|
31907
32012
|
import { resolve as resolve17, relative as relative6, dirname as dirname4 } from "node:path";
|
|
31908
|
-
import { mkdir as mkdir7, readFile as
|
|
31909
|
-
import { existsSync as
|
|
32013
|
+
import { mkdir as mkdir7, readFile as readFile14, writeFile as writeFile12, access as access5, copyFile } from "node:fs/promises";
|
|
32014
|
+
import { existsSync as existsSync12 } from "node:fs";
|
|
31910
32015
|
function validateDuration(value) {
|
|
31911
32016
|
const n = parseFloat(value);
|
|
31912
32017
|
if (!Number.isFinite(n) || n <= 0 || n > 3600) {
|
|
@@ -31970,8 +32075,8 @@ function readClipStart(rootHtml, beatId) {
|
|
|
31970
32075
|
async function resolveNarrationText(value) {
|
|
31971
32076
|
if (!value) return void 0;
|
|
31972
32077
|
const looksLikePath = /\.[a-z]{2,4}$/i.test(value) || value.includes("/") || value.includes("\\");
|
|
31973
|
-
if (looksLikePath &&
|
|
31974
|
-
return (await
|
|
32078
|
+
if (looksLikePath && existsSync12(value)) {
|
|
32079
|
+
return (await readFile14(value, "utf-8")).trim();
|
|
31975
32080
|
}
|
|
31976
32081
|
return value.trim();
|
|
31977
32082
|
}
|
|
@@ -32010,7 +32115,7 @@ async function executeSceneAdd(opts) {
|
|
|
32010
32115
|
if (!opts.force && await pathExists2(scenePath)) {
|
|
32011
32116
|
return errResult(`Scene already exists: ${sceneRel}. Re-run with --force to overwrite.`);
|
|
32012
32117
|
}
|
|
32013
|
-
const rootHtmlBefore = await
|
|
32118
|
+
const rootHtmlBefore = await readFile14(rootPath, "utf-8");
|
|
32014
32119
|
let dims = readRootDims(rootHtmlBefore);
|
|
32015
32120
|
if (!dims) {
|
|
32016
32121
|
const cfg2 = await loadVibeProjectConfig(projectDir);
|
|
@@ -32086,31 +32191,20 @@ async function executeSceneAdd(opts) {
|
|
|
32086
32191
|
);
|
|
32087
32192
|
} else {
|
|
32088
32193
|
opts.onProgress?.("Transcribing narration (Whisper word-level)...");
|
|
32089
|
-
|
|
32090
|
-
|
|
32091
|
-
|
|
32092
|
-
|
|
32093
|
-
|
|
32094
|
-
|
|
32095
|
-
|
|
32096
|
-
|
|
32097
|
-
|
|
32098
|
-
|
|
32099
|
-
|
|
32100
|
-
|
|
32101
|
-
|
|
32102
|
-
|
|
32103
|
-
transcriptWords = transcript.words.map((w) => ({
|
|
32104
|
-
text: w.text,
|
|
32105
|
-
start: w.start,
|
|
32106
|
-
end: w.end
|
|
32107
|
-
}));
|
|
32108
|
-
} else if (transcript.status === "failed") {
|
|
32109
|
-
opts.onProgress?.(`Transcribe failed: ${transcript.error ?? "unknown error"}`);
|
|
32110
|
-
}
|
|
32111
|
-
} catch (error) {
|
|
32112
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
32113
|
-
opts.onProgress?.(`Transcribe failed: ${msg}`);
|
|
32194
|
+
const words = await transcribeNarrationWords(audioAbsPath, {
|
|
32195
|
+
apiKey: whisperKey,
|
|
32196
|
+
language: opts.transcribeLanguage
|
|
32197
|
+
});
|
|
32198
|
+
if (words.length > 0) {
|
|
32199
|
+
transcriptRelPath = beatTranscriptRelPath(id);
|
|
32200
|
+
const transcriptAbs = resolve17(projectDir, transcriptRelPath);
|
|
32201
|
+
await writeFile12(transcriptAbs, JSON.stringify(words, null, 2), "utf-8");
|
|
32202
|
+
transcriptWordCount = words.length;
|
|
32203
|
+
transcriptWords = words;
|
|
32204
|
+
} else {
|
|
32205
|
+
opts.onProgress?.(
|
|
32206
|
+
"Transcribe produced no word timings \u2014 narration plays without word-sync"
|
|
32207
|
+
);
|
|
32114
32208
|
}
|
|
32115
32209
|
}
|
|
32116
32210
|
}
|
|
@@ -32219,7 +32313,7 @@ async function executeSceneAdd(opts) {
|
|
|
32219
32313
|
const shouldSyncStoryboard = opts.syncStoryboard !== false && await pathExists2(storyboardAbsPath);
|
|
32220
32314
|
if (shouldSyncStoryboard) {
|
|
32221
32315
|
opts.onProgress?.("Syncing STORYBOARD.md...");
|
|
32222
|
-
const before = await
|
|
32316
|
+
const before = await readFile14(storyboardAbsPath, "utf-8");
|
|
32223
32317
|
const synced = upsertStoryboardBeat(before, {
|
|
32224
32318
|
beatId: id,
|
|
32225
32319
|
title: opts.headline,
|
|
@@ -32261,7 +32355,7 @@ async function executeSceneAdd(opts) {
|
|
|
32261
32355
|
});
|
|
32262
32356
|
const blockingIssue = syncPlan.issues.find((issue) => issue.severity === "error");
|
|
32263
32357
|
if (blockingIssue) return errResult(blockingIssue.message);
|
|
32264
|
-
const rootHtmlAfterSync = await
|
|
32358
|
+
const rootHtmlAfterSync = await readFile14(rootPath, "utf-8");
|
|
32265
32359
|
start = readClipStart(rootHtmlAfterSync, id) ?? 0;
|
|
32266
32360
|
} else {
|
|
32267
32361
|
start = nextSceneStart(rootHtmlBefore, SCENE_OVERLAP_SECONDS);
|
|
@@ -32293,7 +32387,7 @@ async function resolveSceneRepairTarget(rootArg = "index.html", projectArg = "."
|
|
|
32293
32387
|
const root = rootArg || "index.html";
|
|
32294
32388
|
const project = projectArg || ".";
|
|
32295
32389
|
const defaultProject = project === ".";
|
|
32296
|
-
if (defaultProject && root !== "index.html" && !root.toLowerCase().endsWith(".html") &&
|
|
32390
|
+
if (defaultProject && root !== "index.html" && !root.toLowerCase().endsWith(".html") && existsSync12(resolve17(root, "index.html"))) {
|
|
32297
32391
|
return { projectDir: resolve17(root), rootRel: "index.html" };
|
|
32298
32392
|
}
|
|
32299
32393
|
return { projectDir: resolve17(project), rootRel: root };
|
|
@@ -32324,6 +32418,7 @@ var init_scene = __esm({
|
|
|
32324
32418
|
init_agent_host_detect();
|
|
32325
32419
|
init_install_skill();
|
|
32326
32420
|
init_compose_prompts();
|
|
32421
|
+
init_transcribe_narration();
|
|
32327
32422
|
init_scene_submit();
|
|
32328
32423
|
init_scene_repair();
|
|
32329
32424
|
init_root_sync();
|
|
@@ -32493,7 +32588,7 @@ Run 'vibe schema scene.<command>' for structured parameter info.`
|
|
|
32493
32588
|
}
|
|
32494
32589
|
html = Buffer.concat(chunks).toString("utf-8");
|
|
32495
32590
|
} else if (options.file) {
|
|
32496
|
-
html = await
|
|
32591
|
+
html = await readFile14(resolve17(String(options.file)), "utf-8");
|
|
32497
32592
|
} else {
|
|
32498
32593
|
exitWithError(
|
|
32499
32594
|
usageError(
|
|
@@ -32947,8 +33042,8 @@ __export(remotion_exports, {
|
|
|
32947
33042
|
wrapComponentWithVideo: () => wrapComponentWithVideo
|
|
32948
33043
|
});
|
|
32949
33044
|
import { writeFile as writeFile14, mkdir as mkdir8, rm, copyFile as copyFile2 } from "node:fs/promises";
|
|
32950
|
-
import { existsSync as
|
|
32951
|
-
import { join as
|
|
33045
|
+
import { existsSync as existsSync14 } from "node:fs";
|
|
33046
|
+
import { join as join12 } from "node:path";
|
|
32952
33047
|
import { tmpdir } from "node:os";
|
|
32953
33048
|
import { createHash as createHash3 } from "node:crypto";
|
|
32954
33049
|
async function ensureRemotionInstalled() {
|
|
@@ -32967,7 +33062,7 @@ async function ensureRemotionInstalled() {
|
|
|
32967
33062
|
}
|
|
32968
33063
|
}
|
|
32969
33064
|
async function scaffoldRemotionProject(componentCode, componentName, opts) {
|
|
32970
|
-
const dir =
|
|
33065
|
+
const dir = join12(tmpdir(), `vibe_motion_${Date.now()}`);
|
|
32971
33066
|
await mkdir8(dir, { recursive: true });
|
|
32972
33067
|
const deps = {
|
|
32973
33068
|
remotion: REMOTION_VERSION,
|
|
@@ -32986,7 +33081,7 @@ async function scaffoldRemotionProject(componentCode, componentName, opts) {
|
|
|
32986
33081
|
dependencies: deps
|
|
32987
33082
|
};
|
|
32988
33083
|
const packageJsonStr = JSON.stringify(packageJson, null, 2);
|
|
32989
|
-
await writeFile14(
|
|
33084
|
+
await writeFile14(join12(dir, "package.json"), packageJsonStr);
|
|
32990
33085
|
const tsconfig = {
|
|
32991
33086
|
compilerOptions: {
|
|
32992
33087
|
target: "ES2020",
|
|
@@ -32998,8 +33093,8 @@ async function scaffoldRemotionProject(componentCode, componentName, opts) {
|
|
|
32998
33093
|
skipLibCheck: true
|
|
32999
33094
|
}
|
|
33000
33095
|
};
|
|
33001
|
-
await writeFile14(
|
|
33002
|
-
await writeFile14(
|
|
33096
|
+
await writeFile14(join12(dir, "tsconfig.json"), JSON.stringify(tsconfig, null, 2));
|
|
33097
|
+
await writeFile14(join12(dir, "Component.tsx"), componentCode);
|
|
33003
33098
|
const rootCode = `import { registerRoot, Composition } from "remotion";
|
|
33004
33099
|
import { ${componentName} } from "./Component";
|
|
33005
33100
|
|
|
@@ -33018,14 +33113,14 @@ const Root = () => {
|
|
|
33018
33113
|
|
|
33019
33114
|
registerRoot(Root);
|
|
33020
33115
|
`;
|
|
33021
|
-
await writeFile14(
|
|
33116
|
+
await writeFile14(join12(dir, "Root.tsx"), rootCode);
|
|
33022
33117
|
const depsHash = createHash3("md5").update(packageJsonStr).digest("hex").slice(0, 12);
|
|
33023
|
-
const cacheMarker =
|
|
33024
|
-
const cachedModules =
|
|
33025
|
-
if (
|
|
33118
|
+
const cacheMarker = join12(REMOTION_CACHE_DIR, `.deps-${depsHash}`);
|
|
33119
|
+
const cachedModules = join12(REMOTION_CACHE_DIR, "node_modules");
|
|
33120
|
+
if (existsSync14(cacheMarker) && existsSync14(cachedModules)) {
|
|
33026
33121
|
const { symlink } = await import("node:fs/promises");
|
|
33027
33122
|
try {
|
|
33028
|
-
await symlink(cachedModules,
|
|
33123
|
+
await symlink(cachedModules, join12(dir, "node_modules"), "dir");
|
|
33029
33124
|
} catch {
|
|
33030
33125
|
await this_npmInstall(dir);
|
|
33031
33126
|
}
|
|
@@ -33033,12 +33128,12 @@ registerRoot(Root);
|
|
|
33033
33128
|
await this_npmInstall(dir);
|
|
33034
33129
|
try {
|
|
33035
33130
|
await mkdir8(REMOTION_CACHE_DIR, { recursive: true });
|
|
33036
|
-
await writeFile14(
|
|
33037
|
-
if (
|
|
33131
|
+
await writeFile14(join12(REMOTION_CACHE_DIR, "package.json"), packageJsonStr);
|
|
33132
|
+
if (existsSync14(join12(dir, "node_modules"))) {
|
|
33038
33133
|
const { rename: rename5, symlink } = await import("node:fs/promises");
|
|
33039
|
-
await rename5(
|
|
33134
|
+
await rename5(join12(dir, "node_modules"), cachedModules).catch(() => {
|
|
33040
33135
|
});
|
|
33041
|
-
await symlink(cachedModules,
|
|
33136
|
+
await symlink(cachedModules, join12(dir, "node_modules"), "dir").catch(() => {
|
|
33042
33137
|
});
|
|
33043
33138
|
await writeFile14(cacheMarker, depsHash);
|
|
33044
33139
|
}
|
|
@@ -33075,7 +33170,7 @@ async function renderMotion(options) {
|
|
|
33075
33170
|
}
|
|
33076
33171
|
);
|
|
33077
33172
|
try {
|
|
33078
|
-
const entryPoint =
|
|
33173
|
+
const entryPoint = join12(dir, "Root.tsx");
|
|
33079
33174
|
const { execFile: execFile3 } = await import("node:child_process");
|
|
33080
33175
|
const { promisify: promisify3 } = await import("node:util");
|
|
33081
33176
|
const execFileAsync3 = promisify3(execFile3);
|
|
@@ -33208,10 +33303,10 @@ async function renderWithEmbeddedImage(options) {
|
|
|
33208
33303
|
}
|
|
33209
33304
|
);
|
|
33210
33305
|
try {
|
|
33211
|
-
const publicDir =
|
|
33306
|
+
const publicDir = join12(dir, "public");
|
|
33212
33307
|
await mkdir8(publicDir, { recursive: true });
|
|
33213
|
-
await copyFile2(options.imagePath,
|
|
33214
|
-
const entryPoint =
|
|
33308
|
+
await copyFile2(options.imagePath, join12(publicDir, options.imageFileName));
|
|
33309
|
+
const entryPoint = join12(dir, "Root.tsx");
|
|
33215
33310
|
const mp4Out = options.outputPath.replace(/\.\w+$/, ".mp4");
|
|
33216
33311
|
const { execFile: execFileImg } = await import("node:child_process");
|
|
33217
33312
|
const { promisify: promisifyImg } = await import("node:util");
|
|
@@ -33272,10 +33367,10 @@ async function renderWithEmbeddedVideo(options) {
|
|
|
33272
33367
|
}
|
|
33273
33368
|
);
|
|
33274
33369
|
try {
|
|
33275
|
-
const publicDir =
|
|
33370
|
+
const publicDir = join12(dir, "public");
|
|
33276
33371
|
await mkdir8(publicDir, { recursive: true });
|
|
33277
|
-
await copyFile2(options.videoPath,
|
|
33278
|
-
const entryPoint =
|
|
33372
|
+
await copyFile2(options.videoPath, join12(publicDir, options.videoFileName));
|
|
33373
|
+
const entryPoint = join12(dir, "Root.tsx");
|
|
33279
33374
|
const mp4VideoOnly = options.outputPath.replace(/\.\w+$/, "_video_only.mp4");
|
|
33280
33375
|
const { execFile: execFileVid } = await import("node:child_process");
|
|
33281
33376
|
const { promisify: promisifyVid } = await import("node:util");
|
|
@@ -33691,7 +33786,7 @@ var init_remotion = __esm({
|
|
|
33691
33786
|
"use strict";
|
|
33692
33787
|
init_exec_safe();
|
|
33693
33788
|
REMOTION_VERSION = "4.0.447";
|
|
33694
|
-
REMOTION_CACHE_DIR =
|
|
33789
|
+
REMOTION_CACHE_DIR = join12(tmpdir(), "vibe_remotion_cache");
|
|
33695
33790
|
}
|
|
33696
33791
|
});
|
|
33697
33792
|
|
|
@@ -33702,8 +33797,8 @@ __export(ai_motion_exports, {
|
|
|
33702
33797
|
registerMotionCommand: () => registerMotionCommand
|
|
33703
33798
|
});
|
|
33704
33799
|
import { resolve as resolve19 } from "node:path";
|
|
33705
|
-
import { existsSync as
|
|
33706
|
-
import { readFile as
|
|
33800
|
+
import { existsSync as existsSync15 } from "node:fs";
|
|
33801
|
+
import { readFile as readFile16, writeFile as writeFile15 } from "node:fs/promises";
|
|
33707
33802
|
async function getOptionalGoogleApiKey() {
|
|
33708
33803
|
const configKey = await getApiKeyFromConfig("google");
|
|
33709
33804
|
if (configKey) return configKey;
|
|
@@ -33750,7 +33845,7 @@ async function executeMotion(options) {
|
|
|
33750
33845
|
};
|
|
33751
33846
|
}
|
|
33752
33847
|
const imagePath = resolve19(process.cwd(), options.image);
|
|
33753
|
-
const imageBuffer = await
|
|
33848
|
+
const imageBuffer = await readFile16(imagePath);
|
|
33754
33849
|
const gemini = new GeminiProvider();
|
|
33755
33850
|
await gemini.initialize({ apiKey: geminiApiKey });
|
|
33756
33851
|
const analysisResult = await gemini.analyzeImage(
|
|
@@ -33787,7 +33882,7 @@ Use this image analysis to inform the color palette, typography placement, and o
|
|
|
33787
33882
|
}
|
|
33788
33883
|
if (geminiApiKey) {
|
|
33789
33884
|
const videoPath = resolve19(process.cwd(), options.video);
|
|
33790
|
-
const videoBuffer = await
|
|
33885
|
+
const videoBuffer = await readFile16(videoPath);
|
|
33791
33886
|
const gemini = new GeminiProvider();
|
|
33792
33887
|
await gemini.initialize({ apiKey: geminiApiKey });
|
|
33793
33888
|
const analysisPrompt = options.understandingPrompt || `Analyze this video for motion graphics composition. Return concise production guidance:
|
|
@@ -33818,10 +33913,10 @@ Use this video understanding to place motion graphics in safe zones, match the c
|
|
|
33818
33913
|
let result;
|
|
33819
33914
|
if (options.fromTsx) {
|
|
33820
33915
|
const tsxPath = resolve19(process.cwd(), options.fromTsx);
|
|
33821
|
-
if (!
|
|
33916
|
+
if (!existsSync15(tsxPath)) {
|
|
33822
33917
|
return { success: false, error: `TSX file not found: ${tsxPath}` };
|
|
33823
33918
|
}
|
|
33824
|
-
const existingCode = await
|
|
33919
|
+
const existingCode = await readFile16(tsxPath, "utf-8");
|
|
33825
33920
|
if (useGemini) {
|
|
33826
33921
|
const gemini = new GeminiProvider();
|
|
33827
33922
|
await gemini.initialize({ apiKey });
|
|
@@ -34598,7 +34693,7 @@ var init_ai_helpers = __esm({
|
|
|
34598
34693
|
|
|
34599
34694
|
// src/commands/generate/storyboard.ts
|
|
34600
34695
|
import { resolve as resolve22 } from "node:path";
|
|
34601
|
-
import { readFile as
|
|
34696
|
+
import { readFile as readFile17, writeFile as writeFile18 } from "node:fs/promises";
|
|
34602
34697
|
async function executeStoryboard(options) {
|
|
34603
34698
|
const { content, duration, creativity = "low", output, apiKey } = options;
|
|
34604
34699
|
try {
|
|
@@ -34646,7 +34741,7 @@ function registerStoryboardCommand(parent) {
|
|
|
34646
34741
|
let textContent = content;
|
|
34647
34742
|
if (options.file) {
|
|
34648
34743
|
const filePath = resolve22(process.cwd(), content);
|
|
34649
|
-
textContent = await
|
|
34744
|
+
textContent = await readFile17(filePath, "utf-8");
|
|
34650
34745
|
}
|
|
34651
34746
|
if (options.dryRun) {
|
|
34652
34747
|
outputSuccess({
|
|
@@ -35718,7 +35813,7 @@ __export(ai_video_exports, {
|
|
|
35718
35813
|
executeVideoGenerate: () => executeVideoGenerate,
|
|
35719
35814
|
executeVideoStatus: () => executeVideoStatus
|
|
35720
35815
|
});
|
|
35721
|
-
import { readFile as
|
|
35816
|
+
import { readFile as readFile18, writeFile as writeFile21 } from "node:fs/promises";
|
|
35722
35817
|
import { resolve as resolve25 } from "node:path";
|
|
35723
35818
|
async function executeVideoGenerate(options) {
|
|
35724
35819
|
const {
|
|
@@ -35758,7 +35853,7 @@ async function executeVideoGenerate(options) {
|
|
|
35758
35853
|
let referenceImageMimeType;
|
|
35759
35854
|
if (image) {
|
|
35760
35855
|
const imagePath = resolve25(process.cwd(), image);
|
|
35761
|
-
const imageBuffer = await
|
|
35856
|
+
const imageBuffer = await readFile18(imagePath);
|
|
35762
35857
|
const ext = image.toLowerCase().split(".").pop();
|
|
35763
35858
|
const mimeTypes2 = {
|
|
35764
35859
|
jpg: "image/jpeg",
|
|
@@ -36006,7 +36101,7 @@ async function fileInputToUrlOrDataUri(input, fallbackMimeType) {
|
|
|
36006
36101
|
return input;
|
|
36007
36102
|
}
|
|
36008
36103
|
const absPath = resolve25(process.cwd(), input);
|
|
36009
|
-
const buffer = await
|
|
36104
|
+
const buffer = await readFile18(absPath);
|
|
36010
36105
|
return `data:${mimeTypeForPath(input, fallbackMimeType)};base64,${buffer.toString("base64")}`;
|
|
36011
36106
|
}
|
|
36012
36107
|
function mimeTypeForPath(path2, fallback2) {
|
|
@@ -36258,15 +36353,15 @@ var init_ai_video = __esm({
|
|
|
36258
36353
|
});
|
|
36259
36354
|
|
|
36260
36355
|
// src/commands/_shared/build-asset-metadata.ts
|
|
36261
|
-
import { existsSync as
|
|
36356
|
+
import { existsSync as existsSync16, readFileSync as readFileSync2 } from "node:fs";
|
|
36262
36357
|
import { mkdir as mkdir10, writeFile as writeFile22 } from "node:fs/promises";
|
|
36263
|
-
import { dirname as dirname6, join as
|
|
36358
|
+
import { dirname as dirname6, join as join13 } from "node:path";
|
|
36264
36359
|
function assetMetadataPath(kind, beatId) {
|
|
36265
36360
|
return `.vibeframe/assets/${kind}-${beatId}.json`;
|
|
36266
36361
|
}
|
|
36267
36362
|
function readAssetMetadata(projectDir, kind, beatId) {
|
|
36268
|
-
const path2 =
|
|
36269
|
-
if (!
|
|
36363
|
+
const path2 = join13(projectDir, assetMetadataPath(kind, beatId));
|
|
36364
|
+
if (!existsSync16(path2)) return null;
|
|
36270
36365
|
try {
|
|
36271
36366
|
const parsed = JSON.parse(readFileSync2(path2, "utf-8"));
|
|
36272
36367
|
if (parsed.schemaVersion !== "1" || parsed.kind !== kind || parsed.beatId !== beatId || typeof parsed.cacheKey !== "string") {
|
|
@@ -36295,7 +36390,7 @@ function isFreshCanonicalAsset(opts) {
|
|
|
36295
36390
|
}
|
|
36296
36391
|
async function writeAssetMetadata(opts) {
|
|
36297
36392
|
const rel = assetMetadataPath(opts.kind, opts.beatId);
|
|
36298
|
-
const abs =
|
|
36393
|
+
const abs = join13(opts.projectDir, rel);
|
|
36299
36394
|
const metadata = {
|
|
36300
36395
|
schemaVersion: "1",
|
|
36301
36396
|
kind: opts.kind,
|
|
@@ -36319,20 +36414,20 @@ var init_build_asset_metadata = __esm({
|
|
|
36319
36414
|
|
|
36320
36415
|
// src/commands/_shared/status-jobs.ts
|
|
36321
36416
|
import { createHash as createHash5, randomUUID as randomUUID2 } from "node:crypto";
|
|
36322
|
-
import { existsSync as
|
|
36323
|
-
import { copyFile as copyFile3, mkdir as mkdir11, readFile as
|
|
36324
|
-
import { dirname as dirname7, join as
|
|
36417
|
+
import { existsSync as existsSync17, statSync as statSync2 } from "node:fs";
|
|
36418
|
+
import { copyFile as copyFile3, mkdir as mkdir11, readFile as readFile19, readdir as readdir2, stat as stat4, writeFile as writeFile23 } from "node:fs/promises";
|
|
36419
|
+
import { dirname as dirname7, join as join14, parse as parse2, relative as relative7, resolve as resolve26 } from "node:path";
|
|
36325
36420
|
function isLocalJob(record) {
|
|
36326
36421
|
return record.jobType === "build" || record.jobType === "render";
|
|
36327
36422
|
}
|
|
36328
36423
|
function findProjectRoot(start = process.cwd()) {
|
|
36329
36424
|
let dir = resolve26(start);
|
|
36330
36425
|
try {
|
|
36331
|
-
if (
|
|
36426
|
+
if (existsSync17(dir) && statSync2(dir).isFile()) dir = dirname7(dir);
|
|
36332
36427
|
} catch {
|
|
36333
36428
|
}
|
|
36334
36429
|
for (; ; ) {
|
|
36335
|
-
if (
|
|
36430
|
+
if (existsSync17(join14(dir, "vibe.config.json")) || existsSync17(join14(dir, "STORYBOARD.md"))) {
|
|
36336
36431
|
return dir;
|
|
36337
36432
|
}
|
|
36338
36433
|
const parent = dirname7(dir);
|
|
@@ -36341,7 +36436,7 @@ function findProjectRoot(start = process.cwd()) {
|
|
|
36341
36436
|
}
|
|
36342
36437
|
}
|
|
36343
36438
|
function jobsDir(projectDir) {
|
|
36344
|
-
return
|
|
36439
|
+
return join14(resolve26(projectDir), ".vibeframe", "jobs");
|
|
36345
36440
|
}
|
|
36346
36441
|
function normalizeJobStatus(status) {
|
|
36347
36442
|
const value = String(status ?? "").toLowerCase();
|
|
@@ -36413,18 +36508,18 @@ async function readJobRecord(jobId, projectDir) {
|
|
|
36413
36508
|
const id = normalizeJobId(jobId);
|
|
36414
36509
|
const project = resolve26(projectDir ?? findProjectRoot());
|
|
36415
36510
|
const path2 = jobRecordPath(id, project);
|
|
36416
|
-
if (!
|
|
36417
|
-
return parseJobRecord(await
|
|
36511
|
+
if (!existsSync17(path2)) return null;
|
|
36512
|
+
return parseJobRecord(await readFile19(path2, "utf-8"));
|
|
36418
36513
|
}
|
|
36419
36514
|
async function listJobRecords(projectDir) {
|
|
36420
36515
|
const dir = jobsDir(projectDir);
|
|
36421
|
-
if (!
|
|
36516
|
+
if (!existsSync17(dir)) return [];
|
|
36422
36517
|
const entries = await readdir2(dir, { withFileTypes: true });
|
|
36423
36518
|
const records = [];
|
|
36424
36519
|
for (const entry of entries) {
|
|
36425
36520
|
if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
|
|
36426
36521
|
try {
|
|
36427
|
-
const parsed = parseJobRecord(await
|
|
36522
|
+
const parsed = parseJobRecord(await readFile19(join14(dir, entry.name), "utf-8"));
|
|
36428
36523
|
if (parsed) records.push(parsed);
|
|
36429
36524
|
} catch {
|
|
36430
36525
|
}
|
|
@@ -36518,7 +36613,7 @@ async function refreshLocalJobRecord(record, opts) {
|
|
|
36518
36613
|
const heartbeatAge = record.heartbeatAt ? Date.now() - new Date(record.heartbeatAt).getTime() : Number.POSITIVE_INFINITY;
|
|
36519
36614
|
const orphaned = !isPidAlive(record.pid) || heartbeatAge > LOCAL_JOB_ORPHAN_AFTER_MS;
|
|
36520
36615
|
if (orphaned) {
|
|
36521
|
-
const hint = `The process that ran this ${record.jobType} job is gone. Check ${
|
|
36616
|
+
const hint = `The process that ran this ${record.jobType} job is gone. Check ${join14(record.projectDir, record.jobType === "render" ? "render-report.json" : "build-report.json")} \u2014 the work may have completed before the process exited.`;
|
|
36522
36617
|
warnings.push(hint);
|
|
36523
36618
|
updated = stripUndefined2({
|
|
36524
36619
|
...record,
|
|
@@ -36681,7 +36776,7 @@ function normalizeJobId(jobId) {
|
|
|
36681
36776
|
return id;
|
|
36682
36777
|
}
|
|
36683
36778
|
function jobRecordPath(jobId, projectDir) {
|
|
36684
|
-
return
|
|
36779
|
+
return join14(jobsDir(projectDir), `${normalizeJobId(jobId)}.json`);
|
|
36685
36780
|
}
|
|
36686
36781
|
function parseJobRecord(raw) {
|
|
36687
36782
|
const parsed = JSON.parse(raw);
|
|
@@ -36748,7 +36843,7 @@ async function maybeCacheOutput(record) {
|
|
|
36748
36843
|
}
|
|
36749
36844
|
async function maybeWriteCompletedAssetMetadata(record) {
|
|
36750
36845
|
const kind = assetKindForJob(record);
|
|
36751
|
-
if (record.status !== "completed" || !kind || !record.beatId || !record.assetCue || !record.cacheKey || !record.outputPath || !
|
|
36846
|
+
if (record.status !== "completed" || !kind || !record.beatId || !record.assetCue || !record.cacheKey || !record.outputPath || !existsSync17(record.outputPath)) {
|
|
36752
36847
|
return;
|
|
36753
36848
|
}
|
|
36754
36849
|
try {
|
|
@@ -36767,7 +36862,7 @@ async function maybeWriteCompletedAssetMetadata(record) {
|
|
|
36767
36862
|
}
|
|
36768
36863
|
}
|
|
36769
36864
|
async function refreshBuildReportFromJobs(projectDir, records) {
|
|
36770
|
-
const reportPath =
|
|
36865
|
+
const reportPath = join14(projectDir, "build-report.json");
|
|
36771
36866
|
const report = await readJson(reportPath);
|
|
36772
36867
|
if (!report || !Array.isArray(report.beats)) return;
|
|
36773
36868
|
let changed = false;
|
|
@@ -36820,7 +36915,7 @@ async function refreshBuildReportFromJobs(projectDir, records) {
|
|
|
36820
36915
|
}
|
|
36821
36916
|
}
|
|
36822
36917
|
async function readBuildSummary(projectDir) {
|
|
36823
|
-
const reportPath =
|
|
36918
|
+
const reportPath = join14(projectDir, "build-report.json");
|
|
36824
36919
|
const report = await readJson(reportPath);
|
|
36825
36920
|
if (!report) return null;
|
|
36826
36921
|
const info = await statOrNull(reportPath);
|
|
@@ -36843,7 +36938,7 @@ async function readBuildSummary(projectDir) {
|
|
|
36843
36938
|
});
|
|
36844
36939
|
}
|
|
36845
36940
|
async function readReviewSummary(projectDir) {
|
|
36846
|
-
const reportPath =
|
|
36941
|
+
const reportPath = join14(projectDir, "review-report.json");
|
|
36847
36942
|
const report = await readJson(reportPath);
|
|
36848
36943
|
if (!report) return null;
|
|
36849
36944
|
const info = await statOrNull(reportPath);
|
|
@@ -36932,16 +37027,16 @@ async function readProjectBeatReadiness(projectDir, build, records) {
|
|
|
36932
37027
|
if (fromBeats) return fromBeats;
|
|
36933
37028
|
if (build.beats) return augmentBeatReadinessFromJobs(build.beats, records, report?.jobs);
|
|
36934
37029
|
}
|
|
36935
|
-
const storyboardPath2 =
|
|
36936
|
-
if (!
|
|
37030
|
+
const storyboardPath2 = join14(projectDir, "STORYBOARD.md");
|
|
37031
|
+
if (!existsSync17(storyboardPath2)) {
|
|
36937
37032
|
return { total: 0, assetsReady: 0, compositionsReady: 0, needsAuthor: [] };
|
|
36938
37033
|
}
|
|
36939
37034
|
try {
|
|
36940
|
-
const parsed = parseStoryboard(await
|
|
37035
|
+
const parsed = parseStoryboard(await readFile19(storyboardPath2, "utf-8"));
|
|
36941
37036
|
const needsAuthor = [];
|
|
36942
37037
|
let compositionsReady = 0;
|
|
36943
37038
|
for (const beat of parsed.beats) {
|
|
36944
|
-
if (
|
|
37039
|
+
if (existsSync17(join14(projectDir, "compositions", `scene-${beat.id}.html`))) {
|
|
36945
37040
|
compositionsReady += 1;
|
|
36946
37041
|
} else {
|
|
36947
37042
|
needsAuthor.push(beat.id);
|
|
@@ -36959,7 +37054,7 @@ async function readProjectBeatReadiness(projectDir, build, records) {
|
|
|
36959
37054
|
}
|
|
36960
37055
|
function deriveProjectWorkflow(opts) {
|
|
36961
37056
|
const retryWith = [];
|
|
36962
|
-
const hasStoryboard =
|
|
37057
|
+
const hasStoryboard = existsSync17(join14(opts.project, "STORYBOARD.md"));
|
|
36963
37058
|
if (!hasStoryboard && !opts.build) {
|
|
36964
37059
|
retryWith.push(`vibe init ${opts.project} --from "<brief>" --json`);
|
|
36965
37060
|
return { status: "empty", currentStage: "init", retryWith };
|
|
@@ -37055,7 +37150,7 @@ function beatReadinessFromBuildBeats(projectDir, beats, records = [], reportJobs
|
|
|
37055
37150
|
if (buildBeatAssetsReady(record) || (id ? buildBeatJobsReady(id, records, reportJobs) : false))
|
|
37056
37151
|
assetsReady += 1;
|
|
37057
37152
|
const compositionPath = typeof record.compositionPath === "string" ? record.compositionPath : id ? `compositions/scene-${id}.html` : void 0;
|
|
37058
|
-
if (compositionPath &&
|
|
37153
|
+
if (compositionPath && existsSync17(resolve26(projectDir, compositionPath))) {
|
|
37059
37154
|
compositionsReady += 1;
|
|
37060
37155
|
} else if (id) {
|
|
37061
37156
|
needsAuthor.push(id);
|
|
@@ -37163,7 +37258,7 @@ function projectRelativePath(projectDir, path2) {
|
|
|
37163
37258
|
}
|
|
37164
37259
|
async function readJson(path2) {
|
|
37165
37260
|
try {
|
|
37166
|
-
return JSON.parse(await
|
|
37261
|
+
return JSON.parse(await readFile19(path2, "utf-8"));
|
|
37167
37262
|
} catch {
|
|
37168
37263
|
return null;
|
|
37169
37264
|
}
|
|
@@ -37202,7 +37297,7 @@ var init_status_jobs = __esm({
|
|
|
37202
37297
|
// src/commands/generate/music.ts
|
|
37203
37298
|
import { resolve as resolve27 } from "node:path";
|
|
37204
37299
|
import { writeFile as writeFile24 } from "node:fs/promises";
|
|
37205
|
-
import { existsSync as
|
|
37300
|
+
import { existsSync as existsSync18 } from "node:fs";
|
|
37206
37301
|
async function executeMusic(options) {
|
|
37207
37302
|
try {
|
|
37208
37303
|
const provider = options.provider || "elevenlabs";
|
|
@@ -37355,7 +37450,7 @@ function registerMusicCommand(parent) {
|
|
|
37355
37450
|
if (options.melody) {
|
|
37356
37451
|
spinner2.text = "Uploading melody reference...";
|
|
37357
37452
|
const absPath = resolve27(process.cwd(), options.melody);
|
|
37358
|
-
if (!
|
|
37453
|
+
if (!existsSync18(absPath)) {
|
|
37359
37454
|
spinner2.fail(`Melody file not found: ${options.melody}`);
|
|
37360
37455
|
exitWithError(notFoundError(options.melody));
|
|
37361
37456
|
}
|
|
@@ -37476,8 +37571,8 @@ __export(ai_image_exports, {
|
|
|
37476
37571
|
executeThumbnailBestFrame: () => executeThumbnailBestFrame
|
|
37477
37572
|
});
|
|
37478
37573
|
import { resolve as resolve28, dirname as dirname8 } from "node:path";
|
|
37479
|
-
import { readFile as
|
|
37480
|
-
import { existsSync as
|
|
37574
|
+
import { readFile as readFile20, writeFile as writeFile25, mkdir as mkdir12 } from "node:fs/promises";
|
|
37575
|
+
import { existsSync as existsSync19 } from "node:fs";
|
|
37481
37576
|
async function executeImageGenerate(options) {
|
|
37482
37577
|
const {
|
|
37483
37578
|
prompt: prompt3,
|
|
@@ -37621,10 +37716,10 @@ async function executeGeminiEdit(options) {
|
|
|
37621
37716
|
const imageBuffers = [];
|
|
37622
37717
|
for (const imagePath of imagePaths) {
|
|
37623
37718
|
const absPath = resolve28(process.cwd(), imagePath);
|
|
37624
|
-
if (!
|
|
37719
|
+
if (!existsSync19(absPath)) {
|
|
37625
37720
|
return { success: false, error: `Image not found: ${absPath}` };
|
|
37626
37721
|
}
|
|
37627
|
-
const buffer = await
|
|
37722
|
+
const buffer = await readFile20(absPath);
|
|
37628
37723
|
imageBuffers.push(buffer);
|
|
37629
37724
|
}
|
|
37630
37725
|
const gemini = new GeminiProvider();
|
|
@@ -37669,7 +37764,7 @@ async function executeThumbnailBestFrame(options) {
|
|
|
37669
37764
|
model = "flash",
|
|
37670
37765
|
apiKey
|
|
37671
37766
|
} = options;
|
|
37672
|
-
if (!
|
|
37767
|
+
if (!existsSync19(videoPath)) {
|
|
37673
37768
|
return { success: false, error: `Video not found: ${videoPath}` };
|
|
37674
37769
|
}
|
|
37675
37770
|
if (!commandExists("ffmpeg")) {
|
|
@@ -37682,7 +37777,7 @@ async function executeThumbnailBestFrame(options) {
|
|
|
37682
37777
|
try {
|
|
37683
37778
|
const gemini = new GeminiProvider();
|
|
37684
37779
|
await gemini.initialize({ apiKey: googleKey });
|
|
37685
|
-
const videoData = await
|
|
37780
|
+
const videoData = await readFile20(videoPath);
|
|
37686
37781
|
const analysisPrompt = prompt3 || 'Analyze this video and find the single best frame for a thumbnail. Look for frames that are visually striking, well-composed, emotionally engaging, and representative of the video content. Avoid blurry frames, transitions, or dark scenes. Return ONLY a JSON object: {"timestamp": <seconds as number>, "reason": "<brief explanation>"}';
|
|
37687
37782
|
const modelId = resolveGeminiTextModel(model);
|
|
37688
37783
|
const result = await gemini.analyzeVideo(videoData, analysisPrompt, {
|
|
@@ -37703,7 +37798,7 @@ async function executeThumbnailBestFrame(options) {
|
|
|
37703
37798
|
reason = reasonMatch[1];
|
|
37704
37799
|
}
|
|
37705
37800
|
await execSafe("ffmpeg", ["-ss", String(timestamp2), "-i", videoPath, "-frames:v", "1", "-q:v", "2", outputPath, "-y"], { timeout: 6e4, maxBuffer: 50 * 1024 * 1024 });
|
|
37706
|
-
if (!
|
|
37801
|
+
if (!existsSync19(outputPath)) {
|
|
37707
37802
|
return { success: false, error: "FFmpeg failed to extract frame" };
|
|
37708
37803
|
}
|
|
37709
37804
|
return {
|
|
@@ -37729,7 +37824,7 @@ var init_ai_image = __esm({
|
|
|
37729
37824
|
|
|
37730
37825
|
// src/commands/generate/thumbnail.ts
|
|
37731
37826
|
import { resolve as resolve29, dirname as dirname9, basename as basename5, extname as extname3 } from "node:path";
|
|
37732
|
-
import { existsSync as
|
|
37827
|
+
import { existsSync as existsSync20 } from "node:fs";
|
|
37733
37828
|
import { writeFile as writeFile26, mkdir as mkdir13 } from "node:fs/promises";
|
|
37734
37829
|
function registerThumbnailCommand(parent) {
|
|
37735
37830
|
parent.command("thumbnail").description("Generate video thumbnail (DALL-E) or extract best frame from video (Gemini)").argument("[description]", "Thumbnail description (for DALL-E generation)").option("-k, --api-key <key>", "API key (OpenAI for generation, Google for best-frame)").option("-o, --output <path>", "Output file path").option("--style <style>", "Platform style: youtube, instagram, tiktok, twitter").option("--best-frame <video>", "Extract best thumbnail frame from video using Gemini AI").option("--prompt <prompt>", "Custom prompt for best-frame analysis").option("--model <model>", `Gemini model: ${GEMINI_TEXT_MODEL_HELP}`, "flash").action(async (description, options) => {
|
|
@@ -37741,7 +37836,7 @@ function registerThumbnailCommand(parent) {
|
|
|
37741
37836
|
}
|
|
37742
37837
|
if (options.bestFrame) {
|
|
37743
37838
|
const absVideoPath = resolve29(process.cwd(), options.bestFrame);
|
|
37744
|
-
if (!
|
|
37839
|
+
if (!existsSync20(absVideoPath)) {
|
|
37745
37840
|
exitWithError(notFoundError(absVideoPath));
|
|
37746
37841
|
}
|
|
37747
37842
|
if (!commandExists("ffmpeg")) {
|
|
@@ -39925,7 +40020,7 @@ var init_dist3 = __esm({
|
|
|
39925
40020
|
|
|
39926
40021
|
// src/commands/generate/video.ts
|
|
39927
40022
|
import { resolve as resolve33 } from "node:path";
|
|
39928
|
-
import { readFile as
|
|
40023
|
+
import { readFile as readFile21, writeFile as writeFile30 } from "node:fs/promises";
|
|
39929
40024
|
function registerVideoCommand(parent) {
|
|
39930
40025
|
parent.command("video").alias("vid").description("Generate video using AI (Seedance, Grok, Kling, Runway, or Veo)").argument("[prompt]", "Text prompt describing the video (interactive if omitted)").option(
|
|
39931
40026
|
"-p, --provider <provider>",
|
|
@@ -40036,7 +40131,7 @@ Examples:
|
|
|
40036
40131
|
let isImageToVideo = false;
|
|
40037
40132
|
if (options.image) {
|
|
40038
40133
|
const imagePath = resolve33(process.cwd(), options.image);
|
|
40039
|
-
const imageBuffer = await
|
|
40134
|
+
const imageBuffer = await readFile21(imagePath);
|
|
40040
40135
|
const ext = options.image.toLowerCase().split(".").pop();
|
|
40041
40136
|
const mimeTypes2 = {
|
|
40042
40137
|
jpg: "image/jpeg",
|
|
@@ -40276,7 +40371,7 @@ Examples:
|
|
|
40276
40371
|
let lastFrame;
|
|
40277
40372
|
if (options.lastFrame) {
|
|
40278
40373
|
const lastFramePath = resolve33(process.cwd(), options.lastFrame);
|
|
40279
|
-
const lastFrameBuffer = await
|
|
40374
|
+
const lastFrameBuffer = await readFile21(lastFramePath);
|
|
40280
40375
|
const ext = options.lastFrame.toLowerCase().split(".").pop();
|
|
40281
40376
|
const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : `image/${ext || "png"}`;
|
|
40282
40377
|
lastFrame = `data:${mimeType};base64,${lastFrameBuffer.toString("base64")}`;
|
|
@@ -40286,7 +40381,7 @@ Examples:
|
|
|
40286
40381
|
refImages = [];
|
|
40287
40382
|
for (const refPath of options.refImages.slice(0, 3)) {
|
|
40288
40383
|
const absRefPath = resolve33(process.cwd(), refPath);
|
|
40289
|
-
const refBuffer = await
|
|
40384
|
+
const refBuffer = await readFile21(absRefPath);
|
|
40290
40385
|
const ext = refPath.toLowerCase().split(".").pop();
|
|
40291
40386
|
const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : `image/${ext || "png"}`;
|
|
40292
40387
|
refImages.push({ base64: refBuffer.toString("base64"), mimeType });
|
|
@@ -40418,7 +40513,7 @@ Examples:
|
|
|
40418
40513
|
if (seedanceEndImagePath && seedanceReferences.length === 0) {
|
|
40419
40514
|
try {
|
|
40420
40515
|
const absEndImagePath = resolve33(process.cwd(), seedanceEndImagePath);
|
|
40421
|
-
const endImageBuffer = await
|
|
40516
|
+
const endImageBuffer = await readFile21(absEndImagePath);
|
|
40422
40517
|
const ext = seedanceEndImagePath.toLowerCase().split(".").pop();
|
|
40423
40518
|
const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : `image/${ext || "png"}`;
|
|
40424
40519
|
const uploadHost = await resolveUploadHost();
|
|
@@ -40535,7 +40630,7 @@ async function fileInputToUrlOrDataUri2(input, fallbackMimeType) {
|
|
|
40535
40630
|
return input;
|
|
40536
40631
|
}
|
|
40537
40632
|
const absPath = resolve33(process.cwd(), input);
|
|
40538
|
-
const buffer = await
|
|
40633
|
+
const buffer = await readFile21(absPath);
|
|
40539
40634
|
return `data:${mimeTypeForPath2(input, fallbackMimeType)};base64,${buffer.toString("base64")}`;
|
|
40540
40635
|
}
|
|
40541
40636
|
function mimeTypeForPath2(path2, fallback2) {
|
|
@@ -40708,8 +40803,8 @@ Run 'vibe schema generate.<command>' for structured parameter info.
|
|
|
40708
40803
|
});
|
|
40709
40804
|
|
|
40710
40805
|
// src/commands/_shared/edit/silence-cut.ts
|
|
40711
|
-
import { existsSync as
|
|
40712
|
-
import { readFile as
|
|
40806
|
+
import { existsSync as existsSync21 } from "node:fs";
|
|
40807
|
+
import { readFile as readFile22 } from "node:fs/promises";
|
|
40713
40808
|
async function detectSilencePeriods(videoPath, noiseThreshold, minDuration) {
|
|
40714
40809
|
const totalDuration = await getVideoDuration(videoPath);
|
|
40715
40810
|
const { stdout, stderr } = await execSafe(
|
|
@@ -40759,7 +40854,7 @@ async function detectSilencePeriodsWithGemini(videoPath, minDuration, options) {
|
|
|
40759
40854
|
}
|
|
40760
40855
|
const gemini = new GeminiProvider();
|
|
40761
40856
|
await gemini.initialize({ apiKey: geminiApiKey });
|
|
40762
|
-
const videoBuffer = await
|
|
40857
|
+
const videoBuffer = await readFile22(videoPath);
|
|
40763
40858
|
const modelId = options.model ? resolveGeminiTextModel(options.model) : void 0;
|
|
40764
40859
|
const prompt3 = `Analyze this video and identify all silent or dead segments where there is NO meaningful content.
|
|
40765
40860
|
|
|
@@ -40835,7 +40930,7 @@ async function executeSilenceCut(options) {
|
|
|
40835
40930
|
analyzeOnly = false,
|
|
40836
40931
|
useGemini = false
|
|
40837
40932
|
} = options;
|
|
40838
|
-
if (!
|
|
40933
|
+
if (!existsSync21(videoPath)) {
|
|
40839
40934
|
return { success: false, error: `Video not found: ${videoPath}` };
|
|
40840
40935
|
}
|
|
40841
40936
|
if (!commandExists("ffmpeg")) {
|
|
@@ -40943,11 +41038,11 @@ var init_silence_cut = __esm({
|
|
|
40943
41038
|
});
|
|
40944
41039
|
|
|
40945
41040
|
// src/commands/_shared/edit/jump-cut.ts
|
|
40946
|
-
import { existsSync as
|
|
40947
|
-
import { readFile as
|
|
40948
|
-
import { join as
|
|
41041
|
+
import { existsSync as existsSync22 } from "node:fs";
|
|
41042
|
+
import { readFile as readFile23, writeFile as writeFile31, mkdir as mkdir15 } from "node:fs/promises";
|
|
41043
|
+
import { join as join15 } from "node:path";
|
|
40949
41044
|
async function transcribeWithWords(audioPath, apiKey, language) {
|
|
40950
|
-
const audioBuffer = await
|
|
41045
|
+
const audioBuffer = await readFile23(audioPath);
|
|
40951
41046
|
const audioBlob = new Blob([audioBuffer]);
|
|
40952
41047
|
const formData = new FormData();
|
|
40953
41048
|
formData.append("file", audioBlob, "audio.wav");
|
|
@@ -41006,7 +41101,7 @@ async function executeJumpCut(options) {
|
|
|
41006
41101
|
analyzeOnly = false,
|
|
41007
41102
|
apiKey
|
|
41008
41103
|
} = options;
|
|
41009
|
-
if (!
|
|
41104
|
+
if (!existsSync22(videoPath)) {
|
|
41010
41105
|
return { success: false, error: `Video not found: ${videoPath}` };
|
|
41011
41106
|
}
|
|
41012
41107
|
if (!commandExists("ffmpeg")) {
|
|
@@ -41025,7 +41120,7 @@ async function executeJumpCut(options) {
|
|
|
41025
41120
|
try {
|
|
41026
41121
|
const tmpDir = `/tmp/vibe_jumpcut_${Date.now()}`;
|
|
41027
41122
|
await mkdir15(tmpDir, { recursive: true });
|
|
41028
|
-
const audioPath =
|
|
41123
|
+
const audioPath = join15(tmpDir, "audio.wav");
|
|
41029
41124
|
try {
|
|
41030
41125
|
await execSafe(
|
|
41031
41126
|
"ffmpeg",
|
|
@@ -41079,7 +41174,7 @@ async function executeJumpCut(options) {
|
|
|
41079
41174
|
const segmentPaths = [];
|
|
41080
41175
|
for (let i = 0; i < segments.length; i++) {
|
|
41081
41176
|
const seg = segments[i];
|
|
41082
|
-
const segPath =
|
|
41177
|
+
const segPath = join15(tmpDir, `seg-${i.toString().padStart(4, "0")}.ts`);
|
|
41083
41178
|
const duration = seg.end - seg.start;
|
|
41084
41179
|
await execSafe(
|
|
41085
41180
|
"ffmpeg",
|
|
@@ -41102,7 +41197,7 @@ async function executeJumpCut(options) {
|
|
|
41102
41197
|
segmentPaths.push(segPath);
|
|
41103
41198
|
}
|
|
41104
41199
|
const concatList = segmentPaths.map((p) => `file '${p}'`).join("\n");
|
|
41105
|
-
const listPath =
|
|
41200
|
+
const listPath = join15(tmpDir, "concat.txt");
|
|
41106
41201
|
await writeFile31(listPath, concatList);
|
|
41107
41202
|
await execSafe(
|
|
41108
41203
|
"ffmpeg",
|
|
@@ -41292,9 +41387,9 @@ var init_helpers = __esm({
|
|
|
41292
41387
|
});
|
|
41293
41388
|
|
|
41294
41389
|
// src/commands/_shared/edit/caption.ts
|
|
41295
|
-
import { existsSync as
|
|
41296
|
-
import { readFile as
|
|
41297
|
-
import { dirname as dirname11, basename as basename6, extname as extname4, join as
|
|
41390
|
+
import { existsSync as existsSync23 } from "node:fs";
|
|
41391
|
+
import { readFile as readFile24, writeFile as writeFile32, mkdir as mkdir16 } from "node:fs/promises";
|
|
41392
|
+
import { dirname as dirname11, basename as basename6, extname as extname4, join as join16 } from "node:path";
|
|
41298
41393
|
function getCaptionForceStyle(style, fontSize, fontColor, position) {
|
|
41299
41394
|
const alignment = position === "top" ? 8 : position === "center" ? 5 : 2;
|
|
41300
41395
|
const marginV = position === "center" ? 0 : 30;
|
|
@@ -41322,7 +41417,7 @@ async function executeCaption(options) {
|
|
|
41322
41417
|
position = "bottom",
|
|
41323
41418
|
apiKey
|
|
41324
41419
|
} = options;
|
|
41325
|
-
if (!
|
|
41420
|
+
if (!existsSync23(videoPath)) {
|
|
41326
41421
|
return { success: false, error: `Video not found: ${videoPath}` };
|
|
41327
41422
|
}
|
|
41328
41423
|
if (!commandExists("ffmpeg")) {
|
|
@@ -41341,8 +41436,8 @@ async function executeCaption(options) {
|
|
|
41341
41436
|
try {
|
|
41342
41437
|
const tmpDir = `/tmp/vibe_caption_${Date.now()}`;
|
|
41343
41438
|
await mkdir16(tmpDir, { recursive: true });
|
|
41344
|
-
const audioPath =
|
|
41345
|
-
const srtPath =
|
|
41439
|
+
const audioPath = join16(tmpDir, "audio.wav");
|
|
41440
|
+
const srtPath = join16(tmpDir, "captions.srt");
|
|
41346
41441
|
try {
|
|
41347
41442
|
await execSafe(
|
|
41348
41443
|
"ffmpeg",
|
|
@@ -41363,7 +41458,7 @@ async function executeCaption(options) {
|
|
|
41363
41458
|
);
|
|
41364
41459
|
const whisper = new WhisperProvider();
|
|
41365
41460
|
await whisper.initialize({ apiKey: openaiKey });
|
|
41366
|
-
const audioBuffer = await
|
|
41461
|
+
const audioBuffer = await readFile24(audioPath);
|
|
41367
41462
|
const audioBlob = new Blob([audioBuffer]);
|
|
41368
41463
|
const transcriptResult = await whisper.transcribe(audioBlob, language);
|
|
41369
41464
|
if (transcriptResult.status === "failed" || !transcriptResult.segments || transcriptResult.segments.length === 0) {
|
|
@@ -41411,7 +41506,7 @@ async function executeCaption(options) {
|
|
|
41411
41506
|
const remotionErr = await ensureRemotionInstalled2();
|
|
41412
41507
|
if (remotionErr) {
|
|
41413
41508
|
const outputDir2 = dirname11(outputPath);
|
|
41414
|
-
const outputSrtPath2 =
|
|
41509
|
+
const outputSrtPath2 = join16(
|
|
41415
41510
|
outputDir2,
|
|
41416
41511
|
basename6(outputPath, extname4(outputPath)) + ".srt"
|
|
41417
41512
|
);
|
|
@@ -41450,7 +41545,7 @@ SRT saved to: ${outputSrtPath2}` };
|
|
|
41450
41545
|
});
|
|
41451
41546
|
if (!renderResult.success) {
|
|
41452
41547
|
const outputDir2 = dirname11(outputPath);
|
|
41453
|
-
const outputSrtPath2 =
|
|
41548
|
+
const outputSrtPath2 = join16(
|
|
41454
41549
|
outputDir2,
|
|
41455
41550
|
basename6(outputPath, extname4(outputPath)) + ".srt"
|
|
41456
41551
|
);
|
|
@@ -41463,7 +41558,7 @@ SRT saved to: ${outputSrtPath2}`
|
|
|
41463
41558
|
}
|
|
41464
41559
|
}
|
|
41465
41560
|
const outputDir = dirname11(outputPath);
|
|
41466
|
-
const outputSrtPath =
|
|
41561
|
+
const outputSrtPath = join16(
|
|
41467
41562
|
outputDir,
|
|
41468
41563
|
basename6(outputPath, extname4(outputPath)) + ".srt"
|
|
41469
41564
|
);
|
|
@@ -41500,10 +41595,10 @@ var init_caption = __esm({
|
|
|
41500
41595
|
});
|
|
41501
41596
|
|
|
41502
41597
|
// src/commands/_shared/edit/noise-reduce.ts
|
|
41503
|
-
import { existsSync as
|
|
41598
|
+
import { existsSync as existsSync24 } from "node:fs";
|
|
41504
41599
|
async function executeNoiseReduce(options) {
|
|
41505
41600
|
const { inputPath, outputPath, strength = "medium", noiseFloor } = options;
|
|
41506
|
-
if (!
|
|
41601
|
+
if (!existsSync24(inputPath)) {
|
|
41507
41602
|
return { success: false, error: `File not found: ${inputPath}` };
|
|
41508
41603
|
}
|
|
41509
41604
|
if (!commandExists("ffmpeg")) {
|
|
@@ -41560,7 +41655,7 @@ var init_noise_reduce = __esm({
|
|
|
41560
41655
|
});
|
|
41561
41656
|
|
|
41562
41657
|
// src/commands/_shared/edit/fade.ts
|
|
41563
|
-
import { existsSync as
|
|
41658
|
+
import { existsSync as existsSync25 } from "node:fs";
|
|
41564
41659
|
async function executeFade(options) {
|
|
41565
41660
|
const {
|
|
41566
41661
|
videoPath,
|
|
@@ -41570,7 +41665,7 @@ async function executeFade(options) {
|
|
|
41570
41665
|
audioOnly = false,
|
|
41571
41666
|
videoOnly = false
|
|
41572
41667
|
} = options;
|
|
41573
|
-
if (!
|
|
41668
|
+
if (!existsSync25(videoPath)) {
|
|
41574
41669
|
return { success: false, error: `Video not found: ${videoPath}` };
|
|
41575
41670
|
}
|
|
41576
41671
|
if (!commandExists("ffmpeg")) {
|
|
@@ -41637,8 +41732,8 @@ var init_fade = __esm({
|
|
|
41637
41732
|
});
|
|
41638
41733
|
|
|
41639
41734
|
// src/commands/_shared/edit/translate-srt.ts
|
|
41640
|
-
import { existsSync as
|
|
41641
|
-
import { readFile as
|
|
41735
|
+
import { existsSync as existsSync26 } from "node:fs";
|
|
41736
|
+
import { readFile as readFile25, writeFile as writeFile33 } from "node:fs/promises";
|
|
41642
41737
|
async function executeTranslateSrt(options) {
|
|
41643
41738
|
const {
|
|
41644
41739
|
srtPath,
|
|
@@ -41648,11 +41743,11 @@ async function executeTranslateSrt(options) {
|
|
|
41648
41743
|
sourceLanguage,
|
|
41649
41744
|
apiKey
|
|
41650
41745
|
} = options;
|
|
41651
|
-
if (!
|
|
41746
|
+
if (!existsSync26(srtPath)) {
|
|
41652
41747
|
return { success: false, error: `SRT file not found: ${srtPath}` };
|
|
41653
41748
|
}
|
|
41654
41749
|
try {
|
|
41655
|
-
const srtContent = await
|
|
41750
|
+
const srtContent = await readFile25(srtPath, "utf-8");
|
|
41656
41751
|
const segments = parseSRT(srtContent);
|
|
41657
41752
|
if (segments.length === 0) {
|
|
41658
41753
|
return { success: false, error: "No subtitle segments found in SRT file" };
|
|
@@ -41765,7 +41860,7 @@ var init_translate_srt = __esm({
|
|
|
41765
41860
|
});
|
|
41766
41861
|
|
|
41767
41862
|
// src/commands/_shared/edit/text-overlay.ts
|
|
41768
|
-
import { existsSync as
|
|
41863
|
+
import { existsSync as existsSync27 } from "node:fs";
|
|
41769
41864
|
import { resolve as resolve34 } from "node:path";
|
|
41770
41865
|
function detectSystemFont() {
|
|
41771
41866
|
const platform3 = process.platform;
|
|
@@ -41776,7 +41871,7 @@ function detectSystemFont() {
|
|
|
41776
41871
|
"/Library/Fonts/Arial.ttf"
|
|
41777
41872
|
];
|
|
41778
41873
|
for (const f of candidates) {
|
|
41779
|
-
if (
|
|
41874
|
+
if (existsSync27(f)) return f;
|
|
41780
41875
|
}
|
|
41781
41876
|
} else if (platform3 === "linux") {
|
|
41782
41877
|
const candidates = [
|
|
@@ -41785,7 +41880,7 @@ function detectSystemFont() {
|
|
|
41785
41880
|
"/usr/share/fonts/TTF/DejaVuSans-Bold.ttf"
|
|
41786
41881
|
];
|
|
41787
41882
|
for (const f of candidates) {
|
|
41788
|
-
if (
|
|
41883
|
+
if (existsSync27(f)) return f;
|
|
41789
41884
|
}
|
|
41790
41885
|
} else if (platform3 === "win32") {
|
|
41791
41886
|
const candidates = [
|
|
@@ -41793,7 +41888,7 @@ function detectSystemFont() {
|
|
|
41793
41888
|
"C:\\Windows\\Fonts\\segoeui.ttf"
|
|
41794
41889
|
];
|
|
41795
41890
|
for (const f of candidates) {
|
|
41796
|
-
if (
|
|
41891
|
+
if (existsSync27(f)) return f;
|
|
41797
41892
|
}
|
|
41798
41893
|
}
|
|
41799
41894
|
return null;
|
|
@@ -41814,7 +41909,7 @@ async function applyTextOverlays(options) {
|
|
|
41814
41909
|
}
|
|
41815
41910
|
const absVideoPath = resolve34(process.cwd(), videoPath);
|
|
41816
41911
|
const absOutputPath = resolve34(process.cwd(), outputPath);
|
|
41817
|
-
if (!
|
|
41912
|
+
if (!existsSync27(absVideoPath)) {
|
|
41818
41913
|
return { success: false, error: `Video not found: ${absVideoPath}` };
|
|
41819
41914
|
}
|
|
41820
41915
|
if (!commandExists("ffmpeg")) {
|
|
@@ -41995,7 +42090,7 @@ var init_ai_edit = __esm({
|
|
|
41995
42090
|
|
|
41996
42091
|
// src/commands/ai-edit-cli.ts
|
|
41997
42092
|
import { resolve as resolve35, extname as extname5, basename as basename7 } from "node:path";
|
|
41998
|
-
import { existsSync as
|
|
42093
|
+
import { existsSync as existsSync28 } from "node:fs";
|
|
41999
42094
|
function registerEditCommands(aiCommand) {
|
|
42000
42095
|
aiCommand.command("silence-cut").alias("sc").description("Remove silent segments from video (FFmpeg default, or Gemini for smart detection)").argument("<video>", "Video file path").option("-o, --output <path>", "Output file path (default: <name>-cut.<ext>)").option("--noise <dB>", "Silence threshold in dB (default: -30)", "-30").option("-d, --min-duration <seconds>", "Minimum silence duration to cut (default: 0.5)", "0.5").option("--padding <seconds>", "Padding around non-silent segments (default: 0.1)", "0.1").option("--analyze-only", "(deprecated \u2014 use `vibe detect silence`) Only detect silence, don't cut").option("--use-gemini", "Use Gemini Video Understanding for context-aware silence detection").option("-m, --model <model>", "Gemini model (default: flash)").option("--low-res", "Low resolution mode for longer videos (Gemini only)").option("-k, --api-key <key>", "Google API key override (or set GOOGLE_API_KEY env)").option("--dry-run", "Preview parameters without executing").addHelpText("after", `
|
|
42001
42096
|
Examples:
|
|
@@ -42012,7 +42107,7 @@ No API key needed (FFmpeg only). Use --use-gemini for smart detection (requires
|
|
|
42012
42107
|
validateOutputPath(options.output);
|
|
42013
42108
|
}
|
|
42014
42109
|
const absVideoPath = resolve35(process.cwd(), videoPath);
|
|
42015
|
-
if (!
|
|
42110
|
+
if (!existsSync28(absVideoPath)) {
|
|
42016
42111
|
exitWithError(notFoundError(absVideoPath));
|
|
42017
42112
|
}
|
|
42018
42113
|
if (!commandExists("ffmpeg")) {
|
|
@@ -42120,7 +42215,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
42120
42215
|
validateOutputPath(options.output);
|
|
42121
42216
|
}
|
|
42122
42217
|
const absVideoPath = resolve35(process.cwd(), videoPath);
|
|
42123
|
-
if (!
|
|
42218
|
+
if (!existsSync28(absVideoPath)) {
|
|
42124
42219
|
exitWithError(notFoundError(absVideoPath));
|
|
42125
42220
|
}
|
|
42126
42221
|
if (!commandExists("ffmpeg")) {
|
|
@@ -42201,7 +42296,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
42201
42296
|
validateOutputPath(options.output);
|
|
42202
42297
|
}
|
|
42203
42298
|
const absInputPath = resolve35(process.cwd(), inputPath);
|
|
42204
|
-
if (!
|
|
42299
|
+
if (!existsSync28(absInputPath)) {
|
|
42205
42300
|
exitWithError(notFoundError(absInputPath));
|
|
42206
42301
|
}
|
|
42207
42302
|
if (!commandExists("ffmpeg")) {
|
|
@@ -42267,7 +42362,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
42267
42362
|
validateOutputPath(options.output);
|
|
42268
42363
|
}
|
|
42269
42364
|
const absVideoPath = resolve35(process.cwd(), videoPath);
|
|
42270
|
-
if (!
|
|
42365
|
+
if (!existsSync28(absVideoPath)) {
|
|
42271
42366
|
exitWithError(notFoundError(absVideoPath));
|
|
42272
42367
|
}
|
|
42273
42368
|
if (!commandExists("ffmpeg")) {
|
|
@@ -42342,7 +42437,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
42342
42437
|
exitWithError(usageError("Target language required. Use -t or --target"));
|
|
42343
42438
|
}
|
|
42344
42439
|
const absSrtPath = resolve35(process.cwd(), srtPath);
|
|
42345
|
-
if (!
|
|
42440
|
+
if (!existsSync28(absSrtPath)) {
|
|
42346
42441
|
exitWithError(notFoundError(absSrtPath));
|
|
42347
42442
|
}
|
|
42348
42443
|
if (options.dryRun) {
|
|
@@ -42418,7 +42513,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
42418
42513
|
}
|
|
42419
42514
|
if (options.fillers) rejectControlChars(options.fillers);
|
|
42420
42515
|
const absVideoPath = resolve35(process.cwd(), videoPath);
|
|
42421
|
-
if (!
|
|
42516
|
+
if (!existsSync28(absVideoPath)) {
|
|
42422
42517
|
exitWithError(notFoundError(absVideoPath));
|
|
42423
42518
|
}
|
|
42424
42519
|
if (!commandExists("ffmpeg")) {
|
|
@@ -42518,8 +42613,8 @@ var init_ai_edit_cli = __esm({
|
|
|
42518
42613
|
});
|
|
42519
42614
|
|
|
42520
42615
|
// src/commands/_shared/execute-fill-gaps.ts
|
|
42521
|
-
import { existsSync as
|
|
42522
|
-
import { readFile as
|
|
42616
|
+
import { existsSync as existsSync29 } from "node:fs";
|
|
42617
|
+
import { readFile as readFile26, writeFile as writeFile34, mkdir as mkdir17, rename as renameFs } from "node:fs/promises";
|
|
42523
42618
|
import { resolve as resolve36, dirname as dirname12 } from "node:path";
|
|
42524
42619
|
function detectVideoGaps(videoClips, totalDuration) {
|
|
42525
42620
|
const gaps = [];
|
|
@@ -42576,7 +42671,7 @@ function analyzeGapFillability(gaps, videoClips, sources) {
|
|
|
42576
42671
|
}
|
|
42577
42672
|
async function uploadFrameToImgbb(framePath, imgbbApiKey) {
|
|
42578
42673
|
try {
|
|
42579
|
-
const frameBuffer = await
|
|
42674
|
+
const frameBuffer = await readFile26(framePath);
|
|
42580
42675
|
const frameBase64 = frameBuffer.toString("base64");
|
|
42581
42676
|
const formData = new FormData();
|
|
42582
42677
|
formData.append("key", imgbbApiKey);
|
|
@@ -42601,14 +42696,14 @@ async function executeFillGaps(options) {
|
|
|
42601
42696
|
try {
|
|
42602
42697
|
onProgress("Loading timeline...");
|
|
42603
42698
|
const filePath = await resolveTimelineFile(options.projectPath);
|
|
42604
|
-
if (!
|
|
42699
|
+
if (!existsSync29(filePath)) {
|
|
42605
42700
|
return {
|
|
42606
42701
|
success: false,
|
|
42607
42702
|
error: `Timeline file not found: ${filePath}`,
|
|
42608
42703
|
humanLines
|
|
42609
42704
|
};
|
|
42610
42705
|
}
|
|
42611
|
-
const content = await
|
|
42706
|
+
const content = await readFile26(filePath, "utf-8");
|
|
42612
42707
|
const data = JSON.parse(content);
|
|
42613
42708
|
const project = Project.fromJSON(data);
|
|
42614
42709
|
const clips = project.getClips().sort((a, b) => a.startTime - b.startTime);
|
|
@@ -42736,7 +42831,7 @@ async function executeFillGaps(options) {
|
|
|
42736
42831
|
}
|
|
42737
42832
|
const projectDir = dirname12(filePath);
|
|
42738
42833
|
const footageDir = options.dir ? resolve36(process.cwd(), options.dir) : resolve36(projectDir, "footage");
|
|
42739
|
-
if (!
|
|
42834
|
+
if (!existsSync29(footageDir)) {
|
|
42740
42835
|
await mkdir17(footageDir, { recursive: true });
|
|
42741
42836
|
}
|
|
42742
42837
|
humanLines.push("Generating AI Videos");
|
|
@@ -43050,15 +43145,15 @@ var init_ai_fill_gaps = __esm({
|
|
|
43050
43145
|
});
|
|
43051
43146
|
|
|
43052
43147
|
// src/pipeline/renderers/chrome.ts
|
|
43053
|
-
import { existsSync as
|
|
43148
|
+
import { existsSync as existsSync30 } from "node:fs";
|
|
43054
43149
|
import { homedir as homedir5 } from "node:os";
|
|
43055
|
-
import { join as
|
|
43150
|
+
import { join as join17 } from "node:path";
|
|
43056
43151
|
function findChrome() {
|
|
43057
43152
|
const candidates = [
|
|
43058
43153
|
process.env.HYPERFRAMES_CHROME_PATH,
|
|
43059
43154
|
process.env.CHROME_PATH,
|
|
43060
43155
|
// puppeteer auto-downloaded headless shell
|
|
43061
|
-
|
|
43156
|
+
join17(
|
|
43062
43157
|
homedir5(),
|
|
43063
43158
|
".cache",
|
|
43064
43159
|
"puppeteer",
|
|
@@ -43076,7 +43171,7 @@ function findChrome() {
|
|
|
43076
43171
|
"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"
|
|
43077
43172
|
];
|
|
43078
43173
|
for (const c of candidates) {
|
|
43079
|
-
if (c &&
|
|
43174
|
+
if (c && existsSync30(c)) return c;
|
|
43080
43175
|
}
|
|
43081
43176
|
return void 0;
|
|
43082
43177
|
}
|
|
@@ -43322,7 +43417,7 @@ __export(project_builder_exports, {
|
|
|
43322
43417
|
resolveSourceUrl: () => resolveSourceUrl
|
|
43323
43418
|
});
|
|
43324
43419
|
import { mkdtemp, mkdir as mkdir18, copyFile as copyFile4, writeFile as writeFile35, rm as rm2, readdir as readdir3 } from "node:fs/promises";
|
|
43325
|
-
import { existsSync as
|
|
43420
|
+
import { existsSync as existsSync31 } from "node:fs";
|
|
43326
43421
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
43327
43422
|
import { createRequire } from "node:module";
|
|
43328
43423
|
import * as path from "node:path";
|
|
@@ -43334,7 +43429,7 @@ async function buildTempProject(state, projectFileDir) {
|
|
|
43334
43429
|
for (const source of state.sources) {
|
|
43335
43430
|
if (copied.has(source.id)) continue;
|
|
43336
43431
|
const resolved = resolveSourceUrl(source.url, projectFileDir);
|
|
43337
|
-
if (!
|
|
43432
|
+
if (!existsSync31(resolved)) {
|
|
43338
43433
|
throw new Error(`Source file not found: ${resolved} (from source id=${source.id})`);
|
|
43339
43434
|
}
|
|
43340
43435
|
const dest = path.join(assetsDir, path.basename(resolved));
|
|
@@ -43471,7 +43566,7 @@ __export(motion_overlay_exports, {
|
|
|
43471
43566
|
registerMotionOverlayCommand: () => registerMotionOverlayCommand
|
|
43472
43567
|
});
|
|
43473
43568
|
import { mkdir as mkdir19 } from "node:fs/promises";
|
|
43474
|
-
import { existsSync as
|
|
43569
|
+
import { existsSync as existsSync32 } from "node:fs";
|
|
43475
43570
|
import { basename as basename9, dirname as dirname14, extname as extname6, resolve as resolve38 } from "node:path";
|
|
43476
43571
|
function normalizeUnderstand2(value) {
|
|
43477
43572
|
return value === "off" || value === "required" || value === "auto" ? value : "auto";
|
|
@@ -43602,11 +43697,11 @@ async function executeMotionOverlay(options) {
|
|
|
43602
43697
|
const validationError = assertValidRequest(options);
|
|
43603
43698
|
if (validationError) return { success: false, error: validationError };
|
|
43604
43699
|
const absVideo = resolve38(process.cwd(), options.videoPath);
|
|
43605
|
-
if (!
|
|
43700
|
+
if (!existsSync32(absVideo)) return { success: false, error: `Video file not found: ${absVideo}` };
|
|
43606
43701
|
const outputPath = options.output ? resolve38(process.cwd(), options.output) : defaultOverlayOutput(options.videoPath);
|
|
43607
43702
|
if (options.asset) {
|
|
43608
43703
|
const absAsset = resolve38(process.cwd(), options.asset);
|
|
43609
|
-
if (!
|
|
43704
|
+
if (!existsSync32(absAsset)) return { success: false, error: `Lottie asset not found: ${absAsset}` };
|
|
43610
43705
|
const [videoDuration, size] = await Promise.all([
|
|
43611
43706
|
ffprobeDuration(absVideo),
|
|
43612
43707
|
ffprobeVideoSize(absVideo)
|
|
@@ -43773,7 +43868,7 @@ __export(edit_cmd_exports, {
|
|
|
43773
43868
|
executeUpscale: () => executeUpscale
|
|
43774
43869
|
});
|
|
43775
43870
|
import { resolve as resolve39, dirname as dirname15 } from "node:path";
|
|
43776
|
-
import { readFile as
|
|
43871
|
+
import { readFile as readFile27, writeFile as writeFile36, mkdir as mkdir20 } from "node:fs/promises";
|
|
43777
43872
|
async function executeGrade(options) {
|
|
43778
43873
|
const { videoPath, style, preset, output, analyzeOnly, apiKey } = options;
|
|
43779
43874
|
try {
|
|
@@ -43814,7 +43909,7 @@ async function executeSpeedRamp(options) {
|
|
|
43814
43909
|
await execSafe("ffmpeg", ["-i", absPath, "-vn", "-acodec", "libmp3lame", "-q:a", "2", tempAudio, "-y"]);
|
|
43815
43910
|
const whisper = new WhisperProvider();
|
|
43816
43911
|
await whisper.initialize({ apiKey: openaiKey });
|
|
43817
|
-
const audioBuffer = await
|
|
43912
|
+
const audioBuffer = await readFile27(tempAudio);
|
|
43818
43913
|
const transcript = await whisper.transcribe(new Blob([audioBuffer]), language);
|
|
43819
43914
|
try {
|
|
43820
43915
|
const { unlink: unlink5 } = await import("node:fs/promises");
|
|
@@ -44208,7 +44303,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
|
|
|
44208
44303
|
spinner2.text = "Transcribing audio...";
|
|
44209
44304
|
const whisper = new WhisperProvider();
|
|
44210
44305
|
await whisper.initialize({ apiKey: openaiApiKey });
|
|
44211
|
-
const audioBuffer = await
|
|
44306
|
+
const audioBuffer = await readFile27(tempAudio);
|
|
44212
44307
|
const audioBlob = new Blob([audioBuffer]);
|
|
44213
44308
|
const transcript = await whisper.transcribe(audioBlob, options.language);
|
|
44214
44309
|
if (!transcript.segments || transcript.segments.length === 0) {
|
|
@@ -44341,7 +44436,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
|
|
|
44341
44436
|
for (let i = 1; i <= numKeyframes && i <= 30; i++) {
|
|
44342
44437
|
const framePath = `${tempDir}/frame-${i.toString().padStart(4, "0")}.jpg`;
|
|
44343
44438
|
try {
|
|
44344
|
-
const frameBuffer = await
|
|
44439
|
+
const frameBuffer = await readFile27(framePath);
|
|
44345
44440
|
const frameBase64 = frameBuffer.toString("base64");
|
|
44346
44441
|
const result = await claude.analyzeFrameForReframe(frameBase64, options.aspect, {
|
|
44347
44442
|
focusMode: options.focus,
|
|
@@ -44470,7 +44565,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
|
|
|
44470
44565
|
const imageBuffers = [];
|
|
44471
44566
|
for (const imagePath of imagePaths) {
|
|
44472
44567
|
const absPath = resolve39(process.cwd(), imagePath);
|
|
44473
|
-
const buffer = await
|
|
44568
|
+
const buffer = await readFile27(absPath);
|
|
44474
44569
|
imageBuffers.push(buffer);
|
|
44475
44570
|
}
|
|
44476
44571
|
let result;
|
|
@@ -44746,9 +44841,9 @@ __export(ai_analyze_exports, {
|
|
|
44746
44841
|
executeGeminiVideo: () => executeGeminiVideo,
|
|
44747
44842
|
registerAnalyzeCommands: () => registerAnalyzeCommands
|
|
44748
44843
|
});
|
|
44749
|
-
import { readFile as
|
|
44844
|
+
import { readFile as readFile28 } from "node:fs/promises";
|
|
44750
44845
|
import { extname as extname7, resolve as resolve40 } from "node:path";
|
|
44751
|
-
import { existsSync as
|
|
44846
|
+
import { existsSync as existsSync33 } from "node:fs";
|
|
44752
44847
|
async function executeGeminiVideo(options) {
|
|
44753
44848
|
try {
|
|
44754
44849
|
const apiKey = await getApiKey("GOOGLE_API_KEY", "Google");
|
|
@@ -44762,10 +44857,10 @@ async function executeGeminiVideo(options) {
|
|
|
44762
44857
|
videoData = options.source;
|
|
44763
44858
|
} else {
|
|
44764
44859
|
const absPath = resolve40(process.cwd(), options.source);
|
|
44765
|
-
if (!
|
|
44860
|
+
if (!existsSync33(absPath)) {
|
|
44766
44861
|
return { success: false, error: `File not found: ${absPath}` };
|
|
44767
44862
|
}
|
|
44768
|
-
videoData = await
|
|
44863
|
+
videoData = await readFile28(absPath);
|
|
44769
44864
|
}
|
|
44770
44865
|
const gemini = new GeminiProvider();
|
|
44771
44866
|
await gemini.initialize({ apiKey });
|
|
@@ -44830,10 +44925,10 @@ async function executeAnalyze(options) {
|
|
|
44830
44925
|
imageBuffer = Buffer.from(await response.arrayBuffer());
|
|
44831
44926
|
} else {
|
|
44832
44927
|
const absPath = resolve40(process.cwd(), source);
|
|
44833
|
-
if (!
|
|
44928
|
+
if (!existsSync33(absPath)) {
|
|
44834
44929
|
return { success: false, error: `File not found: ${absPath}` };
|
|
44835
44930
|
}
|
|
44836
|
-
imageBuffer = await
|
|
44931
|
+
imageBuffer = await readFile28(absPath);
|
|
44837
44932
|
}
|
|
44838
44933
|
const result2 = await gemini.analyzeImage(imageBuffer, options.prompt, {
|
|
44839
44934
|
model: modelId,
|
|
@@ -44865,10 +44960,10 @@ async function executeAnalyze(options) {
|
|
|
44865
44960
|
videoData = Buffer.from(await response.arrayBuffer());
|
|
44866
44961
|
} else {
|
|
44867
44962
|
const absPath = resolve40(process.cwd(), source);
|
|
44868
|
-
if (!
|
|
44963
|
+
if (!existsSync33(absPath)) {
|
|
44869
44964
|
return { success: false, error: `File not found: ${absPath}` };
|
|
44870
44965
|
}
|
|
44871
|
-
videoData = await
|
|
44966
|
+
videoData = await readFile28(absPath);
|
|
44872
44967
|
}
|
|
44873
44968
|
const result = await gemini.analyzeVideo(videoData, options.prompt, {
|
|
44874
44969
|
model: modelId,
|
|
@@ -45002,9 +45097,9 @@ __export(ai_review_exports, {
|
|
|
45002
45097
|
executeReview: () => executeReview,
|
|
45003
45098
|
registerReviewCommand: () => registerReviewCommand
|
|
45004
45099
|
});
|
|
45005
|
-
import { readFile as
|
|
45100
|
+
import { readFile as readFile29, rename as rename2 } from "node:fs/promises";
|
|
45006
45101
|
import { resolve as resolve41 } from "node:path";
|
|
45007
|
-
import { existsSync as
|
|
45102
|
+
import { existsSync as existsSync34 } from "node:fs";
|
|
45008
45103
|
function parseReviewFeedback(response) {
|
|
45009
45104
|
let cleaned = response.trim();
|
|
45010
45105
|
if (cleaned.startsWith("```json")) {
|
|
@@ -45036,7 +45131,7 @@ async function executeReview(options) {
|
|
|
45036
45131
|
model = "flash"
|
|
45037
45132
|
} = options;
|
|
45038
45133
|
const absVideoPath = resolve41(process.cwd(), videoPath);
|
|
45039
|
-
if (!
|
|
45134
|
+
if (!existsSync34(absVideoPath)) {
|
|
45040
45135
|
return { success: false, error: `Video not found: ${absVideoPath}` };
|
|
45041
45136
|
}
|
|
45042
45137
|
const apiKey = process.env.GOOGLE_API_KEY || await getApiKey("GOOGLE_API_KEY", "Google");
|
|
@@ -45049,8 +45144,8 @@ async function executeReview(options) {
|
|
|
45049
45144
|
let storyboardContext = "";
|
|
45050
45145
|
if (storyboardPath2) {
|
|
45051
45146
|
const absStoryboardPath = resolve41(process.cwd(), storyboardPath2);
|
|
45052
|
-
if (
|
|
45053
|
-
const content = await
|
|
45147
|
+
if (existsSync34(absStoryboardPath)) {
|
|
45148
|
+
const content = await readFile29(absStoryboardPath, "utf-8");
|
|
45054
45149
|
storyboardContext = `
|
|
45055
45150
|
|
|
45056
45151
|
Original storyboard for reference:
|
|
@@ -45092,7 +45187,7 @@ ${projectContext}` : "";
|
|
|
45092
45187
|
Score each category 1-10. Prefer beatIssues when you can map a problem to a storyboard beat or timestamp. Use the exact beatId values from the storyboard or beat timing summary, and include timeRange in seconds for every localized finding. In particular, flag narration/visual mismatches, overly static holds, and audio-visual sync problems as beatIssues. Use category issue arrays only for problems that cannot be localized to a beat. For fixable issues, provide an FFmpeg filter in autoFixable. Be specific and practical.${storyboardContext}${extraProjectContext}`;
|
|
45093
45188
|
const gemini = new GeminiProvider();
|
|
45094
45189
|
await gemini.initialize({ apiKey });
|
|
45095
|
-
const videoData = await
|
|
45190
|
+
const videoData = await readFile29(absVideoPath);
|
|
45096
45191
|
const analysisResult = await gemini.analyzeVideo(videoData, reviewPrompt, {
|
|
45097
45192
|
model: modelId
|
|
45098
45193
|
});
|
|
@@ -45147,7 +45242,7 @@ Score each category 1-10. Prefer beatIssues when you can map a problem to a stor
|
|
|
45147
45242
|
}
|
|
45148
45243
|
}
|
|
45149
45244
|
if (verify && result.outputPath) {
|
|
45150
|
-
const verifyVideoData = await
|
|
45245
|
+
const verifyVideoData = await readFile29(result.outputPath);
|
|
45151
45246
|
const verifyResult = await gemini.analyzeVideo(
|
|
45152
45247
|
verifyVideoData,
|
|
45153
45248
|
'Rate this video overall quality on a scale of 1-10. Return ONLY a JSON object: {"score": <number>}',
|
|
@@ -45275,9 +45370,9 @@ var init_ai_review = __esm({
|
|
|
45275
45370
|
});
|
|
45276
45371
|
|
|
45277
45372
|
// src/commands/_shared/scene-inspect.ts
|
|
45278
|
-
import { existsSync as
|
|
45279
|
-
import { readdir as readdir4, readFile as
|
|
45280
|
-
import { basename as basename10, isAbsolute as isAbsolute3, join as
|
|
45373
|
+
import { existsSync as existsSync35 } from "node:fs";
|
|
45374
|
+
import { readdir as readdir4, readFile as readFile30, stat as stat5 } from "node:fs/promises";
|
|
45375
|
+
import { basename as basename10, isAbsolute as isAbsolute3, join as join19, relative as relative8, resolve as resolve42 } from "node:path";
|
|
45281
45376
|
async function inspectProject(opts) {
|
|
45282
45377
|
const projectDir = resolve42(opts.projectDir);
|
|
45283
45378
|
const issues = [];
|
|
@@ -45291,7 +45386,7 @@ async function inspectProject(opts) {
|
|
|
45291
45386
|
assets: { checked: 0, missing: [], stale: [], unknownFreshness: [] },
|
|
45292
45387
|
rootSync: { ok: null, issueCount: 0, issues: [] }
|
|
45293
45388
|
};
|
|
45294
|
-
if (!
|
|
45389
|
+
if (!existsSync35(projectDir) || !await isDirectory(projectDir)) {
|
|
45295
45390
|
issues.push({
|
|
45296
45391
|
severity: "error",
|
|
45297
45392
|
code: "PROJECT_NOT_FOUND",
|
|
@@ -45306,15 +45401,15 @@ async function inspectProject(opts) {
|
|
|
45306
45401
|
);
|
|
45307
45402
|
}
|
|
45308
45403
|
const coreFiles = {
|
|
45309
|
-
storyboard:
|
|
45310
|
-
design:
|
|
45311
|
-
config:
|
|
45312
|
-
legacyConfig:
|
|
45313
|
-
buildReport:
|
|
45314
|
-
root:
|
|
45404
|
+
storyboard: join19(projectDir, "STORYBOARD.md"),
|
|
45405
|
+
design: join19(projectDir, "DESIGN.md"),
|
|
45406
|
+
config: join19(projectDir, "vibe.config.json"),
|
|
45407
|
+
legacyConfig: join19(projectDir, "vibe.project.yaml"),
|
|
45408
|
+
buildReport: join19(projectDir, "build-report.json"),
|
|
45409
|
+
root: join19(projectDir, "index.html")
|
|
45315
45410
|
};
|
|
45316
45411
|
for (const [name, path2] of Object.entries(coreFiles)) {
|
|
45317
|
-
checks.files[name] =
|
|
45412
|
+
checks.files[name] = existsSync35(path2);
|
|
45318
45413
|
}
|
|
45319
45414
|
let beatIds = [];
|
|
45320
45415
|
let inspectedBeatIds = [];
|
|
@@ -45328,7 +45423,7 @@ async function inspectProject(opts) {
|
|
|
45328
45423
|
});
|
|
45329
45424
|
retryWith.push(`vibe init ${projectDir} --from "<brief>" --json`);
|
|
45330
45425
|
} else {
|
|
45331
|
-
const storyboard = await
|
|
45426
|
+
const storyboard = await readFile30(coreFiles.storyboard, "utf-8");
|
|
45332
45427
|
const validation = validateStoryboardMarkdown(storyboard);
|
|
45333
45428
|
storyboardBeats = validation.beats;
|
|
45334
45429
|
beatIds = validation.beats.map((beat) => beat.id);
|
|
@@ -45370,7 +45465,7 @@ async function inspectProject(opts) {
|
|
|
45370
45465
|
suggestedFix: "Create DESIGN.md or rerun `vibe init --from` in a new project."
|
|
45371
45466
|
});
|
|
45372
45467
|
} else {
|
|
45373
|
-
const design = await
|
|
45468
|
+
const design = await readFile30(coreFiles.design, "utf-8");
|
|
45374
45469
|
for (const issue of designPlaceholderIssues(design)) {
|
|
45375
45470
|
issues.push(issue);
|
|
45376
45471
|
}
|
|
@@ -45395,20 +45490,20 @@ async function inspectProject(opts) {
|
|
|
45395
45490
|
});
|
|
45396
45491
|
retryWith.push(`vibe build ${projectDir} --stage sync --json`);
|
|
45397
45492
|
}
|
|
45398
|
-
const compositionsDir =
|
|
45399
|
-
const existingComps =
|
|
45493
|
+
const compositionsDir = join19(projectDir, "compositions");
|
|
45494
|
+
const existingComps = existsSync35(compositionsDir) ? new Set(await listHtmlBasenames(compositionsDir)) : /* @__PURE__ */ new Set();
|
|
45400
45495
|
checks.compositions.expected = inspectedBeatIds.length;
|
|
45401
45496
|
for (const beatId of inspectedBeatIds) {
|
|
45402
45497
|
const file = `scene-${beatId}.html`;
|
|
45403
45498
|
if (existingComps.has(file)) {
|
|
45404
45499
|
checks.compositions.found++;
|
|
45405
45500
|
} else {
|
|
45406
|
-
checks.compositions.missing.push(
|
|
45501
|
+
checks.compositions.missing.push(join19("compositions", file));
|
|
45407
45502
|
issues.push({
|
|
45408
45503
|
severity: "error",
|
|
45409
45504
|
code: "MISSING_COMPOSITION",
|
|
45410
45505
|
message: `Composition for beat "${beatId}" is missing.`,
|
|
45411
|
-
file:
|
|
45506
|
+
file: join19("compositions", file),
|
|
45412
45507
|
scene: beatId,
|
|
45413
45508
|
beatId,
|
|
45414
45509
|
suggestedFix: "Run `vibe build --stage compose --json`."
|
|
@@ -45470,7 +45565,7 @@ async function inspectProject(opts) {
|
|
|
45470
45565
|
try {
|
|
45471
45566
|
const lint = await runProjectLint({ projectDir });
|
|
45472
45567
|
const lintFiles = opts.beatId ? lint.files.filter(
|
|
45473
|
-
(file) => file.file === "index.html" || file.file ===
|
|
45568
|
+
(file) => file.file === "index.html" || file.file === join19("compositions", `scene-${opts.beatId}.html`)
|
|
45474
45569
|
) : lint.files;
|
|
45475
45570
|
const lintCounts = countLintFindings(lintFiles);
|
|
45476
45571
|
checks.lint = {
|
|
@@ -45616,7 +45711,7 @@ function projectSourceReports(checks) {
|
|
|
45616
45711
|
async function inspectBuildReport(projectDir, reportPath, beatId, checks, issues, retryWith, storyboardBeats) {
|
|
45617
45712
|
checks.buildReport.exists = true;
|
|
45618
45713
|
try {
|
|
45619
|
-
const report = JSON.parse(await
|
|
45714
|
+
const report = JSON.parse(await readFile30(reportPath, "utf-8"));
|
|
45620
45715
|
if (typeof report.outputPath === "string") checks.buildReport.outputPath = report.outputPath;
|
|
45621
45716
|
const reportBeats = report.beats ?? [];
|
|
45622
45717
|
const selectedReportBeats = reportBeats.filter((item) => !beatId || item.id === beatId);
|
|
@@ -45753,7 +45848,7 @@ function inspectReportedAsset(opts) {
|
|
|
45753
45848
|
opts.checks.assets.checked++;
|
|
45754
45849
|
if (isExternalRef(opts.value)) return;
|
|
45755
45850
|
const abs = isAbsolute3(opts.value) ? opts.value : resolve42(opts.projectDir, opts.value);
|
|
45756
|
-
if (
|
|
45851
|
+
if (existsSync35(abs)) return;
|
|
45757
45852
|
opts.checks.assets.missing.push(opts.value);
|
|
45758
45853
|
opts.issues.push({
|
|
45759
45854
|
severity: "warning",
|
|
@@ -45852,9 +45947,9 @@ var init_scene_inspect = __esm({
|
|
|
45852
45947
|
});
|
|
45853
45948
|
|
|
45854
45949
|
// src/commands/_shared/render-inspect.ts
|
|
45855
|
-
import { existsSync as
|
|
45856
|
-
import { readdir as readdir5, readFile as
|
|
45857
|
-
import { basename as basename11, isAbsolute as isAbsolute4, join as
|
|
45950
|
+
import { existsSync as existsSync36 } from "node:fs";
|
|
45951
|
+
import { readdir as readdir5, readFile as readFile31, stat as stat6 } from "node:fs/promises";
|
|
45952
|
+
import { basename as basename11, isAbsolute as isAbsolute4, join as join20, relative as relative9, resolve as resolve43 } from "node:path";
|
|
45858
45953
|
function parseBlackdetectOutput(output) {
|
|
45859
45954
|
const ranges = [];
|
|
45860
45955
|
const regex2 = /black_start:(-?\d+(?:\.\d+)?)\s+black_end:(-?\d+(?:\.\d+)?)\s+black_duration:(-?\d+(?:\.\d+)?)/g;
|
|
@@ -46340,11 +46435,11 @@ async function inspectRender(opts) {
|
|
|
46340
46435
|
}
|
|
46341
46436
|
async function renderExpectsAudio(projectDir, beatId) {
|
|
46342
46437
|
const root = resolve43(projectDir);
|
|
46343
|
-
const rootHtml = await readTextIfExists(
|
|
46438
|
+
const rootHtml = await readTextIfExists(join20(root, "index.html"));
|
|
46344
46439
|
if (rootHtml && rootHtmlExpectsAudio(rootHtml, beatId)) return true;
|
|
46345
|
-
const buildReport = await readJsonIfExists(
|
|
46440
|
+
const buildReport = await readJsonIfExists(join20(root, "build-report.json"));
|
|
46346
46441
|
if (buildReportExpectsAudio(buildReport, beatId)) return true;
|
|
46347
|
-
const storyboard = await readTextIfExists(
|
|
46442
|
+
const storyboard = await readTextIfExists(join20(root, "STORYBOARD.md"));
|
|
46348
46443
|
if (!storyboard) return false;
|
|
46349
46444
|
try {
|
|
46350
46445
|
const parsed = parseStoryboard(storyboard);
|
|
@@ -46355,7 +46450,7 @@ async function renderExpectsAudio(projectDir, beatId) {
|
|
|
46355
46450
|
`assets/narration-${beat.id}.wav`,
|
|
46356
46451
|
`assets/music-${beat.id}.mp3`,
|
|
46357
46452
|
`assets/music-${beat.id}.wav`
|
|
46358
|
-
].some((asset) =>
|
|
46453
|
+
].some((asset) => existsSync36(join20(root, asset)))
|
|
46359
46454
|
);
|
|
46360
46455
|
} catch {
|
|
46361
46456
|
return false;
|
|
@@ -46463,8 +46558,8 @@ async function maybeWriteRenderReport(projectDir, opts, result) {
|
|
|
46463
46558
|
}
|
|
46464
46559
|
function renderSourceReports(projectDir, videoPath, checks) {
|
|
46465
46560
|
const reports = [];
|
|
46466
|
-
if (
|
|
46467
|
-
if (
|
|
46561
|
+
if (existsSync36(join20(projectDir, "build-report.json"))) reports.push("build-report.json");
|
|
46562
|
+
if (existsSync36(join20(projectDir, "render-report.json"))) reports.push("render-report.json");
|
|
46468
46563
|
if (videoPath) reports.push(displayPath(videoPath));
|
|
46469
46564
|
if (checks.durationSec !== void 0 || checks.width !== void 0 || checks.height !== void 0) {
|
|
46470
46565
|
reports.push("ffprobe");
|
|
@@ -46477,31 +46572,31 @@ function renderSourceReports(projectDir, videoPath, checks) {
|
|
|
46477
46572
|
async function resolveRenderVideoPath(projectDir, explicit, beatId) {
|
|
46478
46573
|
if (explicit) {
|
|
46479
46574
|
const candidate = resolve43(process.cwd(), explicit);
|
|
46480
|
-
return
|
|
46575
|
+
return existsSync36(candidate) ? candidate : null;
|
|
46481
46576
|
}
|
|
46482
|
-
const renderReportPath =
|
|
46483
|
-
if (
|
|
46577
|
+
const renderReportPath = join20(projectDir, "render-report.json");
|
|
46578
|
+
if (existsSync36(renderReportPath)) {
|
|
46484
46579
|
try {
|
|
46485
|
-
const report = JSON.parse(await
|
|
46580
|
+
const report = JSON.parse(await readFile31(renderReportPath, "utf-8"));
|
|
46486
46581
|
const reportBeat = typeof report.beat === "string" ? report.beat : null;
|
|
46487
46582
|
if (!beatId && reportBeat === null || beatId && reportBeat === beatId) {
|
|
46488
46583
|
const candidate = resolveReportedPath(projectDir, report.outputPath);
|
|
46489
|
-
if (candidate &&
|
|
46584
|
+
if (candidate && existsSync36(candidate)) return candidate;
|
|
46490
46585
|
}
|
|
46491
46586
|
} catch {
|
|
46492
46587
|
}
|
|
46493
46588
|
}
|
|
46494
|
-
const reportPath =
|
|
46495
|
-
if (!beatId &&
|
|
46589
|
+
const reportPath = join20(projectDir, "build-report.json");
|
|
46590
|
+
if (!beatId && existsSync36(reportPath)) {
|
|
46496
46591
|
try {
|
|
46497
|
-
const report = JSON.parse(await
|
|
46592
|
+
const report = JSON.parse(await readFile31(reportPath, "utf-8"));
|
|
46498
46593
|
const candidate = resolveReportedPath(projectDir, report.outputPath);
|
|
46499
|
-
if (candidate &&
|
|
46594
|
+
if (candidate && existsSync36(candidate)) return candidate;
|
|
46500
46595
|
} catch {
|
|
46501
46596
|
}
|
|
46502
46597
|
}
|
|
46503
|
-
const rendersDir =
|
|
46504
|
-
if (!
|
|
46598
|
+
const rendersDir = join20(projectDir, "renders");
|
|
46599
|
+
if (!existsSync36(rendersDir)) return null;
|
|
46505
46600
|
const entries = await readdir5(rendersDir, { withFileTypes: true });
|
|
46506
46601
|
const candidates = [];
|
|
46507
46602
|
for (const entry of entries) {
|
|
@@ -46509,7 +46604,7 @@ async function resolveRenderVideoPath(projectDir, explicit, beatId) {
|
|
|
46509
46604
|
const ext = entry.name.slice(entry.name.lastIndexOf(".")).toLowerCase();
|
|
46510
46605
|
if (!VIDEO_EXTENSIONS.has(ext)) continue;
|
|
46511
46606
|
if (beatId && !entry.name.includes(sanitizeFileSegment(beatId))) continue;
|
|
46512
|
-
const full =
|
|
46607
|
+
const full = join20(rendersDir, entry.name);
|
|
46513
46608
|
const info = await stat6(full);
|
|
46514
46609
|
candidates.push({ path: full, mtimeMs: info.mtimeMs });
|
|
46515
46610
|
}
|
|
@@ -46527,14 +46622,14 @@ async function runAiRenderReview(projectDir, videoPath, model) {
|
|
|
46527
46622
|
});
|
|
46528
46623
|
}
|
|
46529
46624
|
function resolveStoryboardPath(projectDir) {
|
|
46530
|
-
const storyboardPath2 =
|
|
46531
|
-
return
|
|
46625
|
+
const storyboardPath2 = join20(projectDir, "STORYBOARD.md");
|
|
46626
|
+
return existsSync36(storyboardPath2) ? storyboardPath2 : null;
|
|
46532
46627
|
}
|
|
46533
46628
|
async function expectedDurationFromBuildReport(projectDir, beatId) {
|
|
46534
|
-
const reportPath =
|
|
46535
|
-
if (!
|
|
46629
|
+
const reportPath = join20(projectDir, "build-report.json");
|
|
46630
|
+
if (!existsSync36(reportPath)) return void 0;
|
|
46536
46631
|
try {
|
|
46537
|
-
const report = JSON.parse(await
|
|
46632
|
+
const report = JSON.parse(await readFile31(reportPath, "utf-8"));
|
|
46538
46633
|
const beats = beatId ? (report.beats ?? []).filter((beat) => beat.id === beatId) : report.beats ?? [];
|
|
46539
46634
|
const durations = beats.map(
|
|
46540
46635
|
(beat) => parseOptionalNumber(beat.sceneDurationSec) ?? parseOptionalNumber(beat.narration?.sceneDurationSec)
|
|
@@ -46546,10 +46641,10 @@ async function expectedDurationFromBuildReport(projectDir, beatId) {
|
|
|
46546
46641
|
}
|
|
46547
46642
|
}
|
|
46548
46643
|
async function beatTimingsFromBuildReport(projectDir, beatId) {
|
|
46549
|
-
const reportPath =
|
|
46550
|
-
if (!
|
|
46644
|
+
const reportPath = join20(projectDir, "build-report.json");
|
|
46645
|
+
if (!existsSync36(reportPath)) return [];
|
|
46551
46646
|
try {
|
|
46552
|
-
const report = JSON.parse(await
|
|
46647
|
+
const report = JSON.parse(await readFile31(reportPath, "utf-8"));
|
|
46553
46648
|
const source = report.beats ?? [];
|
|
46554
46649
|
const selected = beatId ? source.filter((beat) => beat.id === beatId) : source;
|
|
46555
46650
|
let cursor = 0;
|
|
@@ -46575,17 +46670,17 @@ async function beatTimingsFromBuildReport(projectDir, beatId) {
|
|
|
46575
46670
|
}
|
|
46576
46671
|
async function buildRenderReviewProjectContext(projectDir) {
|
|
46577
46672
|
const parts = [];
|
|
46578
|
-
const storyboard = await readTextIfExists(
|
|
46673
|
+
const storyboard = await readTextIfExists(join20(projectDir, "STORYBOARD.md"));
|
|
46579
46674
|
if (storyboard) {
|
|
46580
46675
|
parts.push(`STORYBOARD.md:
|
|
46581
46676
|
${truncateText(storyboard, 4e3)}`);
|
|
46582
46677
|
parts.push(`Storyboard beat cue summary:
|
|
46583
46678
|
${storyboardBeatCueSummary(storyboard)}`);
|
|
46584
46679
|
}
|
|
46585
|
-
const design = await readTextIfExists(
|
|
46680
|
+
const design = await readTextIfExists(join20(projectDir, "DESIGN.md"));
|
|
46586
46681
|
if (design) parts.push(`DESIGN.md:
|
|
46587
46682
|
${truncateText(design, 4e3)}`);
|
|
46588
|
-
const buildReport = await readTextIfExists(
|
|
46683
|
+
const buildReport = await readTextIfExists(join20(projectDir, "build-report.json"));
|
|
46589
46684
|
if (buildReport) parts.push(`build-report.json:
|
|
46590
46685
|
${truncateText(buildReport, 6e3)}`);
|
|
46591
46686
|
const timings = await beatTimingsFromBuildReport(projectDir);
|
|
@@ -46613,9 +46708,9 @@ function storyboardBeatCueSummary(storyboard) {
|
|
|
46613
46708
|
}
|
|
46614
46709
|
}
|
|
46615
46710
|
async function readTextIfExists(path2) {
|
|
46616
|
-
if (!
|
|
46711
|
+
if (!existsSync36(path2)) return null;
|
|
46617
46712
|
try {
|
|
46618
|
-
return await
|
|
46713
|
+
return await readFile31(path2, "utf-8");
|
|
46619
46714
|
} catch {
|
|
46620
46715
|
return null;
|
|
46621
46716
|
}
|
|
@@ -46909,9 +47004,9 @@ var init_video_providers = __esm({
|
|
|
46909
47004
|
});
|
|
46910
47005
|
|
|
46911
47006
|
// src/commands/ai-script-pipeline.ts
|
|
46912
|
-
import { readFile as
|
|
47007
|
+
import { readFile as readFile34, writeFile as writeFile39, unlink as unlink2, rename as rename3 } from "node:fs/promises";
|
|
46913
47008
|
import { resolve as resolve46, extname as extname9 } from "node:path";
|
|
46914
|
-
import { existsSync as
|
|
47009
|
+
import { existsSync as existsSync38 } from "node:fs";
|
|
46915
47010
|
async function executeRegenerateScene(options) {
|
|
46916
47011
|
const result = {
|
|
46917
47012
|
success: false,
|
|
@@ -46920,16 +47015,16 @@ async function executeRegenerateScene(options) {
|
|
|
46920
47015
|
};
|
|
46921
47016
|
try {
|
|
46922
47017
|
const outputDir = resolve46(process.cwd(), options.projectDir);
|
|
46923
|
-
if (!
|
|
47018
|
+
if (!existsSync38(outputDir)) {
|
|
46924
47019
|
return { ...result, error: `Project directory not found: ${outputDir}` };
|
|
46925
47020
|
}
|
|
46926
47021
|
const yamlPath = resolve46(outputDir, "storyboard.yaml");
|
|
46927
47022
|
const jsonPath = resolve46(outputDir, "storyboard.json");
|
|
46928
|
-
const storyboardPath2 =
|
|
47023
|
+
const storyboardPath2 = existsSync38(yamlPath) ? yamlPath : existsSync38(jsonPath) ? jsonPath : null;
|
|
46929
47024
|
if (!storyboardPath2) {
|
|
46930
47025
|
return { ...result, error: `Storyboard not found in: ${outputDir} (expected storyboard.yaml or storyboard.json)` };
|
|
46931
47026
|
}
|
|
46932
|
-
const storyboardContent = await
|
|
47027
|
+
const storyboardContent = await readFile34(storyboardPath2, "utf-8");
|
|
46933
47028
|
const segments = storyboardPath2.endsWith(".yaml") ? (0, import_yaml6.parse)(storyboardContent).scenes : JSON.parse(storyboardContent);
|
|
46934
47029
|
for (const sceneNum of options.scenes) {
|
|
46935
47030
|
if (sceneNum < 1 || sceneNum > segments.length) {
|
|
@@ -47018,15 +47113,15 @@ IMPORTANT - Character appearance must match exactly: ${characterDesc}`;
|
|
|
47018
47113
|
const refSceneNum = options.referenceScene;
|
|
47019
47114
|
if (refSceneNum && refSceneNum >= 1 && refSceneNum <= segments.length && refSceneNum !== sceneNum) {
|
|
47020
47115
|
const refImagePath = resolve46(outputDir, `scene-${refSceneNum}.png`);
|
|
47021
|
-
if (
|
|
47022
|
-
referenceImageBuffer = await
|
|
47116
|
+
if (existsSync38(refImagePath)) {
|
|
47117
|
+
referenceImageBuffer = await readFile34(refImagePath);
|
|
47023
47118
|
}
|
|
47024
47119
|
} else if (!refSceneNum) {
|
|
47025
47120
|
for (let i = 1; i <= segments.length; i++) {
|
|
47026
47121
|
if (i !== sceneNum) {
|
|
47027
47122
|
const otherImagePath = resolve46(outputDir, `scene-${i}.png`);
|
|
47028
|
-
if (
|
|
47029
|
-
referenceImageBuffer = await
|
|
47123
|
+
if (existsSync38(otherImagePath)) {
|
|
47124
|
+
referenceImageBuffer = await readFile34(otherImagePath);
|
|
47030
47125
|
break;
|
|
47031
47126
|
}
|
|
47032
47127
|
}
|
|
@@ -47114,11 +47209,11 @@ Generate the single-person scene image now.`;
|
|
|
47114
47209
|
}
|
|
47115
47210
|
if (regenerateVideo && videoApiKey) {
|
|
47116
47211
|
options.onProgress?.(`Scene ${sceneNum}: regenerating video (${options.generator || "grok"})...`);
|
|
47117
|
-
if (!
|
|
47212
|
+
if (!existsSync38(imagePath)) {
|
|
47118
47213
|
result.failedScenes.push(sceneNum);
|
|
47119
47214
|
continue;
|
|
47120
47215
|
}
|
|
47121
|
-
const imageBuffer = await
|
|
47216
|
+
const imageBuffer = await readFile34(imagePath);
|
|
47122
47217
|
const videoDuration = segment.duration > 5 ? 10 : 5;
|
|
47123
47218
|
const maxRetries = options.retries ?? DEFAULT_VIDEO_RETRIES;
|
|
47124
47219
|
if (options.generator === "grok") {
|
|
@@ -47340,9 +47435,9 @@ var init_ai_script_pipeline = __esm({
|
|
|
47340
47435
|
});
|
|
47341
47436
|
|
|
47342
47437
|
// src/commands/ai-highlights.ts
|
|
47343
|
-
import { readFile as
|
|
47438
|
+
import { readFile as readFile36, writeFile as writeFile41, mkdir as mkdir21 } from "node:fs/promises";
|
|
47344
47439
|
import { resolve as resolve48, dirname as dirname17, basename as basename13, extname as extname10 } from "node:path";
|
|
47345
|
-
import { existsSync as
|
|
47440
|
+
import { existsSync as existsSync40 } from "node:fs";
|
|
47346
47441
|
function filterHighlights(highlights, options) {
|
|
47347
47442
|
let filtered = highlights.filter((h) => h.confidence >= options.threshold);
|
|
47348
47443
|
filtered.sort((a, b) => b.confidence - a.confidence);
|
|
@@ -47382,7 +47477,7 @@ function truncate(text, maxLength) {
|
|
|
47382
47477
|
async function executeHighlights(options) {
|
|
47383
47478
|
try {
|
|
47384
47479
|
const absPath = resolve48(process.cwd(), options.media);
|
|
47385
|
-
if (!
|
|
47480
|
+
if (!existsSync40(absPath)) {
|
|
47386
47481
|
return { success: false, highlights: [], totalDuration: 0, totalHighlightDuration: 0, error: `File not found: ${absPath}` };
|
|
47387
47482
|
}
|
|
47388
47483
|
const ext = extname10(absPath).toLowerCase();
|
|
@@ -47401,7 +47496,7 @@ async function executeHighlights(options) {
|
|
|
47401
47496
|
sourceDuration = await ffprobeDuration(absPath);
|
|
47402
47497
|
const gemini = new GeminiProvider();
|
|
47403
47498
|
await gemini.initialize({ apiKey: geminiApiKey });
|
|
47404
|
-
const videoBuffer = await
|
|
47499
|
+
const videoBuffer = await readFile36(absPath);
|
|
47405
47500
|
const criteriaText = options.criteria === "all" || !options.criteria ? "emotional, informative, and funny moments" : `${options.criteria} moments`;
|
|
47406
47501
|
const durationText = targetDuration ? `Target a total highlight duration of ${targetDuration} seconds.` : "";
|
|
47407
47502
|
const countText = maxCount ? `Find up to ${maxCount} highlights.` : "";
|
|
@@ -47495,10 +47590,10 @@ Analyze both what is SHOWN (visual cues, actions, expressions) and what is SAID
|
|
|
47495
47590
|
}
|
|
47496
47591
|
const whisper = new WhisperProvider();
|
|
47497
47592
|
await whisper.initialize({ apiKey: openaiApiKey });
|
|
47498
|
-
const audioBuffer = await
|
|
47593
|
+
const audioBuffer = await readFile36(audioPath);
|
|
47499
47594
|
const audioBlob = new Blob([audioBuffer]);
|
|
47500
47595
|
const transcriptResult = await whisper.transcribe(audioBlob, options.language);
|
|
47501
|
-
if (tempAudioPath &&
|
|
47596
|
+
if (tempAudioPath && existsSync40(tempAudioPath)) {
|
|
47502
47597
|
const { unlink: unlinkFile } = await import("node:fs/promises");
|
|
47503
47598
|
await unlinkFile(tempAudioPath).catch(() => {
|
|
47504
47599
|
});
|
|
@@ -47585,7 +47680,7 @@ async function executeAutoShorts(options) {
|
|
|
47585
47680
|
return { success: false, shorts: [], error: "FFmpeg not found" };
|
|
47586
47681
|
}
|
|
47587
47682
|
const absPath = resolve48(process.cwd(), options.video);
|
|
47588
|
-
if (!
|
|
47683
|
+
if (!existsSync40(absPath)) {
|
|
47589
47684
|
return { success: false, shorts: [], error: `File not found: ${absPath}` };
|
|
47590
47685
|
}
|
|
47591
47686
|
const targetDuration = options.duration ?? 60;
|
|
@@ -47598,7 +47693,7 @@ async function executeAutoShorts(options) {
|
|
|
47598
47693
|
}
|
|
47599
47694
|
const gemini = new GeminiProvider();
|
|
47600
47695
|
await gemini.initialize({ apiKey: geminiApiKey });
|
|
47601
|
-
const videoBuffer = await
|
|
47696
|
+
const videoBuffer = await readFile36(absPath);
|
|
47602
47697
|
const geminiPrompt = `Analyze this video to find the BEST moments for short-form vertical video content (TikTok, YouTube Shorts, Instagram Reels).
|
|
47603
47698
|
|
|
47604
47699
|
Find ${shortCount * 3} potential clips that are ${targetDuration} seconds or shorter each.
|
|
@@ -47675,7 +47770,7 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
|
|
|
47675
47770
|
await execSafe("ffmpeg", ["-i", absPath, "-vn", "-acodec", "libmp3lame", "-q:a", "2", tempAudio, "-y"]);
|
|
47676
47771
|
const whisper = new WhisperProvider();
|
|
47677
47772
|
await whisper.initialize({ apiKey: openaiApiKey });
|
|
47678
|
-
const audioBuffer = await
|
|
47773
|
+
const audioBuffer = await readFile36(tempAudio);
|
|
47679
47774
|
const audioBlob = new Blob([audioBuffer]);
|
|
47680
47775
|
const transcript = await whisper.transcribe(audioBlob, options.language);
|
|
47681
47776
|
try {
|
|
@@ -47713,7 +47808,7 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
|
|
|
47713
47808
|
};
|
|
47714
47809
|
}
|
|
47715
47810
|
const outputDir = options.outputDir ? resolve48(process.cwd(), options.outputDir) : dirname17(absPath);
|
|
47716
|
-
if (options.outputDir && !
|
|
47811
|
+
if (options.outputDir && !existsSync40(outputDir)) {
|
|
47717
47812
|
await mkdir21(outputDir, { recursive: true });
|
|
47718
47813
|
}
|
|
47719
47814
|
const result = {
|
|
@@ -47796,7 +47891,7 @@ function registerHighlightsCommands(aiCommand) {
|
|
|
47796
47891
|
validateOutputPath(options.output);
|
|
47797
47892
|
}
|
|
47798
47893
|
const absPath = resolve48(process.cwd(), mediaPath);
|
|
47799
|
-
if (!
|
|
47894
|
+
if (!existsSync40(absPath)) {
|
|
47800
47895
|
exitWithError(notFoundError(absPath));
|
|
47801
47896
|
}
|
|
47802
47897
|
if (options.dryRun) {
|
|
@@ -47853,7 +47948,7 @@ function registerHighlightsCommands(aiCommand) {
|
|
|
47853
47948
|
const geminiSpinner = ora("\u{1F3AC} Analyzing video with Gemini (visual + audio)...").start();
|
|
47854
47949
|
const gemini = new GeminiProvider();
|
|
47855
47950
|
await gemini.initialize({ apiKey: geminiApiKey });
|
|
47856
|
-
const videoBuffer = await
|
|
47951
|
+
const videoBuffer = await readFile36(absPath);
|
|
47857
47952
|
const criteriaText = options.criteria === "all" ? "emotional, informative, and funny moments" : `${options.criteria} moments`;
|
|
47858
47953
|
const durationText = targetDuration ? `Target a total highlight duration of ${targetDuration} seconds.` : "";
|
|
47859
47954
|
const countText = maxCount ? `Find up to ${maxCount} highlights.` : "";
|
|
@@ -47978,12 +48073,12 @@ Analyze both what is SHOWN (visual cues, actions, expressions) and what is SAID
|
|
|
47978
48073
|
const transcribeSpinner = ora("\u{1F4DD} Transcribing with Whisper...").start();
|
|
47979
48074
|
const whisper = new WhisperProvider();
|
|
47980
48075
|
await whisper.initialize({ apiKey: openaiApiKey });
|
|
47981
|
-
const audioBuffer = await
|
|
48076
|
+
const audioBuffer = await readFile36(audioPath);
|
|
47982
48077
|
const audioBlob = new Blob([audioBuffer]);
|
|
47983
48078
|
const transcriptResult = await whisper.transcribe(audioBlob, options.language);
|
|
47984
48079
|
if (transcriptResult.status === "failed" || !transcriptResult.segments) {
|
|
47985
48080
|
transcribeSpinner.fail("Transcription failed");
|
|
47986
|
-
if (tempAudioPath &&
|
|
48081
|
+
if (tempAudioPath && existsSync40(tempAudioPath)) {
|
|
47987
48082
|
const { unlink: unlinkFile } = await import("node:fs/promises");
|
|
47988
48083
|
await unlinkFile(tempAudioPath).catch(() => {
|
|
47989
48084
|
});
|
|
@@ -47991,7 +48086,7 @@ Analyze both what is SHOWN (visual cues, actions, expressions) and what is SAID
|
|
|
47991
48086
|
exitWithError(apiError(`Transcription failed: ${transcriptResult.error}`, true));
|
|
47992
48087
|
}
|
|
47993
48088
|
transcribeSpinner.succeed(source_default.green(`Transcribed ${transcriptResult.segments.length} segments`));
|
|
47994
|
-
if (tempAudioPath &&
|
|
48089
|
+
if (tempAudioPath && existsSync40(tempAudioPath)) {
|
|
47995
48090
|
const { unlink: unlinkFile } = await import("node:fs/promises");
|
|
47996
48091
|
await unlinkFile(tempAudioPath).catch(() => {
|
|
47997
48092
|
});
|
|
@@ -48097,7 +48192,7 @@ Analyze both what is SHOWN (visual cues, actions, expressions) and what is SAID
|
|
|
48097
48192
|
validateOutputPath(options.output);
|
|
48098
48193
|
}
|
|
48099
48194
|
const absPath = resolve48(process.cwd(), videoPath);
|
|
48100
|
-
if (!
|
|
48195
|
+
if (!existsSync40(absPath)) {
|
|
48101
48196
|
exitWithError(notFoundError(absPath));
|
|
48102
48197
|
}
|
|
48103
48198
|
if (options.dryRun) {
|
|
@@ -48147,7 +48242,7 @@ Analyze both what is SHOWN (visual cues, actions, expressions) and what is SAID
|
|
|
48147
48242
|
const spinner2 = ora("\u{1F3AC} Analyzing video with Gemini (visual + audio)...").start();
|
|
48148
48243
|
const gemini = new GeminiProvider();
|
|
48149
48244
|
await gemini.initialize({ apiKey: geminiApiKey });
|
|
48150
|
-
const videoBuffer = await
|
|
48245
|
+
const videoBuffer = await readFile36(absPath);
|
|
48151
48246
|
const geminiPrompt = `Analyze this video to find the BEST moments for short-form vertical video content (TikTok, YouTube Shorts, Instagram Reels).
|
|
48152
48247
|
|
|
48153
48248
|
Find ${shortCount * 3} potential clips that are ${targetDuration} seconds or shorter each.
|
|
@@ -48244,7 +48339,7 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
|
|
|
48244
48339
|
spinner2.text = "Transcribing audio...";
|
|
48245
48340
|
const whisper = new WhisperProvider();
|
|
48246
48341
|
await whisper.initialize({ apiKey: openaiApiKey });
|
|
48247
|
-
const audioBuffer = await
|
|
48342
|
+
const audioBuffer = await readFile36(tempAudio);
|
|
48248
48343
|
const audioBlob = new Blob([audioBuffer]);
|
|
48249
48344
|
const transcript = await whisper.transcribe(audioBlob, options.language);
|
|
48250
48345
|
try {
|
|
@@ -48290,7 +48385,7 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
|
|
|
48290
48385
|
return;
|
|
48291
48386
|
}
|
|
48292
48387
|
const outputDir = options.outputDir ? resolve48(process.cwd(), options.outputDir) : dirname17(absPath);
|
|
48293
|
-
if (options.outputDir && !
|
|
48388
|
+
if (options.outputDir && !existsSync40(outputDir)) {
|
|
48294
48389
|
await mkdir21(outputDir, { recursive: true });
|
|
48295
48390
|
}
|
|
48296
48391
|
for (let i = 0; i < selectedHighlights.length; i++) {
|
|
@@ -48307,7 +48402,7 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
|
|
|
48307
48402
|
outputPath = resolve48(outputDir, `${baseName}-short-${i + 1}.mp4`);
|
|
48308
48403
|
}
|
|
48309
48404
|
const parentDir = dirname17(outputPath);
|
|
48310
|
-
if (!
|
|
48405
|
+
if (!existsSync40(parentDir)) {
|
|
48311
48406
|
await mkdir21(parentDir, { recursive: true });
|
|
48312
48407
|
}
|
|
48313
48408
|
const { stdout: probeOut } = await execSafe("ffprobe", [
|
|
@@ -48382,7 +48477,7 @@ var init_ai_highlights = __esm({
|
|
|
48382
48477
|
// src/commands/ai-animated-caption.ts
|
|
48383
48478
|
import { resolve as resolve49, dirname as dirname18, basename as basename14 } from "node:path";
|
|
48384
48479
|
import { writeFile as writeFile42, mkdir as mkdir22, rm as rm3 } from "node:fs/promises";
|
|
48385
|
-
import { existsSync as
|
|
48480
|
+
import { existsSync as existsSync41 } from "node:fs";
|
|
48386
48481
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
48387
48482
|
function groupWords(words, options = {}) {
|
|
48388
48483
|
if (words.length === 0) return [];
|
|
@@ -48559,7 +48654,7 @@ async function executeAnimatedCaption(options) {
|
|
|
48559
48654
|
const groups = groupWords(transcript.words, { wordsPerGroup, maxChars });
|
|
48560
48655
|
const absOutputPath = resolve49(process.cwd(), outputPath);
|
|
48561
48656
|
const outDir = dirname18(absOutputPath);
|
|
48562
|
-
if (!
|
|
48657
|
+
if (!existsSync41(outDir)) {
|
|
48563
48658
|
await mkdir22(outDir, { recursive: true });
|
|
48564
48659
|
}
|
|
48565
48660
|
if (tier === "ass") {
|
|
@@ -58242,7 +58337,7 @@ __export(detect_exports, {
|
|
|
58242
58337
|
executeDetectScenes: () => executeDetectScenes,
|
|
58243
58338
|
executeDetectSilence: () => executeDetectSilence
|
|
58244
58339
|
});
|
|
58245
|
-
import { readFile as
|
|
58340
|
+
import { readFile as readFile38, writeFile as writeFile44 } from "node:fs/promises";
|
|
58246
58341
|
import { resolve as resolve53, basename as basename18 } from "node:path";
|
|
58247
58342
|
async function executeDetectScenes(options) {
|
|
58248
58343
|
try {
|
|
@@ -58486,7 +58581,7 @@ var init_detect = __esm({
|
|
|
58486
58581
|
}
|
|
58487
58582
|
if (options.project) {
|
|
58488
58583
|
const projectPath = resolve53(process.cwd(), options.project);
|
|
58489
|
-
const content = await
|
|
58584
|
+
const content = await readFile38(projectPath, "utf-8");
|
|
58490
58585
|
const data = JSON.parse(content);
|
|
58491
58586
|
const project = Project.fromJSON(data);
|
|
58492
58587
|
let source = project.getSources().find((s) => s.url === absPath);
|
|
@@ -60177,14 +60272,14 @@ var init_adapters = __esm({
|
|
|
60177
60272
|
});
|
|
60178
60273
|
|
|
60179
60274
|
// src/commands/_shared/storyboard-revise.ts
|
|
60180
|
-
import { existsSync as
|
|
60181
|
-
import { readFile as
|
|
60182
|
-
import { join as
|
|
60275
|
+
import { existsSync as existsSync45 } from "node:fs";
|
|
60276
|
+
import { readFile as readFile42, writeFile as writeFile47 } from "node:fs/promises";
|
|
60277
|
+
import { join as join23, resolve as resolve57 } from "node:path";
|
|
60183
60278
|
async function executeStoryboardRevision(opts) {
|
|
60184
60279
|
const projectDir = resolve57(opts.projectDir);
|
|
60185
|
-
const storyboardPath2 =
|
|
60280
|
+
const storyboardPath2 = join23(projectDir, "STORYBOARD.md");
|
|
60186
60281
|
const retryWith = [`vibe storyboard revise ${projectDir} --from "<request>" --dry-run --json`];
|
|
60187
|
-
if (!
|
|
60282
|
+
if (!existsSync45(storyboardPath2)) {
|
|
60188
60283
|
return failureResult({
|
|
60189
60284
|
projectDir,
|
|
60190
60285
|
storyboardPath: storyboardPath2,
|
|
@@ -60194,7 +60289,7 @@ async function executeStoryboardRevision(opts) {
|
|
|
60194
60289
|
retryWith: [`vibe init ${projectDir} --from "<brief>" --json`]
|
|
60195
60290
|
});
|
|
60196
60291
|
}
|
|
60197
|
-
const currentStoryboard = await
|
|
60292
|
+
const currentStoryboard = await readFile42(storyboardPath2, "utf-8");
|
|
60198
60293
|
const projectConfig = await readProjectConfig(projectDir);
|
|
60199
60294
|
const explicitComposer = opts.composer ?? projectConfig.config.providers.composer ?? void 0;
|
|
60200
60295
|
let provider = explicitComposer ?? "claude";
|
|
@@ -60417,18 +60512,18 @@ function diffChangedBeats(before, after) {
|
|
|
60417
60512
|
return ids.filter((id) => JSON.stringify(beforeBeats.get(id) ?? null) !== JSON.stringify(afterBeats.get(id) ?? null));
|
|
60418
60513
|
}
|
|
60419
60514
|
async function readRevisionContext(projectDir) {
|
|
60420
|
-
const designMd = await readOptionalText(
|
|
60421
|
-
const buildReportJson = await readOptionalJsonText(
|
|
60515
|
+
const designMd = await readOptionalText(join23(projectDir, "DESIGN.md"));
|
|
60516
|
+
const buildReportJson = await readOptionalJsonText(join23(projectDir, "build-report.json"));
|
|
60422
60517
|
return { designMd, buildReportJson };
|
|
60423
60518
|
}
|
|
60424
60519
|
async function readOptionalText(path2) {
|
|
60425
|
-
if (!
|
|
60426
|
-
return
|
|
60520
|
+
if (!existsSync45(path2)) return null;
|
|
60521
|
+
return readFile42(path2, "utf-8");
|
|
60427
60522
|
}
|
|
60428
60523
|
async function readOptionalJsonText(path2) {
|
|
60429
|
-
if (!
|
|
60524
|
+
if (!existsSync45(path2)) return null;
|
|
60430
60525
|
try {
|
|
60431
|
-
const parsed = JSON.parse(await
|
|
60526
|
+
const parsed = JSON.parse(await readFile42(path2, "utf-8"));
|
|
60432
60527
|
return JSON.stringify(parsed, null, 2);
|
|
60433
60528
|
} catch {
|
|
60434
60529
|
return null;
|
|
@@ -60476,7 +60571,7 @@ var init_storyboard_revise = __esm({
|
|
|
60476
60571
|
});
|
|
60477
60572
|
|
|
60478
60573
|
// src/commands/_shared/build-asset-reference.ts
|
|
60479
|
-
import { existsSync as
|
|
60574
|
+
import { existsSync as existsSync47 } from "node:fs";
|
|
60480
60575
|
import { extname as extname13, isAbsolute as isAbsolute5, relative as relative10, resolve as resolve59, sep } from "node:path";
|
|
60481
60576
|
function resolveTypedAssetReference(projectDir, kind, value) {
|
|
60482
60577
|
const raw = stringOrUndefined2(value);
|
|
@@ -60507,7 +60602,7 @@ function resolveReference(projectDir, kind, raw, source, forcedError) {
|
|
|
60507
60602
|
const absPath = isAbsolute5(raw) ? resolve59(raw) : resolve59(projectDir, raw);
|
|
60508
60603
|
const rel = relative10(resolve59(projectDir), absPath);
|
|
60509
60604
|
const withinProject = rel.length > 0 && !rel.startsWith("..") && !isAbsolute5(rel);
|
|
60510
|
-
const exists = withinProject &&
|
|
60605
|
+
const exists = withinProject && existsSync47(absPath);
|
|
60511
60606
|
let error = forcedError;
|
|
60512
60607
|
if (!error && inferred && inferred !== kind && !isNarrationAudioReference(kind, ext)) {
|
|
60513
60608
|
error = `Asset reference "${raw}" is a ${inferred} asset, not ${kind}.`;
|
|
@@ -60694,9 +60789,9 @@ var init_build_backdrop_prompt = __esm({
|
|
|
60694
60789
|
});
|
|
60695
60790
|
|
|
60696
60791
|
// src/commands/_shared/build-plan.ts
|
|
60697
|
-
import { existsSync as
|
|
60698
|
-
import { readFile as
|
|
60699
|
-
import { dirname as dirname21, join as
|
|
60792
|
+
import { existsSync as existsSync48 } from "node:fs";
|
|
60793
|
+
import { readFile as readFile44 } from "node:fs/promises";
|
|
60794
|
+
import { dirname as dirname21, join as join24, resolve as resolve60 } from "node:path";
|
|
60700
60795
|
function backdropCostUsd(quality) {
|
|
60701
60796
|
return quality === "hd" ? 0.2 : 0.05;
|
|
60702
60797
|
}
|
|
@@ -60705,11 +60800,11 @@ async function createBuildPlan(opts) {
|
|
|
60705
60800
|
loadPlanEnv(projectDir);
|
|
60706
60801
|
const stage = opts.stage ?? "all";
|
|
60707
60802
|
const config2 = await readProjectConfig(projectDir);
|
|
60708
|
-
const storyboardPath2 =
|
|
60803
|
+
const storyboardPath2 = join24(projectDir, "STORYBOARD.md");
|
|
60709
60804
|
const warnings = [];
|
|
60710
60805
|
const retryWith = [];
|
|
60711
60806
|
const providerResolution = [];
|
|
60712
|
-
if (!
|
|
60807
|
+
if (!existsSync48(storyboardPath2)) {
|
|
60713
60808
|
const validation2 = {
|
|
60714
60809
|
ok: false,
|
|
60715
60810
|
issues: [
|
|
@@ -60738,7 +60833,7 @@ async function createBuildPlan(opts) {
|
|
|
60738
60833
|
validation: validation2
|
|
60739
60834
|
});
|
|
60740
60835
|
}
|
|
60741
|
-
const storyboardMd = await
|
|
60836
|
+
const storyboardMd = await readFile44(storyboardPath2, "utf-8");
|
|
60742
60837
|
const validation = validateStoryboardMarkdown(storyboardMd);
|
|
60743
60838
|
const validationIssues = [...validation.issues];
|
|
60744
60839
|
const parsed = parseStoryboard(storyboardMd);
|
|
@@ -60764,7 +60859,7 @@ async function createBuildPlan(opts) {
|
|
|
60764
60859
|
const imageQuality = opts.imageQuality ?? config2.config.build.imageQuality ?? "hd";
|
|
60765
60860
|
const imageSize2 = opts.imageSize ?? config2.config.build.imageSize ?? "1536x1024";
|
|
60766
60861
|
const imageRatio = imageRatioForSize(imageSize2);
|
|
60767
|
-
const needsComposer = includeCompose && mode !== "agent" && sourceBeats.some((beat) => !
|
|
60862
|
+
const needsComposer = includeCompose && mode !== "agent" && sourceBeats.some((beat) => !existsSync48(join24(projectDir, `compositions/scene-${beat.id}.html`)));
|
|
60768
60863
|
const resolved = await resolvePlanProviders({
|
|
60769
60864
|
projectDir,
|
|
60770
60865
|
config: config2,
|
|
@@ -60912,7 +61007,7 @@ async function createBuildPlan(opts) {
|
|
|
60912
61007
|
active: includeAssets
|
|
60913
61008
|
}) : null;
|
|
60914
61009
|
const compositionPath = `compositions/scene-${beat.id}.html`;
|
|
60915
|
-
const compositionExists =
|
|
61010
|
+
const compositionExists = existsSync48(join24(projectDir, compositionPath));
|
|
60916
61011
|
for (const asset of [narration, backdrop, keyframe, video, music]) {
|
|
60917
61012
|
if (!asset) continue;
|
|
60918
61013
|
if (asset.referenceError) {
|
|
@@ -60981,7 +61076,7 @@ async function createBuildPlan(opts) {
|
|
|
60981
61076
|
}
|
|
60982
61077
|
}
|
|
60983
61078
|
const validationOk = !validationIssues.some((issue) => issue.severity === "error");
|
|
60984
|
-
if (!
|
|
61079
|
+
if (!existsSync48(join24(projectDir, config2.config.composition.entry))) {
|
|
60985
61080
|
missing.add("root-composition");
|
|
60986
61081
|
if (validationOk) retryWith.push(`vibe build ${projectDir} --stage sync --json`);
|
|
60987
61082
|
}
|
|
@@ -61015,7 +61110,7 @@ async function createBuildPlan(opts) {
|
|
|
61015
61110
|
}
|
|
61016
61111
|
if (referenced.size > 0) {
|
|
61017
61112
|
const toGenerate = resolveCharacters(parsed.frontmatter).filter(
|
|
61018
|
-
(c) => referenced.has(c.name) && c.prompt && (opts.force || !
|
|
61113
|
+
(c) => referenced.has(c.name) && c.prompt && (opts.force || !existsSync48(join24(projectDir, `assets/character-${c.name}.png`)))
|
|
61019
61114
|
);
|
|
61020
61115
|
if (toGenerate.length > 0) {
|
|
61021
61116
|
estimatedCostUsd += toGenerate.length * backdropCostUsd(opts.imageQuality ?? "hd");
|
|
@@ -61128,8 +61223,8 @@ function assetPlan(opts) {
|
|
|
61128
61223
|
reason: ready ? "referenced-asset" : "invalid-reference"
|
|
61129
61224
|
};
|
|
61130
61225
|
}
|
|
61131
|
-
const canonicalExists =
|
|
61132
|
-
const cacheHit = opts.cache ?
|
|
61226
|
+
const canonicalExists = existsSync48(join24(opts.projectDir, opts.path));
|
|
61227
|
+
const cacheHit = opts.cache ? existsSync48(join24(opts.projectDir, opts.cache.path)) : false;
|
|
61133
61228
|
const freshness = assetFreshnessFromMetadata({
|
|
61134
61229
|
projectDir: opts.projectDir,
|
|
61135
61230
|
kind: opts.kind,
|
|
@@ -61327,7 +61422,7 @@ async function firstConfiguredComposer(projectDir) {
|
|
|
61327
61422
|
}
|
|
61328
61423
|
function firstExisting2(projectDir, paths) {
|
|
61329
61424
|
for (const path2 of paths) {
|
|
61330
|
-
if (
|
|
61425
|
+
if (existsSync48(join24(projectDir, path2))) return path2;
|
|
61331
61426
|
}
|
|
61332
61427
|
return null;
|
|
61333
61428
|
}
|
|
@@ -61368,12 +61463,12 @@ function unique3(items) {
|
|
|
61368
61463
|
return [...new Set(items.filter((item) => item.length > 0))];
|
|
61369
61464
|
}
|
|
61370
61465
|
function loadPlanEnv(projectDir) {
|
|
61371
|
-
(0, import_dotenv3.config)({ path:
|
|
61466
|
+
(0, import_dotenv3.config)({ path: join24(projectDir, ".env"), quiet: true });
|
|
61372
61467
|
(0, import_dotenv3.config)({ path: resolve60(process.cwd(), ".env"), quiet: true });
|
|
61373
61468
|
let dir = process.cwd();
|
|
61374
61469
|
while (dir !== dirname21(dir)) {
|
|
61375
|
-
if (
|
|
61376
|
-
(0, import_dotenv3.config)({ path:
|
|
61470
|
+
if (existsSync48(join24(dir, "pnpm-workspace.yaml"))) {
|
|
61471
|
+
(0, import_dotenv3.config)({ path: join24(dir, ".env"), quiet: true });
|
|
61377
61472
|
return;
|
|
61378
61473
|
}
|
|
61379
61474
|
dir = dirname21(dir);
|
|
@@ -61454,7 +61549,7 @@ var init_concurrency = __esm({
|
|
|
61454
61549
|
});
|
|
61455
61550
|
|
|
61456
61551
|
// src/commands/_shared/scene-audio-scan.ts
|
|
61457
|
-
import { readFile as
|
|
61552
|
+
import { readFile as readFile45 } from "node:fs/promises";
|
|
61458
61553
|
import { resolve as resolve62 } from "node:path";
|
|
61459
61554
|
function parseRootClips(rootHtml) {
|
|
61460
61555
|
const clips = [];
|
|
@@ -61495,7 +61590,7 @@ function makeFsCompositionReader(projectDir) {
|
|
|
61495
61590
|
return async (compositionSrcRel) => {
|
|
61496
61591
|
const abs = resolve62(projectDir, compositionSrcRel);
|
|
61497
61592
|
try {
|
|
61498
|
-
return await
|
|
61593
|
+
return await readFile45(abs, "utf-8");
|
|
61499
61594
|
} catch {
|
|
61500
61595
|
return null;
|
|
61501
61596
|
}
|
|
@@ -61695,9 +61790,9 @@ __export(scene_render_exports, {
|
|
|
61695
61790
|
qualityToCrf: () => qualityToCrf2
|
|
61696
61791
|
});
|
|
61697
61792
|
import { execFile as execFile2 } from "node:child_process";
|
|
61698
|
-
import { existsSync as
|
|
61699
|
-
import { mkdir as mkdir25, readFile as
|
|
61700
|
-
import { resolve as resolve64, relative as relative11, dirname as dirname23, basename as basename21, join as
|
|
61793
|
+
import { existsSync as existsSync49 } from "node:fs";
|
|
61794
|
+
import { mkdir as mkdir25, readFile as readFile46, stat as stat8, writeFile as writeFile49 } from "node:fs/promises";
|
|
61795
|
+
import { resolve as resolve64, relative as relative11, dirname as dirname23, basename as basename21, join as join25, isAbsolute as isAbsolute6 } from "node:path";
|
|
61701
61796
|
import { promisify as promisify2 } from "node:util";
|
|
61702
61797
|
import {
|
|
61703
61798
|
createRenderJob,
|
|
@@ -61789,7 +61884,7 @@ async function executeSceneRender(opts = {}) {
|
|
|
61789
61884
|
}
|
|
61790
61885
|
let autoSyncApplied = false;
|
|
61791
61886
|
let autoSyncWarning;
|
|
61792
|
-
if (!opts.beatId &&
|
|
61887
|
+
if (!opts.beatId && existsSync49(join25(projectDir, "STORYBOARD.md"))) {
|
|
61793
61888
|
try {
|
|
61794
61889
|
const rootPlan = await createProjectRootSyncPlan({ projectDir });
|
|
61795
61890
|
const subPlans = await createSubCompDurationSyncPlans({
|
|
@@ -61881,7 +61976,7 @@ async function executeSceneRender(opts = {}) {
|
|
|
61881
61976
|
let audioMuxWarning;
|
|
61882
61977
|
try {
|
|
61883
61978
|
opts.onProgress?.(0.95, "Mixing audio");
|
|
61884
|
-
const rootHtml = await
|
|
61979
|
+
const rootHtml = await readFile46(resolve64(projectDir, root), "utf-8");
|
|
61885
61980
|
const audios = await scanSceneAudio({ projectDir, rootHtml });
|
|
61886
61981
|
audioCount = audios.length;
|
|
61887
61982
|
if (audios.length > 0) {
|
|
@@ -61966,11 +62061,11 @@ async function safeStat(p) {
|
|
|
61966
62061
|
}
|
|
61967
62062
|
async function prepareBeatRenderRoot(opts) {
|
|
61968
62063
|
const root = `.vibeframe/tmp/render-beat-${sanitizeFileSegment2(opts.beatId)}.html`;
|
|
61969
|
-
const reportPath =
|
|
62064
|
+
const reportPath = join25(opts.projectDir, "build-report.json");
|
|
61970
62065
|
const retryWith = [
|
|
61971
62066
|
`vibe build ${opts.projectDir} --beat ${opts.beatId} --stage sync --json`
|
|
61972
62067
|
];
|
|
61973
|
-
if (!
|
|
62068
|
+
if (!existsSync49(reportPath)) {
|
|
61974
62069
|
return {
|
|
61975
62070
|
success: false,
|
|
61976
62071
|
root,
|
|
@@ -61981,7 +62076,7 @@ async function prepareBeatRenderRoot(opts) {
|
|
|
61981
62076
|
}
|
|
61982
62077
|
let report;
|
|
61983
62078
|
try {
|
|
61984
|
-
report = JSON.parse(await
|
|
62079
|
+
report = JSON.parse(await readFile46(reportPath, "utf-8"));
|
|
61985
62080
|
} catch (error) {
|
|
61986
62081
|
return {
|
|
61987
62082
|
success: false,
|
|
@@ -62050,8 +62145,8 @@ async function prepareBeatRenderRoot(opts) {
|
|
|
62050
62145
|
durationSec,
|
|
62051
62146
|
narrationPath
|
|
62052
62147
|
});
|
|
62053
|
-
await mkdir25(dirname23(
|
|
62054
|
-
await writeFile49(
|
|
62148
|
+
await mkdir25(dirname23(join25(opts.projectDir, root)), { recursive: true });
|
|
62149
|
+
await writeFile49(join25(opts.projectDir, root), html, "utf-8");
|
|
62055
62150
|
return {
|
|
62056
62151
|
success: true,
|
|
62057
62152
|
root,
|
|
@@ -62115,7 +62210,7 @@ function reportedBeatAssetPaths(beat) {
|
|
|
62115
62210
|
]);
|
|
62116
62211
|
}
|
|
62117
62212
|
async function writeRenderReport(projectDir, result) {
|
|
62118
|
-
const reportPath =
|
|
62213
|
+
const reportPath = join25(projectDir, "render-report.json");
|
|
62119
62214
|
try {
|
|
62120
62215
|
await writeFile49(
|
|
62121
62216
|
reportPath,
|
|
@@ -62157,7 +62252,7 @@ async function writeRenderReport(projectDir, result) {
|
|
|
62157
62252
|
}
|
|
62158
62253
|
function pathExists3(projectDir, value) {
|
|
62159
62254
|
const abs = isAbsolute6(value) ? value : resolve64(projectDir, value);
|
|
62160
|
-
return
|
|
62255
|
+
return existsSync49(abs);
|
|
62161
62256
|
}
|
|
62162
62257
|
function stringValue(value) {
|
|
62163
62258
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
@@ -62216,9 +62311,9 @@ __export(scene_build_exports, {
|
|
|
62216
62311
|
executeSceneBuild: () => executeSceneBuild,
|
|
62217
62312
|
resolveSceneBuildMode: () => resolveSceneBuildMode
|
|
62218
62313
|
});
|
|
62219
|
-
import { existsSync as
|
|
62220
|
-
import { copyFile as copyFile5, mkdir as mkdir26, readFile as
|
|
62221
|
-
import { dirname as dirname24, join as
|
|
62314
|
+
import { existsSync as existsSync50, readFileSync as readFileSync3 } from "node:fs";
|
|
62315
|
+
import { copyFile as copyFile5, mkdir as mkdir26, readFile as readFile47, rm as rm4, writeFile as writeFile50 } from "node:fs/promises";
|
|
62316
|
+
import { dirname as dirname24, join as join26, resolve as resolve65 } from "node:path";
|
|
62222
62317
|
async function executeSceneBuild(opts) {
|
|
62223
62318
|
const startedAt = Date.now();
|
|
62224
62319
|
const projectDir = resolve65(opts.projectDir);
|
|
@@ -62237,7 +62332,7 @@ async function executeSceneBuild(opts) {
|
|
|
62237
62332
|
);
|
|
62238
62333
|
}
|
|
62239
62334
|
let sceneRepair = skippedSceneRepairSummary();
|
|
62240
|
-
const storyboardPath2 =
|
|
62335
|
+
const storyboardPath2 = join26(projectDir, "STORYBOARD.md");
|
|
62241
62336
|
const buildPlan = await createBuildPlan({
|
|
62242
62337
|
projectDir,
|
|
62243
62338
|
stage: selectedStage,
|
|
@@ -62288,8 +62383,8 @@ async function executeSceneBuild(opts) {
|
|
|
62288
62383
|
}
|
|
62289
62384
|
if (!buildPlan.validation.ok) {
|
|
62290
62385
|
let invalidBeats = [];
|
|
62291
|
-
if (
|
|
62292
|
-
invalidBeats = parseStoryboard(await
|
|
62386
|
+
if (existsSync50(storyboardPath2)) {
|
|
62387
|
+
invalidBeats = parseStoryboard(await readFile47(storyboardPath2, "utf-8")).beats;
|
|
62293
62388
|
}
|
|
62294
62389
|
return finishBuildResult({
|
|
62295
62390
|
success: false,
|
|
@@ -62313,7 +62408,7 @@ async function executeSceneBuild(opts) {
|
|
|
62313
62408
|
totalLatencyMs: Date.now() - startedAt
|
|
62314
62409
|
});
|
|
62315
62410
|
}
|
|
62316
|
-
if (!
|
|
62411
|
+
if (!existsSync50(storyboardPath2)) {
|
|
62317
62412
|
return finishBuildResult({
|
|
62318
62413
|
success: false,
|
|
62319
62414
|
phase: "failed",
|
|
@@ -62327,7 +62422,7 @@ async function executeSceneBuild(opts) {
|
|
|
62327
62422
|
totalLatencyMs: Date.now() - startedAt
|
|
62328
62423
|
});
|
|
62329
62424
|
}
|
|
62330
|
-
const storyboardMd = await
|
|
62425
|
+
const storyboardMd = await readFile47(storyboardPath2, "utf-8");
|
|
62331
62426
|
const parsed = parseStoryboard(storyboardMd);
|
|
62332
62427
|
if (parsed.beats.length === 0) {
|
|
62333
62428
|
return finishBuildResult({
|
|
@@ -62434,6 +62529,7 @@ async function executeSceneBuild(opts) {
|
|
|
62434
62529
|
skipVideo: opts.skipVideo ?? false,
|
|
62435
62530
|
skipKeyframe: opts.skipKeyframe ?? false,
|
|
62436
62531
|
skipMusic: opts.skipMusic ?? false,
|
|
62532
|
+
skipTranscript: opts.skipTranscript ?? false,
|
|
62437
62533
|
force: opts.force ?? false,
|
|
62438
62534
|
onProgress,
|
|
62439
62535
|
characterPaths
|
|
@@ -62521,9 +62617,9 @@ async function executeSceneBuild(opts) {
|
|
|
62521
62617
|
let composeData;
|
|
62522
62618
|
if (shouldRunStage(selectedStage, "compose")) {
|
|
62523
62619
|
if (mode === "agent") {
|
|
62524
|
-
const compositionsDir =
|
|
62620
|
+
const compositionsDir = join26(projectDir, "compositions");
|
|
62525
62621
|
const missingBeats = activeBeats.filter(
|
|
62526
|
-
(b) => !
|
|
62622
|
+
(b) => !existsSync50(join26(compositionsDir, `scene-${b.id}.html`))
|
|
62527
62623
|
);
|
|
62528
62624
|
if (missingBeats.length > 0) {
|
|
62529
62625
|
const plan = await getComposePrompts({ projectDir, beatId: opts.beatId });
|
|
@@ -62612,8 +62708,8 @@ async function executeSceneBuild(opts) {
|
|
|
62612
62708
|
if (stageReports.compose.status === "done") {
|
|
62613
62709
|
for (const beat of activeBeats) {
|
|
62614
62710
|
const videoRel = `assets/video-${beat.id}.mp4`;
|
|
62615
|
-
const compositionAbs =
|
|
62616
|
-
if (!
|
|
62711
|
+
const compositionAbs = join26(projectDir, "compositions", `scene-${beat.id}.html`);
|
|
62712
|
+
if (!existsSync50(join26(projectDir, videoRel)) || !existsSync50(compositionAbs)) continue;
|
|
62617
62713
|
const html = readFileSync3(compositionAbs, "utf-8");
|
|
62618
62714
|
if (!html.includes(videoRel)) {
|
|
62619
62715
|
warnings.push(
|
|
@@ -62671,7 +62767,7 @@ async function executeSceneBuild(opts) {
|
|
|
62671
62767
|
});
|
|
62672
62768
|
}
|
|
62673
62769
|
if (shouldRunStage(selectedStage, "sync")) {
|
|
62674
|
-
if (!
|
|
62770
|
+
if (!existsSync50(join26(projectDir, "index.html"))) {
|
|
62675
62771
|
await scaffoldSceneProject({
|
|
62676
62772
|
dir: projectDir,
|
|
62677
62773
|
name: projectDir.split(/[\\/]/).filter(Boolean).pop(),
|
|
@@ -62816,6 +62912,9 @@ async function buildBeatPrimitives(beat, ctx) {
|
|
|
62816
62912
|
ctx.skipVideo ? skipped("video", beat.id, "--skip-video", ctx) : dispatchVideo(beat, ctx, keyframe),
|
|
62817
62913
|
ctx.skipMusic ? skipped("music", beat.id, "--skip-music", ctx) : dispatchMusic(beat, ctx)
|
|
62818
62914
|
]);
|
|
62915
|
+
if (!ctx.skipTranscript) {
|
|
62916
|
+
await dispatchTranscript(beat, narration, ctx);
|
|
62917
|
+
}
|
|
62819
62918
|
return {
|
|
62820
62919
|
outcome: {
|
|
62821
62920
|
beatId: beat.id,
|
|
@@ -62933,7 +63032,7 @@ async function dispatchNarration(beat, ctx) {
|
|
|
62933
63032
|
return {
|
|
62934
63033
|
status: "cached",
|
|
62935
63034
|
path: existingRel,
|
|
62936
|
-
durationSec: await safeAudioDuration(
|
|
63035
|
+
durationSec: await safeAudioDuration(join26(ctx.projectDir, existingRel)),
|
|
62937
63036
|
provider: metadata?.provider,
|
|
62938
63037
|
cachePath: metadata?.cachePath,
|
|
62939
63038
|
cacheKey: metadata?.cacheKey,
|
|
@@ -62957,9 +63056,9 @@ async function dispatchNarration(beat, ctx) {
|
|
|
62957
63056
|
voice,
|
|
62958
63057
|
ext: resolution.audioExtension
|
|
62959
63058
|
});
|
|
62960
|
-
const cacheAbs =
|
|
63059
|
+
const cacheAbs = join26(ctx.projectDir, cache.path);
|
|
62961
63060
|
const rel = `assets/narration-${beat.id}.${resolution.audioExtension}`;
|
|
62962
|
-
const abs =
|
|
63061
|
+
const abs = join26(ctx.projectDir, rel);
|
|
62963
63062
|
if (existingRel && !ctx.force && isFreshCanonicalAsset({
|
|
62964
63063
|
projectDir: ctx.projectDir,
|
|
62965
63064
|
kind: "narration",
|
|
@@ -62973,7 +63072,7 @@ async function dispatchNarration(beat, ctx) {
|
|
|
62973
63072
|
return {
|
|
62974
63073
|
status: "cached",
|
|
62975
63074
|
path: existingRel,
|
|
62976
|
-
durationSec: await safeAudioDuration(
|
|
63075
|
+
durationSec: await safeAudioDuration(join26(ctx.projectDir, existingRel)),
|
|
62977
63076
|
provider: resolution.provider,
|
|
62978
63077
|
cachePath: cache.path,
|
|
62979
63078
|
cacheKey: cache.key,
|
|
@@ -62981,7 +63080,7 @@ async function dispatchNarration(beat, ctx) {
|
|
|
62981
63080
|
freshness: "fresh"
|
|
62982
63081
|
};
|
|
62983
63082
|
}
|
|
62984
|
-
if (
|
|
63083
|
+
if (existsSync50(cacheAbs) && !ctx.force) {
|
|
62985
63084
|
await mkdir26(dirname24(abs), { recursive: true });
|
|
62986
63085
|
await copyFile5(cacheAbs, abs);
|
|
62987
63086
|
await writeAssetMetadata({
|
|
@@ -63016,8 +63115,8 @@ async function dispatchNarration(beat, ctx) {
|
|
|
63016
63115
|
await mkdir26(dirname24(abs), { recursive: true });
|
|
63017
63116
|
await writeFile50(abs, result.audioBuffer);
|
|
63018
63117
|
for (const otherExt of ["mp3", "wav"]) {
|
|
63019
|
-
const sibling =
|
|
63020
|
-
if (sibling !== abs &&
|
|
63118
|
+
const sibling = join26(ctx.projectDir, `assets/narration-${beat.id}.${otherExt}`);
|
|
63119
|
+
if (sibling !== abs && existsSync50(sibling)) await rm4(sibling, { force: true });
|
|
63021
63120
|
}
|
|
63022
63121
|
await mkdir26(dirname24(cacheAbs), { recursive: true });
|
|
63023
63122
|
await writeFile50(cacheAbs, result.audioBuffer);
|
|
@@ -63049,6 +63148,42 @@ async function dispatchNarration(beat, ctx) {
|
|
|
63049
63148
|
freshness: "fresh"
|
|
63050
63149
|
};
|
|
63051
63150
|
}
|
|
63151
|
+
async function dispatchTranscript(beat, narration, ctx) {
|
|
63152
|
+
if (!narration.path) return;
|
|
63153
|
+
const relPath = beatTranscriptRelPath(beat.id);
|
|
63154
|
+
const abs = join26(ctx.projectDir, relPath);
|
|
63155
|
+
const narrationFresh = narration.status === "generated";
|
|
63156
|
+
if (existsSync50(abs) && !ctx.force && !narrationFresh) {
|
|
63157
|
+
ctx.onProgress({ type: "transcript-cached", beatId: beat.id, path: relPath });
|
|
63158
|
+
return;
|
|
63159
|
+
}
|
|
63160
|
+
const apiKey = await getConfiguredApiKey("OPENAI_API_KEY", void 0, { cwd: ctx.projectDir });
|
|
63161
|
+
if (!apiKey) {
|
|
63162
|
+
ctx.onProgress({
|
|
63163
|
+
type: "transcript-skipped",
|
|
63164
|
+
beatId: beat.id,
|
|
63165
|
+
reason: "OPENAI_API_KEY not set \u2014 narration plays but word-sync timings unavailable"
|
|
63166
|
+
});
|
|
63167
|
+
return;
|
|
63168
|
+
}
|
|
63169
|
+
const words = await transcribeNarrationWords(join26(ctx.projectDir, narration.path), { apiKey });
|
|
63170
|
+
if (words.length === 0) {
|
|
63171
|
+
ctx.onProgress({
|
|
63172
|
+
type: "transcript-skipped",
|
|
63173
|
+
beatId: beat.id,
|
|
63174
|
+
reason: "transcription returned no word timings"
|
|
63175
|
+
});
|
|
63176
|
+
return;
|
|
63177
|
+
}
|
|
63178
|
+
await mkdir26(dirname24(abs), { recursive: true });
|
|
63179
|
+
await writeFile50(abs, JSON.stringify(words, null, 2), "utf-8");
|
|
63180
|
+
ctx.onProgress({
|
|
63181
|
+
type: "transcript-generated",
|
|
63182
|
+
beatId: beat.id,
|
|
63183
|
+
path: relPath,
|
|
63184
|
+
wordCount: words.length
|
|
63185
|
+
});
|
|
63186
|
+
}
|
|
63052
63187
|
function characterSheetPrompt(name, description) {
|
|
63053
63188
|
return [
|
|
63054
63189
|
`Character reference sheet of an original fictional character named ${name}: ${description}.`,
|
|
@@ -63066,7 +63201,7 @@ async function buildCharacters(characters, ctx) {
|
|
|
63066
63201
|
const size = ctx.imageSize ?? "1536x1024";
|
|
63067
63202
|
for (const character of characters) {
|
|
63068
63203
|
if (character.imagePath) {
|
|
63069
|
-
if (
|
|
63204
|
+
if (existsSync50(join26(ctx.projectDir, character.imagePath))) {
|
|
63070
63205
|
paths.set(character.name, character.imagePath);
|
|
63071
63206
|
ctx.onProgress({ type: "character-cached", name: character.name, path: character.imagePath });
|
|
63072
63207
|
} else {
|
|
@@ -63079,7 +63214,7 @@ async function buildCharacters(characters, ctx) {
|
|
|
63079
63214
|
if (!character.prompt) continue;
|
|
63080
63215
|
const prompt3 = characterSheetPrompt(character.name, character.prompt);
|
|
63081
63216
|
const rel = `assets/character-${character.name}.png`;
|
|
63082
|
-
const abs =
|
|
63217
|
+
const abs = join26(ctx.projectDir, rel);
|
|
63083
63218
|
const metadataOptions = { quality: ctx.imageQuality, size };
|
|
63084
63219
|
const cache = characterCacheDescriptor({
|
|
63085
63220
|
name: character.name,
|
|
@@ -63088,7 +63223,7 @@ async function buildCharacters(characters, ctx) {
|
|
|
63088
63223
|
quality: ctx.imageQuality,
|
|
63089
63224
|
size
|
|
63090
63225
|
});
|
|
63091
|
-
if (
|
|
63226
|
+
if (existsSync50(abs) && !ctx.force) {
|
|
63092
63227
|
const metadata = readAssetMetadata(ctx.projectDir, "character", character.name);
|
|
63093
63228
|
if (!metadata || isFreshCanonicalAsset({
|
|
63094
63229
|
projectDir: ctx.projectDir,
|
|
@@ -63104,8 +63239,8 @@ async function buildCharacters(characters, ctx) {
|
|
|
63104
63239
|
continue;
|
|
63105
63240
|
}
|
|
63106
63241
|
}
|
|
63107
|
-
const cacheAbs =
|
|
63108
|
-
if (
|
|
63242
|
+
const cacheAbs = join26(ctx.projectDir, cache.path);
|
|
63243
|
+
if (existsSync50(cacheAbs) && !ctx.force) {
|
|
63109
63244
|
await mkdir26(dirname24(abs), { recursive: true });
|
|
63110
63245
|
await copyFile5(cacheAbs, abs);
|
|
63111
63246
|
await writeAssetMetadata({
|
|
@@ -63171,7 +63306,7 @@ async function dispatchBackdrop(beat, ctx) {
|
|
|
63171
63306
|
if (!cue) return { status: "no-cue" };
|
|
63172
63307
|
const prompt3 = augmentBackdropPrompt(cue);
|
|
63173
63308
|
const rel = `assets/backdrop-${beat.id}.png`;
|
|
63174
|
-
const abs =
|
|
63309
|
+
const abs = join26(ctx.projectDir, rel);
|
|
63175
63310
|
const size = ctx.imageSize ?? "1536x1024";
|
|
63176
63311
|
const ratio = imageRatioForSize(size);
|
|
63177
63312
|
const metadataOptions = { quality: ctx.imageQuality, size, ratio };
|
|
@@ -63184,7 +63319,7 @@ async function dispatchBackdrop(beat, ctx) {
|
|
|
63184
63319
|
ratio
|
|
63185
63320
|
});
|
|
63186
63321
|
const metadataPath = assetMetadataPath("backdrop", beat.id);
|
|
63187
|
-
if (
|
|
63322
|
+
if (existsSync50(abs) && !ctx.force) {
|
|
63188
63323
|
const metadata = readAssetMetadata(ctx.projectDir, "backdrop", beat.id);
|
|
63189
63324
|
if (!metadata || isFreshCanonicalAsset({
|
|
63190
63325
|
projectDir: ctx.projectDir,
|
|
@@ -63207,8 +63342,8 @@ async function dispatchBackdrop(beat, ctx) {
|
|
|
63207
63342
|
};
|
|
63208
63343
|
}
|
|
63209
63344
|
}
|
|
63210
|
-
const cacheAbs =
|
|
63211
|
-
if (
|
|
63345
|
+
const cacheAbs = join26(ctx.projectDir, cache.path);
|
|
63346
|
+
if (existsSync50(cacheAbs) && !ctx.force) {
|
|
63212
63347
|
await mkdir26(dirname24(abs), { recursive: true });
|
|
63213
63348
|
await copyFile5(cacheAbs, abs);
|
|
63214
63349
|
await writeAssetMetadata({
|
|
@@ -63340,7 +63475,7 @@ async function editKeyframeImage(prompt3, sheetBuffers, ctx, ratio) {
|
|
|
63340
63475
|
}
|
|
63341
63476
|
async function ensureKeyframe(beat, ctx, keyframeCue, characterRefsRel) {
|
|
63342
63477
|
const rel = `assets/keyframe-${beat.id}.png`;
|
|
63343
|
-
const abs =
|
|
63478
|
+
const abs = join26(ctx.projectDir, rel);
|
|
63344
63479
|
const size = ctx.imageSize ?? "1536x1024";
|
|
63345
63480
|
const ratio = imageRatioForSize(size);
|
|
63346
63481
|
const metadataOptions = { quality: ctx.imageQuality, size, ratio };
|
|
@@ -63353,7 +63488,7 @@ async function ensureKeyframe(beat, ctx, keyframeCue, characterRefsRel) {
|
|
|
63353
63488
|
ratio,
|
|
63354
63489
|
characters: characterRefsRel
|
|
63355
63490
|
});
|
|
63356
|
-
if (
|
|
63491
|
+
if (existsSync50(abs) && !ctx.force) {
|
|
63357
63492
|
const metadata = readAssetMetadata(ctx.projectDir, "keyframe", beat.id);
|
|
63358
63493
|
if (!metadata || isFreshCanonicalAsset({
|
|
63359
63494
|
projectDir: ctx.projectDir,
|
|
@@ -63367,8 +63502,8 @@ async function ensureKeyframe(beat, ctx, keyframeCue, characterRefsRel) {
|
|
|
63367
63502
|
return { status: "cached", rel };
|
|
63368
63503
|
}
|
|
63369
63504
|
}
|
|
63370
|
-
const cacheAbs =
|
|
63371
|
-
if (
|
|
63505
|
+
const cacheAbs = join26(ctx.projectDir, cache.path);
|
|
63506
|
+
if (existsSync50(cacheAbs) && !ctx.force) {
|
|
63372
63507
|
await mkdir26(dirname24(abs), { recursive: true });
|
|
63373
63508
|
await copyFile5(cacheAbs, abs);
|
|
63374
63509
|
await writeAssetMetadata({
|
|
@@ -63386,8 +63521,8 @@ async function ensureKeyframe(beat, ctx, keyframeCue, characterRefsRel) {
|
|
|
63386
63521
|
}
|
|
63387
63522
|
const sheetBuffers = [];
|
|
63388
63523
|
for (const relPath of characterRefsRel) {
|
|
63389
|
-
const sheetAbs =
|
|
63390
|
-
if (
|
|
63524
|
+
const sheetAbs = join26(ctx.projectDir, relPath);
|
|
63525
|
+
if (existsSync50(sheetAbs)) sheetBuffers.push(await readFile47(sheetAbs));
|
|
63391
63526
|
}
|
|
63392
63527
|
const imgCtx = {
|
|
63393
63528
|
projectDir: ctx.projectDir,
|
|
@@ -63456,9 +63591,9 @@ async function dispatchVideo(beat, ctx, keyframe) {
|
|
|
63456
63591
|
const prompt3 = stringOrUndefined4(beat.cues?.video) ?? keyframeCue;
|
|
63457
63592
|
if (!prompt3) return { status: "no-cue" };
|
|
63458
63593
|
const characterRefsRel = beatCharacterNames(beat.cues).map((name) => ctx.characterPaths?.get(name)).filter((p) => typeof p === "string");
|
|
63459
|
-
const characterRefsAbs = characterRefsRel.map((p) =>
|
|
63594
|
+
const characterRefsAbs = characterRefsRel.map((p) => join26(ctx.projectDir, p));
|
|
63460
63595
|
const rel = `assets/video-${beat.id}.mp4`;
|
|
63461
|
-
const abs =
|
|
63596
|
+
const abs = join26(ctx.projectDir, rel);
|
|
63462
63597
|
const cache = videoCacheDescriptor({
|
|
63463
63598
|
beatId: beat.id,
|
|
63464
63599
|
cue: prompt3,
|
|
@@ -63470,7 +63605,7 @@ async function dispatchVideo(beat, ctx, keyframe) {
|
|
|
63470
63605
|
keyframe: keyframeCue
|
|
63471
63606
|
});
|
|
63472
63607
|
const metadataPath = assetMetadataPath("video", beat.id);
|
|
63473
|
-
if (
|
|
63608
|
+
if (existsSync50(abs) && !ctx.force) {
|
|
63474
63609
|
const metadata = readAssetMetadata(ctx.projectDir, "video", beat.id);
|
|
63475
63610
|
if (!metadata || isFreshCanonicalAsset({
|
|
63476
63611
|
projectDir: ctx.projectDir,
|
|
@@ -63493,8 +63628,8 @@ async function dispatchVideo(beat, ctx, keyframe) {
|
|
|
63493
63628
|
};
|
|
63494
63629
|
}
|
|
63495
63630
|
}
|
|
63496
|
-
const cacheAbs =
|
|
63497
|
-
if (
|
|
63631
|
+
const cacheAbs = join26(ctx.projectDir, cache.path);
|
|
63632
|
+
if (existsSync50(cacheAbs) && !ctx.force) {
|
|
63498
63633
|
await mkdir26(dirname24(abs), { recursive: true });
|
|
63499
63634
|
await copyFile5(cacheAbs, abs);
|
|
63500
63635
|
await writeAssetMetadata({
|
|
@@ -63525,7 +63660,7 @@ async function dispatchVideo(beat, ctx, keyframe) {
|
|
|
63525
63660
|
ctx.onProgress({ type: "video-skipped", beatId: beat.id, reason });
|
|
63526
63661
|
return { status: "skipped" };
|
|
63527
63662
|
}
|
|
63528
|
-
const keyframeImageAbs = keyframeImageRel ?
|
|
63663
|
+
const keyframeImageAbs = keyframeImageRel ? join26(ctx.projectDir, keyframeImageRel) : void 0;
|
|
63529
63664
|
loadSceneBuildEnv(ctx.projectDir);
|
|
63530
63665
|
const result = await executeVideoGenerate({
|
|
63531
63666
|
prompt: prompt3,
|
|
@@ -63549,7 +63684,7 @@ async function dispatchVideo(beat, ctx, keyframe) {
|
|
|
63549
63684
|
ctx.onProgress({ type: "video-failed", beatId: beat.id, error });
|
|
63550
63685
|
return { status: "failed", error };
|
|
63551
63686
|
}
|
|
63552
|
-
if (result.status === "completed" &&
|
|
63687
|
+
if (result.status === "completed" && existsSync50(abs)) {
|
|
63553
63688
|
await mkdir26(dirname24(cacheAbs), { recursive: true });
|
|
63554
63689
|
await copyFile5(abs, cacheAbs);
|
|
63555
63690
|
await writeAssetMetadata({
|
|
@@ -63617,7 +63752,7 @@ async function dispatchMusic(beat, ctx) {
|
|
|
63617
63752
|
const prompt3 = stringOrUndefined4(beat.cues?.music);
|
|
63618
63753
|
if (!prompt3) return { status: "no-cue" };
|
|
63619
63754
|
const rel = `assets/music-${beat.id}.mp3`;
|
|
63620
|
-
const abs =
|
|
63755
|
+
const abs = join26(ctx.projectDir, rel);
|
|
63621
63756
|
const duration = normalizeMusicDuration(beat.duration);
|
|
63622
63757
|
const cache = musicCacheDescriptor({
|
|
63623
63758
|
beatId: beat.id,
|
|
@@ -63626,7 +63761,7 @@ async function dispatchMusic(beat, ctx) {
|
|
|
63626
63761
|
duration
|
|
63627
63762
|
});
|
|
63628
63763
|
const metadataPath = assetMetadataPath("music", beat.id);
|
|
63629
|
-
if (
|
|
63764
|
+
if (existsSync50(abs) && !ctx.force) {
|
|
63630
63765
|
const metadata = readAssetMetadata(ctx.projectDir, "music", beat.id);
|
|
63631
63766
|
if (!metadata || isFreshCanonicalAsset({
|
|
63632
63767
|
projectDir: ctx.projectDir,
|
|
@@ -63650,8 +63785,8 @@ async function dispatchMusic(beat, ctx) {
|
|
|
63650
63785
|
};
|
|
63651
63786
|
}
|
|
63652
63787
|
}
|
|
63653
|
-
const cacheAbs =
|
|
63654
|
-
if (
|
|
63788
|
+
const cacheAbs = join26(ctx.projectDir, cache.path);
|
|
63789
|
+
if (existsSync50(cacheAbs) && !ctx.force) {
|
|
63655
63790
|
await mkdir26(dirname24(abs), { recursive: true });
|
|
63656
63791
|
await copyFile5(cacheAbs, abs);
|
|
63657
63792
|
await writeAssetMetadata({
|
|
@@ -63725,7 +63860,7 @@ async function dispatchMusic(beat, ctx) {
|
|
|
63725
63860
|
metadataPath
|
|
63726
63861
|
};
|
|
63727
63862
|
}
|
|
63728
|
-
if (!
|
|
63863
|
+
if (!existsSync50(abs)) {
|
|
63729
63864
|
const error = result.outputPath ? `music output was not written at ${abs}` : "music generation did not return an output file";
|
|
63730
63865
|
ctx.onProgress({ type: "music-failed", beatId: beat.id, error });
|
|
63731
63866
|
return { status: "failed", error };
|
|
@@ -63761,12 +63896,12 @@ async function dispatchMusic(beat, ctx) {
|
|
|
63761
63896
|
};
|
|
63762
63897
|
}
|
|
63763
63898
|
function loadSceneBuildEnv(projectDir) {
|
|
63764
|
-
(0, import_dotenv4.config)({ path:
|
|
63899
|
+
(0, import_dotenv4.config)({ path: join26(projectDir, ".env"), quiet: true });
|
|
63765
63900
|
(0, import_dotenv4.config)({ path: resolve65(process.cwd(), ".env"), quiet: true });
|
|
63766
63901
|
let dir = process.cwd();
|
|
63767
63902
|
while (dir !== dirname24(dir)) {
|
|
63768
|
-
if (
|
|
63769
|
-
(0, import_dotenv4.config)({ path:
|
|
63903
|
+
if (existsSync50(join26(dir, "pnpm-workspace.yaml"))) {
|
|
63904
|
+
(0, import_dotenv4.config)({ path: join26(dir, ".env"), quiet: true });
|
|
63770
63905
|
return;
|
|
63771
63906
|
}
|
|
63772
63907
|
dir = dirname24(dir);
|
|
@@ -63982,7 +64117,7 @@ function mergeBeatOutcomes(base, updates) {
|
|
|
63982
64117
|
}
|
|
63983
64118
|
function firstExisting3(projectDir, relPaths) {
|
|
63984
64119
|
for (const rel of relPaths) {
|
|
63985
|
-
if (
|
|
64120
|
+
if (existsSync50(join26(projectDir, rel))) return rel;
|
|
63986
64121
|
}
|
|
63987
64122
|
return null;
|
|
63988
64123
|
}
|
|
@@ -64013,7 +64148,7 @@ async function apiKeyForVideoProvider(provider, projectDir) {
|
|
|
64013
64148
|
return getApiKeyFromConfig(providerKey, { cwd: projectDir });
|
|
64014
64149
|
}
|
|
64015
64150
|
async function finalizeBuildResult(projectDir, startedAt, result) {
|
|
64016
|
-
const reportPath =
|
|
64151
|
+
const reportPath = join26(projectDir, "build-report.json");
|
|
64017
64152
|
if (result.stageReports) {
|
|
64018
64153
|
for (const report of Object.values(result.stageReports)) {
|
|
64019
64154
|
if (report.status === "pending") report.status = "skipped";
|
|
@@ -64172,7 +64307,7 @@ function normalizeReportDuration(value) {
|
|
|
64172
64307
|
}
|
|
64173
64308
|
function buildReportComposition(projectDir, result, beatId) {
|
|
64174
64309
|
const path2 = `compositions/scene-${beatId}.html`;
|
|
64175
|
-
const exists =
|
|
64310
|
+
const exists = existsSync50(join26(projectDir, path2));
|
|
64176
64311
|
const written = composeWrittenEntry(result, beatId);
|
|
64177
64312
|
if (written) {
|
|
64178
64313
|
return {
|
|
@@ -64229,8 +64364,8 @@ function summarizeBuildBeats(projectDir, beats) {
|
|
|
64229
64364
|
let compositionsReady = 0;
|
|
64230
64365
|
for (const beat of beats) {
|
|
64231
64366
|
if (beatAssetsReady(beat)) assetsReady += 1;
|
|
64232
|
-
const compositionPath =
|
|
64233
|
-
if (
|
|
64367
|
+
const compositionPath = join26(projectDir, "compositions", `scene-${beat.beatId}.html`);
|
|
64368
|
+
if (existsSync50(compositionPath)) {
|
|
64234
64369
|
compositionsReady += 1;
|
|
64235
64370
|
} else {
|
|
64236
64371
|
needsAuthor.push(beat.beatId);
|
|
@@ -64272,6 +64407,8 @@ var init_scene_build = __esm({
|
|
|
64272
64407
|
init_compose_scenes_skills();
|
|
64273
64408
|
init_compose_prompts();
|
|
64274
64409
|
init_config();
|
|
64410
|
+
init_api_key();
|
|
64411
|
+
init_transcribe_narration();
|
|
64275
64412
|
init_scene_render();
|
|
64276
64413
|
init_storyboard_parse();
|
|
64277
64414
|
init_scene_project();
|
|
@@ -64362,18 +64499,18 @@ __export(ai_audio_exports, {
|
|
|
64362
64499
|
executeVoiceClone: () => executeVoiceClone
|
|
64363
64500
|
});
|
|
64364
64501
|
import { resolve as resolve73, dirname as dirname26, basename as basename22, extname as extname15 } from "node:path";
|
|
64365
|
-
import { readFile as
|
|
64366
|
-
import { existsSync as
|
|
64502
|
+
import { readFile as readFile49, writeFile as writeFile51 } from "node:fs/promises";
|
|
64503
|
+
import { existsSync as existsSync55 } from "node:fs";
|
|
64367
64504
|
async function executeTranscribe(options) {
|
|
64368
64505
|
const { audioPath, language, output, format, apiKey } = options;
|
|
64369
64506
|
try {
|
|
64370
64507
|
const key = apiKey || process.env.OPENAI_API_KEY;
|
|
64371
64508
|
if (!key) return { success: false, error: "OPENAI_API_KEY required" };
|
|
64372
64509
|
const absPath = resolve73(process.cwd(), audioPath);
|
|
64373
|
-
if (!
|
|
64510
|
+
if (!existsSync55(absPath)) return { success: false, error: `File not found: ${absPath}` };
|
|
64374
64511
|
const whisper = new WhisperProvider();
|
|
64375
64512
|
await whisper.initialize({ apiKey: key });
|
|
64376
|
-
const audioBuffer = await
|
|
64513
|
+
const audioBuffer = await readFile49(absPath);
|
|
64377
64514
|
const audioBlob = new Blob([audioBuffer]);
|
|
64378
64515
|
const result = await whisper.transcribe(audioBlob, language);
|
|
64379
64516
|
if (result.status === "failed") {
|
|
@@ -64403,8 +64540,8 @@ async function executeIsolate(options) {
|
|
|
64403
64540
|
const key = apiKey || process.env.ELEVENLABS_API_KEY;
|
|
64404
64541
|
if (!key) return { success: false, error: "ELEVENLABS_API_KEY required" };
|
|
64405
64542
|
const absPath = resolve73(process.cwd(), audioPath);
|
|
64406
|
-
if (!
|
|
64407
|
-
const audioBuffer = await
|
|
64543
|
+
if (!existsSync55(absPath)) return { success: false, error: `File not found: ${absPath}` };
|
|
64544
|
+
const audioBuffer = await readFile49(absPath);
|
|
64408
64545
|
const elevenlabs = new ElevenLabsProvider();
|
|
64409
64546
|
await elevenlabs.initialize({ apiKey: key });
|
|
64410
64547
|
const result = await elevenlabs.isolateVocals(audioBuffer);
|
|
@@ -64429,8 +64566,8 @@ async function executeVoiceClone(options) {
|
|
|
64429
64566
|
const audioBuffers = [];
|
|
64430
64567
|
for (const samplePath of samplePaths) {
|
|
64431
64568
|
const absPath = resolve73(process.cwd(), samplePath);
|
|
64432
|
-
if (!
|
|
64433
|
-
const buffer = await
|
|
64569
|
+
if (!existsSync55(absPath)) return { success: false, error: `File not found: ${samplePath}` };
|
|
64570
|
+
const buffer = await readFile49(absPath);
|
|
64434
64571
|
audioBuffers.push(buffer);
|
|
64435
64572
|
}
|
|
64436
64573
|
const elevenlabs = new ElevenLabsProvider();
|
|
@@ -64451,7 +64588,7 @@ async function executeDub(options) {
|
|
|
64451
64588
|
const { mediaPath, language, source, voice, analyzeOnly, output } = options;
|
|
64452
64589
|
try {
|
|
64453
64590
|
const absPath = resolve73(process.cwd(), mediaPath);
|
|
64454
|
-
if (!
|
|
64591
|
+
if (!existsSync55(absPath)) return { success: false, error: `File not found: ${absPath}` };
|
|
64455
64592
|
const openaiKey = process.env.OPENAI_API_KEY;
|
|
64456
64593
|
const anthropicKey = process.env.ANTHROPIC_API_KEY;
|
|
64457
64594
|
const elevenlabsKey = process.env.ELEVENLABS_API_KEY;
|
|
@@ -64468,7 +64605,7 @@ async function executeDub(options) {
|
|
|
64468
64605
|
}
|
|
64469
64606
|
const whisper = new WhisperProvider();
|
|
64470
64607
|
await whisper.initialize({ apiKey: openaiKey });
|
|
64471
|
-
const audioBuffer = await
|
|
64608
|
+
const audioBuffer = await readFile49(audioPath);
|
|
64472
64609
|
const audioBlob = new Blob([audioBuffer]);
|
|
64473
64610
|
const transcriptResult = await whisper.transcribe(audioBlob, source);
|
|
64474
64611
|
if (transcriptResult.status === "failed" || !transcriptResult.segments) {
|
|
@@ -64579,8 +64716,8 @@ async function executeDuck(options) {
|
|
|
64579
64716
|
if (!commandExists("ffmpeg")) return { success: false, error: "FFmpeg not found" };
|
|
64580
64717
|
const absMusicPath = resolve73(process.cwd(), musicPath);
|
|
64581
64718
|
const absVoicePath = resolve73(process.cwd(), voicePath);
|
|
64582
|
-
if (!
|
|
64583
|
-
if (!
|
|
64719
|
+
if (!existsSync55(absMusicPath)) return { success: false, error: `Music file not found: ${absMusicPath}` };
|
|
64720
|
+
if (!existsSync55(absVoicePath)) return { success: false, error: `Voice file not found: ${absVoicePath}` };
|
|
64584
64721
|
const defaultOutput = resolve73(dirname26(absMusicPath), `${basename22(absMusicPath, extname15(absMusicPath))}-ducked${extname15(absMusicPath)}`);
|
|
64585
64722
|
const outputPath = resolve73(process.cwd(), output || defaultOutput);
|
|
64586
64723
|
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`;
|
|
@@ -64594,7 +64731,7 @@ async function executeDuck(options) {
|
|
|
64594
64731
|
"-y",
|
|
64595
64732
|
outputPath
|
|
64596
64733
|
], { timeout: 12e4, maxBuffer: 50 * 1024 * 1024 });
|
|
64597
|
-
if (!
|
|
64734
|
+
if (!existsSync55(outputPath)) return { success: false, error: "FFmpeg failed to create output" };
|
|
64598
64735
|
return { success: true, outputPath };
|
|
64599
64736
|
} catch (error) {
|
|
64600
64737
|
return { success: false, error: `Audio ducking failed: ${error instanceof Error ? error.message : String(error)}` };
|
|
@@ -64611,8 +64748,8 @@ var init_ai_audio = __esm({
|
|
|
64611
64748
|
|
|
64612
64749
|
// src/pipeline/executor.ts
|
|
64613
64750
|
import { resolve as resolve74 } from "node:path";
|
|
64614
|
-
import { readFile as
|
|
64615
|
-
import { existsSync as
|
|
64751
|
+
import { readFile as readFile50, writeFile as writeFile52, mkdir as mkdir28 } from "node:fs/promises";
|
|
64752
|
+
import { existsSync as existsSync56 } from "node:fs";
|
|
64616
64753
|
function maxCostFor(action) {
|
|
64617
64754
|
const cmd = ACTION_METADATA[action]?.command;
|
|
64618
64755
|
if (!cmd) return 0;
|
|
@@ -64927,10 +65064,10 @@ async function ensureActionsRegistered() {
|
|
|
64927
65064
|
}
|
|
64928
65065
|
async function loadPipeline(filePath) {
|
|
64929
65066
|
const absPath = resolve74(process.cwd(), filePath);
|
|
64930
|
-
if (!
|
|
65067
|
+
if (!existsSync56(absPath)) {
|
|
64931
65068
|
throw new Error(`Pipeline file not found: ${absPath}`);
|
|
64932
65069
|
}
|
|
64933
|
-
const content = await
|
|
65070
|
+
const content = await readFile50(absPath, "utf-8");
|
|
64934
65071
|
const manifest2 = (0, import_yaml8.parse)(content);
|
|
64935
65072
|
if (!manifest2.name) throw new Error("Pipeline missing 'name' field");
|
|
64936
65073
|
if (!manifest2.steps || !Array.isArray(manifest2.steps)) throw new Error("Pipeline missing 'steps' array");
|
|
@@ -64952,8 +65089,8 @@ async function executePipeline(manifest2, options = {}) {
|
|
|
64952
65089
|
const startTime = Date.now();
|
|
64953
65090
|
if (options.resume) {
|
|
64954
65091
|
const checkpointPath = resolve74(outputDir, CHECKPOINT_FILE);
|
|
64955
|
-
if (
|
|
64956
|
-
const checkpointContent = await
|
|
65092
|
+
if (existsSync56(checkpointPath)) {
|
|
65093
|
+
const checkpointContent = await readFile50(checkpointPath, "utf-8");
|
|
64957
65094
|
const checkpoint = (0, import_yaml8.parse)(checkpointContent);
|
|
64958
65095
|
for (const cs of checkpoint.completedSteps) {
|
|
64959
65096
|
completedSteps.set(cs.id, {
|
|
@@ -70883,14 +71020,14 @@ var init_edit = __esm({
|
|
|
70883
71020
|
});
|
|
70884
71021
|
|
|
70885
71022
|
// src/commands/ai-suggest-edit.ts
|
|
70886
|
-
import { readFile as
|
|
71023
|
+
import { readFile as readFile51, writeFile as writeFile53 } from "node:fs/promises";
|
|
70887
71024
|
import { resolve as resolve76 } from "node:path";
|
|
70888
71025
|
async function executeSuggestEdit(options) {
|
|
70889
71026
|
try {
|
|
70890
71027
|
const apiKey = options.apiKey ?? process.env.GOOGLE_API_KEY;
|
|
70891
71028
|
if (!apiKey) return { success: false, error: "GOOGLE_API_KEY required for suggest" };
|
|
70892
71029
|
const filePath = resolve76(process.cwd(), options.projectPath);
|
|
70893
|
-
const content = await
|
|
71030
|
+
const content = await readFile51(filePath, "utf-8");
|
|
70894
71031
|
const data = JSON.parse(content);
|
|
70895
71032
|
const project = Project.fromJSON(data);
|
|
70896
71033
|
const gemini = new GeminiProvider();
|
|
@@ -71655,13 +71792,13 @@ var init_generate2 = __esm({
|
|
|
71655
71792
|
});
|
|
71656
71793
|
|
|
71657
71794
|
// src/tools/manifest/storyboard.ts
|
|
71658
|
-
import { existsSync as
|
|
71659
|
-
import { readFile as
|
|
71795
|
+
import { existsSync as existsSync57 } from "node:fs";
|
|
71796
|
+
import { readFile as readFile52, writeFile as writeFile54 } from "node:fs/promises";
|
|
71660
71797
|
import { resolve as resolve78 } from "node:path";
|
|
71661
71798
|
async function readStoryboard2(projectDir) {
|
|
71662
71799
|
const path2 = resolve78(projectDir, "STORYBOARD.md");
|
|
71663
|
-
if (!
|
|
71664
|
-
return { path: path2, markdown: await
|
|
71800
|
+
if (!existsSync57(path2)) return null;
|
|
71801
|
+
return { path: path2, markdown: await readFile52(path2, "utf-8") };
|
|
71665
71802
|
}
|
|
71666
71803
|
var PROJECT_DIR_DESCRIPTION3, projectDirSchema, storyboardListTool, storyboardValidateTool, storyboardGetTool, storyboardSetTool, storyboardMoveTool, storyboardReviseTool, planTool, storyboardTools;
|
|
71667
71804
|
var init_storyboard2 = __esm({
|
|
@@ -71936,7 +72073,7 @@ var init_storyboard2 = __esm({
|
|
|
71936
72073
|
// src/tools/manifest/remix.ts
|
|
71937
72074
|
import { writeFile as writeFile55 } from "node:fs/promises";
|
|
71938
72075
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
71939
|
-
import { join as
|
|
72076
|
+
import { join as join28 } from "node:path";
|
|
71940
72077
|
var pipelineHighlightsTool, pipelineAutoShortsTool, pipelineRunTool, pipelineRegenerateSceneTool, remixTools;
|
|
71941
72078
|
var init_remix = __esm({
|
|
71942
72079
|
"src/tools/manifest/remix.ts"() {
|
|
@@ -72050,7 +72187,7 @@ var init_remix = __esm({
|
|
|
72050
72187
|
let resolvedPath = args.pipelinePath;
|
|
72051
72188
|
let tempPath;
|
|
72052
72189
|
if (args.pipelineYaml) {
|
|
72053
|
-
tempPath =
|
|
72190
|
+
tempPath = join28(tmpdir4(), `vibe-mcp-pipeline-${Date.now()}.yaml`);
|
|
72054
72191
|
await writeFile55(tempPath, args.pipelineYaml, "utf-8");
|
|
72055
72192
|
resolvedPath = tempPath;
|
|
72056
72193
|
}
|
|
@@ -72226,10 +72363,10 @@ var init_detect2 = __esm({
|
|
|
72226
72363
|
});
|
|
72227
72364
|
|
|
72228
72365
|
// src/tools/manifest/_project-io.ts
|
|
72229
|
-
import { readFile as
|
|
72366
|
+
import { readFile as readFile53, writeFile as writeFile56 } from "node:fs/promises";
|
|
72230
72367
|
async function loadProject2(projectPath, cwd) {
|
|
72231
72368
|
const absPath = await resolveTimelineFile(projectPath, cwd);
|
|
72232
|
-
const content = await
|
|
72369
|
+
const content = await readFile53(absPath, "utf-8");
|
|
72233
72370
|
const data = JSON.parse(content);
|
|
72234
72371
|
return { project: Project.fromJSON(data), absPath };
|
|
72235
72372
|
}
|
|
@@ -72593,12 +72730,12 @@ var init_timeline = __esm({
|
|
|
72593
72730
|
});
|
|
72594
72731
|
|
|
72595
72732
|
// src/commands/_shared/project-list.ts
|
|
72596
|
-
import { readdir as readdir7, readFile as
|
|
72597
|
-
import { existsSync as
|
|
72598
|
-
import { join as
|
|
72733
|
+
import { readdir as readdir7, readFile as readFile54, stat as stat9 } from "node:fs/promises";
|
|
72734
|
+
import { existsSync as existsSync58 } from "node:fs";
|
|
72735
|
+
import { join as join29, resolve as resolve80 } from "node:path";
|
|
72599
72736
|
async function executeProjectList(opts = {}) {
|
|
72600
72737
|
const workspaceDir = resolve80(opts.workspaceDir ?? ".");
|
|
72601
|
-
if (!
|
|
72738
|
+
if (!existsSync58(workspaceDir)) {
|
|
72602
72739
|
return {
|
|
72603
72740
|
success: false,
|
|
72604
72741
|
workspaceDir,
|
|
@@ -72615,11 +72752,11 @@ async function executeProjectList(opts = {}) {
|
|
|
72615
72752
|
if (entry.name === "node_modules") continue;
|
|
72616
72753
|
if (entry.name.startsWith(".")) continue;
|
|
72617
72754
|
if (entry.name.startsWith("_")) {
|
|
72618
|
-
archivedCount += await countProjects(
|
|
72755
|
+
archivedCount += await countProjects(join29(workspaceDir, entry.name));
|
|
72619
72756
|
continue;
|
|
72620
72757
|
}
|
|
72621
|
-
const projectDir =
|
|
72622
|
-
if (!
|
|
72758
|
+
const projectDir = join29(workspaceDir, entry.name);
|
|
72759
|
+
if (!existsSync58(join29(projectDir, "STORYBOARD.md"))) continue;
|
|
72623
72760
|
projects.push(await describeProject(entry.name, projectDir));
|
|
72624
72761
|
}
|
|
72625
72762
|
projects.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
@@ -72629,7 +72766,7 @@ async function countProjects(dir) {
|
|
|
72629
72766
|
try {
|
|
72630
72767
|
const entries = await readdir7(dir, { withFileTypes: true });
|
|
72631
72768
|
return entries.filter(
|
|
72632
|
-
(e) => e.isDirectory() &&
|
|
72769
|
+
(e) => e.isDirectory() && existsSync58(join29(dir, e.name, "STORYBOARD.md"))
|
|
72633
72770
|
).length;
|
|
72634
72771
|
} catch {
|
|
72635
72772
|
return 0;
|
|
@@ -72640,8 +72777,8 @@ async function describeProject(name, projectDir) {
|
|
|
72640
72777
|
let storyboardDurationSec = 0;
|
|
72641
72778
|
const timestamps = [];
|
|
72642
72779
|
try {
|
|
72643
|
-
const storyboardPath2 =
|
|
72644
|
-
const parsed = parseStoryboard(await
|
|
72780
|
+
const storyboardPath2 = join29(projectDir, "STORYBOARD.md");
|
|
72781
|
+
const parsed = parseStoryboard(await readFile54(storyboardPath2, "utf-8"));
|
|
72645
72782
|
beats = parsed.beats.length;
|
|
72646
72783
|
storyboardDurationSec = Number(
|
|
72647
72784
|
parsed.beats.reduce((sum, beat) => sum + (beat.duration ?? 0), 0).toFixed(2)
|
|
@@ -72652,8 +72789,8 @@ async function describeProject(name, projectDir) {
|
|
|
72652
72789
|
let buildStatus;
|
|
72653
72790
|
let buildUpdatedAt;
|
|
72654
72791
|
try {
|
|
72655
|
-
const reportPath =
|
|
72656
|
-
const report = JSON.parse(await
|
|
72792
|
+
const reportPath = join29(projectDir, "build-report.json");
|
|
72793
|
+
const report = JSON.parse(await readFile54(reportPath, "utf-8"));
|
|
72657
72794
|
buildStatus = report.status ?? report.phase;
|
|
72658
72795
|
buildUpdatedAt = report.updatedAt;
|
|
72659
72796
|
timestamps.push((await stat9(reportPath)).mtimeMs);
|
|
@@ -72661,12 +72798,12 @@ async function describeProject(name, projectDir) {
|
|
|
72661
72798
|
}
|
|
72662
72799
|
let latestRender;
|
|
72663
72800
|
try {
|
|
72664
|
-
const rendersDir =
|
|
72801
|
+
const rendersDir = join29(projectDir, "renders");
|
|
72665
72802
|
const files = await readdir7(rendersDir);
|
|
72666
72803
|
let newest;
|
|
72667
72804
|
for (const file of files) {
|
|
72668
72805
|
if (!/\.(mp4|webm|mov)$/i.test(file)) continue;
|
|
72669
|
-
const { mtimeMs } = await stat9(
|
|
72806
|
+
const { mtimeMs } = await stat9(join29(rendersDir, file));
|
|
72670
72807
|
if (!newest || mtimeMs > newest.mtimeMs) newest = { file, mtimeMs };
|
|
72671
72808
|
}
|
|
72672
72809
|
if (newest) {
|
|
@@ -72808,7 +72945,7 @@ var init_project2 = __esm({
|
|
|
72808
72945
|
});
|
|
72809
72946
|
|
|
72810
72947
|
// src/commands/export.ts
|
|
72811
|
-
import { readFile as
|
|
72948
|
+
import { readFile as readFile55, access as access7 } from "node:fs/promises";
|
|
72812
72949
|
import { resolve as resolve82, basename as basename24 } from "node:path";
|
|
72813
72950
|
import { spawn as spawn3 } from "node:child_process";
|
|
72814
72951
|
function timelineBaseName(path2) {
|
|
@@ -72877,7 +73014,7 @@ async function runExport(projectPath, outputPath, options = {}) {
|
|
|
72877
73014
|
};
|
|
72878
73015
|
}
|
|
72879
73016
|
const filePath = await resolveTimelineFile(projectPath);
|
|
72880
|
-
const content = await
|
|
73017
|
+
const content = await readFile55(filePath, "utf-8");
|
|
72881
73018
|
const data = JSON.parse(content);
|
|
72882
73019
|
const project = Project.fromJSON(data);
|
|
72883
73020
|
const summary = project.getSummary();
|
|
@@ -73443,14 +73580,14 @@ function getPresetSettings(preset, aspectRatio2) {
|
|
|
73443
73580
|
}
|
|
73444
73581
|
async function runHyperframesExport(projectPath, options, spinner2, startedAt) {
|
|
73445
73582
|
spinner2.text = "Loading project...";
|
|
73446
|
-
const { readFile:
|
|
73583
|
+
const { readFile: readFile57 } = await import("node:fs/promises");
|
|
73447
73584
|
const { resolve: resolve86 } = await import("node:path");
|
|
73448
73585
|
const { Project: Project2 } = await Promise.resolve().then(() => (init_engine(), engine_exports));
|
|
73449
73586
|
const { createHyperframesBackend: createHyperframesBackend2 } = await Promise.resolve().then(() => (init_hyperframes(), hyperframes_exports));
|
|
73450
73587
|
const { exitWithError: exitWithError2, generalError: generalError2, outputSuccess: outputSuccess2 } = await Promise.resolve().then(() => (init_output(), output_exports));
|
|
73451
73588
|
const chalk2 = (await Promise.resolve().then(() => (init_source(), source_exports))).default;
|
|
73452
73589
|
const filePath = await resolveTimelineFile(projectPath);
|
|
73453
|
-
const content = await
|
|
73590
|
+
const content = await readFile57(filePath, "utf-8");
|
|
73454
73591
|
const project = Project2.fromJSON(JSON.parse(content));
|
|
73455
73592
|
const state = project.getState();
|
|
73456
73593
|
const outputPath = options.output ? resolve86(process.cwd(), options.output) : resolve86(process.cwd(), `${timelineBaseName(projectPath)}.${options.format ?? "mp4"}`);
|
|
@@ -73572,7 +73709,7 @@ Run 'vibe schema export' for structured parameter info.`).action(async (projectP
|
|
|
73572
73709
|
}
|
|
73573
73710
|
spinner2.text = "Loading project...";
|
|
73574
73711
|
const filePath = await resolveTimelineFile(projectPath);
|
|
73575
|
-
const content = await
|
|
73712
|
+
const content = await readFile55(filePath, "utf-8");
|
|
73576
73713
|
const data = JSON.parse(content);
|
|
73577
73714
|
const project = Project.fromJSON(data);
|
|
73578
73715
|
const summary = project.getSummary();
|
|
@@ -73707,8 +73844,8 @@ var init_export2 = __esm({
|
|
|
73707
73844
|
});
|
|
73708
73845
|
|
|
73709
73846
|
// src/tools/manifest/agent-only.ts
|
|
73710
|
-
import { readFile as
|
|
73711
|
-
import { resolve as resolve84, join as
|
|
73847
|
+
import { readFile as readFile56, writeFile as writeFile59, readdir as readdir8, stat as stat10, access as access8, unlink as unlink4 } from "node:fs/promises";
|
|
73848
|
+
import { resolve as resolve84, join as join30, basename as basename25, extname as extname16 } from "node:path";
|
|
73712
73849
|
function matchPattern(filename, pattern) {
|
|
73713
73850
|
const regex2 = new RegExp(
|
|
73714
73851
|
"^" + pattern.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".") + "$",
|
|
@@ -73793,7 +73930,7 @@ var init_agent_only = __esm({
|
|
|
73793
73930
|
const results = [];
|
|
73794
73931
|
for (const entry of entries) {
|
|
73795
73932
|
if (args.pattern && !matchPattern(entry.name, args.pattern)) continue;
|
|
73796
|
-
const fullPath =
|
|
73933
|
+
const fullPath = join30(absPath, entry.name);
|
|
73797
73934
|
const stats = await stat10(fullPath);
|
|
73798
73935
|
if (entry.isDirectory()) {
|
|
73799
73936
|
results.push(`[DIR] ${entry.name}/`);
|
|
@@ -73822,7 +73959,7 @@ var init_agent_only = __esm({
|
|
|
73822
73959
|
async execute(args, ctx) {
|
|
73823
73960
|
try {
|
|
73824
73961
|
const absPath = resolve84(ctx.workingDirectory, args.path);
|
|
73825
|
-
const content = await
|
|
73962
|
+
const content = await readFile56(absPath, "utf-8");
|
|
73826
73963
|
const maxLength = 4e3;
|
|
73827
73964
|
const truncated = content.length > maxLength;
|
|
73828
73965
|
const output = truncated ? content.substring(0, maxLength) + "\n... (truncated)" : content;
|
|
@@ -73895,7 +74032,7 @@ var init_agent_only = __esm({
|
|
|
73895
74032
|
async execute(args, ctx) {
|
|
73896
74033
|
try {
|
|
73897
74034
|
const filePath = resolve84(ctx.workingDirectory, args.project);
|
|
73898
|
-
const content = await
|
|
74035
|
+
const content = await readFile56(filePath, "utf-8");
|
|
73899
74036
|
const data = JSON.parse(content);
|
|
73900
74037
|
const project = Project.fromJSON(data);
|
|
73901
74038
|
const dirPath = resolve84(ctx.workingDirectory, args.directory);
|
|
@@ -73906,7 +74043,7 @@ var init_agent_only = __esm({
|
|
|
73906
74043
|
const scanDir = async (dir) => {
|
|
73907
74044
|
const entries = await readdir8(dir, { withFileTypes: true });
|
|
73908
74045
|
for (const entry of entries) {
|
|
73909
|
-
const entryPath =
|
|
74046
|
+
const entryPath = join30(dir, entry.name);
|
|
73910
74047
|
if (entry.isDirectory() && recursive) {
|
|
73911
74048
|
await scanDir(entryPath);
|
|
73912
74049
|
} else if (entry.isFile()) {
|
|
@@ -73967,7 +74104,7 @@ var init_agent_only = __esm({
|
|
|
73967
74104
|
async execute(args, ctx) {
|
|
73968
74105
|
try {
|
|
73969
74106
|
const filePath = resolve84(ctx.workingDirectory, args.project);
|
|
73970
|
-
const content = await
|
|
74107
|
+
const content = await readFile56(filePath, "utf-8");
|
|
73971
74108
|
const data = JSON.parse(content);
|
|
73972
74109
|
const project = Project.fromJSON(data);
|
|
73973
74110
|
const sourceIds = args.sourceIds ?? [];
|
|
@@ -74038,7 +74175,7 @@ var init_agent_only = __esm({
|
|
|
74038
74175
|
async execute(args, ctx) {
|
|
74039
74176
|
try {
|
|
74040
74177
|
const filePath = resolve84(ctx.workingDirectory, args.project);
|
|
74041
|
-
const content = await
|
|
74178
|
+
const content = await readFile56(filePath, "utf-8");
|
|
74042
74179
|
const data = JSON.parse(content);
|
|
74043
74180
|
const project = Project.fromJSON(data);
|
|
74044
74181
|
const clipIds = args.clipIds ?? [];
|
|
@@ -74093,7 +74230,7 @@ var init_agent_only = __esm({
|
|
|
74093
74230
|
async execute(args, ctx) {
|
|
74094
74231
|
try {
|
|
74095
74232
|
const filePath = await resolveTimelineFile(args.project, ctx.workingDirectory);
|
|
74096
|
-
const content = await
|
|
74233
|
+
const content = await readFile56(filePath, "utf-8");
|
|
74097
74234
|
const data = JSON.parse(content);
|
|
74098
74235
|
const project = Project.fromJSON(data);
|
|
74099
74236
|
const what = args.what ?? "clips";
|
|
@@ -74170,7 +74307,7 @@ var init_agent_only = __esm({
|
|
|
74170
74307
|
async execute(args, ctx) {
|
|
74171
74308
|
try {
|
|
74172
74309
|
const filePath = await resolveTimelineFile(args.projectPath, ctx.workingDirectory);
|
|
74173
|
-
const content = await
|
|
74310
|
+
const content = await readFile56(filePath, "utf-8");
|
|
74174
74311
|
const data = JSON.parse(content);
|
|
74175
74312
|
const project = Project.fromJSON(data);
|
|
74176
74313
|
const updates = [];
|
|
@@ -74444,8 +74581,8 @@ Audio:`);
|
|
|
74444
74581
|
],
|
|
74445
74582
|
{ maxBuffer: 50 * 1024 * 1024 }
|
|
74446
74583
|
);
|
|
74447
|
-
const inputBuf = await
|
|
74448
|
-
const outputBuf = await
|
|
74584
|
+
const inputBuf = await readFile56(absInput);
|
|
74585
|
+
const outputBuf = await readFile56(outputPath);
|
|
74449
74586
|
const inputSize = inputBuf.length;
|
|
74450
74587
|
const outputSize = outputBuf.length;
|
|
74451
74588
|
const reduction = (inputSize - outputSize) / inputSize * 100;
|
|
@@ -74996,7 +75133,7 @@ init_source();
|
|
|
74996
75133
|
// package.json
|
|
74997
75134
|
var package_default = {
|
|
74998
75135
|
name: "@vibeframe/cli",
|
|
74999
|
-
version: "0.113.
|
|
75136
|
+
version: "0.113.11",
|
|
75000
75137
|
description: "VibeFrame CLI - agentic video workflow runtime",
|
|
75001
75138
|
private: false,
|
|
75002
75139
|
license: "MIT",
|
|
@@ -75340,8 +75477,8 @@ init_validate();
|
|
|
75340
75477
|
init_output();
|
|
75341
75478
|
init_cost_tier();
|
|
75342
75479
|
init_project_resolver();
|
|
75343
|
-
import { readFile as
|
|
75344
|
-
import { existsSync as
|
|
75480
|
+
import { readFile as readFile15, writeFile as writeFile13 } from "node:fs/promises";
|
|
75481
|
+
import { existsSync as existsSync13 } from "node:fs";
|
|
75345
75482
|
import { resolve as resolve18, basename as basename3, extname } from "node:path";
|
|
75346
75483
|
var timelineCommand = new Command("timeline").description("Low-level timeline JSON commands").addHelpText(
|
|
75347
75484
|
"after",
|
|
@@ -75390,7 +75527,7 @@ timelineCommand.command("add-source").description("Add a media source to the tim
|
|
|
75390
75527
|
return;
|
|
75391
75528
|
}
|
|
75392
75529
|
const filePath = await resolveTimelineFile(projectPath);
|
|
75393
|
-
const content = await
|
|
75530
|
+
const content = await readFile15(filePath, "utf-8");
|
|
75394
75531
|
const data = JSON.parse(content);
|
|
75395
75532
|
const project = Project.fromJSON(data);
|
|
75396
75533
|
const absMediaPath = resolve18(process.cwd(), mediaPath);
|
|
@@ -75455,7 +75592,7 @@ timelineCommand.command("add-clip").description("Add a clip to the timeline").ar
|
|
|
75455
75592
|
return;
|
|
75456
75593
|
}
|
|
75457
75594
|
const filePath = await resolveTimelineFile(projectPath);
|
|
75458
|
-
const content = await
|
|
75595
|
+
const content = await readFile15(filePath, "utf-8");
|
|
75459
75596
|
const data = JSON.parse(content);
|
|
75460
75597
|
const project = Project.fromJSON(data);
|
|
75461
75598
|
const source = project.getSource(sourceId);
|
|
@@ -75532,7 +75669,7 @@ timelineCommand.command("add-track").description("Add a new track").argument("<p
|
|
|
75532
75669
|
return;
|
|
75533
75670
|
}
|
|
75534
75671
|
const filePath = await resolveTimelineFile(projectPath);
|
|
75535
|
-
const content = await
|
|
75672
|
+
const content = await readFile15(filePath, "utf-8");
|
|
75536
75673
|
const data = JSON.parse(content);
|
|
75537
75674
|
const project = Project.fromJSON(data);
|
|
75538
75675
|
const existingTracks = project.getTracksByType(type);
|
|
@@ -75596,7 +75733,7 @@ timelineCommand.command("add-effect").description("Add an effect to a clip").arg
|
|
|
75596
75733
|
return;
|
|
75597
75734
|
}
|
|
75598
75735
|
const filePath = await resolveTimelineFile(projectPath);
|
|
75599
|
-
const content = await
|
|
75736
|
+
const content = await readFile15(filePath, "utf-8");
|
|
75600
75737
|
const data = JSON.parse(content);
|
|
75601
75738
|
const project = Project.fromJSON(data);
|
|
75602
75739
|
const clip = project.getClip(clipId);
|
|
@@ -75666,7 +75803,7 @@ timelineCommand.command("trim-clip").description("Trim a clip").argument("<proje
|
|
|
75666
75803
|
return;
|
|
75667
75804
|
}
|
|
75668
75805
|
const filePath = await resolveTimelineFile(projectPath);
|
|
75669
|
-
const content = await
|
|
75806
|
+
const content = await readFile15(filePath, "utf-8");
|
|
75670
75807
|
const data = JSON.parse(content);
|
|
75671
75808
|
const project = Project.fromJSON(data);
|
|
75672
75809
|
const clip = project.getClip(clipId);
|
|
@@ -75708,10 +75845,10 @@ timelineCommand.command("list").description("List timeline contents").argument("
|
|
|
75708
75845
|
const startedAt = Date.now();
|
|
75709
75846
|
try {
|
|
75710
75847
|
const filePath = await resolveTimelineFile(projectPath);
|
|
75711
|
-
if (!
|
|
75848
|
+
if (!existsSync13(filePath)) {
|
|
75712
75849
|
exitWithError(notFoundError(projectPath));
|
|
75713
75850
|
}
|
|
75714
|
-
const content = await
|
|
75851
|
+
const content = await readFile15(filePath, "utf-8");
|
|
75715
75852
|
const data = JSON.parse(content);
|
|
75716
75853
|
const project = Project.fromJSON(data);
|
|
75717
75854
|
const showAll = !options.sources && !options.tracks && !options.clips;
|
|
@@ -75832,7 +75969,7 @@ timelineCommand.command("split-clip").description("Split a clip at a specific ti
|
|
|
75832
75969
|
return;
|
|
75833
75970
|
}
|
|
75834
75971
|
const filePath = await resolveTimelineFile(projectPath);
|
|
75835
|
-
const content = await
|
|
75972
|
+
const content = await readFile15(filePath, "utf-8");
|
|
75836
75973
|
const data = JSON.parse(content);
|
|
75837
75974
|
const project = Project.fromJSON(data);
|
|
75838
75975
|
const clip = project.getClip(clipId);
|
|
@@ -75894,7 +76031,7 @@ timelineCommand.command("duplicate-clip").description("Duplicate a clip").argume
|
|
|
75894
76031
|
return;
|
|
75895
76032
|
}
|
|
75896
76033
|
const filePath = await resolveTimelineFile(projectPath);
|
|
75897
|
-
const content = await
|
|
76034
|
+
const content = await readFile15(filePath, "utf-8");
|
|
75898
76035
|
const data = JSON.parse(content);
|
|
75899
76036
|
const project = Project.fromJSON(data);
|
|
75900
76037
|
const clip = project.getClip(clipId);
|
|
@@ -75952,7 +76089,7 @@ timelineCommand.command("delete-clip").description("Delete a clip from the timel
|
|
|
75952
76089
|
return;
|
|
75953
76090
|
}
|
|
75954
76091
|
const filePath = await resolveTimelineFile(projectPath);
|
|
75955
|
-
const content = await
|
|
76092
|
+
const content = await readFile15(filePath, "utf-8");
|
|
75956
76093
|
const data = JSON.parse(content);
|
|
75957
76094
|
const project = Project.fromJSON(data);
|
|
75958
76095
|
const clip = project.getClip(clipId);
|
|
@@ -76007,7 +76144,7 @@ timelineCommand.command("move-clip").description("Move a clip to a new position"
|
|
|
76007
76144
|
return;
|
|
76008
76145
|
}
|
|
76009
76146
|
const filePath = await resolveTimelineFile(projectPath);
|
|
76010
|
-
const content = await
|
|
76147
|
+
const content = await readFile15(filePath, "utf-8");
|
|
76011
76148
|
const data = JSON.parse(content);
|
|
76012
76149
|
const project = Project.fromJSON(data);
|
|
76013
76150
|
const clip = project.getClip(clipId);
|
|
@@ -76093,7 +76230,7 @@ init_validate();
|
|
|
76093
76230
|
init_cost_tier();
|
|
76094
76231
|
init_scene_inspect();
|
|
76095
76232
|
init_render_inspect();
|
|
76096
|
-
import { readFile as
|
|
76233
|
+
import { readFile as readFile32, writeFile as writeFile37 } from "node:fs/promises";
|
|
76097
76234
|
import { resolve as resolve44 } from "node:path";
|
|
76098
76235
|
var VALID_RENDER_AI_MODELS = [
|
|
76099
76236
|
"flash",
|
|
@@ -76446,7 +76583,7 @@ inspectCommand.command("suggest").description("Get AI edit suggestions using Gem
|
|
|
76446
76583
|
const apiKey = await requireApiKey("GOOGLE_API_KEY", "Google", options.apiKey);
|
|
76447
76584
|
const spinner2 = ora("Initializing Gemini...").start();
|
|
76448
76585
|
const filePath = resolve44(process.cwd(), projectPath);
|
|
76449
|
-
const content = await
|
|
76586
|
+
const content = await readFile32(filePath, "utf-8");
|
|
76450
76587
|
const data = JSON.parse(content);
|
|
76451
76588
|
const project = Project.fromJSON(data);
|
|
76452
76589
|
const gemini = new GeminiProvider();
|
|
@@ -76515,8 +76652,8 @@ init_ai_helpers();
|
|
|
76515
76652
|
init_output();
|
|
76516
76653
|
init_validate();
|
|
76517
76654
|
import { resolve as resolve45, dirname as dirname16, basename as basename12, extname as extname8 } from "node:path";
|
|
76518
|
-
import { readFile as
|
|
76519
|
-
import { existsSync as
|
|
76655
|
+
import { readFile as readFile33, writeFile as writeFile38 } from "node:fs/promises";
|
|
76656
|
+
import { existsSync as existsSync37 } from "node:fs";
|
|
76520
76657
|
var audioCommand = new Command("audio").alias("au").description("Audio operations (transcribe, dub, duck, isolate, voice clone)").addHelpText(
|
|
76521
76658
|
"after",
|
|
76522
76659
|
`
|
|
@@ -76553,7 +76690,7 @@ audioCommand.command("transcribe").description("Transcribe audio using Whisper")
|
|
|
76553
76690
|
await whisper.initialize({ apiKey });
|
|
76554
76691
|
spinner2.text = "Reading audio file...";
|
|
76555
76692
|
const absPath = resolve45(process.cwd(), audioPath);
|
|
76556
|
-
const audioBuffer = await
|
|
76693
|
+
const audioBuffer = await readFile33(absPath);
|
|
76557
76694
|
const audioBlob = new Blob([audioBuffer]);
|
|
76558
76695
|
spinner2.text = "Transcribing...";
|
|
76559
76696
|
const result = await whisper.transcribe(audioBlob, options.language);
|
|
@@ -76657,7 +76794,7 @@ audioCommand.command("isolate").description("Isolate vocals from audio using Ele
|
|
|
76657
76794
|
const apiKey = await requireApiKey("ELEVENLABS_API_KEY", "ElevenLabs", options.apiKey);
|
|
76658
76795
|
const spinner2 = ora("Reading audio file...").start();
|
|
76659
76796
|
const absPath = resolve45(process.cwd(), audioPath);
|
|
76660
|
-
const audioBuffer = await
|
|
76797
|
+
const audioBuffer = await readFile33(absPath);
|
|
76661
76798
|
spinner2.text = "Isolating vocals...";
|
|
76662
76799
|
const elevenlabs = new ElevenLabsProvider();
|
|
76663
76800
|
await elevenlabs.initialize({ apiKey });
|
|
@@ -76728,11 +76865,11 @@ audioCommand.command("clone-voice").description("Clone a voice from audio sample
|
|
|
76728
76865
|
const audioBuffers = [];
|
|
76729
76866
|
for (const samplePath of samples) {
|
|
76730
76867
|
const absPath = resolve45(process.cwd(), samplePath);
|
|
76731
|
-
if (!
|
|
76868
|
+
if (!existsSync37(absPath)) {
|
|
76732
76869
|
spinner2.fail(`File not found: ${samplePath}`);
|
|
76733
76870
|
exitWithError(notFoundError(samplePath));
|
|
76734
76871
|
}
|
|
76735
|
-
const buffer = await
|
|
76872
|
+
const buffer = await readFile33(absPath);
|
|
76736
76873
|
audioBuffers.push(buffer);
|
|
76737
76874
|
}
|
|
76738
76875
|
spinner2.text = `Cloning voice from ${audioBuffers.length} sample(s)...`;
|
|
@@ -76796,7 +76933,7 @@ audioCommand.command("dub").description("Dub audio/video to another language (tr
|
|
|
76796
76933
|
exitWithError(usageError("Target language is required. Use -l or --language"));
|
|
76797
76934
|
}
|
|
76798
76935
|
const absPath = resolve45(process.cwd(), mediaPath);
|
|
76799
|
-
if (!
|
|
76936
|
+
if (!existsSync37(absPath)) {
|
|
76800
76937
|
exitWithError(notFoundError(mediaPath));
|
|
76801
76938
|
}
|
|
76802
76939
|
const openaiKey = await requireApiKey("OPENAI_API_KEY", "OpenAI");
|
|
@@ -76821,7 +76958,7 @@ audioCommand.command("dub").description("Dub audio/video to another language (tr
|
|
|
76821
76958
|
spinner2.text = "Transcribing audio...";
|
|
76822
76959
|
const whisper = new WhisperProvider();
|
|
76823
76960
|
await whisper.initialize({ apiKey: openaiKey });
|
|
76824
|
-
const audioBuffer = await
|
|
76961
|
+
const audioBuffer = await readFile33(audioPath);
|
|
76825
76962
|
const audioBlob = new Blob([audioBuffer]);
|
|
76826
76963
|
const transcriptResult = await whisper.transcribe(audioBlob, options.source);
|
|
76827
76964
|
if (transcriptResult.status === "failed" || !transcriptResult.segments) {
|
|
@@ -77077,7 +77214,7 @@ init_esm();
|
|
|
77077
77214
|
init_source();
|
|
77078
77215
|
init_ora();
|
|
77079
77216
|
import { resolve as resolve50, basename as basename15 } from "node:path";
|
|
77080
|
-
import { existsSync as
|
|
77217
|
+
import { existsSync as existsSync42 } from "node:fs";
|
|
77081
77218
|
|
|
77082
77219
|
// src/commands/ai-script-pipeline-cli.ts
|
|
77083
77220
|
init_source();
|
|
@@ -77086,9 +77223,9 @@ var import_yaml7 = __toESM(require_dist2(), 1);
|
|
|
77086
77223
|
init_api_key();
|
|
77087
77224
|
init_ai_script_pipeline();
|
|
77088
77225
|
init_output();
|
|
77089
|
-
import { readFile as
|
|
77226
|
+
import { readFile as readFile35, writeFile as writeFile40 } from "node:fs/promises";
|
|
77090
77227
|
import { resolve as resolve47 } from "node:path";
|
|
77091
|
-
import { existsSync as
|
|
77228
|
+
import { existsSync as existsSync39 } from "node:fs";
|
|
77092
77229
|
function registerScriptPipelineCommands(aiCommand) {
|
|
77093
77230
|
aiCommand.command("regenerate-scene", { hidden: true }).description("Regenerate a specific scene in a script-to-video output directory").argument("<project-dir>", "Path to the script-to-video output directory").requiredOption(
|
|
77094
77231
|
"--scene <numbers>",
|
|
@@ -77105,12 +77242,12 @@ function registerScriptPipelineCommands(aiCommand) {
|
|
|
77105
77242
|
try {
|
|
77106
77243
|
const outputDir = resolve47(process.cwd(), projectDir);
|
|
77107
77244
|
const projectPath = resolve47(outputDir, "timeline.json");
|
|
77108
|
-
if (!
|
|
77245
|
+
if (!existsSync39(outputDir)) {
|
|
77109
77246
|
exitWithError(notFoundError(outputDir));
|
|
77110
77247
|
}
|
|
77111
77248
|
const yamlPath = resolve47(outputDir, "storyboard.yaml");
|
|
77112
77249
|
const jsonPath = resolve47(outputDir, "storyboard.json");
|
|
77113
|
-
const storyboardPath2 =
|
|
77250
|
+
const storyboardPath2 = existsSync39(yamlPath) ? yamlPath : existsSync39(jsonPath) ? jsonPath : null;
|
|
77114
77251
|
const storyboardIsYaml = storyboardPath2 === yamlPath;
|
|
77115
77252
|
if (!storyboardPath2) {
|
|
77116
77253
|
exitWithError(notFoundError(`${outputDir}/storyboard.{yaml,json}`));
|
|
@@ -77146,7 +77283,7 @@ function registerScriptPipelineCommands(aiCommand) {
|
|
|
77146
77283
|
});
|
|
77147
77284
|
return;
|
|
77148
77285
|
}
|
|
77149
|
-
const content = await
|
|
77286
|
+
const content = await readFile35(storyboardPath2, "utf-8");
|
|
77150
77287
|
const segments = storyboardIsYaml ? (0, import_yaml7.parse)(content).scenes : JSON.parse(content);
|
|
77151
77288
|
for (const sceneNum of sceneNums) {
|
|
77152
77289
|
if (sceneNum > segments.length) {
|
|
@@ -77229,12 +77366,12 @@ function registerScriptPipelineCommands(aiCommand) {
|
|
|
77229
77366
|
exitWithError(apiError(result.error || "Scene regeneration failed", true));
|
|
77230
77367
|
}
|
|
77231
77368
|
spinner2.succeed(source_default.green(`Regenerated ${result.regeneratedScenes.length} scene(s)`));
|
|
77232
|
-
if (
|
|
77369
|
+
if (existsSync39(projectPath) && result.regeneratedScenes.length > 0) {
|
|
77233
77370
|
const updateSpinner = ora("Updating timeline file...").start();
|
|
77234
77371
|
try {
|
|
77235
|
-
const updatedContent = await
|
|
77372
|
+
const updatedContent = await readFile35(storyboardPath2, "utf-8");
|
|
77236
77373
|
const updatedSegments = storyboardIsYaml ? (0, import_yaml7.parse)(updatedContent).scenes : JSON.parse(updatedContent);
|
|
77237
|
-
const projectContent = await
|
|
77374
|
+
const projectContent = await readFile35(projectPath, "utf-8");
|
|
77238
77375
|
const projectData = JSON.parse(projectContent);
|
|
77239
77376
|
for (const sceneNum of result.regeneratedScenes) {
|
|
77240
77377
|
const segment = updatedSegments[sceneNum - 1];
|
|
@@ -77245,7 +77382,7 @@ function registerScriptPipelineCommands(aiCommand) {
|
|
|
77245
77382
|
(s) => s.name === `Scene ${sceneNum}`
|
|
77246
77383
|
);
|
|
77247
77384
|
if (videoSource) {
|
|
77248
|
-
const hasVideo =
|
|
77385
|
+
const hasVideo = existsSync39(videoPath);
|
|
77249
77386
|
videoSource.url = hasVideo ? videoPath : imagePath;
|
|
77250
77387
|
videoSource.type = hasVideo ? "video" : "image";
|
|
77251
77388
|
videoSource.duration = segment.duration;
|
|
@@ -77370,7 +77507,7 @@ Required API Key: OPENAI_API_KEY (Whisper transcription)
|
|
|
77370
77507
|
const startedAt = Date.now();
|
|
77371
77508
|
try {
|
|
77372
77509
|
const absVideoPath = resolve50(process.cwd(), videoPath);
|
|
77373
|
-
if (!
|
|
77510
|
+
if (!existsSync42(absVideoPath)) {
|
|
77374
77511
|
exitWithError(notFoundError(absVideoPath));
|
|
77375
77512
|
}
|
|
77376
77513
|
if (!ANIMATED_CAPTION_STYLES.includes(options.style)) {
|
|
@@ -81753,8 +81890,8 @@ init_ora();
|
|
|
81753
81890
|
init_engine();
|
|
81754
81891
|
init_output();
|
|
81755
81892
|
init_cost_tier();
|
|
81756
|
-
import { readFile as
|
|
81757
|
-
import { resolve as resolve52, basename as basename17, extname as extname12, join as
|
|
81893
|
+
import { readFile as readFile37, writeFile as writeFile43, readdir as readdir6 } from "node:fs/promises";
|
|
81894
|
+
import { resolve as resolve52, basename as basename17, extname as extname12, join as join21 } from "node:path";
|
|
81758
81895
|
var batchCommand = new Command("batch").description("Batch operations for processing multiple items");
|
|
81759
81896
|
function detectMediaType3(filePath) {
|
|
81760
81897
|
const ext = extname12(filePath).toLowerCase();
|
|
@@ -81812,7 +81949,7 @@ batchCommand.command("import").description("Import multiple media files from a d
|
|
|
81812
81949
|
return;
|
|
81813
81950
|
}
|
|
81814
81951
|
const filePath = resolve52(process.cwd(), projectPath);
|
|
81815
|
-
const content = await
|
|
81952
|
+
const content = await readFile37(filePath, "utf-8");
|
|
81816
81953
|
const data = JSON.parse(content);
|
|
81817
81954
|
const project = Project.fromJSON(data);
|
|
81818
81955
|
const dirPath = resolve52(process.cwd(), directory);
|
|
@@ -81821,7 +81958,7 @@ batchCommand.command("import").description("Import multiple media files from a d
|
|
|
81821
81958
|
const scanDir = async (dir) => {
|
|
81822
81959
|
const entries = await readdir6(dir, { withFileTypes: true });
|
|
81823
81960
|
for (const entry of entries) {
|
|
81824
|
-
const entryPath =
|
|
81961
|
+
const entryPath = join21(dir, entry.name);
|
|
81825
81962
|
if (entry.isDirectory() && options.recursive) {
|
|
81826
81963
|
await scanDir(entryPath);
|
|
81827
81964
|
} else if (entry.isFile()) {
|
|
@@ -81890,7 +82027,7 @@ batchCommand.command("concat").description("Concatenate multiple sources into se
|
|
|
81890
82027
|
return;
|
|
81891
82028
|
}
|
|
81892
82029
|
const filePath = resolve52(process.cwd(), projectPath);
|
|
81893
|
-
const content = await
|
|
82030
|
+
const content = await readFile37(filePath, "utf-8");
|
|
81894
82031
|
const data = JSON.parse(content);
|
|
81895
82032
|
const project = Project.fromJSON(data);
|
|
81896
82033
|
const sources = project.getSources();
|
|
@@ -81969,7 +82106,7 @@ batchCommand.command("apply-effect").description("Apply an effect to multiple cl
|
|
|
81969
82106
|
return;
|
|
81970
82107
|
}
|
|
81971
82108
|
const filePath = resolve52(process.cwd(), projectPath);
|
|
81972
|
-
const content = await
|
|
82109
|
+
const content = await readFile37(filePath, "utf-8");
|
|
81973
82110
|
const data = JSON.parse(content);
|
|
81974
82111
|
const project = Project.fromJSON(data);
|
|
81975
82112
|
const clips = project.getClips();
|
|
@@ -82037,7 +82174,7 @@ batchCommand.command("remove-clips").description("Remove multiple clips from the
|
|
|
82037
82174
|
return;
|
|
82038
82175
|
}
|
|
82039
82176
|
const filePath = resolve52(process.cwd(), projectPath);
|
|
82040
|
-
const content = await
|
|
82177
|
+
const content = await readFile37(filePath, "utf-8");
|
|
82041
82178
|
const data = JSON.parse(content);
|
|
82042
82179
|
const project = Project.fromJSON(data);
|
|
82043
82180
|
const clips = project.getClips();
|
|
@@ -82075,7 +82212,7 @@ batchCommand.command("info").description("Show batch processing statistics").arg
|
|
|
82075
82212
|
const spinner2 = ora("Loading project...").start();
|
|
82076
82213
|
try {
|
|
82077
82214
|
const filePath = resolve52(process.cwd(), projectPath);
|
|
82078
|
-
const content = await
|
|
82215
|
+
const content = await readFile37(filePath, "utf-8");
|
|
82079
82216
|
const data = JSON.parse(content);
|
|
82080
82217
|
const project = Project.fromJSON(data);
|
|
82081
82218
|
const sources = project.getSources();
|
|
@@ -82135,7 +82272,7 @@ init_output();
|
|
|
82135
82272
|
init_tty();
|
|
82136
82273
|
init_api_key();
|
|
82137
82274
|
import { resolve as resolve54 } from "node:path";
|
|
82138
|
-
import { access as access6, readFile as
|
|
82275
|
+
import { access as access6, readFile as readFile39 } from "node:fs/promises";
|
|
82139
82276
|
import { homedir as homedir6 } from "node:os";
|
|
82140
82277
|
|
|
82141
82278
|
// src/utils/key-format.ts
|
|
@@ -83336,7 +83473,7 @@ async function showConfig(opts = { verbose: false }) {
|
|
|
83336
83473
|
let dotenvKeys = {};
|
|
83337
83474
|
if (hasCwdEnv) {
|
|
83338
83475
|
try {
|
|
83339
|
-
const envContent = await
|
|
83476
|
+
const envContent = await readFile39(cwdEnvPath, "utf-8");
|
|
83340
83477
|
dotenvKeys = (0, import_dotenv2.parse)(Buffer.from(envContent));
|
|
83341
83478
|
} catch {
|
|
83342
83479
|
}
|
|
@@ -83433,8 +83570,8 @@ init_source();
|
|
|
83433
83570
|
init_agent_host_detect();
|
|
83434
83571
|
init_scene_project();
|
|
83435
83572
|
init_visual_styles();
|
|
83436
|
-
import { existsSync as
|
|
83437
|
-
import { mkdir as mkdir24, readFile as
|
|
83573
|
+
import { existsSync as existsSync44 } from "node:fs";
|
|
83574
|
+
import { mkdir as mkdir24, readFile as readFile41, writeFile as writeFile46 } from "node:fs/promises";
|
|
83438
83575
|
import { basename as basename19, dirname as dirname20, resolve as resolve56 } from "node:path";
|
|
83439
83576
|
|
|
83440
83577
|
// src/commands/_shared/storyboard-draft.ts
|
|
@@ -83597,10 +83734,10 @@ init_project_config();
|
|
|
83597
83734
|
init_install_skill();
|
|
83598
83735
|
|
|
83599
83736
|
// src/commands/_shared/host-integration.ts
|
|
83600
|
-
import { existsSync as
|
|
83601
|
-
import { mkdir as mkdir23, readFile as
|
|
83737
|
+
import { existsSync as existsSync43 } from "node:fs";
|
|
83738
|
+
import { mkdir as mkdir23, readFile as readFile40, writeFile as writeFile45 } from "node:fs/promises";
|
|
83602
83739
|
import { homedir as homedir7 } from "node:os";
|
|
83603
|
-
import { dirname as dirname19, join as
|
|
83740
|
+
import { dirname as dirname19, join as join22, resolve as resolve55 } from "node:path";
|
|
83604
83741
|
var HOST_SELECTIONS = [
|
|
83605
83742
|
"codex",
|
|
83606
83743
|
"claude",
|
|
@@ -83627,7 +83764,7 @@ function hostDefinitions(_projectDir = process.cwd()) {
|
|
|
83627
83764
|
label: "OpenAI Codex",
|
|
83628
83765
|
surfaces: ["shell", "project-guidance", "mcp"],
|
|
83629
83766
|
guidanceFiles: ["AGENTS.md"],
|
|
83630
|
-
configPath: (dir) =>
|
|
83767
|
+
configPath: (dir) => join22(resolve55(dir), ".codex", "config.toml"),
|
|
83631
83768
|
configKind: "codex-toml",
|
|
83632
83769
|
notes: [
|
|
83633
83770
|
"Codex reads AGENTS.md and can also load project-scoped .codex/config.toml after the project is trusted.",
|
|
@@ -83640,7 +83777,7 @@ function hostDefinitions(_projectDir = process.cwd()) {
|
|
|
83640
83777
|
label: "Claude Code",
|
|
83641
83778
|
surfaces: ["shell", "project-guidance", "mcp"],
|
|
83642
83779
|
guidanceFiles: ["AGENTS.md", "CLAUDE.md", ".claude/skills/hyperframes/"],
|
|
83643
|
-
configPath: (dir) =>
|
|
83780
|
+
configPath: (dir) => join22(resolve55(dir), ".mcp.json"),
|
|
83644
83781
|
configKind: "mcp-json",
|
|
83645
83782
|
notes: [
|
|
83646
83783
|
"Claude Code can drive vibe directly through shell plus AGENTS.md/CLAUDE.md.",
|
|
@@ -83665,7 +83802,7 @@ function hostDefinitions(_projectDir = process.cwd()) {
|
|
|
83665
83802
|
label: "Cursor",
|
|
83666
83803
|
surfaces: ["shell", "project-guidance", "mcp"],
|
|
83667
83804
|
guidanceFiles: ["AGENTS.md", ".cursor/rules/hyperframes.mdc"],
|
|
83668
|
-
configPath: (dir) =>
|
|
83805
|
+
configPath: (dir) => join22(resolve55(dir), ".cursor", "mcp.json"),
|
|
83669
83806
|
configKind: "mcp-json",
|
|
83670
83807
|
notes: [
|
|
83671
83808
|
"Cursor can use AGENTS.md/rules for shell-driven work and .cursor/mcp.json for typed MCP tools."
|
|
@@ -83706,11 +83843,11 @@ async function planHostSetup(host, projectDir, opts) {
|
|
|
83706
83843
|
const warnings = [];
|
|
83707
83844
|
const nextSteps = hostNextSteps(host);
|
|
83708
83845
|
for (const relPath of host.guidanceFiles.filter((p) => !p.endsWith("/"))) {
|
|
83709
|
-
const absPath =
|
|
83846
|
+
const absPath = join22(resolve55(projectDir), relPath);
|
|
83710
83847
|
files.push({
|
|
83711
83848
|
path: absPath,
|
|
83712
|
-
status:
|
|
83713
|
-
reason:
|
|
83849
|
+
status: existsSync43(absPath) ? "skipped-exists" : "missing",
|
|
83850
|
+
reason: existsSync43(absPath) ? "already present" : "run vibe init --agent all first"
|
|
83714
83851
|
});
|
|
83715
83852
|
}
|
|
83716
83853
|
if (!opts.write || opts.dryRun) {
|
|
@@ -83754,17 +83891,17 @@ async function inspectHost(host, projectDir) {
|
|
|
83754
83891
|
const warnings = [];
|
|
83755
83892
|
const nextSteps = hostNextSteps(host);
|
|
83756
83893
|
const guidanceFiles = host.guidanceFiles.map((relPath) => {
|
|
83757
|
-
const absPath = relPath.endsWith("/") ?
|
|
83894
|
+
const absPath = relPath.endsWith("/") ? join22(resolve55(projectDir), relPath) : join22(resolve55(projectDir), relPath);
|
|
83758
83895
|
return {
|
|
83759
83896
|
path: absPath,
|
|
83760
|
-
status:
|
|
83761
|
-
reason:
|
|
83897
|
+
status: existsSync43(absPath) ? "skipped-exists" : "missing",
|
|
83898
|
+
reason: existsSync43(absPath) ? "present" : "missing"
|
|
83762
83899
|
};
|
|
83763
83900
|
});
|
|
83764
83901
|
let configured = false;
|
|
83765
|
-
if (
|
|
83902
|
+
if (existsSync43(configPath)) {
|
|
83766
83903
|
try {
|
|
83767
|
-
const content = await
|
|
83904
|
+
const content = await readFile40(configPath, "utf-8");
|
|
83768
83905
|
if (host.configKind === "codex-toml") {
|
|
83769
83906
|
configured = /\[mcp_servers\.vibeframe\]/.test(content);
|
|
83770
83907
|
} else {
|
|
@@ -83785,9 +83922,9 @@ async function inspectHost(host, projectDir) {
|
|
|
83785
83922
|
}
|
|
83786
83923
|
async function mergeMcpJson(configPath, opts) {
|
|
83787
83924
|
let root = {};
|
|
83788
|
-
const exists =
|
|
83925
|
+
const exists = existsSync43(configPath);
|
|
83789
83926
|
if (exists) {
|
|
83790
|
-
const raw = await
|
|
83927
|
+
const raw = await readFile40(configPath, "utf-8");
|
|
83791
83928
|
try {
|
|
83792
83929
|
root = raw.trim() ? JSON.parse(raw) : {};
|
|
83793
83930
|
} catch (error) {
|
|
@@ -83808,7 +83945,7 @@ async function mergeMcpJson(configPath, opts) {
|
|
|
83808
83945
|
if (exists && opts.backup) {
|
|
83809
83946
|
await writeFile45(
|
|
83810
83947
|
`${configPath}.bak-${timestamp()}`,
|
|
83811
|
-
await
|
|
83948
|
+
await readFile40(configPath, "utf-8"),
|
|
83812
83949
|
"utf-8"
|
|
83813
83950
|
);
|
|
83814
83951
|
}
|
|
@@ -83830,7 +83967,7 @@ async function mergeMcpJson(configPath, opts) {
|
|
|
83830
83967
|
if (exists && opts.backup) {
|
|
83831
83968
|
await writeFile45(
|
|
83832
83969
|
`${configPath}.bak-${timestamp()}`,
|
|
83833
|
-
await
|
|
83970
|
+
await readFile40(configPath, "utf-8"),
|
|
83834
83971
|
"utf-8"
|
|
83835
83972
|
);
|
|
83836
83973
|
}
|
|
@@ -83838,7 +83975,7 @@ async function mergeMcpJson(configPath, opts) {
|
|
|
83838
83975
|
return { path: configPath, status: exists ? "merged" : "wrote" };
|
|
83839
83976
|
}
|
|
83840
83977
|
async function mergeCodexToml(configPath, opts) {
|
|
83841
|
-
const exists =
|
|
83978
|
+
const exists = existsSync43(configPath);
|
|
83842
83979
|
const block = `${CODEX_MANAGED_START}
|
|
83843
83980
|
${CODEX_MCP_BLOCK}${CODEX_MANAGED_END}`;
|
|
83844
83981
|
if (!exists) {
|
|
@@ -83853,7 +83990,7 @@ ${block}
|
|
|
83853
83990
|
);
|
|
83854
83991
|
return { path: configPath, status: "wrote" };
|
|
83855
83992
|
}
|
|
83856
|
-
const raw = await
|
|
83993
|
+
const raw = await readFile40(configPath, "utf-8");
|
|
83857
83994
|
if (raw.includes(CODEX_MANAGED_START)) {
|
|
83858
83995
|
const updated = raw.replace(
|
|
83859
83996
|
new RegExp(
|
|
@@ -83942,16 +84079,16 @@ function hostNextSteps(host) {
|
|
|
83942
84079
|
function claudeDesktopConfigPath() {
|
|
83943
84080
|
const home = homedir7();
|
|
83944
84081
|
if (process.platform === "darwin") {
|
|
83945
|
-
return
|
|
84082
|
+
return join22(home, "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
83946
84083
|
}
|
|
83947
84084
|
if (process.platform === "win32") {
|
|
83948
|
-
return
|
|
83949
|
-
process.env.APPDATA ??
|
|
84085
|
+
return join22(
|
|
84086
|
+
process.env.APPDATA ?? join22(home, "AppData", "Roaming"),
|
|
83950
84087
|
"Claude",
|
|
83951
84088
|
"claude_desktop_config.json"
|
|
83952
84089
|
);
|
|
83953
84090
|
}
|
|
83954
|
-
return
|
|
84091
|
+
return join22(home, ".config", "Claude", "claude_desktop_config.json");
|
|
83955
84092
|
}
|
|
83956
84093
|
function timestamp() {
|
|
83957
84094
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
@@ -84404,7 +84541,7 @@ var initCommand = new Command("init").description("Scaffold a VibeFrame project
|
|
|
84404
84541
|
const wantsGemini = targetHosts.includes("gemini-cli");
|
|
84405
84542
|
const wantsCrossTool = targetHosts.some((h) => h !== "claude-code");
|
|
84406
84543
|
const actions = [];
|
|
84407
|
-
if (!
|
|
84544
|
+
if (!existsSync44(projectDir)) {
|
|
84408
84545
|
if (options.dryRun) {
|
|
84409
84546
|
actions.push({
|
|
84410
84547
|
path: projectDir,
|
|
@@ -84619,11 +84756,11 @@ async function runSceneInit(projectDirArg, options, startedAt) {
|
|
|
84619
84756
|
});
|
|
84620
84757
|
const storyboardPath2 = resolve56(projectDir, "STORYBOARD.md");
|
|
84621
84758
|
const designPath = resolve56(projectDir, "DESIGN.md");
|
|
84622
|
-
if (options.force || result.created.includes(storyboardPath2) || !
|
|
84759
|
+
if (options.force || result.created.includes(storyboardPath2) || !existsSync44(storyboardPath2)) {
|
|
84623
84760
|
await writeFile46(storyboardPath2, draft.storyboardMd, "utf-8");
|
|
84624
84761
|
if (!result.created.includes(storyboardPath2)) result.created.push(storyboardPath2);
|
|
84625
84762
|
}
|
|
84626
|
-
if (options.force || result.created.includes(designPath) || !
|
|
84763
|
+
if (options.force || result.created.includes(designPath) || !existsSync44(designPath)) {
|
|
84627
84764
|
await writeFile46(designPath, draft.designMd, "utf-8");
|
|
84628
84765
|
if (!result.created.includes(designPath)) result.created.push(designPath);
|
|
84629
84766
|
}
|
|
@@ -84747,8 +84884,8 @@ function printGroup(label, files, projectDir) {
|
|
|
84747
84884
|
}
|
|
84748
84885
|
async function readBriefOrLiteral(value, cwd) {
|
|
84749
84886
|
const candidate = resolve56(cwd, value);
|
|
84750
|
-
if (
|
|
84751
|
-
return
|
|
84887
|
+
if (existsSync44(candidate)) {
|
|
84888
|
+
return readFile41(candidate, "utf-8");
|
|
84752
84889
|
}
|
|
84753
84890
|
return value;
|
|
84754
84891
|
}
|
|
@@ -84801,7 +84938,7 @@ function normalizeHostAction(status) {
|
|
|
84801
84938
|
return "would-write";
|
|
84802
84939
|
}
|
|
84803
84940
|
async function writeIfMissing(absPath, content, force, dryRun) {
|
|
84804
|
-
const exists =
|
|
84941
|
+
const exists = existsSync44(absPath);
|
|
84805
84942
|
if (exists && !force) {
|
|
84806
84943
|
return { path: absPath, status: "skipped-exists", reason: "(use --force to overwrite)" };
|
|
84807
84944
|
}
|
|
@@ -84814,12 +84951,12 @@ async function writeIfMissing(absPath, content, force, dryRun) {
|
|
|
84814
84951
|
}
|
|
84815
84952
|
async function mergeGitignore(absPath, additions, dryRun) {
|
|
84816
84953
|
const marker = "# VibeFrame";
|
|
84817
|
-
if (!
|
|
84954
|
+
if (!existsSync44(absPath)) {
|
|
84818
84955
|
if (dryRun) return { path: absPath, status: "would-write" };
|
|
84819
84956
|
await writeFile46(absPath, additions.trimStart(), "utf-8");
|
|
84820
84957
|
return { path: absPath, status: "wrote" };
|
|
84821
84958
|
}
|
|
84822
|
-
const existing = await
|
|
84959
|
+
const existing = await readFile41(absPath, "utf-8");
|
|
84823
84960
|
if (existing.includes(marker)) {
|
|
84824
84961
|
return { path: absPath, status: "skipped-exists", reason: "(already has VibeFrame block)" };
|
|
84825
84962
|
}
|
|
@@ -84852,8 +84989,8 @@ init_storyboard_revise();
|
|
|
84852
84989
|
init_composer_resolve();
|
|
84853
84990
|
init_cost_tier();
|
|
84854
84991
|
init_output();
|
|
84855
|
-
import { readFile as
|
|
84856
|
-
import { existsSync as
|
|
84992
|
+
import { readFile as readFile43, writeFile as writeFile48 } from "node:fs/promises";
|
|
84993
|
+
import { existsSync as existsSync46 } from "node:fs";
|
|
84857
84994
|
import { resolve as resolve58 } from "node:path";
|
|
84858
84995
|
var storyboardCommand = new Command("storyboard").description("Read, validate, and safely mutate STORYBOARD.md cue blocks").addHelpText("after", `
|
|
84859
84996
|
Examples:
|
|
@@ -85036,10 +85173,10 @@ storyboardCommand.command("validate").description("Validate cue blocks and beat
|
|
|
85036
85173
|
});
|
|
85037
85174
|
async function readStoryboard(projectDir) {
|
|
85038
85175
|
const path2 = storyboardPath(projectDir);
|
|
85039
|
-
if (!
|
|
85176
|
+
if (!existsSync46(path2)) {
|
|
85040
85177
|
exitWithError(generalError(`STORYBOARD.md not found at ${path2}`, `Run 'vibe init ${projectDir} --from "<brief>"' first.`));
|
|
85041
85178
|
}
|
|
85042
|
-
return
|
|
85179
|
+
return readFile43(path2, "utf-8");
|
|
85043
85180
|
}
|
|
85044
85181
|
function storyboardPath(projectDir) {
|
|
85045
85182
|
return resolve58(projectDir, "STORYBOARD.md");
|
|
@@ -85051,8 +85188,8 @@ function parseComposer(value) {
|
|
|
85051
85188
|
}
|
|
85052
85189
|
async function readBrief(value, cwd) {
|
|
85053
85190
|
const candidate = resolve58(cwd, value);
|
|
85054
|
-
if (
|
|
85055
|
-
return
|
|
85191
|
+
if (existsSync46(candidate)) {
|
|
85192
|
+
return readFile43(candidate, "utf-8");
|
|
85056
85193
|
}
|
|
85057
85194
|
return value;
|
|
85058
85195
|
}
|
|
@@ -85278,8 +85415,8 @@ init_scene_build();
|
|
|
85278
85415
|
init_build_plan();
|
|
85279
85416
|
init_storyboard_parse();
|
|
85280
85417
|
init_output();
|
|
85281
|
-
import { resolve as resolve66, join as
|
|
85282
|
-
import { readFileSync as readFileSync4, existsSync as
|
|
85418
|
+
import { resolve as resolve66, join as join27 } from "node:path";
|
|
85419
|
+
import { readFileSync as readFileSync4, existsSync as existsSync51 } from "node:fs";
|
|
85283
85420
|
var VALID_MODES2 = ["agent", "batch", "auto"];
|
|
85284
85421
|
var VALID_STAGES2 = ["assets", "compose", "sync", "render", "all"];
|
|
85285
85422
|
var VALID_IMAGE_PROVIDERS2 = ["openai", "gemini", "grok"];
|
|
@@ -85288,7 +85425,10 @@ var VALID_MUSIC_PROVIDERS2 = ["elevenlabs", "replicate"];
|
|
|
85288
85425
|
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(
|
|
85289
85426
|
"--skip-keyframe",
|
|
85290
85427
|
"Don't generate keyframe stills (review keyframes first with --skip-video, then build)"
|
|
85291
|
-
).option("--skip-music", "Don't dispatch music generation even when beats declare music cues").option(
|
|
85428
|
+
).option("--skip-music", "Don't dispatch music generation even when beats declare music cues").option(
|
|
85429
|
+
"--skip-transcript",
|
|
85430
|
+
"Don't transcribe narration for word-sync (default: transcribe when narration + OpenAI key exist)"
|
|
85431
|
+
).option("--skip-render", "Compose only \u2014 don't render to MP4").option("--tts <provider>", "TTS provider: auto|elevenlabs|openai|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(
|
|
85292
85432
|
"after",
|
|
85293
85433
|
`
|
|
85294
85434
|
Examples:
|
|
@@ -85349,6 +85489,7 @@ Advanced equivalent: \`vibe scene build\`.`
|
|
|
85349
85489
|
skipVideo: options.skipVideo ?? false,
|
|
85350
85490
|
skipKeyframe: options.skipKeyframe ?? false,
|
|
85351
85491
|
skipMusic: options.skipMusic ?? false,
|
|
85492
|
+
skipTranscript: options.skipTranscript ?? false,
|
|
85352
85493
|
skipRender: options.skipRender ?? false,
|
|
85353
85494
|
ttsProvider: options.tts,
|
|
85354
85495
|
voice: options.voice,
|
|
@@ -85370,6 +85511,7 @@ Advanced equivalent: \`vibe scene build\`.`
|
|
|
85370
85511
|
skipVideo: options.skipVideo,
|
|
85371
85512
|
skipKeyframe: options.skipKeyframe,
|
|
85372
85513
|
skipMusic: options.skipMusic,
|
|
85514
|
+
skipTranscript: options.skipTranscript,
|
|
85373
85515
|
ttsProvider: options.tts,
|
|
85374
85516
|
voice: options.voice,
|
|
85375
85517
|
imageProvider,
|
|
@@ -85461,6 +85603,7 @@ Advanced equivalent: \`vibe scene build\`.`
|
|
|
85461
85603
|
skipVideo: options.skipVideo,
|
|
85462
85604
|
skipKeyframe: options.skipKeyframe,
|
|
85463
85605
|
skipMusic: options.skipMusic,
|
|
85606
|
+
skipTranscript: options.skipTranscript,
|
|
85464
85607
|
skipRender: options.skipRender,
|
|
85465
85608
|
ttsProvider: options.tts,
|
|
85466
85609
|
voice: options.voice,
|
|
@@ -85473,6 +85616,8 @@ Advanced equivalent: \`vibe scene build\`.`
|
|
|
85473
85616
|
onProgress: (e) => {
|
|
85474
85617
|
if (!spinner2) return;
|
|
85475
85618
|
if (e.type === "phase-start") spinner2.text = `Phase: ${e.phase}...`;
|
|
85619
|
+
else if (e.type === "transcript-generated")
|
|
85620
|
+
spinner2.text = `Transcribed narration: ${e.beatId} (${e.wordCount} words)`;
|
|
85476
85621
|
else if (e.type === "render-done") spinner2.text = `Rendered: ${e.outputPath}`;
|
|
85477
85622
|
}
|
|
85478
85623
|
});
|
|
@@ -85648,8 +85793,8 @@ function buildPlanValidationFailureData(plan) {
|
|
|
85648
85793
|
};
|
|
85649
85794
|
}
|
|
85650
85795
|
function estimateBuildCost(params) {
|
|
85651
|
-
const storyboardPath2 =
|
|
85652
|
-
if (!
|
|
85796
|
+
const storyboardPath2 = join27(params.projectDir, "STORYBOARD.md");
|
|
85797
|
+
if (!existsSync51(storyboardPath2)) return null;
|
|
85653
85798
|
let beats = 0;
|
|
85654
85799
|
try {
|
|
85655
85800
|
const md = readFileSync4(storyboardPath2, "utf-8");
|
|
@@ -86094,7 +86239,7 @@ init_composer_resolve();
|
|
|
86094
86239
|
init_cost_tier();
|
|
86095
86240
|
init_scene_build_mode();
|
|
86096
86241
|
init_output();
|
|
86097
|
-
import { existsSync as
|
|
86242
|
+
import { existsSync as existsSync52 } from "node:fs";
|
|
86098
86243
|
import { resolve as resolve69 } from "node:path";
|
|
86099
86244
|
var COMMAND_KEY_MAP = getCommandKeyMap();
|
|
86100
86245
|
var FREE_COMMANDS = [
|
|
@@ -86222,13 +86367,13 @@ async function runDiagnostics() {
|
|
|
86222
86367
|
installed = false;
|
|
86223
86368
|
}
|
|
86224
86369
|
} else if (toolName === "chrome") {
|
|
86225
|
-
const { existsSync:
|
|
86370
|
+
const { existsSync: existsSync59 } = await import("node:fs");
|
|
86226
86371
|
const { homedir: homedir8 } = await import("node:os");
|
|
86227
|
-
const { join:
|
|
86372
|
+
const { join: join31 } = await import("node:path");
|
|
86228
86373
|
const chromePaths = [
|
|
86229
86374
|
process.env.HYPERFRAMES_CHROME_PATH,
|
|
86230
86375
|
process.env.CHROME_PATH,
|
|
86231
|
-
|
|
86376
|
+
join31(
|
|
86232
86377
|
homedir8(),
|
|
86233
86378
|
".cache",
|
|
86234
86379
|
"puppeteer",
|
|
@@ -86243,7 +86388,7 @@ async function runDiagnostics() {
|
|
|
86243
86388
|
"/usr/bin/chromium",
|
|
86244
86389
|
"/usr/bin/chromium-browser"
|
|
86245
86390
|
];
|
|
86246
|
-
installed = chromePaths.some((p) => p &&
|
|
86391
|
+
installed = chromePaths.some((p) => p && existsSync59(p));
|
|
86247
86392
|
} else {
|
|
86248
86393
|
installed = commandExists(toolName);
|
|
86249
86394
|
}
|
|
@@ -86283,7 +86428,7 @@ async function runDiagnostics() {
|
|
|
86283
86428
|
}
|
|
86284
86429
|
const projectFiles = ["AGENTS.md", "CLAUDE.md", "vibe.project.yaml"].map((rel) => ({
|
|
86285
86430
|
path: rel,
|
|
86286
|
-
exists:
|
|
86431
|
+
exists: existsSync52(resolve69(cwd, rel))
|
|
86287
86432
|
}));
|
|
86288
86433
|
const projectInitialized = projectFiles.some((f) => f.exists);
|
|
86289
86434
|
const nearestProjectConfigPath = await findProjectConfigPath(cwd);
|
|
@@ -86302,8 +86447,8 @@ async function runDiagnostics() {
|
|
|
86302
86447
|
} catch (err) {
|
|
86303
86448
|
if (!(err instanceof ComposerResolveError)) throw err;
|
|
86304
86449
|
}
|
|
86305
|
-
const sceneProjectInCwd =
|
|
86306
|
-
const skillInstalled =
|
|
86450
|
+
const sceneProjectInCwd = existsSync52(resolve69(cwd, "STORYBOARD.md"));
|
|
86451
|
+
const skillInstalled = existsSync52(resolve69(cwd, "SKILL.md"));
|
|
86307
86452
|
return {
|
|
86308
86453
|
system: {
|
|
86309
86454
|
node: { version: nodeVersion, ok: true },
|
|
@@ -86851,7 +86996,7 @@ init_detect();
|
|
|
86851
86996
|
init_ai_edit();
|
|
86852
86997
|
init_output();
|
|
86853
86998
|
import { resolve as resolve71 } from "node:path";
|
|
86854
|
-
import { existsSync as
|
|
86999
|
+
import { existsSync as existsSync53 } from "node:fs";
|
|
86855
87000
|
import { mkdir as mkdir27, rm as rm5 } from "node:fs/promises";
|
|
86856
87001
|
var DEMO_DIR = resolve71(process.cwd(), ".vibeframe-demo");
|
|
86857
87002
|
async function generateTestVideo(outputPath) {
|
|
@@ -86882,7 +87027,7 @@ async function generateTestVideo(outputPath) {
|
|
|
86882
87027
|
"-y",
|
|
86883
87028
|
outputPath
|
|
86884
87029
|
], { timeout: 3e4, maxBuffer: 50 * 1024 * 1024 });
|
|
86885
|
-
return
|
|
87030
|
+
return existsSync53(outputPath);
|
|
86886
87031
|
} catch {
|
|
86887
87032
|
try {
|
|
86888
87033
|
await execSafe("ffmpeg", [
|
|
@@ -86908,7 +87053,7 @@ async function generateTestVideo(outputPath) {
|
|
|
86908
87053
|
"-y",
|
|
86909
87054
|
outputPath
|
|
86910
87055
|
], { timeout: 3e4, maxBuffer: 50 * 1024 * 1024 });
|
|
86911
|
-
return
|
|
87056
|
+
return existsSync53(outputPath);
|
|
86912
87057
|
} catch {
|
|
86913
87058
|
return false;
|
|
86914
87059
|
}
|
|
@@ -87050,8 +87195,8 @@ var demoCommand = new Command("demo").description("Run sample edits on a test vi
|
|
|
87050
87195
|
|
|
87051
87196
|
// src/commands/context.ts
|
|
87052
87197
|
init_esm();
|
|
87053
|
-
import { existsSync as
|
|
87054
|
-
import { readFile as
|
|
87198
|
+
import { existsSync as existsSync54 } from "node:fs";
|
|
87199
|
+
import { readFile as readFile48 } from "node:fs/promises";
|
|
87055
87200
|
import { resolve as resolve72, dirname as dirname25 } from "node:path";
|
|
87056
87201
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
87057
87202
|
var __filename = fileURLToPath2(import.meta.url);
|
|
@@ -87063,7 +87208,7 @@ function resolveContextPath(baseDir = __dirname) {
|
|
|
87063
87208
|
// Source/dev layout: packages/cli/src/commands/context.ts -> packages/cli/CONTEXT.md
|
|
87064
87209
|
resolve72(baseDir, "../../CONTEXT.md")
|
|
87065
87210
|
];
|
|
87066
|
-
return candidates.find((candidate) =>
|
|
87211
|
+
return candidates.find((candidate) => existsSync54(candidate)) ?? null;
|
|
87067
87212
|
}
|
|
87068
87213
|
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) => {
|
|
87069
87214
|
const options = {
|
|
@@ -87169,7 +87314,7 @@ var contextCommand = new Command("context").description("Print CLI context/guide
|
|
|
87169
87314
|
try {
|
|
87170
87315
|
const contextPath = resolveContextPath();
|
|
87171
87316
|
if (!contextPath) throw new Error("CONTEXT.md not found");
|
|
87172
|
-
const content = await
|
|
87317
|
+
const content = await readFile48(contextPath, "utf-8");
|
|
87173
87318
|
console.log(
|
|
87174
87319
|
content.replace(
|
|
87175
87320
|
"## Mental model",
|