@koda-sl/baker-cli 0.80.0 → 0.82.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,7 +9,7 @@ import {
9
9
  defaultRegistry,
10
10
  generateCatalog,
11
11
  validateCanvasDeep
12
- } from "./chunk-NBNUNCY7.js";
12
+ } from "./chunk-KIL2ZJST.js";
13
13
 
14
14
  // src/cli.ts
15
15
  import { defineCommand as defineCommand141, runMain } from "citty";
@@ -9443,7 +9443,7 @@ function buildMotionBoard(blueprint, sceneTurns) {
9443
9443
  ].sort((a, b) => a.at_s - b.at_s);
9444
9444
  return {
9445
9445
  scene: i,
9446
- role: scene.narrative_role?.trim() || inferNarrativeRole(i, blueprint.scenes.length),
9446
+ role: resolveSceneRole(scene, i, blueprint.scenes.length),
9447
9447
  window_s: [round(start_s), round(end_s)],
9448
9448
  storyboard_frames: [`s${i}_start`, `s${i}_end`],
9449
9449
  spoken,
@@ -9480,22 +9480,44 @@ function inferNarrativeRole(index, total) {
9480
9480
  if (index === total - 1) return "cta";
9481
9481
  return "body";
9482
9482
  }
9483
+ function resolveSceneRole(scene, index, total) {
9484
+ return scene.narrative_role?.trim() || inferNarrativeRole(index, total);
9485
+ }
9486
+ function findHookSceneIndex(blueprint) {
9487
+ const total = blueprint.scenes.length;
9488
+ return blueprint.scenes.findIndex((s, i) => resolveSceneRole(s, i, total) === "hook");
9489
+ }
9490
+ var HOOK_OPENER_CRITERIA = "statement not question; benefit by ~2s; first frame legible SOUND-OFF in ~1s (the feed plays muted); no bait-and-switch (the hook sets up the body)";
9491
+ function recraftTextForRole(role, sceneIndex) {
9492
+ if (role === "hook") {
9493
+ return `[RECRAFT HOOK \u2014 the #1 decision, but usually the LIGHTEST move wins: this hook already worked, so default to KEEPING it and building on top (swap only the specifics for ours). REBUILD only when it doesn't transfer \u2014 a claim we lack or a different funnel/awareness stage \u2014 by reaching for its deeper INNER MECHANIC and delivering that truthfully. DIAGNOSE (device + mechanic + stage) \u2192 DECIDE keep/adapt/rebuild \u2192 hold the opener to criteria: ${HOOK_OPENER_CRITERIA}. It lives on s${sceneIndex}_start (first frame) + the scene-${sceneIndex} overlay. See references/hook-craft.md + meta-ads-playbook \xA710/\xA739.]`;
9494
+ }
9495
+ if (role === "cta") {
9496
+ return "[RECRAFT CTA \u2014 the close: state what to do (where to go) + what to expect (offer/bundle/discount) for OUR brand; soft for top-of-funnel, hard for bottom. Do NOT render the reference's words. See references/script-craft.md.]";
9497
+ }
9498
+ return `[RECRAFT: rewrite this ${role} for OUR brand \u2014 true claims only; do NOT render the reference's words. See references/script-craft.md + meta-ads-playbook.]`;
9499
+ }
9483
9500
  function buildScriptRecraft(blueprint) {
9484
9501
  const total = blueprint.scenes.length;
9485
9502
  return blueprint.scenes.map((scene, i) => {
9486
- const role = scene.narrative_role?.trim() || inferNarrativeRole(i, total);
9503
+ const role = resolveSceneRole(scene, i, total);
9487
9504
  const original = (scene.dialogue ?? []).map((d) => d.line?.trim()).filter((l) => Boolean(l)).join(" ");
9488
9505
  return {
9489
9506
  scene: i,
9490
9507
  role,
9491
9508
  original_line: original || null,
9492
- recraft: `[RECRAFT: rewrite this ${role} for OUR brand \u2014 true claims only; do NOT render the reference's words. See references/script-craft.md + meta-ads-playbook.]`
9509
+ recraft: recraftTextForRole(role, i)
9493
9510
  };
9494
9511
  });
9495
9512
  }
9496
9513
  function buildVideoTodo(report, overlayCount, floatingCount, opts, blueprint) {
9514
+ const hookSceneIndex = findHookSceneIndex(blueprint);
9515
+ const h = hookSceneIndex;
9497
9516
  return {
9498
- recraft_the_script_first: "VIDEO IS INSPIRATION, NOT A CLONE. Before rendering, re-craft the script (hook \u2192 body \u2192 CTA) for OUR brand \u2014 the reference already won in-market, but its hook is targeting and may not transfer. Work the per-scene `script_recraft` checklist below; see references/script-craft.md + the meta-ads-playbook skill.",
9517
+ recraft_the_script_first: `VIDEO IS INSPIRATION, NOT A CLONE. Before rendering, re-craft the script (hook \u2192 body \u2192 CTA) for OUR brand \u2014 the reference already won in-market, but its hook is targeting and may not transfer.${h >= 0 ? " The HOOK is the #1 decision (see the `hook` todo);" : ""} ${h >= 0 ? "then work" : "Work"} the per-scene \`script_recraft\` checklist. References: references/hook-craft.md (the hook), references/script-craft.md (body/CTA) + the meta-ads-playbook skill.`,
9518
+ ...h >= 0 ? {
9519
+ hook: `THE HOOK IS THE HIGHEST-LEVERAGE BEAT \u2014 the first frame + first 3\u20134s decide whether the ad is watched at all, and the hook is TARGETING. But highest-leverage does NOT mean always rewrite: this hook already won, so MOST OF THE TIME you KEEP it and build on top (swap only the specifics). REBUILD is the exception \u2014 only when it doesn't transfer (a claim we lack or a different funnel/awareness stage), and then by reaching for its deeper INNER MECHANIC and delivering that truthfully, not inventing a new opener from nothing. For scene ${h}: DIAGNOSE it (device + mechanic + what stage it targets), DECIDE keep/adapt/rebuild, then hold the opener to the criteria \u2014 ${HOOK_OPENER_CRITERIA}. The hook lives across s${h}_start (the scroll-stopping first frame), the scene-${h} overlay text, the s${h}_clip line, an optional ~0.5s micro-hook, and the ramp into the body. Full diagnose\u2192decide\u2192(keep/adapt/rebuild) discipline + the proven hook-type menu: references/hook-craft.md (+ meta-ads-playbook \xA710/\xA717/\xA739).`
9520
+ } : {},
9499
9521
  script_recraft: buildScriptRecraft(blueprint),
9500
9522
  edit_frames_in_place: "Each s<i>_start / s<i>_end node has its own editable params.prompt (FRAME DESCRIPTION). Edit per frame; the blueprint is only a shared style reference. Frames are CLEAN PLATES \u2014 they render no on-screen text; all text is the overlay HTML layer.",
9501
9523
  frames_mode: opts.frames ?? "generate",
@@ -9527,7 +9549,7 @@ function buildVideoTodo(report, overlayCount, floatingCount, opts, blueprint) {
9527
9549
  "Nail the PACK SHOT \u2014 the final product hero sells (motivated camera move, light matched to the rest of the ad).",
9528
9550
  "Geometry/objects drifting frame to frame? A MAP beats a paragraph \u2014 drop a high-angle schematic still (marked positions) as an extra el_*/reference rather than adding more words.",
9529
9551
  "Before/after (dry\u2192wet, skeptic\u2192believer) = two SEPARATE locked reference images \u2014 don't ask words to transform a subject mid-scene (cheaper and more reliable than re-rolling clips).",
9530
- "The hook is VISUAL-FIRST: the feed plays muted, so scene 1's opening frame + its overlay text must read sound-off in ~1 second \u2014 don't bury the hook in the spoken line alone (meta-ads-playbook \xA739 visual-hooks-beat-audio, \xA748 the 1-second/feed-native rule)."
9552
+ "The hook is VISUAL-FIRST: the feed plays muted, so the hook scene's opening frame + its overlay text must read sound-off in ~1 second \u2014 don't bury the hook in the spoken line alone (meta-ads-playbook \xA739 visual-hooks-beat-audio, \xA748 the 1-second/feed-native rule). The hook is also the #1 re-craft decision \u2014 diagnose/decide/rebuild it against criteria in references/hook-craft.md (see the `hook` todo)."
9531
9553
  ]
9532
9554
  },
9533
9555
  transitions: "Scene-to-scene cuts the deconstruct flagged as fade/whip/zoom/dissolve/swipe are reproduced as an ffmpeg xfade at the boundary (everything else stays a hard cut). The overlap is consumed from extra generated footage, so the picture stays exactly on the audio timeline. To change a transition, edit the scene's `transition_out.type` in prompt.json and re-scaffold, or hand-edit the `spine` node's ffmpeg args. For a richer HERO cut (whip-pan, glitch, light-leak, gravitational-lens\u2026), the overlay layer can run a Hyperframes shader/CSS transition instead \u2014 see references/hyperframes/blueprints-and-transitions.md (pick 2\u20133 transition types total; the motion IS the handoff).",
@@ -9664,7 +9686,11 @@ function fail2(code, message) {
9664
9686
  function resolveModels2(args) {
9665
9687
  const pick = (flag, kind, fallback) => args[flag] ? String(args[flag]) : resolveModel2(kind, fallback);
9666
9688
  return {
9667
- deconstructModel: pick("deconstruct-model", "video_deconstruct", "~google/gemini-pro-latest"),
9689
+ // Flash by default: ~2- faster per call than Pro, which keeps each
9690
+ // per-scene deconstruct step well inside the action time budget and turns a
9691
+ // ~9 min run into ~3-4 min. Override with --deconstruct-model
9692
+ // ~google/gemini-pro-latest for the densest extraction when speed matters less.
9693
+ deconstructModel: pick("deconstruct-model", "video_deconstruct", "~google/gemini-flash-latest"),
9668
9694
  selectModel: pick("select-model", "text_generate", "~google/gemini-flash-latest"),
9669
9695
  // Default to the strongest image model (matches the static-ad scaffold); the
9670
9696
  // frame generators need the most faithful text/identity reproduction. Override