@koda-sl/baker-cli 0.90.1 → 0.92.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -9,10 +9,10 @@ import {
9
9
  defaultRegistry,
10
10
  generateCatalog,
11
11
  validateCanvasDeep
12
- } from "./chunk-2E4H2GIJ.js";
12
+ } from "./chunk-LMVDA3EZ.js";
13
13
 
14
14
  // src/cli.ts
15
- import { defineCommand as defineCommand141, runMain } from "citty";
15
+ import { defineCommand as defineCommand148, runMain } from "citty";
16
16
 
17
17
  // src/commands/actions/index.ts
18
18
  import { defineCommand as defineCommand12 } from "citty";
@@ -8287,6 +8287,24 @@ import { promisify as promisify2 } from "util";
8287
8287
  var execFileAsync2 = promisify2(execFile2);
8288
8288
  var PYSCENEDETECT_THRESHOLD = 18;
8289
8289
  var PYSCENEDETECT_MIN_SCENE_LEN_S = 0.25;
8290
+ var PYSCENEDETECT_RECHECK_THRESHOLD = 27;
8291
+ var PYSCENEDETECT_RECHECK_MIN_SCENE_LEN_S = 0.6;
8292
+ function isLikelyOverSegmented(cuts, opts = {}) {
8293
+ const minCuts = opts.minCuts ?? 6;
8294
+ const maxMedianGap = opts.medianGapS ?? 2;
8295
+ const sorted = [...cuts].filter((c) => Number.isFinite(c) && c > 0).sort((a, b) => a - b);
8296
+ if (sorted.length < minCuts) return false;
8297
+ const gaps = [];
8298
+ let prev = 0;
8299
+ for (const c of sorted) {
8300
+ gaps.push(c - prev);
8301
+ prev = c;
8302
+ }
8303
+ gaps.sort((a, b) => a - b);
8304
+ const mid = Math.floor(gaps.length / 2);
8305
+ const median = gaps.length % 2 ? gaps[mid] : (gaps[mid - 1] + gaps[mid]) / 2;
8306
+ return median < maxMedianGap;
8307
+ }
8290
8308
  function timecodeToSeconds(tc) {
8291
8309
  const m = tc.trim().match(/^(\d+):(\d{1,2}):(\d{1,2}(?:\.\d+)?)$/);
8292
8310
  if (!m) return null;
@@ -8306,9 +8324,7 @@ function parsePySceneDetectCsvCuts(csv) {
8306
8324
  }
8307
8325
  return [...new Set(cuts)].sort((a, b) => a - b);
8308
8326
  }
