@vibeframe/mcp-server 0.114.0 → 0.115.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +1102 -855
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -4871,11 +4871,11 @@ async function loadKokoroRuntime() {
4871
4871
  }
4872
4872
  async function loadKokoroFromWorkspace(baseDir = process.cwd()) {
4873
4873
  try {
4874
- const { readFile: readFile50 } = await import("node:fs/promises");
4874
+ const { readFile: readFile48 } = await import("node:fs/promises");
4875
4875
  const { join: join78, resolve: resolve85 } = await import("node:path");
4876
4876
  const { pathToFileURL: pathToFileURL2 } = await import("node:url");
4877
4877
  const pkgDir = resolve85(baseDir, "node_modules", "kokoro-js");
4878
- const pkg = JSON.parse(await readFile50(join78(pkgDir, "package.json"), "utf-8"));
4878
+ const pkg = JSON.parse(await readFile48(join78(pkgDir, "package.json"), "utf-8"));
4879
4879
  const entry = pkg.exports?.node?.import ?? pkg.exports?.default ?? pkg.module ?? pkg.main;
4880
4880
  if (!entry)
4881
4881
  return null;
@@ -4890,7 +4890,7 @@ async function loadBundledKokoroRuntime(runtimeDir = process.env.VIBE_KOKORO_RUN
4890
4890
  if (!base2)
4891
4891
  return null;
4892
4892
  try {
4893
- const { readFile: readFile50, writeFile: writeFile50, mkdir: mkdir29 } = await import("node:fs/promises");
4893
+ const { readFile: readFile48, writeFile: writeFile51, mkdir: mkdir30 } = await import("node:fs/promises");
4894
4894
  const { join: join78, resolve: resolve85 } = await import("node:path");
4895
4895
  const { pathToFileURL: pathToFileURL2 } = await import("node:url");
4896
4896
  const { homedir: homedir8 } = await import("node:os");
@@ -4903,7 +4903,7 @@ async function loadBundledKokoroRuntime(runtimeDir = process.env.VIBE_KOKORO_RUN
4903
4903
  transformers.env.customCache = {
4904
4904
  async match(key2) {
4905
4905
  try {
4906
- return new Response(await readFile50(join78(cacheRoot, encodeURIComponent(String(key2)))));
4906
+ return new Response(await readFile48(join78(cacheRoot, encodeURIComponent(String(key2)))));
4907
4907
  } catch {
4908
4908
  return void 0;
4909
4909
  }
@@ -4911,8 +4911,8 @@ async function loadBundledKokoroRuntime(runtimeDir = process.env.VIBE_KOKORO_RUN
4911
4911
  async put(key2, response) {
4912
4912
  try {
4913
4913
  const buf = Buffer.from(await response.arrayBuffer());
4914
- await mkdir29(cacheRoot, { recursive: true });
4915
- await writeFile50(join78(cacheRoot, encodeURIComponent(String(key2))), buf);
4914
+ await mkdir30(cacheRoot, { recursive: true });
4915
+ await writeFile51(join78(cacheRoot, encodeURIComponent(String(key2))), buf);
4916
4916
  } catch {
4917
4917
  }
4918
4918
  }
@@ -21945,12 +21945,29 @@ function parseBeatDuration(body) {
21945
21945
  const n2 = parseFloat(valueMatch[1]);
21946
21946
  return Number.isFinite(n2) && n2 > 0 ? n2 : void 0;
21947
21947
  }
21948
- var import_yaml3, HEADING_RE, BEAT_PREFIX_RE, DURATION_SUBSECTION_RE, DURATION_VALUE_RE, BEAT_CUES_RE;
21948
+ var import_yaml3, STORYBOARD_CUE_KEYS, HEADING_RE, BEAT_PREFIX_RE, DURATION_SUBSECTION_RE, DURATION_VALUE_RE, BEAT_CUES_RE;
21949
21949
  var init_storyboard_parse = __esm({
21950
21950
  "../cli/src/commands/_shared/storyboard-parse.ts"() {
21951
21951
  "use strict";
21952
21952
  import_yaml3 = __toESM(require_dist3(), 1);
21953
21953
  init_frontmatter();
21954
+ STORYBOARD_CUE_KEYS = [
21955
+ "duration",
21956
+ "narration",
21957
+ "backdrop",
21958
+ "video",
21959
+ "keyframe",
21960
+ "motion",
21961
+ "voice",
21962
+ "music",
21963
+ "asset",
21964
+ "characters",
21965
+ "eyebrow",
21966
+ "title",
21967
+ "caption",
21968
+ "kicker",
21969
+ "sub"
21970
+ ];
21954
21971
  HEADING_RE = /^##\s+(.+?)\s*$/gm;
21955
21972
  BEAT_PREFIX_RE = /^Beat\s+(.+?)\s+(?:—|:|-)\s+(.+)$/i;
21956
21973
  DURATION_SUBSECTION_RE = /###\s+Beat\s+duration\s*\n([\s\S]*?)(?=\n###\s|\n##\s|$)/i;
@@ -21959,22 +21976,509 @@ var init_storyboard_parse = __esm({
21959
21976
  }
21960
21977
  });
21961
21978
 
21979
+ // ../cli/src/commands/_shared/storyboard-edit.ts
21980
+ function validateStoryboardMarkdown(markdown) {
21981
+ const parsed = parseStoryboard(markdown);
21982
+ const sections = splitBeatSections(markdown);
21983
+ const issues = [];
21984
+ if (parsed.beats.length === 0) {
21985
+ issues.push({
21986
+ severity: "error",
21987
+ code: "NO_BEATS",
21988
+ message: "STORYBOARD.md must contain at least one `## Beat ...` heading."
21989
+ });
21990
+ }
21991
+ const seen = /* @__PURE__ */ new Map();
21992
+ for (const beat of parsed.beats) {
21993
+ seen.set(beat.id, (seen.get(beat.id) ?? 0) + 1);
21994
+ }
21995
+ for (const [id, count] of seen.entries()) {
21996
+ if (count > 1) {
21997
+ issues.push({
21998
+ severity: "error",
21999
+ code: "DUPLICATE_BEAT_ID",
22000
+ beatId: id,
22001
+ message: `Beat id "${id}" appears ${count} times. Beat ids must be unique.`
22002
+ });
22003
+ }
22004
+ }
22005
+ for (const section of sections) {
22006
+ const cueBlock = readLeadingCueBlock(section.body);
22007
+ if (cueBlock?.error) {
22008
+ issues.push({
22009
+ severity: "error",
22010
+ code: "MALFORMED_CUE_YAML",
22011
+ beatId: section.id,
22012
+ message: `Beat "${section.id}" has malformed YAML cues: ${cueBlock.error}`
22013
+ });
22014
+ }
22015
+ }
22016
+ const characterPool = new Set(
22017
+ Object.keys(
22018
+ parsed.frontmatter?.characters ?? {}
22019
+ )
22020
+ );
22021
+ for (const beat of parsed.beats) {
22022
+ const cues = beat.cues ?? {};
22023
+ for (const [key2, value] of Object.entries(cues)) {
22024
+ if (!ALLOWED_CUE_KEYS.has(key2)) {
22025
+ issues.push({
22026
+ severity: "warning",
22027
+ code: "UNKNOWN_CUE",
22028
+ beatId: beat.id,
22029
+ message: `Beat "${beat.id}" uses unknown cue "${key2}". Supported cues: ${STORYBOARD_CUE_KEYS.join(", ")}.`
22030
+ });
22031
+ continue;
22032
+ }
22033
+ if (key2 === "characters") {
22034
+ const validShape = typeof value === "string" || Array.isArray(value) && value.every((v4) => typeof v4 === "string");
22035
+ if (!validShape) {
22036
+ issues.push({
22037
+ severity: "error",
22038
+ code: "INVALID_CHARACTERS_VALUE",
22039
+ beatId: beat.id,
22040
+ message: `Beat "${beat.id}" cue "characters" must be a character name or a list of names.`
22041
+ });
22042
+ continue;
22043
+ }
22044
+ for (const name of beatCharacterNames({ characters: value })) {
22045
+ if (!characterPool.has(name)) {
22046
+ issues.push({
22047
+ severity: "warning",
22048
+ code: "UNKNOWN_CHARACTER",
22049
+ beatId: beat.id,
22050
+ message: `Beat "${beat.id}" references character "${name}" not defined in the frontmatter \`characters:\` pool.`
22051
+ });
22052
+ }
22053
+ }
22054
+ continue;
22055
+ }
22056
+ if (key2 === "duration") {
22057
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
22058
+ issues.push({
22059
+ severity: "error",
22060
+ code: "INVALID_DURATION",
22061
+ beatId: beat.id,
22062
+ message: `Beat "${beat.id}" cue "duration" must be a positive number of seconds.`
22063
+ });
22064
+ }
22065
+ continue;
22066
+ }
22067
+ if (STRING_CUE_KEYS.has(key2) && typeof value !== "string") {
22068
+ issues.push({
22069
+ severity: "error",
22070
+ code: "INVALID_CUE_VALUE",
22071
+ beatId: beat.id,
22072
+ message: `Beat "${beat.id}" cue "${key2}" must be a string.`
22073
+ });
22074
+ }
22075
+ }
22076
+ }
22077
+ for (const beat of parsed.beats) {
22078
+ if (beat.duration !== void 0 && beat.duration > MAX_RECOMMENDED_BEAT_SEC) {
22079
+ issues.push({
22080
+ severity: "warning",
22081
+ code: "BEAT_DURATION_TOO_LONG",
22082
+ beatId: beat.id,
22083
+ message: `Beat "${beat.id}" is ${beat.duration}s \u2014 beats longer than ${MAX_RECOMMENDED_BEAT_SEC}s render static and overstuffed. Split it into 6-15s beats (a 90s video should have 6-8 beats).`
22084
+ });
22085
+ }
22086
+ }
22087
+ return {
22088
+ ok: !issues.some((i3) => i3.severity === "error"),
22089
+ beats: parsed.beats,
22090
+ issues
22091
+ };
22092
+ }
22093
+ function getStoryboardBeat(markdown, beatId) {
22094
+ return parseStoryboard(markdown).beats.find((beat) => beat.id === beatId) ?? null;
22095
+ }
22096
+ function upsertStoryboardBeat(markdown, opts) {
22097
+ const beatId = normalizeBeatId(opts.beatId);
22098
+ const sections = splitBeatSections(markdown);
22099
+ const existing = sections.find((section2) => section2.id === beatId);
22100
+ const cues = storyboardCuesFromInput(opts);
22101
+ if (existing) {
22102
+ let next = markdown;
22103
+ for (const [key2, value] of Object.entries(cues)) {
22104
+ next = setStoryboardCue(next, { beatId, key: key2, value });
22105
+ }
22106
+ return { markdown: next, action: "updated" };
22107
+ }
22108
+ const section = buildStoryboardBeatSection({ ...opts, beatId, cues });
22109
+ if (sections.length > 0 && isStarterStoryboard(markdown)) {
22110
+ const global3 = markdown.slice(0, sections[0].start).trimEnd();
22111
+ return {
22112
+ markdown: `${global3}
22113
+
22114
+ ${section}`,
22115
+ action: "replaced-starter"
22116
+ };
22117
+ }
22118
+ const prefix = markdown.trimEnd();
22119
+ return {
22120
+ markdown: `${prefix}${prefix ? "\n\n" : ""}${section}`,
22121
+ action: "appended"
22122
+ };
22123
+ }
22124
+ function setStoryboardCue(markdown, opts) {
22125
+ const section = findBeatSection(markdown, opts.beatId);
22126
+ if (!section) {
22127
+ throw new Error(`Beat "${opts.beatId}" not found.`);
22128
+ }
22129
+ if (!ALLOWED_CUE_KEYS.has(opts.key)) {
22130
+ throw new Error(`Unsupported cue "${opts.key}". Supported cues: ${STORYBOARD_CUE_KEYS.join(", ")}.`);
22131
+ }
22132
+ const cue = readLeadingCueBlock(section.body);
22133
+ if (cue?.error) {
22134
+ throw new Error(`Cannot edit malformed YAML cues for beat "${opts.beatId}": ${cue.error}`);
22135
+ }
22136
+ const cues = cue?.value ? { ...cue.value } : {};
22137
+ if (opts.unset) {
22138
+ delete cues[opts.key];
22139
+ } else {
22140
+ cues[opts.key] = normalizeCueValue(opts.key, opts.value);
22141
+ }
22142
+ const remainingBody = cue ? section.body.slice(cue.full.length).replace(/^\s*\n/, "") : section.body.replace(/^\s*\n/, "");
22143
+ const cueBlock = Object.keys(cues).length > 0 ? "```yaml\n" + (0, import_yaml4.stringify)(cues, { lineWidth: 0 }).trimEnd() + "\n```\n\n" : "";
22144
+ const nextBody = cueBlock + remainingBody.trimStart();
22145
+ const nextSection = section.raw.slice(0, section.headingEnd - section.start) + "\n\n" + nextBody.trimEnd() + "\n";
22146
+ return markdown.slice(0, section.start) + nextSection + markdown.slice(section.end);
22147
+ }
22148
+ function moveStoryboardBeat(markdown, opts) {
22149
+ if (opts.beatId === opts.afterBeatId) return markdown;
22150
+ const sections = splitBeatSections(markdown);
22151
+ const movingIndex = sections.findIndex((section) => section.id === opts.beatId);
22152
+ if (movingIndex === -1) throw new Error(`Beat "${opts.beatId}" not found.`);
22153
+ const afterIndex = sections.findIndex((section) => section.id === opts.afterBeatId);
22154
+ if (afterIndex === -1) throw new Error(`Beat "${opts.afterBeatId}" not found.`);
22155
+ const global3 = sections.length > 0 ? markdown.slice(0, sections[0].start) : markdown;
22156
+ const chunks = sections.map((section) => section.raw);
22157
+ const [moving] = chunks.splice(movingIndex, 1);
22158
+ const adjustedAfterIndex = movingIndex < afterIndex ? afterIndex - 1 : afterIndex;
22159
+ chunks.splice(adjustedAfterIndex + 1, 0, moving);
22160
+ return global3 + normalizeBeatChunks(chunks);
22161
+ }
22162
+ function normalizeBeatChunks(chunks) {
22163
+ return chunks.map((chunk) => chunk.trim()).filter(Boolean).join("\n\n") + "\n";
22164
+ }
22165
+ function normalizeBeatId(id) {
22166
+ return deriveBeatId(id);
22167
+ }
22168
+ function storyboardCuesFromInput(opts) {
22169
+ const cues = {};
22170
+ if (opts.duration !== void 0) cues.duration = opts.duration;
22171
+ if (opts.narration && opts.narration.trim()) cues.narration = opts.narration.trim();
22172
+ if (opts.backdrop && opts.backdrop.trim()) cues.backdrop = opts.backdrop.trim();
22173
+ return cues;
22174
+ }
22175
+ function buildStoryboardBeatSection(opts) {
22176
+ const title = normalizeTitle(opts.title ?? humanizeBeatId(opts.beatId));
22177
+ const cueBlock = Object.keys(opts.cues).length > 0 ? "```yaml\n" + (0, import_yaml4.stringify)(opts.cues, { lineWidth: 0 }).trimEnd() + "\n```\n\n" : "";
22178
+ const body = opts.body?.trim() || "Scene created with `vibe scene add`. Edit this body if the build flow should recompose it.";
22179
+ return `## Beat ${opts.beatId} - ${title}
22180
+
22181
+ ${cueBlock}${body.trimEnd()}
22182
+ `;
22183
+ }
22184
+ function normalizeTitle(value) {
22185
+ return value.replace(/\s+/g, " ").trim() || "Scene";
22186
+ }
22187
+ function humanizeBeatId(id) {
22188
+ return id.split(/[-_]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ") || "Scene";
22189
+ }
22190
+ function isStarterStoryboard(markdown) {
22191
+ const parsed = parseStoryboard(markdown);
22192
+ if (parsed.beats.length !== 3) return false;
22193
+ const ids = parsed.beats.map((beat) => beat.id);
22194
+ if (ids.join(",") !== "hook,proof,close") return false;
22195
+ return markdown.includes("Introduce the promise in one crisp sentence.") && markdown.includes("Show the mechanism or proof point that makes the promise believable.") && markdown.includes("Close with the action the viewer should remember.");
22196
+ }
22197
+ function normalizeCueValue(key2, value) {
22198
+ if (key2 === "duration") {
22199
+ const n2 = typeof value === "number" ? value : Number.parseFloat(String(value ?? ""));
22200
+ if (!Number.isFinite(n2) || n2 <= 0) {
22201
+ throw new Error("Cue duration must be a positive number.");
22202
+ }
22203
+ return n2;
22204
+ }
22205
+ if (typeof value !== "string") return value;
22206
+ return value.trim();
22207
+ }
22208
+ function splitBeatSections(markdown) {
22209
+ const normalized = markdown.replace(/\r\n/g, "\n");
22210
+ const headings = [];
22211
+ HEADING_RE2.lastIndex = 0;
22212
+ let match2;
22213
+ while ((match2 = HEADING_RE2.exec(normalized)) !== null) {
22214
+ headings.push({ start: match2.index, end: match2.index + match2[0].length, line: match2[1].trim() });
22215
+ }
22216
+ return headings.map((heading, index) => {
22217
+ const end = index + 1 < headings.length ? headings[index + 1].start : normalized.length;
22218
+ return {
22219
+ id: deriveBeatId(heading.line),
22220
+ heading: heading.line,
22221
+ start: heading.start,
22222
+ headingEnd: heading.end,
22223
+ end,
22224
+ raw: normalized.slice(heading.start, end),
22225
+ body: normalized.slice(heading.end, end).trim()
22226
+ };
22227
+ });
22228
+ }
22229
+ function findBeatSection(markdown, beatId) {
22230
+ return splitBeatSections(markdown).find((section) => section.id === beatId) ?? null;
22231
+ }
22232
+ function readLeadingCueBlock(body) {
22233
+ const match2 = body.match(LEADING_CUE_RE);
22234
+ if (!match2) return null;
22235
+ try {
22236
+ const parsed = (0, import_yaml4.parse)(match2[2]);
22237
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
22238
+ return { full: match2[0], error: "cue block must parse to a YAML object" };
22239
+ }
22240
+ return { full: match2[0], value: parsed };
22241
+ } catch (error) {
22242
+ return { full: match2[0], error: error instanceof Error ? error.message : String(error) };
22243
+ }
22244
+ }
22245
+ var import_yaml4, HEADING_RE2, LEADING_CUE_RE, ALLOWED_CUE_KEYS, STRING_CUE_KEYS, MAX_RECOMMENDED_BEAT_SEC;
22246
+ var init_storyboard_edit = __esm({
22247
+ "../cli/src/commands/_shared/storyboard-edit.ts"() {
22248
+ "use strict";
22249
+ import_yaml4 = __toESM(require_dist3(), 1);
22250
+ init_storyboard_parse();
22251
+ init_storyboard_parse();
22252
+ HEADING_RE2 = /^##\s+(.+?)\s*$/gm;
22253
+ LEADING_CUE_RE = /^(\s*)```ya?ml\s*\n([\s\S]*?)\n```\s*(?:\n|$)/;
22254
+ ALLOWED_CUE_KEYS = new Set(STORYBOARD_CUE_KEYS);
22255
+ STRING_CUE_KEYS = /* @__PURE__ */ new Set(["narration", "backdrop", "video", "keyframe", "motion", "voice", "music", "asset", "eyebrow", "title", "caption", "kicker", "sub"]);
22256
+ MAX_RECOMMENDED_BEAT_SEC = 15;
22257
+ }
22258
+ });
22259
+
22260
+ // ../cli/src/commands/_shared/storyboard-source.ts
22261
+ import { existsSync as existsSync2 } from "node:fs";
22262
+ import { mkdir as mkdir3, readdir, readFile as readFile4, rename, rm, writeFile as writeFile6 } from "node:fs/promises";
22263
+ import { basename, join as join3 } from "node:path";
22264
+ function sceneFileFrom(dir, filename) {
22265
+ const stem = filename.replace(/\.mdx?$/i, "");
22266
+ const match2 = stem.match(ORDER_PREFIX_RE);
22267
+ const order = match2 ? Number.parseInt(match2[1], 10) : null;
22268
+ const idSource = match2 ? match2[2] : stem;
22269
+ return { id: deriveBeatId(idSource), path: join3(dir, filename), filename, order };
22270
+ }
22271
+ async function listSceneFiles(projectDir) {
22272
+ const dir = join3(projectDir, SCENES_DIRNAME);
22273
+ if (!existsSync2(dir)) return [];
22274
+ const entries2 = await readdir(dir, { withFileTypes: true });
22275
+ const files = entries2.filter((e2) => e2.isFile() && /\.mdx?$/i.test(e2.name) && !e2.name.startsWith("_")).map((e2) => sceneFileFrom(dir, e2.name));
22276
+ return files.sort((a4, b4) => {
22277
+ if (a4.order !== null && b4.order !== null) return a4.order - b4.order;
22278
+ if (a4.order !== null) return -1;
22279
+ if (b4.order !== null) return 1;
22280
+ return a4.filename.localeCompare(b4.filename);
22281
+ });
22282
+ }
22283
+ function detectStoryboardLayout(projectDir, sceneCount) {
22284
+ if (sceneCount > 0) return "bundle";
22285
+ if (existsSync2(join3(projectDir, STORYBOARD_FILENAME))) return "single";
22286
+ return "missing";
22287
+ }
22288
+ function humanise(id) {
22289
+ return id.split(/[-_]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ") || "Scene";
22290
+ }
22291
+ function sceneToBeatSection(scene, contents) {
22292
+ const { data: data2, body } = extractFrontmatter(contents);
22293
+ const cues = { ...data2 ?? {} };
22294
+ delete cues.type;
22295
+ const cueBlock = Object.keys(cues).length > 0 ? "```yaml\n" + (0, import_yaml5.stringify)(cues, { lineWidth: 0 }).trimEnd() + "\n```\n\n" : "";
22296
+ const prose = body.trim();
22297
+ return `## Beat ${scene.id} - ${humanise(scene.id)}
22298
+
22299
+ ${cueBlock}${prose}
22300
+ `;
22301
+ }
22302
+ async function loadStoryboard(projectDir) {
22303
+ const storyboardPath = join3(projectDir, STORYBOARD_FILENAME);
22304
+ const scenes = await listSceneFiles(projectDir);
22305
+ const layout = detectStoryboardLayout(projectDir, scenes.length);
22306
+ if (layout === "missing") {
22307
+ return { layout, markdown: "", scenes: [], storyboardPath, strayBeatIds: [] };
22308
+ }
22309
+ const head = existsSync2(storyboardPath) ? await readFile4(storyboardPath, "utf-8") : "";
22310
+ if (layout === "single") {
22311
+ return { layout, markdown: head, scenes: [], storyboardPath, strayBeatIds: [] };
22312
+ }
22313
+ const headingAt = head.search(FIRST_HEADING_RE);
22314
+ const strayBeatIds = headingAt === -1 ? [] : parseStoryboard(head.slice(headingAt)).beats.map((b4) => b4.id);
22315
+ const preamble = headingAt === -1 ? head : head.slice(0, headingAt);
22316
+ const sections = await Promise.all(
22317
+ scenes.map(async (scene) => sceneToBeatSection(scene, await readFile4(scene.path, "utf-8")))
22318
+ );
22319
+ const prefix = preamble.trimEnd();
22320
+ return {
22321
+ layout,
22322
+ markdown: `${prefix}${prefix ? "\n\n" : ""}${sections.join("\n")}`,
22323
+ scenes,
22324
+ storyboardPath,
22325
+ strayBeatIds
22326
+ };
22327
+ }
22328
+ async function loadStoryboardMarkdown(projectDir) {
22329
+ return (await loadStoryboard(projectDir)).markdown;
22330
+ }
22331
+ function sceneFilename(order, id) {
22332
+ const width = order >= 100 ? 3 : 2;
22333
+ return `${String(order).padStart(width, "0")}-${basename(id)}.md`;
22334
+ }
22335
+ function beatToSceneFile(beat, order) {
22336
+ const cues = { type: "Scene", ...beat.cues ?? {} };
22337
+ if (cues.duration === void 0 && beat.duration !== void 0) {
22338
+ cues.duration = beat.duration;
22339
+ }
22340
+ const front = (0, import_yaml5.stringify)(cues, { lineWidth: 0 }).trimEnd();
22341
+ const body = beat.body.trim();
22342
+ return {
22343
+ path: `${SCENES_DIRNAME}/${sceneFilename(order, beat.id)}`,
22344
+ contents: `---
22345
+ ${front}
22346
+ ---
22347
+
22348
+ ${body}
22349
+ `
22350
+ };
22351
+ }
22352
+ function planMigration(markdown) {
22353
+ const parsed = parseStoryboard(markdown);
22354
+ const data2 = extractFrontmatter(markdown).data ?? {};
22355
+ const scenes = parsed.beats.map((beat, index) => beatToSceneFile(beat, index + 1));
22356
+ const frontBlock = Object.keys(data2).length > 0 ? `---
22357
+ ${(0, import_yaml5.stringify)(data2, { lineWidth: 0 }).trimEnd()}
22358
+ ---
22359
+
22360
+ ` : "";
22361
+ const prose = parsed.global.trim();
22362
+ const note = "Scenes live in `scenes/`, one file per scene, and play in filename order.";
22363
+ const storyboardBody = [prose, note].filter(Boolean).join("\n\n");
22364
+ return {
22365
+ scenes,
22366
+ storyboard: {
22367
+ path: STORYBOARD_FILENAME,
22368
+ contents: `${frontBlock}${storyboardBody}
22369
+ `
22370
+ },
22371
+ beatIds: parsed.beats.map((beat) => beat.id)
22372
+ };
22373
+ }
22374
+ async function writeMigration(projectDir, plan2) {
22375
+ await mkdir3(join3(projectDir, SCENES_DIRNAME), { recursive: true });
22376
+ for (const file of plan2.scenes) {
22377
+ await writeFile6(join3(projectDir, file.path), file.contents, "utf-8");
22378
+ }
22379
+ await writeFile6(join3(projectDir, plan2.storyboard.path), plan2.storyboard.contents, "utf-8");
22380
+ }
22381
+ async function writeStoryboardAsBundle(projectDir, markdown) {
22382
+ const plan2 = planMigration(markdown);
22383
+ await writeMigration(projectDir, plan2);
22384
+ return plan2;
22385
+ }
22386
+ function strayBeatIssues(strayBeatIds) {
22387
+ return strayBeatIds.map((beatId) => ({
22388
+ severity: "error",
22389
+ code: "STRAY_BEATS_IN_STORYBOARD",
22390
+ beatId,
22391
+ message: `STORYBOARD.md still contains a "## Beat ${beatId}" section, but this project uses scenes/. That heading is ignored. Move it to scenes/ as its own file, or delete it from STORYBOARD.md.`
22392
+ }));
22393
+ }
22394
+ async function upsertScene(projectDir, opts) {
22395
+ const beatId = deriveBeatId(opts.beatId);
22396
+ const dir = join3(projectDir, SCENES_DIRNAME);
22397
+ await mkdir3(dir, { recursive: true });
22398
+ const cues = {};
22399
+ if (opts.duration !== void 0) cues.duration = opts.duration;
22400
+ if (opts.narration?.trim()) cues.narration = opts.narration.trim();
22401
+ if (opts.backdrop?.trim()) cues.backdrop = opts.backdrop.trim();
22402
+ if (opts.title?.trim()) cues.title = opts.title.trim();
22403
+ const scenes = await listSceneFiles(projectDir);
22404
+ const existing = scenes.find((s2) => s2.id === beatId);
22405
+ if (existing) {
22406
+ const raw2 = await readFile4(existing.path, "utf-8");
22407
+ const { data: data2, body: body2 } = extractFrontmatter(raw2);
22408
+ const merged = { type: "Scene", ...data2 ?? {}, ...cues };
22409
+ const nextBody = opts.body?.trim() || body2.trim();
22410
+ await writeFile6(
22411
+ existing.path,
22412
+ `---
22413
+ ${(0, import_yaml5.stringify)(merged, { lineWidth: 0 }).trimEnd()}
22414
+ ---
22415
+
22416
+ ${nextBody}
22417
+ `,
22418
+ "utf-8"
22419
+ );
22420
+ return { action: "updated", path: existing.path };
22421
+ }
22422
+ const starter = scenes.length > 0 && isStarterStoryboard((await loadStoryboard(projectDir)).markdown);
22423
+ if (starter) {
22424
+ for (const scene of scenes) await rm(scene.path);
22425
+ }
22426
+ const order = starter ? 1 : scenes.length + 1;
22427
+ const path16 = join3(dir, sceneFilename(order, beatId));
22428
+ const front = { type: "Scene", ...cues };
22429
+ const body = opts.body?.trim() || "Scene created with `vibe scene add`. Edit this body if the build flow should recompose it.";
22430
+ await writeFile6(
22431
+ path16,
22432
+ `---
22433
+ ${(0, import_yaml5.stringify)(front, { lineWidth: 0 }).trimEnd()}
22434
+ ---
22435
+
22436
+ ${body}
22437
+ `,
22438
+ "utf-8"
22439
+ );
22440
+ return { action: starter ? "replaced-starter" : "appended", path: path16 };
22441
+ }
22442
+ async function rewriteBundleFromMarkdown(projectDir, markdown) {
22443
+ const plan2 = planMigration(markdown);
22444
+ const keep = new Set(plan2.scenes.map((file) => basename(file.path)));
22445
+ for (const scene of await listSceneFiles(projectDir)) {
22446
+ if (!keep.has(scene.filename)) await rm(scene.path);
22447
+ }
22448
+ await writeMigration(projectDir, plan2);
22449
+ return plan2;
22450
+ }
22451
+ var import_yaml5, SCENES_DIRNAME, STORYBOARD_FILENAME, ORDER_PREFIX_RE, FIRST_HEADING_RE;
22452
+ var init_storyboard_source = __esm({
22453
+ "../cli/src/commands/_shared/storyboard-source.ts"() {
22454
+ "use strict";
22455
+ import_yaml5 = __toESM(require_dist3(), 1);
22456
+ init_frontmatter();
22457
+ init_storyboard_parse();
22458
+ init_storyboard_edit();
22459
+ SCENES_DIRNAME = "scenes";
22460
+ STORYBOARD_FILENAME = "STORYBOARD.md";
22461
+ ORDER_PREFIX_RE = /^(\d+)[-_.]\s*(.+)$/;
22462
+ FIRST_HEADING_RE = /^## .*/m;
22463
+ }
22464
+ });
22465
+
21962
22466
  // ../cli/src/commands/_shared/status-jobs.ts
21963
22467
  import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
21964
- import { existsSync as existsSync2, statSync } from "node:fs";
21965
- import { copyFile, mkdir as mkdir3, readFile as readFile4, readdir, rename, stat, writeFile as writeFile6 } from "node:fs/promises";
21966
- import { dirname as dirname3, join as join3, parse as parse2, relative, resolve as resolve5 } from "node:path";
22468
+ import { existsSync as existsSync3, statSync } from "node:fs";
22469
+ import { copyFile, mkdir as mkdir4, readFile as readFile5, readdir as readdir2, rename as rename2, stat, writeFile as writeFile7 } from "node:fs/promises";
22470
+ import { dirname as dirname3, join as join4, parse as parse2, relative, resolve as resolve5 } from "node:path";
21967
22471
  function isLocalJob(record) {
21968
22472
  return record.jobType === "build" || record.jobType === "render";
21969
22473
  }
21970
22474
  function findProjectRoot(start = process.cwd()) {
21971
22475
  let dir = resolve5(start);
21972
22476
  try {
21973
- if (existsSync2(dir) && statSync(dir).isFile()) dir = dirname3(dir);
22477
+ if (existsSync3(dir) && statSync(dir).isFile()) dir = dirname3(dir);
21974
22478
  } catch {
21975
22479
  }
21976
22480
  for (; ; ) {
21977
- if (existsSync2(join3(dir, "vibe.config.json")) || existsSync2(join3(dir, "STORYBOARD.md"))) {
22481
+ if (existsSync3(join4(dir, "vibe.config.json")) || existsSync3(join4(dir, "STORYBOARD.md"))) {
21978
22482
  return dir;
21979
22483
  }
21980
22484
  const parent = dirname3(dir);
@@ -21983,7 +22487,7 @@ function findProjectRoot(start = process.cwd()) {
21983
22487
  }
21984
22488
  }
21985
22489
  function jobsDir(projectDir) {
21986
- return join3(resolve5(projectDir), ".vibeframe", "jobs");
22490
+ return join4(resolve5(projectDir), ".vibeframe", "jobs");
21987
22491
  }
21988
22492
  function normalizeJobStatus(status) {
21989
22493
  const value = String(status ?? "").toLowerCase();
@@ -22044,28 +22548,28 @@ async function createAndWriteJobRecord(opts) {
22044
22548
  }
22045
22549
  async function writeJobRecord(record) {
22046
22550
  const dir = jobsDir(record.projectDir);
22047
- await mkdir3(dir, { recursive: true });
22551
+ await mkdir4(dir, { recursive: true });
22048
22552
  const path16 = jobRecordPath(record.id, record.projectDir);
22049
22553
  const tmp = `${path16}.${randomUUID2()}.tmp`;
22050
- await writeFile6(tmp, JSON.stringify(stripUndefined2(record), null, 2) + "\n", "utf-8");
22051
- await rename(tmp, path16);
22554
+ await writeFile7(tmp, JSON.stringify(stripUndefined2(record), null, 2) + "\n", "utf-8");
22555
+ await rename2(tmp, path16);
22052
22556
  }
22053
22557
  async function readJobRecord(jobId, projectDir) {
22054
22558
  const id = normalizeJobId(jobId);
22055
22559
  const project = resolve5(projectDir ?? findProjectRoot());
22056
22560
  const path16 = jobRecordPath(id, project);
22057
- if (!existsSync2(path16)) return null;
22058
- return parseJobRecord(await readFile4(path16, "utf-8"));
22561
+ if (!existsSync3(path16)) return null;
22562
+ return parseJobRecord(await readFile5(path16, "utf-8"));
22059
22563
  }
22060
22564
  async function listJobRecords(projectDir) {
22061
22565
  const dir = jobsDir(projectDir);
22062
- if (!existsSync2(dir)) return [];
22063
- const entries2 = await readdir(dir, { withFileTypes: true });
22566
+ if (!existsSync3(dir)) return [];
22567
+ const entries2 = await readdir2(dir, { withFileTypes: true });
22064
22568
  const records = [];
22065
22569
  for (const entry of entries2) {
22066
22570
  if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
22067
22571
  try {
22068
- const parsed = parseJobRecord(await readFile4(join3(dir, entry.name), "utf-8"));
22572
+ const parsed = parseJobRecord(await readFile5(join4(dir, entry.name), "utf-8"));
22069
22573
  if (parsed) records.push(parsed);
22070
22574
  } catch {
22071
22575
  }
@@ -22092,7 +22596,7 @@ async function refreshJobRecord(record, opts = {}) {
22092
22596
  try {
22093
22597
  if (record.jobType === "generate-video") {
22094
22598
  const output3 = resolveRefreshOutput(record, opts);
22095
- if (output3) await mkdir3(dirname3(output3), { recursive: true });
22599
+ if (output3) await mkdir4(dirname3(output3), { recursive: true });
22096
22600
  const result = await executeVideoStatus({
22097
22601
  taskId: record.providerTaskId,
22098
22602
  provider: record.provider,
@@ -22159,7 +22663,7 @@ async function refreshLocalJobRecord(record, opts) {
22159
22663
  const heartbeatAge = record.heartbeatAt ? Date.now() - new Date(record.heartbeatAt).getTime() : Number.POSITIVE_INFINITY;
22160
22664
  const orphaned = !isPidAlive(record.pid) || heartbeatAge > LOCAL_JOB_ORPHAN_AFTER_MS;
22161
22665
  if (orphaned) {
22162
- const hint = `The process that ran this ${record.jobType} job is gone. Check ${join3(record.projectDir, record.jobType === "render" ? "render-report.json" : "build-report.json")} \u2014 the work may have completed before the process exited.`;
22666
+ const hint = `The process that ran this ${record.jobType} job is gone. Check ${join4(record.projectDir, record.jobType === "render" ? "render-report.json" : "build-report.json")} \u2014 the work may have completed before the process exited.`;
22163
22667
  warnings.push(hint);
22164
22668
  updated = stripUndefined2({
22165
22669
  ...record,
@@ -22322,7 +22826,7 @@ function normalizeJobId(jobId) {
22322
22826
  return id;
22323
22827
  }
22324
22828
  function jobRecordPath(jobId, projectDir) {
22325
- return join3(jobsDir(projectDir), `${normalizeJobId(jobId)}.json`);
22829
+ return join4(jobsDir(projectDir), `${normalizeJobId(jobId)}.json`);
22326
22830
  }
22327
22831
  function parseJobRecord(raw2) {
22328
22832
  const parsed = JSON.parse(raw2);
@@ -22375,21 +22879,21 @@ async function downloadUrl(url, outputPath) {
22375
22879
  const response = await fetch(url);
22376
22880
  if (!response.ok) throw new Error(`Download failed: HTTP ${response.status}`);
22377
22881
  const buffer = Buffer.from(await response.arrayBuffer());
22378
- await mkdir3(dirname3(outputPath), { recursive: true });
22379
- await writeFile6(outputPath, buffer);
22882
+ await mkdir4(dirname3(outputPath), { recursive: true });
22883
+ await writeFile7(outputPath, buffer);
22380
22884
  return outputPath;
22381
22885
  }
22382
22886
  async function maybeCacheOutput(record) {
22383
22887
  if (record.status !== "completed" || !record.outputPath || !record.cachePath) return;
22384
22888
  try {
22385
- await mkdir3(dirname3(record.cachePath), { recursive: true });
22889
+ await mkdir4(dirname3(record.cachePath), { recursive: true });
22386
22890
  await copyFile(record.outputPath, record.cachePath);
22387
22891
  } catch {
22388
22892
  }
22389
22893
  }
22390
22894
  async function maybeWriteCompletedAssetMetadata(record) {
22391
22895
  const kind3 = assetKindForJob(record);
22392
- if (record.status !== "completed" || !kind3 || !record.beatId || !record.assetCue || !record.cacheKey || !record.outputPath || !existsSync2(record.outputPath)) {
22896
+ if (record.status !== "completed" || !kind3 || !record.beatId || !record.assetCue || !record.cacheKey || !record.outputPath || !existsSync3(record.outputPath)) {
22393
22897
  return;
22394
22898
  }
22395
22899
  try {
@@ -22408,7 +22912,7 @@ async function maybeWriteCompletedAssetMetadata(record) {
22408
22912
  }
22409
22913
  }
22410
22914
  async function refreshBuildReportFromJobs(projectDir, records) {
22411
- const reportPath = join3(projectDir, "build-report.json");
22915
+ const reportPath = join4(projectDir, "build-report.json");
22412
22916
  const report = await readJson(reportPath);
22413
22917
  if (!report || !Array.isArray(report.beats)) return;
22414
22918
  let changed = false;
@@ -22456,12 +22960,12 @@ async function refreshBuildReportFromJobs(projectDir, records) {
22456
22960
  }
22457
22961
  if (!changed) return;
22458
22962
  try {
22459
- await writeFile6(reportPath, JSON.stringify(stripUndefined2(report), null, 2) + "\n", "utf-8");
22963
+ await writeFile7(reportPath, JSON.stringify(stripUndefined2(report), null, 2) + "\n", "utf-8");
22460
22964
  } catch {
22461
22965
  }
22462
22966
  }
22463
22967
  async function readBuildSummary(projectDir) {
22464
- const reportPath = join3(projectDir, "build-report.json");
22968
+ const reportPath = join4(projectDir, "build-report.json");
22465
22969
  const report = await readJson(reportPath);
22466
22970
  if (!report) return null;
22467
22971
  const info = await statOrNull(reportPath);
@@ -22484,7 +22988,7 @@ async function readBuildSummary(projectDir) {
22484
22988
  });
22485
22989
  }
22486
22990
  async function readReviewSummary(projectDir) {
22487
- const reportPath = join3(projectDir, "review-report.json");
22991
+ const reportPath = join4(projectDir, "review-report.json");
22488
22992
  const report = await readJson(reportPath);
22489
22993
  if (!report) return null;
22490
22994
  const info = await statOrNull(reportPath);
@@ -22573,16 +23077,16 @@ async function readProjectBeatReadiness(projectDir, build, records) {
22573
23077
  if (fromBeats) return fromBeats;
22574
23078
  if (build.beats) return augmentBeatReadinessFromJobs(build.beats, records, report?.jobs);
22575
23079
  }
22576
- const storyboardPath = join3(projectDir, "STORYBOARD.md");
22577
- if (!existsSync2(storyboardPath)) {
23080
+ const storyboardPath = join4(projectDir, "STORYBOARD.md");
23081
+ if (!existsSync3(storyboardPath)) {
22578
23082
  return { total: 0, assetsReady: 0, compositionsReady: 0, needsAuthor: [] };
22579
23083
  }
22580
23084
  try {
22581
- const parsed = parseStoryboard(await readFile4(storyboardPath, "utf-8"));
23085
+ const parsed = parseStoryboard(await loadStoryboardMarkdown(projectDir));
22582
23086
  const needsAuthor = [];
22583
23087
  let compositionsReady = 0;
22584
23088
  for (const beat of parsed.beats) {
22585
- if (existsSync2(join3(projectDir, "compositions", `scene-${beat.id}.html`))) {
23089
+ if (existsSync3(join4(projectDir, "compositions", `scene-${beat.id}.html`))) {
22586
23090
  compositionsReady += 1;
22587
23091
  } else {
22588
23092
  needsAuthor.push(beat.id);
@@ -22600,7 +23104,7 @@ async function readProjectBeatReadiness(projectDir, build, records) {
22600
23104
  }
22601
23105
  function deriveProjectWorkflow(opts) {
22602
23106
  const retryWith = [];
22603
- const hasStoryboard = existsSync2(join3(opts.project, "STORYBOARD.md"));
23107
+ const hasStoryboard = existsSync3(join4(opts.project, "STORYBOARD.md"));
22604
23108
  if (!hasStoryboard && !opts.build) {
22605
23109
  retryWith.push(`vibe init ${opts.project} --from "<brief>" --json`);
22606
23110
  return { status: "empty", currentStage: "init", retryWith };
@@ -22696,7 +23200,7 @@ function beatReadinessFromBuildBeats(projectDir, beats, records = [], reportJobs
22696
23200
  if (buildBeatAssetsReady(record) || (id ? buildBeatJobsReady(id, records, reportJobs) : false))
22697
23201
  assetsReady += 1;
22698
23202
  const compositionPath = typeof record.compositionPath === "string" ? record.compositionPath : id ? `compositions/scene-${id}.html` : void 0;
22699
- if (compositionPath && existsSync2(resolve5(projectDir, compositionPath))) {
23203
+ if (compositionPath && existsSync3(resolve5(projectDir, compositionPath))) {
22700
23204
  compositionsReady += 1;
22701
23205
  } else if (id) {
22702
23206
  needsAuthor.push(id);
@@ -22804,7 +23308,7 @@ function projectRelativePath(projectDir, path16) {
22804
23308
  }
22805
23309
  async function readJson(path16) {
22806
23310
  try {
22807
- return JSON.parse(await readFile4(path16, "utf-8"));
23311
+ return JSON.parse(await readFile5(path16, "utf-8"));
22808
23312
  } catch {
22809
23313
  return null;
22810
23314
  }
@@ -22836,13 +23340,14 @@ var init_status_jobs = __esm({
22836
23340
  init_build_asset_metadata();
22837
23341
  init_review_report();
22838
23342
  init_storyboard_parse();
23343
+ init_storyboard_source();
22839
23344
  LOCAL_JOB_ORPHAN_AFTER_MS = 9e4;
22840
23345
  }
22841
23346
  });
22842
23347
 
22843
23348
  // ../cli/src/commands/_shared/project-config.ts
22844
- import { existsSync as existsSync3 } from "node:fs";
22845
- import { readFile as readFile5 } from "node:fs/promises";
23349
+ import { existsSync as existsSync4 } from "node:fs";
23350
+ import { readFile as readFile6 } from "node:fs/promises";
22846
23351
  import { resolve as resolve6 } from "node:path";
22847
23352
  function defaultProjectConfig(opts) {
22848
23353
  return {
@@ -22899,9 +23404,9 @@ async function readProjectConfig(projectDir) {
22899
23404
  const dir = resolve6(projectDir);
22900
23405
  const canonicalPath = resolve6(dir, VIBE_CONFIG_FILENAME);
22901
23406
  const legacyPath = resolve6(dir, LEGACY_VIBE_PROJECT_FILENAME);
22902
- if (existsSync3(canonicalPath)) {
23407
+ if (existsSync4(canonicalPath)) {
22903
23408
  try {
22904
- const raw2 = await readFile5(canonicalPath, "utf-8");
23409
+ const raw2 = await readFile6(canonicalPath, "utf-8");
22905
23410
  const parsed = JSON.parse(raw2);
22906
23411
  const name = typeof parsed.name === "string" && parsed.name ? parsed.name : dir.split(/[\\/]/).filter(Boolean).pop() ?? "video";
22907
23412
  const fallback2 = defaultProjectConfig({
@@ -22919,10 +23424,10 @@ async function readProjectConfig(projectDir) {
22919
23424
  } catch {
22920
23425
  }
22921
23426
  }
22922
- if (existsSync3(legacyPath)) {
23427
+ if (existsSync4(legacyPath)) {
22923
23428
  try {
22924
- const raw2 = await readFile5(legacyPath, "utf-8");
22925
- const legacy = (0, import_yaml4.parse)(raw2);
23429
+ const raw2 = await readFile6(legacyPath, "utf-8");
23430
+ const legacy = (0, import_yaml6.parse)(raw2);
22926
23431
  const name = legacy?.name ?? dir.split(/[\\/]/).filter(Boolean).pop() ?? "video";
22927
23432
  const config4 = defaultProjectConfig({
22928
23433
  name,
@@ -22954,19 +23459,19 @@ async function readProjectConfig(projectDir) {
22954
23459
  legacy: false
22955
23460
  };
22956
23461
  }
22957
- var import_yaml4, VIBE_CONFIG_FILENAME, LEGACY_VIBE_PROJECT_FILENAME;
23462
+ var import_yaml6, VIBE_CONFIG_FILENAME, LEGACY_VIBE_PROJECT_FILENAME;
22958
23463
  var init_project_config = __esm({
22959
23464
  "../cli/src/commands/_shared/project-config.ts"() {
22960
23465
  "use strict";
22961
- import_yaml4 = __toESM(require_dist3(), 1);
23466
+ import_yaml6 = __toESM(require_dist3(), 1);
22962
23467
  VIBE_CONFIG_FILENAME = "vibe.config.json";
22963
23468
  LEGACY_VIBE_PROJECT_FILENAME = "vibe.project.yaml";
22964
23469
  }
22965
23470
  });
22966
23471
 
22967
23472
  // ../cli/src/commands/_shared/scene-project.ts
22968
- import { mkdir as mkdir4, readFile as readFile6, writeFile as writeFile7, access as access3 } from "node:fs/promises";
22969
- import { resolve as resolve7, basename } from "node:path";
23473
+ import { mkdir as mkdir5, readFile as readFile7, writeFile as writeFile8, access as access3 } from "node:fs/promises";
23474
+ import { resolve as resolve7, basename as basename2 } from "node:path";
22970
23475
  function isSceneKind(value) {
22971
23476
  return VALID_SCENE_KINDS.includes(value);
22972
23477
  }
@@ -23313,15 +23818,25 @@ media in \`references/\`; older VibeFrame versions vendored composition rules
23313
23818
  there, and current installs keep the rules under \`.agents/skills/hyperframes/\`
23314
23819
  via \`vibe scene install-skill\`.
23315
23820
 
23316
- When a beat should reuse a local file, reference it from \`STORYBOARD.md\`
23317
- with a project-relative path:
23821
+ Scenes live in \`scenes/\`, one file per scene, and play in filename order.
23822
+ Each scene is a markdown file whose frontmatter carries its cues:
23318
23823
 
23319
- \`\`\`yaml
23320
- backdrop: "media/product-shot.png" # existing still image
23321
- video: "media/broll.mp4" # existing video/B-roll
23322
- narration: "media/voice.wav" # existing recorded narration
23323
- asset: "media/logo.png" # generic local asset reference
23324
- \`\`\`
23824
+ ---
23825
+ type: Scene
23826
+ duration: 5
23827
+ narration: "One crisp sentence."
23828
+ backdrop: "Clean editorial background plate, generous negative space"
23829
+ ---
23830
+
23831
+ Direction prose for this scene goes in the body.
23832
+
23833
+ When a scene should reuse a local file, give the cue a project-relative path
23834
+ instead of a prompt:
23835
+
23836
+ backdrop: "media/product-shot.png" # existing still image
23837
+ video: "media/broll.mp4" # existing video/B-roll
23838
+ narration: "media/voice.wav" # existing recorded narration
23839
+ asset: "media/logo.png" # generic local asset reference
23325
23840
 
23326
23841
  Use text cues when you want VibeFrame to generate an asset. Use path cues
23327
23842
  when you want VibeFrame to reuse a local file. Avoid absolute paths or parent
@@ -23561,17 +24076,17 @@ async function pathExists(p2) {
23561
24076
  }
23562
24077
  async function scaffoldSceneProject(opts) {
23563
24078
  const dir = resolve7(opts.dir);
23564
- const name = opts.name ?? basename(dir);
24079
+ const name = opts.name ?? basename2(dir);
23565
24080
  const aspect = opts.aspect ?? "16:9";
23566
24081
  const duration = opts.duration ?? 10;
23567
24082
  const now = opts.now ?? /* @__PURE__ */ new Date();
23568
24083
  const profile = opts.profile ?? "full";
23569
24084
  const kind3 = opts.kind ?? DEFAULT_SCENE_KIND;
23570
- await mkdir4(dir, { recursive: true });
24085
+ await mkdir5(dir, { recursive: true });
23571
24086
  if (profile === "full") {
23572
- await mkdir4(resolve7(dir, "compositions"), { recursive: true });
23573
- await mkdir4(resolve7(dir, "assets"), { recursive: true });
23574
- await mkdir4(resolve7(dir, "renders"), { recursive: true });
24087
+ await mkdir5(resolve7(dir, "compositions"), { recursive: true });
24088
+ await mkdir5(resolve7(dir, "assets"), { recursive: true });
24089
+ await mkdir5(resolve7(dir, "renders"), { recursive: true });
23575
24090
  }
23576
24091
  const created = [];
23577
24092
  const skipped2 = [];
@@ -23580,20 +24095,20 @@ async function scaffoldSceneProject(opts) {
23580
24095
  const hfPath = resolve7(dir, "hyperframes.json");
23581
24096
  const hfDefaults = buildHyperframesConfig();
23582
24097
  if (await pathExists(hfPath)) {
23583
- const existingRaw = await readFile6(hfPath, "utf-8");
24098
+ const existingRaw = await readFile7(hfPath, "utf-8");
23584
24099
  const existing = JSON.parse(existingRaw);
23585
24100
  const mergedConfig = mergeHyperframesConfig(existing, hfDefaults);
23586
- await writeFile7(hfPath, JSON.stringify(mergedConfig, null, 2) + "\n", "utf-8");
24101
+ await writeFile8(hfPath, JSON.stringify(mergedConfig, null, 2) + "\n", "utf-8");
23587
24102
  merged.push(hfPath);
23588
24103
  } else {
23589
- await writeFile7(hfPath, JSON.stringify(hfDefaults, null, 2) + "\n", "utf-8");
24104
+ await writeFile8(hfPath, JSON.stringify(hfDefaults, null, 2) + "\n", "utf-8");
23590
24105
  created.push(hfPath);
23591
24106
  }
23592
24107
  const metaPath = resolve7(dir, "meta.json");
23593
24108
  if (await pathExists(metaPath)) {
23594
24109
  skipped2.push(metaPath);
23595
24110
  } else {
23596
- await writeFile7(
24111
+ await writeFile8(
23597
24112
  metaPath,
23598
24113
  JSON.stringify(buildHyperframesMeta(name, now), null, 2) + "\n",
23599
24114
  "utf-8"
@@ -23604,7 +24119,7 @@ async function scaffoldSceneProject(opts) {
23604
24119
  if (await pathExists(rootPath)) {
23605
24120
  skipped2.push(rootPath);
23606
24121
  } else {
23607
- await writeFile7(rootPath, buildEmptyRootHtml({ aspect, duration }), "utf-8");
24122
+ await writeFile8(rootPath, buildEmptyRootHtml({ aspect, duration }), "utf-8");
23608
24123
  created.push(rootPath);
23609
24124
  }
23610
24125
  }
@@ -23612,7 +24127,7 @@ async function scaffoldSceneProject(opts) {
23612
24127
  if (await pathExists(vibeConfigJsonPath)) {
23613
24128
  skipped2.push(vibeConfigJsonPath);
23614
24129
  } else {
23615
- await writeFile7(vibeConfigJsonPath, projectConfigJson({ name, aspect, kind: kind3 }), "utf-8");
24130
+ await writeFile8(vibeConfigJsonPath, projectConfigJson({ name, aspect, kind: kind3 }), "utf-8");
23616
24131
  created.push(vibeConfigJsonPath);
23617
24132
  }
23618
24133
  if (profile === "agent" || profile === "full") {
@@ -23620,22 +24135,22 @@ async function scaffoldSceneProject(opts) {
23620
24135
  if (await pathExists(agentsPath)) {
23621
24136
  skipped2.push(agentsPath);
23622
24137
  } else {
23623
- await writeFile7(agentsPath, buildProjectAgentsMd(name), "utf-8");
24138
+ await writeFile8(agentsPath, buildProjectAgentsMd(name), "utf-8");
23624
24139
  created.push(agentsPath);
23625
24140
  }
23626
24141
  const claudePath = resolve7(dir, "CLAUDE.md");
23627
24142
  if (await pathExists(claudePath)) {
23628
- const existing = await readFile6(claudePath, "utf-8");
24143
+ const existing = await readFile7(claudePath, "utf-8");
23629
24144
  if (existing.includes("@AGENTS.md")) {
23630
24145
  skipped2.push(claudePath);
23631
24146
  } else {
23632
- await writeFile7(claudePath, `@AGENTS.md
24147
+ await writeFile8(claudePath, `@AGENTS.md
23633
24148
 
23634
24149
  ${existing}`, "utf-8");
23635
24150
  merged.push(claudePath);
23636
24151
  }
23637
24152
  } else {
23638
- await writeFile7(claudePath, buildProjectClaudeMd(name), "utf-8");
24153
+ await writeFile8(claudePath, buildProjectClaudeMd(name), "utf-8");
23639
24154
  created.push(claudePath);
23640
24155
  }
23641
24156
  }
@@ -23643,21 +24158,22 @@ ${existing}`, "utf-8");
23643
24158
  if (await pathExists(designPath)) {
23644
24159
  skipped2.push(designPath);
23645
24160
  } else {
23646
- await writeFile7(designPath, buildDesignMd({ name, style: opts.visualStyle }), "utf-8");
24161
+ await writeFile8(designPath, buildDesignMd({ name, style: opts.visualStyle }), "utf-8");
23647
24162
  created.push(designPath);
23648
24163
  }
23649
24164
  const storyboardPath = resolve7(dir, "STORYBOARD.md");
23650
24165
  if (await pathExists(storyboardPath)) {
23651
24166
  skipped2.push(storyboardPath);
23652
24167
  } else {
23653
- await writeFile7(storyboardPath, buildStoryboardMd(name, duration), "utf-8");
24168
+ const plan2 = await writeStoryboardAsBundle(dir, buildStoryboardMd(name, duration));
23654
24169
  created.push(storyboardPath);
24170
+ for (const file of plan2.scenes) created.push(resolve7(dir, file.path));
23655
24171
  }
23656
24172
  const scriptPath = resolve7(dir, "SCRIPT.md");
23657
24173
  if (await pathExists(scriptPath)) {
23658
24174
  skipped2.push(scriptPath);
23659
24175
  } else {
23660
- await writeFile7(scriptPath, buildScriptMd(name, kind3), "utf-8");
24176
+ await writeFile8(scriptPath, buildScriptMd(name, kind3), "utf-8");
23661
24177
  created.push(scriptPath);
23662
24178
  }
23663
24179
  if (kindHasCast(kind3)) {
@@ -23665,7 +24181,7 @@ ${existing}`, "utf-8");
23665
24181
  if (await pathExists(charactersPath)) {
23666
24182
  skipped2.push(charactersPath);
23667
24183
  } else {
23668
- await writeFile7(charactersPath, buildCharactersMd(name), "utf-8");
24184
+ await writeFile8(charactersPath, buildCharactersMd(name), "utf-8");
23669
24185
  created.push(charactersPath);
23670
24186
  }
23671
24187
  }
@@ -23673,7 +24189,7 @@ ${existing}`, "utf-8");
23673
24189
  if (await pathExists(gitignorePath)) {
23674
24190
  skipped2.push(gitignorePath);
23675
24191
  } else {
23676
- await writeFile7(gitignorePath, buildSceneGitignore(), "utf-8");
24192
+ await writeFile8(gitignorePath, buildSceneGitignore(), "utf-8");
23677
24193
  created.push(gitignorePath);
23678
24194
  }
23679
24195
  return { created, skipped: skipped2, merged, groups: describeSceneScaffold({ dir, profile }) };
@@ -23682,6 +24198,7 @@ var VALID_SCENE_KINDS, DEFAULT_SCENE_KIND, ASPECT_DIMS;
23682
24198
  var init_scene_project = __esm({
23683
24199
  "../cli/src/commands/_shared/scene-project.ts"() {
23684
24200
  "use strict";
24201
+ init_storyboard_source();
23685
24202
  init_project_config();
23686
24203
  VALID_SCENE_KINDS = [
23687
24204
  "cinema",
@@ -35218,7 +35735,7 @@ var require_source_map = __commonJS({
35218
35735
  var require_previous_map = __commonJS({
35219
35736
  "../../node_modules/.pnpm/postcss@8.5.6/node_modules/postcss/lib/previous-map.js"(exports, module) {
35220
35737
  "use strict";
35221
- var { existsSync: existsSync85, readFileSync: readFileSync23 } = __require("fs");
35738
+ var { existsSync: existsSync86, readFileSync: readFileSync23 } = __require("fs");
35222
35739
  var { dirname: dirname40, join: join78 } = __require("path");
35223
35740
  var { SourceMapConsumer, SourceMapGenerator } = require_source_map();
35224
35741
  function fromBase64(str2) {
@@ -35281,7 +35798,7 @@ var require_previous_map = __commonJS({
35281
35798
  }
35282
35799
  loadFile(path16) {
35283
35800
  this.root = dirname40(path16);
35284
- if (existsSync85(path16)) {
35801
+ if (existsSync86(path16)) {
35285
35802
  this.mapFile = path16;
35286
35803
  return readFileSync23(path16, "utf-8").toString().trim();
35287
35804
  }
@@ -87270,7 +87787,7 @@ var require_yauzl = __commonJS({
87270
87787
  // ../../node_modules/.pnpm/@puppeteer+browsers@3.0.6_yauzl@2.10.0/node_modules/@puppeteer/browsers/lib/fileUtil.js
87271
87788
  import { spawnSync, spawn, execFile as execFile2 } from "node:child_process";
87272
87789
  import { constants as constants2, createReadStream, createWriteStream as createWriteStream2 } from "node:fs";
87273
- import { mkdir as mkdir6, readdir as readdir3, symlink as symlink2 } from "node:fs/promises";
87790
+ import { mkdir as mkdir7, readdir as readdir4, symlink as symlink2 } from "node:fs/promises";
87274
87791
  import * as path9 from "node:path";
87275
87792
  import { Stream, Writable as Writable2 } from "node:stream";
87276
87793
  import { pipeline } from "node:stream/promises";
@@ -87280,12 +87797,12 @@ async function unpackArchive(archivePath, folderPath) {
87280
87797
  folderPath = path9.resolve(process.cwd(), folderPath);
87281
87798
  }
87282
87799
  if (archivePath.endsWith(".zip")) {
87283
- await mkdir6(folderPath, { recursive: true });
87800
+ await mkdir7(folderPath, { recursive: true });
87284
87801
  await extractZip(archivePath, folderPath);
87285
87802
  } else if (archivePath.endsWith(".tar.bz2")) {
87286
87803
  await extractTar(archivePath, folderPath, "bzip2");
87287
87804
  } else if (archivePath.endsWith(".dmg")) {
87288
- await mkdir6(folderPath);
87805
+ await mkdir7(folderPath);
87289
87806
  await installDMG(archivePath, folderPath);
87290
87807
  } else if (archivePath.endsWith(".exe")) {
87291
87808
  const result = spawnSync(archivePath, [`/ExtractDir=${folderPath}`], {
@@ -87374,7 +87891,7 @@ async function installDMG(dmgPath, folderPath) {
87374
87891
  }
87375
87892
  const mountPath = volumes[0];
87376
87893
  try {
87377
- const fileNames = await readdir3(mountPath);
87894
+ const fileNames = await readdir4(mountPath);
87378
87895
  const appName = fileNames.find((item) => {
87379
87896
  return typeof item === "string" && item.endsWith(".app");
87380
87897
  });
@@ -87473,10 +87990,10 @@ async function extractZipEntry(zipFile, entry, folderPath) {
87473
87990
  const mode = unixMode === 0 ? isDirectory2 ? 493 : 420 : unixMode & 511;
87474
87991
  const destination = path9.join(folderPath, entry.fileName);
87475
87992
  if (isDirectory2) {
87476
- await mkdir6(destination, { recursive: true, mode });
87993
+ await mkdir7(destination, { recursive: true, mode });
87477
87994
  return;
87478
87995
  }
87479
- await mkdir6(path9.dirname(destination), { recursive: true });
87996
+ await mkdir7(path9.dirname(destination), { recursive: true });
87480
87997
  const readStream = await promisify2(zipFile.openReadStream.bind(zipFile))(entry);
87481
87998
  if (isSymlink) {
87482
87999
  const chunks = [];
@@ -87643,8 +88160,8 @@ var init_ProgressBar = __esm({
87643
88160
  // ../../node_modules/.pnpm/@puppeteer+browsers@3.0.6_yauzl@2.10.0/node_modules/@puppeteer/browsers/lib/install.js
87644
88161
  import assert3 from "node:assert";
87645
88162
  import { spawnSync as spawnSync2 } from "node:child_process";
87646
- import { existsSync as existsSync4, readFileSync as readFileSync2 } from "node:fs";
87647
- import { mkdir as mkdir7, unlink } from "node:fs/promises";
88163
+ import { existsSync as existsSync5, readFileSync as readFileSync2 } from "node:fs";
88164
+ import { mkdir as mkdir8, unlink } from "node:fs/promises";
87648
88165
  import os6 from "node:os";
87649
88166
  import path10 from "node:path";
87650
88167
  function debugTime(label) {
@@ -87696,8 +88213,8 @@ async function installWithProviders(options) {
87696
88213
  continue;
87697
88214
  }
87698
88215
  debugInstall?.(`Successfully got URL from ${provider.getName()}: ${url}`);
87699
- if (!existsSync4(browserRoot)) {
87700
- await mkdir7(browserRoot, { recursive: true });
88216
+ if (!existsSync5(browserRoot)) {
88217
+ await mkdir8(browserRoot, { recursive: true });
87701
88218
  }
87702
88219
  return await installUrl(url, options, provider);
87703
88220
  } catch (err) {
@@ -87728,7 +88245,7 @@ async function installDeps(installedBrowser) {
87728
88245
  return;
87729
88246
  }
87730
88247
  const depsPath = path10.join(path10.dirname(installedBrowser.executablePath), "deb.deps");
87731
- if (!existsSync4(depsPath)) {
88248
+ if (!existsSync5(depsPath)) {
87732
88249
  debugInstall?.(`deb.deps file was not found at ${depsPath}`);
87733
88250
  return;
87734
88251
  }
@@ -87769,14 +88286,14 @@ async function installUrl(url, options, provider) {
87769
88286
  const cache = new Cache(options.cacheDir);
87770
88287
  const browserRoot = cache.browserRoot(options.browser);
87771
88288
  const archivePath = path10.join(browserRoot, `${options.buildId}-${fileName}`);
87772
- if (!existsSync4(browserRoot)) {
87773
- await mkdir7(browserRoot, { recursive: true });
88289
+ if (!existsSync5(browserRoot)) {
88290
+ await mkdir8(browserRoot, { recursive: true });
87774
88291
  }
87775
88292
  if (options.expectedHash) {
87776
88293
  debugInstall?.(`Using provided checksum for ${fileName}: ${options.expectedHash}`);
87777
88294
  }
87778
88295
  if (!options.unpack) {
87779
- if (existsSync4(archivePath)) {
88296
+ if (existsSync5(archivePath)) {
87780
88297
  return archivePath;
87781
88298
  }
87782
88299
  debugInstall?.(`Downloading binary from ${url}`);
@@ -87797,8 +88314,8 @@ async function installUrl(url, options, provider) {
87797
88314
  cache.writeExecutablePath(options.browser, options.platform, options.buildId, relativeExecutablePath7);
87798
88315
  }
87799
88316
  try {
87800
- if (existsSync4(outputPath)) {
87801
- if (!existsSync4(installedBrowser.executablePath)) {
88317
+ if (existsSync5(outputPath)) {
88318
+ if (!existsSync5(installedBrowser.executablePath)) {
87802
88319
  throw new Error(`The browser folder (${outputPath}) exists but the executable (${installedBrowser.executablePath}) is missing`);
87803
88320
  }
87804
88321
  await runSetup(installedBrowser);
@@ -87807,7 +88324,7 @@ async function installUrl(url, options, provider) {
87807
88324
  }
87808
88325
  return installedBrowser;
87809
88326
  }
87810
- if (!existsSync4(archivePath)) {
88327
+ if (!existsSync5(archivePath)) {
87811
88328
  debugInstall?.(`Downloading binary from ${url}`);
87812
88329
  try {
87813
88330
  debugTime("download");
@@ -87836,7 +88353,7 @@ async function installUrl(url, options, provider) {
87836
88353
  }
87837
88354
  return installedBrowser;
87838
88355
  } finally {
87839
- if (existsSync4(archivePath)) {
88356
+ if (existsSync5(archivePath)) {
87840
88357
  await unlink(archivePath);
87841
88358
  }
87842
88359
  }
@@ -87847,7 +88364,7 @@ async function runSetup(installedBrowser) {
87847
88364
  debugTime("permissions");
87848
88365
  const browserDir = path10.dirname(installedBrowser.executablePath);
87849
88366
  const setupExePath = path10.join(browserDir, "setup.exe");
87850
- if (!existsSync4(setupExePath)) {
88367
+ if (!existsSync5(setupExePath)) {
87851
88368
  return;
87852
88369
  }
87853
88370
  spawnSync2(path10.join(browserDir, "setup.exe"), [`--configure-browser-in-directory=` + browserDir], {
@@ -89611,7 +90128,7 @@ var init_process_argv = __esm({
89611
90128
  });
89612
90129
 
89613
90130
  // ../../node_modules/.pnpm/y18n@5.0.8/node_modules/y18n/build/lib/platform-shims/node.js
89614
- import { readFileSync as readFileSync4, statSync as statSync3, writeFile as writeFile8 } from "fs";
90131
+ import { readFileSync as readFileSync4, statSync as statSync3, writeFile as writeFile9 } from "fs";
89615
90132
  import { format as format2 } from "util";
89616
90133
  import { resolve as resolve12 } from "path";
89617
90134
  var node_default;
@@ -89620,7 +90137,7 @@ var init_node = __esm({
89620
90137
  node_default = {
89621
90138
  fs: {
89622
90139
  readFileSync: readFileSync4,
89623
- writeFile: writeFile8
90140
+ writeFile: writeFile9
89624
90141
  },
89625
90142
  format: format2,
89626
90143
  resolve: resolve12,
@@ -89841,7 +90358,7 @@ var require_get_caller_file = __commonJS({
89841
90358
  import { notStrictEqual, strictEqual } from "assert";
89842
90359
  import { inspect } from "util";
89843
90360
  import { fileURLToPath } from "url";
89844
- import { basename as basename3, dirname as dirname7, extname as extname2, relative as relative3, resolve as resolve13, join as join6 } from "path";
90361
+ import { basename as basename4, dirname as dirname7, extname as extname2, relative as relative3, resolve as resolve13, join as join7 } from "path";
89845
90362
  import { createRequire as createRequire2 } from "node:module";
89846
90363
  import { readFileSync as readFileSync5, readdirSync as readdirSync2 } from "node:fs";
89847
90364
  var import_get_caller_file, __dirname2, mainFilename, require3, esm_default;
@@ -89873,12 +90390,12 @@ var init_esm2 = __esm({
89873
90390
  mainFilename: mainFilename || process.cwd(),
89874
90391
  Parser: lib_default,
89875
90392
  path: {
89876
- basename: basename3,
90393
+ basename: basename4,
89877
90394
  dirname: dirname7,
89878
90395
  extname: extname2,
89879
90396
  relative: relative3,
89880
90397
  resolve: resolve13,
89881
- join: join6
90398
+ join: join7
89882
90399
  },
89883
90400
  process: {
89884
90401
  argv: () => process.argv,
@@ -94128,9 +94645,9 @@ var init_PipeTransport = __esm({
94128
94645
  });
94129
94646
 
94130
94647
  // ../../node_modules/.pnpm/puppeteer-core@25.3.0_yauzl@2.10.0/node_modules/puppeteer-core/lib/puppeteer/node/BrowserLauncher.js
94131
- import { existsSync as existsSync5 } from "node:fs";
94648
+ import { existsSync as existsSync6 } from "node:fs";
94132
94649
  import { tmpdir } from "node:os";
94133
- import { join as join7 } from "node:path";
94650
+ import { join as join8 } from "node:path";
94134
94651
  function getBrowserTypeDisplayName(browserType) {
94135
94652
  switch (browserType) {
94136
94653
  case Browser3.FIREFOX:
@@ -94187,7 +94704,7 @@ var init_BrowserLauncher = __esm({
94187
94704
  ...options,
94188
94705
  protocol
94189
94706
  });
94190
- if (!existsSync5(launchArgs.executablePath)) {
94707
+ if (!existsSync6(launchArgs.executablePath)) {
94191
94708
  throw new Error(`Browser was not found at the configured executablePath (${launchArgs.executablePath})`);
94192
94709
  }
94193
94710
  const usePipe = launchArgs.args.includes("--remote-debugging-pipe");
@@ -94264,7 +94781,7 @@ var init_BrowserLauncher = __esm({
94264
94781
  const logs = browserProcess.getRecentLogs().join("\n");
94265
94782
  if (logs.includes("Failed to create a ProcessSingleton for your profile directory") || // On Windows we will not get logs due to the singleton process handover. See
94266
94783
  // https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/process_singleton_win.cc;l=46;drc=fc7952f0422b5073515a205a04ec9c3a1ae81658
94267
- process.platform === "win32" && existsSync5(join7(launchArgs.userDataDir, "lockfile"))) {
94784
+ process.platform === "win32" && existsSync6(join8(launchArgs.userDataDir, "lockfile"))) {
94268
94785
  throw new Error(`The browser is already running for ${launchArgs.userDataDir}. Use a different \`userDataDir\` or stop the running browser first.`);
94269
94786
  }
94270
94787
  if (logs.includes("Missing X server") && options.headless === false) {
@@ -94395,7 +94912,7 @@ var init_BrowserLauncher = __esm({
94395
94912
  */
94396
94913
  async getProfilePath() {
94397
94914
  const config4 = await this.puppeteer.configuration();
94398
- return join7(config4.temporaryDirectory ?? tmpdir(), `puppeteer_dev_${this.browser}_profile-`);
94915
+ return join8(config4.temporaryDirectory ?? tmpdir(), `puppeteer_dev_${this.browser}_profile-`);
94399
94916
  }
94400
94917
  /**
94401
94918
  * @internal
@@ -94404,7 +94921,7 @@ var init_BrowserLauncher = __esm({
94404
94921
  const config4 = await this.puppeteer.configuration();
94405
94922
  let executablePath4 = config4.executablePath;
94406
94923
  if (executablePath4) {
94407
- if (validatePath && !existsSync5(executablePath4)) {
94924
+ if (validatePath && !existsSync6(executablePath4)) {
94408
94925
  throw new Error(`Tried to find the browser at the configured path (${executablePath4}), but no executable was found.`);
94409
94926
  }
94410
94927
  return executablePath4;
@@ -94429,7 +94946,7 @@ var init_BrowserLauncher = __esm({
94429
94946
  browser: browserType,
94430
94947
  buildId: browserVersion
94431
94948
  });
94432
- if (validatePath && !existsSync5(executablePath4)) {
94949
+ if (validatePath && !existsSync6(executablePath4)) {
94433
94950
  const configVersion = config4?.[this.browser]?.version;
94434
94951
  if (configVersion) {
94435
94952
  throw new Error(`Tried to find the browser at the configured path (${executablePath4}) for version ${configVersion}, but no executable was found.`);
@@ -94447,7 +94964,7 @@ For (2), check out our guide on configuring puppeteer at https://pptr.dev/guides
94447
94964
 
94448
94965
  // ../../node_modules/.pnpm/puppeteer-core@25.3.0_yauzl@2.10.0/node_modules/puppeteer-core/lib/puppeteer/node/util/fs.js
94449
94966
  import fs6 from "node:fs";
94450
- async function rm3(path16) {
94967
+ async function rm4(path16) {
94451
94968
  await fs6.promises.rm(path16, rmOptions);
94452
94969
  }
94453
94970
  var rmOptions;
@@ -94577,7 +95094,7 @@ var init_ChromeLauncher = __esm({
94577
95094
  async cleanUserDataDir(path16, opts) {
94578
95095
  if (opts.isTemp) {
94579
95096
  try {
94580
- await rm3(path16);
95097
+ await rm4(path16);
94581
95098
  } catch (error) {
94582
95099
  debugError?.(error);
94583
95100
  throw error;
@@ -94693,7 +95210,7 @@ var init_ChromeLauncher = __esm({
94693
95210
 
94694
95211
  // ../../node_modules/.pnpm/puppeteer-core@25.3.0_yauzl@2.10.0/node_modules/puppeteer-core/lib/puppeteer/node/FirefoxLauncher.js
94695
95212
  import fs7 from "node:fs";
94696
- import { rename as rename2, unlink as unlink2, mkdtemp as mkdtemp2 } from "node:fs/promises";
95213
+ import { rename as rename3, unlink as unlink2, mkdtemp as mkdtemp2 } from "node:fs/promises";
94697
95214
  import os9 from "node:os";
94698
95215
  import path13 from "node:path";
94699
95216
  var FirefoxLauncher;
@@ -94781,7 +95298,7 @@ var init_FirefoxLauncher = __esm({
94781
95298
  async cleanUserDataDir(userDataDir, opts) {
94782
95299
  if (opts.isTemp) {
94783
95300
  try {
94784
- await rm3(userDataDir);
95301
+ await rm4(userDataDir);
94785
95302
  } catch (error) {
94786
95303
  debugError?.(error);
94787
95304
  throw error;
@@ -94795,7 +95312,7 @@ var init_FirefoxLauncher = __esm({
94795
95312
  if (fs7.existsSync(prefsBackupPath)) {
94796
95313
  const prefsPath = path13.join(userDataDir, file);
94797
95314
  await unlink2(prefsPath);
94798
- await rename2(prefsBackupPath, prefsPath);
95315
+ await rename3(prefsBackupPath, prefsPath);
94799
95316
  }
94800
95317
  }));
94801
95318
  for (const result of results) {
@@ -95800,7 +96317,7 @@ var require_src2 = __commonJS({
95800
96317
 
95801
96318
  // ../../node_modules/.pnpm/puppeteer@25.3.0_yauzl@2.10.0/node_modules/puppeteer/lib/puppeteer/getConfiguration.js
95802
96319
  import { homedir as homedir3 } from "node:os";
95803
- import { join as join8 } from "node:path";
96320
+ import { join as join9 } from "node:path";
95804
96321
  function getBooleanEnvVar(name) {
95805
96322
  const env4 = process.env[name];
95806
96323
  if (env4 === void 0) {
@@ -95887,7 +96404,7 @@ var init_getConfiguration = __esm({
95887
96404
  configuration.firefox = getBrowserSetting("firefox", configuration, {
95888
96405
  skipDownload: true
95889
96406
  });
95890
- configuration.cacheDirectory = process.env["PUPPETEER_CACHE_DIR"] ?? configuration.cacheDirectory ?? join8(homedir3(), ".cache", "puppeteer");
96407
+ configuration.cacheDirectory = process.env["PUPPETEER_CACHE_DIR"] ?? configuration.cacheDirectory ?? join9(homedir3(), ".cache", "puppeteer");
95891
96408
  configuration.temporaryDirectory = process.env["PUPPETEER_TMP_DIR"] ?? configuration.temporaryDirectory;
95892
96409
  configuration.experiments ??= {};
95893
96410
  return configuration;
@@ -96246,7 +96763,7 @@ import { pipeline as pipeline2 } from "node:stream/promises";
96246
96763
  import { promisify as promisify3 } from "node:util";
96247
96764
  import assert32 from "node:assert";
96248
96765
  import { spawnSync as spawnSync22 } from "node:child_process";
96249
- import { existsSync as existsSync6, readFileSync as readFileSync22 } from "node:fs";
96766
+ import { existsSync as existsSync7, readFileSync as readFileSync22 } from "node:fs";
96250
96767
  import { mkdir as mkdir32, unlink as unlink3 } from "node:fs/promises";
96251
96768
  import os52 from "node:os";
96252
96769
  import path102 from "node:path";
@@ -96256,7 +96773,7 @@ import { format as format3 } from "util";
96256
96773
  import { normalize as normalize2, resolve as resolve52 } from "path";
96257
96774
  import { readFileSync as readFileSync32 } from "fs";
96258
96775
  import { createRequire as createRequire3 } from "node:module";
96259
- import { readFileSync as readFileSync42, statSync as statSync22, writeFile as writeFile9 } from "fs";
96776
+ import { readFileSync as readFileSync42, statSync as statSync22, writeFile as writeFile10 } from "fs";
96260
96777
  import { format as format22 } from "util";
96261
96778
  import { resolve as resolve62 } from "path";
96262
96779
  import { notStrictEqual as notStrictEqual2, strictEqual as strictEqual2 } from "assert";
@@ -96279,7 +96796,7 @@ import { mkdtemp as mkdtemp3 } from "node:fs/promises";
96279
96796
  import os72 from "node:os";
96280
96797
  import path122 from "node:path";
96281
96798
  import fs72 from "node:fs";
96282
- import { rename as rename3, unlink as unlink22, mkdtemp as mkdtemp22 } from "node:fs/promises";
96799
+ import { rename as rename4, unlink as unlink22, mkdtemp as mkdtemp22 } from "node:fs/promises";
96283
96800
  import os82 from "node:os";
96284
96801
  import path132 from "node:path";
96285
96802
  import { spawn as spawn32, spawnSync as spawnSync32 } from "node:child_process";
@@ -96302,7 +96819,7 @@ import { spawn as spawn5 } from "child_process";
96302
96819
  import { spawn as spawn6 } from "child_process";
96303
96820
  import { readFileSync as readFileSync62 } from "fs";
96304
96821
  import { extname as extname22 } from "path";
96305
- import { copyFileSync, existsSync as existsSync7, mkdirSync as mkdirSync2, readdirSync as readdirSync4, statSync as statSync32, writeFileSync as writeFileSync2 } from "fs";
96822
+ import { copyFileSync, existsSync as existsSync72, mkdirSync as mkdirSync2, readdirSync as readdirSync4, statSync as statSync32, writeFileSync as writeFileSync2 } from "fs";
96306
96823
  import { join as join102, dirname as dirname72, extname as extname32 } from "path";
96307
96824
  import { spawn as spawn7 } from "child_process";
96308
96825
  import { once } from "events";
@@ -96310,7 +96827,7 @@ import { existsSync as existsSync8, mkdirSync as mkdirSync3, statSync as statSyn
96310
96827
  import { dirname as dirname82 } from "path";
96311
96828
  import { createWriteStream as createWriteStream32, existsSync as existsSync9, mkdirSync as mkdirSync4 } from "fs";
96312
96829
  import { createHash as createHash22 } from "crypto";
96313
- import { join as join11, extname as extname4 } from "path";
96830
+ import { join as join112, extname as extname4 } from "path";
96314
96831
  import { Readable as Readable22 } from "stream";
96315
96832
  import { finished } from "stream/promises";
96316
96833
  import { createHash as createHash32, randomUUID as randomUUID3 } from "node:crypto";
@@ -96413,7 +96930,7 @@ import {
96413
96930
  renameSync as renameSync3,
96414
96931
  rmSync as rmSync5
96415
96932
  } from "node:fs";
96416
- import { basename as basename5, dirname as dirname12, extname as extname7, join as join222, resolve as resolve132 } from "node:path";
96933
+ import { basename as basename52, dirname as dirname12, extname as extname7, join as join222, resolve as resolve132 } from "node:path";
96417
96934
  import { join as join23 } from "node:path";
96418
96935
  import { createHash as createHash5 } from "node:crypto";
96419
96936
  import { join as join29 } from "node:path";
@@ -106982,7 +107499,7 @@ async function installWithProviders2(options) {
106982
107499
  continue;
106983
107500
  }
106984
107501
  debugInstall2?.(`Successfully got URL from ${provider.getName()}: ${url}`);
106985
- if (!existsSync6(browserRoot)) {
107502
+ if (!existsSync7(browserRoot)) {
106986
107503
  await mkdir32(browserRoot, { recursive: true });
106987
107504
  }
106988
107505
  return await installUrl2(url, options, provider);
@@ -107014,7 +107531,7 @@ async function installDeps2(installedBrowser) {
107014
107531
  return;
107015
107532
  }
107016
107533
  const depsPath = path102.join(path102.dirname(installedBrowser.executablePath), "deb.deps");
107017
- if (!existsSync6(depsPath)) {
107534
+ if (!existsSync7(depsPath)) {
107018
107535
  debugInstall2?.(`deb.deps file was not found at ${depsPath}`);
107019
107536
  return;
107020
107537
  }
@@ -107055,14 +107572,14 @@ async function installUrl2(url, options, provider) {
107055
107572
  const cache = new Cache2(options.cacheDir);
107056
107573
  const browserRoot = cache.browserRoot(options.browser);
107057
107574
  const archivePath = path102.join(browserRoot, `${options.buildId}-${fileName}`);
107058
- if (!existsSync6(browserRoot)) {
107575
+ if (!existsSync7(browserRoot)) {
107059
107576
  await mkdir32(browserRoot, { recursive: true });
107060
107577
  }
107061
107578
  if (options.expectedHash) {
107062
107579
  debugInstall2?.(`Using provided checksum for ${fileName}: ${options.expectedHash}`);
107063
107580
  }
107064
107581
  if (!options.unpack) {
107065
- if (existsSync6(archivePath)) {
107582
+ if (existsSync7(archivePath)) {
107066
107583
  return archivePath;
107067
107584
  }
107068
107585
  debugInstall2?.(`Downloading binary from ${url}`);
@@ -107083,8 +107600,8 @@ async function installUrl2(url, options, provider) {
107083
107600
  cache.writeExecutablePath(options.browser, options.platform, options.buildId, relativeExecutablePath62);
107084
107601
  }
107085
107602
  try {
107086
- if (existsSync6(outputPath)) {
107087
- if (!existsSync6(installedBrowser.executablePath)) {
107603
+ if (existsSync7(outputPath)) {
107604
+ if (!existsSync7(installedBrowser.executablePath)) {
107088
107605
  throw new Error(`The browser folder (${outputPath}) exists but the executable (${installedBrowser.executablePath}) is missing`);
107089
107606
  }
107090
107607
  await runSetup2(installedBrowser);
@@ -107093,7 +107610,7 @@ async function installUrl2(url, options, provider) {
107093
107610
  }
107094
107611
  return installedBrowser;
107095
107612
  }
107096
- if (!existsSync6(archivePath)) {
107613
+ if (!existsSync7(archivePath)) {
107097
107614
  debugInstall2?.(`Downloading binary from ${url}`);
107098
107615
  try {
107099
107616
  debugTime2("download");
@@ -107122,7 +107639,7 @@ async function installUrl2(url, options, provider) {
107122
107639
  }
107123
107640
  return installedBrowser;
107124
107641
  } finally {
107125
- if (existsSync6(archivePath)) {
107642
+ if (existsSync7(archivePath)) {
107126
107643
  await unlink3(archivePath);
107127
107644
  }
107128
107645
  }
@@ -107133,7 +107650,7 @@ async function runSetup2(installedBrowser) {
107133
107650
  debugTime2("permissions");
107134
107651
  const browserDir = path102.dirname(installedBrowser.executablePath);
107135
107652
  const setupExePath = path102.join(browserDir, "setup.exe");
107136
- if (!existsSync6(setupExePath)) {
107653
+ if (!existsSync7(setupExePath)) {
107137
107654
  return;
107138
107655
  }
107139
107656
  spawnSync22(path102.join(browserDir, "setup.exe"), [`--configure-browser-in-directory=` + browserDir], {
@@ -108832,7 +109349,7 @@ function getBrowserTypeDisplayName2(browserType) {
108832
109349
  return browserType;
108833
109350
  }
108834
109351
  }
108835
- async function rm4(path142) {
109352
+ async function rm5(path142) {
108836
109353
  await fs62.promises.rm(path142, rmOptions2);
108837
109354
  }
108838
109355
  function getFeatures2(flag, options = []) {
@@ -114040,7 +114557,7 @@ function buildEncoderArgs(options, inputArgs, outputPath, gpuEncoder = null) {
114040
114557
  async function encodeFramesFromDir(framesDir, framePattern, outputPath, options, signal, config22) {
114041
114558
  const startTime = Date.now();
114042
114559
  const outputDir = dirname72(outputPath);
114043
- if (!existsSync7(outputDir)) mkdirSync2(outputDir, { recursive: true });
114560
+ if (!existsSync72(outputDir)) mkdirSync2(outputDir, { recursive: true });
114044
114561
  const files = readdirSync4(framesDir).filter((f2) => f2.match(/\.(jpg|jpeg|png)$/i));
114045
114562
  const frameCount = files.length;
114046
114563
  if (frameCount === 0) {
@@ -114086,7 +114603,7 @@ async function encodeFramesFromDir(framesDir, framePattern, outputPath, options,
114086
114603
  )
114087
114604
  };
114088
114605
  }
114089
- const fileSize = existsSync7(outputPath) ? statSync32(outputPath).size : 0;
114606
+ const fileSize = existsSync72(outputPath) ? statSync32(outputPath).size : 0;
114090
114607
  return {
114091
114608
  success: true,
114092
114609
  outputPath,
@@ -114111,7 +114628,7 @@ async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, op
114111
114628
  const chunkSize = Math.max(30, Math.floor(chunkSizeFrames));
114112
114629
  const chunkCount = Math.ceil(files.length / chunkSize);
114113
114630
  const chunkDir = join102(dirname72(outputPath), "chunk-encode");
114114
- if (!existsSync7(chunkDir)) mkdirSync2(chunkDir, { recursive: true });
114631
+ if (!existsSync72(chunkDir)) mkdirSync2(chunkDir, { recursive: true });
114115
114632
  const chunkPaths = [];
114116
114633
  for (let i3 = 0; i3 < chunkCount; i3++) {
114117
114634
  if (signal?.aborted) {
@@ -114199,7 +114716,7 @@ async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, op
114199
114716
  error: concatResult.error
114200
114717
  };
114201
114718
  }
114202
- const fileSize = existsSync7(outputPath) ? statSync32(outputPath).size : 0;
114719
+ const fileSize = existsSync72(outputPath) ? statSync32(outputPath).size : 0;
114203
114720
  return {
114204
114721
  success: true,
114205
114722
  outputPath,
@@ -114210,7 +114727,7 @@ async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, op
114210
114727
  }
114211
114728
  async function muxVideoWithAudio(videoPath, audioPath, outputPath, signal, config22, fps) {
114212
114729
  const outputDir = dirname72(outputPath);
114213
- if (!existsSync7(outputDir)) mkdirSync2(outputDir, { recursive: true });
114730
+ if (!existsSync72(outputDir)) mkdirSync2(outputDir, { recursive: true });
114214
114731
  const isWebm = outputPath.endsWith(".webm");
114215
114732
  const isMov = outputPath.endsWith(".mov");
114216
114733
  const shouldCopyAudio = isWebm ? false : await shouldCopyAacSidecar(audioPath, config22);
@@ -114716,7 +115233,7 @@ async function downloadToTemp(url, destDir, timeoutMs = 3e5) {
114716
115233
  mkdirSync4(destDir, { recursive: true });
114717
115234
  }
114718
115235
  const filename = getFilenameFromUrl(url);
114719
- const localPath = join11(destDir, filename);
115236
+ const localPath = join112(destDir, filename);
114720
115237
  if (existsSync9(localPath)) {
114721
115238
  downloadPathCache.set(url, localPath);
114722
115239
  return localPath;
@@ -123899,7 +124416,7 @@ function composeSignal(signal, deadlineAtMs) {
123899
124416
  function createSiblingTransactionDirectory(destination) {
123900
124417
  const parent = dirname12(destination);
123901
124418
  const extension2 = extname7(destination);
123902
- const stem = extension2 ? basename5(destination, extension2) : basename5(destination);
124419
+ const stem = extension2 ? basename52(destination, extension2) : basename52(destination);
123903
124420
  return mkdtempSync2(join222(parent, `.${stem}.hf-transaction-`));
123904
124421
  }
123905
124422
  function assertReadableNonEmptyFile(path142) {
@@ -247165,7 +247682,7 @@ If you think this is a bug, please report it on the Puppeteer issue tracker.`;
247165
247682
  node_default2 = {
247166
247683
  fs: {
247167
247684
  readFileSync: readFileSync42,
247168
- writeFile: writeFile9
247685
+ writeFile: writeFile10
247169
247686
  },
247170
247687
  format: format22,
247171
247688
  resolve: resolve62,
@@ -250761,7 +251278,7 @@ For (2), check out our guide on configuring puppeteer at https://pptr.dev/guides
250761
251278
  async cleanUserDataDir(path142, opts) {
250762
251279
  if (opts.isTemp) {
250763
251280
  try {
250764
- await rm4(path142);
251281
+ await rm5(path142);
250765
251282
  } catch (error) {
250766
251283
  debugError2?.(error);
250767
251284
  throw error;
@@ -250958,7 +251475,7 @@ For (2), check out our guide on configuring puppeteer at https://pptr.dev/guides
250958
251475
  async cleanUserDataDir(userDataDir, opts) {
250959
251476
  if (opts.isTemp) {
250960
251477
  try {
250961
- await rm4(userDataDir);
251478
+ await rm5(userDataDir);
250962
251479
  } catch (error) {
250963
251480
  debugError2?.(error);
250964
251481
  throw error;
@@ -250972,7 +251489,7 @@ For (2), check out our guide on configuring puppeteer at https://pptr.dev/guides
250972
251489
  if (fs72.existsSync(prefsBackupPath)) {
250973
251490
  const prefsPath = path132.join(userDataDir, file);
250974
251491
  await unlink22(prefsPath);
250975
- await rename3(prefsBackupPath, prefsPath);
251492
+ await rename4(prefsBackupPath, prefsPath);
250976
251493
  }
250977
251494
  }));
250978
251495
  for (const result of results) {
@@ -266625,7 +267142,7 @@ ${allInlineStyles}`.replace(/\/\*[\s\S]*?\*\//g, "");
266625
267142
  this.fileSystem = fileSystem;
266626
267143
  this.destinationPath = resolve132(destinationPath);
266627
267144
  this.transactionDirectory = createSiblingTransactionDirectory(this.destinationPath);
266628
- this.stagingPath = join222(this.transactionDirectory, basename5(this.destinationPath));
267145
+ this.stagingPath = join222(this.transactionDirectory, basename52(this.destinationPath));
266629
267146
  this.backupPath = join222(this.transactionDirectory, "backup");
266630
267147
  }
266631
267148
  destinationPath;
@@ -267495,7 +268012,7 @@ Check that each file referenced by data-composition-src contains valid HTML with
267495
268012
  });
267496
268013
 
267497
268014
  // ../cli/src/commands/_shared/scene-lint.ts
267498
- import { readdir as readdir5, readFile as readFile8, writeFile as writeFile10, stat as stat4 } from "node:fs/promises";
268015
+ import { readdir as readdir6, readFile as readFile9, writeFile as writeFile11, stat as stat4 } from "node:fs/promises";
267499
268016
  import { existsSync as existsSync38 } from "node:fs";
267500
268017
  import { resolve as resolve21, relative as relative5, join as join53 } from "node:path";
267501
268018
  async function discoverSceneFiles(opts) {
@@ -267511,7 +268028,7 @@ async function discoverSceneFiles(opts) {
267511
268028
  return { root: root2, subs };
267512
268029
  }
267513
268030
  async function collectHtmlRecursive(dir, into) {
267514
- const entries2 = await readdir5(dir, { withFileTypes: true });
268031
+ const entries2 = await readdir6(dir, { withFileTypes: true });
267515
268032
  for (const entry of entries2) {
267516
268033
  const full = join53(dir, entry.name);
267517
268034
  if (entry.isDirectory()) {
@@ -267597,7 +268114,7 @@ async function runProjectLint(opts) {
267597
268114
  for (const sub of subs) targets.push({ abs: sub, isSub: true });
267598
268115
  for (const { abs, isSub } of targets) {
267599
268116
  const rel = relative5(projectDir, abs) || abs;
267600
- const html = await readFile8(abs, "utf-8");
268117
+ const html = await readFile9(abs, "utf-8");
267601
268118
  const prepared = {
267602
268119
  html,
267603
268120
  entryFile: rel,
@@ -267612,7 +268129,7 @@ async function runProjectLint(opts) {
267612
268129
  if (opts.fix && findings.length > 0) {
267613
268130
  const { html: nextHtml, fixedCodes } = applyMechanicalFixes(html, findings);
267614
268131
  if (fixedCodes.length > 0) {
267615
- await writeFile10(abs, nextHtml, "utf-8");
268132
+ await writeFile11(abs, nextHtml, "utf-8");
267616
268133
  fixed.push({ file: rel, codes: fixedCodes });
267617
268134
  const reLinted = await runHyperframeLint({ ...prepared, html: nextHtml });
267618
268135
  files.push({
@@ -268213,7 +268730,7 @@ var init_compose_beat_prompt = __esm({
268213
268730
 
268214
268731
  // ../cli/src/commands/_shared/root-sync.ts
268215
268732
  import { existsSync as existsSync40 } from "node:fs";
268216
- import { readFile as readFile9, writeFile as writeFile11 } from "node:fs/promises";
268733
+ import { readFile as readFile10, writeFile as writeFile12 } from "node:fs/promises";
268217
268734
  import { join as join55, resolve as resolve24 } from "node:path";
268218
268735
  async function createRootSyncPlan(opts) {
268219
268736
  const projectDir = resolve24(opts.projectDir);
@@ -268222,7 +268739,7 @@ async function createRootSyncPlan(opts) {
268222
268739
  if (!existsSync40(rootPath)) {
268223
268740
  return { rootPath, rootRel, exists: false, changed: false, fixCodes: [], issues: [] };
268224
268741
  }
268225
- const html = await readFile9(rootPath, "utf-8");
268742
+ const html = await readFile10(rootPath, "utf-8");
268226
268743
  if (opts.beats.length === 0) {
268227
268744
  return { rootPath, rootRel, exists: true, changed: false, fixCodes: [], issues: [] };
268228
268745
  }
@@ -268276,14 +268793,14 @@ async function createProjectRootSyncPlan(opts) {
268276
268793
  }
268277
268794
  async function syncRootComposition(opts) {
268278
268795
  const plan2 = await createRootSyncPlan(opts);
268279
- if (plan2.nextHtml) await writeFile11(plan2.rootPath, plan2.nextHtml, "utf-8");
268796
+ if (plan2.nextHtml) await writeFile12(plan2.rootPath, plan2.nextHtml, "utf-8");
268280
268797
  return plan2;
268281
268798
  }
268282
268799
  async function loadProjectRootSyncBeats(projectDir) {
268283
268800
  const root2 = resolve24(projectDir);
268284
268801
  const storyboardPath = join55(root2, "STORYBOARD.md");
268285
268802
  if (!existsSync40(storyboardPath)) return [];
268286
- const parsed = parseStoryboard(await readFile9(storyboardPath, "utf-8"));
268803
+ const parsed = parseStoryboard(await loadStoryboardMarkdown(root2));
268287
268804
  const reportBeats = await readBuildReportBeats(root2);
268288
268805
  return parsed.beats.map((beat) => {
268289
268806
  const reportBeat = reportBeats.find((item) => item.id === beat.id);
@@ -268511,7 +269028,7 @@ async function readBuildReportBeats(projectDir) {
268511
269028
  const reportPath = join55(projectDir, "build-report.json");
268512
269029
  if (!existsSync40(reportPath)) return [];
268513
269030
  try {
268514
- const report = JSON.parse(await readFile9(reportPath, "utf-8"));
269031
+ const report = JSON.parse(await readFile10(reportPath, "utf-8"));
268515
269032
  return Array.isArray(report.beats) ? report.beats : [];
268516
269033
  } catch {
268517
269034
  return [];
@@ -268541,6 +269058,7 @@ var init_root_sync = __esm({
268541
269058
  "use strict";
268542
269059
  init_audio();
268543
269060
  init_storyboard_parse();
269061
+ init_storyboard_source();
268544
269062
  ROOT_SYNC_FIX_CODES = {
268545
269063
  clipRefs: "root_clip_refs_synced",
268546
269064
  duration: "root_duration_synced",
@@ -268553,13 +269071,13 @@ var init_root_sync = __esm({
268553
269071
 
268554
269072
  // ../cli/src/commands/_shared/transcribe-narration.ts
268555
269073
  import { existsSync as existsSync41 } from "node:fs";
268556
- import { readFile as readFile10 } from "node:fs/promises";
269074
+ import { readFile as readFile11 } from "node:fs/promises";
268557
269075
  import { join as join56 } from "node:path";
268558
269076
  async function transcribeNarrationWords(audioAbsPath, opts) {
268559
269077
  try {
268560
269078
  const whisper = new WhisperProvider();
268561
269079
  await whisper.initialize({ apiKey: opts.apiKey });
268562
- const audioBytes = await readFile10(audioAbsPath);
269080
+ const audioBytes = await readFile11(audioAbsPath);
268563
269081
  const audioBlob = new Blob([new Uint8Array(audioBytes)]);
268564
269082
  const transcript = await whisper.transcribe(audioBlob, void 0, {
268565
269083
  granularity: "word",
@@ -268580,7 +269098,7 @@ async function readBeatTranscript(projectDir, beatId) {
268580
269098
  const abs = join56(projectDir, beatTranscriptRelPath(beatId));
268581
269099
  if (!existsSync41(abs)) return void 0;
268582
269100
  try {
268583
- const parsed = JSON.parse(await readFile10(abs, "utf-8"));
269101
+ const parsed = JSON.parse(await readFile11(abs, "utf-8"));
268584
269102
  if (!Array.isArray(parsed)) return void 0;
268585
269103
  const words = parsed.filter(
268586
269104
  (w4) => typeof w4 === "object" && w4 !== null && typeof w4.text === "string" && Number.isFinite(w4.start) && Number.isFinite(w4.end)
@@ -268599,7 +269117,6 @@ var init_transcribe_narration = __esm({
268599
269117
 
268600
269118
  // ../cli/src/commands/_shared/compose-prompts.ts
268601
269119
  import { existsSync as existsSync43 } from "node:fs";
268602
- import { readFile as readFile11 } from "node:fs/promises";
268603
269120
  import { join as join57, relative as relative6, resolve as resolve25 } from "node:path";
268604
269121
  async function getComposePrompts(opts) {
268605
269122
  const projectDir = resolve25(opts.projectDir);
@@ -268633,7 +269150,7 @@ async function getComposePrompts(opts) {
268633
269150
  `Hyperframes composition rules are not installed \u2014 you will be authoring without them in context. Run \`vibe scene install-skill\` (or \`${HYPERFRAMES_SKILL_INSTALL_COMMAND}\`) first.`
268634
269151
  );
268635
269152
  }
268636
- const storyboardMd = await readFile11(storyboardPath, "utf-8");
269153
+ const storyboardMd = await loadStoryboardMarkdown(projectDir);
268637
269154
  const parsed = parseStoryboard(storyboardMd);
268638
269155
  if (parsed.beats.length === 0) {
268639
269156
  return baseError(`STORYBOARD.md has no \`## Beat \u2026\` headings.`);
@@ -268714,7 +269231,8 @@ function buildInstructions(args) {
268714
269231
  if (args.compositionRef) {
268715
269232
  lines.push(`2b. Read \`${args.compositionRef}\` for the project STRUCTURAL contract (layout system, GSAP timeline conventions, element/track rules) \u2014 a HARD-GATE parallel to DESIGN.md; every composition must satisfy it.`);
268716
269233
  }
268717
- lines.push(`3. For each beat in the \`beats\` array below, author HTML at \`outputPath\` matching the \`userPrompt\`. The beat \`body\` carries the narrative + visual + animation intent; \`cues\` carries machine-readable per-beat overrides (narration, duration, backdrop, voice).`);
269234
+ lines.push(`3. For each beat in the \`beats\` array below, author HTML at \`outputPath\` matching the \`userPrompt\`. The beat \`body\` carries the narrative intent; \`cues\` carries machine-readable per-beat overrides (${STORYBOARD_CUE_KEYS.join(", ")}).`);
269235
+ lines.push(` The \`motion\` cue, when present, is the animation direction for this beat - treat it as binding on how elements enter, hold, and leave, the same way \`narration\` is binding on the audio.`);
268718
269236
  lines.push(`3b. Use each beat's \`finalDurationSec\` (narration-synced) for \`data-duration\` and timeline anchors when present \u2014 NOT the storyboard \`duration\`, which is only the minimum. Scenes composed at the storyboard duration end early and render black tails.`);
268719
269237
  lines.push(`3c. Never give inner \`.clip\` elements a non-zero \`data-start\` \u2014 the renderer does not toggle internal clip visibility inside sub-compositions, so phased clips render all phases at once (overlapping text). Use full-window clips and GSAP autoAlpha phase transitions instead. Also keep beats 6-15s; split anything longer in the storyboard first.`);
268720
269238
  lines.push(`3d. If your environment cannot write files (e.g. Claude Desktop / MCP-only hosts), author each beat's HTML and submit it with the \`scene_submit\` tool (beat id + html). It validates with the same Hyperframes lint and writes the file for you; on lint errors it returns the findings without writing \u2014 fix and resubmit.`);
@@ -268734,6 +269252,7 @@ var init_compose_prompts = __esm({
268734
269252
  "../cli/src/commands/_shared/compose-prompts.ts"() {
268735
269253
  "use strict";
268736
269254
  init_storyboard_parse();
269255
+ init_storyboard_source();
268737
269256
  init_compose_beat_prompt();
268738
269257
  init_root_sync();
268739
269258
  init_install_skill();
@@ -268743,7 +269262,7 @@ var init_compose_prompts = __esm({
268743
269262
 
268744
269263
  // ../cli/src/commands/_shared/sub-comp-duration-sync.ts
268745
269264
  import { existsSync as existsSync45 } from "node:fs";
268746
- import { readFile as readFile13 } from "node:fs/promises";
269265
+ import { readFile as readFile12 } from "node:fs/promises";
268747
269266
  import { join as join59, resolve as resolve27 } from "node:path";
268748
269267
  function syncSubCompositionDurationHtml(html, beatId, newDurationSec, fileRel) {
268749
269268
  const compositionId = `scene-${beatId}`;
@@ -268844,7 +269363,7 @@ async function createSubCompDurationSyncPlans(opts) {
268844
269363
  narrationPath: beat.narrationPath,
268845
269364
  sceneDurationSec: beat.sceneDurationSec
268846
269365
  });
268847
- const html = await readFile13(filePath, "utf-8");
269366
+ const html = await readFile12(filePath, "utf-8");
268848
269367
  const outcome = syncSubCompositionDurationHtml(html, beat.id, newDurationSec, fileRel);
268849
269368
  results.push({ ...outcome, file: fileRel, beatId: beat.id });
268850
269369
  }
@@ -268868,7 +269387,7 @@ var init_sub_comp_duration_sync = __esm({
268868
269387
  });
268869
269388
 
268870
269389
  // ../cli/src/commands/_shared/scene-repair.ts
268871
- import { readFile as readFile14, writeFile as writeFile13 } from "node:fs/promises";
269390
+ import { readFile as readFile13, writeFile as writeFile14 } from "node:fs/promises";
268872
269391
  import { relative as relative7, resolve as resolve28 } from "node:path";
268873
269392
  async function executeSceneRepair(opts) {
268874
269393
  const projectDir = resolve28(opts.projectDir);
@@ -268888,7 +269407,7 @@ async function executeSceneRepair(opts) {
268888
269407
  if (dryRun) {
268889
269408
  wouldFix.push(item);
268890
269409
  } else {
268891
- await writeFile13(rootSync.rootPath, rootSync.nextHtml, "utf-8");
269410
+ await writeFile14(rootSync.rootPath, rootSync.nextHtml, "utf-8");
268892
269411
  fixed.push(item);
268893
269412
  }
268894
269413
  } else if (rootSync.issues.length > 0) {
@@ -268904,7 +269423,7 @@ async function executeSceneRepair(opts) {
268904
269423
  if (dryRun) {
268905
269424
  wouldFix.push(item);
268906
269425
  } else {
268907
- await writeFile13(resolve28(projectDir, plan2.file), plan2.nextHtml, "utf-8");
269426
+ await writeFile14(resolve28(projectDir, plan2.file), plan2.nextHtml, "utf-8");
268908
269427
  fixed.push(item);
268909
269428
  }
268910
269429
  }
@@ -268913,7 +269432,7 @@ async function executeSceneRepair(opts) {
268913
269432
  }
268914
269433
  for (const target of targets) {
268915
269434
  const rel = relative7(projectDir, target.abs) || target.abs;
268916
- const html = await readFile14(target.abs, "utf-8");
269435
+ const html = await readFile13(target.abs, "utf-8");
268917
269436
  const findings = await lintHtml(html, rel, target.isSub);
268918
269437
  const { html: nextHtml, fixedCodes } = applyMechanicalFixes(html, findings);
268919
269438
  if (fixedCodes.length > 0) {
@@ -268921,7 +269440,7 @@ async function executeSceneRepair(opts) {
268921
269440
  if (dryRun) {
268922
269441
  wouldFix.push(item);
268923
269442
  } else {
268924
- await writeFile13(target.abs, nextHtml, "utf-8");
269443
+ await writeFile14(target.abs, nextHtml, "utf-8");
268925
269444
  fixed.push(item);
268926
269445
  }
268927
269446
  files.push({
@@ -268993,303 +269512,6 @@ var init_scene_repair = __esm({
268993
269512
  }
268994
269513
  });
268995
269514
 
268996
- // ../cli/src/commands/_shared/storyboard-edit.ts
268997
- function validateStoryboardMarkdown(markdown) {
268998
- const parsed = parseStoryboard(markdown);
268999
- const sections = splitBeatSections(markdown);
269000
- const issues = [];
269001
- if (parsed.beats.length === 0) {
269002
- issues.push({
269003
- severity: "error",
269004
- code: "NO_BEATS",
269005
- message: "STORYBOARD.md must contain at least one `## Beat ...` heading."
269006
- });
269007
- }
269008
- const seen = /* @__PURE__ */ new Map();
269009
- for (const beat of parsed.beats) {
269010
- seen.set(beat.id, (seen.get(beat.id) ?? 0) + 1);
269011
- }
269012
- for (const [id, count] of seen.entries()) {
269013
- if (count > 1) {
269014
- issues.push({
269015
- severity: "error",
269016
- code: "DUPLICATE_BEAT_ID",
269017
- beatId: id,
269018
- message: `Beat id "${id}" appears ${count} times. Beat ids must be unique.`
269019
- });
269020
- }
269021
- }
269022
- for (const section of sections) {
269023
- const cueBlock = readLeadingCueBlock(section.body);
269024
- if (cueBlock?.error) {
269025
- issues.push({
269026
- severity: "error",
269027
- code: "MALFORMED_CUE_YAML",
269028
- beatId: section.id,
269029
- message: `Beat "${section.id}" has malformed YAML cues: ${cueBlock.error}`
269030
- });
269031
- }
269032
- }
269033
- const characterPool = new Set(
269034
- Object.keys(
269035
- parsed.frontmatter?.characters ?? {}
269036
- )
269037
- );
269038
- for (const beat of parsed.beats) {
269039
- const cues = beat.cues ?? {};
269040
- for (const [key2, value] of Object.entries(cues)) {
269041
- if (!ALLOWED_CUE_KEYS.has(key2)) {
269042
- issues.push({
269043
- severity: "warning",
269044
- code: "UNKNOWN_CUE",
269045
- beatId: beat.id,
269046
- message: `Beat "${beat.id}" uses unknown cue "${key2}". Supported cues: ${STORYBOARD_CUE_KEYS.join(", ")}.`
269047
- });
269048
- continue;
269049
- }
269050
- if (key2 === "characters") {
269051
- const validShape = typeof value === "string" || Array.isArray(value) && value.every((v4) => typeof v4 === "string");
269052
- if (!validShape) {
269053
- issues.push({
269054
- severity: "error",
269055
- code: "INVALID_CHARACTERS_VALUE",
269056
- beatId: beat.id,
269057
- message: `Beat "${beat.id}" cue "characters" must be a character name or a list of names.`
269058
- });
269059
- continue;
269060
- }
269061
- for (const name of beatCharacterNames({ characters: value })) {
269062
- if (!characterPool.has(name)) {
269063
- issues.push({
269064
- severity: "warning",
269065
- code: "UNKNOWN_CHARACTER",
269066
- beatId: beat.id,
269067
- message: `Beat "${beat.id}" references character "${name}" not defined in the frontmatter \`characters:\` pool.`
269068
- });
269069
- }
269070
- }
269071
- continue;
269072
- }
269073
- if (key2 === "duration") {
269074
- if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
269075
- issues.push({
269076
- severity: "error",
269077
- code: "INVALID_DURATION",
269078
- beatId: beat.id,
269079
- message: `Beat "${beat.id}" cue "duration" must be a positive number of seconds.`
269080
- });
269081
- }
269082
- continue;
269083
- }
269084
- if (STRING_CUE_KEYS.has(key2) && typeof value !== "string") {
269085
- issues.push({
269086
- severity: "error",
269087
- code: "INVALID_CUE_VALUE",
269088
- beatId: beat.id,
269089
- message: `Beat "${beat.id}" cue "${key2}" must be a string.`
269090
- });
269091
- }
269092
- }
269093
- }
269094
- for (const beat of parsed.beats) {
269095
- if (beat.duration !== void 0 && beat.duration > MAX_RECOMMENDED_BEAT_SEC) {
269096
- issues.push({
269097
- severity: "warning",
269098
- code: "BEAT_DURATION_TOO_LONG",
269099
- beatId: beat.id,
269100
- message: `Beat "${beat.id}" is ${beat.duration}s \u2014 beats longer than ${MAX_RECOMMENDED_BEAT_SEC}s render static and overstuffed. Split it into 6-15s beats (a 90s video should have 6-8 beats).`
269101
- });
269102
- }
269103
- }
269104
- return {
269105
- ok: !issues.some((i3) => i3.severity === "error"),
269106
- beats: parsed.beats,
269107
- issues
269108
- };
269109
- }
269110
- function getStoryboardBeat(markdown, beatId) {
269111
- return parseStoryboard(markdown).beats.find((beat) => beat.id === beatId) ?? null;
269112
- }
269113
- function upsertStoryboardBeat(markdown, opts) {
269114
- const beatId = normalizeBeatId(opts.beatId);
269115
- const sections = splitBeatSections(markdown);
269116
- const existing = sections.find((section2) => section2.id === beatId);
269117
- const cues = storyboardCuesFromInput(opts);
269118
- if (existing) {
269119
- let next = markdown;
269120
- for (const [key2, value] of Object.entries(cues)) {
269121
- next = setStoryboardCue(next, { beatId, key: key2, value });
269122
- }
269123
- return { markdown: next, action: "updated" };
269124
- }
269125
- const section = buildStoryboardBeatSection({ ...opts, beatId, cues });
269126
- if (sections.length > 0 && isStarterStoryboard(markdown)) {
269127
- const global3 = markdown.slice(0, sections[0].start).trimEnd();
269128
- return {
269129
- markdown: `${global3}
269130
-
269131
- ${section}`,
269132
- action: "replaced-starter"
269133
- };
269134
- }
269135
- const prefix = markdown.trimEnd();
269136
- return {
269137
- markdown: `${prefix}${prefix ? "\n\n" : ""}${section}`,
269138
- action: "appended"
269139
- };
269140
- }
269141
- function setStoryboardCue(markdown, opts) {
269142
- const section = findBeatSection(markdown, opts.beatId);
269143
- if (!section) {
269144
- throw new Error(`Beat "${opts.beatId}" not found.`);
269145
- }
269146
- if (!ALLOWED_CUE_KEYS.has(opts.key)) {
269147
- throw new Error(`Unsupported cue "${opts.key}". Supported cues: ${STORYBOARD_CUE_KEYS.join(", ")}.`);
269148
- }
269149
- const cue = readLeadingCueBlock(section.body);
269150
- if (cue?.error) {
269151
- throw new Error(`Cannot edit malformed YAML cues for beat "${opts.beatId}": ${cue.error}`);
269152
- }
269153
- const cues = cue?.value ? { ...cue.value } : {};
269154
- if (opts.unset) {
269155
- delete cues[opts.key];
269156
- } else {
269157
- cues[opts.key] = normalizeCueValue(opts.key, opts.value);
269158
- }
269159
- const remainingBody = cue ? section.body.slice(cue.full.length).replace(/^\s*\n/, "") : section.body.replace(/^\s*\n/, "");
269160
- const cueBlock = Object.keys(cues).length > 0 ? "```yaml\n" + (0, import_yaml5.stringify)(cues, { lineWidth: 0 }).trimEnd() + "\n```\n\n" : "";
269161
- const nextBody = cueBlock + remainingBody.trimStart();
269162
- const nextSection = section.raw.slice(0, section.headingEnd - section.start) + "\n\n" + nextBody.trimEnd() + "\n";
269163
- return markdown.slice(0, section.start) + nextSection + markdown.slice(section.end);
269164
- }
269165
- function moveStoryboardBeat(markdown, opts) {
269166
- if (opts.beatId === opts.afterBeatId) return markdown;
269167
- const sections = splitBeatSections(markdown);
269168
- const movingIndex = sections.findIndex((section) => section.id === opts.beatId);
269169
- if (movingIndex === -1) throw new Error(`Beat "${opts.beatId}" not found.`);
269170
- const afterIndex = sections.findIndex((section) => section.id === opts.afterBeatId);
269171
- if (afterIndex === -1) throw new Error(`Beat "${opts.afterBeatId}" not found.`);
269172
- const global3 = sections.length > 0 ? markdown.slice(0, sections[0].start) : markdown;
269173
- const chunks = sections.map((section) => section.raw);
269174
- const [moving] = chunks.splice(movingIndex, 1);
269175
- const adjustedAfterIndex = movingIndex < afterIndex ? afterIndex - 1 : afterIndex;
269176
- chunks.splice(adjustedAfterIndex + 1, 0, moving);
269177
- return global3 + normalizeBeatChunks(chunks);
269178
- }
269179
- function normalizeBeatChunks(chunks) {
269180
- return chunks.map((chunk) => chunk.trim()).filter(Boolean).join("\n\n") + "\n";
269181
- }
269182
- function normalizeBeatId(id) {
269183
- return deriveBeatId(id);
269184
- }
269185
- function storyboardCuesFromInput(opts) {
269186
- const cues = {};
269187
- if (opts.duration !== void 0) cues.duration = opts.duration;
269188
- if (opts.narration && opts.narration.trim()) cues.narration = opts.narration.trim();
269189
- if (opts.backdrop && opts.backdrop.trim()) cues.backdrop = opts.backdrop.trim();
269190
- return cues;
269191
- }
269192
- function buildStoryboardBeatSection(opts) {
269193
- const title = normalizeTitle(opts.title ?? humanizeBeatId(opts.beatId));
269194
- const cueBlock = Object.keys(opts.cues).length > 0 ? "```yaml\n" + (0, import_yaml5.stringify)(opts.cues, { lineWidth: 0 }).trimEnd() + "\n```\n\n" : "";
269195
- const body = opts.body?.trim() || "Scene created with `vibe scene add`. Edit this body if the build flow should recompose it.";
269196
- return `## Beat ${opts.beatId} - ${title}
269197
-
269198
- ${cueBlock}${body.trimEnd()}
269199
- `;
269200
- }
269201
- function normalizeTitle(value) {
269202
- return value.replace(/\s+/g, " ").trim() || "Scene";
269203
- }
269204
- function humanizeBeatId(id) {
269205
- return id.split(/[-_]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ") || "Scene";
269206
- }
269207
- function isStarterStoryboard(markdown) {
269208
- const parsed = parseStoryboard(markdown);
269209
- if (parsed.beats.length !== 3) return false;
269210
- const ids = parsed.beats.map((beat) => beat.id);
269211
- if (ids.join(",") !== "hook,proof,close") return false;
269212
- return markdown.includes("Introduce the promise in one crisp sentence.") && markdown.includes("Show the mechanism or proof point that makes the promise believable.") && markdown.includes("Close with the action the viewer should remember.");
269213
- }
269214
- function normalizeCueValue(key2, value) {
269215
- if (key2 === "duration") {
269216
- const n2 = typeof value === "number" ? value : Number.parseFloat(String(value ?? ""));
269217
- if (!Number.isFinite(n2) || n2 <= 0) {
269218
- throw new Error("Cue duration must be a positive number.");
269219
- }
269220
- return n2;
269221
- }
269222
- if (typeof value !== "string") return value;
269223
- return value.trim();
269224
- }
269225
- function splitBeatSections(markdown) {
269226
- const normalized = markdown.replace(/\r\n/g, "\n");
269227
- const headings = [];
269228
- HEADING_RE2.lastIndex = 0;
269229
- let match2;
269230
- while ((match2 = HEADING_RE2.exec(normalized)) !== null) {
269231
- headings.push({ start: match2.index, end: match2.index + match2[0].length, line: match2[1].trim() });
269232
- }
269233
- return headings.map((heading, index) => {
269234
- const end = index + 1 < headings.length ? headings[index + 1].start : normalized.length;
269235
- return {
269236
- id: deriveBeatId(heading.line),
269237
- heading: heading.line,
269238
- start: heading.start,
269239
- headingEnd: heading.end,
269240
- end,
269241
- raw: normalized.slice(heading.start, end),
269242
- body: normalized.slice(heading.end, end).trim()
269243
- };
269244
- });
269245
- }
269246
- function findBeatSection(markdown, beatId) {
269247
- return splitBeatSections(markdown).find((section) => section.id === beatId) ?? null;
269248
- }
269249
- function readLeadingCueBlock(body) {
269250
- const match2 = body.match(LEADING_CUE_RE);
269251
- if (!match2) return null;
269252
- try {
269253
- const parsed = (0, import_yaml5.parse)(match2[2]);
269254
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
269255
- return { full: match2[0], error: "cue block must parse to a YAML object" };
269256
- }
269257
- return { full: match2[0], value: parsed };
269258
- } catch (error) {
269259
- return { full: match2[0], error: error instanceof Error ? error.message : String(error) };
269260
- }
269261
- }
269262
- var import_yaml5, STORYBOARD_CUE_KEYS, HEADING_RE2, LEADING_CUE_RE, ALLOWED_CUE_KEYS, STRING_CUE_KEYS, MAX_RECOMMENDED_BEAT_SEC;
269263
- var init_storyboard_edit = __esm({
269264
- "../cli/src/commands/_shared/storyboard-edit.ts"() {
269265
- "use strict";
269266
- import_yaml5 = __toESM(require_dist3(), 1);
269267
- init_storyboard_parse();
269268
- STORYBOARD_CUE_KEYS = [
269269
- "duration",
269270
- "narration",
269271
- "backdrop",
269272
- "video",
269273
- "keyframe",
269274
- "motion",
269275
- "voice",
269276
- "music",
269277
- "asset",
269278
- "characters",
269279
- "eyebrow",
269280
- "title",
269281
- "caption",
269282
- "kicker",
269283
- "sub"
269284
- ];
269285
- HEADING_RE2 = /^##\s+(.+?)\s*$/gm;
269286
- LEADING_CUE_RE = /^(\s*)```ya?ml\s*\n([\s\S]*?)\n```\s*(?:\n|$)/;
269287
- ALLOWED_CUE_KEYS = new Set(STORYBOARD_CUE_KEYS);
269288
- STRING_CUE_KEYS = /* @__PURE__ */ new Set(["narration", "backdrop", "video", "keyframe", "motion", "voice", "music", "asset", "eyebrow", "title", "caption", "kicker", "sub"]);
269289
- MAX_RECOMMENDED_BEAT_SEC = 15;
269290
- }
269291
- });
269292
-
269293
269515
  // ../cli/src/pipeline/renderers/chrome.ts
269294
269516
  import { existsSync as existsSync47 } from "node:fs";
269295
269517
  import { homedir as homedir7 } from "node:os";
@@ -269365,7 +269587,7 @@ var init_ffmpeg_gate = __esm({
269365
269587
  });
269366
269588
 
269367
269589
  // ../cli/src/commands/_shared/scene-audio-scan.ts
269368
- import { readFile as readFile16 } from "node:fs/promises";
269590
+ import { readFile as readFile15 } from "node:fs/promises";
269369
269591
  import { resolve as resolve30 } from "node:path";
269370
269592
  function parseRootClips(rootHtml) {
269371
269593
  const clips = [];
@@ -269406,7 +269628,7 @@ function makeFsCompositionReader(projectDir) {
269406
269628
  return async (compositionSrcRel) => {
269407
269629
  const abs = resolve30(projectDir, compositionSrcRel);
269408
269630
  try {
269409
- return await readFile16(abs, "utf-8");
269631
+ return await readFile15(abs, "utf-8");
269410
269632
  } catch {
269411
269633
  return null;
269412
269634
  }
@@ -269472,7 +269694,7 @@ var init_scene_audio_scan = __esm({
269472
269694
  });
269473
269695
 
269474
269696
  // ../cli/src/commands/_shared/scene-audio-mux.ts
269475
- import { rename as rename4, unlink as unlink4 } from "node:fs/promises";
269697
+ import { rename as rename5, unlink as unlink4 } from "node:fs/promises";
269476
269698
  import { resolve as resolve31, dirname as dirname25, extname as extname10, basename as basename7 } from "node:path";
269477
269699
  function buildAudioMuxFilter(audios) {
269478
269700
  if (audios.length === 0) return null;
@@ -269581,7 +269803,7 @@ async function muxAudioIntoVideo(opts) {
269581
269803
  error: `ffmpeg mux failed: ${msg}`
269582
269804
  };
269583
269805
  }
269584
- await rename4(tmpPath, opts.videoPath);
269806
+ await rename5(tmpPath, opts.videoPath);
269585
269807
  return {
269586
269808
  success: true,
269587
269809
  outputPath: opts.videoPath,
@@ -269597,7 +269819,7 @@ var init_scene_audio_mux = __esm({
269597
269819
 
269598
269820
  // ../cli/src/commands/_shared/scene-assemble.ts
269599
269821
  import { existsSync as existsSync48 } from "node:fs";
269600
- import { readFile as readFile17 } from "node:fs/promises";
269822
+ import { readFile as readFile16 } from "node:fs/promises";
269601
269823
  import { resolve as resolve32, isAbsolute as isAbsolute7 } from "node:path";
269602
269824
  async function executeSceneAssemble(opts) {
269603
269825
  const projectDir = resolve32(opts.projectDir);
@@ -269628,7 +269850,7 @@ async function executeSceneAssemble(opts) {
269628
269850
  let audioMuxWarning;
269629
269851
  try {
269630
269852
  opts.onProgress?.(0.1, "Scanning audio");
269631
- const rootHtml = await readFile17(rootPath, "utf-8");
269853
+ const rootHtml = await readFile16(rootPath, "utf-8");
269632
269854
  const audios = await scanSceneAudio({ projectDir, rootHtml });
269633
269855
  audioCount = audios.length;
269634
269856
  if (audios.length > 0) {
@@ -269682,7 +269904,7 @@ __export(scene_render_exports, {
269682
269904
  });
269683
269905
  import { execFile as execFile4 } from "node:child_process";
269684
269906
  import { existsSync as existsSync49 } from "node:fs";
269685
- import { mkdir as mkdir11, readFile as readFile18, stat as stat5, writeFile as writeFile15 } from "node:fs/promises";
269907
+ import { mkdir as mkdir12, readFile as readFile17, stat as stat5, writeFile as writeFile16 } from "node:fs/promises";
269686
269908
  import { resolve as resolve33, relative as relative9, dirname as dirname26, basename as basename8, join as join61, isAbsolute as isAbsolute8 } from "node:path";
269687
269909
  import { promisify as promisify4 } from "node:util";
269688
269910
  function qualityToCrf(quality = "standard") {
@@ -269828,7 +270050,7 @@ async function executeSceneRender(opts = {}) {
269828
270050
  }
269829
270051
  const projectName = projectConfig.config.name;
269830
270052
  const outputPath = opts.output ? resolve33(projectDir, opts.output) : defaultOutputPath({ projectDir, projectName, format: opts.format, beatId: opts.beatId });
269831
- await mkdir11(dirname26(outputPath), { recursive: true });
270053
+ await mkdir12(dirname26(outputPath), { recursive: true });
269832
270054
  const config4 = buildRenderConfig({
269833
270055
  fps: opts.fps,
269834
270056
  quality: opts.quality,
@@ -269955,7 +270177,7 @@ async function prepareBeatRenderRoot(opts) {
269955
270177
  }
269956
270178
  let report;
269957
270179
  try {
269958
- report = JSON.parse(await readFile18(reportPath, "utf-8"));
270180
+ report = JSON.parse(await readFile17(reportPath, "utf-8"));
269959
270181
  } catch (error) {
269960
270182
  return {
269961
270183
  success: false,
@@ -270024,8 +270246,8 @@ async function prepareBeatRenderRoot(opts) {
270024
270246
  durationSec,
270025
270247
  narrationPath
270026
270248
  });
270027
- await mkdir11(dirname26(join61(opts.projectDir, root2)), { recursive: true });
270028
- await writeFile15(join61(opts.projectDir, root2), html, "utf-8");
270249
+ await mkdir12(dirname26(join61(opts.projectDir, root2)), { recursive: true });
270250
+ await writeFile16(join61(opts.projectDir, root2), html, "utf-8");
270029
270251
  return {
270030
270252
  success: true,
270031
270253
  root: root2,
@@ -270091,7 +270313,7 @@ function reportedBeatAssetPaths(beat) {
270091
270313
  async function writeRenderReport(projectDir, result) {
270092
270314
  const reportPath = join61(projectDir, "render-report.json");
270093
270315
  try {
270094
- await writeFile15(
270316
+ await writeFile16(
270095
270317
  reportPath,
270096
270318
  JSON.stringify(
270097
270319
  {
@@ -270254,7 +270476,7 @@ function validateDesignMarkdown(md) {
270254
270476
  const fm = raw2.match(DESIGN_FRONTMATTER_RE);
270255
270477
  if (fm) {
270256
270478
  try {
270257
- const parsed = (0, import_yaml6.parse)(fm[1]);
270479
+ const parsed = (0, import_yaml7.parse)(fm[1]);
270258
270480
  if (!isPlainObject5(parsed)) {
270259
270481
  issues.push({
270260
270482
  severity: "warning",
@@ -270295,11 +270517,11 @@ function validateDesignMarkdown(md) {
270295
270517
  }
270296
270518
  return issues;
270297
270519
  }
270298
- var import_yaml6, DESIGN_FRONTMATTER_RE, SECTION_RE, HEX_RE;
270520
+ var import_yaml7, DESIGN_FRONTMATTER_RE, SECTION_RE, HEX_RE;
270299
270521
  var init_design_parse = __esm({
270300
270522
  "../cli/src/commands/_shared/design-parse.ts"() {
270301
270523
  "use strict";
270302
- import_yaml6 = __toESM(require_dist3(), 1);
270524
+ import_yaml7 = __toESM(require_dist3(), 1);
270303
270525
  init_frontmatter();
270304
270526
  DESIGN_FRONTMATTER_RE = /^---\s*\n([\s\S]*?)\n---\s*(?:\n|$)/;
270305
270527
  SECTION_RE = /^##\s+(.+?)\s*$/gm;
@@ -270309,21 +270531,21 @@ var init_design_parse = __esm({
270309
270531
 
270310
270532
  // ../cli/src/commands/_shared/compose-aivideo.ts
270311
270533
  import { existsSync as existsSync50 } from "node:fs";
270312
- import { mkdir as mkdir12, readFile as readFile19, writeFile as writeFile16 } from "node:fs/promises";
270534
+ import { mkdir as mkdir13, readFile as readFile18, writeFile as writeFile17 } from "node:fs/promises";
270313
270535
  import { join as join63, resolve as resolve34 } from "node:path";
270314
270536
  function headingDescriptor(heading) {
270315
270537
  const stripped = heading.replace(/^\s*beat\s+/i, "");
270316
270538
  const m2 = stripped.match(/^[A-Za-z0-9][\w-]*(?:\s*[—:]\s*|\s+-\s+)(.+)$/);
270317
270539
  const desc = clean2(m2 ? m2[1] : stripped);
270318
- return /\s/.test(desc) || !/[-_]/.test(desc) ? desc : humanise2(desc);
270540
+ return /\s/.test(desc) || !/[-_]/.test(desc) ? desc : humanise3(desc);
270319
270541
  }
270320
- function humanise2(id) {
270542
+ function humanise3(id) {
270321
270543
  return id.split(/[-_\s]+/).filter(Boolean).map((w4) => w4.charAt(0).toUpperCase() + w4.slice(1)).join(" ");
270322
270544
  }
270323
270545
  function beatOverlayText(beat) {
270324
270546
  const cues = beat.cues ?? {};
270325
270547
  const descriptor = headingDescriptor(beat.heading);
270326
- const idHuman = humanise2(beat.id);
270548
+ const idHuman = humanise3(beat.id);
270327
270549
  const titleCue = clean2(cues.title);
270328
270550
  const title = titleCue || descriptor || idHuman;
270329
270551
  const kickerCue = clean2(
@@ -270459,8 +270681,7 @@ async function composeAiVideo(opts) {
270459
270681
  if (!commandExists("ffmpeg")) {
270460
270682
  throw new Error("ffmpeg not found in PATH \u2014 required to concatenate AI-video clips.");
270461
270683
  }
270462
- const storyboardPath = join63(projectDir, "STORYBOARD.md");
270463
- const md = await readFile19(storyboardPath, "utf-8");
270684
+ const md = await loadStoryboardMarkdown(projectDir);
270464
270685
  const beats = parseStoryboard(md).beats;
270465
270686
  if (beats.length === 0) throw new Error("STORYBOARD.md has no beats to compose.");
270466
270687
  const tokens = opts.tokens ?? await loadProjectTokens(projectDir);
@@ -270473,7 +270694,7 @@ async function composeAiVideo(opts) {
270473
270694
  }
270474
270695
  clipRels.push(rel);
270475
270696
  }
270476
- await mkdir12(join63(projectDir, "assets"), { recursive: true });
270697
+ await mkdir13(join63(projectDir, "assets"), { recursive: true });
270477
270698
  const bgRel = "assets/bg-full.mp4";
270478
270699
  const concatArgs = buildBgConcatArgs(
270479
270700
  clipRels.map((r2) => join63(projectDir, r2)),
@@ -270481,7 +270702,7 @@ async function composeAiVideo(opts) {
270481
270702
  );
270482
270703
  await execSafe("ffmpeg", concatArgs, { timeout: 6e5 });
270483
270704
  const totalDurationSec = await probeDurationSec(join63(projectDir, bgRel));
270484
- await mkdir12(join63(projectDir, "compositions"), { recursive: true });
270705
+ await mkdir13(join63(projectDir, "compositions"), { recursive: true });
270485
270706
  const sceneFiles = [];
270486
270707
  for (let i3 = 0; i3 < beats.length; i3++) {
270487
270708
  const beat = beats[i3];
@@ -270498,12 +270719,12 @@ async function composeAiVideo(opts) {
270498
270719
  isLast: i3 === beats.length - 1
270499
270720
  });
270500
270721
  const rel = `compositions/scene-${beat.id}.html`;
270501
- await writeFile16(join63(projectDir, rel), html, "utf-8");
270722
+ await writeFile17(join63(projectDir, rel), html, "utf-8");
270502
270723
  sceneFiles.push(rel);
270503
270724
  }
270504
270725
  const indexPath = join63(projectDir, "index.html");
270505
- const next = existsSync50(indexPath) ? injectBgVideo(await readFile19(indexPath, "utf-8"), bgRel, totalDurationSec) : buildRootShell(tokens, bgRel, totalDurationSec);
270506
- await writeFile16(indexPath, next, "utf-8");
270726
+ const next = existsSync50(indexPath) ? injectBgVideo(await readFile18(indexPath, "utf-8"), bgRel, totalDurationSec) : buildRootShell(tokens, bgRel, totalDurationSec);
270727
+ await writeFile17(indexPath, next, "utf-8");
270507
270728
  return { bgVideoRel: bgRel, sceneFiles, totalDurationSec, beats: beats.length };
270508
270729
  }
270509
270730
  function buildRootShell(tokens, bgRel, totalDuration) {
@@ -270554,7 +270775,7 @@ async function loadProjectTokens(projectDir) {
270554
270775
  const designPath = join63(projectDir, "DESIGN.md");
270555
270776
  if (!existsSync50(designPath)) return DEFAULT_AIVIDEO_TOKENS;
270556
270777
  try {
270557
- return extractDesignTokens(await readFile19(designPath, "utf-8"));
270778
+ return extractDesignTokens(await readFile18(designPath, "utf-8"));
270558
270779
  } catch {
270559
270780
  return DEFAULT_AIVIDEO_TOKENS;
270560
270781
  }
@@ -270613,6 +270834,7 @@ var init_compose_aivideo = __esm({
270613
270834
  "use strict";
270614
270835
  init_exec_safe();
270615
270836
  init_storyboard_parse();
270837
+ init_storyboard_source();
270616
270838
  init_design_parse();
270617
270839
  BG_VIDEO_TRACK = 9;
270618
270840
  DEFAULT_AIVIDEO_TOKENS = {
@@ -270906,7 +271128,6 @@ var init_composer_resolve = __esm({
270906
271128
 
270907
271129
  // ../cli/src/commands/_shared/build-plan.ts
270908
271130
  import { existsSync as existsSync53 } from "node:fs";
270909
- import { readFile as readFile20 } from "node:fs/promises";
270910
271131
  import { dirname as dirname27, join as join64, resolve as resolve36 } from "node:path";
270911
271132
  function backdropCostUsd(quality) {
270912
271133
  return quality === "hd" ? 0.2 : 0.05;
@@ -270958,9 +271179,13 @@ async function createBuildPlan(opts) {
270958
271179
  validation: validation4
270959
271180
  });
270960
271181
  }
270961
- const storyboardMd = await readFile20(storyboardPath, "utf-8");
271182
+ const loaded = await loadStoryboard(projectDir);
271183
+ const storyboardMd = loaded.markdown;
270962
271184
  const validation3 = validateStoryboardMarkdown(storyboardMd);
270963
- const validationIssues = [...validation3.issues];
271185
+ const validationIssues = [
271186
+ ...strayBeatIssues(loaded.strayBeatIds),
271187
+ ...validation3.issues
271188
+ ];
270964
271189
  const parsed = parseStoryboard(storyboardMd);
270965
271190
  let sourceBeats = parsed.beats;
270966
271191
  if (opts.beat) {
@@ -271618,6 +271843,7 @@ var init_build_plan = __esm({
271618
271843
  init_build_backdrop_prompt();
271619
271844
  init_composer_resolve();
271620
271845
  init_storyboard_parse();
271846
+ init_storyboard_source();
271621
271847
  init_project_config();
271622
271848
  init_scene_project();
271623
271849
  init_storyboard_edit();
@@ -271661,7 +271887,7 @@ var init_validate = __esm({
271661
271887
 
271662
271888
  // ../cli/src/commands/generate/music.ts
271663
271889
  import { resolve as resolve38 } from "node:path";
271664
- import { writeFile as writeFile17 } from "node:fs/promises";
271890
+ import { writeFile as writeFile18 } from "node:fs/promises";
271665
271891
  import { existsSync as existsSync54 } from "node:fs";
271666
271892
  async function executeMusic(options) {
271667
271893
  try {
@@ -271684,7 +271910,7 @@ async function executeMusic(options) {
271684
271910
  return { success: false, error: result2.error || "Music generation failed" };
271685
271911
  }
271686
271912
  const outputPath2 = resolve38(process.cwd(), options.output || "music.mp3");
271687
- await writeFile17(outputPath2, result2.audioBuffer);
271913
+ await writeFile18(outputPath2, result2.audioBuffer);
271688
271914
  return { success: true, outputPath: outputPath2, provider: "elevenlabs", duration: duration2 };
271689
271915
  }
271690
271916
  const apiKey = await getConfiguredApiKey("REPLICATE_API_TOKEN");
@@ -271718,7 +271944,7 @@ async function executeMusic(options) {
271718
271944
  return { success: false, error: "Failed to download generated audio" };
271719
271945
  const audioBuffer = Buffer.from(await response.arrayBuffer());
271720
271946
  const outputPath = resolve38(process.cwd(), options.output || "music.mp3");
271721
- await writeFile17(outputPath, audioBuffer);
271947
+ await writeFile18(outputPath, audioBuffer);
271722
271948
  return { success: true, outputPath, provider: "replicate", duration };
271723
271949
  } catch (error) {
271724
271950
  return {
@@ -271782,7 +272008,7 @@ function registerMusicCommand(parent) {
271782
272008
  exitWithError(apiError(result.error || "Music generation failed", true));
271783
272009
  }
271784
272010
  const outputPath = resolve38(process.cwd(), options.output);
271785
- await writeFile17(outputPath, result.audioBuffer);
272011
+ await writeFile18(outputPath, result.audioBuffer);
271786
272012
  spinner2.succeed(source_default.green("Music generated successfully"));
271787
272013
  if (isJsonMode()) {
271788
272014
  outputSuccess({
@@ -271868,7 +272094,7 @@ function registerMusicCommand(parent) {
271868
272094
  }
271869
272095
  const audioBuffer = Buffer.from(await response.arrayBuffer());
271870
272096
  const outputPath = resolve38(process.cwd(), options.output);
271871
- await writeFile17(outputPath, audioBuffer);
272097
+ await writeFile18(outputPath, audioBuffer);
271872
272098
  spinner2.succeed(source_default.green("Music generated successfully"));
271873
272099
  if (isJsonMode()) {
271874
272100
  outputSuccess({
@@ -271946,7 +272172,7 @@ __export(scene_build_exports, {
271946
272172
  resolveSceneBuildMode: () => resolveSceneBuildMode
271947
272173
  });
271948
272174
  import { existsSync as existsSync55, readFileSync as readFileSync21 } from "node:fs";
271949
- import { copyFile as copyFile4, mkdir as mkdir13, readFile as readFile21, rm as rm5, writeFile as writeFile18 } from "node:fs/promises";
272175
+ import { copyFile as copyFile4, mkdir as mkdir14, readFile as readFile19, rm as rm6, writeFile as writeFile19 } from "node:fs/promises";
271950
272176
  import { dirname as dirname28, join as join65, resolve as resolve39 } from "node:path";
271951
272177
  function readProjectKindSync(projectDir) {
271952
272178
  try {
@@ -272033,7 +272259,7 @@ async function executeSceneBuild(opts) {
272033
272259
  if (!buildPlan.validation.ok) {
272034
272260
  let invalidBeats = [];
272035
272261
  if (existsSync55(storyboardPath)) {
272036
- invalidBeats = parseStoryboard(await readFile21(storyboardPath, "utf-8")).beats;
272262
+ invalidBeats = parseStoryboard(await loadStoryboardMarkdown(projectDir)).beats;
272037
272263
  }
272038
272264
  return finishBuildResult({
272039
272265
  success: false,
@@ -272071,7 +272297,7 @@ async function executeSceneBuild(opts) {
272071
272297
  totalLatencyMs: Date.now() - startedAt
272072
272298
  });
272073
272299
  }
272074
- const storyboardMd = await readFile21(storyboardPath, "utf-8");
272300
+ const storyboardMd = await loadStoryboardMarkdown(projectDir);
272075
272301
  const parsed = parseStoryboard(storyboardMd);
272076
272302
  if (parsed.beats.length === 0) {
272077
272303
  return finishBuildResult({
@@ -272730,7 +272956,7 @@ async function dispatchNarration(beat, ctx) {
272730
272956
  };
272731
272957
  }
272732
272958
  if (existsSync55(cacheAbs) && !ctx.force) {
272733
- await mkdir13(dirname28(abs), { recursive: true });
272959
+ await mkdir14(dirname28(abs), { recursive: true });
272734
272960
  await copyFile4(cacheAbs, abs);
272735
272961
  await writeAssetMetadata({
272736
272962
  projectDir: ctx.projectDir,
@@ -272761,14 +272987,14 @@ async function dispatchNarration(beat, ctx) {
272761
272987
  ctx.onProgress({ type: "narration-failed", beatId: beat.id, error });
272762
272988
  return { status: "failed", error };
272763
272989
  }
272764
- await mkdir13(dirname28(abs), { recursive: true });
272765
- await writeFile18(abs, result.audioBuffer);
272990
+ await mkdir14(dirname28(abs), { recursive: true });
272991
+ await writeFile19(abs, result.audioBuffer);
272766
272992
  for (const otherExt of ["mp3", "wav"]) {
272767
272993
  const sibling = join65(ctx.projectDir, `assets/narration-${beat.id}.${otherExt}`);
272768
- if (sibling !== abs && existsSync55(sibling)) await rm5(sibling, { force: true });
272994
+ if (sibling !== abs && existsSync55(sibling)) await rm6(sibling, { force: true });
272769
272995
  }
272770
- await mkdir13(dirname28(cacheAbs), { recursive: true });
272771
- await writeFile18(cacheAbs, result.audioBuffer);
272996
+ await mkdir14(dirname28(cacheAbs), { recursive: true });
272997
+ await writeFile19(cacheAbs, result.audioBuffer);
272772
272998
  await writeAssetMetadata({
272773
272999
  projectDir: ctx.projectDir,
272774
273000
  kind: "narration",
@@ -272824,8 +273050,8 @@ async function dispatchTranscript(beat, narrationPath, narrationFresh, ctx) {
272824
273050
  return "skipped";
272825
273051
  }
272826
273052
  try {
272827
- await mkdir13(dirname28(abs), { recursive: true });
272828
- await writeFile18(abs, JSON.stringify(words, null, 2), "utf-8");
273053
+ await mkdir14(dirname28(abs), { recursive: true });
273054
+ await writeFile19(abs, JSON.stringify(words, null, 2), "utf-8");
272829
273055
  } catch (err) {
272830
273056
  ctx.onProgress({
272831
273057
  type: "transcript-skipped",
@@ -272912,7 +273138,7 @@ async function buildCharacters(characters, ctx) {
272912
273138
  }
272913
273139
  const cacheAbs = join65(ctx.projectDir, cache.path);
272914
273140
  if (existsSync55(cacheAbs) && !ctx.force) {
272915
- await mkdir13(dirname28(abs), { recursive: true });
273141
+ await mkdir14(dirname28(abs), { recursive: true });
272916
273142
  await copyFile4(cacheAbs, abs);
272917
273143
  await writeAssetMetadata({
272918
273144
  projectDir: ctx.projectDir,
@@ -272944,10 +273170,10 @@ async function buildCharacters(characters, ctx) {
272944
273170
  ctx.onProgress({ type: "character-failed", name: character.name, error: generated.error });
272945
273171
  continue;
272946
273172
  }
272947
- await mkdir13(dirname28(abs), { recursive: true });
272948
- await writeFile18(abs, generated.buffer);
272949
- await mkdir13(dirname28(cacheAbs), { recursive: true });
272950
- await writeFile18(cacheAbs, generated.buffer);
273173
+ await mkdir14(dirname28(abs), { recursive: true });
273174
+ await writeFile19(abs, generated.buffer);
273175
+ await mkdir14(dirname28(cacheAbs), { recursive: true });
273176
+ await writeFile19(cacheAbs, generated.buffer);
272951
273177
  await writeAssetMetadata({
272952
273178
  projectDir: ctx.projectDir,
272953
273179
  kind: "character",
@@ -273015,7 +273241,7 @@ async function dispatchBackdrop(beat, ctx) {
273015
273241
  }
273016
273242
  const cacheAbs = join65(ctx.projectDir, cache.path);
273017
273243
  if (existsSync55(cacheAbs) && !ctx.force) {
273018
- await mkdir13(dirname28(abs), { recursive: true });
273244
+ await mkdir14(dirname28(abs), { recursive: true });
273019
273245
  await copyFile4(cacheAbs, abs);
273020
273246
  await writeAssetMetadata({
273021
273247
  projectDir: ctx.projectDir,
@@ -273045,11 +273271,11 @@ async function dispatchBackdrop(beat, ctx) {
273045
273271
  ctx.onProgress({ type: "backdrop-failed", beatId: beat.id, error });
273046
273272
  return { status: "failed", error };
273047
273273
  }
273048
- await mkdir13(dirname28(abs), { recursive: true });
273274
+ await mkdir14(dirname28(abs), { recursive: true });
273049
273275
  const buffer = generated.buffer;
273050
- await writeFile18(abs, buffer);
273051
- await mkdir13(dirname28(cacheAbs), { recursive: true });
273052
- await writeFile18(cacheAbs, buffer);
273276
+ await writeFile19(abs, buffer);
273277
+ await mkdir14(dirname28(cacheAbs), { recursive: true });
273278
+ await writeFile19(cacheAbs, buffer);
273053
273279
  await writeAssetMetadata({
273054
273280
  projectDir: ctx.projectDir,
273055
273281
  kind: "backdrop",
@@ -273175,7 +273401,7 @@ async function ensureKeyframe(beat, ctx, keyframeCue, characterRefsRel) {
273175
273401
  }
273176
273402
  const cacheAbs = join65(ctx.projectDir, cache.path);
273177
273403
  if (existsSync55(cacheAbs) && !ctx.force) {
273178
- await mkdir13(dirname28(abs), { recursive: true });
273404
+ await mkdir14(dirname28(abs), { recursive: true });
273179
273405
  await copyFile4(cacheAbs, abs);
273180
273406
  await writeAssetMetadata({
273181
273407
  projectDir: ctx.projectDir,
@@ -273193,7 +273419,7 @@ async function ensureKeyframe(beat, ctx, keyframeCue, characterRefsRel) {
273193
273419
  const sheetBuffers = [];
273194
273420
  for (const relPath of characterRefsRel) {
273195
273421
  const sheetAbs = join65(ctx.projectDir, relPath);
273196
- if (existsSync55(sheetAbs)) sheetBuffers.push(await readFile21(sheetAbs));
273422
+ if (existsSync55(sheetAbs)) sheetBuffers.push(await readFile19(sheetAbs));
273197
273423
  }
273198
273424
  const imgCtx = {
273199
273425
  projectDir: ctx.projectDir,
@@ -273204,10 +273430,10 @@ async function ensureKeyframe(beat, ctx, keyframeCue, characterRefsRel) {
273204
273430
  const identityLock = "Keep the EXACT same person and face as the reference image(s): identical facial features (eyes, nose shape, jawline), skin tone and complexion, hairstyle, hair color, and wardrobe. Do not restyle or change the face. Change only the scene, pose, framing, and lighting. ";
273205
273431
  const generated = sheetBuffers.length > 0 ? await editKeyframeImage(`${identityLock}${keyframeCue}`, sheetBuffers, imgCtx, ratio) : await generateBackdropImage(keyframeCue, imgCtx, ratio);
273206
273432
  if (!generated.success) return { status: "failed", error: generated.error };
273207
- await mkdir13(dirname28(abs), { recursive: true });
273208
- await writeFile18(abs, generated.buffer);
273209
- await mkdir13(dirname28(cacheAbs), { recursive: true });
273210
- await writeFile18(cacheAbs, generated.buffer);
273433
+ await mkdir14(dirname28(abs), { recursive: true });
273434
+ await writeFile19(abs, generated.buffer);
273435
+ await mkdir14(dirname28(cacheAbs), { recursive: true });
273436
+ await writeFile19(cacheAbs, generated.buffer);
273211
273437
  await writeAssetMetadata({
273212
273438
  projectDir: ctx.projectDir,
273213
273439
  kind: "keyframe",
@@ -273301,7 +273527,7 @@ async function dispatchVideo(beat, ctx, keyframe) {
273301
273527
  }
273302
273528
  const cacheAbs = join65(ctx.projectDir, cache.path);
273303
273529
  if (existsSync55(cacheAbs) && !ctx.force) {
273304
- await mkdir13(dirname28(abs), { recursive: true });
273530
+ await mkdir14(dirname28(abs), { recursive: true });
273305
273531
  await copyFile4(cacheAbs, abs);
273306
273532
  await writeAssetMetadata({
273307
273533
  projectDir: ctx.projectDir,
@@ -273356,7 +273582,7 @@ async function dispatchVideo(beat, ctx, keyframe) {
273356
273582
  return { status: "failed", error };
273357
273583
  }
273358
273584
  if (result.status === "completed" && existsSync55(abs)) {
273359
- await mkdir13(dirname28(cacheAbs), { recursive: true });
273585
+ await mkdir14(dirname28(cacheAbs), { recursive: true });
273360
273586
  await copyFile4(abs, cacheAbs);
273361
273587
  await writeAssetMetadata({
273362
273588
  projectDir: ctx.projectDir,
@@ -273458,7 +273684,7 @@ async function dispatchMusic(beat, ctx) {
273458
273684
  }
273459
273685
  const cacheAbs = join65(ctx.projectDir, cache.path);
273460
273686
  if (existsSync55(cacheAbs) && !ctx.force) {
273461
- await mkdir13(dirname28(abs), { recursive: true });
273687
+ await mkdir14(dirname28(abs), { recursive: true });
273462
273688
  await copyFile4(cacheAbs, abs);
273463
273689
  await writeAssetMetadata({
273464
273690
  projectDir: ctx.projectDir,
@@ -273536,7 +273762,7 @@ async function dispatchMusic(beat, ctx) {
273536
273762
  ctx.onProgress({ type: "music-failed", beatId: beat.id, error });
273537
273763
  return { status: "failed", error };
273538
273764
  }
273539
- await mkdir13(dirname28(cacheAbs), { recursive: true });
273765
+ await mkdir14(dirname28(cacheAbs), { recursive: true });
273540
273766
  await copyFile4(abs, cacheAbs);
273541
273767
  await writeAssetMetadata({
273542
273768
  projectDir: ctx.projectDir,
@@ -273836,7 +274062,7 @@ async function finalizeBuildResult(projectDir, startedAt, result) {
273836
274062
  totalLatencyMs: Date.now() - startedAt
273837
274063
  };
273838
274064
  try {
273839
- await writeFile18(
274065
+ await writeFile19(
273840
274066
  reportPath,
273841
274067
  JSON.stringify(toBuildReport(projectDir, withMeta), null, 2) + "\n",
273842
274068
  "utf-8"
@@ -274074,6 +274300,7 @@ var init_scene_build = __esm({
274074
274300
  init_transcribe_narration();
274075
274301
  init_scene_render();
274076
274302
  init_storyboard_parse();
274303
+ init_storyboard_source();
274077
274304
  init_scene_project();
274078
274305
  init_build_plan();
274079
274306
  init_build_asset_reference();
@@ -274200,7 +274427,7 @@ __export(ai_audio_exports, {
274200
274427
  executeVoiceClone: () => executeVoiceClone
274201
274428
  });
274202
274429
  import { resolve as resolve41, dirname as dirname29, basename as basename9, extname as extname12 } from "node:path";
274203
- import { readFile as readFile23, writeFile as writeFile19 } from "node:fs/promises";
274430
+ import { readFile as readFile20, writeFile as writeFile20 } from "node:fs/promises";
274204
274431
  import { existsSync as existsSync56 } from "node:fs";
274205
274432
  async function executeTranscribe(options) {
274206
274433
  const { audioPath, language, output: output3, format: format4, apiKey } = options;
@@ -274211,7 +274438,7 @@ async function executeTranscribe(options) {
274211
274438
  if (!existsSync56(absPath)) return { success: false, error: `File not found: ${absPath}` };
274212
274439
  const whisper = new WhisperProvider();
274213
274440
  await whisper.initialize({ apiKey: key2 });
274214
- const audioBuffer = await readFile23(absPath);
274441
+ const audioBuffer = await readFile20(absPath);
274215
274442
  const audioBlob = new Blob([audioBuffer]);
274216
274443
  const result = await whisper.transcribe(audioBlob, language);
274217
274444
  if (result.status === "failed") {
@@ -274222,7 +274449,7 @@ async function executeTranscribe(options) {
274222
274449
  outputPath = resolve41(process.cwd(), output3);
274223
274450
  const fmt = detectFormat(output3, format4);
274224
274451
  const content = formatTranscript(result, fmt);
274225
- await writeFile19(outputPath, content, "utf-8");
274452
+ await writeFile20(outputPath, content, "utf-8");
274226
274453
  }
274227
274454
  return {
274228
274455
  success: true,
@@ -274242,7 +274469,7 @@ async function executeIsolate(options) {
274242
274469
  if (!key2) return { success: false, error: "ELEVENLABS_API_KEY required" };
274243
274470
  const absPath = resolve41(process.cwd(), audioPath);
274244
274471
  if (!existsSync56(absPath)) return { success: false, error: `File not found: ${absPath}` };
274245
- const audioBuffer = await readFile23(absPath);
274472
+ const audioBuffer = await readFile20(absPath);
274246
274473
  const elevenlabs = new ElevenLabsProvider();
274247
274474
  await elevenlabs.initialize({ apiKey: key2 });
274248
274475
  const result = await elevenlabs.isolateVocals(audioBuffer);
@@ -274250,7 +274477,7 @@ async function executeIsolate(options) {
274250
274477
  return { success: false, error: result.error || "Audio isolation failed" };
274251
274478
  }
274252
274479
  const outputPath = resolve41(process.cwd(), output3);
274253
- await writeFile19(outputPath, result.audioBuffer);
274480
+ await writeFile20(outputPath, result.audioBuffer);
274254
274481
  return { success: true, outputPath };
274255
274482
  } catch (error) {
274256
274483
  return { success: false, error: `Audio isolation failed: ${error instanceof Error ? error.message : String(error)}` };
@@ -274268,7 +274495,7 @@ async function executeVoiceClone(options) {
274268
274495
  for (const samplePath of samplePaths) {
274269
274496
  const absPath = resolve41(process.cwd(), samplePath);
274270
274497
  if (!existsSync56(absPath)) return { success: false, error: `File not found: ${samplePath}` };
274271
- const buffer = await readFile23(absPath);
274498
+ const buffer = await readFile20(absPath);
274272
274499
  audioBuffers.push(buffer);
274273
274500
  }
274274
274501
  const elevenlabs = new ElevenLabsProvider();
@@ -274306,7 +274533,7 @@ async function executeDub(options) {
274306
274533
  }
274307
274534
  const whisper = new WhisperProvider();
274308
274535
  await whisper.initialize({ apiKey: openaiKey });
274309
- const audioBuffer = await readFile23(audioPath);
274536
+ const audioBuffer = await readFile20(audioPath);
274310
274537
  const audioBlob = new Blob([audioBuffer]);
274311
274538
  const transcriptResult = await whisper.transcribe(audioBlob, source3);
274312
274539
  if (transcriptResult.status === "failed" || !transcriptResult.segments) {
@@ -274361,7 +274588,7 @@ ${segmentTexts}`,
274361
274588
  translated: translatedSegments[i3]?.text || s2.text
274362
274589
  }))
274363
274590
  };
274364
- await writeFile19(timingPath, JSON.stringify(timingData, null, 2));
274591
+ await writeFile20(timingPath, JSON.stringify(timingData, null, 2));
274365
274592
  }
274366
274593
  return {
274367
274594
  success: true,
@@ -274384,7 +274611,7 @@ ${segmentTexts}`,
274384
274611
  const outputExt = isVideo ? ".mp3" : extname12(absPath);
274385
274612
  const defaultOutputPath2 = resolve41(dirname29(absPath), `${basename9(absPath, extname12(absPath))}-${language}${outputExt}`);
274386
274613
  const finalOutputPath = resolve41(process.cwd(), output3 || defaultOutputPath2);
274387
- await writeFile19(finalOutputPath, combinedBuffer);
274614
+ await writeFile20(finalOutputPath, combinedBuffer);
274388
274615
  if (isVideo && audioPath !== absPath) {
274389
274616
  try {
274390
274617
  const { unlink: unlink6 } = await import("node:fs/promises");
@@ -274449,7 +274676,7 @@ var init_ai_audio = __esm({
274449
274676
 
274450
274677
  // ../cli/src/commands/_shared/edit/silence-cut.ts
274451
274678
  import { existsSync as existsSync57 } from "node:fs";
274452
- import { readFile as readFile24 } from "node:fs/promises";
274679
+ import { readFile as readFile21 } from "node:fs/promises";
274453
274680
  async function detectSilencePeriods(videoPath, noiseThreshold, minDuration) {
274454
274681
  const totalDuration = await getVideoDuration(videoPath);
274455
274682
  const { stdout, stderr } = await execSafe(
@@ -274499,7 +274726,7 @@ async function detectSilencePeriodsWithGemini(videoPath, minDuration, options) {
274499
274726
  }
274500
274727
  const gemini = new GeminiProvider();
274501
274728
  await gemini.initialize({ apiKey: geminiApiKey });
274502
- const videoBuffer = await readFile24(videoPath);
274729
+ const videoBuffer = await readFile21(videoPath);
274503
274730
  const modelId = options.model ? resolveGeminiTextModel(options.model) : void 0;
274504
274731
  const prompt3 = `Analyze this video and identify all silent or dead segments where there is NO meaningful content.
274505
274732
 
@@ -274684,10 +274911,10 @@ var init_silence_cut = __esm({
274684
274911
 
274685
274912
  // ../cli/src/commands/_shared/edit/jump-cut.ts
274686
274913
  import { existsSync as existsSync58 } from "node:fs";
274687
- import { readFile as readFile25, writeFile as writeFile20, mkdir as mkdir14 } from "node:fs/promises";
274914
+ import { readFile as readFile23, writeFile as writeFile21, mkdir as mkdir15 } from "node:fs/promises";
274688
274915
  import { join as join66 } from "node:path";
274689
274916
  async function transcribeWithWords(audioPath, apiKey, language) {
274690
- const audioBuffer = await readFile25(audioPath);
274917
+ const audioBuffer = await readFile23(audioPath);
274691
274918
  const audioBlob = new Blob([audioBuffer]);
274692
274919
  const formData = new FormData();
274693
274920
  formData.append("file", audioBlob, "audio.wav");
@@ -274764,7 +274991,7 @@ async function executeJumpCut(options) {
274764
274991
  }
274765
274992
  try {
274766
274993
  const tmpDir = `/tmp/vibe_jumpcut_${Date.now()}`;
274767
- await mkdir14(tmpDir, { recursive: true });
274994
+ await mkdir15(tmpDir, { recursive: true });
274768
274995
  const audioPath = join66(tmpDir, "audio.wav");
274769
274996
  try {
274770
274997
  await execSafe(
@@ -274843,7 +275070,7 @@ async function executeJumpCut(options) {
274843
275070
  }
274844
275071
  const concatList = segmentPaths.map((p2) => `file '${p2}'`).join("\n");
274845
275072
  const listPath = join66(tmpDir, "concat.txt");
274846
- await writeFile20(listPath, concatList);
275073
+ await writeFile21(listPath, concatList);
274847
275074
  await execSafe(
274848
275075
  "ffmpeg",
274849
275076
  [
@@ -274870,8 +275097,8 @@ async function executeJumpCut(options) {
274870
275097
  };
274871
275098
  } finally {
274872
275099
  try {
274873
- const { rm: rm9 } = await import("node:fs/promises");
274874
- await rm9(tmpDir, { recursive: true, force: true });
275100
+ const { rm: rm10 } = await import("node:fs/promises");
275101
+ await rm10(tmpDir, { recursive: true, force: true });
274875
275102
  } catch {
274876
275103
  }
274877
275104
  }
@@ -274948,7 +275175,7 @@ __export(remotion_exports, {
274948
275175
  wrapComponentWithImage: () => wrapComponentWithImage,
274949
275176
  wrapComponentWithVideo: () => wrapComponentWithVideo
274950
275177
  });
274951
- import { writeFile as writeFile21, mkdir as mkdir15, rm as rm6, copyFile as copyFile5 } from "node:fs/promises";
275178
+ import { writeFile as writeFile23, mkdir as mkdir16, rm as rm7, copyFile as copyFile5 } from "node:fs/promises";
274952
275179
  import { existsSync as existsSync59 } from "node:fs";
274953
275180
  import { join as join67 } from "node:path";
274954
275181
  import { tmpdir as tmpdir9 } from "node:os";
@@ -274970,7 +275197,7 @@ async function ensureRemotionInstalled() {
274970
275197
  }
274971
275198
  async function scaffoldRemotionProject(componentCode, componentName, opts) {
274972
275199
  const dir = join67(tmpdir9(), `vibe_motion_${Date.now()}`);
274973
- await mkdir15(dir, { recursive: true });
275200
+ await mkdir16(dir, { recursive: true });
274974
275201
  const deps = {
274975
275202
  remotion: REMOTION_VERSION,
274976
275203
  "@remotion/cli": REMOTION_VERSION,
@@ -274988,7 +275215,7 @@ async function scaffoldRemotionProject(componentCode, componentName, opts) {
274988
275215
  dependencies: deps
274989
275216
  };
274990
275217
  const packageJsonStr = JSON.stringify(packageJson, null, 2);
274991
- await writeFile21(join67(dir, "package.json"), packageJsonStr);
275218
+ await writeFile23(join67(dir, "package.json"), packageJsonStr);
274992
275219
  const tsconfig = {
274993
275220
  compilerOptions: {
274994
275221
  target: "ES2020",
@@ -275000,8 +275227,8 @@ async function scaffoldRemotionProject(componentCode, componentName, opts) {
275000
275227
  skipLibCheck: true
275001
275228
  }
275002
275229
  };
275003
- await writeFile21(join67(dir, "tsconfig.json"), JSON.stringify(tsconfig, null, 2));
275004
- await writeFile21(join67(dir, "Component.tsx"), componentCode);
275230
+ await writeFile23(join67(dir, "tsconfig.json"), JSON.stringify(tsconfig, null, 2));
275231
+ await writeFile23(join67(dir, "Component.tsx"), componentCode);
275005
275232
  const rootCode = `import { registerRoot, Composition } from "remotion";
275006
275233
  import { ${componentName} } from "./Component";
275007
275234
 
@@ -275020,7 +275247,7 @@ const Root = () => {
275020
275247
 
275021
275248
  registerRoot(Root);
275022
275249
  `;
275023
- await writeFile21(join67(dir, "Root.tsx"), rootCode);
275250
+ await writeFile23(join67(dir, "Root.tsx"), rootCode);
275024
275251
  const depsHash = createHash11("md5").update(packageJsonStr).digest("hex").slice(0, 12);
275025
275252
  const cacheMarker = join67(REMOTION_CACHE_DIR, `.deps-${depsHash}`);
275026
275253
  const cachedModules = join67(REMOTION_CACHE_DIR, "node_modules");
@@ -275034,15 +275261,15 @@ registerRoot(Root);
275034
275261
  } else {
275035
275262
  await this_npmInstall(dir);
275036
275263
  try {
275037
- await mkdir15(REMOTION_CACHE_DIR, { recursive: true });
275038
- await writeFile21(join67(REMOTION_CACHE_DIR, "package.json"), packageJsonStr);
275264
+ await mkdir16(REMOTION_CACHE_DIR, { recursive: true });
275265
+ await writeFile23(join67(REMOTION_CACHE_DIR, "package.json"), packageJsonStr);
275039
275266
  if (existsSync59(join67(dir, "node_modules"))) {
275040
- const { rename: rename6, symlink: symlink4 } = await import("node:fs/promises");
275041
- await rename6(join67(dir, "node_modules"), cachedModules).catch(() => {
275267
+ const { rename: rename7, symlink: symlink4 } = await import("node:fs/promises");
275268
+ await rename7(join67(dir, "node_modules"), cachedModules).catch(() => {
275042
275269
  });
275043
275270
  await symlink4(cachedModules, join67(dir, "node_modules"), "dir").catch(() => {
275044
275271
  });
275045
- await writeFile21(cacheMarker, depsHash);
275272
+ await writeFile23(cacheMarker, depsHash);
275046
275273
  }
275047
275274
  } catch {
275048
275275
  }
@@ -275142,7 +275369,7 @@ async function renderMotion(options) {
275142
275369
  stderr: ${stderr}` : ""}`);
275143
275370
  return { success: false, error: `Remotion render failed: ${msg}` };
275144
275371
  } finally {
275145
- await rm6(dir, { recursive: true, force: true }).catch(() => {
275372
+ await rm7(dir, { recursive: true, force: true }).catch(() => {
275146
275373
  });
275147
275374
  }
275148
275375
  }
@@ -275211,7 +275438,7 @@ async function renderWithEmbeddedImage(options) {
275211
275438
  );
275212
275439
  try {
275213
275440
  const publicDir = join67(dir, "public");
275214
- await mkdir15(publicDir, { recursive: true });
275441
+ await mkdir16(publicDir, { recursive: true });
275215
275442
  await copyFile5(options.imagePath, join67(publicDir, options.imageFileName));
275216
275443
  const entryPoint = join67(dir, "Root.tsx");
275217
275444
  const mp4Out = options.outputPath.replace(/\.\w+$/, ".mp4");
@@ -275234,7 +275461,7 @@ async function renderWithEmbeddedImage(options) {
275234
275461
  const msg = error instanceof Error ? error.message : String(error);
275235
275462
  return { success: false, error: `Remotion image embed render failed: ${msg}` };
275236
275463
  } finally {
275237
- await rm6(dir, { recursive: true, force: true }).catch(() => {
275464
+ await rm7(dir, { recursive: true, force: true }).catch(() => {
275238
275465
  });
275239
275466
  }
275240
275467
  }
@@ -275275,7 +275502,7 @@ async function renderWithEmbeddedVideo(options) {
275275
275502
  );
275276
275503
  try {
275277
275504
  const publicDir = join67(dir, "public");
275278
- await mkdir15(publicDir, { recursive: true });
275505
+ await mkdir16(publicDir, { recursive: true });
275279
275506
  await copyFile5(options.videoPath, join67(publicDir, options.videoFileName));
275280
275507
  const entryPoint = join67(dir, "Root.tsx");
275281
275508
  const mp4VideoOnly = options.outputPath.replace(/\.\w+$/, "_video_only.mp4");
@@ -275311,14 +275538,14 @@ async function renderWithEmbeddedVideo(options) {
275311
275538
  "-shortest",
275312
275539
  mp4Out
275313
275540
  ], { timeout: 12e4 });
275314
- await rm6(mp4VideoOnly, { force: true }).catch(() => {
275541
+ await rm7(mp4VideoOnly, { force: true }).catch(() => {
275315
275542
  });
275316
275543
  return { success: true, outputPath: mp4Out };
275317
275544
  } catch (error) {
275318
275545
  const msg = error instanceof Error ? error.message : String(error);
275319
275546
  return { success: false, error: `Remotion video embed render failed: ${msg}` };
275320
275547
  } finally {
275321
- await rm6(dir, { recursive: true, force: true }).catch(() => {
275548
+ await rm7(dir, { recursive: true, force: true }).catch(() => {
275322
275549
  });
275323
275550
  }
275324
275551
  }
@@ -275699,7 +275926,7 @@ var init_remotion = __esm({
275699
275926
 
275700
275927
  // ../cli/src/commands/_shared/edit/caption.ts
275701
275928
  import { existsSync as existsSync60 } from "node:fs";
275702
- import { readFile as readFile26, writeFile as writeFile23, mkdir as mkdir16 } from "node:fs/promises";
275929
+ import { readFile as readFile24, writeFile as writeFile24, mkdir as mkdir17 } from "node:fs/promises";
275703
275930
  import { dirname as dirname30, basename as basename10, extname as extname13, join as join68 } from "node:path";
275704
275931
  function getCaptionForceStyle(style, fontSize, fontColor, position) {
275705
275932
  const alignment = position === "top" ? 8 : position === "center" ? 5 : 2;
@@ -275746,7 +275973,7 @@ async function executeCaption(options) {
275746
275973
  }
275747
275974
  try {
275748
275975
  const tmpDir = `/tmp/vibe_caption_${Date.now()}`;
275749
- await mkdir16(tmpDir, { recursive: true });
275976
+ await mkdir17(tmpDir, { recursive: true });
275750
275977
  const audioPath = join68(tmpDir, "audio.wav");
275751
275978
  const srtPath = join68(tmpDir, "captions.srt");
275752
275979
  try {
@@ -275769,7 +275996,7 @@ async function executeCaption(options) {
275769
275996
  );
275770
275997
  const whisper = new WhisperProvider();
275771
275998
  await whisper.initialize({ apiKey: openaiKey });
275772
- const audioBuffer = await readFile26(audioPath);
275999
+ const audioBuffer = await readFile24(audioPath);
275773
276000
  const audioBlob = new Blob([audioBuffer]);
275774
276001
  const transcriptResult = await whisper.transcribe(audioBlob, language);
275775
276002
  if (transcriptResult.status === "failed" || !transcriptResult.segments || transcriptResult.segments.length === 0) {
@@ -275779,7 +276006,7 @@ async function executeCaption(options) {
275779
276006
  };
275780
276007
  }
275781
276008
  const srtContent = formatSRT(transcriptResult.segments);
275782
- await writeFile23(srtPath, srtContent);
276009
+ await writeFile24(srtPath, srtContent);
275783
276010
  const { width, height } = await getVideoResolution(videoPath);
275784
276011
  const fontSize = customFontSize || Math.round(height / 18);
275785
276012
  let hasSubtitles = false;
@@ -275821,7 +276048,7 @@ async function executeCaption(options) {
275821
276048
  outputDir2,
275822
276049
  basename10(outputPath, extname13(outputPath)) + ".srt"
275823
276050
  );
275824
- await writeFile23(outputSrtPath2, srtContent);
276051
+ await writeFile24(outputSrtPath2, srtContent);
275825
276052
  return { success: false, error: `${remotionErr}
275826
276053
  SRT saved to: ${outputSrtPath2}` };
275827
276054
  }
@@ -275860,7 +276087,7 @@ SRT saved to: ${outputSrtPath2}` };
275860
276087
  outputDir2,
275861
276088
  basename10(outputPath, extname13(outputPath)) + ".srt"
275862
276089
  );
275863
- await writeFile23(outputSrtPath2, srtContent);
276090
+ await writeFile24(outputSrtPath2, srtContent);
275864
276091
  return {
275865
276092
  success: false,
275866
276093
  error: `${renderResult.error}
@@ -275873,7 +276100,7 @@ SRT saved to: ${outputSrtPath2}`
275873
276100
  outputDir,
275874
276101
  basename10(outputPath, extname13(outputPath)) + ".srt"
275875
276102
  );
275876
- await writeFile23(outputSrtPath, srtContent);
276103
+ await writeFile24(outputSrtPath, srtContent);
275877
276104
  return {
275878
276105
  success: true,
275879
276106
  outputPath,
@@ -275882,8 +276109,8 @@ SRT saved to: ${outputSrtPath2}`
275882
276109
  };
275883
276110
  } finally {
275884
276111
  try {
275885
- const { rm: rm9 } = await import("node:fs/promises");
275886
- await rm9(tmpDir, { recursive: true, force: true });
276112
+ const { rm: rm10 } = await import("node:fs/promises");
276113
+ await rm10(tmpDir, { recursive: true, force: true });
275887
276114
  } catch {
275888
276115
  }
275889
276116
  }
@@ -276044,7 +276271,7 @@ var init_fade = __esm({
276044
276271
 
276045
276272
  // ../cli/src/commands/_shared/edit/translate-srt.ts
276046
276273
  import { existsSync as existsSync64 } from "node:fs";
276047
- import { readFile as readFile27, writeFile as writeFile24 } from "node:fs/promises";
276274
+ import { readFile as readFile25, writeFile as writeFile25 } from "node:fs/promises";
276048
276275
  async function executeTranslateSrt(options) {
276049
276276
  const {
276050
276277
  srtPath,
@@ -276058,7 +276285,7 @@ async function executeTranslateSrt(options) {
276058
276285
  return { success: false, error: `SRT file not found: ${srtPath}` };
276059
276286
  }
276060
276287
  try {
276061
- const srtContent = await readFile27(srtPath, "utf-8");
276288
+ const srtContent = await readFile25(srtPath, "utf-8");
276062
276289
  const segments = parseSRT(srtContent);
276063
276290
  if (segments.length === 0) {
276064
276291
  return { success: false, error: "No subtitle segments found in SRT file" };
@@ -276148,7 +276375,7 @@ ${textsToTranslate}`;
276148
276375
  }
276149
276376
  }
276150
276377
  const translatedSrt = formatSRT(translatedSegments);
276151
- await writeFile24(outputPath, translatedSrt);
276378
+ await writeFile25(outputPath, translatedSrt);
276152
276379
  return {
276153
276380
  success: true,
276154
276381
  outputPath,
@@ -277312,7 +277539,7 @@ var init_project_resolver = __esm({
277312
277539
 
277313
277540
  // ../cli/src/commands/_shared/execute-fill-gaps.ts
277314
277541
  import { existsSync as existsSync67 } from "node:fs";
277315
- import { readFile as readFile28, writeFile as writeFile25, mkdir as mkdir17, rename as renameFs } from "node:fs/promises";
277542
+ import { readFile as readFile26, writeFile as writeFile26, mkdir as mkdir18, rename as renameFs } from "node:fs/promises";
277316
277543
  import { resolve as resolve46, dirname as dirname31 } from "node:path";
277317
277544
  function detectVideoGaps(videoClips, totalDuration) {
277318
277545
  const gaps = [];
@@ -277369,7 +277596,7 @@ function analyzeGapFillability(gaps, videoClips, sources) {
277369
277596
  }
277370
277597
  async function uploadFrameToImgbb(framePath, imgbbApiKey) {
277371
277598
  try {
277372
- const frameBuffer = await readFile28(framePath);
277599
+ const frameBuffer = await readFile26(framePath);
277373
277600
  const frameBase64 = frameBuffer.toString("base64");
277374
277601
  const formData = new FormData();
277375
277602
  formData.append("key", imgbbApiKey);
@@ -277401,7 +277628,7 @@ async function executeFillGaps(options) {
277401
277628
  humanLines
277402
277629
  };
277403
277630
  }
277404
- const content = await readFile28(filePath, "utf-8");
277631
+ const content = await readFile26(filePath, "utf-8");
277405
277632
  const data2 = JSON.parse(content);
277406
277633
  const project = Project.fromJSON(data2);
277407
277634
  const clips = project.getClips().sort((a4, b4) => a4.startTime - b4.startTime);
@@ -277530,7 +277757,7 @@ async function executeFillGaps(options) {
277530
277757
  const projectDir = dirname31(filePath);
277531
277758
  const footageDir = options.dir ? resolve46(process.cwd(), options.dir) : resolve46(projectDir, "footage");
277532
277759
  if (!existsSync67(footageDir)) {
277533
- await mkdir17(footageDir, { recursive: true });
277760
+ await mkdir18(footageDir, { recursive: true });
277534
277761
  }
277535
277762
  humanLines.push("Generating AI Videos");
277536
277763
  const imgbbApiKey = options.imgbbApiKey || await getApiKey("IMGBB_API_KEY", "imgbb", void 0);
@@ -277624,7 +277851,7 @@ async function executeFillGaps(options) {
277624
277851
  const videoPath = resolve46(footageDir, videoFileName);
277625
277852
  onProgress("Downloading generated video...");
277626
277853
  const videoBuffer = await downloadVideo(finalResult.videoUrl);
277627
- await writeFile25(videoPath, videoBuffer);
277854
+ await writeFile26(videoPath, videoBuffer);
277628
277855
  generatedDuration = finalResult.duration || parseInt(klingDuration);
277629
277856
  generatedVideos.push(videoPath);
277630
277857
  humanLines.push(` Generated: ${videoFileName} (${generatedDuration}s)`);
@@ -277694,11 +277921,11 @@ async function executeFillGaps(options) {
277694
277921
  `gap-fill-${gap.start.toFixed(2)}-${gap.end.toFixed(2)}-seg${segmentIndex}.mp4`
277695
277922
  );
277696
277923
  const segVideoBuffer = await downloadVideo(segFinalResult.videoUrl);
277697
- await writeFile25(segVideoPath, segVideoBuffer);
277924
+ await writeFile26(segVideoPath, segVideoBuffer);
277698
277925
  const concatListPath = resolve46(footageDir, `concat-${gap.start.toFixed(2)}.txt`);
277699
277926
  const concatList = generatedVideos.map((v4) => `file '${v4}'`).join("\n") + `
277700
277927
  file '${segVideoPath}'`;
277701
- await writeFile25(concatListPath, concatList);
277928
+ await writeFile26(concatListPath, concatList);
277702
277929
  const concatOutputPath = resolve46(
277703
277930
  footageDir,
277704
277931
  `gap-fill-${gap.start.toFixed(2)}-${gap.end.toFixed(2)}-merged.mp4`
@@ -277756,7 +277983,7 @@ file '${segVideoPath}'`;
277756
277983
  let outputPath = filePath;
277757
277984
  if (generatedCount > 0) {
277758
277985
  outputPath = options.output ? resolve46(process.cwd(), options.output) : filePath;
277759
- await writeFile25(outputPath, JSON.stringify(project.toJSON(), null, 2));
277986
+ await writeFile26(outputPath, JSON.stringify(project.toJSON(), null, 2));
277760
277987
  humanLines.push(`\u2714 Filled ${generatedCount} gap(s) with AI-generated video`);
277761
277988
  humanLines.push(`Project saved: ${outputPath}`);
277762
277989
  } else {
@@ -277850,7 +278077,7 @@ __export(ai_motion_exports, {
277850
278077
  });
277851
278078
  import { resolve as resolve47 } from "node:path";
277852
278079
  import { existsSync as existsSync68 } from "node:fs";
277853
- import { readFile as readFile29, writeFile as writeFile26 } from "node:fs/promises";
278080
+ import { readFile as readFile27, writeFile as writeFile27 } from "node:fs/promises";
277854
278081
  async function getOptionalGoogleApiKey() {
277855
278082
  const configKey = await getApiKeyFromConfig("google");
277856
278083
  if (configKey) return configKey;
@@ -277897,7 +278124,7 @@ async function executeMotion(options) {
277897
278124
  };
277898
278125
  }
277899
278126
  const imagePath = resolve47(process.cwd(), options.image);
277900
- const imageBuffer = await readFile29(imagePath);
278127
+ const imageBuffer = await readFile27(imagePath);
277901
278128
  const gemini = new GeminiProvider();
277902
278129
  await gemini.initialize({ apiKey: geminiApiKey });
277903
278130
  const analysisResult = await gemini.analyzeImage(
@@ -277934,7 +278161,7 @@ Use this image analysis to inform the color palette, typography placement, and o
277934
278161
  }
277935
278162
  if (geminiApiKey) {
277936
278163
  const videoPath = resolve47(process.cwd(), options.video);
277937
- const videoBuffer = await readFile29(videoPath);
278164
+ const videoBuffer = await readFile27(videoPath);
277938
278165
  const gemini = new GeminiProvider();
277939
278166
  await gemini.initialize({ apiKey: geminiApiKey });
277940
278167
  const analysisPrompt = options.understandingPrompt || `Analyze this video for motion graphics composition. Return concise production guidance:
@@ -277968,7 +278195,7 @@ Use this video understanding to place motion graphics in safe zones, match the c
277968
278195
  if (!existsSync68(tsxPath)) {
277969
278196
  return { success: false, error: `TSX file not found: ${tsxPath}` };
277970
278197
  }
277971
- const existingCode = await readFile29(tsxPath, "utf-8");
278198
+ const existingCode = await readFile27(tsxPath, "utf-8");
277972
278199
  if (useGemini) {
277973
278200
  const gemini = new GeminiProvider();
277974
278201
  await gemini.initialize({ apiKey });
@@ -278020,7 +278247,7 @@ Use this video understanding to place motion graphics in safe zones, match the c
278020
278247
  const defaultOutput = options.video || options.image ? "motion-output.mp4" : options.render ? "motion.webm" : "motion.tsx";
278021
278248
  const outputPath = resolve47(process.cwd(), options.output || defaultOutput);
278022
278249
  const codePath = outputPath.replace(/\.\w+$/, ".tsx");
278023
- await writeFile26(codePath, component.code, "utf-8");
278250
+ await writeFile27(codePath, component.code, "utf-8");
278024
278251
  const shouldRender = options.render || !!options.video || !!options.image;
278025
278252
  if (!shouldRender) {
278026
278253
  return { success: true, codePath, componentName: component.name };
@@ -278499,7 +278726,7 @@ __export(project_builder_exports, {
278499
278726
  buildTempProject: () => buildTempProject,
278500
278727
  resolveSourceUrl: () => resolveSourceUrl
278501
278728
  });
278502
- import { mkdtemp as mkdtemp5, mkdir as mkdir18, copyFile as copyFile6, writeFile as writeFile27, rm as rm7, readdir as readdir6 } from "node:fs/promises";
278729
+ import { mkdtemp as mkdtemp5, mkdir as mkdir19, copyFile as copyFile6, writeFile as writeFile28, rm as rm8, readdir as readdir7 } from "node:fs/promises";
278503
278730
  import { existsSync as existsSync69 } from "node:fs";
278504
278731
  import { tmpdir as tmpdir10 } from "node:os";
278505
278732
  import { createRequire as createRequire4 } from "node:module";
@@ -278507,7 +278734,7 @@ import * as path15 from "node:path";
278507
278734
  async function buildTempProject(state, projectFileDir) {
278508
278735
  const dir = await mkdtemp5(path15.join(tmpdir10(), "vibeframe-hf-"));
278509
278736
  const assetsDir = path15.join(dir, "assets");
278510
- await mkdir18(assetsDir, { recursive: true });
278737
+ await mkdir19(assetsDir, { recursive: true });
278511
278738
  const copied = /* @__PURE__ */ new Map();
278512
278739
  for (const source3 of state.sources) {
278513
278740
  if (copied.has(source3.id)) continue;
@@ -278523,10 +278750,10 @@ async function buildTempProject(state, projectFileDir) {
278523
278750
  await copyLottieRuntime(dir);
278524
278751
  }
278525
278752
  const html = generateCompositionHtml(state);
278526
- await writeFile27(path15.join(dir, "index.html"), html, "utf-8");
278753
+ await writeFile28(path15.join(dir, "index.html"), html, "utf-8");
278527
278754
  return {
278528
278755
  dir,
278529
- cleanup: () => rm7(dir, { recursive: true, force: true })
278756
+ cleanup: () => rm8(dir, { recursive: true, force: true })
278530
278757
  };
278531
278758
  }
278532
278759
  async function copyLottieRuntime(tempDir) {
@@ -278537,8 +278764,8 @@ async function copyLottieRuntime(tempDir) {
278537
278764
  const wasmSrc = path15.join(path15.dirname(webEntry), "dotlottie-player.wasm");
278538
278765
  const vendorDir = path15.join(tempDir, "vendor");
278539
278766
  const wcDest = path15.join(vendorDir, "dotlottie-wc");
278540
- await mkdir18(wcDest, { recursive: true });
278541
- for (const file of await readdir6(wcDistDir)) {
278767
+ await mkdir19(wcDest, { recursive: true });
278768
+ for (const file of await readdir7(wcDistDir)) {
278542
278769
  if (file.endsWith(".map") || file.endsWith(".d.ts")) continue;
278543
278770
  await copyFile6(path15.join(wcDistDir, file), path15.join(wcDest, file));
278544
278771
  }
@@ -278645,7 +278872,7 @@ __export(motion_overlay_exports, {
278645
278872
  executeMotionOverlay: () => executeMotionOverlay,
278646
278873
  registerMotionOverlayCommand: () => registerMotionOverlayCommand
278647
278874
  });
278648
- import { mkdir as mkdir19 } from "node:fs/promises";
278875
+ import { mkdir as mkdir20 } from "node:fs/promises";
278649
278876
  import { existsSync as existsSync70 } from "node:fs";
278650
278877
  import { basename as basename13, dirname as dirname33, extname as extname15, resolve as resolve49 } from "node:path";
278651
278878
  function normalizeUnderstand2(value) {
@@ -278793,7 +279020,7 @@ async function executeMotionOverlay(options) {
278793
279020
  const scale = Math.max(0.01, Math.min(2, numberOption(options.scale, position === "full" ? 1 : 0.25)));
278794
279021
  const opacity = Math.max(0, Math.min(1, numberOption(options.opacity, 1)));
278795
279022
  const loop = options.loop ?? true;
278796
- await mkdir19(dirname33(outputPath), { recursive: true });
279023
+ await mkdir20(dirname33(outputPath), { recursive: true });
278797
279024
  const backend = createHyperframesBackend();
278798
279025
  const result2 = await backend.render({
278799
279026
  projectState: buildLottieTimelineState({
@@ -278819,7 +279046,7 @@ async function executeMotionOverlay(options) {
278819
279046
  return { success: true, outputPath, provider: "lottie" };
278820
279047
  }
278821
279048
  const duration = options.duration ?? await ffprobeDuration(absVideo).catch(() => void 0);
278822
- await mkdir19(dirname33(outputPath), { recursive: true });
279049
+ await mkdir20(dirname33(outputPath), { recursive: true });
278823
279050
  const result = await executeMotion({
278824
279051
  description: options.description.trim(),
278825
279052
  video: absVideo,
@@ -278948,7 +279175,7 @@ __export(edit_cmd_exports, {
278948
279175
  executeUpscale: () => executeUpscale
278949
279176
  });
278950
279177
  import { resolve as resolve50, dirname as dirname34 } from "node:path";
278951
- import { readFile as readFile30, writeFile as writeFile28, mkdir as mkdir20 } from "node:fs/promises";
279178
+ import { readFile as readFile28, writeFile as writeFile29, mkdir as mkdir21 } from "node:fs/promises";
278952
279179
  async function executeGrade(options) {
278953
279180
  const { videoPath, style, preset: preset3, output: output3, analyzeOnly, apiKey } = options;
278954
279181
  try {
@@ -278989,7 +279216,7 @@ async function executeSpeedRamp(options) {
278989
279216
  await execSafe("ffmpeg", ["-i", absPath, "-vn", "-acodec", "libmp3lame", "-q:a", "2", tempAudio, "-y"]);
278990
279217
  const whisper = new WhisperProvider();
278991
279218
  await whisper.initialize({ apiKey: openaiKey });
278992
- const audioBuffer = await readFile30(tempAudio);
279219
+ const audioBuffer = await readFile28(tempAudio);
278993
279220
  const transcript = await whisper.transcribe(new Blob([audioBuffer]), language);
278994
279221
  try {
278995
279222
  const { unlink: unlink6 } = await import("node:fs/promises");
@@ -279383,7 +279610,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
279383
279610
  spinner2.text = "Transcribing audio...";
279384
279611
  const whisper = new WhisperProvider();
279385
279612
  await whisper.initialize({ apiKey: openaiApiKey });
279386
- const audioBuffer = await readFile30(tempAudio);
279613
+ const audioBuffer = await readFile28(tempAudio);
279387
279614
  const audioBlob = new Blob([audioBuffer]);
279388
279615
  const transcript = await whisper.transcribe(audioBlob, options.language);
279389
279616
  if (!transcript.segments || transcript.segments.length === 0) {
@@ -279516,7 +279743,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
279516
279743
  for (let i3 = 1; i3 <= numKeyframes && i3 <= 30; i3++) {
279517
279744
  const framePath = `${tempDir}/frame-${i3.toString().padStart(4, "0")}.jpg`;
279518
279745
  try {
279519
- const frameBuffer = await readFile30(framePath);
279746
+ const frameBuffer = await readFile28(framePath);
279520
279747
  const frameBase64 = frameBuffer.toString("base64");
279521
279748
  const result = await claude.analyzeFrameForReframe(frameBase64, options.aspect, {
279522
279749
  focusMode: options.focus,
@@ -279576,7 +279803,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
279576
279803
  console.log();
279577
279804
  if (options.keyframes) {
279578
279805
  const keyframesPath = resolve50(process.cwd(), options.keyframes);
279579
- await writeFile28(keyframesPath, JSON.stringify(cropKeyframes, null, 2));
279806
+ await writeFile29(keyframesPath, JSON.stringify(cropKeyframes, null, 2));
279580
279807
  console.log(source_default.green(`Keyframes saved to: ${keyframesPath}`));
279581
279808
  }
279582
279809
  if (options.analyzeOnly) {
@@ -279645,7 +279872,7 @@ Run 'vibe schema edit.<command>' for structured parameter info.
279645
279872
  const imageBuffers = [];
279646
279873
  for (const imagePath of imagePaths) {
279647
279874
  const absPath = resolve50(process.cwd(), imagePath);
279648
- const buffer = await readFile30(absPath);
279875
+ const buffer = await readFile28(absPath);
279649
279876
  imageBuffers.push(buffer);
279650
279877
  }
279651
279878
  let result;
@@ -279695,14 +279922,14 @@ Run 'vibe schema edit.<command>' for structured parameter info.
279695
279922
  const img = result.images[0];
279696
279923
  const outputPath = resolve50(process.cwd(), options.output);
279697
279924
  const saveImage = async () => {
279698
- await mkdir20(dirname34(outputPath), { recursive: true });
279925
+ await mkdir21(dirname34(outputPath), { recursive: true });
279699
279926
  if (img.base64) {
279700
279927
  const buffer = Buffer.from(img.base64, "base64");
279701
- await writeFile28(outputPath, buffer);
279928
+ await writeFile29(outputPath, buffer);
279702
279929
  } else if (img.url) {
279703
279930
  const resp = await fetch(img.url);
279704
279931
  const arrayBuf = await resp.arrayBuffer();
279705
- await writeFile28(outputPath, Buffer.from(arrayBuf));
279932
+ await writeFile29(outputPath, Buffer.from(arrayBuf));
279706
279933
  }
279707
279934
  };
279708
279935
  const resultModel = result.model;
@@ -279922,8 +280149,8 @@ __export(ai_image_exports, {
279922
280149
  executeThumbnailBestFrame: () => executeThumbnailBestFrame
279923
280150
  });
279924
280151
  import { resolve as resolve53, dirname as dirname36 } from "node:path";
279925
- import { readFile as readFile31, writeFile as writeFile30, mkdir as mkdir23 } from "node:fs/promises";
279926
- import { existsSync as existsSync72 } from "node:fs";
280152
+ import { readFile as readFile29, writeFile as writeFile31, mkdir as mkdir24 } from "node:fs/promises";
280153
+ import { existsSync as existsSync73 } from "node:fs";
279927
280154
  async function executeImageGenerate(options) {
279928
280155
  const {
279929
280156
  prompt: prompt3,
@@ -279965,8 +280192,8 @@ async function executeImageGenerate(options) {
279965
280192
  return { success: false, error: "No image data available" };
279966
280193
  }
279967
280194
  outputPath = resolve53(process.cwd(), output3);
279968
- await mkdir23(dirname36(outputPath), { recursive: true });
279969
- await writeFile30(outputPath, buffer);
280195
+ await mkdir24(dirname36(outputPath), { recursive: true });
280196
+ await writeFile31(outputPath, buffer);
279970
280197
  }
279971
280198
  return {
279972
280199
  success: true,
@@ -280000,8 +280227,8 @@ async function executeImageGenerate(options) {
280000
280227
  const img = result.images[0];
280001
280228
  if (img.base64) {
280002
280229
  outputPath = resolve53(process.cwd(), output3);
280003
- await mkdir23(dirname36(outputPath), { recursive: true });
280004
- await writeFile30(outputPath, Buffer.from(img.base64, "base64"));
280230
+ await mkdir24(dirname36(outputPath), { recursive: true });
280231
+ await writeFile31(outputPath, Buffer.from(img.base64, "base64"));
280005
280232
  }
280006
280233
  }
280007
280234
  return {
@@ -280036,8 +280263,8 @@ async function executeImageGenerate(options) {
280036
280263
  return { success: false, error: "No image data available" };
280037
280264
  }
280038
280265
  outputPath = resolve53(process.cwd(), output3);
280039
- await mkdir23(dirname36(outputPath), { recursive: true });
280040
- await writeFile30(outputPath, buffer);
280266
+ await mkdir24(dirname36(outputPath), { recursive: true });
280267
+ await writeFile31(outputPath, buffer);
280041
280268
  }
280042
280269
  return {
280043
280270
  success: true,
@@ -280067,10 +280294,10 @@ async function executeGeminiEdit(options) {
280067
280294
  const imageBuffers = [];
280068
280295
  for (const imagePath of imagePaths) {
280069
280296
  const absPath = resolve53(process.cwd(), imagePath);
280070
- if (!existsSync72(absPath)) {
280297
+ if (!existsSync73(absPath)) {
280071
280298
  return { success: false, error: `Image not found: ${absPath}` };
280072
280299
  }
280073
- const buffer = await readFile31(absPath);
280300
+ const buffer = await readFile29(absPath);
280074
280301
  imageBuffers.push(buffer);
280075
280302
  }
280076
280303
  const gemini = new GeminiProvider();
@@ -280095,8 +280322,8 @@ async function executeGeminiEdit(options) {
280095
280322
  let outputPath;
280096
280323
  if (img.base64) {
280097
280324
  outputPath = resolve53(process.cwd(), output3);
280098
- await mkdir23(dirname36(outputPath), { recursive: true });
280099
- await writeFile30(outputPath, Buffer.from(img.base64, "base64"));
280325
+ await mkdir24(dirname36(outputPath), { recursive: true });
280326
+ await writeFile31(outputPath, Buffer.from(img.base64, "base64"));
280100
280327
  }
280101
280328
  return {
280102
280329
  success: true,
@@ -280115,7 +280342,7 @@ async function executeThumbnailBestFrame(options) {
280115
280342
  model = "flash",
280116
280343
  apiKey
280117
280344
  } = options;
280118
- if (!existsSync72(videoPath)) {
280345
+ if (!existsSync73(videoPath)) {
280119
280346
  return { success: false, error: `Video not found: ${videoPath}` };
280120
280347
  }
280121
280348
  if (!commandExists("ffmpeg")) {
@@ -280128,7 +280355,7 @@ async function executeThumbnailBestFrame(options) {
280128
280355
  try {
280129
280356
  const gemini = new GeminiProvider();
280130
280357
  await gemini.initialize({ apiKey: googleKey });
280131
- const videoData = await readFile31(videoPath);
280358
+ const videoData = await readFile29(videoPath);
280132
280359
  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>"}';
280133
280360
  const modelId = resolveGeminiTextModel(model);
280134
280361
  const result = await gemini.analyzeVideo(videoData, analysisPrompt, {
@@ -280149,7 +280376,7 @@ async function executeThumbnailBestFrame(options) {
280149
280376
  reason = reasonMatch[1];
280150
280377
  }
280151
280378
  await execSafe("ffmpeg", ["-ss", String(timestamp), "-i", videoPath, "-frames:v", "1", "-q:v", "2", outputPath, "-y"], { timeout: 6e4, maxBuffer: 50 * 1024 * 1024 });
280152
- if (!existsSync72(outputPath)) {
280379
+ if (!existsSync73(outputPath)) {
280153
280380
  return { success: false, error: "FFmpeg failed to extract frame" };
280154
280381
  }
280155
280382
  return {
@@ -280180,9 +280407,9 @@ __export(ai_analyze_exports, {
280180
280407
  executeGeminiVideo: () => executeGeminiVideo,
280181
280408
  registerAnalyzeCommands: () => registerAnalyzeCommands
280182
280409
  });
280183
- import { readFile as readFile33 } from "node:fs/promises";
280410
+ import { readFile as readFile30 } from "node:fs/promises";
280184
280411
  import { extname as extname16, resolve as resolve54 } from "node:path";
280185
- import { existsSync as existsSync73 } from "node:fs";
280412
+ import { existsSync as existsSync74 } from "node:fs";
280186
280413
  async function executeGeminiVideo(options) {
280187
280414
  try {
280188
280415
  const apiKey = await getApiKey("GOOGLE_API_KEY", "Google");
@@ -280196,10 +280423,10 @@ async function executeGeminiVideo(options) {
280196
280423
  videoData = options.source;
280197
280424
  } else {
280198
280425
  const absPath = resolve54(process.cwd(), options.source);
280199
- if (!existsSync73(absPath)) {
280426
+ if (!existsSync74(absPath)) {
280200
280427
  return { success: false, error: `File not found: ${absPath}` };
280201
280428
  }
280202
- videoData = await readFile33(absPath);
280429
+ videoData = await readFile30(absPath);
280203
280430
  }
280204
280431
  const gemini = new GeminiProvider();
280205
280432
  await gemini.initialize({ apiKey });
@@ -280264,10 +280491,10 @@ async function executeAnalyze(options) {
280264
280491
  imageBuffer = Buffer.from(await response.arrayBuffer());
280265
280492
  } else {
280266
280493
  const absPath = resolve54(process.cwd(), source3);
280267
- if (!existsSync73(absPath)) {
280494
+ if (!existsSync74(absPath)) {
280268
280495
  return { success: false, error: `File not found: ${absPath}` };
280269
280496
  }
280270
- imageBuffer = await readFile33(absPath);
280497
+ imageBuffer = await readFile30(absPath);
280271
280498
  }
280272
280499
  const result2 = await gemini.analyzeImage(imageBuffer, options.prompt, {
280273
280500
  model: modelId,
@@ -280299,10 +280526,10 @@ async function executeAnalyze(options) {
280299
280526
  videoData = Buffer.from(await response.arrayBuffer());
280300
280527
  } else {
280301
280528
  const absPath = resolve54(process.cwd(), source3);
280302
- if (!existsSync73(absPath)) {
280529
+ if (!existsSync74(absPath)) {
280303
280530
  return { success: false, error: `File not found: ${absPath}` };
280304
280531
  }
280305
- videoData = await readFile33(absPath);
280532
+ videoData = await readFile30(absPath);
280306
280533
  }
280307
280534
  const result = await gemini.analyzeVideo(videoData, options.prompt, {
280308
280535
  model: modelId,
@@ -280435,9 +280662,9 @@ var ai_review_exports = {};
280435
280662
  __export(ai_review_exports, {
280436
280663
  executeReview: () => executeReview
280437
280664
  });
280438
- import { readFile as readFile36, rename as rename5 } from "node:fs/promises";
280665
+ import { readFile as readFile34, rename as rename6 } from "node:fs/promises";
280439
280666
  import { resolve as resolve57 } from "node:path";
280440
- import { existsSync as existsSync75 } from "node:fs";
280667
+ import { existsSync as existsSync76 } from "node:fs";
280441
280668
  function parseReviewFeedback(response) {
280442
280669
  let cleaned = response.trim();
280443
280670
  if (cleaned.startsWith("```json")) {
@@ -280469,7 +280696,7 @@ async function executeReview(options) {
280469
280696
  model = "flash"
280470
280697
  } = options;
280471
280698
  const absVideoPath = resolve57(process.cwd(), videoPath);
280472
- if (!existsSync75(absVideoPath)) {
280699
+ if (!existsSync76(absVideoPath)) {
280473
280700
  return { success: false, error: `Video not found: ${absVideoPath}` };
280474
280701
  }
280475
280702
  const apiKey = process.env.GOOGLE_API_KEY || await getApiKey("GOOGLE_API_KEY", "Google");
@@ -280482,8 +280709,8 @@ async function executeReview(options) {
280482
280709
  let storyboardContext = "";
280483
280710
  if (storyboardPath) {
280484
280711
  const absStoryboardPath = resolve57(process.cwd(), storyboardPath);
280485
- if (existsSync75(absStoryboardPath)) {
280486
- const content = await readFile36(absStoryboardPath, "utf-8");
280712
+ if (existsSync76(absStoryboardPath)) {
280713
+ const content = await readFile34(absStoryboardPath, "utf-8");
280487
280714
  storyboardContext = `
280488
280715
 
280489
280716
  Original storyboard for reference:
@@ -280525,7 +280752,7 @@ ${projectContext}` : "";
280525
280752
  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}`;
280526
280753
  const gemini = new GeminiProvider();
280527
280754
  await gemini.initialize({ apiKey });
280528
- const videoData = await readFile36(absVideoPath);
280755
+ const videoData = await readFile34(absVideoPath);
280529
280756
  const analysisResult = await gemini.analyzeVideo(videoData, reviewPrompt, {
280530
280757
  model: modelId
280531
280758
  });
@@ -280572,7 +280799,7 @@ Score each category 1-10. Prefer beatIssues when you can map a problem to a stor
280572
280799
  if (currentInput !== absVideoPath) {
280573
280800
  const finalOutput = outputBase;
280574
280801
  try {
280575
- await rename5(currentInput, finalOutput);
280802
+ await rename6(currentInput, finalOutput);
280576
280803
  result.outputPath = finalOutput;
280577
280804
  } catch {
280578
280805
  result.outputPath = currentInput;
@@ -280580,7 +280807,7 @@ Score each category 1-10. Prefer beatIssues when you can map a problem to a stor
280580
280807
  }
280581
280808
  }
280582
280809
  if (verify && result.outputPath) {
280583
- const verifyVideoData = await readFile36(result.outputPath);
280810
+ const verifyVideoData = await readFile34(result.outputPath);
280584
280811
  const verifyResult = await gemini.analyzeVideo(
280585
280812
  verifyVideoData,
280586
280813
  'Rate this video overall quality on a scale of 1-10. Return ONLY a JSON object: {"score": <number>}',
@@ -280611,7 +280838,7 @@ var init_ai_review = __esm({
280611
280838
 
280612
280839
  // ../cli/src/commands/generate/sound-effect.ts
280613
280840
  import { resolve as resolve60 } from "node:path";
280614
- import { writeFile as writeFile33 } from "node:fs/promises";
280841
+ import { writeFile as writeFile34 } from "node:fs/promises";
280615
280842
  async function executeSoundEffect(options) {
280616
280843
  try {
280617
280844
  const apiKey = await getConfiguredApiKey("ELEVENLABS_API_KEY");
@@ -280636,7 +280863,7 @@ async function executeSoundEffect(options) {
280636
280863
  process.cwd(),
280637
280864
  options.output || "sound-effect.mp3"
280638
280865
  );
280639
- await writeFile33(outputPath, result.audioBuffer);
280866
+ await writeFile34(outputPath, result.audioBuffer);
280640
280867
  return { success: true, outputPath };
280641
280868
  } catch (error) {
280642
280869
  return {
@@ -280688,7 +280915,7 @@ function registerSoundEffectCommand(parent) {
280688
280915
  );
280689
280916
  }
280690
280917
  const outputPath = resolve60(process.cwd(), options.output);
280691
- await writeFile33(outputPath, result.audioBuffer);
280918
+ await writeFile34(outputPath, result.audioBuffer);
280692
280919
  spinner2.succeed(source_default.green("Sound effect generated"));
280693
280920
  if (isJsonMode()) {
280694
280921
  outputSuccess({
@@ -280855,7 +281082,7 @@ var init_tty = __esm({
280855
281082
 
280856
281083
  // ../cli/src/commands/generate/speech.ts
280857
281084
  import { resolve as resolve61 } from "node:path";
280858
- import { writeFile as writeFile34 } from "node:fs/promises";
281085
+ import { writeFile as writeFile35 } from "node:fs/promises";
280859
281086
  async function executeSpeech(options) {
280860
281087
  try {
280861
281088
  const apiKey = await getConfiguredApiKey("ELEVENLABS_API_KEY");
@@ -280873,7 +281100,7 @@ async function executeSpeech(options) {
280873
281100
  return { success: false, error: result.error || "TTS generation failed" };
280874
281101
  }
280875
281102
  const outputPath = resolve61(process.cwd(), options.output || "output.mp3");
280876
- await writeFile34(outputPath, result.audioBuffer);
281103
+ await writeFile35(outputPath, result.audioBuffer);
280877
281104
  return { success: true, outputPath, characterCount: result.characterCount };
280878
281105
  } catch (error) {
280879
281106
  return {
@@ -280947,8 +281174,8 @@ var init_speech = __esm({
280947
281174
 
280948
281175
  // ../cli/src/commands/generate/thumbnail.ts
280949
281176
  import { resolve as resolve63, dirname as dirname37, basename as basename17, extname as extname17 } from "node:path";
280950
- import { existsSync as existsSync77 } from "node:fs";
280951
- import { writeFile as writeFile35, mkdir as mkdir24 } from "node:fs/promises";
281177
+ import { existsSync as existsSync78 } from "node:fs";
281178
+ import { writeFile as writeFile36, mkdir as mkdir25 } from "node:fs/promises";
280952
281179
  function registerThumbnailCommand(parent) {
280953
281180
  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) => {
280954
281181
  const startedAt = Date.now();
@@ -280959,7 +281186,7 @@ function registerThumbnailCommand(parent) {
280959
281186
  }
280960
281187
  if (options.bestFrame) {
280961
281188
  const absVideoPath = resolve63(process.cwd(), options.bestFrame);
280962
- if (!existsSync77(absVideoPath)) {
281189
+ if (!existsSync78(absVideoPath)) {
280963
281190
  exitWithError(notFoundError(absVideoPath));
280964
281191
  }
280965
281192
  if (!commandExists("ffmpeg")) {
@@ -281039,8 +281266,8 @@ function registerThumbnailCommand(parent) {
281039
281266
  throw new Error("No image data available");
281040
281267
  }
281041
281268
  outputPath = resolve63(process.cwd(), options.output);
281042
- await mkdir24(dirname37(outputPath), { recursive: true });
281043
- await writeFile35(outputPath, buffer);
281269
+ await mkdir25(dirname37(outputPath), { recursive: true });
281270
+ await writeFile36(outputPath, buffer);
281044
281271
  }
281045
281272
  outputSuccess({
281046
281273
  command: "generate thumbnail",
@@ -281070,8 +281297,8 @@ function registerThumbnailCommand(parent) {
281070
281297
  throw new Error("No image data available");
281071
281298
  }
281072
281299
  const outputPath = resolve63(process.cwd(), options.output);
281073
- await mkdir24(dirname37(outputPath), { recursive: true });
281074
- await writeFile35(outputPath, buffer);
281300
+ await mkdir25(dirname37(outputPath), { recursive: true });
281301
+ await writeFile36(outputPath, buffer);
281075
281302
  saveSpinner.succeed(source_default.green(`Saved to: ${outputPath}`));
281076
281303
  } catch {
281077
281304
  saveSpinner.fail(source_default.red("Failed to save thumbnail"));
@@ -281099,7 +281326,7 @@ var init_thumbnail = __esm({
281099
281326
 
281100
281327
  // ../cli/src/commands/generate/video-extend.ts
281101
281328
  import { resolve as resolve64 } from "node:path";
281102
- import { writeFile as writeFile36 } from "node:fs/promises";
281329
+ import { writeFile as writeFile37 } from "node:fs/promises";
281103
281330
  function registerVideoExtendCommand(parent) {
281104
281331
  parent.command("video-extend", { hidden: true }).description("Extend video duration (Kling by video ID, Veo by operation name)").argument("<id>", "Kling video ID or Veo operation name").option("-p, --provider <provider>", "Provider: kling, veo", "kling").option("-k, --api-key <key>", "API key (KLING_API_KEY or GOOGLE_API_KEY)").option("-o, --output <path>", "Output file path").option("--prompt <text>", "Continuation prompt").option("-d, --duration <sec>", "Duration: 5 or 10 (Kling), 4/6/8 (Veo)", "5").option("--negative <prompt>", "Negative prompt (what to avoid, Kling only)").option("--veo-model <model>", "Veo model: 3.0, 3.1, 3.1-fast", "3.1").option("--no-wait", "Start extension and return task ID without waiting").option("--dry-run", "Preview parameters without executing").action(async (id, options) => {
281105
281332
  const startedAt = Date.now();
@@ -281176,7 +281403,7 @@ function registerVideoExtendCommand(parent) {
281176
281403
  if (options.output && finalResult.videoUrl) {
281177
281404
  const buffer = await downloadVideo(finalResult.videoUrl, apiKey);
281178
281405
  outputPath = resolve64(process.cwd(), options.output);
281179
- await writeFile36(outputPath, buffer);
281406
+ await writeFile37(outputPath, buffer);
281180
281407
  }
281181
281408
  outputSuccess({
281182
281409
  command: "generate video-extend",
@@ -281204,7 +281431,7 @@ function registerVideoExtendCommand(parent) {
281204
281431
  try {
281205
281432
  const buffer = await downloadVideo(finalResult.videoUrl, apiKey);
281206
281433
  const outputPath = resolve64(process.cwd(), options.output);
281207
- await writeFile36(outputPath, buffer);
281434
+ await writeFile37(outputPath, buffer);
281208
281435
  downloadSpinner.succeed(source_default.green(`Saved to: ${outputPath}`));
281209
281436
  } catch (err) {
281210
281437
  downloadSpinner.fail(
@@ -281265,7 +281492,7 @@ function registerVideoExtendCommand(parent) {
281265
281492
  if (options.output && finalResult.videoUrl) {
281266
281493
  const buffer = await downloadVideo(finalResult.videoUrl, apiKey);
281267
281494
  outputPath = resolve64(process.cwd(), options.output);
281268
- await writeFile36(outputPath, buffer);
281495
+ await writeFile37(outputPath, buffer);
281269
281496
  }
281270
281497
  outputSuccess({
281271
281498
  command: "generate video-extend",
@@ -281290,7 +281517,7 @@ function registerVideoExtendCommand(parent) {
281290
281517
  try {
281291
281518
  const buffer = await downloadVideo(finalResult.videoUrl, apiKey);
281292
281519
  const outputPath = resolve64(process.cwd(), options.output);
281293
- await writeFile36(outputPath, buffer);
281520
+ await writeFile37(outputPath, buffer);
281294
281521
  downloadSpinner.succeed(source_default.green(`Saved to: ${outputPath}`));
281295
281522
  } catch (err) {
281296
281523
  downloadSpinner.fail(
@@ -281414,7 +281641,7 @@ var init_openai_image2 = __esm({
281414
281641
  // ../cli/src/commands/generate/image.ts
281415
281642
  import { resolve as resolve65, dirname as dirname38 } from "node:path";
281416
281643
  import { fileURLToPath as fileURLToPath8 } from "node:url";
281417
- import { writeFile as writeFile37, mkdir as mkdir25 } from "node:fs/promises";
281644
+ import { writeFile as writeFile38, mkdir as mkdir26 } from "node:fs/promises";
281418
281645
  function registerImageCommand(parent) {
281419
281646
  parent.command("image").alias("img").description("Generate image using AI (Gemini, OpenAI gpt-image, Grok, or Runway)").argument("[prompt]", "Image description prompt (interactive if omitted)").option(
281420
281647
  "-p, --provider <provider>",
@@ -281551,8 +281778,8 @@ Examples:
281551
281778
  } else {
281552
281779
  throw new Error("No image data available");
281553
281780
  }
281554
- await mkdir25(dirname38(outputPath), { recursive: true });
281555
- await writeFile37(outputPath, buffer);
281781
+ await mkdir26(dirname38(outputPath), { recursive: true });
281782
+ await writeFile38(outputPath, buffer);
281556
281783
  }
281557
281784
  outputSuccess({
281558
281785
  command: "generate image",
@@ -281598,8 +281825,8 @@ Examples:
281598
281825
  throw new Error("No image data available");
281599
281826
  }
281600
281827
  const outputPath = resolve65(process.cwd(), options.output);
281601
- await mkdir25(dirname38(outputPath), { recursive: true });
281602
- await writeFile37(outputPath, buffer);
281828
+ await mkdir26(dirname38(outputPath), { recursive: true });
281829
+ await writeFile38(outputPath, buffer);
281603
281830
  saveSpinner.succeed(source_default.green(`Saved to: ${outputPath}`));
281604
281831
  } catch (err) {
281605
281832
  saveSpinner.fail(
@@ -281673,8 +281900,8 @@ Examples:
281673
281900
  if (outputPath && result.images.length > 0) {
281674
281901
  const img = result.images[0];
281675
281902
  const buffer = Buffer.from(img.base64, "base64");
281676
- await mkdir25(dirname38(outputPath), { recursive: true });
281677
- await writeFile37(outputPath, buffer);
281903
+ await mkdir26(dirname38(outputPath), { recursive: true });
281904
+ await writeFile38(outputPath, buffer);
281678
281905
  }
281679
281906
  outputSuccess({
281680
281907
  command: "generate image",
@@ -281706,8 +281933,8 @@ Examples:
281706
281933
  const img = result.images[0];
281707
281934
  const buffer = Buffer.from(img.base64, "base64");
281708
281935
  const outputPath = resolve65(process.cwd(), options.output);
281709
- await mkdir25(dirname38(outputPath), { recursive: true });
281710
- await writeFile37(outputPath, buffer);
281936
+ await mkdir26(dirname38(outputPath), { recursive: true });
281937
+ await writeFile38(outputPath, buffer);
281711
281938
  saveSpinner.succeed(source_default.green(`Saved to: ${outputPath}`));
281712
281939
  } catch (err) {
281713
281940
  saveSpinner.fail(
@@ -281766,8 +281993,8 @@ Examples:
281766
281993
  } else {
281767
281994
  throw new Error("No image data available");
281768
281995
  }
281769
- await mkdir25(dirname38(outputPath), { recursive: true });
281770
- await writeFile37(outputPath, buffer);
281996
+ await mkdir26(dirname38(outputPath), { recursive: true });
281997
+ await writeFile38(outputPath, buffer);
281771
281998
  }
281772
281999
  outputSuccess({
281773
282000
  command: "generate image",
@@ -281807,8 +282034,8 @@ Examples:
281807
282034
  throw new Error("No image data available");
281808
282035
  }
281809
282036
  const outputPath = resolve65(process.cwd(), options.output);
281810
- await mkdir25(dirname38(outputPath), { recursive: true });
281811
- await writeFile37(outputPath, buffer);
282037
+ await mkdir26(dirname38(outputPath), { recursive: true });
282038
+ await writeFile38(outputPath, buffer);
281812
282039
  saveSpinner.succeed(source_default.green(`Saved to: ${outputPath}`));
281813
282040
  } catch (err) {
281814
282041
  saveSpinner.fail(
@@ -282838,7 +283065,7 @@ var init_dist5 = __esm({
282838
283065
 
282839
283066
  // ../cli/src/commands/generate/video.ts
282840
283067
  import { resolve as resolve66 } from "node:path";
282841
- import { readFile as readFile38, writeFile as writeFile38 } from "node:fs/promises";
283068
+ import { readFile as readFile36, writeFile as writeFile39 } from "node:fs/promises";
282842
283069
  function registerVideoCommand(parent) {
282843
283070
  parent.command("video").alias("vid").description("Generate video using AI (Seedance, Grok, Kling, Runway, Veo, or Gemini Omni)").argument("[prompt]", "Text prompt describing the video (interactive if omitted)").option(
282844
283071
  "-p, --provider <provider>",
@@ -282950,7 +283177,7 @@ Examples:
282950
283177
  let isImageToVideo = false;
282951
283178
  if (options.image) {
282952
283179
  const imagePath = resolve66(process.cwd(), options.image);
282953
- const imageBuffer = await readFile38(imagePath);
283180
+ const imageBuffer = await readFile36(imagePath);
282954
283181
  const ext = options.image.toLowerCase().split(".").pop();
282955
283182
  const mimeTypes = {
282956
283183
  jpg: "image/jpeg",
@@ -283192,7 +283419,7 @@ Examples:
283192
283419
  let lastFrame;
283193
283420
  if (options.lastFrame) {
283194
283421
  const lastFramePath = resolve66(process.cwd(), options.lastFrame);
283195
- const lastFrameBuffer = await readFile38(lastFramePath);
283422
+ const lastFrameBuffer = await readFile36(lastFramePath);
283196
283423
  const ext = options.lastFrame.toLowerCase().split(".").pop();
283197
283424
  const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : `image/${ext || "png"}`;
283198
283425
  lastFrame = `data:${mimeType};base64,${lastFrameBuffer.toString("base64")}`;
@@ -283202,7 +283429,7 @@ Examples:
283202
283429
  refImages = [];
283203
283430
  for (const refPath of options.refImages.slice(0, 3)) {
283204
283431
  const absRefPath = resolve66(process.cwd(), refPath);
283205
- const refBuffer = await readFile38(absRefPath);
283432
+ const refBuffer = await readFile36(absRefPath);
283206
283433
  const ext = refPath.toLowerCase().split(".").pop();
283207
283434
  const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : `image/${ext || "png"}`;
283208
283435
  refImages.push({ base64: refBuffer.toString("base64"), mimeType });
@@ -283334,7 +283561,7 @@ Examples:
283334
283561
  if (seedanceEndImagePath && seedanceReferences.length === 0) {
283335
283562
  try {
283336
283563
  const absEndImagePath = resolve66(process.cwd(), seedanceEndImagePath);
283337
- const endImageBuffer = await readFile38(absEndImagePath);
283564
+ const endImageBuffer = await readFile36(absEndImagePath);
283338
283565
  const ext = seedanceEndImagePath.toLowerCase().split(".").pop();
283339
283566
  const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : `image/${ext || "png"}`;
283340
283567
  const uploadHost = await resolveUploadHost();
@@ -283390,7 +283617,7 @@ Examples:
283390
283617
  if (options.output && finalResult.videoUrl) {
283391
283618
  const buffer = await downloadVideo(finalResult.videoUrl, apiKey);
283392
283619
  outputPath = resolve66(process.cwd(), options.output);
283393
- await writeFile38(outputPath, buffer);
283620
+ await writeFile39(outputPath, buffer);
283394
283621
  }
283395
283622
  outputSuccess({
283396
283623
  command: "generate video",
@@ -283418,7 +283645,7 @@ Examples:
283418
283645
  try {
283419
283646
  const buffer = await downloadVideo(finalResult.videoUrl, apiKey);
283420
283647
  const outputPath = resolve66(process.cwd(), options.output);
283421
- await writeFile38(outputPath, buffer);
283648
+ await writeFile39(outputPath, buffer);
283422
283649
  downloadSpinner.succeed(source_default.green(`Saved to: ${outputPath}`));
283423
283650
  } catch (err) {
283424
283651
  downloadSpinner.fail(
@@ -283461,7 +283688,7 @@ async function fileInputToUrlOrDataUri2(input3, fallbackMimeType) {
283461
283688
  return input3;
283462
283689
  }
283463
283690
  const absPath = resolve66(process.cwd(), input3);
283464
- const buffer = await readFile38(absPath);
283691
+ const buffer = await readFile36(absPath);
283465
283692
  return `data:${mimeTypeForPath2(input3, fallbackMimeType)};base64,${buffer.toString("base64")}`;
283466
283693
  }
283467
283694
  function mimeTypeForPath2(path16, fallback2) {
@@ -283542,7 +283769,7 @@ var init_sanitize = __esm({
283542
283769
 
283543
283770
  // ../cli/src/commands/generate/storyboard.ts
283544
283771
  import { resolve as resolve67 } from "node:path";
283545
- import { writeFile as writeFile39 } from "node:fs/promises";
283772
+ import { writeFile as writeFile40 } from "node:fs/promises";
283546
283773
  async function executeStoryboard(options) {
283547
283774
  const { content, duration, creativity = "low", output: output3, apiKey } = options;
283548
283775
  try {
@@ -283561,7 +283788,7 @@ async function executeStoryboard(options) {
283561
283788
  let outputPath;
283562
283789
  if (output3) {
283563
283790
  outputPath = resolve67(process.cwd(), output3);
283564
- await writeFile39(outputPath, JSON.stringify(segments, null, 2), "utf-8");
283791
+ await writeFile40(outputPath, JSON.stringify(segments, null, 2), "utf-8");
283565
283792
  }
283566
283793
  return { success: true, segments, segmentCount: segments.length, outputPath };
283567
283794
  } catch (error) {
@@ -302590,7 +302817,7 @@ __export(detect_exports, {
302590
302817
  executeDetectScenes: () => executeDetectScenes,
302591
302818
  executeDetectSilence: () => executeDetectSilence
302592
302819
  });
302593
- import { readFile as readFile43, writeFile as writeFile43 } from "node:fs/promises";
302820
+ import { readFile as readFile41, writeFile as writeFile44 } from "node:fs/promises";
302594
302821
  import { resolve as resolve73, basename as basename20 } from "node:path";
302595
302822
  async function executeDetectScenes(options) {
302596
302823
  try {
@@ -302629,7 +302856,7 @@ async function executeDetectScenes(options) {
302629
302856
  }));
302630
302857
  if (options.outputPath) {
302631
302858
  const outputPath = resolve73(process.cwd(), options.outputPath);
302632
- await writeFile43(outputPath, JSON.stringify({ source: absPath, totalDuration, threshold, scenes: result }, null, 2), "utf-8");
302859
+ await writeFile44(outputPath, JSON.stringify({ source: absPath, totalDuration, threshold, scenes: result }, null, 2), "utf-8");
302633
302860
  }
302634
302861
  return { success: true, scenes: result, totalDuration };
302635
302862
  } catch (error) {
@@ -302676,7 +302903,7 @@ async function executeDetectSilence(options) {
302676
302903
  }
302677
302904
  if (options.outputPath) {
302678
302905
  const outputPath = resolve73(process.cwd(), options.outputPath);
302679
- await writeFile43(outputPath, JSON.stringify({ source: absPath, silences }, null, 2), "utf-8");
302906
+ await writeFile44(outputPath, JSON.stringify({ source: absPath, silences }, null, 2), "utf-8");
302680
302907
  }
302681
302908
  return { success: true, silences };
302682
302909
  } catch (error) {
@@ -302725,7 +302952,7 @@ async function executeDetectBeats(options) {
302725
302952
  }
302726
302953
  if (options.outputPath) {
302727
302954
  const outputPath = resolve73(process.cwd(), options.outputPath);
302728
- await writeFile43(outputPath, JSON.stringify({ source: absPath, beatCount: beats.length, beats }, null, 2), "utf-8");
302955
+ await writeFile44(outputPath, JSON.stringify({ source: absPath, beatCount: beats.length, beats }, null, 2), "utf-8");
302729
302956
  }
302730
302957
  return { success: true, beats, beatCount: beats.length };
302731
302958
  } catch (error) {
@@ -302829,12 +303056,12 @@ var init_detect = __esm({
302829
303056
  duration: (i3 < scenes.length - 1 ? scenes[i3 + 1].timestamp : totalDuration) - s2.timestamp
302830
303057
  }))
302831
303058
  };
302832
- await writeFile43(outputPath, JSON.stringify(result, null, 2), "utf-8");
303059
+ await writeFile44(outputPath, JSON.stringify(result, null, 2), "utf-8");
302833
303060
  console.log(source_default.green(`Saved to: ${outputPath}`));
302834
303061
  }
302835
303062
  if (options.project) {
302836
303063
  const projectPath = resolve73(process.cwd(), options.project);
302837
- const content = await readFile43(projectPath, "utf-8");
303064
+ const content = await readFile41(projectPath, "utf-8");
302838
303065
  const data2 = JSON.parse(content);
302839
303066
  const project = Project.fromJSON(data2);
302840
303067
  let source3 = project.getSources().find((s2) => s2.url === absPath);
@@ -302859,7 +303086,7 @@ var init_detect = __esm({
302859
303086
  sourceEndOffset: end
302860
303087
  });
302861
303088
  }
302862
- await writeFile43(projectPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
303089
+ await writeFile44(projectPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
302863
303090
  console.log(source_default.green(`Added ${scenes.length} clips to project`));
302864
303091
  }
302865
303092
  } catch (error) {
@@ -302941,7 +303168,7 @@ var init_detect = __esm({
302941
303168
  console.log();
302942
303169
  if (options.output) {
302943
303170
  const outputPath = resolve73(process.cwd(), options.output);
302944
- await writeFile43(
303171
+ await writeFile44(
302945
303172
  outputPath,
302946
303173
  JSON.stringify({ source: absPath, silences }, null, 2),
302947
303174
  "utf-8"
@@ -303026,7 +303253,7 @@ var init_detect = __esm({
303026
303253
  console.log();
303027
303254
  if (options.output) {
303028
303255
  const outputPath = resolve73(process.cwd(), options.output);
303029
- await writeFile43(
303256
+ await writeFile44(
303030
303257
  outputPath,
303031
303258
  JSON.stringify({ source: absPath, beatCount: beats.length, beats }, null, 2),
303032
303259
  "utf-8"
@@ -303620,7 +303847,7 @@ init_scene_project();
303620
303847
  init_project_config();
303621
303848
  init_cost_tier();
303622
303849
  import { resolve as resolve29, relative as relative8, dirname as dirname24 } from "node:path";
303623
- import { mkdir as mkdir10, readFile as readFile15, writeFile as writeFile14, access as access4, copyFile as copyFile3 } from "node:fs/promises";
303850
+ import { mkdir as mkdir11, readFile as readFile14, writeFile as writeFile15, access as access4, copyFile as copyFile3 } from "node:fs/promises";
303624
303851
  import { existsSync as existsSync46 } from "node:fs";
303625
303852
 
303626
303853
  // ../cli/src/commands/_shared/scene-html-emit.ts
@@ -303646,7 +303873,7 @@ function esc(text) {
303646
303873
  function clean(text) {
303647
303874
  return (text ?? "").replace(/\s+/g, " ").trim();
303648
303875
  }
303649
- function humanise(id) {
303876
+ function humanise2(id) {
303650
303877
  return id.split(/[-_]+/).filter(Boolean).map((w4) => w4.charAt(0).toUpperCase() + w4.slice(1)).join(" ");
303651
303878
  }
303652
303879
  function sceneCompositionId(beatId) {
@@ -303698,7 +303925,7 @@ function buildPreset(input3) {
303698
303925
  const id = input3.id;
303699
303926
  const scope = `[data-composition-id="${id}"]`;
303700
303927
  const displayId = input3.beatId ?? id;
303701
- const headline = clean(input3.headline) || humanise(displayId);
303928
+ const headline = clean(input3.headline) || humanise2(displayId);
303702
303929
  const subhead = clean(input3.subhead);
303703
303930
  const kicker = clean(input3.kicker);
303704
303931
  const dur = input3.duration;
@@ -303807,7 +304034,7 @@ function buildPreset(input3) {
303807
304034
  };
303808
304035
  }
303809
304036
  case "explainer": {
303810
- const k2 = kicker || humanise(displayId).toUpperCase();
304037
+ const k2 = kicker || humanise2(displayId).toUpperCase();
303811
304038
  const sub = subhead || "";
303812
304039
  const transcript = input3.transcript;
303813
304040
  const useWordSync = !!(transcript && transcript.length > 0 && sub);
@@ -303895,7 +304122,7 @@ function buildPreset(input3) {
303895
304122
  };
303896
304123
  }
303897
304124
  case "product-shot": {
303898
- const label = kicker || humanise(displayId);
304125
+ const label = kicker || humanise2(displayId);
303899
304126
  const headlineFontPx = fitFontSize(headline, 72, 40, 50);
303900
304127
  return {
303901
304128
  css: `${scope} {
@@ -304097,9 +304324,10 @@ init_transcribe_narration();
304097
304324
  // ../cli/src/commands/_shared/scene-submit.ts
304098
304325
  init_scene_lint();
304099
304326
  init_storyboard_parse();
304327
+ init_storyboard_source();
304100
304328
  init_root_sync();
304101
304329
  import { existsSync as existsSync44 } from "node:fs";
304102
- import { mkdir as mkdir9, readFile as readFile12, writeFile as writeFile12 } from "node:fs/promises";
304330
+ import { mkdir as mkdir10, writeFile as writeFile13 } from "node:fs/promises";
304103
304331
  import { join as join58, resolve as resolve26 } from "node:path";
304104
304332
  async function executeSceneSubmit(opts) {
304105
304333
  const projectDir = resolve26(opts.projectDir);
@@ -304122,7 +304350,7 @@ async function executeSceneSubmit(opts) {
304122
304350
  error: `STORYBOARD.md not found at ${storyboardPath}. Run \`vibe init <dir>\` first.`
304123
304351
  };
304124
304352
  }
304125
- const parsed = parseStoryboard(await readFile12(storyboardPath, "utf-8"));
304353
+ const parsed = parseStoryboard(await loadStoryboardMarkdown(projectDir));
304126
304354
  const beat = parsed.beats.find((b4) => b4.id === beatId);
304127
304355
  if (!beat) {
304128
304356
  const available = parsed.beats.map((b4) => b4.id).join(", ") || "(none)";
@@ -304193,8 +304421,8 @@ async function executeSceneSubmit(opts) {
304193
304421
  return { ...base2, success: true };
304194
304422
  }
304195
304423
  const absPath = join58(projectDir, scenePath);
304196
- await mkdir9(join58(projectDir, "compositions"), { recursive: true });
304197
- await writeFile12(absPath, html.endsWith("\n") ? html : `${html}
304424
+ await mkdir10(join58(projectDir, "compositions"), { recursive: true });
304425
+ await writeFile13(absPath, html.endsWith("\n") ? html : `${html}
304198
304426
  `, "utf-8");
304199
304427
  return {
304200
304428
  ...base2,
@@ -304211,6 +304439,7 @@ async function executeSceneSubmit(opts) {
304211
304439
  init_scene_repair();
304212
304440
  init_root_sync();
304213
304441
  init_storyboard_edit();
304442
+ init_storyboard_source();
304214
304443
  function validateDuration(value) {
304215
304444
  const n2 = parseFloat(value);
304216
304445
  if (!Number.isFinite(n2) || n2 <= 0 || n2 > 3600) {
@@ -304387,7 +304616,7 @@ sceneCommand.command("submit").description(
304387
304616
  }
304388
304617
  html = Buffer.concat(chunks).toString("utf-8");
304389
304618
  } else if (options.file) {
304390
- html = await readFile15(resolve29(String(options.file)), "utf-8");
304619
+ html = await readFile14(resolve29(String(options.file)), "utf-8");
304391
304620
  } else {
304392
304621
  exitWithError(
304393
304622
  usageError(
@@ -304718,7 +304947,7 @@ async function resolveNarrationText(value) {
304718
304947
  if (!value) return void 0;
304719
304948
  const looksLikePath = /\.[a-z]{2,4}$/i.test(value) || value.includes("/") || value.includes("\\");
304720
304949
  if (looksLikePath && existsSync46(value)) {
304721
- return (await readFile15(value, "utf-8")).trim();
304950
+ return (await readFile14(value, "utf-8")).trim();
304722
304951
  }
304723
304952
  return value.trim();
304724
304953
  }
@@ -304759,7 +304988,7 @@ async function executeSceneAdd(opts) {
304759
304988
  if (!opts.force && await pathExists2(scenePath)) {
304760
304989
  return errResult(`Scene already exists: ${sceneRel}. Re-run with --force to overwrite.`);
304761
304990
  }
304762
- const rootHtmlBefore = await readFile15(rootPath, "utf-8");
304991
+ const rootHtmlBefore = await readFile14(rootPath, "utf-8");
304763
304992
  let dims = readRootDims(rootHtmlBefore);
304764
304993
  if (!dims) {
304765
304994
  const cfg2 = await loadVibeProjectConfig(projectDir);
@@ -304783,7 +305012,7 @@ async function executeSceneAdd(opts) {
304783
305012
  }
304784
305013
  audioRelPath = `assets/narration-${id}.${ext}`;
304785
305014
  audioAbsPath = resolve29(projectDir, audioRelPath);
304786
- await mkdir10(dirname24(audioAbsPath), { recursive: true });
305015
+ await mkdir11(dirname24(audioAbsPath), { recursive: true });
304787
305016
  await copyFile3(sourceAbs, audioAbsPath);
304788
305017
  try {
304789
305018
  narrationDuration = await getAudioDuration(audioAbsPath);
@@ -304816,8 +305045,8 @@ async function executeSceneAdd(opts) {
304816
305045
  }
304817
305046
  audioRelPath = `assets/narration-${id}.${resolution.audioExtension}`;
304818
305047
  audioAbsPath = resolve29(projectDir, audioRelPath);
304819
- await mkdir10(dirname24(audioAbsPath), { recursive: true });
304820
- await writeFile14(audioAbsPath, tts.audioBuffer);
305048
+ await mkdir11(dirname24(audioAbsPath), { recursive: true });
305049
+ await writeFile15(audioAbsPath, tts.audioBuffer);
304821
305050
  try {
304822
305051
  narrationDuration = await getAudioDuration(audioAbsPath);
304823
305052
  } catch {
@@ -304842,7 +305071,7 @@ async function executeSceneAdd(opts) {
304842
305071
  if (words.length > 0) {
304843
305072
  transcriptRelPath = beatTranscriptRelPath(id);
304844
305073
  const transcriptAbs = resolve29(projectDir, transcriptRelPath);
304845
- await writeFile14(transcriptAbs, JSON.stringify(words, null, 2), "utf-8");
305074
+ await writeFile15(transcriptAbs, JSON.stringify(words, null, 2), "utf-8");
304846
305075
  transcriptWordCount = words.length;
304847
305076
  transcriptWords = words;
304848
305077
  } else {
@@ -304879,7 +305108,7 @@ async function executeSceneAdd(opts) {
304879
305108
  const img = imageResult.images[0];
304880
305109
  imageRelPath = `assets/scene-${id}.png`;
304881
305110
  imageAbsPath = resolve29(projectDir, imageRelPath);
304882
- await mkdir10(dirname24(imageAbsPath), { recursive: true });
305111
+ await mkdir11(dirname24(imageAbsPath), { recursive: true });
304883
305112
  let buffer;
304884
305113
  if (img.base64) {
304885
305114
  buffer = Buffer.from(img.base64, "base64");
@@ -304889,7 +305118,7 @@ async function executeSceneAdd(opts) {
304889
305118
  } else {
304890
305119
  return errResult("OpenAI returned no image data");
304891
305120
  }
304892
- await writeFile14(imageAbsPath, buffer);
305121
+ await writeFile15(imageAbsPath, buffer);
304893
305122
  } else {
304894
305123
  const googleKey = await getApiKey("GOOGLE_API_KEY", "Google");
304895
305124
  if (!googleKey) {
@@ -304906,9 +305135,9 @@ async function executeSceneAdd(opts) {
304906
305135
  }
304907
305136
  imageRelPath = `assets/scene-${id}.png`;
304908
305137
  imageAbsPath = resolve29(projectDir, imageRelPath);
304909
- await mkdir10(dirname24(imageAbsPath), { recursive: true });
305138
+ await mkdir11(dirname24(imageAbsPath), { recursive: true });
304910
305139
  const buffer = Buffer.from(imageResult.images[0].base64, "base64");
304911
- await writeFile14(imageAbsPath, buffer);
305140
+ await writeFile15(imageAbsPath, buffer);
304912
305141
  }
304913
305142
  }
304914
305143
  let lottieRelPath;
@@ -304925,7 +305154,7 @@ async function executeSceneAdd(opts) {
304925
305154
  }
304926
305155
  lottieRelPath = `assets/lottie-${id}.${ext}`;
304927
305156
  lottieAbsPath = resolve29(projectDir, lottieRelPath);
304928
- await mkdir10(dirname24(lottieAbsPath), { recursive: true });
305157
+ await mkdir11(dirname24(lottieAbsPath), { recursive: true });
304929
305158
  await copyFile3(sourceAbs, lottieAbsPath);
304930
305159
  lottieInput = {
304931
305160
  src: lottieRelPath,
@@ -304956,21 +305185,29 @@ async function executeSceneAdd(opts) {
304956
305185
  let storyboardAction = "skipped";
304957
305186
  const shouldSyncStoryboard = opts.syncStoryboard !== false && await pathExists2(storyboardAbsPath);
304958
305187
  if (shouldSyncStoryboard) {
304959
- opts.onProgress?.("Syncing STORYBOARD.md...");
304960
- const before2 = await readFile15(storyboardAbsPath, "utf-8");
304961
- const synced = upsertStoryboardBeat(before2, {
305188
+ const upsert = {
304962
305189
  beatId: id,
304963
305190
  title: opts.headline,
304964
305191
  duration,
304965
305192
  narration: narrationText && !opts.skipAudio ? narrationText : void 0,
304966
305193
  backdrop: opts.visuals && !opts.skipImage ? opts.visuals : void 0
304967
- });
304968
- if (synced.markdown !== before2) {
304969
- await writeFile14(storyboardAbsPath, synced.markdown, "utf-8");
305194
+ };
305195
+ if ((await loadStoryboard(projectDir)).layout === "bundle") {
305196
+ opts.onProgress?.("Syncing scenes/...");
305197
+ const synced = await upsertScene(projectDir, upsert);
305198
+ storyboardPath = relative8(process.cwd(), synced.path) || synced.path;
305199
+ storyboardAction = synced.action;
305200
+ } else {
305201
+ opts.onProgress?.("Syncing STORYBOARD.md...");
305202
+ const before2 = await readFile14(storyboardAbsPath, "utf-8");
305203
+ const synced = upsertStoryboardBeat(before2, upsert);
305204
+ if (synced.markdown !== before2) {
305205
+ await writeFile15(storyboardAbsPath, synced.markdown, "utf-8");
305206
+ }
305207
+ storyboardPath = relative8(process.cwd(), storyboardAbsPath) || storyboardAbsPath;
305208
+ storyboardAction = synced.action;
304970
305209
  }
304971
- storyboardPath = relative8(process.cwd(), storyboardAbsPath) || storyboardAbsPath;
304972
305210
  storyboardSynced = true;
304973
- storyboardAction = synced.action;
304974
305211
  }
304975
305212
  opts.onProgress?.("Emitting scene HTML...");
304976
305213
  const sceneHtml = emitSceneHtml({
@@ -304987,8 +305224,8 @@ async function executeSceneAdd(opts) {
304987
305224
  transcript: transcriptWords,
304988
305225
  lottie: lottieInput
304989
305226
  });
304990
- await mkdir10(dirname24(scenePath), { recursive: true });
304991
- await writeFile14(scenePath, sceneHtml, "utf-8");
305227
+ await mkdir11(dirname24(scenePath), { recursive: true });
305228
+ await writeFile15(scenePath, sceneHtml, "utf-8");
304992
305229
  opts.onProgress?.("Updating root composition...");
304993
305230
  let start = 0;
304994
305231
  if (storyboardSynced) {
@@ -304999,14 +305236,14 @@ async function executeSceneAdd(opts) {
304999
305236
  });
305000
305237
  const blockingIssue = syncPlan.issues.find((issue) => issue.severity === "error");
305001
305238
  if (blockingIssue) return errResult(blockingIssue.message);
305002
- const rootHtmlAfterSync = await readFile15(rootPath, "utf-8");
305239
+ const rootHtmlAfterSync = await readFile14(rootPath, "utf-8");
305003
305240
  start = readClipStart(rootHtmlAfterSync, id) ?? 0;
305004
305241
  } else {
305005
305242
  start = nextSceneStart(rootHtmlBefore, SCENE_OVERLAP_SECONDS);
305006
305243
  const existingClipCount = (rootHtmlBefore.match(/<div\s+class="clip"/g) || []).length;
305007
305244
  const trackIndex = existingClipCount % 2 + 1;
305008
305245
  const updated = insertClipIntoRoot(rootHtmlBefore, { id, start, duration, trackIndex });
305009
- await writeFile14(rootPath, updated, "utf-8");
305246
+ await writeFile15(rootPath, updated, "utf-8");
305010
305247
  }
305011
305248
  const transcriptAbsPath = transcriptRelPath ? resolve29(projectDir, transcriptRelPath) : void 0;
305012
305249
  return {
@@ -306037,7 +306274,7 @@ init_api_key();
306037
306274
  init_exec_safe();
306038
306275
  init_remotion();
306039
306276
  import { resolve as resolve51, dirname as dirname35, basename as basename14 } from "node:path";
306040
- import { writeFile as writeFile29, mkdir as mkdir21, rm as rm8 } from "node:fs/promises";
306277
+ import { writeFile as writeFile30, mkdir as mkdir23, rm as rm9 } from "node:fs/promises";
306041
306278
  import { existsSync as existsSync71 } from "node:fs";
306042
306279
  import { tmpdir as tmpdir11 } from "node:os";
306043
306280
  var ASS_STYLES = ["karaoke-sweep", "typewriter"];
@@ -306189,7 +306426,7 @@ async function executeAnimatedCaption(options) {
306189
306426
  }
306190
306427
  const effectiveFontSize = fontSize ?? Math.round(height * 0.04);
306191
306428
  const tmpAudioDir = resolve51(tmpdir11(), `vf-ac-${Date.now()}`);
306192
- await mkdir21(tmpAudioDir, { recursive: true });
306429
+ await mkdir23(tmpAudioDir, { recursive: true });
306193
306430
  const audioPath = resolve51(tmpAudioDir, "audio.wav");
306194
306431
  await execSafe("ffmpeg", [
306195
306432
  "-y",
@@ -306206,13 +306443,13 @@ async function executeAnimatedCaption(options) {
306206
306443
  ], { timeout: 12e4 });
306207
306444
  const apiKey = await getApiKey("OPENAI_API_KEY", "OpenAI");
306208
306445
  if (!apiKey) {
306209
- await rm8(tmpAudioDir, { recursive: true, force: true }).catch(() => {
306446
+ await rm9(tmpAudioDir, { recursive: true, force: true }).catch(() => {
306210
306447
  });
306211
306448
  return { success: false, error: "OPENAI_API_KEY required for Whisper transcription. Run 'vibe setup' or set OPENAI_API_KEY in .env" };
306212
306449
  }
306213
306450
  const transcript = await transcribeWithWords(audioPath, apiKey, language);
306214
306451
  if (!transcript.words || transcript.words.length === 0) {
306215
- await rm8(tmpAudioDir, { recursive: true, force: true }).catch(() => {
306452
+ await rm9(tmpAudioDir, { recursive: true, force: true }).catch(() => {
306216
306453
  });
306217
306454
  return { success: false, error: "No words detected in transcription" };
306218
306455
  }
@@ -306220,7 +306457,7 @@ async function executeAnimatedCaption(options) {
306220
306457
  const absOutputPath = resolve51(process.cwd(), outputPath);
306221
306458
  const outDir = dirname35(absOutputPath);
306222
306459
  if (!existsSync71(outDir)) {
306223
- await mkdir21(outDir, { recursive: true });
306460
+ await mkdir23(outDir, { recursive: true });
306224
306461
  }
306225
306462
  if (tier === "ass") {
306226
306463
  const assContent = generateASS(
@@ -306229,7 +306466,7 @@ async function executeAnimatedCaption(options) {
306229
306466
  { highlightColor, fontSize: effectiveFontSize, position, width, height }
306230
306467
  );
306231
306468
  const assPath = resolve51(tmpAudioDir, "captions.ass");
306232
- await writeFile29(assPath, assContent, "utf-8");
306469
+ await writeFile30(assPath, assContent, "utf-8");
306233
306470
  const escapedAssPath = assPath.replace(/\\/g, "\\\\").replace(/:/g, "\\:");
306234
306471
  await execSafe("ffmpeg", [
306235
306472
  "-y",
@@ -306266,12 +306503,12 @@ async function executeAnimatedCaption(options) {
306266
306503
  outputPath: absOutputPath
306267
306504
  });
306268
306505
  if (!renderResult.success) {
306269
- await rm8(tmpAudioDir, { recursive: true, force: true }).catch(() => {
306506
+ await rm9(tmpAudioDir, { recursive: true, force: true }).catch(() => {
306270
306507
  });
306271
306508
  return { success: false, error: renderResult.error };
306272
306509
  }
306273
306510
  }
306274
- await rm8(tmpAudioDir, { recursive: true, force: true }).catch(() => {
306511
+ await rm9(tmpAudioDir, { recursive: true, force: true }).catch(() => {
306275
306512
  });
306276
306513
  return {
306277
306514
  success: true,
@@ -306766,14 +307003,14 @@ init_ai_analyze();
306766
307003
  init_dist();
306767
307004
  init_engine();
306768
307005
  init_ai_helpers();
306769
- import { readFile as readFile34, writeFile as writeFile31 } from "node:fs/promises";
307006
+ import { readFile as readFile31, writeFile as writeFile33 } from "node:fs/promises";
306770
307007
  import { resolve as resolve55 } from "node:path";
306771
307008
  async function executeSuggestEdit(options) {
306772
307009
  try {
306773
307010
  const apiKey = options.apiKey ?? process.env.GOOGLE_API_KEY;
306774
307011
  if (!apiKey) return { success: false, error: "GOOGLE_API_KEY required for suggest" };
306775
307012
  const filePath = resolve55(process.cwd(), options.projectPath);
306776
- const content = await readFile34(filePath, "utf-8");
307013
+ const content = await readFile31(filePath, "utf-8");
306777
307014
  const data2 = JSON.parse(content);
306778
307015
  const project = Project.fromJSON(data2);
306779
307016
  const gemini = new GeminiProvider();
@@ -306784,7 +307021,7 @@ async function executeSuggestEdit(options) {
306784
307021
  const first3 = suggestions[0];
306785
307022
  const applied = applySuggestion(project, first3);
306786
307023
  if (applied) {
306787
- await writeFile31(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
307024
+ await writeFile33(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
306788
307025
  return {
306789
307026
  success: true,
306790
307027
  suggestions,
@@ -306810,8 +307047,8 @@ init_project_config();
306810
307047
  init_scene_lint();
306811
307048
  init_root_sync();
306812
307049
  init_review_report();
306813
- import { existsSync as existsSync74 } from "node:fs";
306814
- import { readdir as readdir7, readFile as readFile35, stat as stat7 } from "node:fs/promises";
307050
+ import { existsSync as existsSync75 } from "node:fs";
307051
+ import { readdir as readdir8, readFile as readFile33, stat as stat7 } from "node:fs/promises";
306815
307052
  import { basename as basename15, isAbsolute as isAbsolute12, join as join70, relative as relative13, resolve as resolve56 } from "node:path";
306816
307053
  async function inspectProject(opts) {
306817
307054
  const projectDir = resolve56(opts.projectDir);
@@ -306826,7 +307063,7 @@ async function inspectProject(opts) {
306826
307063
  assets: { checked: 0, missing: [], stale: [], unknownFreshness: [] },
306827
307064
  rootSync: { ok: null, issueCount: 0, issues: [] }
306828
307065
  };
306829
- if (!existsSync74(projectDir) || !await isDirectory(projectDir)) {
307066
+ if (!existsSync75(projectDir) || !await isDirectory(projectDir)) {
306830
307067
  issues.push({
306831
307068
  severity: "error",
306832
307069
  code: "PROJECT_NOT_FOUND",
@@ -306849,7 +307086,7 @@ async function inspectProject(opts) {
306849
307086
  root: join70(projectDir, "index.html")
306850
307087
  };
306851
307088
  for (const [name, path16] of Object.entries(coreFiles)) {
306852
- checks.files[name] = existsSync74(path16);
307089
+ checks.files[name] = existsSync75(path16);
306853
307090
  }
306854
307091
  let beatIds = [];
306855
307092
  let inspectedBeatIds = [];
@@ -306863,7 +307100,7 @@ async function inspectProject(opts) {
306863
307100
  });
306864
307101
  retryWith.push(`vibe init ${projectDir} --from "<brief>" --json`);
306865
307102
  } else {
306866
- const storyboard = await readFile35(coreFiles.storyboard, "utf-8");
307103
+ const storyboard = await readFile33(coreFiles.storyboard, "utf-8");
306867
307104
  const validation3 = validateStoryboardMarkdown(storyboard);
306868
307105
  storyboardBeats = validation3.beats;
306869
307106
  beatIds = validation3.beats.map((beat) => beat.id);
@@ -306905,7 +307142,7 @@ async function inspectProject(opts) {
306905
307142
  suggestedFix: "Create DESIGN.md or rerun `vibe init --from` in a new project."
306906
307143
  });
306907
307144
  } else {
306908
- const design = await readFile35(coreFiles.design, "utf-8");
307145
+ const design = await readFile33(coreFiles.design, "utf-8");
306909
307146
  for (const issue of designPlaceholderIssues(design)) {
306910
307147
  issues.push(issue);
306911
307148
  }
@@ -306931,7 +307168,7 @@ async function inspectProject(opts) {
306931
307168
  retryWith.push(`vibe build ${projectDir} --stage sync --json`);
306932
307169
  }
306933
307170
  const compositionsDir = join70(projectDir, "compositions");
306934
- const existingComps = existsSync74(compositionsDir) ? new Set(await listHtmlBasenames(compositionsDir)) : /* @__PURE__ */ new Set();
307171
+ const existingComps = existsSync75(compositionsDir) ? new Set(await listHtmlBasenames(compositionsDir)) : /* @__PURE__ */ new Set();
306935
307172
  checks.compositions.expected = inspectedBeatIds.length;
306936
307173
  for (const beatId of inspectedBeatIds) {
306937
307174
  const file = `scene-${beatId}.html`;
@@ -307151,7 +307388,7 @@ function projectSourceReports(checks) {
307151
307388
  async function inspectBuildReport(projectDir, reportPath, beatId, checks, issues, retryWith, storyboardBeats) {
307152
307389
  checks.buildReport.exists = true;
307153
307390
  try {
307154
- const report = JSON.parse(await readFile35(reportPath, "utf-8"));
307391
+ const report = JSON.parse(await readFile33(reportPath, "utf-8"));
307155
307392
  if (typeof report.outputPath === "string") checks.buildReport.outputPath = report.outputPath;
307156
307393
  const reportBeats = report.beats ?? [];
307157
307394
  const selectedReportBeats = reportBeats.filter((item) => !beatId || item.id === beatId);
@@ -307288,7 +307525,7 @@ function inspectReportedAsset(opts) {
307288
307525
  opts.checks.assets.checked++;
307289
307526
  if (isExternalRef2(opts.value)) return;
307290
307527
  const abs = isAbsolute12(opts.value) ? opts.value : resolve56(opts.projectDir, opts.value);
307291
- if (existsSync74(abs)) return;
307528
+ if (existsSync75(abs)) return;
307292
307529
  opts.checks.assets.missing.push(opts.value);
307293
307530
  opts.issues.push({
307294
307531
  severity: "warning",
@@ -307369,7 +307606,7 @@ async function isDirectory(path16) {
307369
307606
  }
307370
307607
  }
307371
307608
  async function listHtmlBasenames(dir) {
307372
- const entries2 = await readdir7(dir, { withFileTypes: true });
307609
+ const entries2 = await readdir8(dir, { withFileTypes: true });
307373
307610
  return entries2.filter((entry) => entry.isFile() && entry.name.endsWith(".html")).map((entry) => entry.name);
307374
307611
  }
307375
307612
  function isExternalRef2(value) {
@@ -307382,8 +307619,8 @@ init_exec_safe();
307382
307619
  init_project_config();
307383
307620
  init_storyboard_parse();
307384
307621
  init_review_report();
307385
- import { existsSync as existsSync76 } from "node:fs";
307386
- import { readdir as readdir8, readFile as readFile37, stat as stat8 } from "node:fs/promises";
307622
+ import { existsSync as existsSync77 } from "node:fs";
307623
+ import { readdir as readdir9, readFile as readFile35, stat as stat8 } from "node:fs/promises";
307387
307624
  import { basename as basename16, isAbsolute as isAbsolute13, join as join71, relative as relative14, resolve as resolve58 } from "node:path";
307388
307625
  var VIDEO_EXTENSIONS = /* @__PURE__ */ new Set([".mp4", ".mov", ".webm", ".m4v"]);
307389
307626
  var DEFAULT_AI_MODEL = "flash";
@@ -307900,7 +308137,7 @@ async function renderExpectsAudio(projectDir, beatId) {
307900
308137
  `assets/narration-${beat.id}.wav`,
307901
308138
  `assets/music-${beat.id}.mp3`,
307902
308139
  `assets/music-${beat.id}.wav`
307903
- ].some((asset) => existsSync76(join71(root2, asset)))
308140
+ ].some((asset) => existsSync77(join71(root2, asset)))
307904
308141
  );
307905
308142
  } catch {
307906
308143
  return false;
@@ -308008,8 +308245,8 @@ async function maybeWriteRenderReport(projectDir, opts, result) {
308008
308245
  }
308009
308246
  function renderSourceReports(projectDir, videoPath, checks) {
308010
308247
  const reports = [];
308011
- if (existsSync76(join71(projectDir, "build-report.json"))) reports.push("build-report.json");
308012
- if (existsSync76(join71(projectDir, "render-report.json"))) reports.push("render-report.json");
308248
+ if (existsSync77(join71(projectDir, "build-report.json"))) reports.push("build-report.json");
308249
+ if (existsSync77(join71(projectDir, "render-report.json"))) reports.push("render-report.json");
308013
308250
  if (videoPath) reports.push(displayPath(videoPath));
308014
308251
  if (checks.durationSec !== void 0 || checks.width !== void 0 || checks.height !== void 0) {
308015
308252
  reports.push("ffprobe");
@@ -308022,32 +308259,32 @@ function renderSourceReports(projectDir, videoPath, checks) {
308022
308259
  async function resolveRenderVideoPath(projectDir, explicit, beatId) {
308023
308260
  if (explicit) {
308024
308261
  const candidate = resolve58(process.cwd(), explicit);
308025
- return existsSync76(candidate) ? candidate : null;
308262
+ return existsSync77(candidate) ? candidate : null;
308026
308263
  }
308027
308264
  const renderReportPath = join71(projectDir, "render-report.json");
308028
- if (existsSync76(renderReportPath)) {
308265
+ if (existsSync77(renderReportPath)) {
308029
308266
  try {
308030
- const report = JSON.parse(await readFile37(renderReportPath, "utf-8"));
308267
+ const report = JSON.parse(await readFile35(renderReportPath, "utf-8"));
308031
308268
  const reportBeat = typeof report.beat === "string" ? report.beat : null;
308032
308269
  if (!beatId && reportBeat === null || beatId && reportBeat === beatId) {
308033
308270
  const candidate = resolveReportedPath(projectDir, report.outputPath);
308034
- if (candidate && existsSync76(candidate)) return candidate;
308271
+ if (candidate && existsSync77(candidate)) return candidate;
308035
308272
  }
308036
308273
  } catch {
308037
308274
  }
308038
308275
  }
308039
308276
  const reportPath = join71(projectDir, "build-report.json");
308040
- if (!beatId && existsSync76(reportPath)) {
308277
+ if (!beatId && existsSync77(reportPath)) {
308041
308278
  try {
308042
- const report = JSON.parse(await readFile37(reportPath, "utf-8"));
308279
+ const report = JSON.parse(await readFile35(reportPath, "utf-8"));
308043
308280
  const candidate = resolveReportedPath(projectDir, report.outputPath);
308044
- if (candidate && existsSync76(candidate)) return candidate;
308281
+ if (candidate && existsSync77(candidate)) return candidate;
308045
308282
  } catch {
308046
308283
  }
308047
308284
  }
308048
308285
  const rendersDir = join71(projectDir, "renders");
308049
- if (!existsSync76(rendersDir)) return null;
308050
- const entries2 = await readdir8(rendersDir, { withFileTypes: true });
308286
+ if (!existsSync77(rendersDir)) return null;
308287
+ const entries2 = await readdir9(rendersDir, { withFileTypes: true });
308051
308288
  const candidates = [];
308052
308289
  for (const entry of entries2) {
308053
308290
  if (!entry.isFile()) continue;
@@ -308073,13 +308310,13 @@ async function runAiRenderReview(projectDir, videoPath, model) {
308073
308310
  }
308074
308311
  function resolveStoryboardPath(projectDir) {
308075
308312
  const storyboardPath = join71(projectDir, "STORYBOARD.md");
308076
- return existsSync76(storyboardPath) ? storyboardPath : null;
308313
+ return existsSync77(storyboardPath) ? storyboardPath : null;
308077
308314
  }
308078
308315
  async function expectedDurationFromBuildReport(projectDir, beatId) {
308079
308316
  const reportPath = join71(projectDir, "build-report.json");
308080
- if (!existsSync76(reportPath)) return void 0;
308317
+ if (!existsSync77(reportPath)) return void 0;
308081
308318
  try {
308082
- const report = JSON.parse(await readFile37(reportPath, "utf-8"));
308319
+ const report = JSON.parse(await readFile35(reportPath, "utf-8"));
308083
308320
  const beats = beatId ? (report.beats ?? []).filter((beat) => beat.id === beatId) : report.beats ?? [];
308084
308321
  const durations = beats.map(
308085
308322
  (beat) => parseOptionalNumber2(beat.sceneDurationSec) ?? parseOptionalNumber2(beat.narration?.sceneDurationSec)
@@ -308092,9 +308329,9 @@ async function expectedDurationFromBuildReport(projectDir, beatId) {
308092
308329
  }
308093
308330
  async function beatTimingsFromBuildReport(projectDir, beatId) {
308094
308331
  const reportPath = join71(projectDir, "build-report.json");
308095
- if (!existsSync76(reportPath)) return [];
308332
+ if (!existsSync77(reportPath)) return [];
308096
308333
  try {
308097
- const report = JSON.parse(await readFile37(reportPath, "utf-8"));
308334
+ const report = JSON.parse(await readFile35(reportPath, "utf-8"));
308098
308335
  const source3 = report.beats ?? [];
308099
308336
  const selected = beatId ? source3.filter((beat) => beat.id === beatId) : source3;
308100
308337
  let cursor = 0;
@@ -308158,9 +308395,9 @@ function storyboardBeatCueSummary(storyboard) {
308158
308395
  }
308159
308396
  }
308160
308397
  async function readTextIfExists(path16) {
308161
- if (!existsSync76(path16)) return null;
308398
+ if (!existsSync77(path16)) return null;
308162
308399
  try {
308163
- return await readFile37(path16, "utf-8");
308400
+ return await readFile35(path16, "utf-8");
308164
308401
  } catch {
308165
308402
  return null;
308166
308403
  }
@@ -308782,16 +309019,16 @@ var generateTools = [
308782
309019
  ];
308783
309020
 
308784
309021
  // ../cli/src/tools/manifest/storyboard.ts
308785
- import { existsSync as existsSync79 } from "node:fs";
308786
- import { readFile as readFile40, writeFile as writeFile41 } from "node:fs/promises";
309022
+ import { existsSync as existsSync80 } from "node:fs";
309023
+ import { readFile as readFile38, writeFile as writeFile42 } from "node:fs/promises";
308787
309024
  import { resolve as resolve69 } from "node:path";
308788
309025
  import { z as z9 } from "zod";
308789
309026
  init_storyboard_parse();
308790
309027
  init_storyboard_edit();
308791
309028
 
308792
309029
  // ../cli/src/commands/_shared/storyboard-revise.ts
308793
- import { existsSync as existsSync78 } from "node:fs";
308794
- import { readFile as readFile39, writeFile as writeFile40 } from "node:fs/promises";
309030
+ import { existsSync as existsSync79 } from "node:fs";
309031
+ import { readFile as readFile37, writeFile as writeFile41 } from "node:fs/promises";
308795
309032
  import { join as join73, resolve as resolve68 } from "node:path";
308796
309033
 
308797
309034
  // ../cli/src/agent/adapters/index.ts
@@ -308841,12 +309078,13 @@ async function createAdapter(provider) {
308841
309078
  init_composer_resolve();
308842
309079
  init_project_config();
308843
309080
  init_storyboard_parse();
309081
+ init_storyboard_source();
308844
309082
  init_storyboard_edit();
308845
309083
  async function executeStoryboardRevision(opts) {
308846
309084
  const projectDir = resolve68(opts.projectDir);
308847
309085
  const storyboardPath = join73(projectDir, "STORYBOARD.md");
308848
309086
  const retryWith = [`vibe storyboard revise ${projectDir} --from "<request>" --dry-run --json`];
308849
- if (!existsSync78(storyboardPath)) {
309087
+ if (!existsSync79(storyboardPath)) {
308850
309088
  return failureResult({
308851
309089
  projectDir,
308852
309090
  storyboardPath,
@@ -308856,7 +309094,8 @@ async function executeStoryboardRevision(opts) {
308856
309094
  retryWith: [`vibe init ${projectDir} --from "<brief>" --json`]
308857
309095
  });
308858
309096
  }
308859
- const currentStoryboard = await readFile39(storyboardPath, "utf-8");
309097
+ const loaded = await loadStoryboard(projectDir);
309098
+ const currentStoryboard = loaded.markdown;
308860
309099
  const projectConfig = await readProjectConfig(projectDir);
308861
309100
  const explicitComposer = opts.composer ?? projectConfig.config.providers.composer ?? void 0;
308862
309101
  let provider = explicitComposer ?? "claude";
@@ -308955,7 +309194,11 @@ async function executeStoryboardRevision(opts) {
308955
309194
  }
308956
309195
  const storyboardMd = normalizeMarkdown(payload.storyboardMd);
308957
309196
  if (!opts.dryRun) {
308958
- await writeFile40(storyboardPath, storyboardMd, "utf-8");
309197
+ if (loaded.layout === "bundle") {
309198
+ await rewriteBundleFromMarkdown(projectDir, storyboardMd);
309199
+ } else {
309200
+ await writeFile41(storyboardPath, storyboardMd, "utf-8");
309201
+ }
308959
309202
  }
308960
309203
  const changedBeats = unique5([...diffChangedBeats(currentStoryboard, storyboardMd), ...payload.changedBeats]);
308961
309204
  return stripUndefined4({
@@ -308990,7 +309233,10 @@ function revisionMessages(opts) {
308990
309233
  "Return exactly one JSON object, with no markdown fence and no prose outside JSON.",
308991
309234
  "The JSON object must have: storyboardMd string, summary string, changedBeats string[], warnings string[].",
308992
309235
  "Preserve existing frontmatter, beat ids, cue YAML keys, and useful prose unless the user asks to change structure.",
308993
- "Keep cue YAML valid. Allowed cue keys are duration, narration, backdrop, video, motion, voice, music, asset.",
309236
+ // Rendered from the SSOT: this list used to be a hand-typed subset of 8,
309237
+ // so a revise pass could silently drop `characters`, `keyframe`, and the
309238
+ // lower-third cues it had never been told about.
309239
+ `Keep cue YAML valid. Allowed cue keys are ${STORYBOARD_CUE_KEYS.join(", ")}.`,
308994
309240
  "If a target duration is supplied, every beat must have a positive duration cue and durations must sum to that target.",
308995
309241
  "Aim for 6-15 seconds per beat and never exceed ~15s \u2014 long beats render as static, overstuffed scenes. A 90-second video should have 6-8 beats."
308996
309242
  ].join("\n");
@@ -309084,13 +309330,13 @@ async function readRevisionContext(projectDir) {
309084
309330
  return { designMd, buildReportJson };
309085
309331
  }
309086
309332
  async function readOptionalText(path16) {
309087
- if (!existsSync78(path16)) return null;
309088
- return readFile39(path16, "utf-8");
309333
+ if (!existsSync79(path16)) return null;
309334
+ return readFile37(path16, "utf-8");
309089
309335
  }
309090
309336
  async function readOptionalJsonText(path16) {
309091
- if (!existsSync78(path16)) return null;
309337
+ if (!existsSync79(path16)) return null;
309092
309338
  try {
309093
- const parsed = JSON.parse(await readFile39(path16, "utf-8"));
309339
+ const parsed = JSON.parse(await readFile37(path16, "utf-8"));
309094
309340
  return JSON.stringify(parsed, null, 2);
309095
309341
  } catch {
309096
309342
  return null;
@@ -309135,8 +309381,8 @@ var projectDirSchema = z9.object({
309135
309381
  });
309136
309382
  async function readStoryboard(projectDir) {
309137
309383
  const path16 = resolve69(projectDir, "STORYBOARD.md");
309138
- if (!existsSync79(path16)) return null;
309139
- return { path: path16, markdown: await readFile40(path16, "utf-8") };
309384
+ if (!existsSync80(path16)) return null;
309385
+ return { path: path16, markdown: await readFile38(path16, "utf-8") };
309140
309386
  }
309141
309387
  var storyboardListTool = defineTool({
309142
309388
  name: "storyboard_list",
@@ -309249,7 +309495,7 @@ var storyboardSetTool = defineTool({
309249
309495
  value,
309250
309496
  unset: args.unset
309251
309497
  });
309252
- await writeFile41(file.path, next, "utf-8");
309498
+ await writeFile42(file.path, next, "utf-8");
309253
309499
  return {
309254
309500
  success: true,
309255
309501
  data: { beat: args.beat, key: args.key },
@@ -309281,7 +309527,7 @@ var storyboardMoveTool = defineTool({
309281
309527
  beatId: args.beat,
309282
309528
  afterBeatId: args.after
309283
309529
  });
309284
- await writeFile41(file.path, next, "utf-8");
309530
+ await writeFile42(file.path, next, "utf-8");
309285
309531
  return {
309286
309532
  success: true,
309287
309533
  data: { beat: args.beat, after: args.after },
@@ -309393,8 +309639,8 @@ var storyboardTools = [
309393
309639
  ];
309394
309640
 
309395
309641
  // ../cli/src/tools/manifest/design.ts
309396
- import { existsSync as existsSync80 } from "node:fs";
309397
- import { readFile as readFile41 } from "node:fs/promises";
309642
+ import { existsSync as existsSync81 } from "node:fs";
309643
+ import { readFile as readFile39 } from "node:fs/promises";
309398
309644
  import { resolve as resolve70, join as join74 } from "node:path";
309399
309645
  import { z as z10 } from "zod";
309400
309646
  init_design_parse();
@@ -309413,8 +309659,8 @@ var designValidateTool = defineTool({
309413
309659
  async execute(args, ctx) {
309414
309660
  const projectDir = args.projectDir ? resolve70(ctx.workingDirectory, args.projectDir) : ctx.workingDirectory;
309415
309661
  const path16 = join74(projectDir, "DESIGN.md");
309416
- if (!existsSync80(path16)) return { success: false, error: `DESIGN.md not found in ${projectDir}` };
309417
- const md = await readFile41(path16, "utf-8");
309662
+ if (!existsSync81(path16)) return { success: false, error: `DESIGN.md not found in ${projectDir}` };
309663
+ const md = await readFile39(path16, "utf-8");
309418
309664
  const issues = validateDesignMarkdown(md);
309419
309665
  const design = parseDesign(md);
309420
309666
  const ok = !issues.some((issue) => issue.severity === "error");
@@ -309435,15 +309681,15 @@ var designValidateTool = defineTool({
309435
309681
  var designTools = [designValidateTool];
309436
309682
 
309437
309683
  // ../cli/src/tools/manifest/remix.ts
309438
- import { writeFile as writeFile45 } from "node:fs/promises";
309684
+ import { writeFile as writeFile46 } from "node:fs/promises";
309439
309685
  import { tmpdir as tmpdir12 } from "node:os";
309440
309686
  import { join as join75 } from "node:path";
309441
309687
  import { z as z11 } from "zod";
309442
309688
 
309443
309689
  // ../cli/src/commands/ai-highlights.ts
309444
- import { readFile as readFile42, writeFile as writeFile42, mkdir as mkdir26 } from "node:fs/promises";
309690
+ import { readFile as readFile40, writeFile as writeFile43, mkdir as mkdir27 } from "node:fs/promises";
309445
309691
  import { resolve as resolve71, dirname as dirname39, basename as basename19, extname as extname18 } from "node:path";
309446
- import { existsSync as existsSync81 } from "node:fs";
309692
+ import { existsSync as existsSync82 } from "node:fs";
309447
309693
  init_dist();
309448
309694
  init_engine();
309449
309695
  init_api_key();
@@ -309474,7 +309720,7 @@ function filterHighlights(highlights, options) {
309474
309720
  async function executeHighlights(options) {
309475
309721
  try {
309476
309722
  const absPath = resolve71(process.cwd(), options.media);
309477
- if (!existsSync81(absPath)) {
309723
+ if (!existsSync82(absPath)) {
309478
309724
  return { success: false, highlights: [], totalDuration: 0, totalHighlightDuration: 0, error: `File not found: ${absPath}` };
309479
309725
  }
309480
309726
  const ext = extname18(absPath).toLowerCase();
@@ -309493,7 +309739,7 @@ async function executeHighlights(options) {
309493
309739
  sourceDuration = await ffprobeDuration(absPath);
309494
309740
  const gemini = new GeminiProvider();
309495
309741
  await gemini.initialize({ apiKey: geminiApiKey });
309496
- const videoBuffer = await readFile42(absPath);
309742
+ const videoBuffer = await readFile40(absPath);
309497
309743
  const criteriaText = options.criteria === "all" || !options.criteria ? "emotional, informative, and funny moments" : `${options.criteria} moments`;
309498
309744
  const durationText = targetDuration ? `Target a total highlight duration of ${targetDuration} seconds.` : "";
309499
309745
  const countText = maxCount ? `Find up to ${maxCount} highlights.` : "";
@@ -309587,10 +309833,10 @@ Analyze both what is SHOWN (visual cues, actions, expressions) and what is SAID
309587
309833
  }
309588
309834
  const whisper = new WhisperProvider();
309589
309835
  await whisper.initialize({ apiKey: openaiApiKey });
309590
- const audioBuffer = await readFile42(audioPath);
309836
+ const audioBuffer = await readFile40(audioPath);
309591
309837
  const audioBlob = new Blob([audioBuffer]);
309592
309838
  const transcriptResult = await whisper.transcribe(audioBlob, options.language);
309593
- if (tempAudioPath && existsSync81(tempAudioPath)) {
309839
+ if (tempAudioPath && existsSync82(tempAudioPath)) {
309594
309840
  const { unlink: unlinkFile } = await import("node:fs/promises");
309595
309841
  await unlinkFile(tempAudioPath).catch(() => {
309596
309842
  });
@@ -309622,7 +309868,7 @@ Analyze both what is SHOWN (visual cues, actions, expressions) and what is SAID
309622
309868
  };
309623
309869
  if (options.output) {
309624
309870
  const outputPath = resolve71(process.cwd(), options.output);
309625
- await writeFile42(outputPath, JSON.stringify({
309871
+ await writeFile43(outputPath, JSON.stringify({
309626
309872
  sourceFile: absPath,
309627
309873
  totalDuration: sourceDuration,
309628
309874
  criteria: options.criteria || "all",
@@ -309657,7 +309903,7 @@ Analyze both what is SHOWN (visual cues, actions, expressions) and what is SAID
309657
309903
  }
309658
309904
  }
309659
309905
  const projectPath = resolve71(process.cwd(), options.project);
309660
- await writeFile42(projectPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
309906
+ await writeFile43(projectPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
309661
309907
  extractResult.projectPath = projectPath;
309662
309908
  }
309663
309909
  return extractResult;
@@ -309677,7 +309923,7 @@ async function executeAutoShorts(options) {
309677
309923
  return { success: false, shorts: [], error: "FFmpeg not found" };
309678
309924
  }
309679
309925
  const absPath = resolve71(process.cwd(), options.video);
309680
- if (!existsSync81(absPath)) {
309926
+ if (!existsSync82(absPath)) {
309681
309927
  return { success: false, shorts: [], error: `File not found: ${absPath}` };
309682
309928
  }
309683
309929
  const targetDuration = options.duration ?? 60;
@@ -309690,7 +309936,7 @@ async function executeAutoShorts(options) {
309690
309936
  }
309691
309937
  const gemini = new GeminiProvider();
309692
309938
  await gemini.initialize({ apiKey: geminiApiKey });
309693
- const videoBuffer = await readFile42(absPath);
309939
+ const videoBuffer = await readFile40(absPath);
309694
309940
  const geminiPrompt = `Analyze this video to find the BEST moments for short-form vertical video content (TikTok, YouTube Shorts, Instagram Reels).
309695
309941
 
309696
309942
  Find ${shortCount * 3} potential clips that are ${targetDuration} seconds or shorter each.
@@ -309767,7 +310013,7 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
309767
310013
  await execSafe("ffmpeg", ["-i", absPath, "-vn", "-acodec", "libmp3lame", "-q:a", "2", tempAudio, "-y"]);
309768
310014
  const whisper = new WhisperProvider();
309769
310015
  await whisper.initialize({ apiKey: openaiApiKey });
309770
- const audioBuffer = await readFile42(tempAudio);
310016
+ const audioBuffer = await readFile40(tempAudio);
309771
310017
  const audioBlob = new Blob([audioBuffer]);
309772
310018
  const transcript = await whisper.transcribe(audioBlob, options.language);
309773
310019
  try {
@@ -309805,8 +310051,8 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
309805
310051
  };
309806
310052
  }
309807
310053
  const outputDir = options.outputDir ? resolve71(process.cwd(), options.outputDir) : dirname39(absPath);
309808
- if (options.outputDir && !existsSync81(outputDir)) {
309809
- await mkdir26(outputDir, { recursive: true });
310054
+ if (options.outputDir && !existsSync82(outputDir)) {
310055
+ await mkdir27(outputDir, { recursive: true });
309810
310056
  }
309811
310057
  const result = {
309812
310058
  success: true,
@@ -309882,10 +310128,10 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
309882
310128
  }
309883
310129
 
309884
310130
  // ../cli/src/pipeline/executor.ts
309885
- var import_yaml7 = __toESM(require_dist3(), 1);
310131
+ var import_yaml8 = __toESM(require_dist3(), 1);
309886
310132
  import { resolve as resolve74 } from "node:path";
309887
- import { readFile as readFile44, writeFile as writeFile44, mkdir as mkdir27 } from "node:fs/promises";
309888
- import { existsSync as existsSync82 } from "node:fs";
310133
+ import { readFile as readFile42, writeFile as writeFile45, mkdir as mkdir28 } from "node:fs/promises";
310134
+ import { existsSync as existsSync83 } from "node:fs";
309889
310135
 
309890
310136
  // ../cli/src/pipeline/resolver.ts
309891
310137
  function resolveStepParams(params, completedSteps) {
@@ -310275,11 +310521,11 @@ async function ensureActionsRegistered() {
310275
310521
  }
310276
310522
  async function loadPipeline(filePath) {
310277
310523
  const absPath = resolve74(process.cwd(), filePath);
310278
- if (!existsSync82(absPath)) {
310524
+ if (!existsSync83(absPath)) {
310279
310525
  throw new Error(`Pipeline file not found: ${absPath}`);
310280
310526
  }
310281
- const content = await readFile44(absPath, "utf-8");
310282
- const manifest2 = (0, import_yaml7.parse)(content);
310527
+ const content = await readFile42(absPath, "utf-8");
310528
+ const manifest2 = (0, import_yaml8.parse)(content);
310283
310529
  if (!manifest2.name) throw new Error("Pipeline missing 'name' field");
310284
310530
  if (!manifest2.steps || !Array.isArray(manifest2.steps)) throw new Error("Pipeline missing 'steps' array");
310285
310531
  const ids = /* @__PURE__ */ new Set();
@@ -310295,15 +310541,15 @@ var CHECKPOINT_FILE = ".pipeline-state.yaml";
310295
310541
  async function executePipeline(manifest2, options = {}) {
310296
310542
  await ensureActionsRegistered();
310297
310543
  const outputDir = resolve74(process.cwd(), options.outputDir || `${manifest2.name}-output`);
310298
- await mkdir27(outputDir, { recursive: true });
310544
+ await mkdir28(outputDir, { recursive: true });
310299
310545
  const completedSteps = /* @__PURE__ */ new Map();
310300
310546
  const results = [];
310301
310547
  const startTime = Date.now();
310302
310548
  if (options.resume) {
310303
310549
  const checkpointPath = resolve74(outputDir, CHECKPOINT_FILE);
310304
- if (existsSync82(checkpointPath)) {
310305
- const checkpointContent = await readFile44(checkpointPath, "utf-8");
310306
- const checkpoint = (0, import_yaml7.parse)(checkpointContent);
310550
+ if (existsSync83(checkpointPath)) {
310551
+ const checkpointContent = await readFile42(checkpointPath, "utf-8");
310552
+ const checkpoint = (0, import_yaml8.parse)(checkpointContent);
310307
310553
  for (const cs of checkpoint.completedSteps) {
310308
310554
  completedSteps.set(cs.id, {
310309
310555
  id: cs.id,
@@ -310420,9 +310666,9 @@ async function executePipeline(manifest2, options = {}) {
310420
310666
  data: s2.data
310421
310667
  }))
310422
310668
  };
310423
- await writeFile44(
310669
+ await writeFile45(
310424
310670
  resolve74(outputDir, CHECKPOINT_FILE),
310425
- (0, import_yaml7.stringify)(checkpoint, { indent: 2 }),
310671
+ (0, import_yaml8.stringify)(checkpoint, { indent: 2 }),
310426
310672
  "utf-8"
310427
310673
  );
310428
310674
  } else {
@@ -310575,7 +310821,7 @@ var pipelineRunTool = defineTool({
310575
310821
  let tempPath;
310576
310822
  if (args.pipelineYaml) {
310577
310823
  tempPath = join75(tmpdir12(), `vibe-mcp-pipeline-${Date.now()}.yaml`);
310578
- await writeFile45(tempPath, args.pipelineYaml, "utf-8");
310824
+ await writeFile46(tempPath, args.pipelineYaml, "utf-8");
310579
310825
  resolvedPath = tempPath;
310580
310826
  }
310581
310827
  try {
@@ -310712,7 +310958,7 @@ var detectTools = [
310712
310958
 
310713
310959
  // ../cli/src/tools/manifest/timeline.ts
310714
310960
  import { z as z13 } from "zod";
310715
- import { mkdir as mkdir28, writeFile as writeFile47 } from "node:fs/promises";
310961
+ import { mkdir as mkdir29, writeFile as writeFile48 } from "node:fs/promises";
310716
310962
  import { basename as basename21, resolve as resolve75 } from "node:path";
310717
310963
  init_engine();
310718
310964
  init_project_resolver();
@@ -310720,15 +310966,15 @@ init_project_resolver();
310720
310966
  // ../cli/src/tools/manifest/_project-io.ts
310721
310967
  init_engine();
310722
310968
  init_project_resolver();
310723
- import { readFile as readFile45, writeFile as writeFile46 } from "node:fs/promises";
310969
+ import { readFile as readFile43, writeFile as writeFile47 } from "node:fs/promises";
310724
310970
  async function loadProject(projectPath, cwd) {
310725
310971
  const absPath = await resolveTimelineFile(projectPath, cwd);
310726
- const content = await readFile45(absPath, "utf-8");
310972
+ const content = await readFile43(absPath, "utf-8");
310727
310973
  const data2 = JSON.parse(content);
310728
310974
  return { project: Project.fromJSON(data2), absPath };
310729
310975
  }
310730
310976
  async function saveProject(absPath, project) {
310731
- await writeFile46(absPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
310977
+ await writeFile47(absPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
310732
310978
  }
310733
310979
 
310734
310980
  // ../cli/src/tools/manifest/timeline.ts
@@ -310768,10 +311014,10 @@ var timelineCreateTool = defineTool({
310768
311014
  outputPath = resolve75(ctx.workingDirectory, args.outputPath);
310769
311015
  } else {
310770
311016
  const dirPath = resolve75(ctx.workingDirectory, args.name);
310771
- await mkdir28(dirPath, { recursive: true });
311017
+ await mkdir29(dirPath, { recursive: true });
310772
311018
  outputPath = resolve75(dirPath, TIMELINE_FILENAME);
310773
311019
  }
310774
- await writeFile47(outputPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
311020
+ await writeFile48(outputPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
310775
311021
  return {
310776
311022
  success: true,
310777
311023
  data: { name: projectName, outputPath },
@@ -311069,17 +311315,18 @@ var timelineTools = [
311069
311315
  // ../cli/src/tools/manifest/project.ts
311070
311316
  import { z as z14 } from "zod";
311071
311317
  import { resolve as resolve77 } from "node:path";
311072
- import { writeFile as writeFile48 } from "node:fs/promises";
311318
+ import { writeFile as writeFile49 } from "node:fs/promises";
311073
311319
  init_engine();
311074
311320
 
311075
311321
  // ../cli/src/commands/_shared/project-list.ts
311076
311322
  init_storyboard_parse();
311077
- import { readdir as readdir9, readFile as readFile46, stat as stat9 } from "node:fs/promises";
311078
- import { existsSync as existsSync83 } from "node:fs";
311323
+ init_storyboard_source();
311324
+ import { readdir as readdir10, readFile as readFile44, stat as stat9 } from "node:fs/promises";
311325
+ import { existsSync as existsSync84 } from "node:fs";
311079
311326
  import { join as join76, resolve as resolve76 } from "node:path";
311080
311327
  async function executeProjectList(opts = {}) {
311081
311328
  const workspaceDir = resolve76(opts.workspaceDir ?? ".");
311082
- if (!existsSync83(workspaceDir)) {
311329
+ if (!existsSync84(workspaceDir)) {
311083
311330
  return {
311084
311331
  success: false,
311085
311332
  workspaceDir,
@@ -311088,7 +311335,7 @@ async function executeProjectList(opts = {}) {
311088
311335
  error: `Workspace directory not found: ${workspaceDir}`
311089
311336
  };
311090
311337
  }
311091
- const entries2 = await readdir9(workspaceDir, { withFileTypes: true });
311338
+ const entries2 = await readdir10(workspaceDir, { withFileTypes: true });
311092
311339
  const projects = [];
311093
311340
  let archivedCount = 0;
311094
311341
  for (const entry of entries2) {
@@ -311100,7 +311347,7 @@ async function executeProjectList(opts = {}) {
311100
311347
  continue;
311101
311348
  }
311102
311349
  const projectDir = join76(workspaceDir, entry.name);
311103
- if (!existsSync83(join76(projectDir, "STORYBOARD.md"))) continue;
311350
+ if (!existsSync84(join76(projectDir, "STORYBOARD.md"))) continue;
311104
311351
  projects.push(await describeProject(entry.name, projectDir));
311105
311352
  }
311106
311353
  projects.sort((a4, b4) => b4.updatedAt.localeCompare(a4.updatedAt));
@@ -311108,9 +311355,9 @@ async function executeProjectList(opts = {}) {
311108
311355
  }
311109
311356
  async function countProjects(dir) {
311110
311357
  try {
311111
- const entries2 = await readdir9(dir, { withFileTypes: true });
311358
+ const entries2 = await readdir10(dir, { withFileTypes: true });
311112
311359
  return entries2.filter(
311113
- (e2) => e2.isDirectory() && existsSync83(join76(dir, e2.name, "STORYBOARD.md"))
311360
+ (e2) => e2.isDirectory() && existsSync84(join76(dir, e2.name, "STORYBOARD.md"))
311114
311361
  ).length;
311115
311362
  } catch {
311116
311363
  return 0;
@@ -311122,7 +311369,7 @@ async function describeProject(name, projectDir) {
311122
311369
  const timestamps = [];
311123
311370
  try {
311124
311371
  const storyboardPath = join76(projectDir, "STORYBOARD.md");
311125
- const parsed = parseStoryboard(await readFile46(storyboardPath, "utf-8"));
311372
+ const parsed = parseStoryboard(await loadStoryboardMarkdown(projectDir));
311126
311373
  beats = parsed.beats.length;
311127
311374
  storyboardDurationSec = Number(
311128
311375
  parsed.beats.reduce((sum, beat) => sum + (beat.duration ?? 0), 0).toFixed(2)
@@ -311134,7 +311381,7 @@ async function describeProject(name, projectDir) {
311134
311381
  let buildUpdatedAt;
311135
311382
  try {
311136
311383
  const reportPath = join76(projectDir, "build-report.json");
311137
- const report = JSON.parse(await readFile46(reportPath, "utf-8"));
311384
+ const report = JSON.parse(await readFile44(reportPath, "utf-8"));
311138
311385
  buildStatus = report.status ?? report.phase;
311139
311386
  buildUpdatedAt = report.updatedAt;
311140
311387
  timestamps.push((await stat9(reportPath)).mtimeMs);
@@ -311143,7 +311390,7 @@ async function describeProject(name, projectDir) {
311143
311390
  let latestRender;
311144
311391
  try {
311145
311392
  const rendersDir = join76(projectDir, "renders");
311146
- const files = await readdir9(rendersDir);
311393
+ const files = await readdir10(rendersDir);
311147
311394
  let newest;
311148
311395
  for (const file of files) {
311149
311396
  if (!/\.(mp4|webm|mov)$/i.test(file)) continue;
@@ -311226,7 +311473,7 @@ var projectCreateTool = defineTool({
311226
311473
  const outputPath = resolve77(ctx.workingDirectory, args.outputPath ?? `${args.name}.vibe.json`);
311227
311474
  const project = new Project(args.name);
311228
311475
  if (args.fps) project.setFrameRate(args.fps);
311229
- await writeFile48(outputPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
311476
+ await writeFile49(outputPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
311230
311477
  return {
311231
311478
  success: true,
311232
311479
  data: { name: args.name, outputPath },
@@ -311277,7 +311524,7 @@ import { resolve as resolve79 } from "node:path";
311277
311524
  init_engine();
311278
311525
  init_exec_safe();
311279
311526
  init_project_resolver();
311280
- import { readFile as readFile47, access as access6 } from "node:fs/promises";
311527
+ import { readFile as readFile45, access as access6 } from "node:fs/promises";
311281
311528
  import { resolve as resolve78 } from "node:path";
311282
311529
  import { spawn as spawn9 } from "node:child_process";
311283
311530
  async function getMediaDuration(filePath, mediaType, defaultImageDuration = 5) {
@@ -311348,7 +311595,7 @@ async function runExport(projectPath, outputPath, options = {}) {
311348
311595
  };
311349
311596
  }
311350
311597
  const filePath = await resolveTimelineFile(projectPath);
311351
- const content = await readFile47(filePath, "utf-8");
311598
+ const content = await readFile45(filePath, "utf-8");
311352
311599
  const data2 = JSON.parse(content);
311353
311600
  const project = Project.fromJSON(data2);
311354
311601
  const summary = project.getSummary();
@@ -311947,7 +312194,7 @@ var exportVideoTool = defineTool({
311947
312194
  var exportTools = [exportVideoTool];
311948
312195
 
311949
312196
  // ../cli/src/tools/manifest/agent-only.ts
311950
- import { readFile as readFile48, writeFile as writeFile49, readdir as readdir10, stat as stat10, access as access7, unlink as unlink5 } from "node:fs/promises";
312197
+ import { readFile as readFile46, writeFile as writeFile50, readdir as readdir11, stat as stat10, access as access7, unlink as unlink5 } from "node:fs/promises";
311951
312198
  import { resolve as resolve80, join as join77, basename as basename23, extname as extname19 } from "node:path";
311952
312199
  import { z as z16 } from "zod";
311953
312200
  init_engine();
@@ -312023,7 +312270,7 @@ var fsListTool = defineTool({
312023
312270
  const dirPath = args.path ?? ".";
312024
312271
  try {
312025
312272
  const absPath = resolve80(ctx.workingDirectory, dirPath);
312026
- const entries2 = await readdir10(absPath, { withFileTypes: true });
312273
+ const entries2 = await readdir11(absPath, { withFileTypes: true });
312027
312274
  const results = [];
312028
312275
  for (const entry of entries2) {
312029
312276
  if (args.pattern && !matchPattern(entry.name, args.pattern)) continue;
@@ -312056,7 +312303,7 @@ var fsReadTool = defineTool({
312056
312303
  async execute(args, ctx) {
312057
312304
  try {
312058
312305
  const absPath = resolve80(ctx.workingDirectory, args.path);
312059
- const content = await readFile48(absPath, "utf-8");
312306
+ const content = await readFile46(absPath, "utf-8");
312060
312307
  const maxLength = 4e3;
312061
312308
  const truncated = content.length > maxLength;
312062
312309
  const output3 = truncated ? content.substring(0, maxLength) + "\n... (truncated)" : content;
@@ -312081,7 +312328,7 @@ var fsWriteTool = defineTool({
312081
312328
  async execute(args, ctx) {
312082
312329
  try {
312083
312330
  const absPath = resolve80(ctx.workingDirectory, args.path);
312084
- await writeFile49(absPath, args.content, "utf-8");
312331
+ await writeFile50(absPath, args.content, "utf-8");
312085
312332
  return { success: true, data: { bytes: args.content.length }, humanLines: [`File written: ${args.path} (${formatSize(args.content.length)})`] };
312086
312333
  } catch (error) {
312087
312334
  return { success: false, error: `Failed to write file: ${error instanceof Error ? error.message : String(error)}` };
@@ -312129,7 +312376,7 @@ var batchImportTool = defineTool({
312129
312376
  async execute(args, ctx) {
312130
312377
  try {
312131
312378
  const filePath = resolve80(ctx.workingDirectory, args.project);
312132
- const content = await readFile48(filePath, "utf-8");
312379
+ const content = await readFile46(filePath, "utf-8");
312133
312380
  const data2 = JSON.parse(content);
312134
312381
  const project = Project.fromJSON(data2);
312135
312382
  const dirPath = resolve80(ctx.workingDirectory, args.directory);
@@ -312138,7 +312385,7 @@ var batchImportTool = defineTool({
312138
312385
  const recursive = args.recursive ?? false;
312139
312386
  const mediaFiles = [];
312140
312387
  const scanDir = async (dir) => {
312141
- const entries2 = await readdir10(dir, { withFileTypes: true });
312388
+ const entries2 = await readdir11(dir, { withFileTypes: true });
312142
312389
  for (const entry of entries2) {
312143
312390
  const entryPath = join77(dir, entry.name);
312144
312391
  if (entry.isDirectory() && recursive) {
@@ -312167,7 +312414,7 @@ var batchImportTool = defineTool({
312167
312414
  const source3 = project.addSource({ name: mediaName, type: mediaType, url: mediaFile, duration });
312168
312415
  addedSources.push({ id: source3.id, name: mediaName, type: mediaType });
312169
312416
  }
312170
- await writeFile49(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
312417
+ await writeFile50(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
312171
312418
  return {
312172
312419
  success: true,
312173
312420
  data: { count: addedSources.length, sources: addedSources },
@@ -312201,7 +312448,7 @@ var batchConcatTool = defineTool({
312201
312448
  async execute(args, ctx) {
312202
312449
  try {
312203
312450
  const filePath = resolve80(ctx.workingDirectory, args.project);
312204
- const content = await readFile48(filePath, "utf-8");
312451
+ const content = await readFile46(filePath, "utf-8");
312205
312452
  const data2 = JSON.parse(content);
312206
312453
  const project = Project.fromJSON(data2);
312207
312454
  const sourceIds = args.sourceIds ?? [];
@@ -312237,7 +312484,7 @@ var batchConcatTool = defineTool({
312237
312484
  createdClips.push({ id: clip.id, sourceName: source3.name, startTime: currentTime, duration: source3.duration });
312238
312485
  currentTime += source3.duration + gap;
312239
312486
  }
312240
- await writeFile49(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
312487
+ await writeFile50(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
312241
312488
  const totalDuration = currentTime - gap - startTime;
312242
312489
  return {
312243
312490
  success: true,
@@ -312272,7 +312519,7 @@ var batchApplyEffectTool = defineTool({
312272
312519
  async execute(args, ctx) {
312273
312520
  try {
312274
312521
  const filePath = resolve80(ctx.workingDirectory, args.project);
312275
- const content = await readFile48(filePath, "utf-8");
312522
+ const content = await readFile46(filePath, "utf-8");
312276
312523
  const data2 = JSON.parse(content);
312277
312524
  const project = Project.fromJSON(data2);
312278
312525
  const clipIds = args.clipIds ?? [];
@@ -312300,7 +312547,7 @@ var batchApplyEffectTool = defineTool({
312300
312547
  });
312301
312548
  if (effect) appliedEffects.push({ clipId: clip.id, effectId: effect.id });
312302
312549
  }
312303
- await writeFile49(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
312550
+ await writeFile50(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
312304
312551
  return {
312305
312552
  success: true,
312306
312553
  data: { applied: appliedEffects.length, effectType: args.effectType },
@@ -312327,7 +312574,7 @@ var timelineClearTool = defineTool({
312327
312574
  async execute(args, ctx) {
312328
312575
  try {
312329
312576
  const filePath = await resolveTimelineFile(args.project, ctx.workingDirectory);
312330
- const content = await readFile48(filePath, "utf-8");
312577
+ const content = await readFile46(filePath, "utf-8");
312331
312578
  const data2 = JSON.parse(content);
312332
312579
  const project = Project.fromJSON(data2);
312333
312580
  const what = args.what ?? "clips";
@@ -312369,7 +312616,7 @@ var timelineClearTool = defineTool({
312369
312616
  removed.sources++;
312370
312617
  }
312371
312618
  }
312372
- await writeFile49(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
312619
+ await writeFile50(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
312373
312620
  const parts = [];
312374
312621
  if (removed.clips > 0) parts.push(`${removed.clips} clips`);
312375
312622
  if (removed.tracks > 0) parts.push(`${removed.tracks} tracks`);
@@ -312404,7 +312651,7 @@ var projectSetTool = defineTool({
312404
312651
  async execute(args, ctx) {
312405
312652
  try {
312406
312653
  const filePath = await resolveTimelineFile(args.projectPath, ctx.workingDirectory);
312407
- const content = await readFile48(filePath, "utf-8");
312654
+ const content = await readFile46(filePath, "utf-8");
312408
312655
  const data2 = JSON.parse(content);
312409
312656
  const project = Project.fromJSON(data2);
312410
312657
  const updates = [];
@@ -312420,7 +312667,7 @@ var projectSetTool = defineTool({
312420
312667
  project.setFrameRate(args.fps);
312421
312668
  updates.push(`Frame Rate: ${args.fps} fps`);
312422
312669
  }
312423
- await writeFile49(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
312670
+ await writeFile50(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
312424
312671
  return {
312425
312672
  success: true,
312426
312673
  data: { updates },
@@ -312678,8 +312925,8 @@ var mediaCompressTool = defineTool({
312678
312925
  ],
312679
312926
  { maxBuffer: 50 * 1024 * 1024 }
312680
312927
  );
312681
- const inputBuf = await readFile48(absInput);
312682
- const outputBuf = await readFile48(outputPath);
312928
+ const inputBuf = await readFile46(absInput);
312929
+ const outputBuf = await readFile46(outputPath);
312683
312930
  const inputSize = inputBuf.length;
312684
312931
  const outputSize = outputBuf.length;
312685
312932
  const reduction = (inputSize - outputSize) / inputSize * 100;
@@ -312790,7 +313037,7 @@ var mediaConcatTool = defineTool({
312790
313037
  } else {
312791
313038
  const tempList = resolve80(ctx.workingDirectory, `concat-list-${Date.now()}.txt`);
312792
313039
  const listContent = args.inputs.map((i3) => `file '${resolve80(ctx.workingDirectory, i3)}'`).join("\n");
312793
- await writeFile49(tempList, listContent, "utf-8");
313040
+ await writeFile50(tempList, listContent, "utf-8");
312794
313041
  try {
312795
313042
  await execSafe(
312796
313043
  "ffmpeg",
@@ -313703,7 +313950,7 @@ async function handleToolCall(name, args, extra) {
313703
313950
 
313704
313951
  // src/resources/index.ts
313705
313952
  init_engine();
313706
- import { readFile as readFile49 } from "node:fs/promises";
313953
+ import { readFile as readFile47 } from "node:fs/promises";
313707
313954
  import { resolve as resolve83 } from "node:path";
313708
313955
  var resources = [
313709
313956
  {
@@ -313740,7 +313987,7 @@ var resources = [
313740
313987
  var currentProjectPath = process.env.VIBE_PROJECT_PATH || null;
313741
313988
  async function loadProject2(projectPath) {
313742
313989
  const absPath = resolve83(process.cwd(), projectPath);
313743
- const content = await readFile49(absPath, "utf-8");
313990
+ const content = await readFile47(absPath, "utf-8");
313744
313991
  const data2 = JSON.parse(content);
313745
313992
  return Project.fromJSON(data2);
313746
313993
  }
@@ -314132,14 +314379,14 @@ This creates a music video-style edit where cuts happen in rhythm with the music
314132
314379
  }
314133
314380
 
314134
314381
  // src/instructions.ts
314135
- import { existsSync as existsSync84, mkdirSync as mkdirSync23 } from "node:fs";
314382
+ import { existsSync as existsSync85, mkdirSync as mkdirSync23 } from "node:fs";
314136
314383
  import { delimiter as delimiter2, isAbsolute as isAbsolute14, resolve as resolve84 } from "node:path";
314137
314384
  var DEFAULT_EXTRA_PATH_DIRS = process.platform === "darwin" ? ["/opt/homebrew/bin", "/usr/local/bin", "/opt/local/bin"] : ["/usr/local/bin"];
314138
314385
  function ensureSystemPath(env4 = process.env, extraDirs = DEFAULT_EXTRA_PATH_DIRS) {
314139
314386
  const current = (env4.PATH ?? "").split(delimiter2).filter(Boolean);
314140
314387
  const added = [];
314141
314388
  for (const dir of extraDirs) {
314142
- if (!current.includes(dir) && existsSync84(dir)) {
314389
+ if (!current.includes(dir) && existsSync85(dir)) {
314143
314390
  current.push(dir);
314144
314391
  added.push(dir);
314145
314392
  }
@@ -314242,7 +314489,7 @@ function handleCliFlags(argv, version2) {
314242
314489
  }
314243
314490
 
314244
314491
  // src/index.ts
314245
- if (handleCliFlags(process.argv.slice(2), "0.114.0")) {
314492
+ if (handleCliFlags(process.argv.slice(2), "0.115.0")) {
314246
314493
  process.exit(0);
314247
314494
  }
314248
314495
  console.log = (...args) => console.error(...args);
@@ -314255,7 +314502,7 @@ var server = new Server(
314255
314502
  {
314256
314503
  name: "vibeframe",
314257
314504
  // Stamped with the package version by build.js at bundle time.
314258
- version: "0.114.0"
314505
+ version: "0.115.0"
314259
314506
  },
314260
314507
  {
314261
314508
  capabilities: {