@koda-sl/baker-cli 0.138.0 → 0.139.0-dev.da48e17cb

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
@@ -34,7 +34,7 @@ import {
34
34
  toModelSafeImage,
35
35
  ulid,
36
36
  validateCanvasDeep
37
- } from "./chunk-TAGQCU36.js";
37
+ } from "./chunk-TXNOPO6P.js";
38
38
  import {
39
39
  csvOrJson,
40
40
  daysAgoIso,
@@ -73,7 +73,7 @@ import {
73
73
  } from "./chunk-RK67WL4O.js";
74
74
 
75
75
  // src/cli.ts
76
- import { defineCommand as defineCommand174, runMain } from "citty";
76
+ import { defineCommand as defineCommand176, runMain } from "citty";
77
77
 
78
78
  // src/commands/actions/index.ts
79
79
  import { defineCommand as defineCommand18 } from "citty";
@@ -5455,11 +5455,11 @@ function microsFlag(value, flag) {
5455
5455
  if (value === void 0 || value === null || value === "") {
5456
5456
  return void 0;
5457
5457
  }
5458
- const num = Number(value);
5459
- if (Number.isNaN(num) || num <= 0) {
5458
+ const num2 = Number(value);
5459
+ if (Number.isNaN(num2) || num2 <= 0) {
5460
5460
  failWriteValidation(`${flag} must be a positive amount (major currency units, e.g. 50 or 50.00)`);
5461
5461
  }
5462
- return toMicros(num);
5462
+ return toMicros(num2);
5463
5463
  }
5464
5464
  function listFlag(value) {
5465
5465
  if (typeof value !== "string" || value.length === 0) {
@@ -5487,11 +5487,11 @@ function rawTextEntries(value) {
5487
5487
  const values = Array.isArray(value) ? value : typeof value === "string" ? [value] : [];
5488
5488
  return values.filter((v) => typeof v === "string").flatMap((v) => v.split(",")).map((v) => v.trim()).filter(Boolean);
5489
5489
  }
5490
- function rawFileEntries(path23) {
5491
- if (typeof path23 !== "string" || path23.length === 0) {
5490
+ function rawFileEntries(path27) {
5491
+ if (typeof path27 !== "string" || path27.length === 0) {
5492
5492
  return [];
5493
5493
  }
5494
- return readFileSync2(path23, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
5494
+ return readFileSync2(path27, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
5495
5495
  }
5496
5496
  function keywordEntries(args) {
5497
5497
  const defaultMatch = typeof args["match-type"] === "string" ? args["match-type"].toUpperCase() : void 0;
@@ -5514,19 +5514,19 @@ function keywordEntries(args) {
5514
5514
  }
5515
5515
  return entries;
5516
5516
  }
5517
- function loadJsonFileArg(path23) {
5518
- if (typeof path23 !== "string" || path23.length === 0) {
5517
+ function loadJsonFileArg(path27) {
5518
+ if (typeof path27 !== "string" || path27.length === 0) {
5519
5519
  return {};
5520
5520
  }
5521
5521
  try {
5522
- const parsed = JSON.parse(readFileSync2(path23, "utf8"));
5522
+ const parsed = JSON.parse(readFileSync2(path27, "utf8"));
5523
5523
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
5524
- failWriteValidation(`${path23} must contain a JSON object`);
5524
+ failWriteValidation(`${path27} must contain a JSON object`);
5525
5525
  }
5526
5526
  return parsed;
5527
5527
  } catch (err) {
5528
5528
  if (err instanceof SyntaxError) {
5529
- failWriteValidation(`${path23} is not valid JSON: ${err.message}`);
5529
+ failWriteValidation(`${path27} is not valid JSON: ${err.message}`);
5530
5530
  }
5531
5531
  throw err;
5532
5532
  }
@@ -5637,10 +5637,10 @@ async function stageUpdate(kind, customerId, target, payload) {
5637
5637
  async function stageTarget(kind, customerId, target) {
5638
5638
  await stageGoogleOp({ kind, customerId, target });
5639
5639
  }
5640
- async function draftAction(path23, body) {
5640
+ async function draftAction(path27, body) {
5641
5641
  try {
5642
5642
  const chatId = requireChatId();
5643
- const response = await apiPost(path23, { chatId, ...body });
5643
+ const response = await apiPost(path27, { chatId, ...body });
5644
5644
  writeJsonEnvelope(response);
5645
5645
  } catch (err) {
5646
5646
  handleGoogleError(err);
@@ -9203,19 +9203,19 @@ function failWriteValidation2(message) {
9203
9203
  writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
9204
9204
  process.exit(1);
9205
9205
  }
9206
- function loadJsonFileArg2(path23) {
9207
- if (typeof path23 !== "string" || path23.length === 0) {
9206
+ function loadJsonFileArg2(path27) {
9207
+ if (typeof path27 !== "string" || path27.length === 0) {
9208
9208
  return {};
9209
9209
  }
9210
9210
  try {
9211
- const parsed = JSON.parse(readFileSync6(path23, "utf8"));
9211
+ const parsed = JSON.parse(readFileSync6(path27, "utf8"));
9212
9212
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
9213
- failWriteValidation2(`${path23} must contain a JSON object`);
9213
+ failWriteValidation2(`${path27} must contain a JSON object`);
9214
9214
  }
9215
9215
  return parsed;
9216
9216
  } catch (err) {
9217
9217
  if (err instanceof SyntaxError) {
9218
- failWriteValidation2(`${path23} is not valid JSON: ${err.message}`);
9218
+ failWriteValidation2(`${path27} is not valid JSON: ${err.message}`);
9219
9219
  }
9220
9220
  throw err;
9221
9221
  }
@@ -9300,15 +9300,15 @@ function parseLocaleFlag(value) {
9300
9300
  }
9301
9301
  return { language: match[1], country: match[2].toUpperCase() };
9302
9302
  }
9303
- function loadTargetingFileArg(path23) {
9304
- if (typeof path23 !== "string" || path23.length === 0) {
9303
+ function loadTargetingFileArg(path27) {
9304
+ if (typeof path27 !== "string" || path27.length === 0) {
9305
9305
  return void 0;
9306
9306
  }
9307
- const parsed = loadJsonFileArg2(path23);
9307
+ const parsed = loadJsonFileArg2(path27);
9308
9308
  const criteria = parsed.targetingCriteria ?? parsed;
9309
9309
  if (!criteria.include) {
9310
9310
  failWriteValidation2(
9311
- `${path23} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
9311
+ `${path27} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
9312
9312
  );
9313
9313
  }
9314
9314
  return criteria;
@@ -9343,14 +9343,14 @@ function parseCsvLine(line) {
9343
9343
  cells.push(current);
9344
9344
  return cells.map((cell) => cell.trim());
9345
9345
  }
9346
- function parseListFileArg(path23, maxRows) {
9347
- if (typeof path23 !== "string" || path23.length === 0) {
9346
+ function parseListFileArg(path27, maxRows) {
9347
+ if (typeof path27 !== "string" || path27.length === 0) {
9348
9348
  return void 0;
9349
9349
  }
9350
- const raw = readFileSync6(path23, "utf8");
9350
+ const raw = readFileSync6(path27, "utf8");
9351
9351
  const lines = raw.split(/\r?\n/).filter((line) => line.trim().length > 0);
9352
9352
  if (lines.length < 2) {
9353
- failWriteValidation2(`${path23} needs a header row and at least one data row`);
9353
+ failWriteValidation2(`${path27} needs a header row and at least one data row`);
9354
9354
  }
9355
9355
  const columns = parseCsvLine(lines[0]).map((column) => column.trim());
9356
9356
  const rows = [];
@@ -9369,7 +9369,7 @@ function parseListFileArg(path23, maxRows) {
9369
9369
  }
9370
9370
  }
9371
9371
  if (rows.length > maxRows) {
9372
- failWriteValidation2(`${path23} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
9372
+ failWriteValidation2(`${path27} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
9373
9373
  }
9374
9374
  return { columns, rows };
9375
9375
  }
@@ -11452,11 +11452,11 @@ var updateStatusSchema = z14.enum(UPDATE_STATUSES);
11452
11452
  function currencyMinimums2(currencyCode) {
11453
11453
  return CURRENCY_MINIMUMS2[currencyCode] ?? DEFAULT_CURRENCY_MINIMUM2;
11454
11454
  }
11455
- function validateDailyBudgetFloor(money, ctx, path23) {
11455
+ function validateDailyBudgetFloor(money, ctx, path27) {
11456
11456
  if (money?.currencyCode) {
11457
11457
  const min = currencyMinimums2(money.currencyCode).dailyBudgetMin;
11458
11458
  if (Number(money.amount) < min) {
11459
- ctx.addIssue({ code: "custom", path: path23, message: `below the ${min} ${money.currencyCode} daily minimum` });
11459
+ ctx.addIssue({ code: "custom", path: path27, message: `below the ${min} ${money.currencyCode} daily minimum` });
11460
11460
  }
11461
11461
  }
11462
11462
  }
@@ -11944,19 +11944,19 @@ function failWriteValidation3(message) {
11944
11944
  writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
11945
11945
  process.exit(1);
11946
11946
  }
11947
- function loadJsonFileArg3(path23) {
11948
- if (typeof path23 !== "string" || path23.length === 0) {
11947
+ function loadJsonFileArg3(path27) {
11948
+ if (typeof path27 !== "string" || path27.length === 0) {
11949
11949
  return {};
11950
11950
  }
11951
11951
  try {
11952
- const parsed = JSON.parse(readFileSync8(path23, "utf8"));
11952
+ const parsed = JSON.parse(readFileSync8(path27, "utf8"));
11953
11953
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
11954
- failWriteValidation3(`${path23} must contain a JSON object`);
11954
+ failWriteValidation3(`${path27} must contain a JSON object`);
11955
11955
  }
11956
11956
  return parsed;
11957
11957
  } catch (err) {
11958
11958
  if (err instanceof SyntaxError) {
11959
- failWriteValidation3(`${path23} is not valid JSON: ${err.message}`);
11959
+ failWriteValidation3(`${path27} is not valid JSON: ${err.message}`);
11960
11960
  }
11961
11961
  throw err;
11962
11962
  }
@@ -16123,13 +16123,13 @@ function referenceRank(type) {
16123
16123
  function orderSlotsByIdentity(slots) {
16124
16124
  return [...slots].sort((a, b) => referenceRank(a.type) - referenceRank(b.type));
16125
16125
  }
16126
- function buildFramePrompt(edge, sceneIndex, framePrompt, present, hasAnchor, mode, sceneStyle, imageModel) {
16126
+ function buildFramePrompt(edge, sceneIndex, framePrompt, present2, hasAnchor, mode, sceneStyle, imageModel) {
16127
16127
  const profile = imageModel ? imageProfileFor(imageModel) : void 0;
16128
16128
  const exclusionsLast = profile?.constraintPlacement === "last";
16129
16129
  const photoreal = profile ? profile.photorealCue : true;
16130
16130
  const EDGE = edge.toUpperCase();
16131
16131
  const legend = [
16132
- ...present.map((s) => `- ${s.label} \u2014 ${roleForSlot(s)}`),
16132
+ ...present2.map((s) => `- ${s.label} \u2014 ${roleForSlot(s)}`),
16133
16133
  ...hasAnchor ? [
16134
16134
  "- ORIGINAL_FRAME \u2014 use ONLY for composition: framing, camera angle, shot size, subject placement, pose, and proportions. IGNORE its text, logo, brand name, colors, AND the identity of every person/animal/object in it \u2014 those come from the labeled reference images above, never from this frame. It is a DIFFERENT brand's footage with DIFFERENT actors, here ONLY to anchor where things sit and how the shot is framed (e.g. a profile/side angle stays a profile/side angle), never who they are or what palette to use."
16135
16135
  ] : []
@@ -16187,7 +16187,7 @@ function buildFramePrompt(edge, sceneIndex, framePrompt, present, hasAnchor, mod
16187
16187
  // RECAST is the whole point of a transform: the dropped el_* images define who/
16188
16188
  // what is on screen, NOT the source footage and NOT the prose. Without this, the
16189
16189
  // model reproduces the original ad's people (a proven failure mode).
16190
- ...present.length > 0 ? [
16190
+ ...present2.length > 0 ? [
16191
16191
  "IDENTITY & AESTHETIC \u2014 RECAST (this is a transform, not a copy):",
16192
16192
  "Identity comes from the reference image, never from the source footage or this prose. Render every",
16193
16193
  "person, animal, product, and set to MATCH its labeled reference image above \u2014 that image is the ONLY",
@@ -16242,17 +16242,17 @@ function ingestFrameRef(url, edge, ctx, nodes) {
16242
16242
  ctx.ingestCache?.set(url, ref);
16243
16243
  return ref;
16244
16244
  }
16245
- function buildFrameRef(edge, url, framePrompt, present, ctx, nodes) {
16245
+ function buildFrameRef(edge, url, framePrompt, present2, ctx, nodes) {
16246
16246
  const tag = ctx.tag ?? "";
16247
16247
  if (ctx.reuse && url) return ingestFrameRef(url, edge, ctx, nodes);
16248
- const castSlots = present.filter((s) => {
16248
+ const castSlots = present2.filter((s) => {
16249
16249
  const t = s.type.toLowerCase();
16250
16250
  return t === "person" || t === "animal";
16251
16251
  });
16252
16252
  const useOriginalAnchor = Boolean(url) && castSlots.length === 0;
16253
16253
  const hasOriginal = useOriginalAnchor;
16254
16254
  const originalRef = useOriginalAnchor && url ? ingestFrameRef(url, edge, ctx, nodes) : void 0;
16255
- const presentOrdered = orderSlotsByIdentity(present);
16255
+ const presentOrdered = orderSlotsByIdentity(present2);
16256
16256
  const reference = [...presentOrdered.map((s) => s.ref), ...originalRef ? [originalRef] : []];
16257
16257
  const imageProfile = imageProfileFor(ctx.imageModel);
16258
16258
  const genParams = {
@@ -16388,13 +16388,13 @@ function spokenTextParts(scene, nativeLine, loc, profile) {
16388
16388
  if (transcript) parts.push(`Transcript: ${loc(transcript)}`);
16389
16389
  return parts;
16390
16390
  }
16391
- function buildSeedancePrompt(scene, sceneIndex, present, mode, audio, nativeLine, nativeLang, uiRouted, videoModel) {
16391
+ function buildSeedancePrompt(scene, sceneIndex, present2, mode, audio, nativeLine, nativeLang, uiRouted, videoModel) {
16392
16392
  const profile = (videoModel ? clipProfileFor(videoModel) : void 0) ?? SEEDANCE_PROFILE;
16393
16393
  const loc = (s) => nativeLine ? localizeNumeralsForNative(s, nativeLang) : s;
16394
16394
  const routed = uiRouted ?? (compositeRegionsOf(scene) !== null && isUiOnlyComposite(compositeRegionsOf(scene) ?? []));
16395
16395
  const plate = routed ? compositePlateRegion(scene) ?? {} : null;
16396
16396
  const parts = visualBriefParts(scene, sceneIndex, plate);
16397
- const refs = plate ? present.filter((s) => !UI_SURFACE_RE.test(s.description ?? "")) : present;
16397
+ const refs = plate ? present2.filter((s) => !UI_SURFACE_RE.test(s.description ?? "")) : present2;
16398
16398
  if (refs.length > 0) {
16399
16399
  parts.push(
16400
16400
  `Keep these consistent with their references: ${refs.map((s) => `${s.label} (${s.description ?? s.type})`).join("; ")}`
@@ -16433,15 +16433,15 @@ function sceneOutTransition(scene, isLast) {
16433
16433
  const xfade = type ? XFADE_BY_TYPE[type] : void 0;
16434
16434
  return xfade ? { xfade, dur: TRANSITION_DEFAULT_S } : null;
16435
16435
  }
16436
- function sceneShootMode(scene, present, nativeTurn, cameraOn, casts) {
16436
+ function sceneShootMode(scene, present2, nativeTurn, cameraOn, casts) {
16437
16437
  const talking = Boolean(nativeTurn) || cameraOn && (scene.dialogue ?? []).some(
16438
16438
  (d) => d.line?.trim() && isOnCameraSpeaker(d.speaker ?? "voiceover", casts, cameraOn)
16439
16439
  );
16440
16440
  return deriveShootMode({
16441
16441
  explicit: scene.shoot_mode,
16442
16442
  talking,
16443
- hasPerson: present.some((s) => s.type.toLowerCase() === "person" || s.type.toLowerCase() === "animal"),
16444
- hasProduct: present.some((s) => s.type.toLowerCase() === "product")
16443
+ hasPerson: present2.some((s) => s.type.toLowerCase() === "person" || s.type.toLowerCase() === "animal"),
16444
+ hasProduct: present2.some((s) => s.type.toLowerCase() === "product")
16445
16445
  });
16446
16446
  }
16447
16447
  function emitSceneNativeAudio(i, nativeTurn, ambientBroll, lengths, clock, nodes, voTracks, nativeSegments, clipRef) {
@@ -16485,12 +16485,12 @@ function sameSpeakerTrackEntries(segments) {
16485
16485
  return sorted.map((s, k) => {
16486
16486
  const next = sorted[k + 1];
16487
16487
  const naturalLen = s.end_s - s.start_s;
16488
- const cap2 = next ? Math.min(s.end_s, next.start_s) - s.start_s : naturalLen;
16489
- const capped = cap2 < naturalLen - 1e-3;
16488
+ const cap3 = next ? Math.min(s.end_s, next.start_s) - s.start_s : naturalLen;
16489
+ const capped = cap3 < naturalLen - 1e-3;
16490
16490
  return {
16491
16491
  slot: `seg${k}`,
16492
16492
  start_s: s.start_s,
16493
- ...capped ? { duration_s: Math.round(Math.max(0.1, cap2) * 1e3) / 1e3 } : {}
16493
+ ...capped ? { duration_s: Math.round(Math.max(0.1, cap3) * 1e3) / 1e3 } : {}
16494
16494
  };
16495
16495
  });
16496
16496
  }
@@ -16529,7 +16529,7 @@ function buildPerSpeakerVoiceConversion(segments, totalMs, nodes) {
16529
16529
  }
16530
16530
  return tracks;
16531
16531
  }
16532
- function emitSceneClip(i, scene, present, mode, nativeTurn, ambientBroll, frames, lengths, out, opts, nodes, tag = "", defer = false) {
16532
+ function emitSceneClip(i, scene, present2, mode, nativeTurn, ambientBroll, frames, lengths, out, opts, nodes, tag = "", defer = false) {
16533
16533
  const profile = clipProfileFor(opts.videoModel) ?? SEEDANCE_PROFILE;
16534
16534
  const clipParams = {
16535
16535
  model: opts.videoModel,
@@ -16539,7 +16539,7 @@ function emitSceneClip(i, scene, present, mode, nativeTurn, ambientBroll, frames
16539
16539
  prompt: buildSeedancePrompt(
16540
16540
  scene,
16541
16541
  i,
16542
- present,
16542
+ present2,
16543
16543
  mode,
16544
16544
  Boolean(nativeTurn) || ambientBroll,
16545
16545
  nativeTurn?.text,
@@ -16638,9 +16638,9 @@ function uiRoutedRuns(input) {
16638
16638
  flushRun();
16639
16639
  return runs;
16640
16640
  }
16641
- function sceneIsFullScreenUi(scene, present) {
16641
+ function sceneIsFullScreenUi(scene, present2) {
16642
16642
  if (scene.narrative_role?.trim() === "cta") return false;
16643
- const hasCast = present.some((s) => {
16643
+ const hasCast = present2.some((s) => {
16644
16644
  const t = s.type.toLowerCase();
16645
16645
  return t === "person" || t === "animal";
16646
16646
  });
@@ -16675,14 +16675,14 @@ function presenterIndexOf(regions, hasNative) {
16675
16675
  if (flagged >= 0) return flagged;
16676
16676
  return hasNative ? 0 : -1;
16677
16677
  }
16678
- function slotsForRegion(present, isPresenter) {
16679
- return present.filter((s) => {
16678
+ function slotsForRegion(present2, isPresenter) {
16679
+ return present2.filter((s) => {
16680
16680
  const t = s.type.toLowerCase();
16681
16681
  const person = t === "person" || t === "animal";
16682
16682
  return isPresenter ? person : !person;
16683
16683
  });
16684
16684
  }
16685
- function buildCompositeScene(layout, regions, comp, scene, i, present, mode, nativeTurn, lengths, out, opts, nodes) {
16685
+ function buildCompositeScene(layout, regions, comp, scene, i, present2, mode, nativeTurn, lengths, out, opts, nodes) {
16686
16686
  const dims = canvasDims(opts.outAr);
16687
16687
  const presIdx = presenterIndexOf(regions, Boolean(nativeTurn));
16688
16688
  const regionRefs = [];
@@ -16690,7 +16690,7 @@ function buildCompositeScene(layout, regions, comp, scene, i, present, mode, nat
16690
16690
  regions.forEach((region, r) => {
16691
16691
  const isPresenter = r === presIdx;
16692
16692
  const tag = `_r${r}`;
16693
- const regionSlots = slotsForRegion(present, isPresenter);
16693
+ const regionSlots = slotsForRegion(present2, isPresenter);
16694
16694
  const ctx = {
16695
16695
  sceneIndex: i,
16696
16696
  genAr: opts.genAr,
@@ -16790,14 +16790,14 @@ function sceneTiming(scene, isLast, nativeTurn, durationSet = SEEDANCE_DURATIONS
16790
16790
  const genDur = ceilToDurations(durationSet, Math.max(trimTarget, speech));
16791
16791
  return { dur, out, trimTarget, genDur, speech };
16792
16792
  }
16793
- function emitCompositeScene(composite, scene, i, present, mode, nativeTurn, lengths, out, opts, clock, nodes, voTracks, nativeSegments, clips) {
16793
+ function emitCompositeScene(composite, scene, i, present2, mode, nativeTurn, lengths, out, opts, clock, nodes, voTracks, nativeSegments, clips) {
16794
16794
  const built = buildCompositeScene(
16795
16795
  composite.layout,
16796
16796
  composite.regions,
16797
16797
  composite.comp,
16798
16798
  scene,
16799
16799
  i,
16800
- present,
16800
+ present2,
16801
16801
  mode,
16802
16802
  nativeTurn,
16803
16803
  { dur: lengths.dur, trimTarget: lengths.trimTarget, genDur: lengths.genDur },
@@ -16902,9 +16902,9 @@ function emitGraphicScene(i, scene, lengths, out, surfaceIngests, nodes, clips)
16902
16902
  clips.push({ ref: `$ref:${refId}.asset`, scene_s: lengths.dur, out, still: { fit: "pad" } });
16903
16903
  }
16904
16904
  var BRAND_CARD_RE = /\b(?:solid|plain|flat|brand|logo|wordmark|end[- ]?card|cta card|title card|colou?r background|background colou?r)\b/i;
16905
- function sceneIsBrandCard(scene, present, isCta) {
16905
+ function sceneIsBrandCard(scene, present2, isCta) {
16906
16906
  if (!isCta) return false;
16907
- const hasCast = present.some((s) => {
16907
+ const hasCast = present2.some((s) => {
16908
16908
  const t = s.type.toLowerCase();
16909
16909
  return t === "person" || t === "animal";
16910
16910
  });
@@ -17240,7 +17240,7 @@ function emitPhraseClip(phrase, voiceNode, env, nodes, out) {
17240
17240
  const anchor = phrase.shownScenes[0];
17241
17241
  const anchorScene = env.blueprint.scenes[anchor];
17242
17242
  if (!anchorScene) return;
17243
- const present = slotsForScene(env.slots, anchor);
17243
+ const present2 = slotsForScene(env.slots, anchor);
17244
17244
  const nativeTurn = {
17245
17245
  sceneIndex: anchor,
17246
17246
  speaker: phrase.speaker,
@@ -17250,7 +17250,7 @@ function emitPhraseClip(phrase, voiceNode, env, nodes, out) {
17250
17250
  voiceNode,
17251
17251
  native: true
17252
17252
  };
17253
- const mode = sceneShootMode(anchorScene, present, nativeTurn, env.cameraOn, env.casts);
17253
+ const mode = sceneShootMode(anchorScene, present2, nativeTurn, env.cameraOn, env.casts);
17254
17254
  const ctx = {
17255
17255
  sceneIndex: anchor,
17256
17256
  genAr: env.genAr,
@@ -17293,7 +17293,7 @@ function emitPhraseClip(phrase, voiceNode, env, nodes, out) {
17293
17293
  prompt: buildSeedancePrompt(
17294
17294
  anchorScene,
17295
17295
  anchor,
17296
- present,
17296
+ present2,
17297
17297
  mode,
17298
17298
  true,
17299
17299
  phrase.text,
@@ -17410,7 +17410,7 @@ function emitPhraseTts(phrase, voiceNode, idx, used, clock, nodes, out, language
17410
17410
  });
17411
17411
  }
17412
17412
  function emitCompositeInTimeline(composite, scene, i, isLast, env, canonical, ensureVoiceNode, usedVoIds, nodes, out) {
17413
- const present = slotsForScene(env.slots, i);
17413
+ const present2 = slotsForScene(env.slots, i);
17414
17414
  const onCam = (scene.dialogue ?? []).filter(
17415
17415
  (l) => Boolean(l.line?.trim()) && isOnCameraSpeaker(l.speaker ?? "voiceover", env.casts, env.cameraOn)
17416
17416
  );
@@ -17431,13 +17431,13 @@ function emitCompositeInTimeline(composite, scene, i, isLast, env, canonical, en
17431
17431
  speech_words: wordCount(text)
17432
17432
  });
17433
17433
  }
17434
- const mode = sceneShootMode(scene, present, nativeTurn, env.cameraOn, env.casts);
17434
+ const mode = sceneShootMode(scene, present2, nativeTurn, env.cameraOn, env.casts);
17435
17435
  const lengths = sceneTiming(scene, isLast, nativeTurn, clipProfileFor(env.opts.videoModel)?.durationSet);
17436
17436
  emitCompositeScene(
17437
17437
  composite,
17438
17438
  scene,
17439
17439
  i,
17440
- present,
17440
+ present2,
17441
17441
  mode,
17442
17442
  nativeTurn,
17443
17443
  lengths,
@@ -17532,8 +17532,8 @@ function brollKeyframes(scene, i, env, ctx, lengths, prevEndFrame, nodes) {
17532
17532
  return { first, last, sharesPrevFrame };
17533
17533
  }
17534
17534
  function emitBrollScene(scene, i, isLast, env, nodes, out, prevEndFrame) {
17535
- const present = slotsForScene(env.slots, i);
17536
- const mode = sceneShootMode(scene, present, void 0, env.cameraOn, env.casts);
17535
+ const present2 = slotsForScene(env.slots, i);
17536
+ const mode = sceneShootMode(scene, present2, void 0, env.cameraOn, env.casts);
17537
17537
  const ambientBroll = Boolean(env.opts.ambient) && mode !== "ugc_selfie";
17538
17538
  const lengths = sceneTiming(scene, isLast, void 0, clipProfileFor(env.opts.videoModel)?.durationSet);
17539
17539
  const ctx = {
@@ -17550,13 +17550,13 @@ function emitBrollScene(scene, i, isLast, env, nodes, out, prevEndFrame) {
17550
17550
  out.routes.set(i, "graphic_plate");
17551
17551
  return void 0;
17552
17552
  }
17553
- if (!env.reuse && sceneIsFullScreenUi(scene, present)) {
17553
+ if (!env.reuse && sceneIsFullScreenUi(scene, present2)) {
17554
17554
  emitScreenScene(i, scene, lengths, lengths.out, env.surfaceIngests, nodes, out.clips);
17555
17555
  out.routes.set(i, "screen_still");
17556
17556
  return void 0;
17557
17557
  }
17558
17558
  const isCta = scene.narrative_role?.trim() === "cta" || isLast;
17559
- if (!env.reuse && sceneIsBrandCard(scene, present, isCta)) {
17559
+ if (!env.reuse && sceneIsBrandCard(scene, present2, isCta)) {
17560
17560
  emitBrandCardScene(i, lengths, lengths.out, env.outAr, brandPlateColor(env.blueprint), nodes, out.clips);
17561
17561
  out.routes.set(i, "brand_card");
17562
17562
  return void 0;
@@ -17571,7 +17571,7 @@ function emitBrollScene(scene, i, isLast, env, nodes, out, prevEndFrame) {
17571
17571
  const clip = emitSceneClip(
17572
17572
  i,
17573
17573
  scene,
17574
- present,
17574
+ present2,
17575
17575
  mode,
17576
17576
  void 0,
17577
17577
  ambientBroll,
@@ -18333,7 +18333,7 @@ function sceneSpokenText(scene) {
18333
18333
  return (scene.dialogue ?? []).map((d) => d.line?.trim()).filter((l) => Boolean(l)).join(" ") || null;
18334
18334
  }
18335
18335
  function buildMotionBoard(blueprint) {
18336
- const round4 = (n) => Math.round(n * 100) / 100;
18336
+ const round5 = (n) => Math.round(n * 100) / 100;
18337
18337
  let cursor = 0;
18338
18338
  return blueprint.scenes.map((scene, i) => {
18339
18339
  const start_s = scene.start_s ?? cursor;
@@ -18345,15 +18345,15 @@ function buildMotionBoard(blueprint) {
18345
18345
  const graphics = [
18346
18346
  ...(overlays.success ? overlays.data : []).filter((ov) => ov.text?.trim()).map((ov) => ({
18347
18347
  kind: "text",
18348
- at_s: round4(ov.appears_at_s ?? start_s),
18349
- dur_s: round4(ov.duration_s ?? 2.5),
18348
+ at_s: round5(ov.appears_at_s ?? start_s),
18349
+ dur_s: round5(ov.duration_s ?? 2.5),
18350
18350
  position: ov.position ?? "bottom_center",
18351
18351
  text: ov.text?.trim()
18352
18352
  })),
18353
18353
  ...(floats.success ? floats.data : []).map((fe) => ({
18354
18354
  kind: "graphic",
18355
- at_s: round4(fe.appears_at_s ?? start_s),
18356
- dur_s: round4(fe.duration_s ?? 2.5),
18355
+ at_s: round5(fe.appears_at_s ?? start_s),
18356
+ dur_s: round5(fe.duration_s ?? 2.5),
18357
18357
  position: fe.position ?? "bottom_center",
18358
18358
  label: fe.brand_name || fe.what_it_represents || fe.description || fe.kind || "element"
18359
18359
  }))
@@ -18361,7 +18361,7 @@ function buildMotionBoard(blueprint) {
18361
18361
  return {
18362
18362
  scene: i,
18363
18363
  role: resolveSceneRole(scene, i, blueprint.scenes.length),
18364
- window_s: [round4(start_s), round4(end_s)],
18364
+ window_s: [round5(start_s), round5(end_s)],
18365
18365
  // A continuation b-roll scene shares the previous scene's end frame as its start
18366
18366
  // (no own `s<i>_start` node), so point the storyboard at that shared keyframe.
18367
18367
  storyboard_frames: [scene.continues_previous && i > 0 ? `s${i - 1}_end` : `s${i}_start`],
@@ -19026,10 +19026,10 @@ function runDirsToPrune(entries, keep, currentRunId) {
19026
19026
  return runs.slice(0, Math.max(0, runs.length - keep));
19027
19027
  }
19028
19028
  async function pruneOldRuns(outputsDir, keep, currentRunId, log) {
19029
- const { readdir: readdir7 } = await import("fs/promises");
19029
+ const { readdir: readdir8 } = await import("fs/promises");
19030
19030
  let entries;
19031
19031
  try {
19032
- entries = await readdir7(outputsDir);
19032
+ entries = await readdir8(outputsDir);
19033
19033
  } catch {
19034
19034
  return;
19035
19035
  }
@@ -20801,10 +20801,10 @@ function resolveShippedCanvasDir(name, startDir, exists = existsSync3, maxDepth
20801
20801
  import { appendFile, readFile as readFile13 } from "fs/promises";
20802
20802
  import path19 from "path";
20803
20803
  function missingGitignoreEntries(existing, entries) {
20804
- const present = new Set(
20804
+ const present2 = new Set(
20805
20805
  existing.split("\n").map((l) => l.trim().replace(/\/+$/, "")).filter((l) => l.length > 0 && !l.startsWith("#"))
20806
20806
  );
20807
- return entries.filter((e) => !present.has(e.trim().replace(/\/+$/, "")));
20807
+ return entries.filter((e) => !present2.has(e.trim().replace(/\/+$/, "")));
20808
20808
  }
20809
20809
  async function ensureGitignore(dir, entries) {
20810
20810
  const file = path19.join(dir, ".gitignore");
@@ -22076,12 +22076,12 @@ function listFlowSlugs() {
22076
22076
  return readdirSync2(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith("_") && entry.name !== ".gitkeep").map((entry) => entry.name).sort();
22077
22077
  }
22078
22078
  function readFlowTree(slug) {
22079
- const path23 = join3(flowsDir(), slug, "_data.json");
22080
- if (!existsSync4(path23)) {
22079
+ const path27 = join3(flowsDir(), slug, "_data.json");
22080
+ if (!existsSync4(path27)) {
22081
22081
  failLocal(`No form "${slug}". Run "baker flows list" to see the forms in this workspace.`);
22082
22082
  }
22083
22083
  try {
22084
- return JSON.parse(readFileSync9(path23, "utf-8"));
22084
+ return JSON.parse(readFileSync9(path27, "utf-8"));
22085
22085
  } catch (error) {
22086
22086
  failLocal(`Could not read form "${slug}": ${error instanceof Error ? error.message : String(error)}`);
22087
22087
  }
@@ -23237,9 +23237,9 @@ async function readImageBuffer(pathOrUrl) {
23237
23237
  }
23238
23238
  return readFile18(pathOrUrl);
23239
23239
  }
23240
- async function isDirectory(path23) {
23240
+ async function isDirectory(path27) {
23241
23241
  try {
23242
- const s = await stat4(path23);
23242
+ const s = await stat4(path27);
23243
23243
  return s.isDirectory();
23244
23244
  } catch {
23245
23245
  return false;
@@ -24939,7 +24939,7 @@ function emitError3(err) {
24939
24939
  process.exit(1);
24940
24940
  }
24941
24941
  function coerceRawArgs(args) {
24942
- const num = (v) => {
24942
+ const num2 = (v) => {
24943
24943
  if (typeof v === "number") return v;
24944
24944
  if (typeof v === "string" && v.length > 0) {
24945
24945
  const n = Number(v);
@@ -24954,8 +24954,8 @@ function coerceRawArgs(args) {
24954
24954
  color: str(args.color),
24955
24955
  "remove-bg": bool(args["remove-bg"]),
24956
24956
  "shrink-to-content": bool(args["shrink-to-content"]),
24957
- height: num(args.height),
24958
- width: num(args.width),
24957
+ height: num2(args.height),
24958
+ width: num2(args.width),
24959
24959
  size: str(args.size),
24960
24960
  fit: str(args.fit),
24961
24961
  output: str(args.output),
@@ -25802,8 +25802,1031 @@ Paid transforms (run on the Convex backend, cost-tracked):
25802
25802
  }
25803
25803
  });
25804
25804
 
25805
- // src/commands/mcp/index.ts
25805
+ // src/commands/landing/index.ts
25806
+ import { defineCommand as defineCommand134 } from "citty";
25807
+
25808
+ // src/commands/landing/critique.ts
25809
+ import { stat as stat6 } from "fs/promises";
25810
+ import path26 from "path";
25806
25811
  import { defineCommand as defineCommand133 } from "citty";
25812
+
25813
+ // src/engine/landing/lib/brand-tokens.ts
25814
+ import { readFile as readFile20 } from "fs/promises";
25815
+ import path23 from "path";
25816
+
25817
+ // src/engine/landing/lib/color.ts
25818
+ var NEUTRAL_COLOR_KEYWORDS = /* @__PURE__ */ new Set([
25819
+ "transparent",
25820
+ "currentcolor",
25821
+ "black",
25822
+ "white",
25823
+ "gray",
25824
+ "grey",
25825
+ "silver",
25826
+ "dimgray",
25827
+ "dimgrey",
25828
+ "darkgray",
25829
+ "darkgrey",
25830
+ "lightgray",
25831
+ "lightgrey",
25832
+ "gainsboro",
25833
+ "whitesmoke"
25834
+ ]);
25835
+ function hexChannels(color) {
25836
+ const long = color.match(/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})(?:[0-9a-f]{2})?$/i);
25837
+ if (long)
25838
+ return [
25839
+ Number.parseInt(long[1] ?? "0", 16),
25840
+ Number.parseInt(long[2] ?? "0", 16),
25841
+ Number.parseInt(long[3] ?? "0", 16)
25842
+ ];
25843
+ const short = color.match(/^#([0-9a-f])([0-9a-f])([0-9a-f])(?:[0-9a-f])?$/i);
25844
+ if (short) {
25845
+ const dup = (s) => Number.parseInt(`${s ?? "0"}${s ?? "0"}`, 16);
25846
+ return [dup(short[1]), dup(short[2]), dup(short[3])];
25847
+ }
25848
+ return null;
25849
+ }
25850
+ function parseColor2(raw) {
25851
+ const c = String(raw || "").trim().toLowerCase();
25852
+ if (!c || c === "transparent") return null;
25853
+ const rgb = c.match(/rgba?\(\s*([\d.]+)[\s,]+([\d.]+)[\s,]+([\d.]+)(?:[\s,/]+([\d.]+%?))?\s*\)/i);
25854
+ if (rgb) {
25855
+ const alpha = rgb[4];
25856
+ const a = alpha === void 0 ? 1 : alpha.endsWith("%") ? Number.parseFloat(alpha) / 100 : Number.parseFloat(alpha);
25857
+ return { r: Number(rgb[1]), g: Number(rgb[2]), b: Number(rgb[3]), a };
25858
+ }
25859
+ const hex = hexChannels(c);
25860
+ if (hex) return { r: hex[0], g: hex[1], b: hex[2], a: 1 };
25861
+ return null;
25862
+ }
25863
+ function isNeutralAuthoredColor(rawColor) {
25864
+ const c = String(rawColor || "").trim().toLowerCase();
25865
+ if (!c) return false;
25866
+ if (NEUTRAL_COLOR_KEYWORDS.has(c)) return true;
25867
+ if (/^rgba?\(/i.test(c)) {
25868
+ const channels2 = c.match(/^rgba?\(\s*([\d.]+)[\s,]+([\d.]+)[\s,]+([\d.]+)/i);
25869
+ if (channels2) {
25870
+ const values = [1, 2, 3].map((i) => Number(channels2[i]));
25871
+ return Math.max(...values) - Math.min(...values) < 30;
25872
+ }
25873
+ return false;
25874
+ }
25875
+ const oklch = c.match(/oklch\(\s*[\d.]+%?\s+([\d.-]+)/i);
25876
+ if (oklch) return Number.parseFloat(oklch[1] ?? "0") < 0.02;
25877
+ const lch = c.match(/lch\(\s*[\d.]+%?\s+([\d.-]+)/i);
25878
+ if (lch) return Number.parseFloat(lch[1] ?? "0") < 3;
25879
+ const hsl = c.match(/hsla?\(\s*[\d.-]+\s*,?\s*([\d.]+)%/i);
25880
+ if (hsl) return Number.parseFloat(hsl[1] ?? "0") < 10;
25881
+ const channels = hexChannels(c);
25882
+ if (channels) return Math.max(...channels) - Math.min(...channels) < 30;
25883
+ return false;
25884
+ }
25885
+ function hasChroma(c, threshold = 30) {
25886
+ if (!c) return false;
25887
+ return Math.max(c.r, c.g, c.b) - Math.min(c.r, c.g, c.b) >= threshold;
25888
+ }
25889
+ function getHue(c) {
25890
+ if (!c) return 0;
25891
+ const r = c.r / 255;
25892
+ const g = c.g / 255;
25893
+ const b = c.b / 255;
25894
+ const max = Math.max(r, g, b);
25895
+ const min = Math.min(r, g, b);
25896
+ if (max === min) return 0;
25897
+ const d = max - min;
25898
+ let h;
25899
+ if (max === r) h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
25900
+ else if (max === g) h = ((b - r) / d + 2) / 6;
25901
+ else h = ((r - g) / d + 4) / 6;
25902
+ return Math.round(h * 360);
25903
+ }
25904
+ function isAiPurpleHue(hue) {
25905
+ return hue >= 260 && hue <= 310;
25906
+ }
25907
+ function isCreamColor(c) {
25908
+ if (Math.min(c.r, c.g, c.b) < 209) return false;
25909
+ if (!(c.r >= c.g && c.g >= c.b)) return false;
25910
+ const warmth = c.r - c.b;
25911
+ return warmth >= 6 && warmth <= 48;
25912
+ }
25913
+
25914
+ // src/engine/landing/lib/brand-tokens.ts
25915
+ var EMPTY = { fonts: /* @__PURE__ */ new Set(), colors: [], hasTokens: false };
25916
+ function parseBrandTokens(globalCss, brandMd) {
25917
+ const fonts = /* @__PURE__ */ new Set();
25918
+ const colors = [];
25919
+ parseGlobalCss(globalCss, fonts, colors);
25920
+ parseBrandMd(brandMd, fonts, colors);
25921
+ return { fonts, colors, hasTokens: fonts.size > 0 || colors.length > 0 };
25922
+ }
25923
+ function parseGlobalCss(globalCss, fonts, colors) {
25924
+ for (const m of globalCss.matchAll(/--font-[\w-]*\s*:\s*([^;]+);/gi)) {
25925
+ const first = (m[1] ?? "").split(",")[0]?.trim().replace(/^['"]|['"]$/g, "").toLowerCase();
25926
+ if (first) fonts.add(first);
25927
+ }
25928
+ for (const m of globalCss.matchAll(
25929
+ /--color-[\w-]*\s*:\s*(#[0-9a-f]{3,8}\b|rgba?\([^)]+\)|oklch\([^)]+\)|hsla?\([^)]+\))/gi
25930
+ )) {
25931
+ const c = parseColor2(m[1] ?? "");
25932
+ if (c) colors.push(c);
25933
+ }
25934
+ }
25935
+ function parseBrandMd(brandMd, fonts, colors) {
25936
+ for (const m of brandMd.matchAll(/#[0-9a-f]{6}\b/gi)) {
25937
+ const c = parseColor2(m[0]);
25938
+ if (c) colors.push(c);
25939
+ }
25940
+ for (const line of brandMd.split("\n")) {
25941
+ if (!/\b(?:font|typeface|family)\b/i.test(line)) continue;
25942
+ const named = line.match(/["'`]([A-Za-z][\w ]{1,30})["'`]/g);
25943
+ if (named) for (const n of named) fonts.add(n.slice(1, -1).trim().toLowerCase());
25944
+ }
25945
+ }
25946
+ async function loadBrandTokens(projectRoot) {
25947
+ const globalCss = await safeRead(path23.join(projectRoot, "src", "styles", "global.css"));
25948
+ const brandMd = await safeRead(path23.join(projectRoot, "src", "brand", "BRAND.md"));
25949
+ if (!globalCss && !brandMd) return EMPTY;
25950
+ return parseBrandTokens(globalCss, brandMd);
25951
+ }
25952
+ async function safeRead(file) {
25953
+ try {
25954
+ return await readFile20(file, "utf8");
25955
+ } catch {
25956
+ return "";
25957
+ }
25958
+ }
25959
+ function brandCommitsFont(brand, snippet) {
25960
+ if (brand.fonts.size === 0) return false;
25961
+ const lower = snippet.toLowerCase();
25962
+ for (const f of brand.fonts) if (f.length >= 3 && lower.includes(f)) return true;
25963
+ return false;
25964
+ }
25965
+ function brandCommitsAiHue(brand) {
25966
+ return brand.colors.some((c) => hasChroma(c, 40) && isAiPurpleHue(getHue(c)));
25967
+ }
25968
+ function brandCommitsCream(brand) {
25969
+ return brand.colors.some((c) => isCreamColor(c));
25970
+ }
25971
+
25972
+ // src/engine/landing/lib/constants.ts
25973
+ var OVERUSED_FONTS = /* @__PURE__ */ new Set([
25974
+ // Older monoculture (still ubiquitous):
25975
+ "inter",
25976
+ "roboto",
25977
+ "open sans",
25978
+ "lato",
25979
+ "montserrat",
25980
+ "arial",
25981
+ "helvetica",
25982
+ // Newer monoculture (the Anthropic-skill / Vercel / GitHub default wave):
25983
+ "fraunces",
25984
+ "instrument sans",
25985
+ "instrument serif",
25986
+ "geist",
25987
+ "geist sans",
25988
+ "geist mono",
25989
+ "mona sans",
25990
+ "plus jakarta sans",
25991
+ "space grotesk",
25992
+ "space mono",
25993
+ "recoleta",
25994
+ "dm sans",
25995
+ "dm serif display",
25996
+ "dm serif text",
25997
+ "outfit",
25998
+ "syne",
25999
+ "cormorant",
26000
+ "playfair display",
26001
+ "lora",
26002
+ "crimson",
26003
+ "newsreader",
26004
+ "ibm plex",
26005
+ "ibm plex sans",
26006
+ "ibm plex serif"
26007
+ ]);
26008
+ var GENERIC_FONTS = /* @__PURE__ */ new Set([
26009
+ "serif",
26010
+ "sans-serif",
26011
+ "monospace",
26012
+ "cursive",
26013
+ "fantasy",
26014
+ "system-ui",
26015
+ "ui-serif",
26016
+ "ui-sans-serif",
26017
+ "ui-monospace",
26018
+ "ui-rounded",
26019
+ "-apple-system",
26020
+ "blinkmacsystemfont",
26021
+ "segoe ui",
26022
+ "inherit",
26023
+ "initial",
26024
+ "unset",
26025
+ "revert"
26026
+ ]);
26027
+ var EM_DASH_FLOOR = 8;
26028
+ var EM_DASH_CHARS_PER_DASH = 500;
26029
+ var TAILWIND_TEXT_PX = {
26030
+ "text-xs": 12,
26031
+ "text-sm": 14,
26032
+ "text-base": 16,
26033
+ "text-lg": 18,
26034
+ "text-xl": 20,
26035
+ "text-2xl": 24,
26036
+ "text-3xl": 30,
26037
+ "text-4xl": 36,
26038
+ "text-5xl": 48,
26039
+ "text-6xl": 60,
26040
+ "text-7xl": 72,
26041
+ "text-8xl": 96,
26042
+ "text-9xl": 128
26043
+ };
26044
+ var BUZZWORDS = [
26045
+ "streamline your",
26046
+ "empower your",
26047
+ "supercharge your",
26048
+ "unleash your",
26049
+ "unleash the power",
26050
+ "leverage the power",
26051
+ "built for the modern",
26052
+ "trusted by leading",
26053
+ "trusted by the world",
26054
+ "best-in-class",
26055
+ "industry-leading",
26056
+ "world-class",
26057
+ "enterprise-grade",
26058
+ "next-generation",
26059
+ "cutting-edge",
26060
+ "transform your business",
26061
+ "revolutionize",
26062
+ "game-changer",
26063
+ "game changing",
26064
+ "mission-critical",
26065
+ "best of breed",
26066
+ "future-proof",
26067
+ "future proof",
26068
+ "seamless experience",
26069
+ "seamlessly integrate",
26070
+ "drive engagement",
26071
+ "drive growth",
26072
+ "drive results",
26073
+ "harness the power"
26074
+ ];
26075
+ var AI_PURPLE_HEXES = /* @__PURE__ */ new Set([
26076
+ "#7c3aed",
26077
+ "#8b5cf6",
26078
+ "#a855f7",
26079
+ "#9333ea",
26080
+ "#7e22ce",
26081
+ "#6d28d9",
26082
+ "#6366f1",
26083
+ "#764ba2",
26084
+ "#667eea"
26085
+ ]);
26086
+ var CREAM_TAILWIND_BG = /* @__PURE__ */ new Set([
26087
+ "bg-amber-50",
26088
+ "bg-amber-100",
26089
+ "bg-orange-50",
26090
+ "bg-orange-100",
26091
+ "bg-yellow-50",
26092
+ "bg-stone-50",
26093
+ "bg-stone-100",
26094
+ "bg-stone-200"
26095
+ ]);
26096
+ var RULE_META = {
26097
+ "gradient-text": {
26098
+ family: "color",
26099
+ severity: "block",
26100
+ note: "Gradient text is a top AI tell. Emphasis comes from weight or size, not a clipped gradient fill."
26101
+ },
26102
+ "broken-image": {
26103
+ family: "integrity",
26104
+ severity: "block",
26105
+ note: "An <img> with an empty or placeholder src ships a broken image. Source a real asset via `baker images`."
26106
+ },
26107
+ "side-tab": {
26108
+ family: "borders_depth",
26109
+ severity: "warn",
26110
+ note: "A thick colored border on one edge is the 'side-tab' AI tell. Use full borders, a background, or spacing instead."
26111
+ },
26112
+ "border-accent-on-rounded": {
26113
+ family: "borders_depth",
26114
+ severity: "warn",
26115
+ note: "A colored top/bottom accent bar on a rounded card is an AI tell. Let the card's own surface carry the accent."
26116
+ },
26117
+ "overused-font": {
26118
+ family: "typography",
26119
+ severity: "warn",
26120
+ brandAware: "font",
26121
+ note: "This face is a training-data default. Pick a face with a point of view \u2014 unless BRAND.md commits it."
26122
+ },
26123
+ "single-font": {
26124
+ family: "typography",
26125
+ severity: "advisory",
26126
+ note: "Only one face across the page flattens hierarchy. A display/body pair usually reads richer."
26127
+ },
26128
+ "flat-type-hierarchy": {
26129
+ family: "typography",
26130
+ severity: "warn",
26131
+ note: "Type steps are too close (<2\xD7 span). Widen the scale so the primary, secondary, and body are obvious at a glance."
26132
+ },
26133
+ "ai-color-palette": {
26134
+ family: "color",
26135
+ severity: "warn",
26136
+ brandAware: "color",
26137
+ note: "Purple/indigo-on-heading or purple\u2192cyan gradients are the AI palette cluster. Commit to the brand's own hues."
26138
+ },
26139
+ "cream-palette": {
26140
+ family: "color",
26141
+ severity: "warn",
26142
+ brandAware: "color",
26143
+ note: "Cream/parchment ground is the 'measured rendition' AI default. Rework from the brand's saturated materials."
26144
+ },
26145
+ "gray-on-color": {
26146
+ family: "color",
26147
+ severity: "warn",
26148
+ note: "Gray text on a colored surface reads muddy. Tint secondary text from the surface hue, never neutral gray."
26149
+ },
26150
+ "bounce-easing": {
26151
+ family: "motion",
26152
+ severity: "warn",
26153
+ note: "Bounce/elastic easing feels dated. Use natural deceleration (e.g. cubic-bezier(0.16, 1, 0.3, 1))."
26154
+ },
26155
+ "layout-transition": {
26156
+ family: "motion",
26157
+ severity: "advisory",
26158
+ note: "Transitioning width/height/padding/margin janks. Animate transform/opacity, or clip-path/max-height with care."
26159
+ },
26160
+ "monotonous-spacing": {
26161
+ family: "spacing",
26162
+ severity: "warn",
26163
+ note: "One spacing value dominates. Create rhythm \u2014 tight groups, generous separation, more space above a heading than below."
26164
+ },
26165
+ "marketing-buzzword": {
26166
+ family: "copy",
26167
+ severity: "warn",
26168
+ note: "SaaS filler ('supercharge your\u2026') is generic. Name names, use numbers, say the specific thing only this product proves."
26169
+ },
26170
+ "aphoristic-cadence": {
26171
+ family: "copy",
26172
+ severity: "warn",
26173
+ note: "Manufactured-contrast cadence ('Not an X. A Y.') is an AI copy tell. Vary sentence shape; make a real claim."
26174
+ },
26175
+ "em-dash-overuse": {
26176
+ family: "copy",
26177
+ severity: "warn",
26178
+ note: "Dense em-dashes are an AI cadence tell (and brand copy bans them). Recast with commas, colons, or full stops."
26179
+ },
26180
+ "dark-glow": {
26181
+ family: "borders_depth",
26182
+ severity: "warn",
26183
+ note: "A zero-offset colored glow is decoration, not depth. Shadows carry an offset and a soft blur."
26184
+ },
26185
+ marquee: {
26186
+ family: "motion",
26187
+ severity: "warn",
26188
+ note: "Auto-scrolling marquees are a dated tell. Let the visitor control the pace, or drop the motion."
26189
+ }
26190
+ };
26191
+ var SEVERITY_WEIGHT = {
26192
+ block: 0.5,
26193
+ warn: 0.2,
26194
+ advisory: 0.05
26195
+ };
26196
+
26197
+ // src/engine/landing/lib/rules.ts
26198
+ var cap2 = (m, i) => m[i] ?? "";
26199
+ var num = (m, i) => Number(m[i] ?? 0);
26200
+ var hasRounded = (line) => /\brounded(?:-\w+)?\b/.test(line) || /border-radius/i.test(line);
26201
+ var isSafeElement = (line) => /<(?:blockquote|nav[\s>]|pre[\s>]|code[\s>]|a\s|input[\s>]|span[\s>])/i.test(line);
26202
+ function isNeutralBorderColor(str) {
26203
+ const m = str.match(/solid\s+((?:rgba?|hsla?|oklch|oklab|lab|lch|hwb|color)\([^)]*\)|#[0-9a-f]{3,8}\b|[a-z]+)/i);
26204
+ if (!m) return false;
26205
+ return isNeutralAuthoredColor(m[1] ?? "");
26206
+ }
26207
+ var LINE_MATCHERS = [
26208
+ // ── Side-tab: a thick colored border on one edge ──────────────────────────
26209
+ {
26210
+ id: "side-tab",
26211
+ regex: /\bborder-[lrse]-(\d+)\b/g,
26212
+ test: (m, line) => hasRounded(line) ? num(m, 1) >= 2 : num(m, 1) >= 4,
26213
+ fmt: (m) => cap2(m, 0)
26214
+ },
26215
+ {
26216
+ id: "side-tab",
26217
+ regex: /border-(?:left|right)\s*:\s*(\d+)px\s+solid[^;]*/gi,
26218
+ test: (m, line) => {
26219
+ if (isSafeElement(line)) return false;
26220
+ if (isNeutralBorderColor(cap2(m, 0))) return false;
26221
+ return hasRounded(line) ? num(m, 1) >= 2 : num(m, 1) >= 3;
26222
+ },
26223
+ fmt: (m) => cap2(m, 0).replace(/\s*;?\s*$/, "")
26224
+ },
26225
+ {
26226
+ id: "side-tab",
26227
+ regex: /border-(?:left|right)-width\s*:\s*(\d+)px/gi,
26228
+ test: (m, line) => !isSafeElement(line) && num(m, 1) >= 3,
26229
+ fmt: (m) => cap2(m, 0)
26230
+ },
26231
+ {
26232
+ id: "side-tab",
26233
+ regex: /border-inline-(?:start|end)(?:-width)?\s*:\s*(\d+)px/gi,
26234
+ test: (m, line) => !isSafeElement(line) && num(m, 1) >= 3,
26235
+ fmt: (m) => cap2(m, 0)
26236
+ },
26237
+ // ── Border accent on a rounded card (top/bottom colored bar) ──────────────
26238
+ {
26239
+ id: "border-accent-on-rounded",
26240
+ regex: /\bborder-[tb]-(\d+)\b/g,
26241
+ test: (m, line) => hasRounded(line) && num(m, 1) >= 1,
26242
+ fmt: (m) => cap2(m, 0)
26243
+ },
26244
+ {
26245
+ id: "border-accent-on-rounded",
26246
+ regex: /border-(?:top|bottom)\s*:\s*(\d+)px\s+solid/gi,
26247
+ test: (m, line) => num(m, 1) >= 3 && hasRounded(line),
26248
+ fmt: (m) => cap2(m, 0)
26249
+ },
26250
+ // ── Gradient text ─────────────────────────────────────────────────────────
26251
+ {
26252
+ id: "gradient-text",
26253
+ regex: /background-clip\s*:\s*text|-webkit-background-clip\s*:\s*text/gi,
26254
+ test: (_m, line) => /gradient/i.test(line),
26255
+ fmt: () => "background-clip: text + gradient"
26256
+ },
26257
+ {
26258
+ id: "gradient-text",
26259
+ regex: /\bbg-clip-text\b/g,
26260
+ test: (_m, line) => /\bbg-gradient-to-|\bbg-\[linear-gradient/i.test(line),
26261
+ fmt: () => "bg-clip-text + gradient"
26262
+ },
26263
+ // ── Gray text on a colored background (Tailwind) ──────────────────────────
26264
+ {
26265
+ id: "gray-on-color",
26266
+ regex: /\btext-(?:gray|slate|zinc|neutral|stone)-(\d+)\b/g,
26267
+ test: (_m, line) => /\bbg-(?:red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-\d+\b/.test(
26268
+ line
26269
+ ),
26270
+ fmt: (m, line) => {
26271
+ const bg = line.match(
26272
+ /\bbg-(?:red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-\d+\b/
26273
+ );
26274
+ return `${cap2(m, 0)} on ${bg?.[0] || "?"}`;
26275
+ }
26276
+ },
26277
+ // ── AI color palette (Tailwind purple/indigo + banned hero hexes) ─────────
26278
+ {
26279
+ id: "ai-color-palette",
26280
+ regex: /\btext-(?:purple|violet|indigo)-(\d+)\b/g,
26281
+ test: (_m, line) => /\btext-(?:[2-9]xl)\b|<h[1-3]/i.test(line),
26282
+ fmt: (m) => `${cap2(m, 0)} on heading`
26283
+ },
26284
+ {
26285
+ id: "ai-color-palette",
26286
+ regex: /\bfrom-(?:purple|violet|indigo)-(\d+)\b/g,
26287
+ test: (_m, line) => /\bto-(?:purple|violet|indigo|blue|cyan|pink|fuchsia)-\d+\b/.test(line),
26288
+ fmt: (m) => `${cap2(m, 0)} gradient`
26289
+ },
26290
+ {
26291
+ id: "ai-color-palette",
26292
+ regex: /#[0-9a-f]{6}\b/gi,
26293
+ test: (m) => AI_PURPLE_HEXES.has(cap2(m, 0).toLowerCase()),
26294
+ fmt: (m) => `${cap2(m, 0)} (AI-cluster purple)`
26295
+ },
26296
+ // ── Cream/parchment ground (Tailwind + authored background hex) ───────────
26297
+ {
26298
+ id: "cream-palette",
26299
+ regex: /\bbg-(?:amber|orange|yellow|stone)-(?:50|100|200)\b/g,
26300
+ test: (m) => CREAM_TAILWIND_BG.has(cap2(m, 0)),
26301
+ fmt: (m) => `${cap2(m, 0)} (cream ground)`
26302
+ },
26303
+ {
26304
+ id: "cream-palette",
26305
+ regex: /background(?:-color)?\s*:\s*(#[0-9a-f]{6}\b|rgba?\([^)]+\))/gi,
26306
+ test: (m) => {
26307
+ const c = parseColor2(cap2(m, 1));
26308
+ return c !== null && isCreamColor(c);
26309
+ },
26310
+ fmt: (m) => `${cap2(m, 1)} (cream ground)`
26311
+ },
26312
+ // ── Bounce / elastic easing ───────────────────────────────────────────────
26313
+ {
26314
+ id: "bounce-easing",
26315
+ regex: /\banimate-bounce\b/g,
26316
+ test: () => true,
26317
+ fmt: () => "animate-bounce (Tailwind)"
26318
+ },
26319
+ {
26320
+ id: "bounce-easing",
26321
+ regex: /animation(?:-name)?\s*:\s*([^;{}]*(?:bounce|elastic|wobble|jiggle|spring)[^;{}]*)/gi,
26322
+ test: () => true,
26323
+ fmt: (m) => `animation: ${cap2(m, 1).trim()}`
26324
+ },
26325
+ {
26326
+ id: "bounce-easing",
26327
+ regex: /cubic-bezier\(\s*([\d.-]+)\s*,\s*([\d.-]+)\s*,\s*([\d.-]+)\s*,\s*([\d.-]+)\s*\)/g,
26328
+ test: (m) => {
26329
+ const y1 = Number.parseFloat(cap2(m, 2));
26330
+ const y2 = Number.parseFloat(cap2(m, 4));
26331
+ return y1 < -0.1 || y1 > 1.1 || y2 < -0.1 || y2 > 1.1;
26332
+ },
26333
+ fmt: (m) => `cubic-bezier(${cap2(m, 1)}, ${cap2(m, 2)}, ${cap2(m, 3)}, ${cap2(m, 4)})`
26334
+ },
26335
+ // ── Transitioning layout properties (jank) ────────────────────────────────
26336
+ {
26337
+ id: "layout-transition",
26338
+ regex: /transition(?:-property)?\s*:\s*([^;{}]+)/gi,
26339
+ test: (m) => {
26340
+ const val = cap2(m, 1).toLowerCase();
26341
+ if (/\ball\b/.test(val)) return false;
26342
+ return /\b(?:(?:max|min)-)?(?:width|height)\b|\bpadding\b|\bmargin\b/.test(val);
26343
+ },
26344
+ fmt: (m) => {
26345
+ const found = cap2(m, 1).match(
26346
+ /\b(?:(?:max|min)-)?(?:width|height)\b|\bpadding(?:-(?:top|right|bottom|left))?\b|\bmargin(?:-(?:top|right|bottom|left))?\b/gi
26347
+ );
26348
+ return `transition: ${found ? found.join(", ") : cap2(m, 1).trim()}`;
26349
+ }
26350
+ },
26351
+ // ── Broken image ──────────────────────────────────────────────────────────
26352
+ {
26353
+ id: "broken-image",
26354
+ regex: /<img\b[^>]*?\bsrc\s*=\s*(?:""|''|"\s+"|'\s+'|"#"|'#')/gi,
26355
+ test: () => true,
26356
+ fmt: (m) => cap2(m, 0).slice(0, 100)
26357
+ },
26358
+ {
26359
+ id: "broken-image",
26360
+ regex: /<img\b(?:(?!\bsrc\s*=)[^>])*>/gi,
26361
+ test: (m) => !/\bsrc\s*=/i.test(cap2(m, 0)),
26362
+ fmt: (m) => cap2(m, 0).slice(0, 100)
26363
+ }
26364
+ ];
26365
+ function stripHtmlToText(html) {
26366
+ return html.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, " ").replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, " ").replace(/<!--[\s\S]*?-->/g, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ");
26367
+ }
26368
+ var ANALYZERS = [
26369
+ // Overused primary font (font-family declarations + Google Fonts URLs).
26370
+ (text, file) => {
26371
+ const out = [];
26372
+ const seen = /* @__PURE__ */ new Set();
26373
+ for (const m of text.matchAll(/font-family\s*:\s*([^;}{]+)/gi)) {
26374
+ const first = firstFamily(cap2(m, 1));
26375
+ if (first && !GENERIC_FONTS.has(first) && OVERUSED_FONTS.has(first) && !seen.has(first)) {
26376
+ seen.add(first);
26377
+ out.push({ id: "overused-font", snippet: `font-family: ${first}`, file, line: lineOf(text, m.index ?? 0) });
26378
+ }
26379
+ }
26380
+ for (const m of text.matchAll(/fonts\.googleapis\.com\/css2?\?[^"'\s)<>]*/gi)) {
26381
+ for (const fam of googleFontFamilies(cap2(m, 0))) {
26382
+ if (OVERUSED_FONTS.has(fam) && !seen.has(fam)) {
26383
+ seen.add(fam);
26384
+ out.push({ id: "overused-font", snippet: `Google Fonts: ${fam}`, file, line: lineOf(text, m.index ?? 0) });
26385
+ }
26386
+ }
26387
+ }
26388
+ return out;
26389
+ },
26390
+ // Single font across a substantial page.
26391
+ (text, file) => {
26392
+ const fonts = /* @__PURE__ */ new Set();
26393
+ for (const m of text.matchAll(/font-family\s*:\s*([^;}{]+)/gi)) {
26394
+ for (const f of cap2(m, 1).split(",").map(
26395
+ (x) => x.trim().replace(/^['"]|['"]$/g, "").toLowerCase()
26396
+ )) {
26397
+ if (f && !GENERIC_FONTS.has(f)) fonts.add(f);
26398
+ }
26399
+ }
26400
+ for (const f of allGoogleFontFamilies(text)) fonts.add(f);
26401
+ if (fonts.size !== 1 || text.split("\n").length < 20) return [];
26402
+ const only = [...fonts][0] ?? "";
26403
+ return [{ id: "single-font", snippet: `only font used is ${only}`, file }];
26404
+ },
26405
+ // Flat type hierarchy (all sizes span <2×).
26406
+ (text, file) => {
26407
+ const sizes = /* @__PURE__ */ new Set();
26408
+ const REM = 16;
26409
+ for (const m of text.matchAll(/font-size\s*:\s*([\d.]+)(px|rem|em)\b/gi)) {
26410
+ const px = cap2(m, 2) === "px" ? num(m, 1) : num(m, 1) * REM;
26411
+ if (px > 0 && px < 200) sizes.add(Math.round(px * 10) / 10);
26412
+ }
26413
+ for (const [cls, px] of Object.entries(TAILWIND_TEXT_PX)) {
26414
+ if (new RegExp(`\\b${cls}\\b`).test(text)) sizes.add(px);
26415
+ }
26416
+ if (sizes.size < 3) return [];
26417
+ const sorted = [...sizes].sort((a, b) => a - b);
26418
+ const min = sorted[0] ?? 1;
26419
+ const max = sorted[sorted.length - 1] ?? 1;
26420
+ const ratio = max / min;
26421
+ if (ratio >= 2) return [];
26422
+ return [
26423
+ {
26424
+ id: "flat-type-hierarchy",
26425
+ snippet: `sizes ${sorted.map((s) => `${s}px`).join(", ")} (ratio ${ratio.toFixed(1)}:1)`,
26426
+ file
26427
+ }
26428
+ ];
26429
+ },
26430
+ // Monotonous spacing (one value dominates).
26431
+ (text, file) => {
26432
+ const vals = [];
26433
+ for (const m of text.matchAll(/(?:padding|margin)(?:-(?:top|right|bottom|left))?\s*:\s*(\d+)px/gi)) {
26434
+ const v = num(m, 1);
26435
+ if (v > 0 && v < 200) vals.push(v);
26436
+ }
26437
+ for (const m of text.matchAll(/(?:padding|margin)(?:-(?:top|right|bottom|left))?\s*:\s*([\d.]+)rem/gi)) {
26438
+ const v = Math.round(Number.parseFloat(cap2(m, 1)) * 16);
26439
+ if (v > 0 && v < 200) vals.push(v);
26440
+ }
26441
+ for (const m of text.matchAll(/\b(?:p|px|py|pt|pb|pl|pr|m|mx|my|mt|mb|ml|mr|gap)-(\d+)\b/g))
26442
+ vals.push(num(m, 1) * 4);
26443
+ const rounded = vals.map((v) => Math.round(v / 4) * 4);
26444
+ if (rounded.length < 10) return [];
26445
+ const counts = {};
26446
+ for (const v of rounded) counts[v] = (counts[v] || 0) + 1;
26447
+ const maxCount = Math.max(...Object.values(counts));
26448
+ const pct = maxCount / rounded.length;
26449
+ const unique = [...new Set(rounded)].filter((v) => v > 0);
26450
+ if (pct <= 0.6 || unique.length > 3) return [];
26451
+ const dominant = Object.entries(counts).sort((a, b) => b[1] - a[1])[0]?.[0] ?? "?";
26452
+ return [
26453
+ {
26454
+ id: "monotonous-spacing",
26455
+ snippet: `~${dominant}px used ${maxCount}/${rounded.length} times (${Math.round(pct * 100)}%)`,
26456
+ file
26457
+ }
26458
+ ];
26459
+ },
26460
+ // Em-dash overuse (density-gated).
26461
+ (text, file) => {
26462
+ const body = stripHtmlToText(text).replace(/&mdash;|&#0*8212;|&#x0*2014;/gi, "\u2014");
26463
+ const count = (body.match(/[—]|--(?=\S)/g) ?? []).length;
26464
+ if (count < EM_DASH_FLOOR) return [];
26465
+ if (body.length > count * EM_DASH_CHARS_PER_DASH) return [];
26466
+ return [{ id: "em-dash-overuse", snippet: `${count} em-dashes in body copy`, file }];
26467
+ },
26468
+ // Marketing buzzwords.
26469
+ (text, file) => {
26470
+ const body = stripHtmlToText(text);
26471
+ const lower = body.toLowerCase();
26472
+ let count = 0;
26473
+ let firstSample = "";
26474
+ for (const phrase of BUZZWORDS) {
26475
+ let from = 0;
26476
+ for (; ; ) {
26477
+ const idx = lower.indexOf(phrase, from);
26478
+ if (idx === -1) break;
26479
+ count++;
26480
+ if (!firstSample) {
26481
+ firstSample = body.slice(Math.max(0, idx - 12), Math.min(body.length, idx + phrase.length + 12)).trim();
26482
+ }
26483
+ from = idx + phrase.length;
26484
+ }
26485
+ }
26486
+ if (count === 0) return [];
26487
+ return [
26488
+ {
26489
+ id: "marketing-buzzword",
26490
+ snippet: `${count} buzzword phrase${count === 1 ? "" : "s"}: "${firstSample}"`,
26491
+ file
26492
+ }
26493
+ ];
26494
+ },
26495
+ // Aphoristic cadence (manufactured contrast + short rebuttal).
26496
+ (text, file) => {
26497
+ const body = stripHtmlToText(text);
26498
+ let count = 0;
26499
+ let firstSample = "";
26500
+ for (const m of body.matchAll(/\bNot an? [a-z][^.!?]{1,40}[.!]\s+[A-Z][^.!?]{1,60}[.!]/g)) {
26501
+ count++;
26502
+ if (!firstSample) firstSample = cap2(m, 0).trim().slice(0, 80);
26503
+ }
26504
+ for (const m of body.matchAll(/\b[A-Z][^.!?]{4,80}[.!]\s+(No|Just)\s+[a-z][^.!?]{2,60}[.!]/g)) {
26505
+ count++;
26506
+ if (!firstSample) firstSample = cap2(m, 0).trim().slice(0, 80);
26507
+ }
26508
+ if (count < 3) return [];
26509
+ return [{ id: "aphoristic-cadence", snippet: `${count} aphoristic constructions: "${firstSample}"`, file }];
26510
+ },
26511
+ // Dark glow: a zero-offset chromatic shadow used as decoration.
26512
+ (text, file) => {
26513
+ const out = [];
26514
+ for (const m of text.matchAll(/(?:box|text)-shadow\s*:\s*([^;{}]+)/gi)) {
26515
+ if (shadowIsGlow(cap2(m, 1))) {
26516
+ out.push({
26517
+ id: "dark-glow",
26518
+ snippet: `shadow: ${cap2(m, 1).trim().slice(0, 60)}`,
26519
+ file,
26520
+ line: lineOf(text, m.index ?? 0)
26521
+ });
26522
+ }
26523
+ }
26524
+ return out;
26525
+ },
26526
+ // Marquee (<marquee> element).
26527
+ (text, file) => /<marquee\b/i.test(text) ? [{ id: "marquee", snippet: "<marquee> element", file }] : []
26528
+ ];
26529
+ function shadowIsGlow(value) {
26530
+ const first = (value.split(/,(?![^(]*\))/)[0] ?? "").trim();
26531
+ const colorMatch = first.match(/#[0-9a-f]{3,8}\b|rgba?\([^)]+\)|hsla?\([^)]+\)/i);
26532
+ if (!colorMatch) return false;
26533
+ const lengthsPart = first.replace(colorMatch[0], " ").replace(/\binset\b/gi, " ");
26534
+ const offsets = lengthsPart.match(/-?\d*\.?\d+(?:px|rem|em)?/g);
26535
+ if (!offsets || offsets.length < 3) return false;
26536
+ const x = Number.parseFloat(offsets[0] ?? "0");
26537
+ const y = Number.parseFloat(offsets[1] ?? "0");
26538
+ const blur = Number.parseFloat(offsets[2] ?? "0");
26539
+ if (!(x === 0 && y === 0 && blur > 4)) return false;
26540
+ const c = parseColor2(colorMatch[0]);
26541
+ return c !== null && hasChroma(c, 30);
26542
+ }
26543
+ function firstFamily(decl) {
26544
+ return (decl.split(",")[0] ?? "").trim().replace(/^['"]|['"]$/g, "").toLowerCase();
26545
+ }
26546
+ function googleFontFamilies(url) {
26547
+ const q = url.indexOf("?");
26548
+ if (q === -1) return [];
26549
+ const params = new URLSearchParams(url.slice(q + 1).replace(/&amp;/g, "&"));
26550
+ const out = [];
26551
+ for (const value of params.getAll("family")) {
26552
+ for (const part of value.split("|")) out.push((part.split(":")[0] ?? "").trim().toLowerCase());
26553
+ }
26554
+ return out.filter(Boolean);
26555
+ }
26556
+ function allGoogleFontFamilies(text) {
26557
+ const out = [];
26558
+ for (const m of text.matchAll(/fonts\.googleapis\.com\/css2?\?[^"'\s)<>]*/gi))
26559
+ out.push(...googleFontFamilies(cap2(m, 0)));
26560
+ return out;
26561
+ }
26562
+ function lineOf(text, index) {
26563
+ let line = 1;
26564
+ for (let i = 0; i < index && i < text.length; i++) if (text[i] === "\n") line++;
26565
+ return line;
26566
+ }
26567
+ function detectSource(source) {
26568
+ const { path: file, text } = source;
26569
+ const findings = [];
26570
+ const lines = text.split("\n");
26571
+ for (const matcher of LINE_MATCHERS) {
26572
+ for (let i = 0; i < lines.length; i++) {
26573
+ const line = lines[i] ?? "";
26574
+ for (const m of line.matchAll(matcher.regex)) {
26575
+ if (matcher.test(m, line)) findings.push({ id: matcher.id, snippet: matcher.fmt(m, line), file, line: i + 1 });
26576
+ }
26577
+ }
26578
+ }
26579
+ for (const analyzer of ANALYZERS) findings.push(...analyzer(text, file));
26580
+ return dedupe(findings);
26581
+ }
26582
+ function dedupe(findings) {
26583
+ const out = [];
26584
+ for (const f of findings) {
26585
+ const dupe = out.some(
26586
+ (d) => d.id === f.id && d.snippet === f.snippet && Math.abs((d.line ?? 0) - (f.line ?? 0)) <= 2
26587
+ );
26588
+ if (!dupe) out.push(f);
26589
+ }
26590
+ return out;
26591
+ }
26592
+
26593
+ // src/engine/landing/lib/critique.ts
26594
+ var FAMILIES = ["typography", "color", "borders_depth", "motion", "spacing", "copy", "integrity"];
26595
+ function round4(n) {
26596
+ return Math.round(n * 100) / 100;
26597
+ }
26598
+ function clamp012(n) {
26599
+ return Math.max(0, Math.min(1, n));
26600
+ }
26601
+ function applyBrand(id, snippet, severity, brand) {
26602
+ const meta = RULE_META[id];
26603
+ if (!meta?.brandAware || !brand.hasTokens) return { severity, brandDrift: false };
26604
+ const matched = meta.brandAware === "font" && brandCommitsFont(brand, snippet) || meta.brandAware === "color" && id === "ai-color-palette" && brandCommitsAiHue(brand) || meta.brandAware === "color" && id === "cream-palette" && brandCommitsCream(brand);
26605
+ return matched ? { severity: "advisory", brandDrift: true } : { severity, brandDrift: false };
26606
+ }
26607
+ function critiqueLanding(input) {
26608
+ const sources = Array.isArray(input.sources) ? input.sources : [];
26609
+ const findings = [];
26610
+ for (const source of sources) {
26611
+ for (const raw of detectSource(source)) {
26612
+ const meta = RULE_META[raw.id];
26613
+ if (!meta) continue;
26614
+ const { severity, brandDrift } = applyBrand(raw.id, raw.snippet, meta.severity, input.brand);
26615
+ findings.push({
26616
+ id: raw.id,
26617
+ severity,
26618
+ family: meta.family,
26619
+ snippet: raw.snippet,
26620
+ file: raw.file,
26621
+ line: raw.line,
26622
+ note: meta.note,
26623
+ ...brandDrift ? { brandDrift: true } : {}
26624
+ });
26625
+ }
26626
+ }
26627
+ const dimensions = FAMILIES.map((family) => {
26628
+ const fam = findings.filter((f) => f.family === family);
26629
+ const penalty = fam.reduce((s, f) => s + SEVERITY_WEIGHT[f.severity], 0);
26630
+ const score = round4(clamp012(1 - penalty));
26631
+ const note = fam.length === 0 ? "clean" : `${fam.length} finding(s): ${describeCounts(fam)}`;
26632
+ return { dimension: family, score, note };
26633
+ });
26634
+ const overall = round4(dimensions.reduce((s, d) => s + d.score, 0) / dimensions.length);
26635
+ const counts = {
26636
+ block: findings.filter((f) => f.severity === "block").length,
26637
+ warn: findings.filter((f) => f.severity === "warn").length,
26638
+ advisory: findings.filter((f) => f.severity === "advisory").length
26639
+ };
26640
+ return { advisory: true, overall, dimensions, findings, counts };
26641
+ }
26642
+ function describeCounts(findings) {
26643
+ const b = findings.filter((f) => f.severity === "block").length;
26644
+ const w = findings.filter((f) => f.severity === "warn").length;
26645
+ const a = findings.filter((f) => f.severity === "advisory").length;
26646
+ return [b ? `${b} block` : "", w ? `${w} warn` : "", a ? `${a} advisory` : ""].filter(Boolean).join(", ");
26647
+ }
26648
+
26649
+ // src/commands/landing/snapshot.ts
26650
+ import { mkdir as mkdir7, writeFile as writeFile10 } from "fs/promises";
26651
+ import path24 from "path";
26652
+ var CRITIC_VERSION = "1";
26653
+ function critiqueCacheDir(projectRoot) {
26654
+ return path24.join(projectRoot, ".cache", "landing-critique");
26655
+ }
26656
+ function snapshotPath(projectRoot, slug) {
26657
+ return path24.join(critiqueCacheDir(projectRoot), `${slug}.json`);
26658
+ }
26659
+ async function writeCritiqueSnapshot(projectRoot, snapshot) {
26660
+ await mkdir7(critiqueCacheDir(projectRoot), { recursive: true });
26661
+ await writeFile10(snapshotPath(projectRoot, snapshot.slug), `${JSON.stringify(snapshot, null, 2)}
26662
+ `, "utf8");
26663
+ }
26664
+
26665
+ // src/commands/landing/source-version.ts
26666
+ import { readdir as readdir7, readFile as readFile21, stat as stat5 } from "fs/promises";
26667
+ import path25 from "path";
26668
+ async function landingSourceRelPaths(landingDir) {
26669
+ const rel = [];
26670
+ if (await isFile(path25.join(landingDir, "index.astro"))) rel.push("index.astro");
26671
+ const componentsDir = path25.join(landingDir, "_components");
26672
+ for (const abs of await walkAstro(componentsDir)) {
26673
+ rel.push(path25.relative(landingDir, abs).split(path25.sep).join("/"));
26674
+ }
26675
+ return rel.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
26676
+ }
26677
+ async function readLandingSources(landingDir) {
26678
+ const rel = await landingSourceRelPaths(landingDir);
26679
+ const out = [];
26680
+ for (const r of rel) out.push({ path: r, text: await readFile21(path25.join(landingDir, r), "utf8") });
26681
+ return out;
26682
+ }
26683
+ async function computeLandingSourceSha(landingDir) {
26684
+ const rel = await landingSourceRelPaths(landingDir);
26685
+ const parts = [];
26686
+ for (const r of rel) {
26687
+ let bytes;
26688
+ try {
26689
+ bytes = await readFile21(path25.join(landingDir, r));
26690
+ } catch {
26691
+ bytes = Buffer.alloc(0);
26692
+ }
26693
+ parts.push(Buffer.from(`${r}\0${bytes.length}\0`), bytes);
26694
+ }
26695
+ return sha256Hex(Buffer.concat(parts));
26696
+ }
26697
+ async function isFile(p) {
26698
+ try {
26699
+ return (await stat5(p)).isFile();
26700
+ } catch {
26701
+ return false;
26702
+ }
26703
+ }
26704
+ async function walkAstro(dir) {
26705
+ let entries;
26706
+ try {
26707
+ entries = await readdir7(dir, { withFileTypes: true });
26708
+ } catch {
26709
+ return [];
26710
+ }
26711
+ const out = [];
26712
+ for (const entry of entries) {
26713
+ const abs = path25.join(dir, entry.name);
26714
+ if (entry.isDirectory()) out.push(...await walkAstro(abs));
26715
+ else if (entry.isFile() && entry.name.endsWith(".astro")) out.push(abs);
26716
+ }
26717
+ return out;
26718
+ }
26719
+
26720
+ // src/commands/landing/critique.ts
26721
+ registerSchema({
26722
+ command: "landing.critique",
26723
+ description: "Deterministic design-quality critic for a landing page (ADVISORY \u2014 never blocks, always exits 0). Flags the known AI 'slop' tells (gradient text, overused fonts, side-tab borders, cream palettes, buzzword copy, broken images\u2026) tiered block/warn/advisory, and records the critique the publish quality gate requires.",
26724
+ args: {
26725
+ slug: { type: "string", description: "Landing slug (folder under src/pages/)", required: true },
26726
+ full: {
26727
+ type: "boolean",
26728
+ description: "Also print advisory-tier findings (default: block + warn only)",
26729
+ required: false
26730
+ }
26731
+ }
26732
+ });
26733
+ var critiqueCommand2 = defineCommand133({
26734
+ meta: {
26735
+ name: "critique",
26736
+ description: "Deterministic design-quality critic for a landing (ADVISORY \u2014 never blocks). Flags the known AI design tells (gradient text, overused fonts, side-tab borders, cream palettes, buzzword copy, broken images) tiered block/warn/advisory, respecting the client's BRAND.md as the allowlist. Also records the critique that `baker publish` requires \u2014 run it before finishing a landing."
26737
+ },
26738
+ args: {
26739
+ slug: { type: "positional", required: true, description: "Landing slug (folder under src/pages/)" },
26740
+ full: { type: "boolean", description: "Also print advisory-tier findings", default: false }
26741
+ },
26742
+ async run({ args }) {
26743
+ const slug = String(args.slug);
26744
+ const projectRoot = process.cwd();
26745
+ const landingDir = path26.resolve(projectRoot, "src", "pages", slug);
26746
+ if (!await isDir(landingDir)) {
26747
+ process.stderr.write(
26748
+ `${JSON.stringify(
26749
+ { ok: false, error: { code: "not-found", message: `No landing at src/pages/${slug}/` } },
26750
+ null,
26751
+ 2
26752
+ )}
26753
+ `
26754
+ );
26755
+ process.exit(2);
26756
+ return;
26757
+ }
26758
+ const [sources, brand, sourceSha] = await Promise.all([
26759
+ readLandingSources(landingDir),
26760
+ loadBrandTokens(projectRoot),
26761
+ computeLandingSourceSha(landingDir)
26762
+ ]);
26763
+ const report = critiqueLanding({ slug, sources, brand });
26764
+ try {
26765
+ await writeCritiqueSnapshot(projectRoot, {
26766
+ slug,
26767
+ sourceSha,
26768
+ criticVersion: CRITIC_VERSION,
26769
+ at: (/* @__PURE__ */ new Date()).toISOString(),
26770
+ blockCount: report.counts.block,
26771
+ warnCount: report.counts.warn
26772
+ });
26773
+ } catch {
26774
+ }
26775
+ const shown = report.findings.filter((f) => args.full ? true : f.severity !== "advisory");
26776
+ process.stdout.write(
26777
+ `${JSON.stringify(
26778
+ {
26779
+ ok: true,
26780
+ advisory: true,
26781
+ slug,
26782
+ overall: report.overall,
26783
+ counts: report.counts,
26784
+ dimensions: report.dimensions,
26785
+ findings: shown.map(present),
26786
+ hint: report.counts.block > 0 ? "Block-tier tells are held at publish \u2014 fix them. Warn/advisory are guidance. Scores are 0\u20131 (higher is better)." : "No block-tier tells. Warn/advisory findings are guidance, not gates. Re-run after edits so the publish gate stays fresh."
26787
+ },
26788
+ null,
26789
+ 2
26790
+ )}
26791
+ `
26792
+ );
26793
+ }
26794
+ });
26795
+ function present(f) {
26796
+ return {
26797
+ id: f.id,
26798
+ severity: f.severity,
26799
+ family: f.family,
26800
+ where: f.line ? `${f.file}:${f.line}` : f.file,
26801
+ snippet: f.snippet,
26802
+ note: f.note,
26803
+ ...f.brandDrift ? { brandDrift: true } : {}
26804
+ };
26805
+ }
26806
+ async function isDir(p) {
26807
+ try {
26808
+ return (await stat6(p)).isDirectory();
26809
+ } catch {
26810
+ return false;
26811
+ }
26812
+ }
26813
+
26814
+ // src/commands/landing/index.ts
26815
+ var landingCommand = defineCommand134({
26816
+ meta: {
26817
+ name: "landing",
26818
+ description: `Design-quality tools for landing pages (src/pages/<slug>/).
26819
+
26820
+ Subcommands:
26821
+ baker landing critique <slug> \u2014 deterministic design-quality critic (advisory): flags the known AI 'slop' tells (gradient text, overused fonts, side-tab borders, cream palettes, buzzword copy, broken images) tiered block/warn/advisory, respecting the client's BRAND.md. Records the critique the publish quality gate requires \u2014 run it before finishing a landing.`
26822
+ },
26823
+ subCommands: {
26824
+ critique: critiqueCommand2
26825
+ }
26826
+ });
26827
+
26828
+ // src/commands/mcp/index.ts
26829
+ import { defineCommand as defineCommand135 } from "citty";
25807
26830
  var SCOPES = ["user", "user_org", "company", "org"];
25808
26831
  function parseScope(raw) {
25809
26832
  const scope = raw === void 0 ? "company" : String(raw);
@@ -25842,7 +26865,7 @@ registerSchema({
25842
26865
  description: "List the custom MCP servers this company's chats see (org + company + your own user scope).",
25843
26866
  args: {}
25844
26867
  });
25845
- var listCommand8 = defineCommand133({
26868
+ var listCommand8 = defineCommand135({
25846
26869
  meta: { name: "list", description: "List custom MCP servers visible to this company's chats." },
25847
26870
  run: async () => {
25848
26871
  try {
@@ -25867,7 +26890,7 @@ registerSchema({
25867
26890
  header: { type: "string", description: 'Auth header "Key: Value" (repeatable)', required: false }
25868
26891
  }
25869
26892
  });
25870
- var addCommand = defineCommand133({
26893
+ var addCommand = defineCommand135({
25871
26894
  meta: {
25872
26895
  name: "add",
25873
26896
  description: `Register a custom MCP server. Tools appear as mcp__<name>__* on the NEXT message.
@@ -25908,7 +26931,7 @@ registerSchema({
25908
26931
  description: "Remove a company custom MCP server by name.",
25909
26932
  args: { name: { type: "string", description: "Server name to remove", required: true } }
25910
26933
  });
25911
- var removeCommand4 = defineCommand133({
26934
+ var removeCommand4 = defineCommand135({
25912
26935
  meta: {
25913
26936
  name: "remove",
25914
26937
  description: `Remove a company custom MCP server by name.
@@ -25930,7 +26953,7 @@ Example:
25930
26953
  }
25931
26954
  }
25932
26955
  });
25933
- var mcpCommand = defineCommand133({
26956
+ var mcpCommand = defineCommand135({
25934
26957
  meta: {
25935
26958
  name: "mcp",
25936
26959
  description: `Custom MCP servers for this company \u2014 point the agent at any HTTPS MCP endpoint.
@@ -25954,10 +26977,10 @@ Examples:
25954
26977
  });
25955
26978
 
25956
26979
  // src/commands/research/index.ts
25957
- import { defineCommand as defineCommand144 } from "citty";
26980
+ import { defineCommand as defineCommand146 } from "citty";
25958
26981
 
25959
26982
  // src/commands/research/advertisers.ts
25960
- import { defineCommand as defineCommand134 } from "citty";
26983
+ import { defineCommand as defineCommand136 } from "citty";
25961
26984
 
25962
26985
  // src/commands/research/output.ts
25963
26986
  var RESEARCH_DATA_NOTE = "Estimates based on third-party SERP data \u2014 not exact figures. Use for directional insights, not precise measurement.";
@@ -26070,7 +27093,7 @@ var FIELDS3 = {
26070
27093
  etv: "Estimated traffic value (USD)",
26071
27094
  visibility: "SERP visibility score (0-1)"
26072
27095
  };
26073
- var advertisersCommand = defineCommand134({
27096
+ var advertisersCommand = defineCommand136({
26074
27097
  meta: {
26075
27098
  name: "advertisers",
26076
27099
  description: `Find domains competing for a keyword in Google SERPs.
@@ -26117,7 +27140,7 @@ Examples:
26117
27140
  });
26118
27141
 
26119
27142
  // src/commands/research/autocomplete.ts
26120
- import { defineCommand as defineCommand135 } from "citty";
27143
+ import { defineCommand as defineCommand137 } from "citty";
26121
27144
  registerSchema({
26122
27145
  command: "research.autocomplete",
26123
27146
  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).",
@@ -26140,7 +27163,7 @@ registerSchema({
26140
27163
  var FIELDS4 = {
26141
27164
  suggestion: "Autocomplete suggestion from Google"
26142
27165
  };
26143
- var autocompleteCommand = defineCommand135({
27166
+ var autocompleteCommand = defineCommand137({
26144
27167
  meta: {
26145
27168
  name: "autocomplete",
26146
27169
  description: `Get Google Autocomplete suggestions for keyword expansion.
@@ -26186,7 +27209,7 @@ Examples:
26186
27209
  });
26187
27210
 
26188
27211
  // src/commands/research/countries.ts
26189
- import { defineCommand as defineCommand136 } from "citty";
27212
+ import { defineCommand as defineCommand138 } from "citty";
26190
27213
  registerSchema({
26191
27214
  command: "research.countries",
26192
27215
  description: "List all supported country codes for --location flag in research commands.",
@@ -26243,7 +27266,7 @@ var FIELDS5 = {
26243
27266
  code: "Country code to pass as --location",
26244
27267
  name: "Country name"
26245
27268
  };
26246
- var countriesCommand = defineCommand136({
27269
+ var countriesCommand = defineCommand138({
26247
27270
  meta: {
26248
27271
  name: "countries",
26249
27272
  description: "List all supported country codes for --location flag."
@@ -26254,7 +27277,7 @@ var countriesCommand = defineCommand136({
26254
27277
  });
26255
27278
 
26256
27279
  // src/commands/research/intent.ts
26257
- import { defineCommand as defineCommand137 } from "citty";
27280
+ import { defineCommand as defineCommand139 } from "citty";
26258
27281
  registerSchema({
26259
27282
  command: "research.intent",
26260
27283
  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.",
@@ -26277,7 +27300,7 @@ var FIELDS6 = {
26277
27300
  intent: "Primary Google Search intent: informational, navigational, commercial, transactional",
26278
27301
  probability: "Confidence score 0.0-1.0"
26279
27302
  };
26280
- var intentCommand = defineCommand137({
27303
+ var intentCommand = defineCommand139({
26281
27304
  meta: {
26282
27305
  name: "intent",
26283
27306
  description: `Classify Google Search intent for keywords. Returns intent type and confidence.
@@ -26325,7 +27348,7 @@ Examples:
26325
27348
  });
26326
27349
 
26327
27350
  // src/commands/research/keyword-gap.ts
26328
- import { defineCommand as defineCommand138 } from "citty";
27351
+ import { defineCommand as defineCommand140 } from "citty";
26329
27352
  registerSchema({
26330
27353
  command: "research.keyword-gap",
26331
27354
  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.",
@@ -26354,7 +27377,7 @@ var FIELDS7 = {
26354
27377
  cpc: "Cost per click USD",
26355
27378
  their_position: "Competitor's ranking position"
26356
27379
  };
26357
- var keywordGapCommand = defineCommand138({
27380
+ var keywordGapCommand = defineCommand140({
26358
27381
  meta: {
26359
27382
  name: "keyword-gap",
26360
27383
  description: `Find keywords a competitor has that you don't. Supports pagination via --offset.
@@ -26428,7 +27451,7 @@ Examples:
26428
27451
  });
26429
27452
 
26430
27453
  // src/commands/research/keywords-for-site.ts
26431
- import { defineCommand as defineCommand139 } from "citty";
27454
+ import { defineCommand as defineCommand141 } from "citty";
26432
27455
  registerSchema({
26433
27456
  command: "research.keywords-for-site",
26434
27457
  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.",
@@ -26461,7 +27484,7 @@ var FIELDS8 = {
26461
27484
  competition: "LOW, MEDIUM, or HIGH",
26462
27485
  competition_index: "Competition score 0-100"
26463
27486
  };
26464
- var keywordsForSiteCommand = defineCommand139({
27487
+ var keywordsForSiteCommand = defineCommand141({
26465
27488
  meta: {
26466
27489
  name: "keywords-for-site",
26467
27490
  description: `Get keywords a competitor targets in Google. Use --type to filter paid/organic.
@@ -26514,7 +27537,7 @@ Examples:
26514
27537
  });
26515
27538
 
26516
27539
  // src/commands/research/languages.ts
26517
- import { defineCommand as defineCommand140 } from "citty";
27540
+ import { defineCommand as defineCommand142 } from "citty";
26518
27541
  registerSchema({
26519
27542
  command: "research.languages",
26520
27543
  description: "List all supported language codes for --language flag in research commands.",
@@ -26544,7 +27567,7 @@ var FIELDS9 = {
26544
27567
  code: "Language code to pass as --language",
26545
27568
  name: "Language name (also accepted by --language)"
26546
27569
  };
26547
- var languagesCommand2 = defineCommand140({
27570
+ var languagesCommand2 = defineCommand142({
26548
27571
  meta: {
26549
27572
  name: "languages",
26550
27573
  description: "List all supported language codes for --language flag."
@@ -26555,7 +27578,7 @@ var languagesCommand2 = defineCommand140({
26555
27578
  });
26556
27579
 
26557
27580
  // src/commands/research/lighthouse.ts
26558
- import { defineCommand as defineCommand141 } from "citty";
27581
+ import { defineCommand as defineCommand143 } from "citty";
26559
27582
  registerSchema({
26560
27583
  command: "research.lighthouse",
26561
27584
  description: "Landing page performance audit. Returns metrics that affect Google Ads Quality Score and CPC.",
@@ -26574,7 +27597,7 @@ var FIELDS10 = {
26574
27597
  speed_index_ms: "Speed Index in ms (good: < 3400)",
26575
27598
  interactive_ms: "Time to Interactive in ms (good: < 3800)"
26576
27599
  };
26577
- var lighthouseCommand = defineCommand141({
27600
+ var lighthouseCommand = defineCommand143({
26578
27601
  meta: {
26579
27602
  name: "lighthouse",
26580
27603
  description: `Landing page performance audit. Metrics affecting Google Ads Quality Score.
@@ -26612,7 +27635,7 @@ Examples:
26612
27635
  });
26613
27636
 
26614
27637
  // src/commands/research/relevant-pages.ts
26615
- import { defineCommand as defineCommand142 } from "citty";
27638
+ import { defineCommand as defineCommand144 } from "citty";
26616
27639
  registerSchema({
26617
27640
  command: "research.relevant-pages",
26618
27641
  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).",
@@ -26638,7 +27661,7 @@ var FIELDS11 = {
26638
27661
  keywords: "Total organic keywords the page ranks for",
26639
27662
  top_10: "Keywords in positions 1-10"
26640
27663
  };
26641
- var relevantPagesCommand = defineCommand142({
27664
+ var relevantPagesCommand = defineCommand144({
26642
27665
  meta: {
26643
27666
  name: "relevant-pages",
26644
27667
  description: `Get the top pages of a competitor domain with traffic data.
@@ -26684,7 +27707,7 @@ Examples:
26684
27707
  });
26685
27708
 
26686
27709
  // src/commands/research/web.ts
26687
- import { defineCommand as defineCommand143 } from "citty";
27710
+ import { defineCommand as defineCommand145 } from "citty";
26688
27711
  registerSchema({
26689
27712
  command: "research.web",
26690
27713
  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).",
@@ -26735,7 +27758,7 @@ async function runDeepResearch(question) {
26735
27758
  }
26736
27759
  throw new Error("Deep research timed out");
26737
27760
  }
26738
- var webCommand = defineCommand143({
27761
+ var webCommand = defineCommand145({
26739
27762
  meta: {
26740
27763
  name: "web",
26741
27764
  description: `Search the web with AI to answer any open-ended marketing question. Uses live internet data via Google Search.
@@ -26795,7 +27818,7 @@ Examples:
26795
27818
  });
26796
27819
 
26797
27820
  // src/commands/research/index.ts
26798
- var researchCommand = defineCommand144({
27821
+ var researchCommand = defineCommand146({
26799
27822
  meta: {
26800
27823
  name: "research",
26801
27824
  description: `Competitive intelligence and AI-powered research commands.
@@ -26835,10 +27858,10 @@ Examples:
26835
27858
  });
26836
27859
 
26837
27860
  // src/commands/scheduled-actions/index.ts
26838
- import { defineCommand as defineCommand151 } from "citty";
27861
+ import { defineCommand as defineCommand153 } from "citty";
26839
27862
 
26840
27863
  // src/commands/scheduled-actions/create.ts
26841
- import { defineCommand as defineCommand145 } from "citty";
27864
+ import { defineCommand as defineCommand147 } from "citty";
26842
27865
 
26843
27866
  // src/commands/scheduled-actions/shared.ts
26844
27867
  var TEMP_SCHEDULED_ACTION_PREFIX = "temp_sched_";
@@ -26953,7 +27976,7 @@ registerSchema({
26953
27976
  prompt: { type: "string", description: "Additional prompt instructions for the spawned agent", required: false }
26954
27977
  }
26955
27978
  });
26956
- var createCommand2 = defineCommand145({
27979
+ var createCommand2 = defineCommand147({
26957
27980
  meta: {
26958
27981
  name: "create",
26959
27982
  description: 'Stage a scheduled action. Example: baker scheduled-actions create --name "Weekly report" --description "..." --cron "0 9 * * MON"'
@@ -27002,7 +28025,7 @@ var createCommand2 = defineCommand145({
27002
28025
  });
27003
28026
 
27004
28027
  // src/commands/scheduled-actions/delete.ts
27005
- import { defineCommand as defineCommand146 } from "citty";
28028
+ import { defineCommand as defineCommand148 } from "citty";
27006
28029
  registerSchema({
27007
28030
  command: "scheduled-actions.delete",
27008
28031
  description: "Stage deletion of a published scheduled action or cancellation of a temp_sched_* draft creation.",
@@ -27010,7 +28033,7 @@ registerSchema({
27010
28033
  id: { type: "string", description: "Published scheduled action ID or temp_sched_* draft ID", required: true }
27011
28034
  }
27012
28035
  });
27013
- var deleteCommand2 = defineCommand146({
28036
+ var deleteCommand2 = defineCommand148({
27014
28037
  meta: {
27015
28038
  name: "delete",
27016
28039
  description: "Stage scheduled action deletion. Example: baker scheduled-actions delete <id-or-temp_sched_id>"
@@ -27039,7 +28062,7 @@ var deleteCommand2 = defineCommand146({
27039
28062
  });
27040
28063
 
27041
28064
  // src/commands/scheduled-actions/get.ts
27042
- import { defineCommand as defineCommand147 } from "citty";
28065
+ import { defineCommand as defineCommand149 } from "citty";
27043
28066
  registerSchema({
27044
28067
  command: "scheduled-actions.get",
27045
28068
  description: "Get a published scheduled action or a temp_sched_* draft-created scheduled action.",
@@ -27047,7 +28070,7 @@ registerSchema({
27047
28070
  id: { type: "string", description: "Published scheduled action ID or temp_sched_* draft ID", required: true }
27048
28071
  }
27049
28072
  });
27050
- var getCommand3 = defineCommand147({
28073
+ var getCommand3 = defineCommand149({
27051
28074
  meta: {
27052
28075
  name: "get",
27053
28076
  description: "Get a scheduled action. Example: baker scheduled-actions get <id-or-temp_sched_id>"
@@ -27084,13 +28107,13 @@ var getCommand3 = defineCommand147({
27084
28107
  });
27085
28108
 
27086
28109
  // src/commands/scheduled-actions/list.ts
27087
- import { defineCommand as defineCommand148 } from "citty";
28110
+ import { defineCommand as defineCommand150 } from "citty";
27088
28111
  registerSchema({
27089
28112
  command: "scheduled-actions.list",
27090
28113
  description: "List published scheduled actions. Includes draft state when BAKER_CHAT_ID is set.",
27091
28114
  args: {}
27092
28115
  });
27093
- var listCommand9 = defineCommand148({
28116
+ var listCommand9 = defineCommand150({
27094
28117
  meta: {
27095
28118
  name: "list",
27096
28119
  description: "List scheduled actions. Includes staged draft ops when BAKER_CHAT_ID is set."
@@ -27111,7 +28134,7 @@ var listCommand9 = defineCommand148({
27111
28134
  });
27112
28135
 
27113
28136
  // src/commands/scheduled-actions/trigger.ts
27114
- import { defineCommand as defineCommand149 } from "citty";
28137
+ import { defineCommand as defineCommand151 } from "citty";
27115
28138
  registerSchema({
27116
28139
  command: "scheduled-actions.trigger",
27117
28140
  description: "Immediately trigger a published scheduled action. Does not require BAKER_CHAT_ID and rejects temp_sched_* IDs.",
@@ -27119,7 +28142,7 @@ registerSchema({
27119
28142
  id: { type: "string", description: "Published scheduled action ID", required: true }
27120
28143
  }
27121
28144
  });
27122
- var triggerCommand = defineCommand149({
28145
+ var triggerCommand = defineCommand151({
27123
28146
  meta: {
27124
28147
  name: "trigger",
27125
28148
  description: "Immediately trigger a published scheduled action. Example: baker scheduled-actions trigger <id>"
@@ -27156,7 +28179,7 @@ var triggerCommand = defineCommand149({
27156
28179
  });
27157
28180
 
27158
28181
  // src/commands/scheduled-actions/update.ts
27159
- import { defineCommand as defineCommand150 } from "citty";
28182
+ import { defineCommand as defineCommand152 } from "citty";
27160
28183
  registerSchema({
27161
28184
  command: "scheduled-actions.update",
27162
28185
  description: "Stage an update to a published scheduled action or temp_sched_* draft-created scheduled action.",
@@ -27181,7 +28204,7 @@ registerSchema({
27181
28204
  prompt: { type: "string", description: "Replacement additional spawned-agent instructions", required: false }
27182
28205
  }
27183
28206
  });
27184
- var updateCommand2 = defineCommand150({
28207
+ var updateCommand2 = defineCommand152({
27185
28208
  meta: {
27186
28209
  name: "update",
27187
28210
  description: "Stage a scheduled action update. Example: baker scheduled-actions update <id> --enabled false"
@@ -27252,7 +28275,7 @@ var updateCommand2 = defineCommand150({
27252
28275
  });
27253
28276
 
27254
28277
  // src/commands/scheduled-actions/index.ts
27255
- var scheduledActionsCommand = defineCommand151({
28278
+ var scheduledActionsCommand = defineCommand153({
27256
28279
  meta: {
27257
28280
  name: "scheduled-actions",
27258
28281
  description: `Manage Scheduled Actions. Subcommands: list, get, create, update, delete, trigger.
@@ -27278,8 +28301,8 @@ Examples:
27278
28301
  });
27279
28302
 
27280
28303
  // src/commands/schema.ts
27281
- import { defineCommand as defineCommand152 } from "citty";
27282
- var schemaCommand = defineCommand152({
28304
+ import { defineCommand as defineCommand154 } from "citty";
28305
+ var schemaCommand = defineCommand154({
27283
28306
  meta: {
27284
28307
  name: "schema",
27285
28308
  description: "Inspect command argument schemas (for AI agent introspection). Lists all commands if no argument given. Example: baker schema images.search"
@@ -27315,7 +28338,7 @@ var schemaCommand = defineCommand152({
27315
28338
  });
27316
28339
 
27317
28340
  // src/commands/tags/index.ts
27318
- import { defineCommand as defineCommand153 } from "citty";
28341
+ import { defineCommand as defineCommand155 } from "citty";
27319
28342
 
27320
28343
  // src/commands/tags/shared.ts
27321
28344
  function failApi3(err) {
@@ -27381,7 +28404,7 @@ async function listTags(json) {
27381
28404
  failApi3(err);
27382
28405
  }
27383
28406
  }
27384
- var listCommand10 = defineCommand153({
28407
+ var listCommand10 = defineCommand155({
27385
28408
  meta: {
27386
28409
  name: "list",
27387
28410
  description: "Effective tags for this chat (production + staged), with each tag's full readable config (secrets excluded) \u2014 reuse a stored value to pre-fill a change rather than asking the user. Refs printed here are what flow side-effect tagIds should use. Example: baker tags list"
@@ -27400,7 +28423,7 @@ async function listDraft3() {
27400
28423
  failApi3(err);
27401
28424
  }
27402
28425
  }
27403
- var draftCommand3 = defineCommand153({
28426
+ var draftCommand3 = defineCommand155({
27404
28427
  meta: {
27405
28428
  name: "draft",
27406
28429
  description: "Review the tag changes staged in this chat (read-only). Staged changes were approved via request_tag_input and apply when the chat is published; to amend or drop one, propose a follow-up change through the same tool (a delete on a tag_temp_* ref drops the staged create)."
@@ -27409,7 +28432,7 @@ var draftCommand3 = defineCommand153({
27409
28432
  await listDraft3();
27410
28433
  }
27411
28434
  });
27412
- var tagsCommand3 = defineCommand153({
28435
+ var tagsCommand3 = defineCommand155({
27413
28436
  meta: {
27414
28437
  name: "tags",
27415
28438
  description: `Read the client's marketing/analytics tags (Meta pixel, GA4, Google Ads, GTM, Clarity, Hotjar, \u2026) \u2014 production tags plus the changes staged in this chat.
@@ -27434,10 +28457,10 @@ Examples:
27434
28457
  });
27435
28458
 
27436
28459
  // src/commands/testimonials/index.ts
27437
- import { defineCommand as defineCommand157 } from "citty";
28460
+ import { defineCommand as defineCommand159 } from "citty";
27438
28461
 
27439
28462
  // src/commands/testimonials/get.ts
27440
- import { defineCommand as defineCommand154 } from "citty";
28463
+ import { defineCommand as defineCommand156 } from "citty";
27441
28464
  registerSchema({
27442
28465
  command: "testimonials.get",
27443
28466
  description: "Get a single testimonial by ID",
@@ -27445,7 +28468,7 @@ registerSchema({
27445
28468
  id: { type: "string", description: "Testimonial ID", required: true }
27446
28469
  }
27447
28470
  });
27448
- var getCommand4 = defineCommand154({
28471
+ var getCommand4 = defineCommand156({
27449
28472
  meta: { name: "get", description: "Get a single testimonial by ID. Example: baker testimonials get j571abc123" },
27450
28473
  args: {
27451
28474
  id: { type: "positional", description: "Testimonial ID", required: false },
@@ -27482,7 +28505,7 @@ var getCommand4 = defineCommand154({
27482
28505
  });
27483
28506
 
27484
28507
  // src/commands/testimonials/list.ts
27485
- import { defineCommand as defineCommand155 } from "citty";
28508
+ import { defineCommand as defineCommand157 } from "citty";
27486
28509
  registerSchema({
27487
28510
  command: "testimonials.list",
27488
28511
  description: "List testimonials with optional filters.",
@@ -27512,7 +28535,7 @@ registerSchema({
27512
28535
  limit: { type: "number", description: "Max results (default 50)", required: false, default: 50 }
27513
28536
  }
27514
28537
  });
27515
- var listCommand11 = defineCommand155({
28538
+ var listCommand11 = defineCommand157({
27516
28539
  meta: {
27517
28540
  name: "list",
27518
28541
  description: "List testimonials with optional filters. Example: baker testimonials list --source google --sentiment positive"
@@ -27561,7 +28584,7 @@ var listCommand11 = defineCommand155({
27561
28584
  });
27562
28585
 
27563
28586
  // src/commands/testimonials/search.ts
27564
- import { defineCommand as defineCommand156 } from "citty";
28587
+ import { defineCommand as defineCommand158 } from "citty";
27565
28588
  function languageBiasHint(results, requestedLanguage) {
27566
28589
  if (requestedLanguage) {
27567
28590
  return null;
@@ -27639,7 +28662,7 @@ function buildSearchRequest(query, args) {
27639
28662
  }
27640
28663
  return body;
27641
28664
  }
27642
- var searchCommand2 = defineCommand156({
28665
+ var searchCommand2 = defineCommand158({
27643
28666
  meta: {
27644
28667
  name: "search",
27645
28668
  description: "Semantic search testimonials by text query. Uses hybrid BM25 + vector + reranking. Example: baker testimonials search 'great service' --rating-min 4"
@@ -27695,7 +28718,7 @@ var searchCommand2 = defineCommand156({
27695
28718
  var tagsCommand4 = makeTagsCommand("testimonials", "testimonial", "/api/testimonials/tags");
27696
28719
 
27697
28720
  // src/commands/testimonials/index.ts
27698
- var testimonialsCommand = defineCommand157({
28721
+ var testimonialsCommand = defineCommand159({
27699
28722
  meta: {
27700
28723
  name: "testimonials",
27701
28724
  description: `Find and browse testimonials in Baker. Subcommands: search, get, list, tags.
@@ -27716,10 +28739,10 @@ Examples:
27716
28739
  });
27717
28740
 
27718
28741
  // src/commands/videos/index.ts
27719
- import { defineCommand as defineCommand162 } from "citty";
28742
+ import { defineCommand as defineCommand164 } from "citty";
27720
28743
 
27721
28744
  // src/commands/videos/delete.ts
27722
- import { defineCommand as defineCommand158 } from "citty";
28745
+ import { defineCommand as defineCommand160 } from "citty";
27723
28746
  registerSchema({
27724
28747
  command: "videos.delete",
27725
28748
  description: "Delete a video by ID",
@@ -27733,7 +28756,7 @@ registerSchema({
27733
28756
  }
27734
28757
  }
27735
28758
  });
27736
- var deleteCommand3 = defineCommand158({
28759
+ var deleteCommand3 = defineCommand160({
27737
28760
  meta: {
27738
28761
  name: "delete",
27739
28762
  description: "Delete a video by ID. Use --dry-run to preview. Example: baker videos delete j571abc123 --dry-run"
@@ -27774,7 +28797,7 @@ var deleteCommand3 = defineCommand158({
27774
28797
  });
27775
28798
 
27776
28799
  // src/commands/videos/get.ts
27777
- import { defineCommand as defineCommand159 } from "citty";
28800
+ import { defineCommand as defineCommand161 } from "citty";
27778
28801
  registerSchema({
27779
28802
  command: "videos.get",
27780
28803
  description: "Get a single video by ID",
@@ -27782,7 +28805,7 @@ registerSchema({
27782
28805
  id: { type: "string", description: "Video ID", required: true }
27783
28806
  }
27784
28807
  });
27785
- var getCommand5 = defineCommand159({
28808
+ var getCommand5 = defineCommand161({
27786
28809
  meta: { name: "get", description: "Get a single video by ID. Example: baker videos get j571abc123" },
27787
28810
  args: {
27788
28811
  id: { type: "positional", description: "Video ID", required: false },
@@ -27819,7 +28842,7 @@ var getCommand5 = defineCommand159({
27819
28842
  });
27820
28843
 
27821
28844
  // src/commands/videos/search.ts
27822
- import { defineCommand as defineCommand160 } from "citty";
28845
+ import { defineCommand as defineCommand162 } from "citty";
27823
28846
  registerSchema({
27824
28847
  command: "videos.search",
27825
28848
  description: "Search videos by text query. Only returns ready videos.",
@@ -27829,7 +28852,7 @@ registerSchema({
27829
28852
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
27830
28853
  }
27831
28854
  });
27832
- var searchCommand3 = defineCommand160({
28855
+ var searchCommand3 = defineCommand162({
27833
28856
  meta: {
27834
28857
  name: "search",
27835
28858
  description: "Semantic search videos by text query. Uses hybrid BM25 + vector + reranking. Example: baker videos search 'product demo' --tags tutorial"
@@ -27879,9 +28902,9 @@ var searchCommand3 = defineCommand160({
27879
28902
  var tagsCommand5 = makeTagsCommand("videos", "video", "/api/videos/tags");
27880
28903
 
27881
28904
  // src/commands/videos/upload.ts
27882
- import { readFile as readFile20, stat as stat5 } from "fs/promises";
28905
+ import { readFile as readFile22, stat as stat7 } from "fs/promises";
27883
28906
  import { extname as extname3 } from "path";
27884
- import { defineCommand as defineCommand161 } from "citty";
28907
+ import { defineCommand as defineCommand163 } from "citty";
27885
28908
  var MIME_MAP = {
27886
28909
  ".mp4": "video/mp4",
27887
28910
  ".mov": "video/quicktime",
@@ -27915,7 +28938,7 @@ function detectContentType(filePath) {
27915
28938
  }
27916
28939
  return mime;
27917
28940
  }
27918
- var uploadCommand2 = defineCommand161({
28941
+ var uploadCommand2 = defineCommand163({
27919
28942
  meta: {
27920
28943
  name: "upload",
27921
28944
  description: "Upload a video file to Baker via Mux direct upload. Auto-detects content type. Example: baker videos upload ./demo.mp4"
@@ -27934,7 +28957,7 @@ var uploadCommand2 = defineCommand161({
27934
28957
  }
27935
28958
  const contentType = args["content-type"] || detectContentType(filePath);
27936
28959
  if (args["dry-run"]) {
27937
- const fileStats = await stat5(filePath);
28960
+ const fileStats = await stat7(filePath);
27938
28961
  writeJson({
27939
28962
  ok: true,
27940
28963
  dryRun: true,
@@ -27944,7 +28967,7 @@ var uploadCommand2 = defineCommand161({
27944
28967
  return;
27945
28968
  }
27946
28969
  const { uploadUrl, videoId } = await apiPost("/api/videos/upload", {});
27947
- const fileBuffer = await readFile20(filePath);
28970
+ const fileBuffer = await readFile22(filePath);
27948
28971
  const uploadResponse = await fetch(uploadUrl, {
27949
28972
  method: "PUT",
27950
28973
  headers: { "Content-Type": contentType },
@@ -27969,7 +28992,7 @@ var uploadCommand2 = defineCommand161({
27969
28992
  });
27970
28993
 
27971
28994
  // src/commands/videos/index.ts
27972
- var videosCommand = defineCommand162({
28995
+ var videosCommand = defineCommand164({
27973
28996
  meta: {
27974
28997
  name: "videos",
27975
28998
  description: `Find and manage videos in Baker. Subcommands: search, get, upload, delete, tags.
@@ -27992,10 +29015,10 @@ Examples:
27992
29015
  });
27993
29016
 
27994
29017
  // src/commands/winning-ads/index.ts
27995
- import { defineCommand as defineCommand173 } from "citty";
29018
+ import { defineCommand as defineCommand175 } from "citty";
27996
29019
 
27997
29020
  // src/commands/winning-ads/advertisers.ts
27998
- import { defineCommand as defineCommand163 } from "citty";
29021
+ import { defineCommand as defineCommand165 } from "citty";
27999
29022
 
28000
29023
  // src/commands/winning-ads/shared.ts
28001
29024
  function splitList(value) {
@@ -28048,7 +29071,7 @@ function advertiserNormalizer(record, full) {
28048
29071
  last_synced_at: record.last_synced_at ?? null
28049
29072
  };
28050
29073
  }
28051
- var advertisersCommand2 = defineCommand163({
29074
+ var advertisersCommand2 = defineCommand165({
28052
29075
  meta: {
28053
29076
  name: "advertisers",
28054
29077
  description: 'List corpus advertisers by name or domain. Find your own advertiser for --exclude-advertiser, or a competitor for --advertiser-id / winners. Example: baker winning-ads advertisers "Deel" --output md'
@@ -28106,7 +29129,7 @@ var advertisersCommand2 = defineCommand163({
28106
29129
  });
28107
29130
 
28108
29131
  // src/commands/winning-ads/brief.ts
28109
- import { defineCommand as defineCommand164 } from "citty";
29132
+ import { defineCommand as defineCommand166 } from "citty";
28110
29133
  registerSchema({
28111
29134
  command: "winning-ads.brief",
28112
29135
  description: "Generate a creative brief grounded in strategically-similar winning ads. Optionally describe the target creative with --dna (JSON) and steer with --notes.",
@@ -28152,7 +29175,7 @@ function parseDna(raw) {
28152
29175
  }
28153
29176
  return parsed;
28154
29177
  }
28155
- var briefCommand = defineCommand164({
29178
+ var briefCommand = defineCommand166({
28156
29179
  meta: {
28157
29180
  name: "brief",
28158
29181
  description: `Generate a creative brief from winning references. Example: baker winning-ads brief --dna '{"angle":"cost savings"}' --notes "B2B, LinkedIn video" --k 8`
@@ -28188,7 +29211,7 @@ var briefCommand = defineCommand164({
28188
29211
  });
28189
29212
 
28190
29213
  // src/commands/winning-ads/feed.ts
28191
- import { defineCommand as defineCommand165 } from "citty";
29214
+ import { defineCommand as defineCommand167 } from "citty";
28192
29215
  function buildFeedParams(input) {
28193
29216
  const params = {};
28194
29217
  const advertiser = splitList(input.advertiser);
@@ -28240,7 +29263,7 @@ registerSchema({
28240
29263
  format: { type: "string", description: "Comma-separated formats to include (e.g. static,video)", required: false }
28241
29264
  }
28242
29265
  });
28243
- var feedCommand = defineCommand165({
29266
+ var feedCommand = defineCommand167({
28244
29267
  meta: {
28245
29268
  name: "feed",
28246
29269
  description: "Winners across every brand you follow (browse, then trim per advertiser). Example: baker winning-ads feed --per-advertiser 5 --output md"
@@ -28325,7 +29348,7 @@ var feedCommand = defineCommand165({
28325
29348
  });
28326
29349
 
28327
29350
  // src/commands/winning-ads/follow.ts
28328
- import { defineCommand as defineCommand166 } from "citty";
29351
+ import { defineCommand as defineCommand168 } from "citty";
28329
29352
  var PLATFORMS = ["meta", "linkedin"];
28330
29353
  registerSchema({
28331
29354
  command: "winning-ads.follow",
@@ -28340,7 +29363,7 @@ registerSchema({
28340
29363
  label: { type: "string", description: "Optional display label (defaults to the resolved name)", required: false }
28341
29364
  }
28342
29365
  });
28343
- var followCommand = defineCommand166({
29366
+ var followCommand = defineCommand168({
28344
29367
  meta: {
28345
29368
  name: "follow",
28346
29369
  description: 'Follow a brand to track ALL its ads \u2014 every platform and country. --platform is how we read your input, not a limit. A domain tracks both Meta + LinkedIn. Example: baker winning-ads follow "deel.com" --platform meta'
@@ -28387,7 +29410,7 @@ var followCommand = defineCommand166({
28387
29410
  });
28388
29411
 
28389
29412
  // src/commands/winning-ads/following.ts
28390
- import { defineCommand as defineCommand167 } from "citty";
29413
+ import { defineCommand as defineCommand169 } from "citty";
28391
29414
  registerSchema({
28392
29415
  command: "winning-ads.following",
28393
29416
  description: "List the brands you follow in your ad-dna library, with each one's status (ready vs still adding) and cached ad counts.",
@@ -28420,7 +29443,7 @@ function followingNormalizer(record, full) {
28420
29443
  platforms: Array.isArray(record.platforms) ? record.platforms : []
28421
29444
  };
28422
29445
  }
28423
- var followingCommand = defineCommand167({
29446
+ var followingCommand = defineCommand169({
28424
29447
  meta: {
28425
29448
  name: "following",
28426
29449
  description: "List brands you follow, with status (ready / adding\u2026) and cached counts. Example: baker winning-ads following --output md"
@@ -28455,7 +29478,7 @@ var followingCommand = defineCommand167({
28455
29478
  });
28456
29479
 
28457
29480
  // src/commands/winning-ads/patterns.ts
28458
- import { defineCommand as defineCommand168 } from "citty";
29481
+ import { defineCommand as defineCommand170 } from "citty";
28459
29482
  registerSchema({
28460
29483
  command: "winning-ads.patterns",
28461
29484
  description: "Mine what separates two cohorts of ads: pass a comma-list of winning ad ids (--winners) and a comma-list of weaker ad ids (--duds). Returns the discriminating DNA fields.",
@@ -28494,7 +29517,7 @@ function discriminatorRow(record) {
28494
29517
  top_values_duds: Array.isArray(record.top_values_b) ? record.top_values_b.join(", ") : ""
28495
29518
  };
28496
29519
  }
28497
- var patternsCommand = defineCommand168({
29520
+ var patternsCommand = defineCommand170({
28498
29521
  meta: {
28499
29522
  name: "patterns",
28500
29523
  description: "Discover what separates winning ads from weak ones. Example: baker winning-ads patterns --winners a_1,a_2,a_3 --duds a_9,a_8 --output md"
@@ -28550,7 +29573,7 @@ var patternsCommand = defineCommand168({
28550
29573
  });
28551
29574
 
28552
29575
  // src/commands/winning-ads/search.ts
28553
- import { defineCommand as defineCommand169 } from "citty";
29576
+ import { defineCommand as defineCommand171 } from "citty";
28554
29577
  registerSchema({
28555
29578
  command: "winning-ads.search",
28556
29579
  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.",
@@ -28658,7 +29681,7 @@ function buildSearchBody(args) {
28658
29681
  }
28659
29682
  return body;
28660
29683
  }
28661
- var searchCommand4 = defineCommand169({
29684
+ var searchCommand4 = defineCommand171({
28662
29685
  meta: {
28663
29686
  name: "search",
28664
29687
  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"
@@ -28773,7 +29796,7 @@ var searchCommand4 = defineCommand169({
28773
29796
  });
28774
29797
 
28775
29798
  // src/commands/winning-ads/seeds.ts
28776
- import { defineCommand as defineCommand170 } from "citty";
29799
+ import { defineCommand as defineCommand172 } from "citty";
28777
29800
  function leanRow(r) {
28778
29801
  return {
28779
29802
  key: r.key,
@@ -28801,7 +29824,7 @@ function makeSeedCommand(opts) {
28801
29824
  limit: { type: "number", description: "Max keys 1-100 (default 20)", required: false, default: 20 }
28802
29825
  }
28803
29826
  });
28804
- return defineCommand170({
29827
+ return defineCommand172({
28805
29828
  meta: { name: opts.name, description: opts.description },
28806
29829
  args: {
28807
29830
  platform: { type: "string", description: "Single platform to segment on", required: false },
@@ -28850,7 +29873,7 @@ var formatsCommand = makeSeedCommand({
28850
29873
  });
28851
29874
 
28852
29875
  // src/commands/winning-ads/unfollow.ts
28853
- import { defineCommand as defineCommand171 } from "citty";
29876
+ import { defineCommand as defineCommand173 } from "citty";
28854
29877
  registerSchema({
28855
29878
  command: "winning-ads.unfollow",
28856
29879
  description: "Stop following a brand \u2014 removes it from your ad-dna library by advertiser id.",
@@ -28858,7 +29881,7 @@ registerSchema({
28858
29881
  advertiser: { type: "string", description: "Advertiser id to unfollow", required: true }
28859
29882
  }
28860
29883
  });
28861
- var unfollowCommand = defineCommand171({
29884
+ var unfollowCommand = defineCommand173({
28862
29885
  meta: {
28863
29886
  name: "unfollow",
28864
29887
  description: "Stop following a brand by advertiser id. Example: baker winning-ads unfollow adv_123"
@@ -28879,7 +29902,7 @@ var unfollowCommand = defineCommand171({
28879
29902
  });
28880
29903
 
28881
29904
  // src/commands/winning-ads/winners.ts
28882
- import { defineCommand as defineCommand172 } from "citty";
29905
+ import { defineCommand as defineCommand174 } from "citty";
28883
29906
  registerSchema({
28884
29907
  command: "winning-ads.winners",
28885
29908
  description: "Top winning ads for one advertiser id (from `advertisers` or `following`). Returns lean winner cards; add --full for DNA + longevity.",
@@ -28889,7 +29912,7 @@ registerSchema({
28889
29912
  platform: { type: "string", description: "Filter to a single platform: meta|linkedin", required: false }
28890
29913
  }
28891
29914
  });
28892
- var winnersCommand = defineCommand172({
29915
+ var winnersCommand = defineCommand174({
28893
29916
  meta: {
28894
29917
  name: "winners",
28895
29918
  description: "Top winning ads for a specific advertiser id. Example: baker winning-ads winners adv_123 --top 15 --output md"
@@ -28939,7 +29962,7 @@ var winnersCommand = defineCommand172({
28939
29962
  });
28940
29963
 
28941
29964
  // src/commands/winning-ads/index.ts
28942
- var winningAdsCommand = defineCommand173({
29965
+ var winningAdsCommand = defineCommand175({
28943
29966
  meta: {
28944
29967
  name: "winning-ads",
28945
29968
  description: `Search the ad-dna corpus of scored "winning" ads for reference creatives to reproduce, and manage the brands your library tracks. Proxied through the Baker backend (BAKER_API_KEY) \u2014 no separate token needed.
@@ -29004,7 +30027,7 @@ function getCliVersion() {
29004
30027
  }
29005
30028
 
29006
30029
  // src/cli.ts
29007
- var main = defineCommand174({
30030
+ var main = defineCommand176({
29008
30031
  meta: {
29009
30032
  name: "baker",
29010
30033
  version: getCliVersion(),
@@ -29026,6 +30049,7 @@ Introspection: Run 'baker schema <command>' to inspect argument schemas.`
29026
30049
  creatives: creativesCommand3,
29027
30050
  flows: flowsCommand,
29028
30051
  images: imagesCommand,
30052
+ landing: landingCommand,
29029
30053
  videos: videosCommand,
29030
30054
  testimonials: testimonialsCommand,
29031
30055
  canvas: canvasCommand,