8309
- async function detectSceneCutsPySceneDetect(filePath, opts = {}) {
8310
- const threshold = opts.threshold ?? PYSCENEDETECT_THRESHOLD;
8311
- const minSceneLenS = opts.minSceneLenS ?? PYSCENEDETECT_MIN_SCENE_LEN_S;
8327
+ async function runSceneDetectOnce(filePath, threshold, minSceneLenS, timeoutMs) {
8312
8328
  const outDir = await mkdtemp(join2(tmpdir(), "baker-scenedetect-"));
8313
8329
  try {
8314
8330
  await execFileAsync2(
@@ -8326,7 +8342,7 @@ async function detectSceneCutsPySceneDetect(filePath, opts = {}) {
8326
8342
  "list-scenes",
8327
8343
  "--quiet"
8328
8344
  ],
8329
- { encoding: "utf-8", maxBuffer: 32 * 1024 * 1024, timeout: opts.timeout_ms ?? 12e4 }
8345
+ { encoding: "utf-8", maxBuffer: 32 * 1024 * 1024, timeout: timeoutMs }
8330
8346
  );
8331
8347
  const csvName = (await readdir2(outDir)).find((f) => f.toLowerCase().endsWith(".csv"));
8332
8348
  if (!csvName) return [];
@@ -8335,6 +8351,22 @@ async function detectSceneCutsPySceneDetect(filePath, opts = {}) {
8335
8351
  await rm(outDir, { recursive: true, force: true });
8336
8352
  }
8337
8353
  }
8354
+ async function detectSceneCutsPySceneDetect(filePath, opts = {}) {
8355
+ const pinned = opts.threshold !== void 0;
8356
+ const threshold = opts.threshold ?? PYSCENEDETECT_THRESHOLD;
8357
+ const minSceneLenS = opts.minSceneLenS ?? PYSCENEDETECT_MIN_SCENE_LEN_S;
8358
+ const timeoutMs = opts.timeout_ms ?? 12e4;
8359
+ const cuts = await runSceneDetectOnce(filePath, threshold, minSceneLenS, timeoutMs);
8360
+ if (!pinned && isLikelyOverSegmented(cuts)) {
8361
+ return await runSceneDetectOnce(
8362
+ filePath,
8363
+ PYSCENEDETECT_RECHECK_THRESHOLD,
8364
+ PYSCENEDETECT_RECHECK_MIN_SCENE_LEN_S,
8365
+ timeoutMs
8366
+ );
8367
+ }
8368
+ return cuts;
8369
+ }
8338
8370
 
8339
8371
  // src/engine/scaffold/video.ts
8340
8372
  import { z as z3 } from "zod";
@@ -8438,7 +8470,7 @@ var FIXED_TTS_MODEL = "elevenlabs/eleven_v3";
8438
8470
  var FIXED_SFX_MODEL = "elevenlabs/eleven_text_to_sound_v2";
8439
8471
  var FIXED_MUSIC_MODEL = "elevenlabs/music-v1";
8440
8472
  var FIXED_VOICE_CONVERT_MODEL = "elevenlabs/eleven_multilingual_sts_v2";
8441
- var MUSIC_BED_GAIN_DB = -12;
8473
+ var MUSIC_BED_GAIN_DB = -20;
8442
8474
  var AMBIENT_BED_GAIN_DB = -20;
8443
8475
  var TRANSITION_DEFAULT_S = 0.4;
8444
8476
  var XFADE_BY_TYPE = {
@@ -8821,7 +8853,16 @@ function todoPath2(el, label) {
8821
8853
  return `[TODO: drop one real source image for ${label} (${el.type})${desc}${expr} \u2014 reused across every frame it appears in${fresh}${same}]`;
8822
8854
  }
8823
8855
  function buildElementSlots(elements) {
8824
- const usedIds = /* @__PURE__ */ new Set(["prompt", "spine", "overlaid", "audio_mix", "final", "music_bed"]);
8856
+ const usedIds = /* @__PURE__ */ new Set([
8857
+ "prompt",
8858
+ "spine",
8859
+ "overlaid",
8860
+ "captions",
8861
+ "captions_transcript",
8862
+ "audio_mix",
8863
+ "final",
8864
+ "music_bed"
8865
+ ]);
8825
8866
  const slots = [];
8826
8867
  assignElementLabels2(elements).forEach(({ el, label }, i) => {
8827
8868
  let id = sanitizeId2(`el_${label}`, `el_${i}`);
@@ -8892,6 +8933,9 @@ function buildFramePrompt(edge, sceneIndex, framePrompt, present, hasAnchor, mod
8892
8933
  "a studio) contains any text or graphics, DO NOT reproduce them \u2014 render the subject/scene",
8893
8934
  "only, leaving the regions where overlays will sit clean. Imperfect/garbled letterforms or",
8894
8935
  "stray icons are the worst outcome; leave those areas blank.",
8936
+ "A SCREEN/UI surface \u2014 an app, website, chat, dashboard, or phone display \u2014 is NEVER",
8937
+ "rendered here: leave any phone/screen OFF or blank-screened. The real interface is",
8938
+ "composited later as a screenshot or a brand HTML block, never AI-generated.",
8895
8939
  "",
8896
8940
  "FRAMING \u2014 ONE UNCUT FRAME:",
8897
8941
  "Render ONE single uncut photographic frame: NO split screen, NO panels, NO dividing line,",
@@ -8959,8 +9003,13 @@ function ingestFrameRef(url, edge, ctx, nodes) {
8959
9003
  function buildFrameRef(edge, url, framePrompt, present, ctx, nodes) {
8960
9004
  const tag = ctx.tag ?? "";
8961
9005
  if (ctx.reuse && url) return ingestFrameRef(url, edge, ctx, nodes);
8962
- const hasOriginal = Boolean(url);
8963
- const originalRef = hasOriginal && url ? ingestFrameRef(url, edge, ctx, nodes) : void 0;
9006
+ const hasPersonOrAnimal = present.some((s) => {
9007
+ const t = s.type.toLowerCase();
9008
+ return t === "person" || t === "animal";
9009
+ });
9010
+ const useOriginalAnchor = Boolean(url) && !hasPersonOrAnimal;
9011
+ const hasOriginal = useOriginalAnchor;
9012
+ const originalRef = useOriginalAnchor && url ? ingestFrameRef(url, edge, ctx, nodes) : void 0;
8964
9013
  const reference = [...present.map((s) => s.ref), ...originalRef ? [originalRef] : []];
8965
9014
  const genParams = {
8966
9015
  model: ctx.imageModel,
@@ -9151,12 +9200,21 @@ function emitSceneClip(i, scene, present, mode, nativeTurn, ambientBroll, frames
9151
9200
  return { ref: `$ref:s${i}${tag}_clip_trim.video`, scene_s: lengths.dur, out };
9152
9201
  }
9153
9202
  var COMPOSITE_LAYOUTS = /* @__PURE__ */ new Set(["split_screen", "pip", "keyed_overlay"]);
9203
+ var UI_SURFACE_RE = /\b(?:app|ui|web ?site|web ?page|website|browser|chat|interface|mock-?up|in[- ]?app|dashboard|app screen|phone screen|screen[- ]?(?:recording|capture|grab|share))\b/i;
9204
+ function regionIsUiSurface(r) {
9205
+ return UI_SURFACE_RE.test(`${r.panel ?? ""} ${r.summary ?? ""} ${r.frame_prompt ?? ""}`);
9206
+ }
9207
+ function isUiOnlyComposite(regions) {
9208
+ const ui = regions.filter(regionIsUiSurface).length;
9209
+ return ui >= 1 && regions.length - ui <= 1;
9210
+ }
9154
9211
  function layeredComposition(scene) {
9155
9212
  const comp = scene.composition;
9156
9213
  const layout = (comp?.layout ?? "").toLowerCase();
9157
9214
  if (!COMPOSITE_LAYOUTS.has(layout)) return null;
9158
9215
  const regions = (comp?.regions ?? []).filter((r) => Boolean(r) && typeof r === "object");
9159
9216
  if (regions.length < 2) return null;
9217
+ if (isUiOnlyComposite(regions)) return null;
9160
9218
  return { layout, regions, comp: comp ?? {} };
9161
9219
  }
9162
9220
  function splitAxisOf(comp, regions) {
@@ -9320,24 +9378,15 @@ function emitFlashHold(i, scene, slots, ctx, lengths, out, ar, nodes, clips) {
9320
9378
  });
9321
9379
  clips.push({ ref: `$ref:s${i}_clip.video`, scene_s: lengths.dur, out });
9322
9380
  }
9323
- function musicScriptDigest(blueprint) {
9324
- const lines = blueprint.scenes.flatMap((s) => (s.dialogue ?? []).map((d) => d.line?.trim())).filter((l) => Boolean(l));
9325
- const script = lines.join(" ").slice(0, 500);
9381
+ function musicArcDigest(blueprint) {
9326
9382
  const roles = blueprint.scenes.map((s) => s.narrative_role).filter((r) => Boolean(r));
9327
9383
  const arc = roles.length > 0 ? roles.join(" \u2192 ") : "";
9328
- const parts = [];
9329
- if (script) {
9330
- parts.push(
9331
- `Ad script (the bed must SUPPORT these words \u2014 leave room for the voice, swell on the payoff): "${script}"`
9332
- );
9333
- }
9334
- if (arc) parts.push(`Emotional arc across scenes: ${arc}. Shape the bed's energy to this arc.`);
9335
- return parts.length > 0 ? `
9384
+ return arc ? `
9336
9385
 
9337
- ${parts.join("\n")}` : "";
9386
+ Emotional arc across scenes: ${arc}. Shape the bed's energy to this arc, swelling on the payoff. Purely instrumental \u2014 no vocals, no singing, no spoken words.` : "";
9338
9387
  }
9339
9388
  function musicBedPrompt(blueprint, musicPrompt) {
9340
- const digest = musicScriptDigest(blueprint);
9389
+ const digest = musicArcDigest(blueprint);
9341
9390
  const track2 = blueprint.global?.music?.identified_track;
9342
9391
  const title = track2?.title?.trim();
9343
9392
  const vibe = title ? `
@@ -9881,13 +9930,21 @@ function buildSfxMusic(blueprint, nodes) {
9881
9930
  const musicPrompt = blueprint.global?.music?.music_prompt;
9882
9931
  if (musicPrompt) {
9883
9932
  const total = blueprint.source?.duration_s ?? lastSceneEnd(blueprint);
9884
- const startAt = Math.min(Math.max(blueprint.global?.music?.starts_at_s ?? 0, 0), Math.max(total - 0.5, 0));
9933
+ const hookEnd = blueprint.scenes[0]?.end_s ?? 0;
9934
+ const startAt = Math.min(Math.max(blueprint.global?.music?.starts_at_s ?? 0, hookEnd), Math.max(total - 0.5, 0));
9885
9935
  const totalMs = Math.round((total - startAt) * 1e3);
9886
9936
  const musicMs = Math.min(Math.max(totalMs, 3e3), ELEVENLABS_MAX_MUSIC_LENGTH_MS);
9887
9937
  nodes.push({
9888
9938
  id: "music_bed",
9889
9939
  type: "music",
9890
- params: { model: FIXED_MUSIC_MODEL, prompt: musicBedPrompt(blueprint, musicPrompt), music_length_ms: musicMs }
9940
+ // force_instrumental: the model is vocal-capable; without this it can SING the
9941
+ // mood (and feeding it the script made it sing the ad). The voice owns the words.
9942
+ params: {
9943
+ model: FIXED_MUSIC_MODEL,
9944
+ prompt: musicBedPrompt(blueprint, musicPrompt),
9945
+ music_length_ms: musicMs,
9946
+ force_instrumental: true
9947
+ }
9891
9948
  });
9892
9949
  tracks.push({
9893
9950
  slot: "music",
@@ -9931,9 +9988,20 @@ function normalizeAnim(animation) {
9931
9988
  const mapped = animation === "slide" ? "slide_up" : animation;
9932
9989
  return SUPPORTED_ANIMS.has(mapped) ? mapped : void 0;
9933
9990
  }
9991
+ var FACE_ZONE_POSITIONS = /* @__PURE__ */ new Set([
9992
+ "center",
9993
+ "centre",
9994
+ "mid-center",
9995
+ "mid-centre",
9996
+ "middle-center",
9997
+ "center-center",
9998
+ "mid",
9999
+ "middle"
10000
+ ]);
9934
10001
  function positionClass(position) {
9935
10002
  const p = (position ?? "bottom_center").toLowerCase().replace(/[^a-z]+/g, "-");
9936
- return `pos-${p}`;
10003
+ const safe = FACE_ZONE_POSITIONS.has(p) ? "bottom-center" : p;
10004
+ return `pos-${safe}`;
9937
10005
  }
9938
10006
  function collectCaptions(blueprint) {
9939
10007
  return blueprint.scenes.flatMap((scene) => {
@@ -10004,6 +10072,26 @@ function floatingStub(fe, sceneStart) {
10004
10072
  `<img class="ov ${positionClass(fe.position)}" src="your-${slug}.png" data-start="${at}" data-dur="${dur}" alt="" /> -->`
10005
10073
  ].join("\n");
10006
10074
  }
10075
+ function uiPipStub(scene) {
10076
+ const comp = scene.composition;
10077
+ const layout = (comp?.layout ?? "").toLowerCase();
10078
+ if (!COMPOSITE_LAYOUTS.has(layout)) return "";
10079
+ const regions = (comp?.regions ?? []).filter((r) => Boolean(r) && typeof r === "object");
10080
+ if (regions.length < 2 || !isUiOnlyComposite(regions)) return "";
10081
+ const ui = regions.find(regionIsUiSurface);
10082
+ const at = scene.start_s ?? 0;
10083
+ const dur = Math.max(0.5, Math.round(((scene.end_s ?? at + 2.5) - at) * 100) / 100);
10084
+ const label = commentSafe(ui?.summary || ui?.frame_prompt || ui?.panel || "the app screen");
10085
+ return [
10086
+ `<!-- PHONE UI @ ${at}s for ${dur}s \u2014 the app/site screen this scene shows: ${label}.`,
10087
+ " Build it as a REAL surface, NEVER AI: capture the live page \u2014",
10088
+ " baker images screenshot https://<brand-domain>/<path> (image-library skill)",
10089
+ " \u2014 OR hand-build a brand-accurate HTML screen; then frame it in a phone mockup:",
10090
+ " npx hyperframes add phone-scroll (writes compositions/phone-scroll.html)",
10091
+ " drop the screenshot as screenshot.png in this dir and nest it as a PIP clip:",
10092
+ ` <div data-composition-src="compositions/phone-scroll.html" data-start="${at}" data-duration="${dur}" data-track-index="2" data-width="1080" data-height="1920"></div> -->`
10093
+ ].join("\n");
10094
+ }
10007
10095
  function buildOverlayHtml(input) {
10008
10096
  const blueprint = VideoBlueprint.parse(input);
10009
10097
  const blocks = [
@@ -10029,6 +10117,8 @@ function buildOverlayHtml(input) {
10029
10117
  const sceneStart = scene.start_s ?? 0;
10030
10118
  const floats = z3.array(FloatingElement).safeParse(scene.floating_elements ?? []);
10031
10119
  const parts = (floats.success ? floats.data.map((fe) => floatingStub(fe, sceneStart)) : []).filter(Boolean);
10120
+ const pip = uiPipStub(scene);
10121
+ if (pip) parts.push(pip);
10032
10122
  if (parts.length > 0) blocks.push(parts.join("\n"));
10033
10123
  }
10034
10124
  return blocks.join("\n\n");
@@ -10110,13 +10200,14 @@ function scaffoldVideoCanvas(input, elementsInput, opts) {
10110
10200
  params: { source: "path", path: todoPath2(elements[i], slot.label), expect: "image" }
10111
10201
  });
10112
10202
  });
10113
- if (opts.actorSheets) applyActorSheets(slots, nodes);
10203
+ applyActorSheets(slots, nodes);
10114
10204
  const { clips, voTracks, vo_segments, talking_scenes } = buildTimeline(blueprint, slots, opts, nodes);
10115
10205
  let videoRef = buildSpine(clips, nodes);
10116
10206
  let videoNode = "spine";
10117
10207
  const overlays = blueprint.scenes.flatMap((s) => s.overlays ?? []);
10118
10208
  const floating = blueprint.scenes.flatMap((s) => s.floating_elements ?? []);
10119
- if (overlays.length > 0 || floating.length > 0) {
10209
+ const hasUiPip = blueprint.scenes.some((s) => uiPipStub(s) !== "");
10210
+ if (overlays.length > 0 || floating.length > 0 || hasUiPip) {
10120
10211
  nodes.push({
10121
10212
  id: "overlaid",
10122
10213
  type: "hyperframe_render",
@@ -10126,10 +10217,28 @@ function scaffoldVideoCanvas(input, elementsInput, opts) {
10126
10217
  videoRef = "$ref:overlaid.video";
10127
10218
  videoNode = "overlaid";
10128
10219
  }
10220
+ if (opts.captionsCompositionPath && opts.transcriptPath) {
10221
+ nodes.push({
10222
+ id: "captions_transcript",
10223
+ type: "ingest",
10224
+ params: { source: "path", path: opts.transcriptPath, expect: "json" }
10225
+ });
10226
+ nodes.push({
10227
+ id: "captions",
10228
+ type: "hyperframe_render",
10229
+ inputs: { background: videoRef, transcript: "$ref:captions_transcript.asset" },
10230
+ params: { composition: opts.captionsCompositionPath }
10231
+ });
10232
+ videoRef = "$ref:captions.video";
10233
+ videoNode = "captions";
10234
+ }
10129
10235
  const tracks = [...voTracks, ...buildSfxMusic(blueprint, nodes)];
10130
10236
  if (tracks.length > 0) {
10131
10237
  const mixInputs = {};
10132
10238
  for (const t of tracks) mixInputs[t.slot] = t.ref;
10239
+ const musicTrack = tracks.find((t) => t.kind === "music");
10240
+ const voiceSlots = tracks.filter((t) => t.kind === "vo").map((t) => t.slot);
10241
+ const duck = musicTrack && voiceSlots.length > 0 ? { duck: { track: musicTrack.slot, against: voiceSlots } } : {};
10133
10242
  nodes.push({
10134
10243
  id: "audio_mix",
10135
10244
  type: "audio_timeline",
@@ -10140,7 +10249,8 @@ function scaffoldVideoCanvas(input, elementsInput, opts) {
10140
10249
  start_s: t.start_s,
10141
10250
  ...t.gain_db !== void 0 ? { gain_db: t.gain_db } : {}
10142
10251
  })),
10143
- total_ms: Math.round((blueprint.source?.duration_s ?? lastSceneEnd(blueprint)) * 1e3)
10252
+ total_ms: Math.round((blueprint.source?.duration_s ?? lastSceneEnd(blueprint)) * 1e3),
10253
+ ...duck
10144
10254
  }
10145
10255
  });
10146
10256
  nodes.push({
@@ -10432,6 +10542,7 @@ function resolveShippedCanvasDir(name, startDir, exists = existsSync3, maxDepth
10432
10542
 
10433
10543
  // src/commands/canvas/scaffold-video.ts
10434
10544
  var SHIPPED_COMPOSITION_DIR = resolveShippedCanvasDir("video-overlay-composition", import.meta.dirname);
10545
+ var SHIPPED_CAPTIONS_DIR = resolveShippedCanvasDir("tiktok-captions-composition", import.meta.dirname);
10435
10546
  function resolveModel2(kind, preferred) {
10436
10547
  const ids = Object.keys(MODEL_REGISTRY[kind]);
10437
10548
  return ids.includes(preferred) ? preferred : ids[0] ?? preferred;
@@ -10463,6 +10574,24 @@ async function loadAssetText2(ref, label) {
10463
10574
  }
10464
10575
  throw new Error(`${label}: output had no readable path or url`);
10465
10576
  }
10577
+ async function loadTranscriptBestEffort(ref) {
10578
+ if (!ref) return void 0;
10579
+ try {
10580
+ return await loadAssetText2(ref, "deconstruct transcript");
10581
+ } catch {
10582
+ return void 0;
10583
+ }
10584
+ }
10585
+ async function stageCaptions(outDir, transcript) {
10586
+ const text = transcript?.trim();
10587
+ if (!text || text === "[]") return {};
10588
+ const transcriptPath = path5.join(outDir, "transcript.json");
10589
+ await writeFile2(transcriptPath, `${text}
10590
+ `, "utf8");
10591
+ const compositionPath = path5.join(outDir, "tiktok-captions-composition");
10592
+ await cp(SHIPPED_CAPTIONS_DIR, compositionPath, { recursive: true });
10593
+ return { compositionPath, transcriptPath };
10594
+ }
10466
10595
  function parseElements2(raw) {
10467
10596
  const parsed = JSON.parse(raw);
10468
10597
  if (Array.isArray(parsed)) return parsed;
@@ -10568,10 +10697,12 @@ async function runAnalysisPasses(deconstructCanvas, selectModel) {
10568
10697
  }
10569
10698
  };
10570
10699
  let blueprint;
10700
+ let transcript;
10571
10701
  try {
10572
10702
  const r1 = await engine.run(deconstructCanvas, {});
10573
10703
  addCredits(r1.stats);
10574
10704
  blueprint = JSON.parse(await loadAssetText2(r1.outputs_by_node.deconstruct?.analysis, "deconstruct output"));
10705
+ transcript = await loadTranscriptBestEffort(r1.outputs_by_node.deconstruct?.transcript);
10575
10706
  } catch (e) {
10576
10707
  if (e instanceof ValidationError) return fail2("validation", JSON.stringify(e.issues));
10577
10708
  if (e instanceof SyntaxError) return fail2("read_outputs", e.message);
@@ -10582,7 +10713,7 @@ async function runAnalysisPasses(deconstructCanvas, selectModel) {
10582
10713
  const r2 = await engine.run(buildSelectCanvas(selectModel, slimJson), {});
10583
10714
  addCredits(r2.stats);
10584
10715
  const elements = parseElements2(await loadAssetText2(r2.outputs_by_node.select?.text, "selection output"));
10585
- return { blueprint, elements, creditsSpent: sawCredits ? credits : void 0 };
10716
+ return { blueprint, elements, transcript, creditsSpent: sawCredits ? credits : void 0 };
10586
10717
  } catch (e) {
10587
10718
  if (e instanceof ValidationError) return fail2("validation", JSON.stringify(e.issues));
10588
10719
  if (e instanceof SyntaxError) return fail2("read_outputs", e.message);
@@ -10602,14 +10733,10 @@ var scaffoldVideoCommand = defineCommand76({
10602
10733
  type: "boolean",
10603
10734
  description: "Give silent b-roll scenes native diegetic ambient mixed deep under the music bed (off by default)"
10604
10735
  },
10605
- "actor-sheets": {
10606
- type: "boolean",
10607
- description: "Lock a recast person/animal that recurs across \u22652 scenes to ONE turnaround sheet grounding every frame"
10608
- },
10609
10736
  "max-scenes": { type: "string", description: "Cap the number of scenes the deconstruct emits" },
10610
10737
  "shot-threshold": {
10611
10738
  type: "string",
10612
- description: "PySceneDetect content threshold (default 18; lower = more/softer cuts, higher = fewer)"
10739
+ description: "PySceneDetect content threshold. Default is adaptive (18, auto re-checked at 27 when a continuous shot looks over-segmented); pinning a value disables the re-check. Lower = more/softer cuts, higher = fewer."
10613
10740
  },
10614
10741
  language: { type: "string", description: "Transcript/dialogue language hint (e.g. fr, en)" },
10615
10742
  focus: { type: "string", description: "Known provenance/emphasis to ground the deconstruct" },
@@ -10641,7 +10768,7 @@ var scaffoldVideoCommand = defineCommand76({
10641
10768
  focus: args.focus ? String(args.focus) : void 0,
10642
10769
  shotCuts
10643
10770
  });
10644
- const { blueprint, elements, creditsSpent } = await runAnalysisPasses(deconstructCanvas, selectModel);
10771
+ const { blueprint, elements, transcript, creditsSpent } = await runAnalysisPasses(deconstructCanvas, selectModel);
10645
10772
  await mkdir(outDir, { recursive: true });
10646
10773
  const annotated = annotateBlueprintWithElements(blueprint, elements);
10647
10774
  await writeFile2(blueprintPath, `${JSON.stringify(annotated, null, 2)}
@@ -10659,14 +10786,16 @@ var scaffoldVideoCommand = defineCommand76({
10659
10786
  );
10660
10787
  }
10661
10788
  await writeFile2(indexPath, injected, "utf8");
10789
+ const captions = await stageCaptions(outDir, transcript);
10662
10790
  const opts = {
10663
10791
  imageModel,
10664
10792
  videoModel,
10665
10793
  overlayCompositionPath: compositionDest,
10794
+ captionsCompositionPath: captions.compositionPath,
10795
+ transcriptPath: captions.transcriptPath,
10666
10796
  blueprintPath,
10667
10797
  frames,
10668
- ambient: Boolean(args.ambient),
10669
- actorSheets: Boolean(args["actor-sheets"])
10798
+ ambient: Boolean(args.ambient)
10670
10799
  };
10671
10800
  let canvas;
10672
10801
  let report;
@@ -14178,11 +14307,292 @@ Paid transforms (run on the Convex backend, cost-tracked):
14178
14307
  }
14179
14308
  });
14180
14309
 
14310
+ // src/commands/missions/index.ts
14311
+ import { defineCommand as defineCommand116 } from "citty";
14312
+
14313
+ // src/commands/missions/add-action.ts
14314
+ import { defineCommand as defineCommand110 } from "citty";
14315
+ registerSchema({
14316
+ command: "missions.add-action",
14317
+ description: "Attach an action to a mission as an ordered step. Both --mission and --action accept a real id or a temp_* ref from a create op in the same draft. --order is the 0-based position in the plan. Applies on chat publish.",
14318
+ args: {
14319
+ mission: { type: "string", description: "Mission id or missionTempId", required: true },
14320
+ action: { type: "string", description: "Action id or tempId", required: true },
14321
+ order: { type: "string", description: "0-based position within the mission", required: true }
14322
+ }
14323
+ });
14324
+ var addActionCommand = defineCommand110({
14325
+ meta: {
14326
+ name: "add-action",
14327
+ description: "Attach an action to a mission as an ordered step. Example: baker missions add-action --mission <ref> --action <ref> --order 0"
14328
+ },
14329
+ args: {
14330
+ mission: { type: "string", description: "Mission ref", required: false },
14331
+ action: { type: "string", description: "Action ref", required: false },
14332
+ order: { type: "string", description: "0-based order", required: false }
14333
+ },
14334
+ run: async ({ args }) => {
14335
+ try {
14336
+ const mission = args.mission;
14337
+ const action = args.action;
14338
+ if (!mission || !action) {
14339
+ failValidation("--mission and --action are required.");
14340
+ }
14341
+ const order = Number.parseInt(args.order ?? "", 10);
14342
+ if (Number.isNaN(order) || order < 0) {
14343
+ failValidation("--order must be a non-negative integer.");
14344
+ }
14345
+ const chatId = requireChatId();
14346
+ await apiPost("/api/missions/add-action", {
14347
+ chatId,
14348
+ missionRef: mission,
14349
+ actionRef: action,
14350
+ order
14351
+ });
14352
+ writeOk();
14353
+ } catch (err) {
14354
+ failApi(err);
14355
+ }
14356
+ }
14357
+ });
14358
+
14359
+ // src/commands/missions/create.ts
14360
+ import { defineCommand as defineCommand111 } from "citty";
14361
+ registerSchema({
14362
+ command: "missions.create",
14363
+ description: "Open a Mission to group the ordered steps of a multi-step request. A Mission has a title and a markdown overview (what you're doing and why \u2014 the human-readable plan, written as numbered Phases with NO dates and NO effort sizing). Returns a missionTempId. Next: create the action steps with `baker actions create`, then attach each in order with `baker missions add-action`. Applies when the chat is published \u2014 publishing = the user approving the plan.",
14364
+ args: {
14365
+ title: { type: "string", description: "Mission title \u2014 the goal, short", required: true },
14366
+ overview: {
14367
+ type: "string",
14368
+ description: "Markdown overview: the plan as numbered Phases (goal / what it produces / what unlocks next). No calendar or effort framing.",
14369
+ required: false
14370
+ },
14371
+ "temp-id": { type: "string", description: "Custom missionTempId (auto-generated if omitted)", required: false }
14372
+ }
14373
+ });
14374
+ var createCommand2 = defineCommand111({
14375
+ meta: {
14376
+ name: "create",
14377
+ description: 'Open a Mission to group ordered steps. Example: baker missions create --title "Audit Google Ads" --overview "..."'
14378
+ },
14379
+ args: {
14380
+ title: { type: "string", description: "Mission title", required: false },
14381
+ overview: { type: "string", description: "Markdown overview (numbered Phases)", required: false, default: "" },
14382
+ "temp-id": { type: "string", description: "Optional custom missionTempId", required: false }
14383
+ },
14384
+ run: async ({ args }) => {
14385
+ try {
14386
+ const title = args.title;
14387
+ if (!title || title.trim().length === 0) {
14388
+ failValidation("--title is required.");
14389
+ }
14390
+ const chatId = requireChatId();
14391
+ const missionTempId = args["temp-id"] || generateTempId();
14392
+ const response = await apiPost("/api/missions/create", {
14393
+ chatId,
14394
+ missionTempId,
14395
+ title,
14396
+ overview: args.overview ?? ""
14397
+ });
14398
+ const hints = [
14399
+ `Add ordered steps: create each action, then baker missions add-action --mission ${missionTempId} --action <tempId> --order 0`
14400
+ ];
14401
+ if (!args.overview) {
14402
+ hints.push(
14403
+ "Tip: pass --overview with the plan as numbered Phases (goal / produces / unlocks; no dates or effort sizing)."
14404
+ );
14405
+ }
14406
+ writeJson({ ...response, hints });
14407
+ } catch (err) {
14408
+ failApi(err);
14409
+ }
14410
+ }
14411
+ });
14412
+
14413
+ // src/commands/missions/get.ts
14414
+ import { defineCommand as defineCommand112 } from "citty";
14415
+ registerSchema({
14416
+ command: "missions.get",
14417
+ description: "Get one mission with its overview, progress, and ordered steps.",
14418
+ args: {
14419
+ id: { type: "string", description: "Mission ID", required: true }
14420
+ }
14421
+ });
14422
+ var getCommand3 = defineCommand112({
14423
+ meta: { name: "get", description: "Get a mission by ID. Example: baker missions get <mission-id>" },
14424
+ args: {
14425
+ id: { type: "positional", description: "Mission ID", required: false },
14426
+ "mission-id": { type: "string", description: "Mission ID (alternative to positional)", required: false }
14427
+ },
14428
+ run: async ({ args }) => {
14429
+ try {
14430
+ const id = args.id || args["mission-id"];
14431
+ if (!id) {
14432
+ failValidation("Mission ID is required.");
14433
+ }
14434
+ validateConvexId(id);
14435
+ const response = await apiPost("/api/missions/get", { missionId: id });
14436
+ writeJson(response);
14437
+ } catch (err) {
14438
+ failApi(err);
14439
+ }
14440
+ }
14441
+ });
14442
+
14443
+ // src/commands/missions/list.ts
14444
+ import { defineCommand as defineCommand113 } from "citty";
14445
+ registerSchema({
14446
+ command: "missions.list",
14447
+ description: "List the company's missions with per-mission progress (done/total) and ordered steps. Aborted missions are hidden unless --include-aborted.",
14448
+ args: {
14449
+ "include-aborted": {
14450
+ type: "boolean",
14451
+ description: "Include aborted missions (default: false)",
14452
+ required: false,
14453
+ default: false
14454
+ }
14455
+ }
14456
+ });
14457
+ var listCommand2 = defineCommand113({
14458
+ meta: {
14459
+ name: "list",
14460
+ description: "List missions with progress and ordered steps. Example: baker missions list"
14461
+ },
14462
+ args: {
14463
+ "include-aborted": { type: "boolean", description: "Include aborted missions", required: false, default: false }
14464
+ },
14465
+ run: async ({ args }) => {
14466
+ try {
14467
+ const body = {};
14468
+ if (args["include-aborted"] === true) {
14469
+ body.includeAborted = true;
14470
+ }
14471
+ const response = await apiPost("/api/missions/list", body);
14472
+ writeJson(response);
14473
+ } catch (err) {
14474
+ failApi(err);
14475
+ }
14476
+ }
14477
+ });
14478
+
14479
+ // src/commands/missions/set-status.ts
14480
+ import { defineCommand as defineCommand114 } from "citty";
14481
+ registerSchema({
14482
+ command: "missions.set-status",
14483
+ description: "Set a mission's status: accomplished (the goal is genuinely met), aborted (abandoned), or active (reopen). Do NOT mark accomplished just because all steps are closed \u2014 only when the goal is done. Applies on chat publish.",
14484
+ args: {
14485
+ id: { type: "string", description: "Mission ID", required: true },
14486
+ status: { type: "string", description: "active | accomplished | aborted", required: true }
14487
+ }
14488
+ });
14489
+ var setStatusCommand = defineCommand114({
14490
+ meta: {
14491
+ name: "set-status",
14492
+ description: "Set mission status. Example: baker missions set-status <id> --status accomplished"
14493
+ },
14494
+ args: {
14495
+ id: { type: "positional", description: "Mission ID", required: false },
14496
+ "mission-id": { type: "string", description: "Mission ID", required: false },
14497
+ status: { type: "string", description: "active | accomplished | aborted", required: false }
14498
+ },
14499
+ run: async ({ args }) => {
14500
+ try {
14501
+ const id = args.id || args["mission-id"];
14502
+ if (!id) {
14503
+ failValidation("Mission ID is required.");
14504
+ }
14505
+ validateConvexId(id);
14506
+ const status = args.status;
14507
+ if (status !== "active" && status !== "accomplished" && status !== "aborted") {
14508
+ failValidation("--status must be one of: active, accomplished, aborted.");
14509
+ }
14510
+ const chatId = requireChatId();
14511
+ await apiPost("/api/missions/set-status", { chatId, missionId: id, status });
14512
+ writeOk();
14513
+ } catch (err) {
14514
+ failApi(err);
14515
+ }
14516
+ }
14517
+ });
14518
+
14519
+ // src/commands/missions/update.ts
14520
+ import { defineCommand as defineCommand115 } from "citty";
14521
+ registerSchema({
14522
+ command: "missions.update",
14523
+ description: "Stage an update to a mission's title and/or overview (real mission id only). Applies on chat publish.",
14524
+ args: {
14525
+ id: { type: "string", description: "Mission ID", required: true },
14526
+ title: { type: "string", description: "New title", required: false },
14527
+ overview: { type: "string", description: "New markdown overview (numbered Phases)", required: false }
14528
+ }
14529
+ });
14530
+ var updateCommand2 = defineCommand115({
14531
+ meta: {
14532
+ name: "update",
14533
+ description: 'Stage a mission update. Example: baker missions update <id> --overview "..."'
14534
+ },
14535
+ args: {
14536
+ id: { type: "positional", description: "Mission ID", required: false },
14537
+ "mission-id": { type: "string", description: "Mission ID", required: false },
14538
+ title: { type: "string", description: "New title", required: false },
14539
+ overview: { type: "string", description: "New markdown overview", required: false }
14540
+ },
14541
+ run: async ({ args }) => {
14542
+ try {
14543
+ const id = args.id || args["mission-id"];
14544
+ if (!id) {
14545
+ failValidation("Mission ID is required.");
14546
+ }
14547
+ validateConvexId(id);
14548
+ if (args.title === void 0 && args.overview === void 0) {
14549
+ failValidation("Provide at least one of --title, --overview.");
14550
+ }
14551
+ const chatId = requireChatId();
14552
+ await apiPost("/api/missions/update", {
14553
+ chatId,
14554
+ missionId: id,
14555
+ title: args.title,
14556
+ overview: args.overview
14557
+ });
14558
+ writeOk();
14559
+ } catch (err) {
14560
+ failApi(err);
14561
+ }
14562
+ }
14563
+ });
14564
+
14565
+ // src/commands/missions/index.ts
14566
+ var missionsCommand = defineCommand116({
14567
+ meta: {
14568
+ name: "missions",
14569
+ description: `Group ordered actions into a Mission \u2014 one goal with a markdown overview (the plan) and ordered action "steps". Subcommands: list, get, create, add-action, update, set-status.
14570
+
14571
+ Use a mission whenever a request decomposes into 2+ ordered actions (audits, campaigns, multi-step plans). A single one-off capture stays a loose action.
14572
+
14573
+ Flow: create the mission \u2192 create each action step in order \u2192 attach each with add-action \u2192 wire hard dependencies with 'baker actions link' \u2192 publish the chat (= the user approving the plan). The overview is forward planning: write it as numbered Phases, no dates, no effort sizing. Mark accomplished/aborted explicitly \u2014 never auto-conclude from step status.
14574
+
14575
+ Examples:
14576
+ baker missions create --title "Audit Google Ads" --overview "..."
14577
+ baker missions add-action --mission <ref> --action <ref> --order 0
14578
+ baker missions list
14579
+ baker missions set-status <id> --status accomplished`
14580
+ },
14581
+ subCommands: {
14582
+ list: listCommand2,
14583
+ get: getCommand3,
14584
+ create: createCommand2,
14585
+ "add-action": addActionCommand,
14586
+ update: updateCommand2,
14587
+ "set-status": setStatusCommand
14588
+ }
14589
+ });
14590
+
14181
14591
  // src/commands/research/index.ts
14182
- import { defineCommand as defineCommand120 } from "citty";
14592
+ import { defineCommand as defineCommand127 } from "citty";
14183
14593
 
14184
14594
  // src/commands/research/advertisers.ts
14185
- import { defineCommand as defineCommand110 } from "citty";
14595
+ import { defineCommand as defineCommand117 } from "citty";
14186
14596
 
14187
14597
  // src/commands/research/output.ts
14188
14598
  var RESEARCH_DATA_NOTE = "Estimates based on third-party SERP data \u2014 not exact figures. Use for directional insights, not precise measurement.";
@@ -14295,7 +14705,7 @@ var FIELDS3 = {
14295
14705
  etv: "Estimated traffic value (USD)",
14296
14706
  visibility: "SERP visibility score (0-1)"
14297
14707
  };
14298
- var advertisersCommand = defineCommand110({
14708
+ var advertisersCommand = defineCommand117({
14299
14709
  meta: {
14300
14710
  name: "advertisers",
14301
14711
  description: `Find domains competing for a keyword in Google SERPs.
@@ -14342,7 +14752,7 @@ Examples:
14342
14752
  });
14343
14753
 
14344
14754
  // src/commands/research/autocomplete.ts
14345
- import { defineCommand as defineCommand111 } from "citty";
14755
+ import { defineCommand as defineCommand118 } from "citty";
14346
14756
  registerSchema({
14347
14757
  command: "research.autocomplete",
14348
14758
  description: "Get Google Autocomplete suggestions for a seed keyword. Useful for keyword expansion and discovering what people actually search for. IMPORTANT: If --location and --language are omitted, defaults to United States (us) and English (en).",
@@ -14365,7 +14775,7 @@ registerSchema({
14365
14775
  var FIELDS4 = {
14366
14776
  suggestion: "Autocomplete suggestion from Google"
14367
14777
  };
14368
- var autocompleteCommand = defineCommand111({
14778
+ var autocompleteCommand = defineCommand118({
14369
14779
  meta: {
14370
14780
  name: "autocomplete",
14371
14781
  description: `Get Google Autocomplete suggestions for keyword expansion.
@@ -14411,7 +14821,7 @@ Examples:
14411
14821
  });
14412
14822
 
14413
14823
  // src/commands/research/countries.ts
14414
- import { defineCommand as defineCommand112 } from "citty";
14824
+ import { defineCommand as defineCommand119 } from "citty";
14415
14825
  registerSchema({
14416
14826
  command: "research.countries",
14417
14827
  description: "List all supported country codes for --location flag in research commands.",
@@ -14468,7 +14878,7 @@ var FIELDS5 = {
14468
14878
  code: "Country code to pass as --location",
14469
14879
  name: "Country name"
14470
14880
  };
14471
- var countriesCommand = defineCommand112({
14881
+ var countriesCommand = defineCommand119({
14472
14882
  meta: {
14473
14883
  name: "countries",
14474
14884
  description: "List all supported country codes for --location flag."
@@ -14479,7 +14889,7 @@ var countriesCommand = defineCommand112({
14479
14889
  });
14480
14890
 
14481
14891
  // src/commands/research/intent.ts
14482
- import { defineCommand as defineCommand113 } from "citty";
14892
+ import { defineCommand as defineCommand120 } from "citty";
14483
14893
  registerSchema({
14484
14894
  command: "research.intent",
14485
14895
  description: "Classify Google Search intent for keywords. Determines if someone searching is looking to buy, research, or navigate. IMPORTANT: If --language is omitted, defaults to English (en). The response includes a query_context object showing which language was used.",
@@ -14502,7 +14912,7 @@ var FIELDS6 = {
14502
14912
  intent: "Primary Google Search intent: informational, navigational, commercial, transactional",
14503
14913
  probability: "Confidence score 0.0-1.0"
14504
14914
  };
14505
- var intentCommand = defineCommand113({
14915
+ var intentCommand = defineCommand120({
14506
14916
  meta: {
14507
14917
  name: "intent",
14508
14918
  description: `Classify Google Search intent for keywords. Returns intent type and confidence.
@@ -14550,7 +14960,7 @@ Examples:
14550
14960
  });
14551
14961
 
14552
14962
  // src/commands/research/keyword-gap.ts
14553
- import { defineCommand as defineCommand114 } from "citty";
14963
+ import { defineCommand as defineCommand121 } from "citty";
14554
14964
  registerSchema({
14555
14965
  command: "research.keyword-gap",
14556
14966
  description: "Find keywords a competitor ranks for (organic or paid) that you don't. Discovers expansion opportunities. IMPORTANT: If --location and --language are omitted, defaults to United States (us) and English (en). The response includes a query_context object showing which location/language were used.",
@@ -14579,7 +14989,7 @@ var FIELDS7 = {
14579
14989
  cpc: "Cost per click USD",
14580
14990
  their_position: "Competitor's ranking position"
14581
14991
  };
14582
- var keywordGapCommand = defineCommand114({
14992
+ var keywordGapCommand = defineCommand121({
14583
14993
  meta: {
14584
14994
  name: "keyword-gap",
14585
14995
  description: `Find keywords a competitor has that you don't. Supports pagination via --offset.
@@ -14653,7 +15063,7 @@ Examples:
14653
15063
  });
14654
15064
 
14655
15065
  // src/commands/research/keywords-for-site.ts
14656
- import { defineCommand as defineCommand115 } from "citty";
15066
+ import { defineCommand as defineCommand122 } from "citty";
14657
15067
  registerSchema({
14658
15068
  command: "research.keywords-for-site",
14659
15069
  description: "Get keywords a competitor targets in Google. Use --type paid to see only paid keywords, --type organic for organic only. IMPORTANT: If --location and --language are omitted, defaults to United States (us) and English (en). The response includes a query_context object showing which location/language were used.",
@@ -14686,7 +15096,7 @@ var FIELDS8 = {
14686
15096
  competition: "LOW, MEDIUM, or HIGH",
14687
15097
  competition_index: "Competition score 0-100"
14688
15098
  };
14689
- var keywordsForSiteCommand = defineCommand115({
15099
+ var keywordsForSiteCommand = defineCommand122({
14690
15100
  meta: {
14691
15101
  name: "keywords-for-site",
14692
15102
  description: `Get keywords a competitor targets in Google. Use --type to filter paid/organic.
@@ -14739,7 +15149,7 @@ Examples:
14739
15149
  });
14740
15150
 
14741
15151
  // src/commands/research/languages.ts
14742
- import { defineCommand as defineCommand116 } from "citty";
15152
+ import { defineCommand as defineCommand123 } from "citty";
14743
15153
  registerSchema({
14744
15154
  command: "research.languages",
14745
15155
  description: "List all supported language codes for --language flag in research commands.",
@@ -14769,7 +15179,7 @@ var FIELDS9 = {
14769
15179
  code: "Language code to pass as --language",
14770
15180
  name: "Language name (also accepted by --language)"
14771
15181
  };
14772
- var languagesCommand2 = defineCommand116({
15182
+ var languagesCommand2 = defineCommand123({
14773
15183
  meta: {
14774
15184
  name: "languages",
14775
15185
  description: "List all supported language codes for --language flag."
@@ -14780,7 +15190,7 @@ var languagesCommand2 = defineCommand116({
14780
15190
  });
14781
15191
 
14782
15192
  // src/commands/research/lighthouse.ts
14783
- import { defineCommand as defineCommand117 } from "citty";
15193
+ import { defineCommand as defineCommand124 } from "citty";
14784
15194
  registerSchema({
14785
15195
  command: "research.lighthouse",
14786
15196
  description: "Landing page performance audit. Returns metrics that affect Google Ads Quality Score and CPC.",
@@ -14799,7 +15209,7 @@ var FIELDS10 = {
14799
15209
  speed_index_ms: "Speed Index in ms (good: < 3400)",
14800
15210
  interactive_ms: "Time to Interactive in ms (good: < 3800)"
14801
15211
  };
14802
- var lighthouseCommand = defineCommand117({
15212
+ var lighthouseCommand = defineCommand124({
14803
15213
  meta: {
14804
15214
  name: "lighthouse",
14805
15215
  description: `Landing page performance audit. Metrics affecting Google Ads Quality Score.
@@ -14837,7 +15247,7 @@ Examples:
14837
15247
  });
14838
15248
 
14839
15249
  // src/commands/research/relevant-pages.ts
14840
- import { defineCommand as defineCommand118 } from "citty";
15250
+ import { defineCommand as defineCommand125 } from "citty";
14841
15251
  registerSchema({
14842
15252
  command: "research.relevant-pages",
14843
15253
  description: "Get the top pages of a competitor domain with organic traffic and ranking data. Shows which pages drive the most traffic. IMPORTANT: If --location and --language are omitted, defaults to United States (us) and English (en).",
@@ -14863,7 +15273,7 @@ var FIELDS11 = {
14863
15273
  keywords: "Total organic keywords the page ranks for",
14864
15274
  top_10: "Keywords in positions 1-10"
14865
15275
  };
14866
- var relevantPagesCommand = defineCommand118({
15276
+ var relevantPagesCommand = defineCommand125({
14867
15277
  meta: {
14868
15278
  name: "relevant-pages",
14869
15279
  description: `Get the top pages of a competitor domain with traffic data.
@@ -14909,7 +15319,7 @@ Examples:
14909
15319
  });
14910
15320
 
14911
15321
  // src/commands/research/web.ts
14912
- import { defineCommand as defineCommand119 } from "citty";
15322
+ import { defineCommand as defineCommand126 } from "citty";
14913
15323
  registerSchema({
14914
15324
  command: "research.web",
14915
15325
  description: "Search the web with AI to answer marketing questions \u2014 competitors, ICP, pricing, pain points, market trends. Three depth levels: medium (quick, default), high (thorough), xhigh (exhaustive deep research).",
@@ -14960,7 +15370,7 @@ async function runDeepResearch(question) {
14960
15370
  }
14961
15371
  throw new Error("Deep research timed out");
14962
15372
  }
14963
- var webCommand = defineCommand119({
15373
+ var webCommand = defineCommand126({
14964
15374
  meta: {
14965
15375
  name: "web",
14966
15376
  description: `Search the web with AI to answer any open-ended marketing question. Uses live internet data via Google Search.
@@ -15020,7 +15430,7 @@ Examples:
15020
15430
  });
15021
15431
 
15022
15432
  // src/commands/research/index.ts
15023
- var researchCommand = defineCommand120({
15433
+ var researchCommand = defineCommand127({
15024
15434
  meta: {
15025
15435
  name: "research",
15026
15436
  description: `Competitive intelligence and AI-powered research commands.
@@ -15060,10 +15470,10 @@ Examples:
15060
15470
  });
15061
15471
 
15062
15472
  // src/commands/scheduled-actions/index.ts
15063
- import { defineCommand as defineCommand127 } from "citty";
15473
+ import { defineCommand as defineCommand134 } from "citty";
15064
15474
 
15065
15475
  // src/commands/scheduled-actions/create.ts
15066
- import { defineCommand as defineCommand121 } from "citty";
15476
+ import { defineCommand as defineCommand128 } from "citty";
15067
15477
 
15068
15478
  // src/commands/scheduled-actions/shared.ts
15069
15479
  var TEMP_SCHEDULED_ACTION_PREFIX = "temp_sched_";
@@ -15168,7 +15578,7 @@ registerSchema({
15168
15578
  prompt: { type: "string", description: "Additional prompt instructions for the spawned agent", required: false }
15169
15579
  }
15170
15580
  });
15171
- var createCommand2 = defineCommand121({
15581
+ var createCommand3 = defineCommand128({
15172
15582
  meta: {
15173
15583
  name: "create",
15174
15584
  description: 'Stage a scheduled action. Example: baker scheduled-actions create --name "Weekly report" --description "..." --cron "0 9 * * MON"'
@@ -15216,7 +15626,7 @@ var createCommand2 = defineCommand121({
15216
15626
  });
15217
15627
 
15218
15628
  // src/commands/scheduled-actions/delete.ts
15219
- import { defineCommand as defineCommand122 } from "citty";
15629
+ import { defineCommand as defineCommand129 } from "citty";
15220
15630
  registerSchema({
15221
15631
  command: "scheduled-actions.delete",
15222
15632
  description: "Stage deletion of a published scheduled action or cancellation of a temp_sched_* draft creation.",
@@ -15224,7 +15634,7 @@ registerSchema({
15224
15634
  id: { type: "string", description: "Published scheduled action ID or temp_sched_* draft ID", required: true }
15225
15635
  }
15226
15636
  });
15227
- var deleteCommand2 = defineCommand122({
15637
+ var deleteCommand2 = defineCommand129({
15228
15638
  meta: {
15229
15639
  name: "delete",
15230
15640
  description: "Stage scheduled action deletion. Example: baker scheduled-actions delete <id-or-temp_sched_id>"
@@ -15253,7 +15663,7 @@ var deleteCommand2 = defineCommand122({
15253
15663
  });
15254
15664
 
15255
15665
  // src/commands/scheduled-actions/get.ts
15256
- import { defineCommand as defineCommand123 } from "citty";
15666
+ import { defineCommand as defineCommand130 } from "citty";
15257
15667
  registerSchema({
15258
15668
  command: "scheduled-actions.get",
15259
15669
  description: "Get a published scheduled action or a temp_sched_* draft-created scheduled action.",
@@ -15261,7 +15671,7 @@ registerSchema({
15261
15671
  id: { type: "string", description: "Published scheduled action ID or temp_sched_* draft ID", required: true }
15262
15672
  }
15263
15673
  });
15264
- var getCommand3 = defineCommand123({
15674
+ var getCommand4 = defineCommand130({
15265
15675
  meta: {
15266
15676
  name: "get",
15267
15677
  description: "Get a scheduled action. Example: baker scheduled-actions get <id-or-temp_sched_id>"
@@ -15298,13 +15708,13 @@ var getCommand3 = defineCommand123({
15298
15708
  });
15299
15709
 
15300
15710
  // src/commands/scheduled-actions/list.ts
15301
- import { defineCommand as defineCommand124 } from "citty";
15711
+ import { defineCommand as defineCommand131 } from "citty";
15302
15712
  registerSchema({
15303
15713
  command: "scheduled-actions.list",
15304
15714
  description: "List published scheduled actions. Includes draft state when BAKER_CHAT_ID is set.",
15305
15715
  args: {}
15306
15716
  });
15307
- var listCommand2 = defineCommand124({
15717
+ var listCommand3 = defineCommand131({
15308
15718
  meta: {
15309
15719
  name: "list",
15310
15720
  description: "List scheduled actions. Includes staged draft ops when BAKER_CHAT_ID is set."
@@ -15325,7 +15735,7 @@ var listCommand2 = defineCommand124({
15325
15735
  });
15326
15736
 
15327
15737
  // src/commands/scheduled-actions/trigger.ts
15328
- import { defineCommand as defineCommand125 } from "citty";
15738
+ import { defineCommand as defineCommand132 } from "citty";
15329
15739
  registerSchema({
15330
15740
  command: "scheduled-actions.trigger",
15331
15741
  description: "Immediately trigger a published scheduled action. Does not require BAKER_CHAT_ID and rejects temp_sched_* IDs.",
@@ -15333,7 +15743,7 @@ registerSchema({
15333
15743
  id: { type: "string", description: "Published scheduled action ID", required: true }
15334
15744
  }
15335
15745
  });
15336
- var triggerCommand = defineCommand125({
15746
+ var triggerCommand = defineCommand132({
15337
15747
  meta: {
15338
15748
  name: "trigger",
15339
15749
  description: "Immediately trigger a published scheduled action. Example: baker scheduled-actions trigger <id>"
@@ -15370,7 +15780,7 @@ var triggerCommand = defineCommand125({
15370
15780
  });
15371
15781
 
15372
15782
  // src/commands/scheduled-actions/update.ts
15373
- import { defineCommand as defineCommand126 } from "citty";
15783
+ import { defineCommand as defineCommand133 } from "citty";
15374
15784
  registerSchema({
15375
15785
  command: "scheduled-actions.update",
15376
15786
  description: "Stage an update to a published scheduled action or temp_sched_* draft-created scheduled action.",
@@ -15395,7 +15805,7 @@ registerSchema({
15395
15805
  prompt: { type: "string", description: "Replacement additional spawned-agent instructions", required: false }
15396
15806
  }
15397
15807
  });
15398
- var updateCommand2 = defineCommand126({
15808
+ var updateCommand3 = defineCommand133({
15399
15809
  meta: {
15400
15810
  name: "update",
15401
15811
  description: "Stage a scheduled action update. Example: baker scheduled-actions update <id> --enabled false"
@@ -15465,7 +15875,7 @@ var updateCommand2 = defineCommand126({
15465
15875
  });
15466
15876
 
15467
15877
  // src/commands/scheduled-actions/index.ts
15468
- var scheduledActionsCommand = defineCommand127({
15878
+ var scheduledActionsCommand = defineCommand134({
15469
15879
  meta: {
15470
15880
  name: "scheduled-actions",
15471
15881
  description: `Manage Scheduled Actions. Subcommands: list, get, create, update, delete, trigger.
@@ -15481,18 +15891,18 @@ Examples:
15481
15891
  baker scheduled-actions trigger <id>`
15482
15892
  },
15483
15893
  subCommands: {
15484
- list: listCommand2,
15485
- get: getCommand3,
15486
- create: createCommand2,
15487
- update: updateCommand2,
15894
+ list: listCommand3,
15895
+ get: getCommand4,
15896
+ create: createCommand3,
15897
+ update: updateCommand3,
15488
15898
  delete: deleteCommand2,
15489
15899
  trigger: triggerCommand
15490
15900
  }
15491
15901
  });
15492
15902
 
15493
15903
  // src/commands/schema.ts
15494
- import { defineCommand as defineCommand128 } from "citty";
15495
- var schemaCommand = defineCommand128({
15904
+ import { defineCommand as defineCommand135 } from "citty";
15905
+ var schemaCommand = defineCommand135({
15496
15906
  meta: {
15497
15907
  name: "schema",
15498
15908
  description: "Inspect command argument schemas (for AI agent introspection). Lists all commands if no argument given. Example: baker schema images.search"
@@ -15528,10 +15938,10 @@ var schemaCommand = defineCommand128({
15528
15938
  });
15529
15939
 
15530
15940
  // src/commands/testimonials/index.ts
15531
- import { defineCommand as defineCommand132 } from "citty";
15941
+ import { defineCommand as defineCommand139 } from "citty";
15532
15942
 
15533
15943
  // src/commands/testimonials/get.ts
15534
- import { defineCommand as defineCommand129 } from "citty";
15944
+ import { defineCommand as defineCommand136 } from "citty";
15535
15945
  registerSchema({
15536
15946
  command: "testimonials.get",
15537
15947
  description: "Get a single testimonial by ID",
@@ -15539,7 +15949,7 @@ registerSchema({
15539
15949
  id: { type: "string", description: "Testimonial ID", required: true }
15540
15950
  }
15541
15951
  });
15542
- var getCommand4 = defineCommand129({
15952
+ var getCommand5 = defineCommand136({
15543
15953
  meta: { name: "get", description: "Get a single testimonial by ID. Example: baker testimonials get j571abc123" },
15544
15954
  args: {
15545
15955
  id: { type: "positional", description: "Testimonial ID", required: false },
@@ -15576,7 +15986,7 @@ var getCommand4 = defineCommand129({
15576
15986
  });
15577
15987
 
15578
15988
  // src/commands/testimonials/list.ts
15579
- import { defineCommand as defineCommand130 } from "citty";
15989
+ import { defineCommand as defineCommand137 } from "citty";
15580
15990
  registerSchema({
15581
15991
  command: "testimonials.list",
15582
15992
  description: "List testimonials with optional filters.",
@@ -15606,7 +16016,7 @@ registerSchema({
15606
16016
  limit: { type: "number", description: "Max results (default 50)", required: false, default: 50 }
15607
16017
  }
15608
16018
  });
15609
- var listCommand3 = defineCommand130({
16019
+ var listCommand4 = defineCommand137({
15610
16020
  meta: {
15611
16021
  name: "list",
15612
16022
  description: "List testimonials with optional filters. Example: baker testimonials list --source google --sentiment positive"
@@ -15655,7 +16065,7 @@ var listCommand3 = defineCommand130({
15655
16065
  });
15656
16066
 
15657
16067
  // src/commands/testimonials/search.ts
15658
- import { defineCommand as defineCommand131 } from "citty";
16068
+ import { defineCommand as defineCommand138 } from "citty";
15659
16069
  registerSchema({
15660
16070
  command: "testimonials.search",
15661
16071
  description: "Search testimonials by text query. Uses hybrid BM25 + vector + reranking.",
@@ -15686,7 +16096,7 @@ registerSchema({
15686
16096
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
15687
16097
  }
15688
16098
  });
15689
- var searchCommand2 = defineCommand131({
16099
+ var searchCommand2 = defineCommand138({
15690
16100
  meta: {
15691
16101
  name: "search",
15692
16102
  description: "Semantic search testimonials by text query. Uses hybrid BM25 + vector + reranking. Example: baker testimonials search 'great service' --rating-min 4"
@@ -15757,7 +16167,7 @@ var searchCommand2 = defineCommand131({
15757
16167
  });
15758
16168
 
15759
16169
  // src/commands/testimonials/index.ts
15760
- var testimonialsCommand = defineCommand132({
16170
+ var testimonialsCommand = defineCommand139({
15761
16171
  meta: {
15762
16172
  name: "testimonials",
15763
16173
  description: `Find and browse testimonials in Baker. Subcommands: search, get, list.
@@ -15769,17 +16179,17 @@ Examples:
15769
16179
  baker testimonials list --source google --sentiment positive`
15770
16180
  },
15771
16181
  subCommands: {
15772
- get: getCommand4,
16182
+ get: getCommand5,
15773
16183
  search: searchCommand2,
15774
- list: listCommand3
16184
+ list: listCommand4
15775
16185
  }
15776
16186
  });
15777
16187
 
15778
16188
  // src/commands/videos/index.ts
15779
- import { defineCommand as defineCommand137 } from "citty";
16189
+ import { defineCommand as defineCommand144 } from "citty";
15780
16190
 
15781
16191
  // src/commands/videos/delete.ts
15782
- import { defineCommand as defineCommand133 } from "citty";
16192
+ import { defineCommand as defineCommand140 } from "citty";
15783
16193
  registerSchema({
15784
16194
  command: "videos.delete",
15785
16195
  description: "Delete a video by ID",
@@ -15793,7 +16203,7 @@ registerSchema({
15793
16203
  }
15794
16204
  }
15795
16205
  });
15796
- var deleteCommand3 = defineCommand133({
16206
+ var deleteCommand3 = defineCommand140({
15797
16207
  meta: {
15798
16208
  name: "delete",
15799
16209
  description: "Delete a video by ID. Use --dry-run to preview. Example: baker videos delete j571abc123 --dry-run"
@@ -15834,7 +16244,7 @@ var deleteCommand3 = defineCommand133({
15834
16244
  });
15835
16245
 
15836
16246
  // src/commands/videos/get.ts
15837
- import { defineCommand as defineCommand134 } from "citty";
16247
+ import { defineCommand as defineCommand141 } from "citty";
15838
16248
  registerSchema({
15839
16249
  command: "videos.get",
15840
16250
  description: "Get a single video by ID",
@@ -15842,7 +16252,7 @@ registerSchema({
15842
16252
  id: { type: "string", description: "Video ID", required: true }
15843
16253
  }
15844
16254
  });
15845
- var getCommand5 = defineCommand134({
16255
+ var getCommand6 = defineCommand141({
15846
16256
  meta: { name: "get", description: "Get a single video by ID. Example: baker videos get j571abc123" },
15847
16257
  args: {
15848
16258
  id: { type: "positional", description: "Video ID", required: false },
@@ -15879,7 +16289,7 @@ var getCommand5 = defineCommand134({
15879
16289
  });
15880
16290
 
15881
16291
  // src/commands/videos/search.ts
15882
- import { defineCommand as defineCommand135 } from "citty";
16292
+ import { defineCommand as defineCommand142 } from "citty";
15883
16293
  registerSchema({
15884
16294
  command: "videos.search",
15885
16295
  description: "Search videos by text query. Only returns ready videos.",
@@ -15889,7 +16299,7 @@ registerSchema({
15889
16299
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
15890
16300
  }
15891
16301
  });
15892
- var searchCommand3 = defineCommand135({
16302
+ var searchCommand3 = defineCommand142({
15893
16303
  meta: {
15894
16304
  name: "search",
15895
16305
  description: "Semantic search videos by text query. Uses hybrid BM25 + vector + reranking. Example: baker videos search 'product demo' --tags tutorial"
@@ -15938,7 +16348,7 @@ var searchCommand3 = defineCommand135({
15938
16348
  // src/commands/videos/upload.ts
15939
16349
  import { readFile as readFile10, stat as stat3 } from "fs/promises";
15940
16350
  import { extname as extname3 } from "path";
15941
- import { defineCommand as defineCommand136 } from "citty";
16351
+ import { defineCommand as defineCommand143 } from "citty";
15942
16352
  var MIME_MAP2 = {
15943
16353
  ".mp4": "video/mp4",
15944
16354
  ".mov": "video/quicktime",
@@ -15972,7 +16382,7 @@ function detectContentType2(filePath) {
15972
16382
  }
15973
16383
  return mime;
15974
16384
  }
15975
- var uploadCommand2 = defineCommand136({
16385
+ var uploadCommand2 = defineCommand143({
15976
16386
  meta: {
15977
16387
  name: "upload",
15978
16388
  description: "Upload a video file to Baker via Mux direct upload. Auto-detects content type. Example: baker videos upload ./demo.mp4"
@@ -16026,7 +16436,7 @@ var uploadCommand2 = defineCommand136({
16026
16436
  });
16027
16437
 
16028
16438
  // src/commands/videos/index.ts
16029
- var videosCommand = defineCommand137({
16439
+ var videosCommand = defineCommand144({
16030
16440
  meta: {
16031
16441
  name: "videos",
16032
16442
  description: `Find and manage videos in Baker. Subcommands: search, get, upload, delete.
@@ -16039,7 +16449,7 @@ Examples:
16039
16449
  baker videos delete <video-id> --dry-run`
16040
16450
  },
16041
16451
  subCommands: {
16042
- get: getCommand5,
16452
+ get: getCommand6,
16043
16453
  search: searchCommand3,
16044
16454
  upload: uploadCommand2,
16045
16455
  delete: deleteCommand3
@@ -16047,10 +16457,10 @@ Examples:
16047
16457
  });
16048
16458
 
16049
16459
  // src/commands/winning-ads/index.ts
16050
- import { defineCommand as defineCommand140 } from "citty";
16460
+ import { defineCommand as defineCommand147 } from "citty";
16051
16461
 
16052
16462
  // src/commands/winning-ads/advertisers.ts
16053
- import { defineCommand as defineCommand138 } from "citty";
16463
+ import { defineCommand as defineCommand145 } from "citty";
16054
16464
  registerSchema({
16055
16465
  command: "winning-ads.advertisers",
16056
16466
  description: "Resolve a brand name to advertiser_id(s) in the ad-dna corpus \u2014 to find your OWN advertiser (to --exclude-advertiser) or a competitor (to --advertiser-id).",
@@ -16063,7 +16473,7 @@ registerSchema({
16063
16473
  function identity(record) {
16064
16474
  return record;
16065
16475
  }
16066
- var advertisersCommand2 = defineCommand138({
16476
+ var advertisersCommand2 = defineCommand145({
16067
16477
  meta: {
16068
16478
  name: "advertisers",
16069
16479
  description: 'Resolve a brand name to advertiser_id(s). Use it to find your own advertiser for --exclude-advertiser, or a competitor for --advertiser-id. Example: baker winning-ads advertisers "Deel" --output md'
@@ -16114,7 +16524,7 @@ var advertisersCommand2 = defineCommand138({
16114
16524
  });
16115
16525
 
16116
16526
  // src/commands/winning-ads/search.ts
16117
- import { defineCommand as defineCommand139 } from "citty";
16527
+ import { defineCommand as defineCommand146 } from "citty";
16118
16528
  registerSchema({
16119
16529
  command: "winning-ads.search",
16120
16530
  description: "Search the ad-dna corpus of scored winning ads. Returns a lean shortlist (advertiser, summary, scores, media_url) to pick a reference to reproduce.",
@@ -16222,7 +16632,7 @@ function buildSearchBody(args) {
16222
16632
  }
16223
16633
  return body;
16224
16634
  }
16225
- var searchCommand4 = defineCommand139({
16635
+ var searchCommand4 = defineCommand146({
16226
16636
  meta: {
16227
16637
  name: "search",
16228
16638
  description: "Search winning reference ads. Example: baker winning-ads search 'B2B SaaS before/after AI automation' --platform meta --format static --winner-category winner --exclude-advertiser adv_123 --output md"
@@ -16334,7 +16744,7 @@ var searchCommand4 = defineCommand139({
16334
16744
  });
16335
16745
 
16336
16746
  // src/commands/winning-ads/index.ts
16337
- var winningAdsCommand = defineCommand140({
16747
+ var winningAdsCommand = defineCommand147({
16338
16748
  meta: {
16339
16749
  name: "winning-ads",
16340
16750
  description: `Search the ad-dna corpus of scored "winning" ads for reference creatives to reproduce. Proxied through the Baker backend (BAKER_API_KEY) \u2014 no separate token needed.
@@ -16374,7 +16784,7 @@ function getCliVersion() {
16374
16784
  }
16375
16785
 
16376
16786
  // src/cli.ts
16377
- var main = defineCommand141({
16787
+ var main = defineCommand148({
16378
16788
  meta: {
16379
16789
  name: "baker",
16380
16790
  version: getCliVersion(),
@@ -16388,6 +16798,7 @@ Introspection: Run 'baker schema <command>' to inspect argument schemas.`
16388
16798
  },
16389
16799
  subCommands: {
16390
16800
  actions: actionsCommand,
16801
+ missions: missionsCommand,
16391
16802
  "scheduled-actions": scheduledActionsCommand,
16392
16803
  ads: adsCommand,
16393
16804
  ga4: ga4Command,