@koda-sl/baker-cli 0.253.0 → 0.259.0-dev.dcbb0c10b

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/README.md +9 -1
  2. package/dist/{chunk-P6ZNIFAQ.js → chunk-2VM2J6MB.js} +4 -4
  3. package/dist/{chunk-3FBQKD7O.js → chunk-ECPNYXOA.js} +3 -3
  4. package/dist/{chunk-2IB3IL4K.js → chunk-GXRUZ4LP.js} +2 -2
  5. package/dist/{chunk-DZUVUGEP.js → chunk-POYXIDOD.js} +5 -1
  6. package/dist/{chunk-DZUVUGEP.js.map → chunk-POYXIDOD.js.map} +1 -1
  7. package/dist/{chunk-SJPRTSGY.js → chunk-R4MBS2OI.js} +149 -36
  8. package/dist/chunk-R4MBS2OI.js.map +1 -0
  9. package/dist/{chunk-5I66YT7A.js → chunk-TBEVU7XF.js} +3 -3
  10. package/dist/cli.js +858 -678
  11. package/dist/cli.js.map +1 -1
  12. package/dist/client-7QIDRQXG.js +15 -0
  13. package/dist/engine/index.js +3 -3
  14. package/dist/{env-FWMZXMQK.js → env-HZNFGXAR.js} +4 -2
  15. package/dist/{output-WRHUJR74.js → output-MWMAXN4M.js} +5 -5
  16. package/dist/{shared-T66YQ6MB.js → shared-NFLXWGHK.js} +6 -6
  17. package/package.json +1 -1
  18. package/dist/chunk-SJPRTSGY.js.map +0 -1
  19. package/dist/client-32DO7WEZ.js +0 -15
  20. /package/dist/{chunk-P6ZNIFAQ.js.map → chunk-2VM2J6MB.js.map} +0 -0
  21. /package/dist/{chunk-3FBQKD7O.js.map → chunk-ECPNYXOA.js.map} +0 -0
  22. /package/dist/{chunk-2IB3IL4K.js.map → chunk-GXRUZ4LP.js.map} +0 -0
  23. /package/dist/{chunk-5I66YT7A.js.map → chunk-TBEVU7XF.js.map} +0 -0
  24. /package/dist/{client-32DO7WEZ.js.map → client-7QIDRQXG.js.map} +0 -0
  25. /package/dist/{env-FWMZXMQK.js.map → env-HZNFGXAR.js.map} +0 -0
  26. /package/dist/{output-WRHUJR74.js.map → output-MWMAXN4M.js.map} +0 -0
  27. /package/dist/{shared-T66YQ6MB.js.map → shared-NFLXWGHK.js.map} +0 -0
package/dist/cli.js CHANGED
@@ -58,7 +58,7 @@ import {
58
58
  ulid,
59
59
  validateCanvasDeep,
60
60
  ytDlpBlockSignal
61
- } from "./chunk-SJPRTSGY.js";
61
+ } from "./chunk-R4MBS2OI.js";
62
62
  import {
63
63
  csvOrJson,
64
64
  daysAgoIso,
@@ -67,7 +67,7 @@ import {
67
67
  resolveAccountIdArg,
68
68
  resolveEffectiveStatus,
69
69
  todayIso
70
- } from "./chunk-P6ZNIFAQ.js";
70
+ } from "./chunk-2VM2J6MB.js";
71
71
  import {
72
72
  buildQueryCacheKey,
73
73
  cacheGet,
@@ -83,17 +83,17 @@ import {
83
83
  writeAdsJson,
84
84
  writeAdsOutput,
85
85
  writeJsonEnvelope
86
- } from "./chunk-5I66YT7A.js";
86
+ } from "./chunk-TBEVU7XF.js";
87
87
  import {
88
88
  ApiError,
89
89
  apiGet,
90
90
  apiPost,
91
91
  validateConvexId
92
- } from "./chunk-3FBQKD7O.js";
92
+ } from "./chunk-ECPNYXOA.js";
93
93
  import {
94
94
  installStreamTaps,
95
95
  logInvocation
96
- } from "./chunk-2IB3IL4K.js";
96
+ } from "./chunk-GXRUZ4LP.js";
97
97
  import {
98
98
  captureBudgetMs,
99
99
  captureProxyCredentials,
@@ -105,10 +105,10 @@ import {
105
105
  requireChatId,
106
106
  resolveChatId,
107
107
  shouldEscalate
108
- } from "./chunk-DZUVUGEP.js";
108
+ } from "./chunk-POYXIDOD.js";
109
109
 
110
110
  // src/cli.ts
111
- import { defineCommand as defineCommand227, runMain } from "citty";
111
+ import { defineCommand as defineCommand228, runMain } from "citty";
112
112
 
113
113
  // src/cache-flag.ts
114
114
  var NO_CACHE_ARG = {
@@ -3682,6 +3682,7 @@ var avatarProfileSchema = z7.object({
3682
3682
  subjectDescription: z7.string().trim().min(1).max(2e3),
3683
3683
  persona: z7.string().trim().max(2e3).optional(),
3684
3684
  speech: z7.string().trim().max(2e3).optional(),
3685
+ accent: z7.string().trim().max(2e3).optional(),
3685
3686
  motion: z7.string().trim().max(2e3).optional(),
3686
3687
  wardrobe: z7.string().trim().max(2e3).optional(),
3687
3688
  setting: z7.string().trim().max(2e3).optional(),
@@ -3697,7 +3698,6 @@ var avatarSummarySchema = z7.object({
3697
3698
  /** The identity sheet to wire into `--reference`. Absent until the build settles. */
3698
3699
  sheetUrl: z7.string().optional(),
3699
3700
  coverUrl: z7.string().optional(),
3700
- hasVoice: z7.boolean(),
3701
3701
  errorMessage: z7.string().optional()
3702
3702
  });
3703
3703
  var avatarDetailSchema = avatarSummarySchema.extend({
@@ -4292,7 +4292,7 @@ var FLOW_NODE_CATALOG = [
4292
4292
  }
4293
4293
  }
4294
4294
  },
4295
- gotcha: "`fieldtype` is one of: checkbox, date_picker, date_time_range, email, hidden, input, multi_select, number, phone, select, textarea. `select`/`multi_select` need non-empty `options`; `phone` needs `defaultCountry`; a field rendered by your own code is declared `hidden`. A field your page validates itself (postal code against a service area, VAT number, order reference) also needs `testValue` \u2014 the Form's own checks invent `TestValue` otherwise and never get past that step. A field holding something that has no business on a dashboard the client's whole team can open \u2014 a date of birth, a policy or account number, a salary, anything medical or legal \u2014 takes `sensitive: true`: an email or phone is masked already, everything else is reported verbatim unless marked. A contact field that is not the visitor's own (a manager's address, the office number) takes `identity: \"none\"`, or two people are filed as one.",
4295
+ gotcha: "`fieldtype` is one of: checkbox, date_picker, date_time_range, email, hidden, input, multi_select, number, phone, select, textarea. `select`/`multi_select` need non-empty `options`; `phone` needs `defaultCountry`; a field rendered by your own code is declared `hidden`. A field your page validates itself (postal code against a service area, VAT number, order reference) also needs `testValue` \u2014 the Form's own checks invent `TestValue` otherwise and never get past that step.",
4296
4296
  reference: NODE_REF("customForm")
4297
4297
  },
4298
4298
  {
@@ -7411,7 +7411,11 @@ registerSchema({
7411
7411
  command: "actions.status",
7412
7412
  description: "Resolve one or more Work Action refs by real action ID or temp_* ref in a single batch call. When BAKER_CHAT_ID is set, a temp_* ref still staged in THIS chat resolves to status 'draft' (not 'not_found') \u2014 staged ops only become published actions on chat publish.",
7413
7413
  args: {
7414
- refs: { type: "string", description: "One or more action refs: real action IDs or temp_* refs", required: true }
7414
+ ref: {
7415
+ type: "positional",
7416
+ description: "One or more action refs: real action IDs or temp_* refs (space-separated)",
7417
+ required: true
7418
+ }
7415
7419
  }
7416
7420
  });
7417
7421
  var statusCommand = defineCommand11({
@@ -9780,11 +9784,11 @@ function rawTextEntries(value) {
9780
9784
  const values = Array.isArray(value) ? value : typeof value === "string" ? [value] : [];
9781
9785
  return values.filter((v) => typeof v === "string").flatMap((v) => v.split(",")).map((v) => v.trim()).filter(Boolean);
9782
9786
  }
9783
- function rawFileEntries(path38) {
9784
- if (typeof path38 !== "string" || path38.length === 0) {
9787
+ function rawFileEntries(path39) {
9788
+ if (typeof path39 !== "string" || path39.length === 0) {
9785
9789
  return [];
9786
9790
  }
9787
- return readFileSync2(path38, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
9791
+ return readFileSync2(path39, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
9788
9792
  }
9789
9793
  function keywordEntries(args) {
9790
9794
  const defaultMatch = typeof args["match-type"] === "string" ? args["match-type"].toUpperCase() : void 0;
@@ -9807,19 +9811,19 @@ function keywordEntries(args) {
9807
9811
  }
9808
9812
  return entries;
9809
9813
  }
9810
- function loadJsonFileArg(path38) {
9811
- if (typeof path38 !== "string" || path38.length === 0) {
9814
+ function loadJsonFileArg(path39) {
9815
+ if (typeof path39 !== "string" || path39.length === 0) {
9812
9816
  return {};
9813
9817
  }
9814
9818
  try {
9815
- const parsed = JSON.parse(readFileSync2(path38, "utf8"));
9819
+ const parsed = JSON.parse(readFileSync2(path39, "utf8"));
9816
9820
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
9817
- failWriteValidation(`${path38} must contain a JSON object`);
9821
+ failWriteValidation(`${path39} must contain a JSON object`);
9818
9822
  }
9819
9823
  return parsed;
9820
9824
  } catch (err) {
9821
9825
  if (err instanceof SyntaxError) {
9822
- failWriteValidation(`${path38} is not valid JSON: ${err.message}`);
9826
+ failWriteValidation(`${path39} is not valid JSON: ${err.message}`);
9823
9827
  }
9824
9828
  throw err;
9825
9829
  }
@@ -9949,10 +9953,10 @@ async function stageUpdate(kind, customerId, target, payload, hints) {
9949
9953
  async function stageTarget(kind, customerId, target, hints) {
9950
9954
  await stageGoogleOp({ kind, customerId, target }, hints);
9951
9955
  }
9952
- async function draftAction(path38, body, chat) {
9956
+ async function draftAction(path39, body, chat) {
9953
9957
  try {
9954
9958
  const chatId = resolveChatId(chat);
9955
- const response = await apiPost(path38, { chatId, ...body });
9959
+ const response = await apiPost(path39, { chatId, ...body });
9956
9960
  writeJsonEnvelope(response);
9957
9961
  } catch (err) {
9958
9962
  handleGoogleError(err);
@@ -12316,10 +12320,10 @@ var analyticsPresetSchema = z24.enum([
12316
12320
  */
12317
12321
  "geo",
12318
12322
  /**
12319
- * Which machines are fetching this site.
12323
+ * Which machines are fetching this site, and whether they are genuine.
12320
12324
  *
12321
- * Every other preset here excludes them, which is correct — a crawler is not
12322
- * an audience and a verify check now enforces that rather than trusting it. The rows are kept, though, and this is what
12325
+ * Every other preset here ends "and exclude the bots", which is correct — a
12326
+ * crawler is not an audience. The rows are kept, though, and this is what
12323
12327
  * they are kept for: whether Google is still crawling a page that was
12324
12328
  * republished, whether Meta fetched the link preview before spend went
12325
12329
  * behind the URL, and how much of the traffic is now AI answer engines.
@@ -12989,6 +12993,12 @@ var analyticsBotRowSchema = z24.object({
12989
12993
  bot: z24.string(),
12990
12994
  /** `search` | `ai` | `social` | `seo` | `monitoring` | `tool` | `automation`. */
12991
12995
  category: z24.string(),
12996
+ /**
12997
+ * Cloudflare confirmed it by the network it came from, rather than believing
12998
+ * its user agent. A page fetched only by unverified self-declared Googlebots
12999
+ * was not crawled by Google.
13000
+ */
13001
+ verified: z24.boolean(),
12992
13002
  hits: z24.number().int().nonnegative(),
12993
13003
  pages: z24.number().int().nonnegative(),
12994
13004
  lastSeen: z24.string(),
@@ -15160,19 +15170,19 @@ function failWriteValidation2(message) {
15160
15170
  writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
15161
15171
  process.exit(1);
15162
15172
  }
15163
- function loadJsonFileArg2(path38) {
15164
- if (typeof path38 !== "string" || path38.length === 0) {
15173
+ function loadJsonFileArg2(path39) {
15174
+ if (typeof path39 !== "string" || path39.length === 0) {
15165
15175
  return {};
15166
15176
  }
15167
15177
  try {
15168
- const parsed = JSON.parse(readFileSync4(path38, "utf8"));
15178
+ const parsed = JSON.parse(readFileSync4(path39, "utf8"));
15169
15179
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
15170
- failWriteValidation2(`${path38} must contain a JSON object`);
15180
+ failWriteValidation2(`${path39} must contain a JSON object`);
15171
15181
  }
15172
15182
  return parsed;
15173
15183
  } catch (err) {
15174
15184
  if (err instanceof SyntaxError) {
15175
- failWriteValidation2(`${path38} is not valid JSON: ${err.message}`);
15185
+ failWriteValidation2(`${path39} is not valid JSON: ${err.message}`);
15176
15186
  }
15177
15187
  throw err;
15178
15188
  }
@@ -15257,15 +15267,15 @@ function parseLocaleFlag(value) {
15257
15267
  }
15258
15268
  return { language: match[1], country: match[2].toUpperCase() };
15259
15269
  }
15260
- function loadTargetingFileArg(path38) {
15261
- if (typeof path38 !== "string" || path38.length === 0) {
15270
+ function loadTargetingFileArg(path39) {
15271
+ if (typeof path39 !== "string" || path39.length === 0) {
15262
15272
  return void 0;
15263
15273
  }
15264
- const parsed = loadJsonFileArg2(path38);
15274
+ const parsed = loadJsonFileArg2(path39);
15265
15275
  const criteria = parsed.targetingCriteria ?? parsed;
15266
15276
  if (!criteria.include) {
15267
15277
  failWriteValidation2(
15268
- `${path38} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
15278
+ `${path39} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
15269
15279
  );
15270
15280
  }
15271
15281
  return criteria;
@@ -15300,14 +15310,14 @@ function parseCsvLine(line) {
15300
15310
  cells.push(current);
15301
15311
  return cells.map((cell2) => cell2.trim());
15302
15312
  }
15303
- function parseListFileArg(path38, maxRows) {
15304
- if (typeof path38 !== "string" || path38.length === 0) {
15313
+ function parseListFileArg(path39, maxRows) {
15314
+ if (typeof path39 !== "string" || path39.length === 0) {
15305
15315
  return void 0;
15306
15316
  }
15307
- const raw = readFileSync4(path38, "utf8");
15317
+ const raw = readFileSync4(path39, "utf8");
15308
15318
  const lines = raw.split(/\r?\n/).filter((line) => line.trim().length > 0);
15309
15319
  if (lines.length < 2) {
15310
- failWriteValidation2(`${path38} needs a header row and at least one data row`);
15320
+ failWriteValidation2(`${path39} needs a header row and at least one data row`);
15311
15321
  }
15312
15322
  const columns = parseCsvLine(lines[0]).map((column) => column.trim());
15313
15323
  const rows = [];
@@ -15326,7 +15336,7 @@ function parseListFileArg(path38, maxRows) {
15326
15336
  }
15327
15337
  }
15328
15338
  if (rows.length > maxRows) {
15329
- failWriteValidation2(`${path38} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
15339
+ failWriteValidation2(`${path39} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
15330
15340
  }
15331
15341
  return { columns, rows };
15332
15342
  }
@@ -15422,11 +15432,11 @@ function readPositionals(args) {
15422
15432
  function splitIdList(raw) {
15423
15433
  return raw.split(",").map((id) => id.trim()).filter(Boolean);
15424
15434
  }
15425
- function idsFileEntries(path38) {
15426
- if (typeof path38 !== "string" || path38.length === 0) {
15435
+ function idsFileEntries(path39) {
15436
+ if (typeof path39 !== "string" || path39.length === 0) {
15427
15437
  return [];
15428
15438
  }
15429
- return readFileSync4(path38, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).flatMap(splitIdList);
15439
+ return readFileSync4(path39, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).flatMap(splitIdList);
15430
15440
  }
15431
15441
  function requireTargets(args, entity) {
15432
15442
  const positionals = readPositionals(args);
@@ -18047,9 +18057,9 @@ function compactRow(row) {
18047
18057
  ...destination.postUrn ? { postUrn: destination.postUrn } : {}
18048
18058
  };
18049
18059
  }
18050
- function readPath(row, path38) {
18060
+ function readPath(row, path39) {
18051
18061
  let current = row;
18052
- for (const segment of path38.split(".")) {
18062
+ for (const segment of path39.split(".")) {
18053
18063
  const record = asRecord2(current);
18054
18064
  if (!record) return void 0;
18055
18065
  current = record[segment];
@@ -18059,10 +18069,10 @@ function readPath(row, path38) {
18059
18069
  function projectFields(rows, paths) {
18060
18070
  return rows.map((row) => {
18061
18071
  const projected = {};
18062
- for (const path38 of paths) {
18063
- const value = readPath(row, path38);
18072
+ for (const path39 of paths) {
18073
+ const value = readPath(row, path39);
18064
18074
  if (value !== void 0) {
18065
- projected[path38] = value;
18075
+ projected[path39] = value;
18066
18076
  }
18067
18077
  }
18068
18078
  return projected;
@@ -19260,14 +19270,6 @@ var CTA_TYPES2 = [
19260
19270
  "BUY_TICKETS",
19261
19271
  "NO_BUTTON"
19262
19272
  ];
19263
- var LEAD_FORM_CTA_TYPES = [
19264
- "APPLY_NOW",
19265
- "DOWNLOAD",
19266
- "GET_QUOTE",
19267
- "LEARN_MORE",
19268
- "SIGN_UP",
19269
- "SUBSCRIBE"
19270
- ];
19271
19273
  var AD_FORMATS2 = ["SINGLE_IMAGE", "CAROUSEL", "SINGLE_VIDEO"];
19272
19274
  var ENROLL_STATUSES = ["OPT_IN", "OPT_OUT"];
19273
19275
  var STAGEABLE_CREATE_STATUSES3 = ["ACTIVE", "PAUSED"];
@@ -19370,11 +19372,11 @@ var updateStatusSchema = z25.enum(UPDATE_STATUSES);
19370
19372
  function currencyMinimums2(currencyCode) {
19371
19373
  return CURRENCY_MINIMUMS2[currencyCode] ?? DEFAULT_CURRENCY_MINIMUM2;
19372
19374
  }
19373
- function validateDailyBudgetFloor(money, ctx, path38) {
19375
+ function validateDailyBudgetFloor(money, ctx, path39) {
19374
19376
  if (money?.currencyCode) {
19375
19377
  const min = currencyMinimums2(money.currencyCode).dailyBudgetMin;
19376
19378
  if (Number(money.amount) < min) {
19377
- ctx.addIssue({ code: "custom", path: path38, message: `below the ${min} ${money.currencyCode} daily minimum` });
19379
+ ctx.addIssue({ code: "custom", path: path39, message: `below the ${min} ${money.currencyCode} daily minimum` });
19378
19380
  }
19379
19381
  }
19380
19382
  }
@@ -19690,23 +19692,7 @@ var descriptionSchema = z25.string().min(1).max(META_LIMITS.creative.description
19690
19692
  var callToActionSchema = z25.object({
19691
19693
  type: z25.enum(CTA_TYPES2),
19692
19694
  /** Overrides the base link for the CTA button; defaults to the ad's link. */
19693
- link: httpsUrlSchema3.optional(),
19694
- /**
19695
- * The instant form the button opens — Meta's `call_to_action.value.lead_gen_form_id`.
19696
- * This is the ONLY place an instant form is named: the ad set says leads open
19697
- * on the ad (`destination_type: ON_AD`), and the creative says which form.
19698
- * Without it Meta refuses every ad in that ad set with subcode 3390001,
19699
- * "Choose or create an instant form for your leads campaign".
19700
- */
19701
- lead_gen_form_id: z25.string().regex(NUMERIC_ID_REGEX3).optional()
19702
- }).superRefine((p, ctx) => {
19703
- if (p.lead_gen_form_id && !LEAD_FORM_CTA_TYPES.some((allowed) => allowed === p.type)) {
19704
- ctx.addIssue({
19705
- code: "custom",
19706
- path: ["type"],
19707
- message: `${p.type} cannot open an instant form \u2014 Meta refuses it (subcode 1856030). Use one of ${LEAD_FORM_CTA_TYPES.join(", ")}, or drop lead_gen_form_id if this ad should go to the website.`
19708
- });
19709
- }
19695
+ link: httpsUrlSchema3.optional()
19710
19696
  });
19711
19697
  var creativeEnhancementsSchema = z25.object({
19712
19698
  standardEnhancements: z25.enum(ENROLL_STATUSES).optional(),
@@ -19804,19 +19790,6 @@ var carouselCreativeSchema2 = z25.object({
19804
19790
  link: httpsUrlSchema3.optional(),
19805
19791
  call_to_action: callToActionSchema.optional(),
19806
19792
  cards: z25.array(carouselCardSchema).min(META_LIMITS.creative.carouselCardsMin).max(META_LIMITS.creative.carouselCardsMax)
19807
- }).superRefine((p, ctx) => {
19808
- const forms = new Set(
19809
- [p.call_to_action?.lead_gen_form_id, ...p.cards.map((card) => card.call_to_action?.lead_gen_form_id)].filter(
19810
- Boolean
19811
- )
19812
- );
19813
- if (forms.size > 1) {
19814
- ctx.addIssue({
19815
- code: "custom",
19816
- path: ["cards"],
19817
- message: `every card of a carousel must open the SAME instant form \u2014 this one names ${[...forms].join(" and ")}`
19818
- });
19819
- }
19820
19793
  });
19821
19794
  var dynamicImageSchema = z25.object({ ...imageMediaFields }).refine((p) => countImageRefs(p) === 1, "each dynamic image needs exactly one reference");
19822
19795
  var dynamicVideoSchema = z25.object({
@@ -20072,19 +20045,19 @@ function failWriteValidation3(message) {
20072
20045
  writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
20073
20046
  process.exit(1);
20074
20047
  }
20075
- function loadJsonFileArg3(path38) {
20076
- if (typeof path38 !== "string" || path38.length === 0) {
20048
+ function loadJsonFileArg3(path39) {
20049
+ if (typeof path39 !== "string" || path39.length === 0) {
20077
20050
  return {};
20078
20051
  }
20079
20052
  try {
20080
- const parsed = JSON.parse(readFileSync8(path38, "utf8"));
20053
+ const parsed = JSON.parse(readFileSync8(path39, "utf8"));
20081
20054
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
20082
- failWriteValidation3(`${path38} must contain a JSON object`);
20055
+ failWriteValidation3(`${path39} must contain a JSON object`);
20083
20056
  }
20084
20057
  return parsed;
20085
20058
  } catch (err) {
20086
20059
  if (err instanceof SyntaxError) {
20087
- failWriteValidation3(`${path38} is not valid JSON: ${err.message}`);
20060
+ failWriteValidation3(`${path39} is not valid JSON: ${err.message}`);
20088
20061
  }
20089
20062
  throw err;
20090
20063
  }
@@ -20323,10 +20296,7 @@ var adSetWriteArgs = {
20323
20296
  "daily-budget": { type: "string", description: "Daily budget (ABO; omit if the campaign uses CBO)" },
20324
20297
  "lifetime-budget": { type: "string", description: "Lifetime budget (needs --end)" },
20325
20298
  "bid-amount": { type: "string", description: "Bid amount (required for COST_CAP / bid-cap strategies)" },
20326
- "bid-strategy": {
20327
- type: "string",
20328
- description: "LOWEST_COST_WITHOUT_CAP (auto-bid) | LOWEST_COST_WITH_BID_CAP | COST_CAP \u2014 needs --bid-amount. Omit and Baker sets auto-bid; never leave it to Meta, whose default is a bid cap that then fails without an amount."
20329
- },
20299
+ "bid-strategy": { type: "string", description: "Bid strategy override" },
20330
20300
  currency: { type: "string", description: "3-letter currency (defaults to the account currency)" },
20331
20301
  start: { type: "string", description: "Start time" },
20332
20302
  end: { type: "string", description: "End time" },
@@ -20393,7 +20363,7 @@ function creativePayloadFromFlags(args) {
20393
20363
  headline: args.headline,
20394
20364
  description: args.description,
20395
20365
  caption: args.caption,
20396
- call_to_action: creativeCta(args),
20366
+ call_to_action: args.cta ? { type: upper(args.cta) } : void 0,
20397
20367
  imageHash: args["image-hash"],
20398
20368
  imageRef: args["image-ref"],
20399
20369
  videoId: args["video-id"],
@@ -20403,26 +20373,6 @@ function creativePayloadFromFlags(args) {
20403
20373
  enhancements
20404
20374
  };
20405
20375
  }
20406
- function ctaPatchFromFlags(args) {
20407
- const patch = {
20408
- ...args.cta ? { type: String(upper(args.cta)) } : {},
20409
- ...args["lead-form"] === void 0 ? {} : { lead_gen_form_id: String(args["lead-form"]) }
20410
- };
20411
- return Object.keys(patch).length > 0 ? patch : void 0;
20412
- }
20413
- function creativeCta(args) {
20414
- const patch = ctaPatchFromFlags(args);
20415
- return patch ? { type: "SIGN_UP", ...patch } : void 0;
20416
- }
20417
- function creativeContentPatch2(file, args) {
20418
- const cta = ctaPatchFromFlags(args);
20419
- if (!cta) {
20420
- return void 0;
20421
- }
20422
- const fileContent = file.content ?? {};
20423
- const fileCta = fileContent.call_to_action ?? {};
20424
- return { ...fileContent, call_to_action: { ...fileCta, ...cta } };
20425
- }
20426
20376
  function parseEnroll(value) {
20427
20377
  const raw = String(value).toLowerCase();
20428
20378
  if (raw === "on" || raw === "true" || raw === "opt_in") return "OPT_IN";
@@ -20449,10 +20399,6 @@ Example: baker ads meta creatives create --page 555 --message "Save now" --link
20449
20399
  description: { type: "string", description: "Description" },
20450
20400
  caption: { type: "string", description: "Display URL / caption" },
20451
20401
  cta: { type: "string", description: "Call-to-action type (SHOP_NOW|LEARN_MORE|SIGN_UP|\u2026)" },
20452
- "lead-form": {
20453
- type: "string",
20454
- description: "Instant form id the button opens \u2014 REQUIRED for an ad set that collects leads on the ad. List them with `baker ads meta lead-forms`. With a form, --cta must be APPLY_NOW|DOWNLOAD|GET_QUOTE|LEARN_MORE|SIGN_UP|SUBSCRIBE"
20455
- },
20456
20402
  "image-hash": { type: "string", description: "Meta ad-image hash (already uploaded)" },
20457
20403
  "image-ref": { type: "string", description: "meta_temp_* ref of a staged media upload" },
20458
20404
  "video-id": { type: "string", description: "Meta video id (already uploaded)" },
@@ -20486,11 +20432,6 @@ Amending a staged (meta_temp_*) creative merges fields into the create. Example:
20486
20432
  ...accountArgs2,
20487
20433
  name: { type: "string", description: "New name" },
20488
20434
  status: { type: "string", description: "ACTIVE|PAUSED|ARCHIVED" },
20489
- cta: { type: "string", description: "Call-to-action type (staged creatives only)" },
20490
- "lead-form": {
20491
- type: "string",
20492
- description: "Instant form id the button opens (staged creatives only) \u2014 `baker ads meta lead-forms` lists them"
20493
- },
20494
20435
  file: {
20495
20436
  type: "string",
20496
20437
  description: "JSON file with fields to change (content only merges into a staged creative)"
@@ -20498,15 +20439,7 @@ Amending a staged (meta_temp_*) creative merges fields into the create. Example:
20498
20439
  },
20499
20440
  run: async ({ args }) => {
20500
20441
  const accountId = bareAccountId2(args);
20501
- const file = loadJsonFileArg3(args.file);
20502
- const payload = mergePayload3(file, {
20503
- name: args.name,
20504
- status: upper(args.status),
20505
- // A creative's content is immutable on Meta, so this only ever lands on a
20506
- // creative still staged in this chat — which is exactly the fix path when
20507
- // staging an ad refuses it for naming no instant form.
20508
- content: creativeContentPatch2(file, args)
20509
- });
20442
+ const payload = mergePayload3(loadJsonFileArg3(args.file), { name: args.name, status: upper(args.status) });
20510
20443
  await stageOp2({
20511
20444
  kind: "adCreative.update",
20512
20445
  accountId,
@@ -20677,10 +20610,10 @@ function duplicateCommand2(entity, label) {
20677
20610
  replace: { type: "boolean", description: "Pause the original once the copy publishes" }
20678
20611
  },
20679
20612
  run: async ({ args }) => {
20680
- const { apiPost: apiPost2 } = await import("./client-32DO7WEZ.js");
20681
- const { requireChatId: requireChatId2 } = await import("./env-FWMZXMQK.js");
20682
- const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-WRHUJR74.js");
20683
- const { handleMetaError: handleMetaError2 } = await import("./shared-T66YQ6MB.js");
20613
+ const { apiPost: apiPost2 } = await import("./client-7QIDRQXG.js");
20614
+ const { requireChatId: requireChatId2 } = await import("./env-HZNFGXAR.js");
20615
+ const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-MWMAXN4M.js");
20616
+ const { handleMetaError: handleMetaError2 } = await import("./shared-NFLXWGHK.js");
20684
20617
  try {
20685
20618
  const accountId = bareAccountId2(args);
20686
20619
  const chatId = requireChatId2();
@@ -23696,7 +23629,7 @@ var ANALYTICS_PRESET_INFO = [
23696
23629
  },
23697
23630
  {
23698
23631
  name: "flow",
23699
- description: "One Form in depth: conversions counted at the node that produced them, step-to-step paths for a branching Form, the per-step table, every trigger raised, and each marked conversion by name. Read convertedSessions rather than submits \u2014 a Form whose scheduling widget sits mid-flow never emits a submit and reports zero on every other report. An empty `conversions` array means nobody has marked what this Form is for, so the numbers rest on a built-in guess; mark it in the Forms builder or with `conversions: [{ triggerId }]` on the node. `flowDestinations` is the other half and is evidence rather than a count: which Forms demonstrably handed something over in this window. Read it against the scaffold's `flow-no-destination` warning before acting on one \u2014 the stored picture of a Form is only rewritten when it is published, so a destination added since then shows there and not in the tree.",
23632
+ description: "One Form in depth: conversions counted at the node that produced them, step-to-step paths for a branching Form, the per-step table, every trigger raised, and each marked conversion by name. Read convertedSessions rather than submits \u2014 a Form whose scheduling widget sits mid-flow never emits a submit and reports zero on every other report. An empty `conversions` array means nobody has marked what this Form is for, so the numbers rest on a built-in guess; mark it in the Forms builder or with `conversions: [{ triggerId }]` on the node.",
23700
23633
  playbook: "flow-builder \u2014 fix the branch or the step that loses people"
23701
23634
  },
23702
23635
  {
@@ -23731,7 +23664,7 @@ var ANALYTICS_PRESET_INFO = [
23731
23664
  },
23732
23665
  {
23733
23666
  name: "bots",
23734
- description: "Which machines fetch this site. Every other report here excludes them; this one reads them. Answers whether Google is still crawling a page after it was republished, whether Meta fetched a link preview before spend went behind the URL, and how much of the traffic is now AI answer engines.",
23667
+ description: "Which machines fetch this site and whether they are genuine. Every other report here excludes them; this one reads them. Answers whether Google is still crawling a page after it was republished, whether Meta fetched a link preview before spend went behind the URL, and how much of the traffic is now AI answer engines.",
23735
23668
  playbook: "landing \u2014 a page that stopped being crawled stops ranking, weeks before the traffic shows it"
23736
23669
  },
23737
23670
  {
@@ -24175,7 +24108,7 @@ var funnelCommand = presetCommand({
24175
24108
  var flowCommand = presetCommand({
24176
24109
  name: "flow",
24177
24110
  preset: "flow",
24178
- description: "One Form in depth: how many people it converted, where they went between steps, the per-step table, and every trigger it raised. Every count is DISTINCT VISITS, so quote shares against `funnels[].visits` \u2014 the visits that opened the Form \u2014 and never against `starts`, which is only the visits that touched it. The gap between the two is people who read the Form and left, and on most Forms it is the largest loss there is. Read `flowSummary.convertedSessions`, not `submits` \u2014 a Form that books a call on a scheduling node in the MIDDLE of the flow never emits a submit, so it reports zero submits and every one of its real bookings as conversions. `flowPaths` is the flow as edges between steps, bracketed by __flow_start__, __flow_converted__ and __flow_exit__, which is how a branching Form is read at all: a per-step table cannot say that of the people who left step two, forty went to the booking branch and ninety went nowhere. `flowDestinations` is the other half and is evidence rather than a count: which Forms demonstrably handed something over in this window. Read it against the scaffold's `flow-no-destination` warning before acting on one \u2014 the stored picture of a Form is only rewritten when it is published, so a destination added since then shows there and not in the tree.",
24111
+ description: "One Form in depth: how many people it converted, where they went between steps, the per-step table, and every trigger it raised. Every count is DISTINCT VISITS, so quote shares against `funnels[].visits` \u2014 the visits that opened the Form \u2014 and never against `starts`, which is only the visits that touched it. The gap between the two is people who read the Form and left, and on most Forms it is the largest loss there is. Read `flowSummary.convertedSessions`, not `submits` \u2014 a Form that books a call on a scheduling node in the MIDDLE of the flow never emits a submit, so it reports zero submits and every one of its real bookings as conversions. `flowPaths` is the flow as edges between steps, bracketed by __flow_start__, __flow_converted__ and __flow_exit__, which is how a branching Form is read at all: a per-step table cannot say that of the people who left step two, forty went to the booking branch and ninety went nowhere.",
24179
24112
  extraArgs: { flow: { type: "string", description: "Form slug (default: every Form)", required: false } },
24180
24113
  resolve: (args) => ({ preset: "flow", flowSlug: args.flow ? String(args.flow) : void 0 })
24181
24114
  });
@@ -24517,7 +24450,8 @@ import { defineCommand as defineCommand94 } from "citty";
24517
24450
  import { defineCommand as defineCommand89 } from "citty";
24518
24451
 
24519
24452
  // src/commands/avatars/casting.ts
24520
- var VERBATIM_RULE = "Copy `subjectDescription` into the prompt VERBATIM, word for word. Re-phrasing it per generation is the other reason a face drifts across a set \u2014 treat it as an identifier that happens to read as prose.";
24453
+ var CAST_FLAG_RULE = "Cast with `--avatar <handle>` on `baker studio generate` and `baker studio animate`. Do NOT hand-roll it by pasting `subjectDescription` into the prompt or by passing the sheet through `--reference` \u2014 both render something that merely resembles them. `--avatar` is also the ONLY thing that carries their pinned VOICE and how they speak into a clip; without it the video model invents a different voice, and no later step can put theirs back.";
24454
+ var VERBATIM_RULE = "When you do write the description yourself \u2014 a canvas node, a landing image, anywhere `--avatar` does not exist \u2014 copy `subjectDescription` VERBATIM, word for word. Re-phrasing it per generation is the other reason a face drifts across a set.";
24521
24455
  var VIDEO_ROUTING = "In video, a photoreal presenter renders on `google/veo-3.1` (or `google/veo-3.1-fast`), never `bytedance/seedance-2.0` \u2014 it refuses photoreal human faces, AI-generated ones included. Scaffolding a video creative: `baker canvas scaffold-video \u2026 --real-face`.";
24522
24456
  function castingHints(avatar) {
24523
24457
  if (avatar.status === "generating") {
@@ -24541,7 +24475,8 @@ function castingHints(avatar) {
24541
24475
  ];
24542
24476
  }
24543
24477
  return [
24544
- `Ready to cast. Ground every render on the identity sheet, never on a source photo: \`baker images generate "<your scene>" --reference ${avatar.sheetUrl}\`.`,
24478
+ `Ready to cast: \`baker studio generate "<your scene>" --avatar ${avatar.handle}\` for a still, \`baker studio animate "<the motion, and the line they say>" --avatar ${avatar.handle}\` for a clip.`,
24479
+ CAST_FLAG_RULE,
24545
24480
  VERBATIM_RULE,
24546
24481
  VIDEO_ROUTING
24547
24482
  ];
@@ -24553,7 +24488,6 @@ function rosterRow(avatar) {
24553
24488
  status: avatar.status,
24554
24489
  likeness: avatar.likeness,
24555
24490
  sheetUrl: avatar.sheetUrl ?? null,
24556
- hasVoice: avatar.hasVoice,
24557
24491
  avatarId: avatar.avatarId,
24558
24492
  ...avatar.errorMessage ? { errorMessage: avatar.errorMessage } : {}
24559
24493
  };
@@ -24581,10 +24515,19 @@ function rosterHints(roster, statusFilter) {
24581
24515
  }
24582
24516
  return hints;
24583
24517
  }
24584
- function creationHints(created) {
24518
+ function thinProfileHint(profile, handle) {
24519
+ const missing = ["motion", "persona", "speech", "accent", "wardrobe", "setting"].filter(
24520
+ (field) => !profile?.[field]?.trim()
24521
+ );
24522
+ if (missing.length === 0) return null;
24523
+ return `MISSING ${missing.map((field) => `--${field}`).join(" ")}. A bare subject description holds the face still and nothing else, so clips of @${handle} come out stiff and generic \u2014 measured, filling these in nearly doubled the movement in the same brief. --accent is the one nobody guesses and the only one that fails silently: it decides the accent the avatar SPEAKS with in every clip, so name the region the user is writing to you from ("Spanish from Spain, Madrid accent") \u2014 "Spanish" on its own renders as no accent at all. The user almost certainly told you how this person talks and carries themselves; put it in: \`baker avatars update ${handle} ${missing.map((field) => `--${field} "\u2026"`).join(" ")}\`.`;
24524
+ }
24525
+ function creationHints(created, profile) {
24526
+ const thin = thinProfileHint(profile, created.handle);
24585
24527
  const hints = [
24528
+ ...thin ? [thin] : [],
24586
24529
  ...castingHints({ handle: created.handle, status: created.status }),
24587
- `Once it is ready, every render grounds on its identity sheet (\`--reference <sheetUrl>\`) and reuses the subject description you just wrote, word for word. Those two things are what keep the face identical across a set.`
24530
+ `Once it is ready, cast it with \`--avatar ${created.handle}\` on \`baker studio generate\` / \`baker studio animate\` \u2014 that flag is what grounds the render on its identity sheet and, for a clip, what makes it speak in this avatar's own voice.`
24588
24531
  ];
24589
24532
  if (created.likeness === "licensed") {
24590
24533
  hints.push(
@@ -24670,6 +24613,7 @@ var TEXT_FIELDS = [
24670
24613
  ["subject", "subjectDescription"],
24671
24614
  ["persona", "persona"],
24672
24615
  ["speech", "speech"],
24616
+ ["accent", "accent"],
24673
24617
  ["motion", "motion"],
24674
24618
  ["wardrobe", "wardrobe"],
24675
24619
  ["setting", "setting"]
@@ -24722,11 +24666,11 @@ function unwrap(response) {
24722
24666
  }
24723
24667
  return response.data;
24724
24668
  }
24725
- async function readAvatars(path38, params) {
24726
- return unwrap(await apiGet(path38, params));
24669
+ async function readAvatars(path39, params) {
24670
+ return unwrap(await apiGet(path39, params));
24727
24671
  }
24728
- async function writeAvatars(path38, body) {
24729
- return unwrap(await apiPost(path38, body));
24672
+ async function writeAvatars(path39, body) {
24673
+ return unwrap(await apiPost(path39, body));
24730
24674
  }
24731
24675
 
24732
24676
  // src/commands/avatars/create.ts
@@ -24758,7 +24702,12 @@ registerSchema({
24758
24702
  required: true
24759
24703
  },
24760
24704
  persona: { type: "string", description: "Who they are on camera \u2014 role, attitude, energy", required: false },
24761
- speech: { type: "string", description: "How they speak \u2014 pace, register, accent, verbal habits", required: false },
24705
+ speech: { type: "string", description: "How they speak \u2014 pace, register, verbal habits", required: false },
24706
+ accent: {
24707
+ type: "string",
24708
+ description: "Where their voice is FROM, as a place: 'Spanish from Spain, Madrid accent', 'Mexican Spanish', 'British English, London'. Name the region \u2014 an adjective like 'clear Spanish accent' is read by the video model as NO accent, and the clip comes out sounding like nowhere. Set it whenever the avatar will speak; the user's own language is the default to assume, not English.",
24709
+ required: false
24710
+ },
24762
24711
  motion: { type: "string", description: "Body language and gesture in video", required: false },
24763
24712
  wardrobe: { type: "string", description: "Default outfit when the brief does not name one", required: false },
24764
24713
  setting: { type: "string", description: "Default surroundings when the brief leaves them open", required: false },
@@ -24792,6 +24741,11 @@ var createCommand2 = defineCommand89({
24792
24741
  subject: { type: "string", description: "The person, plainly described \u2014 reused verbatim", required: false },
24793
24742
  persona: { type: "string", description: "Who they are on camera", required: false },
24794
24743
  speech: { type: "string", description: "How they speak", required: false },
24744
+ accent: {
24745
+ type: "string",
24746
+ description: "Where their voice is FROM \u2014 name the region, e.g. 'Spanish from Spain, Madrid accent'",
24747
+ required: false
24748
+ },
24795
24749
  motion: { type: "string", description: "Body language in video", required: false },
24796
24750
  wardrobe: { type: "string", description: "Default outfit", required: false },
24797
24751
  setting: { type: "string", description: "Default surroundings", required: false },
@@ -24854,7 +24808,7 @@ var createCommand2 = defineCommand89({
24854
24808
  ...chatIdFromEnv() ? { chatId: chatIdFromEnv() } : {}
24855
24809
  };
24856
24810
  const data = await writeAvatars("/api/avatars", body);
24857
- writeJson({ ok: true, data, hints: creationHints(data) });
24811
+ writeJson({ ok: true, data, hints: creationHints(data, body.profile) });
24858
24812
  } catch (err) {
24859
24813
  failAvatarApi(err, handle);
24860
24814
  }
@@ -25560,12 +25514,12 @@ function missingFontFiles(urls, available) {
25560
25514
  function planFontAdoption(sources, families) {
25561
25515
  const wanted = new Map(families.map((family) => [normalizeFamily(family), family]));
25562
25516
  const byFamily = /* @__PURE__ */ new Map();
25563
- for (const { path: path38, source } of sources) {
25564
- const dir = posix.dirname(path38);
25517
+ for (const { path: path39, source } of sources) {
25518
+ const dir = posix.dirname(path39);
25565
25519
  for (const face of declaredFontFaces(source)) {
25566
25520
  if (!wanted.has(face.family)) continue;
25567
25521
  const perFile = byFamily.get(face.family) ?? /* @__PURE__ */ new Map();
25568
- perFile.set(path38, [...perFile.get(path38) ?? [], rebaseFontFaceSrc(face.block, dir)]);
25522
+ perFile.set(path39, [...perFile.get(path39) ?? [], rebaseFontFaceSrc(face.block, dir)]);
25569
25523
  byFamily.set(face.family, perFile);
25570
25524
  }
25571
25525
  }
@@ -26287,7 +26241,7 @@ Subcommands:
26287
26241
  });
26288
26242
 
26289
26243
  // src/commands/canvas/index.ts
26290
- import { defineCommand as defineCommand107 } from "citty";
26244
+ import { defineCommand as defineCommand108 } from "citty";
26291
26245
 
26292
26246
  // src/commands/canvas/catalog.ts
26293
26247
  import { defineCommand as defineCommand97 } from "citty";
@@ -29986,7 +29940,7 @@ function buildVideoTodo(report, overlayCount, floatingCount, opts, blueprint) {
29986
29940
  ]
29987
29941
  },
29988
29942
  transitions: "Scene-to-scene cuts the deconstruct flagged as fade/whip/zoom/dissolve/swipe are reproduced as an ffmpeg xfade at the boundary (everything else stays a hard cut). The overlap is consumed from extra generated footage, so the picture stays exactly on the audio timeline. To change a transition, edit the scene's `transition_out.type` in prompt.json and re-scaffold, or hand-edit the `spine` node's ffmpeg args. For a richer HERO cut (whip-pan, glitch, light-leak, gravitational-lens\u2026), the overlay layer can run a Hyperframes shader/CSS transition instead \u2014 see references/hyperframes/blueprints-and-transitions.md (pick 2\u20133 transition types total; the motion IS the handoff).",
29989
- overlay_capabilities: 'The overlay layer (video-overlay-composition/index.html) is a REAL Hyperframes composition, not a plain text layer \u2014 our hyperframe_render node runs the genuine `npx hyperframes` renderer. So it can do far more than fade/slide/pop: (1) PULL a ready-made block \u2014 `npx hyperframes catalog --type block` then `npx hyperframes add <id>` into the composition dir, and nest it with a <div data-composition-src="compositions/<id>.html" data-start data-track-index data-width data-height> clip (~97 blocks: lower-thirds, social-proof cards, stat counters, charts, code, logo stings); (2) animate with the named GSAP motion-rule vocabulary (kinetic-beat-slam, svg-path-draw, counting-dynamic-scale, multi-phase-camera\u2026); (3) frame a talking head as a video-call/PIP/split. The whole engine + how to build new effects is documented in references/hyperframes/ (start at README.md \u2192 catalog.md, motion-rules.md). Reach for this whenever the reference\'s graphics are richer than plain text.',
29943
+ overlay_capabilities: 'The overlay layer (video-overlay-composition/index.html) is a REAL Hyperframes composition, not a plain text layer \u2014 our hyperframe_render node runs the genuine `npx hyperframes` renderer. So it can do far more than fade/slide/pop: (1) PULL a ready-made block \u2014 `npx hyperframes catalog --type block` then `npx hyperframes add <id>` into the composition dir, and nest it with a <div data-composition-src="compositions/<id>.html" data-start data-track-index data-width data-height> clip (~154 blocks: lower-thirds, social-proof cards, stat counters, charts, code, logo stings); (2) animate with the named GSAP motion-rule vocabulary (kinetic-beat-slam, svg-path-draw, counting-dynamic-scale, multi-phase-camera\u2026); (3) frame a talking head as a video-call/PIP/split. The whole engine + how to build new effects is documented in references/hyperframes/ (start at README.md \u2192 catalog.md, motion-rules.md). Reach for this whenever the reference\'s graphics are richer than plain text.',
29990
29944
  caption_system: "Sound-off feed = burned-in captions carry the message. The deconstruct produced a word-level transcript \u2014 drive a real caption track off it (karaoke highlight \u2192 themed \u2192 kinetic), not hand-typed text. Floor: `npx hyperframes add caption-highlight` (TikTok karaoke); escalate by content register (caption-kinetic-slam for hype, caption-editorial-emphasis for premium, caption-neon-glow for AI/crypto). Group on meaning, sync within 80ms. Full ladder + transcript contract: references/hyperframes/captions-and-audio.md.",
29991
29945
  talking_head_overlay: "For a scene where a presenter shows something (screen-share, app demo, product, a 'video call' look), frame the talking-head clip in the overlay composition: presenter full-bleed \u2192 shrinks to a rounded corner PIP pill while a content card takes the stage (or split/stack/overlay-glass-card). The pattern, the #video-wrap\u2192corner-pill tween, the glass call card, and the corner-coord presets are in references/hyperframes/talking-head-and-overlays.md. A ready exemplar ships as the `video-call-composition` move \u2014 copy it next to your canvas, drop brand fonts in the dir, point a `hyperframe_render` at it with the presenter clip as its `background`.",
29992
29946
  text_overlays: {
@@ -32189,11 +32143,241 @@ var scaffoldStaticAdCommand = defineCommand103({
32189
32143
  }
32190
32144
  });
32191
32145
 
32146
+ // src/commands/canvas/scaffold-ad.ts
32147
+ import { cp, mkdir as mkdir7, readFile as readFile14, writeFile as writeFile9 } from "fs/promises";
32148
+ import path22 from "path";
32149
+ import { defineCommand as defineCommand104 } from "citty";
32150
+
32151
+ // src/engine/scaffold/ad-spec.ts
32152
+ import { z as z29 } from "zod";
32153
+ var oneClause = (line) => (line.match(/[.!?](\s|$)/g) ?? []).length <= 1;
32154
+ var Beat = z29.object({
32155
+ /**
32156
+ * The line the voice says in this beat — ONE clause, ending in its own
32157
+ * punctuation. It becomes the caption card verbatim.
32158
+ */
32159
+ say: z29.string().min(1).refine(oneClause, "a beat is one clause: split a second sentence into its own beat"),
32160
+ /** What is on screen while it is said, as a shot brief for the video model. */
32161
+ show: z29.string().min(1),
32162
+ /**
32163
+ * Set only when this beat's subject is talking to camera. Off by default, and
32164
+ * the default is load-bearing: a person visibly speaking under a voice that is
32165
+ * not theirs reads as bad dubbing, which is what sank the first cut of this.
32166
+ */
32167
+ on_camera: z29.boolean().optional()
32168
+ });
32169
+ var AdSpec = z29.object({
32170
+ format: z29.object({
32171
+ aspect_ratio: z29.string().default("9:16"),
32172
+ resolution: z29.string().default("720p")
32173
+ }).default({ aspect_ratio: "9:16", resolution: "720p" }),
32174
+ brand: z29.object({
32175
+ name: z29.string().optional(),
32176
+ /** Figures to lift typographically — the one gold number every ad has. */
32177
+ highlight: z29.array(z29.string()).optional()
32178
+ }).optional(),
32179
+ voice: z29.object({
32180
+ description: z29.string().min(1),
32181
+ language: z29.string().optional()
32182
+ }).optional(),
32183
+ /**
32184
+ * A bed, described in words. Omitted leaves the ad dry — the engine only wires
32185
+ * music when asked, and silence is a legitimate choice.
32186
+ *
32187
+ * The bed never plays over the first beat: the opening line is the
32188
+ * scroll-stopper and has to read clean. That is the engine's rule, not this
32189
+ * one, and it is why the prompt is all this needs to carry.
32190
+ */
32191
+ music: z29.string().min(1).optional(),
32192
+ beats: z29.array(Beat).min(2)
32193
+ });
32194
+ function narrativeRole(index, total) {
32195
+ if (index === 0) return "hook";
32196
+ if (index === total - 1) return "cta";
32197
+ return index < total / 2 ? "problem" : "proof";
32198
+ }
32199
+ function estimateSeconds(line) {
32200
+ const words2 = line.trim().split(/\s+/).length;
32201
+ return Math.max(1.6, Math.round(words2 / 2.6 * 10) / 10);
32202
+ }
32203
+ function adSpecToBlueprint(spec) {
32204
+ const total = spec.beats.length;
32205
+ let clock = 0;
32206
+ const scenes = spec.beats.map((beat, i) => {
32207
+ const duration = estimateSeconds(beat.say);
32208
+ const start = clock;
32209
+ clock += duration;
32210
+ return {
32211
+ start_s: Math.round(start * 100) / 100,
32212
+ end_s: Math.round(clock * 100) / 100,
32213
+ duration_s: duration,
32214
+ summary: beat.say,
32215
+ narrative_role: narrativeRole(i, total),
32216
+ // The read is built from per-scene dialogue, not from the global
32217
+ // transcript: emitting only the transcript scaffolds a SILENT ad. And
32218
+ // `on_camera` is the engine's own switch for "this line is narration" —
32219
+ // absent, it guesses from who is in frame, which is how a still photograph
32220
+ // once got lip-synced.
32221
+ dialogue: [
32222
+ {
32223
+ line: beat.say,
32224
+ start_s: Math.round(start * 100) / 100,
32225
+ end_s: Math.round(clock * 100) / 100,
32226
+ on_camera: beat.on_camera === true,
32227
+ ...spec.voice?.description ? { voice_description: spec.voice.description } : {}
32228
+ }
32229
+ ],
32230
+ motion_prompt: beat.on_camera ? beat.show : (
32231
+ // The subject must not be forming words: the voice belongs to the
32232
+ // narration, and a mouth moving under it reads as dubbing.
32233
+ `${beat.show} Nobody in frame is speaking \u2014 mouths closed, no dialogue.`
32234
+ ),
32235
+ start_frame_prompt: beat.show
32236
+ };
32237
+ });
32238
+ return {
32239
+ global: {
32240
+ transcript: spec.beats.map((b) => b.say).join(" "),
32241
+ voiceover: {
32242
+ // Narration over the picture. Never `on_camera`: that is what tells the
32243
+ // engine a mouth is a lip-sync candidate.
32244
+ mode: "voiceover",
32245
+ ...spec.voice?.description ? { voice_description: spec.voice.description } : {}
32246
+ },
32247
+ ...spec.brand?.name ? { brand: { name: spec.brand.name } } : {},
32248
+ ...spec.music ? { music: { music_prompt: spec.music } } : {}
32249
+ },
32250
+ scenes
32251
+ };
32252
+ }
32253
+
32254
+ // src/commands/canvas/composition-path.ts
32255
+ import { existsSync as existsSync4 } from "fs";
32256
+ import path21 from "path";
32257
+ function resolveShippedCanvasDir(name, startDir, exists = existsSync4, maxDepth = 8) {
32258
+ const rel = path21.join("canvas", name);
32259
+ let dir = startDir;
32260
+ for (let i = 0; i < maxDepth; i++) {
32261
+ const candidate = path21.join(dir, rel);
32262
+ if (exists(path21.join(candidate, "meta.json"))) return candidate;
32263
+ const parent = path21.dirname(dir);
32264
+ if (parent === dir) break;
32265
+ dir = parent;
32266
+ }
32267
+ return path21.resolve(startDir, "../../../", rel);
32268
+ }
32269
+
32270
+ // src/commands/canvas/scaffold-ad.ts
32271
+ var SHIPPED_COMPOSITION_DIR = resolveShippedCanvasDir("video-overlay-composition", import.meta.dirname);
32272
+ var SHIPPED_CAPTIONS_DIR = resolveShippedCanvasDir("tiktok-captions-composition", import.meta.dirname);
32273
+ registerSchema({
32274
+ command: "canvas.scaffold-ad",
32275
+ description: "Build a captioned vertical ad from a WRITTEN SPEC \u2014 no reference video. Start here when someone asks for an ad, a promo or a UGC-style clip and there is nothing to copy: `scaffold-video` reproduces a video you already have, this one writes a new one. You supply a spec of BEATS, and a beat is one line the voice says plus one shot the viewer sees while it is said. Everything else \u2014 the read, the caption cards, the clips, the music, the render \u2014 is derived from that pair, so the ad cannot drift out of sync with itself.",
32276
+ args: {
32277
+ spec: {
32278
+ type: "string",
32279
+ required: true,
32280
+ description: "Path to the ad spec JSON. Shape: { format?, brand?, voice?, beats: [{ say, show, on_camera? }] }. `say` is ONE clause ending in its own punctuation \u2014 it becomes a caption card verbatim, so two sentences in one beat produce a card holding both. `show` is the shot brief for that line. Set `on_camera` ONLY when that beat's subject talks to camera."
32281
+ },
32282
+ slug: {
32283
+ type: "string",
32284
+ required: false,
32285
+ description: "Creative slug \u2014 writes to src/creatives/<slug>/<slug>.canvas.json"
32286
+ },
32287
+ out: {
32288
+ type: "string",
32289
+ required: false,
32290
+ description: "Output canvas path (default <spec-dir>/<name>.canvas.json)"
32291
+ }
32292
+ }
32293
+ });
32294
+ var scaffoldAdCommand = defineCommand104({
32295
+ meta: {
32296
+ name: "scaffold-ad",
32297
+ description: 'Build a captioned vertical ad from a written spec, with no reference video to copy.\n\nA spec is a list of BEATS. One beat = one line the voice says + one shot on screen while it is said. That pairing is what keeps the ad honest: the caption card IS the line, so it can never end mid-sentence, and the picture can never drift from the read.\n\nTwo rules the spec enforces for you, both learned the expensive way:\n \u2022 `say` is ONE clause with its own punctuation. Two sentences in a beat is rejected.\n \u2022 Nobody in shot is speaking unless you set `on_camera` on that beat. A person visibly talking under a voice that is not theirs reads as bad dubbing.\n\nExample spec:\n { "beats": [\n { "say": "Mientras t\xFA sigues pagando la luz a precio de oro,", "show": "Aerial over Spanish rooftops at golden hour." },\n { "say": "Haz clic en el enlace y reserva tu estudio gratuito.", "show": "A couple walking home at sunset." } ] }\n\nThen: baker canvas run <the canvas it writes>'
32298
+ },
32299
+ args: {
32300
+ spec: { type: "string", required: true, description: "Path to the ad spec JSON" },
32301
+ slug: {
32302
+ type: "string",
32303
+ required: false,
32304
+ description: "Creative slug \u2014 writes to src/creatives/<slug>/<slug>.canvas.json"
32305
+ },
32306
+ out: { type: "string", required: false, description: "Output canvas path" }
32307
+ },
32308
+ async run({ args }) {
32309
+ const specPath = args.spec;
32310
+ let parsed;
32311
+ try {
32312
+ parsed = JSON.parse(await readFile14(specPath, "utf-8"));
32313
+ } catch (e) {
32314
+ writeJson({
32315
+ ok: false,
32316
+ error: {
32317
+ code: "VALIDATION_ERROR",
32318
+ message: `Could not read the ad spec at ${specPath}: ${e instanceof Error ? e.message : String(e)}`,
32319
+ fix: "Write the spec first \u2014 a JSON file with a `beats` array, each beat holding `say` (one clause) and `show` (the shot)."
32320
+ }
32321
+ });
32322
+ process.exit(1);
32323
+ return;
32324
+ }
32325
+ const spec = AdSpec.safeParse(parsed);
32326
+ if (!spec.success) {
32327
+ writeJson({
32328
+ ok: false,
32329
+ error: {
32330
+ code: "VALIDATION_ERROR",
32331
+ message: `That ad spec is not usable: ${spec.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")}`,
32332
+ fix: "A beat is ONE line and ONE shot. If a beat holds two sentences, split it into two beats \u2014 the line becomes a caption card verbatim, and a card holding two sentences is the defect this refuses."
32333
+ }
32334
+ });
32335
+ process.exit(1);
32336
+ return;
32337
+ }
32338
+ const blueprint = adSpecToBlueprint(spec.data);
32339
+ const slug = args.slug ?? path22.basename(specPath).replace(/\.[^.]+$/, "");
32340
+ const outPath = args.out ?? (args.slug ? path22.join("src/creatives", slug, `${slug}.canvas.json`) : path22.join(path22.dirname(specPath), `${slug}.canvas.json`));
32341
+ const outDir = path22.dirname(outPath);
32342
+ await mkdir7(outDir, { recursive: true });
32343
+ const compositionDest = path22.join(outDir, "video-overlay-composition");
32344
+ const captionsDest = path22.join(outDir, "tiktok-captions-composition");
32345
+ await cp(SHIPPED_COMPOSITION_DIR, compositionDest, { recursive: true });
32346
+ await cp(SHIPPED_CAPTIONS_DIR, captionsDest, { recursive: true });
32347
+ const blueprintPath = path22.join(outDir, "prompt.json");
32348
+ const blueprintStylePath = path22.join(outDir, "prompt.style.json");
32349
+ await writeSceneFiles(outDir, blueprint);
32350
+ await writeFile9(blueprintStylePath, renderStyleProjectionFromValue(blueprint), "utf8");
32351
+ const opts = {
32352
+ imageModel: DEFAULT_IMAGE_GENERATE_MODEL,
32353
+ videoModel: DEFAULT_VIDEO_GENERATE_MODEL,
32354
+ overlayCompositionPath: path22.relative(outDir, compositionDest),
32355
+ captionsCompositionPath: path22.relative(outDir, captionsDest),
32356
+ blueprintPath: path22.relative(outDir, blueprintPath),
32357
+ blueprintStylePath: path22.relative(outDir, blueprintStylePath),
32358
+ aspect: spec.data.format.aspect_ratio,
32359
+ resolution: spec.data.format.resolution
32360
+ };
32361
+ const canvas = scaffoldVideoCanvas(blueprint, [], opts);
32362
+ await writeFile9(outPath, `${JSON.stringify(canvas, null, 2)}
32363
+ `, "utf-8");
32364
+ writeJson({
32365
+ ok: true,
32366
+ data: { canvas: outPath, beats: spec.data.beats.length, slug },
32367
+ hints: [
32368
+ `Wrote ${spec.data.beats.length} beats to ${outPath}. Run it with \`baker canvas run ${outPath}\`.`,
32369
+ "The caption cards are your `say` lines verbatim \u2014 read them back before running; that is the copy the viewer sees.",
32370
+ "Every clip renders with nobody speaking unless a beat set `on_camera`. If a beat needs a presenter talking, set it there rather than describing it in `show`."
32371
+ ]
32372
+ });
32373
+ }
32374
+ });
32375
+
32192
32376
  // src/commands/canvas/scaffold-video.ts
32193
- import { access as access2, cp, mkdir as mkdir7, readFile as readFile16, rm as rm6, writeFile as writeFile9 } from "fs/promises";
32377
+ import { access as access2, cp as cp2, mkdir as mkdir8, readFile as readFile17, rm as rm6, writeFile as writeFile10 } from "fs/promises";
32194
32378
  import { tmpdir as tmpdir2 } from "os";
32195
- import path23 from "path";
32196
- import { defineCommand as defineCommand104 } from "citty";
32379
+ import path24 from "path";
32380
+ import { defineCommand as defineCommand105 } from "citty";
32197
32381
 
32198
32382
  // src/engine/scaffold/lib/model-router.ts
32199
32383
  var SEEDANCE = "bytedance/seedance-2.0";
@@ -32232,7 +32416,7 @@ function routeVideoModel(input) {
32232
32416
 
32233
32417
  // src/engine/nodes/local/lib/sceneDetect.ts
32234
32418
  import { execFile as execFile2 } from "child_process";
32235
- import { mkdtemp, readdir as readdir7, readFile as readFile14, rm as rm5 } from "fs/promises";
32419
+ import { mkdtemp, readdir as readdir7, readFile as readFile15, rm as rm5 } from "fs/promises";
32236
32420
  import { tmpdir } from "os";
32237
32421
  import { join as join2 } from "path";
32238
32422
  import { promisify as promisify2 } from "util";
@@ -32308,7 +32492,7 @@ async function runSceneDetectOnce(filePath, threshold, minSceneLenS, timeoutMs)
32308
32492
  );
32309
32493
  const csvName = (await readdir7(outDir)).find((f) => f.toLowerCase().endsWith(".csv"));
32310
32494
  if (!csvName) return [];
32311
- return parsePySceneDetectCsvCuts(await readFile14(join2(outDir, csvName), "utf-8"));
32495
+ return parsePySceneDetectCsvCuts(await readFile15(join2(outDir, csvName), "utf-8"));
32312
32496
  } finally {
32313
32497
  await rm5(outDir, { recursive: true, force: true });
32314
32498
  }
@@ -32331,25 +32515,9 @@ async function detectSceneCutsPySceneDetect(filePath, opts = {}) {
32331
32515
  return cuts;
32332
32516
  }
32333
32517
 
32334
- // src/commands/canvas/composition-path.ts
32335
- import { existsSync as existsSync4 } from "fs";
32336
- import path21 from "path";
32337
- function resolveShippedCanvasDir(name, startDir, exists = existsSync4, maxDepth = 8) {
32338
- const rel = path21.join("canvas", name);
32339
- let dir = startDir;
32340
- for (let i = 0; i < maxDepth; i++) {
32341
- const candidate = path21.join(dir, rel);
32342
- if (exists(path21.join(candidate, "meta.json"))) return candidate;
32343
- const parent = path21.dirname(dir);
32344
- if (parent === dir) break;
32345
- dir = parent;
32346
- }
32347
- return path21.resolve(startDir, "../../../", rel);
32348
- }
32349
-
32350
32518
  // src/commands/canvas/gitignore.ts
32351
- import { appendFile, readFile as readFile15 } from "fs/promises";
32352
- import path22 from "path";
32519
+ import { appendFile, readFile as readFile16 } from "fs/promises";
32520
+ import path23 from "path";
32353
32521
  function missingGitignoreEntries(existing, entries) {
32354
32522
  const present2 = new Set(
32355
32523
  existing.split("\n").map((l) => l.trim().replace(/\/+$/, "")).filter((l) => l.length > 0 && !l.startsWith("#"))
@@ -32357,10 +32525,10 @@ function missingGitignoreEntries(existing, entries) {
32357
32525
  return entries.filter((e) => !present2.has(e.trim().replace(/\/+$/, "")));
32358
32526
  }
32359
32527
  async function ensureGitignore(dir, entries) {
32360
- const file = path22.join(dir, ".gitignore");
32528
+ const file = path23.join(dir, ".gitignore");
32361
32529
  let existing;
32362
32530
  try {
32363
- existing = await readFile15(file, "utf8");
32531
+ existing = await readFile16(file, "utf8");
32364
32532
  } catch {
32365
32533
  return;
32366
32534
  }
@@ -32374,8 +32542,8 @@ ${missing.join("\n")}
32374
32542
  }
32375
32543
 
32376
32544
  // src/commands/canvas/scaffold-video.ts
32377
- var SHIPPED_COMPOSITION_DIR = resolveShippedCanvasDir("video-overlay-composition", import.meta.dirname);
32378
- var SHIPPED_CAPTIONS_DIR = resolveShippedCanvasDir("tiktok-captions-composition", import.meta.dirname);
32545
+ var SHIPPED_COMPOSITION_DIR2 = resolveShippedCanvasDir("video-overlay-composition", import.meta.dirname);
32546
+ var SHIPPED_CAPTIONS_DIR2 = resolveShippedCanvasDir("tiktok-captions-composition", import.meta.dirname);
32379
32547
  function resolveModel2(kind, preferred) {
32380
32548
  const ids = Object.keys(MODEL_REGISTRY[kind]);
32381
32549
  return ids.includes(preferred) ? preferred : ids[0] ?? preferred;
@@ -32399,7 +32567,7 @@ ONE PERSON, MULTIPLE LOOKS: if a single individual plays MULTIPLE personas or wa
32399
32567
  For each kept element return: { "type": one of person|animal|product|logo|badge|location, "label": a short UPPER_SNAKE_CASE name (e.g. HERO, CREATOR_SKEPTIC, INSURANCE_CARD, LOGO), "description": a concrete reusable description to source/shoot the real asset \u2014 for a person/animal give a NEUTRAL castable role (e.g. "hero pet-owner, woman in her 30s" or "a small beagle"), NOT the original individual's literal face/identity: we RECAST with a FRESH person/animal, so never tell the agent to reuse the original. "expression": a living subject's typical expression or null, "cast_id": the global.cast id if it maps to one else null, "same_as": the label of another element this is the SAME individual as (different wardrobe/persona) else null, "scenes": the 0-based indices of ONLY the scenes where the element is ACTUALLY VISIBLE ON SCREEN \u2014 judged from that scene's start_frame_prompt / end_frame_prompt subjects and its action_detail, NOT from who is merely speaking. A narrator heard over b-roll is NOT present in that b-roll scene; a dog-running cutaway does NOT contain the couch creator just because she talks across it. Do NOT pad the list \u2014 an element wrongly listed in a scene makes the reproduction render the wrong subject there (e.g. the creator appearing in a pure-dog b-roll). When in doubt, leave a scene OUT. Output ONLY the JSON object.`;
32400
32568
  async function loadAssetText2(ref, label) {
32401
32569
  const r = ref;
32402
- if (typeof r?.path === "string") return readFile16(r.path, "utf8");
32570
+ if (typeof r?.path === "string") return readFile17(r.path, "utf8");
32403
32571
  if (typeof r?.url === "string") {
32404
32572
  const res = await fetch(r.url);
32405
32573
  if (!res.ok) throw new Error(`failed to fetch ${label} (${res.status})`);
@@ -32418,8 +32586,8 @@ async function loadTranscriptBestEffort(ref) {
32418
32586
  async function stageCaptions(outDir, transcript) {
32419
32587
  const text2 = transcript?.trim();
32420
32588
  if (!text2 || text2 === "[]") return {};
32421
- const compositionPath = path23.join(outDir, "tiktok-captions-composition");
32422
- await cp(SHIPPED_CAPTIONS_DIR, compositionPath, { recursive: true });
32589
+ const compositionPath = path24.join(outDir, "tiktok-captions-composition");
32590
+ await cp2(SHIPPED_CAPTIONS_DIR2, compositionPath, { recursive: true });
32423
32591
  return { compositionPath };
32424
32592
  }
32425
32593
  function patchCompositionMeta(metaJson, dims) {
@@ -32436,12 +32604,12 @@ function patchCompositionHtml(html, dims) {
32436
32604
  return html.replace(/(<meta\s+name="viewport"\s+content="width=)\d+(,\s*height=)\d+(")/i, `$1${dims.w}$2${dims.h}$3`).replace(/(width:\s*)\d+(px;\s*height:\s*)\d+(px;)/i, `$1${dims.w}$2${dims.h}$3`).replace(/(data-width=")\d+(")/i, `$1${dims.w}$2`).replace(/(data-height=")\d+(")/i, `$1${dims.h}$2`);
32437
32605
  }
32438
32606
  async function stampCompositionDims(compositionDir, dims) {
32439
- const metaPath = path23.join(compositionDir, "meta.json");
32440
- const rawMeta = await readFile16(metaPath, "utf8");
32441
- await writeFile9(metaPath, patchCompositionMeta(rawMeta, dims), "utf8");
32442
- const htmlPath = path23.join(compositionDir, "index.html");
32443
- const rawHtml = await readFile16(htmlPath, "utf8");
32444
- await writeFile9(htmlPath, patchCompositionHtml(rawHtml, dims), "utf8");
32607
+ const metaPath = path24.join(compositionDir, "meta.json");
32608
+ const rawMeta = await readFile17(metaPath, "utf8");
32609
+ await writeFile10(metaPath, patchCompositionMeta(rawMeta, dims), "utf8");
32610
+ const htmlPath = path24.join(compositionDir, "index.html");
32611
+ const rawHtml = await readFile17(htmlPath, "utf8");
32612
+ await writeFile10(htmlPath, patchCompositionHtml(rawHtml, dims), "utf8");
32445
32613
  }
32446
32614
  function parseElements2(raw) {
32447
32615
  const parsed = JSON.parse(raw);
@@ -32488,7 +32656,7 @@ var VIDEO_EXT_BY_MIME = {
32488
32656
  "video/x-matroska": ".mkv"
32489
32657
  };
32490
32658
  function referenceVideoExt(url, contentType) {
32491
- const fromPath = path23.extname(new URL(url).pathname).toLowerCase();
32659
+ const fromPath = path24.extname(new URL(url).pathname).toLowerCase();
32492
32660
  if (fromPath && fromPath.length <= 5) return fromPath;
32493
32661
  const mime = (contentType ?? "").split(";")[0]?.trim().toLowerCase();
32494
32662
  return mime && VIDEO_EXT_BY_MIME[mime] || ".mp4";
@@ -32514,7 +32682,7 @@ function videoDefinitionDescription(blueprint) {
32514
32682
  return typeof product === "string" && product.trim() ? product.trim() : void 0;
32515
32683
  }
32516
32684
  async function materializeReferenceVideo(fileArg2) {
32517
- if (!/^https?:\/\//i.test(fileArg2)) return path23.resolve(fileArg2);
32685
+ if (!/^https?:\/\//i.test(fileArg2)) return path24.resolve(fileArg2);
32518
32686
  let bytes;
32519
32687
  let contentType;
32520
32688
  try {
@@ -32526,11 +32694,11 @@ async function materializeReferenceVideo(fileArg2) {
32526
32694
  throw new Error(`failed to download reference video: ${e instanceof Error ? e.message : String(e)}`);
32527
32695
  }
32528
32696
  if (bytes.length === 0) throw new Error("reference video download was empty");
32529
- const dest = path23.join(
32697
+ const dest = path24.join(
32530
32698
  tmpdir2(),
32531
32699
  `baker-ref-${sha256Hex(bytes).slice(0, 16)}${referenceVideoExt(fileArg2, contentType)}`
32532
32700
  );
32533
- await writeFile9(dest, bytes);
32701
+ await writeFile10(dest, bytes);
32534
32702
  return dest;
32535
32703
  }
32536
32704
  function resolveSeamDedup(raw) {
@@ -32654,7 +32822,7 @@ async function runAnalysisPasses(deconstructCanvas, selectModel) {
32654
32822
  return fail4("deconstruct", e instanceof Error ? e.message : String(e));
32655
32823
  }
32656
32824
  }
32657
- var scaffoldVideoCommand = defineCommand104({
32825
+ var scaffoldVideoCommand = defineCommand105({
32658
32826
  meta: {
32659
32827
  name: "scaffold-video",
32660
32828
  description: "Turn a reference video into a runnable reproduction canvas in one command. Runs billed passes \u2014 video_deconstruct (the full scene-by-scene blueprint + transcript, baked to a split, editable blueprint: a global prompt.json plus one small scenes/sNN.json per scene) and an AI selection of the video's RECURRING identity elements (person/animal/product/logo) \u2014 then scaffolds a pipeline where every scene boundary is a static-ad-grade frame (the blueprint as target_blueprint, a reference legend, the real frame as anchor) and each recurring element gets ONE shared [TODO] ingest slot wired into every frame it appears in. The clips feed Seedance an ultra-detailed motion brief (action, camera, dialogue, transcript). Edit a scene's scenes/sNN.json (or prompt.json for global cast/palette/brand), drop the real source images, then `baker canvas run`."
@@ -32746,11 +32914,11 @@ var scaffoldVideoCommand = defineCommand104({
32746
32914
  } catch (e) {
32747
32915
  return fail4("download", e instanceof Error ? e.message : String(e));
32748
32916
  }
32749
- const base = path23.basename(videoPath, path23.extname(videoPath));
32750
- const outPath = args.out ? path23.resolve(String(args.out)) : slug ? path23.join(process.cwd(), "src", "creatives", slug, `${slug}.canvas.json`) : path23.join(path23.dirname(videoPath), `${base}.video.canvas.json`);
32751
- const outDir = path23.dirname(outPath);
32752
- const blueprintPath = path23.join(outDir, "prompt.json");
32753
- const blueprintStylePath = path23.join(outDir, "prompt.style.json");
32917
+ const base = path24.basename(videoPath, path24.extname(videoPath));
32918
+ const outPath = args.out ? path24.resolve(String(args.out)) : slug ? path24.join(process.cwd(), "src", "creatives", slug, `${slug}.canvas.json`) : path24.join(path24.dirname(videoPath), `${base}.video.canvas.json`);
32919
+ const outDir = path24.dirname(outPath);
32920
+ const blueprintPath = path24.join(outDir, "prompt.json");
32921
+ const blueprintStylePath = path24.join(outDir, "prompt.style.json");
32754
32922
  const frames = args.frames === "reuse" ? "reuse" : "generate";
32755
32923
  const maxScenes = args["max-scenes"] ? Number(args["max-scenes"]) : void 0;
32756
32924
  if (Number.isFinite(maxScenes)) {
@@ -32774,10 +32942,10 @@ var scaffoldVideoCommand = defineCommand104({
32774
32942
  process.stderr.write(`\u{1F3AC} Photoreal on-camera cast detected \u2192 generating clips on ${videoModel} (dodges Seedance's real-person filter).
32775
32943
  `);
32776
32944
  }
32777
- await mkdir7(outDir, { recursive: true });
32945
+ await mkdir8(outDir, { recursive: true });
32778
32946
  const annotated = annotateBlueprintWithElements(blueprint, elements);
32779
32947
  await writeSceneFiles(outDir, annotated);
32780
- await writeFile9(blueprintStylePath, renderStyleProjectionFromValue(annotated), "utf8");
32948
+ await writeFile10(blueprintStylePath, renderStyleProjectionFromValue(annotated), "utf8");
32781
32949
  let aspect;
32782
32950
  try {
32783
32951
  aspect = resolveAspect(
@@ -32795,12 +32963,12 @@ var scaffoldVideoCommand = defineCommand104({
32795
32963
  `
32796
32964
  );
32797
32965
  }
32798
- const compositionDest = path23.join(outDir, "video-overlay-composition");
32799
- await cp(SHIPPED_COMPOSITION_DIR, compositionDest, { recursive: true });
32966
+ const compositionDest = path24.join(outDir, "video-overlay-composition");
32967
+ await cp2(SHIPPED_COMPOSITION_DIR2, compositionDest, { recursive: true });
32800
32968
  await stampCompositionDims(compositionDest, outDims);
32801
- const indexPath = path23.join(compositionDest, "index.html");
32969
+ const indexPath = path24.join(compositionDest, "index.html");
32802
32970
  const overlayHtml = buildOverlayHtml(blueprint, { captionsActive: Boolean(transcript) });
32803
- const indexHtml = await readFile16(indexPath, "utf8");
32971
+ const indexHtml = await readFile17(indexPath, "utf8");
32804
32972
  const injected = indexHtml.replace("<!--OVERLAYS-->", () => overlayHtml);
32805
32973
  if (injected === indexHtml && overlayHtml.trim()) {
32806
32974
  fail4(
@@ -32808,16 +32976,16 @@ var scaffoldVideoCommand = defineCommand104({
32808
32976
  `video-overlay-composition/index.html is missing the <!--OVERLAYS--> marker \u2014 cannot inject the overlay layer`
32809
32977
  );
32810
32978
  }
32811
- await writeFile9(indexPath, injected, "utf8");
32979
+ await writeFile10(indexPath, injected, "utf8");
32812
32980
  const captions = await stageCaptions(outDir, transcript);
32813
32981
  if (captions.compositionPath) await stampCompositionDims(captions.compositionPath, outDims);
32814
32982
  const opts = {
32815
32983
  imageModel,
32816
32984
  videoModel,
32817
- overlayCompositionPath: path23.relative(outDir, compositionDest),
32818
- captionsCompositionPath: captions.compositionPath ? path23.relative(outDir, captions.compositionPath) : void 0,
32819
- blueprintPath: path23.relative(outDir, blueprintPath),
32820
- blueprintStylePath: path23.relative(outDir, blueprintStylePath),
32985
+ overlayCompositionPath: path24.relative(outDir, compositionDest),
32986
+ captionsCompositionPath: captions.compositionPath ? path24.relative(outDir, captions.compositionPath) : void 0,
32987
+ blueprintPath: path24.relative(outDir, blueprintPath),
32988
+ blueprintStylePath: path24.relative(outDir, blueprintStylePath),
32821
32989
  frames,
32822
32990
  ambient: Boolean(args.ambient),
32823
32991
  seamDedup: resolveSeamDedup(args["seam-dedup"]),
@@ -32842,10 +33010,10 @@ var scaffoldVideoCommand = defineCommand104({
32842
33010
  todo.blocking_validation_issues = validation.issues;
32843
33011
  meta.todo = todo;
32844
33012
  }
32845
- await writeFile9(outPath, `${JSON.stringify(canvas, null, 2)}
33013
+ await writeFile10(outPath, `${JSON.stringify(canvas, null, 2)}
32846
33014
  `, "utf8");
32847
- await writeFile9(
32848
- path23.join(outDir, REBUILD_FILE),
33015
+ await writeFile10(
33016
+ path24.join(outDir, REBUILD_FILE),
32849
33017
  `${JSON.stringify({ elements, opts }, null, 2)}
32850
33018
  `,
32851
33019
  "utf8"
@@ -32870,9 +33038,9 @@ var scaffoldVideoCommand = defineCommand104({
32870
33038
  await ensureGitignore(process.cwd(), ["canvas/", ".context/"]);
32871
33039
  const sourceRef = videoSourceReference(blueprint, fileArg2);
32872
33040
  if (slug) {
32873
- const definitionPath = path23.join(outDir, "_definition.md");
33041
+ const definitionPath = path24.join(outDir, "_definition.md");
32874
33042
  if (!await fileExists2(definitionPath)) {
32875
- await writeFile9(
33043
+ await writeFile10(
32876
33044
  definitionPath,
32877
33045
  buildCreativeDefinition({
32878
33046
  title: titleFromSlug(slug),
@@ -32925,7 +33093,7 @@ var scaffoldVideoCommand = defineCommand104({
32925
33093
  graph: canvas.metadata?.video?.graph_stats
32926
33094
  },
32927
33095
  checklist: {
32928
- edit_prompt: `The blueprint is split so you edit ONE small file at a time \u2014 never a giant one. Per-scene content (a scene's dialogue, action, frame prompts, overlays) lives in \`scenes/sNN.json\` \u2014 edit the single scene you want to change. Global cast/palette/brand/copy lives in \`${path23.basename(blueprintPath)}\`. \`baker canvas validate\`/\`run\` re-assemble the blueprint and re-flow every edited scene back into the render (and regenerate ${path23.basename(blueprintStylePath)}, the projection each frame's target_blueprint reads) \u2014 so your scene edits reach the render automatically. Never hand-edit the inlined node prompts in the canvas or the derived ${path23.basename(blueprintStylePath)}; both are regenerated.`,
33096
+ edit_prompt: `The blueprint is split so you edit ONE small file at a time \u2014 never a giant one. Per-scene content (a scene's dialogue, action, frame prompts, overlays) lives in \`scenes/sNN.json\` \u2014 edit the single scene you want to change. Global cast/palette/brand/copy lives in \`${path24.basename(blueprintPath)}\`. \`baker canvas validate\`/\`run\` re-assemble the blueprint and re-flow every edited scene back into the render (and regenerate ${path24.basename(blueprintStylePath)}, the projection each frame's target_blueprint reads) \u2014 so your scene edits reach the render automatically. Never hand-edit the inlined node prompts in the canvas or the derived ${path24.basename(blueprintStylePath)}; both are regenerated.`,
32929
33097
  recurring_elements_to_supply: report.elements,
32930
33098
  voices_to_confirm: report.dialogue.map((d) => ({
32931
33099
  scene: d.scene,
@@ -32962,9 +33130,9 @@ var scaffoldVideoCommand = defineCommand104({
32962
33130
  });
32963
33131
 
32964
33132
  // src/commands/canvas/set-prompt.ts
32965
- import { readFile as readFile17, writeFile as writeFile10 } from "fs/promises";
32966
- import path24 from "path";
32967
- import { defineCommand as defineCommand105 } from "citty";
33133
+ import { readFile as readFile18, writeFile as writeFile11 } from "fs/promises";
33134
+ import path25 from "path";
33135
+ import { defineCommand as defineCommand106 } from "citty";
32968
33136
  function setNodePrompt(canvas, nodeId, text2) {
32969
33137
  const nodes = canvas?.nodes;
32970
33138
  if (!Array.isArray(nodes)) throw new Error("canvas has no nodes array");
@@ -32979,7 +33147,7 @@ function setNodePrompt(canvas, nodeId, text2) {
32979
33147
  newNodes[idx] = newNode;
32980
33148
  return { ...canvas, nodes: newNodes };
32981
33149
  }
32982
- var setPromptCommand = defineCommand105({
33150
+ var setPromptCommand = defineCommand106({
32983
33151
  meta: {
32984
33152
  name: "set-prompt",
32985
33153
  description: "Safely set a node's params.prompt (a frame description, motion prompt, etc.) without hand-editing the JSON. Prefer --text-file for multi-line/accented copy \u2014 it preserves UTF-8 exactly, unlike shell-quoted jq."
@@ -32991,17 +33159,17 @@ var setPromptCommand = defineCommand105({
32991
33159
  "text-file": { type: "string", description: "Read the new prompt from a UTF-8 file (preserves accents/newlines)" }
32992
33160
  },
32993
33161
  async run({ args }) {
32994
- const filePath = path24.resolve(String(args.file));
33162
+ const filePath = path25.resolve(String(args.file));
32995
33163
  let canvas;
32996
33164
  try {
32997
- canvas = JSON.parse(await readFile17(filePath, "utf8"));
33165
+ canvas = JSON.parse(await readFile18(filePath, "utf8"));
32998
33166
  } catch (e) {
32999
33167
  process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "parse", message: String(e) } }, null, 2)}
33000
33168
  `);
33001
33169
  process.exit(2);
33002
33170
  }
33003
33171
  let text2;
33004
- if (args["text-file"]) text2 = await readFile17(path24.resolve(String(args["text-file"])), "utf8");
33172
+ if (args["text-file"]) text2 = await readFile18(path25.resolve(String(args["text-file"])), "utf8");
33005
33173
  else if (args.text !== void 0) text2 = String(args.text);
33006
33174
  else {
33007
33175
  process.stderr.write(
@@ -33022,14 +33190,14 @@ var setPromptCommand = defineCommand105({
33022
33190
  process.exit(2);
33023
33191
  return;
33024
33192
  }
33025
- const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated, path24.dirname(filePath)), defaultRegistry());
33193
+ const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated, path25.dirname(filePath)), defaultRegistry());
33026
33194
  if (!validation.ok) {
33027
33195
  process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "validation", issues: validation.issues } }, null, 2)}
33028
33196
  `);
33029
33197
  process.exit(2);
33030
33198
  return;
33031
33199
  }
33032
- await writeFile10(filePath, `${JSON.stringify(updated, null, 2)}
33200
+ await writeFile11(filePath, `${JSON.stringify(updated, null, 2)}
33033
33201
  `, "utf8");
33034
33202
  process.stdout.write(`${JSON.stringify({ ok: true, node: String(args.node), bytes: text2.length }, null, 2)}
33035
33203
  `);
@@ -33037,18 +33205,18 @@ var setPromptCommand = defineCommand105({
33037
33205
  });
33038
33206
 
33039
33207
  // src/commands/canvas/validate.ts
33040
- import { readFile as readFile18 } from "fs/promises";
33041
- import path25 from "path";
33042
- import { defineCommand as defineCommand106 } from "citty";
33043
- var validateCommand = defineCommand106({
33208
+ import { readFile as readFile19 } from "fs/promises";
33209
+ import path26 from "path";
33210
+ import { defineCommand as defineCommand107 } from "citty";
33211
+ var validateCommand = defineCommand107({
33044
33212
  meta: {
33045
33213
  name: "validate",
33046
33214
  description: "Validate a canvas JSON file (no execution). Includes a per-node cost preview and runs each node's deep validators (composition meta checks for hyperframe_render/_snapshot)."
33047
33215
  },
33048
33216
  args: { file: { type: "positional", required: true, description: "Path to canvas JSON" } },
33049
33217
  async run({ args }) {
33050
- const filePath = path25.resolve(String(args.file));
33051
- const raw = await readFile18(filePath, "utf8");
33218
+ const filePath = path26.resolve(String(args.file));
33219
+ const raw = await readFile19(filePath, "utf8");
33052
33220
  let parsed;
33053
33221
  try {
33054
33222
  parsed = JSON.parse(raw);
@@ -33060,7 +33228,7 @@ var validateCommand = defineCommand106({
33060
33228
  }
33061
33229
  const healed = await healAbsoluteCanvasPaths(filePath, parsed);
33062
33230
  parsed = healed.canvas;
33063
- parsed = resolveRelativeCanvasPaths(parsed, path25.dirname(filePath));
33231
+ parsed = resolveRelativeCanvasPaths(parsed, path26.dirname(filePath));
33064
33232
  let styleProjection = "not_applicable";
33065
33233
  try {
33066
33234
  styleProjection = await syncStyleProjection(parsed, (line) => process.stderr.write(`${line}
@@ -33113,7 +33281,7 @@ var validateCommand = defineCommand106({
33113
33281
  });
33114
33282
 
33115
33283
  // src/commands/canvas/index.ts
33116
- var canvasCommand = defineCommand107({
33284
+ var canvasCommand = defineCommand108({
33117
33285
  meta: {
33118
33286
  name: "canvas",
33119
33287
  description: `Run Baker creative canvas JSON files locally. Local nodes execute in-process; remote nodes POST to the Convex backend gateway.
@@ -33128,7 +33296,8 @@ Subcommands:
33128
33296
  baker canvas sync-definition <path> \u2014 push a creative's current node-graph + input thumbnails to the dashboard without rendering (runs automatically on canvas/prompt edits)
33129
33297
  baker canvas catalog \u2014 print the agent-facing node + composition catalog (JSON Schema)
33130
33298
  baker canvas inspect <run_id> \u2014 one-page summary of a completed run
33131
- baker canvas scaffold-video <video> \u2014 turn a reference video into a runnable reproduction canvas (deconstruct + recurring-element detection)
33299
+ baker canvas scaffold-ad --spec <spec.json> \u2014 DEFAULT for a new ad: write beats (one spoken line + one shot each), get the whole runnable canvas
33300
+ baker canvas scaffold-video <video> \u2014 reproduce a video the USER gave you (an ad you found is inspiration, not a copy target)
33132
33301
  baker canvas scaffold-static-ad <image> \u2014 turn a source image into a runnable static-ad canvas (describe + element detection)
33133
33302
  Full guide: __tooling__/docs/tools/baker/canvas.md`
33134
33303
  },
@@ -33139,6 +33308,7 @@ Full guide: __tooling__/docs/tools/baker/canvas.md`
33139
33308
  critique: critiqueCommand,
33140
33309
  catalog: catalogCommand,
33141
33310
  inspect: inspectCommand,
33311
+ "scaffold-ad": scaffoldAdCommand,
33142
33312
  "scaffold-video": scaffoldVideoCommand,
33143
33313
  "scaffold-static-ad": scaffoldStaticAdCommand,
33144
33314
  "set-prompt": setPromptCommand,
@@ -33147,7 +33317,7 @@ Full guide: __tooling__/docs/tools/baker/canvas.md`
33147
33317
  });
33148
33318
 
33149
33319
  // src/commands/capabilities/index.ts
33150
- import { defineCommand as defineCommand108 } from "citty";
33320
+ import { defineCommand as defineCommand109 } from "citty";
33151
33321
  registerSchema({
33152
33322
  command: "capabilities",
33153
33323
  description: "Start here, before promising the user anything on an ad platform, in Analytics, or in Tag Manager. Answers three questions in one call: what is connected and with what access, what a write on each surface actually does when it lands, and what CANNOT be done here and why. Run it with no arguments for every surface; name one surface to also get the exact fields every change on it accepts.",
@@ -33190,7 +33360,7 @@ function capabilityHints(surfaces) {
33190
33360
  }
33191
33361
  return hints;
33192
33362
  }
33193
- var runCapabilities = defineCommand108({
33363
+ var runCapabilities = defineCommand109({
33194
33364
  meta: {
33195
33365
  name: "capabilities",
33196
33366
  description: `What Baker can and cannot do for this company, before any work starts.
@@ -33246,7 +33416,7 @@ Full guide: __tooling__/docs/tools/baker/capabilities.md`
33246
33416
  var capabilitiesCommand = runCapabilities;
33247
33417
 
33248
33418
  // src/commands/chats/index.ts
33249
- import { defineCommand as defineCommand109 } from "citty";
33419
+ import { defineCommand as defineCommand110 } from "citty";
33250
33420
  var STATUS_GROUPS = ["active", "archived", "all"];
33251
33421
  var REPO_SURFACES = ["knowledge", "company", "brand", "landings", "flows", "creatives"];
33252
33422
  function parseBoundedInt(raw, name, min, max) {
@@ -33268,7 +33438,7 @@ registerSchema({
33268
33438
  full: { type: "boolean", description: "Include each Session's full change list", required: false, default: false }
33269
33439
  }
33270
33440
  });
33271
- var listCommand11 = defineCommand109({
33441
+ var listCommand11 = defineCommand110({
33272
33442
  meta: { name: "list", description: "List other Sessions on this account, newest first." },
33273
33443
  args: {
33274
33444
  status: { type: "string", description: "Filter: active|archived|all (default: all)", required: false },
@@ -33323,7 +33493,7 @@ registerSchema({
33323
33493
  }
33324
33494
  }
33325
33495
  });
33326
- var viewCommand = defineCommand109({
33496
+ var viewCommand = defineCommand110({
33327
33497
  meta: {
33328
33498
  name: "view",
33329
33499
  description: "Inspect one Session's full effects \u2014 git content + actions/tags/ads, kickoff, commits."
@@ -33367,7 +33537,7 @@ registerSchema({
33367
33537
  }
33368
33538
  }
33369
33539
  });
33370
- var transcriptCommand = defineCommand109({
33540
+ var transcriptCommand = defineCommand110({
33371
33541
  meta: { name: "transcript", description: "Read another Session's conversation." },
33372
33542
  args: {
33373
33543
  id: { type: "positional", description: "The Session id", required: true },
@@ -33419,7 +33589,7 @@ registerSchema({
33419
33589
  }
33420
33590
  }
33421
33591
  });
33422
- var diffCommand = defineCommand109({
33592
+ var diffCommand = defineCommand110({
33423
33593
  meta: { name: "diff", description: "See a published Session's real file-level changes." },
33424
33594
  args: {
33425
33595
  id: { type: "positional", description: "The Session id", required: true },
@@ -33463,7 +33633,7 @@ var diffCommand = defineCommand109({
33463
33633
  }
33464
33634
  }
33465
33635
  });
33466
- var chatsCommand = defineCommand109({
33636
+ var chatsCommand = defineCommand110({
33467
33637
  meta: {
33468
33638
  name: "chats",
33469
33639
  description: `Inspect other Sessions on this account (read-only): list them, view what they produced, read their conversation, and see their real file changes \u2014 so you can reuse past work for a new goal.
@@ -33478,13 +33648,13 @@ Full guide: __tooling__/docs/tools/baker/chats.md`
33478
33648
  });
33479
33649
 
33480
33650
  // src/commands/creatives/index.ts
33481
- import { defineCommand as defineCommand111 } from "citty";
33651
+ import { defineCommand as defineCommand112 } from "citty";
33482
33652
 
33483
33653
  // src/commands/creatives/publish.ts
33484
- import { defineCommand as defineCommand110 } from "citty";
33654
+ import { defineCommand as defineCommand111 } from "citty";
33485
33655
 
33486
33656
  // src/commands/images/api.ts
33487
- import { readFile as readFile19 } from "fs/promises";
33657
+ import { readFile as readFile20 } from "fs/promises";
33488
33658
  import { basename, extname } from "path";
33489
33659
  var imageProcessingTimeoutMs = 18e4;
33490
33660
  var imageReadyPollIntervalMs = 2e3;
@@ -33498,7 +33668,7 @@ var mimeMap = {
33498
33668
  ".avif": "image/avif"
33499
33669
  };
33500
33670
  var defaultImageApiDeps = {
33501
- readFile: readFile19,
33671
+ readFile: readFile20,
33502
33672
  post: apiPost,
33503
33673
  get: apiGet,
33504
33674
  sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms))
@@ -33628,7 +33798,7 @@ async function publishCreative(args, deps = defaultImageApiDeps) {
33628
33798
  chatId: chatIdFromEnv2()
33629
33799
  });
33630
33800
  }
33631
- var publishCommand = defineCommand110({
33801
+ var publishCommand = defineCommand111({
33632
33802
  meta: {
33633
33803
  name: "publish",
33634
33804
  description: "Publish a final static creative image to Baker Creatives and print the creative reference JSON."
@@ -33686,7 +33856,7 @@ var publishCommand = defineCommand110({
33686
33856
  });
33687
33857
 
33688
33858
  // src/commands/creatives/index.ts
33689
- var creativesCommand3 = defineCommand111({
33859
+ var creativesCommand3 = defineCommand112({
33690
33860
  meta: {
33691
33861
  name: "creatives",
33692
33862
  description: `Publish static ad creatives as first-class Baker outputs.
@@ -33703,11 +33873,11 @@ Full guide: __tooling__/docs/tools/baker/creatives.md`
33703
33873
  });
33704
33874
 
33705
33875
  // src/commands/flows/index.ts
33706
- import { defineCommand as defineCommand116 } from "citty";
33876
+ import { defineCommand as defineCommand117 } from "citty";
33707
33877
 
33708
33878
  // src/commands/flows/add.ts
33709
33879
  import { randomUUID } from "crypto";
33710
- import { defineCommand as defineCommand112 } from "citty";
33880
+ import { defineCommand as defineCommand113 } from "citty";
33711
33881
 
33712
33882
  // src/commands/flows/shared.ts
33713
33883
  import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync9, writeFileSync as writeFileSync3 } from "fs";
@@ -33760,12 +33930,12 @@ function collectSideEffects(tree) {
33760
33930
  );
33761
33931
  }
33762
33932
  function readFlowTree(slug) {
33763
- const path38 = join3(flowsDir(), slug, "_data.json");
33764
- if (!existsSync5(path38)) {
33933
+ const path39 = join3(flowsDir(), slug, "_data.json");
33934
+ if (!existsSync5(path39)) {
33765
33935
  failLocal(`No form "${slug}". Run "baker flows list" to see the forms in this workspace.`);
33766
33936
  }
33767
33937
  try {
33768
- return JSON.parse(readFileSync9(path38, "utf-8"));
33938
+ return JSON.parse(readFileSync9(path39, "utf-8"));
33769
33939
  } catch (error) {
33770
33940
  failLocal(`Could not read form "${slug}": ${error instanceof Error ? error.message : String(error)}`);
33771
33941
  }
@@ -33917,7 +34087,7 @@ registerSchema({
33917
34087
  parent: { type: "string", description: "Parent node id. Omit to append to the root's children.", required: false }
33918
34088
  }
33919
34089
  });
33920
- var addNodeCommand = defineCommand112({
34090
+ var addNodeCommand = defineCommand113({
33921
34091
  meta: {
33922
34092
  name: "add-node",
33923
34093
  description: 'Add a step to a form, with the skeleton its type requires. Example: baker flows add-node contact --type customForm --name "Contact Info"'
@@ -34013,7 +34183,7 @@ registerSchema({
34013
34183
  }
34014
34184
  }
34015
34185
  });
34016
- var addSideEffectCommand = defineCommand112({
34186
+ var addSideEffectCommand = defineCommand113({
34017
34187
  meta: {
34018
34188
  name: "add-side-effect",
34019
34189
  description: "Add a side effect to a step, with the skeleton its type requires. Example: baker flows add-side-effect contact --node <id> --type googleSpreadsheet"
@@ -34095,7 +34265,7 @@ var addSideEffectCommand = defineCommand112({
34095
34265
 
34096
34266
  // src/commands/flows/map.ts
34097
34267
  import { readFileSync as readFileSync11 } from "fs";
34098
- import { defineCommand as defineCommand113 } from "citty";
34268
+ import { defineCommand as defineCommand114 } from "citty";
34099
34269
 
34100
34270
  // src/commands/flows/value-expression.ts
34101
34271
  import { existsSync as existsSync6, readFileSync as readFileSync10 } from "fs";
@@ -34157,10 +34327,10 @@ function parseValueExpression(raw) {
34157
34327
  return parts.map(parsePart);
34158
34328
  }
34159
34329
  function trackingFieldIds() {
34160
- const path38 = join4(flowsDir(), "..", "tracking.ts");
34161
- if (!existsSync6(path38)) return null;
34330
+ const path39 = join4(flowsDir(), "..", "tracking.ts");
34331
+ if (!existsSync6(path39)) return null;
34162
34332
  try {
34163
- const source = readFileSync10(path38, "utf-8");
34333
+ const source = readFileSync10(path39, "utf-8");
34164
34334
  const block2 = source.match(/TRACKING_FIELD_IDS\s*=\s*\[([\s\S]*?)\]\s*as const/)?.[1];
34165
34335
  if (!block2) return null;
34166
34336
  const ids = [...block2.matchAll(/"(tracking\.[a-z0-9_]+)"/g)].map((match) => match[1]);
@@ -34712,13 +34882,13 @@ function specsFromFile(parsed) {
34712
34882
  return `${destField}${type}=${entry?.value ?? ""}`;
34713
34883
  });
34714
34884
  }
34715
- function readSpecFile(path38) {
34885
+ function readSpecFile(path39) {
34716
34886
  let raw;
34717
34887
  try {
34718
- raw = path38 === "-" ? readFileSync11(0, "utf-8") : readFileSync11(path38, "utf-8");
34888
+ raw = path39 === "-" ? readFileSync11(0, "utf-8") : readFileSync11(path39, "utf-8");
34719
34889
  } catch (error) {
34720
34890
  refuse(
34721
- `Could not read ${path38 === "-" ? "the mapping from stdin" : `"${path38}"`}: ${error instanceof Error ? error.message : String(error)}`
34891
+ `Could not read ${path39 === "-" ? "the mapping from stdin" : `"${path39}"`}: ${error instanceof Error ? error.message : String(error)}`
34722
34892
  );
34723
34893
  }
34724
34894
  let parsed;
@@ -34726,7 +34896,7 @@ function readSpecFile(path38) {
34726
34896
  parsed = JSON.parse(raw);
34727
34897
  } catch (error) {
34728
34898
  refuse(
34729
- `${path38 === "-" ? "stdin" : `"${path38}"`} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
34899
+ `${path39 === "-" ? "stdin" : `"${path39}"`} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
34730
34900
  'Expected { "map": { "<destField>": "<value>", \u2026 } }'
34731
34901
  );
34732
34902
  }
@@ -34868,7 +35038,7 @@ async function mapOptions(args, fromFile, into) {
34868
35038
  destinationFields: hubspotForm ? await hubspotFieldCatalog(hubspotForm) : void 0
34869
35039
  };
34870
35040
  }
34871
- var mapCommand2 = defineCommand113({
35041
+ var mapCommand2 = defineCommand114({
34872
35042
  meta: {
34873
35043
  name: "map",
34874
35044
  description: "Map form fields onto any value a side effect sends \u2014 never hand-write a mapping. Start here: baker flows map contact --from map.json --write"
@@ -34966,7 +35136,7 @@ var mapCommand2 = defineCommand113({
34966
35136
  });
34967
35137
 
34968
35138
  // src/commands/flows/normalize.ts
34969
- import { defineCommand as defineCommand114 } from "citty";
35139
+ import { defineCommand as defineCommand115 } from "citty";
34970
35140
  var OPTIONAL_STRINGS = ["oauthProviderId"];
34971
35141
  var ARRAY_FIELDS = [
34972
35142
  "fieldMapping",
@@ -34980,18 +35150,18 @@ var ARRAY_FIELDS = [
34980
35150
  "tagIds"
34981
35151
  ];
34982
35152
  var ARRAY_OWNERS = ["", "body"];
34983
- function dropUnsetOptionals(sideEffect, path38) {
35153
+ function dropUnsetOptionals(sideEffect, path39) {
34984
35154
  return OPTIONAL_STRINGS.flatMap((key) => {
34985
35155
  if (!(key in sideEffect) || sideEffect[key] !== null && sideEffect[key] !== "") return [];
34986
35156
  delete sideEffect[key];
34987
- return [{ path: path38, change: `dropped \`${key}\` (an optional string is absent, never null)` }];
35157
+ return [{ path: path39, change: `dropped \`${key}\` (an optional string is absent, never null)` }];
34988
35158
  });
34989
35159
  }
34990
- function fillNulledArrays(target, prefix, path38) {
35160
+ function fillNulledArrays(target, prefix, path39) {
34991
35161
  return ARRAY_FIELDS.flatMap((key) => {
34992
35162
  if (!(key in target) || target[key] !== null) return [];
34993
35163
  target[key] = [];
34994
- return [{ path: path38, change: `\`${prefix}${key}: null\` \u2192 \`[]\`` }];
35164
+ return [{ path: path39, change: `\`${prefix}${key}: null\` \u2192 \`[]\`` }];
34995
35165
  });
34996
35166
  }
34997
35167
  function sideEffectsOf(node) {
@@ -35001,13 +35171,13 @@ function sideEffectsOf(node) {
35001
35171
  );
35002
35172
  }
35003
35173
  function normalizeSideEffect(sideEffect, where) {
35004
- const path38 = `${where} \u2192 ${String(sideEffect.id ?? "side effect")}`;
35174
+ const path39 = `${where} \u2192 ${String(sideEffect.id ?? "side effect")}`;
35005
35175
  const arrays = ARRAY_OWNERS.flatMap((owner) => {
35006
35176
  const target = owner ? sideEffect[owner] : sideEffect;
35007
35177
  if (!target || typeof target !== "object") return [];
35008
- return fillNulledArrays(target, owner ? `${owner}.` : "", path38);
35178
+ return fillNulledArrays(target, owner ? `${owner}.` : "", path39);
35009
35179
  });
35010
- return [...dropUnsetOptionals(sideEffect, path38), ...arrays];
35180
+ return [...dropUnsetOptionals(sideEffect, path39), ...arrays];
35011
35181
  }
35012
35182
  function normalizeFlowTree(tree) {
35013
35183
  const changes = [];
@@ -35035,7 +35205,7 @@ registerSchema({
35035
35205
  }
35036
35206
  }
35037
35207
  });
35038
- var normalizeCommand = defineCommand114({
35208
+ var normalizeCommand = defineCommand115({
35039
35209
  meta: {
35040
35210
  name: "normalize",
35041
35211
  description: "Repair the shapes a form file is allowed to get slightly wrong. Example: baker flows normalize contact --write"
@@ -35066,7 +35236,7 @@ var normalizeCommand = defineCommand114({
35066
35236
  });
35067
35237
 
35068
35238
  // src/commands/flows/schema.ts
35069
- import { defineCommand as defineCommand115 } from "citty";
35239
+ import { defineCommand as defineCommand116 } from "citty";
35070
35240
  registerSchema({
35071
35241
  command: "flows.schema",
35072
35242
  description: "The shape of one node or side-effect type: a pasteable skeleton, who fills each part, and the reference file with the craft. Start here before authoring anything in a form's _data.json.",
@@ -35091,7 +35261,7 @@ function triggerHints(triggers) {
35091
35261
  `A side effect on this step carries \`triggerId: "${triggers.completion}"\`, and \`baker flows add-side-effect\` writes it for you. The trigger belongs to the STEP, not to the side-effect type: one that does not match is valid, ships, and never runs.` + (others.length > 0 ? ` Other moments this step raises: ${others.join(", ")}.` : "")
35092
35262
  ];
35093
35263
  }
35094
- var schemaCommand = defineCommand115({
35264
+ var schemaCommand = defineCommand116({
35095
35265
  meta: {
35096
35266
  name: "schema",
35097
35267
  description: "Show a node or side-effect type's shape. Start here: baker flows schema node customForm \xB7 baker flows schema side-effect googleSpreadsheet \xB7 baker flows schema (lists both)"
@@ -35225,7 +35395,7 @@ function displayName(slug) {
35225
35395
  const name = tree.displayName;
35226
35396
  return typeof name === "string" && name.trim() ? name.trim() : slug;
35227
35397
  }
35228
- var listCommand12 = defineCommand116({
35398
+ var listCommand12 = defineCommand117({
35229
35399
  meta: {
35230
35400
  name: "list",
35231
35401
  description: "List Forms in this workspace with the count of confidential fields still needing setup. Example: baker flows list"
@@ -35246,7 +35416,7 @@ var listCommand12 = defineCommand116({
35246
35416
  });
35247
35417
  }
35248
35418
  });
35249
- var showCommand3 = defineCommand116({
35419
+ var showCommand3 = defineCommand117({
35250
35420
  meta: {
35251
35421
  name: "show",
35252
35422
  description: "Show a Form's confidential fields and their configuration status (never secret values). Example: baker flows show contact"
@@ -35274,7 +35444,7 @@ var showCommand3 = defineCommand116({
35274
35444
  });
35275
35445
  }
35276
35446
  });
35277
- var flowsCommand = defineCommand116({
35447
+ var flowsCommand = defineCommand117({
35278
35448
  meta: {
35279
35449
  name: "flows",
35280
35450
  description: `Read and shape this workspace's Forms (flows).
@@ -35308,10 +35478,10 @@ Full guide: __tooling__/docs/tools/baker/flows.md`
35308
35478
  });
35309
35479
 
35310
35480
  // src/commands/ga4/index.ts
35311
- import { defineCommand as defineCommand123 } from "citty";
35481
+ import { defineCommand as defineCommand124 } from "citty";
35312
35482
 
35313
35483
  // src/commands/ga4/audit.ts
35314
- import { defineCommand as defineCommand117 } from "citty";
35484
+ import { defineCommand as defineCommand118 } from "citty";
35315
35485
 
35316
35486
  // src/commands/ga4/resolve.ts
35317
35487
  async function fetchProperties(useCache = true) {
@@ -35377,7 +35547,7 @@ registerSchema({
35377
35547
  "no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
35378
35548
  }
35379
35549
  });
35380
- var auditCommand2 = defineCommand117({
35550
+ var auditCommand2 = defineCommand118({
35381
35551
  meta: {
35382
35552
  name: "audit",
35383
35553
  description: `Run all GA4 admin health checks. Returns property config with playbook warnings.
@@ -35432,7 +35602,7 @@ Examples:
35432
35602
  });
35433
35603
 
35434
35604
  // src/commands/ga4/config.ts
35435
- import { defineCommand as defineCommand118 } from "citty";
35605
+ import { defineCommand as defineCommand119 } from "citty";
35436
35606
 
35437
35607
  // src/commands/ga4/shared.ts
35438
35608
  import { readFileSync as readFileSync12 } from "fs";
@@ -35545,10 +35715,10 @@ async function stageOps(ops) {
35545
35715
  handleError2(err);
35546
35716
  }
35547
35717
  }
35548
- async function draftAction2(path38, body, chat) {
35718
+ async function draftAction2(path39, body, chat) {
35549
35719
  const chatId = resolveChatId(chat);
35550
35720
  try {
35551
- const data = await apiPost(path38, { chatId, ...body });
35721
+ const data = await apiPost(path39, { chatId, ...body });
35552
35722
  writeJsonEnvelope({ ok: true, data });
35553
35723
  return data;
35554
35724
  } catch (err) {
@@ -35628,7 +35798,7 @@ function configHints(config) {
35628
35798
  }
35629
35799
  return hints;
35630
35800
  }
35631
- var configCommand = defineCommand118({
35801
+ var configCommand = defineCommand119({
35632
35802
  meta: {
35633
35803
  name: "config",
35634
35804
  description: `Read how the property is configured to measure \u2014 key events, custom definitions, custom events, retention
@@ -35660,7 +35830,7 @@ Examples:
35660
35830
  });
35661
35831
 
35662
35832
  // src/commands/ga4/draft.ts
35663
- import { defineCommand as defineCommand119 } from "citty";
35833
+ import { defineCommand as defineCommand120 } from "citty";
35664
35834
  registerSchema({
35665
35835
  command: "ga4.draft",
35666
35836
  description: "Review and undo the Google Analytics changes staged on this chat. Use `list` to see everything staged, `show` to inspect one change in full before the chat completes, `amend` to correct one in place, and `remove`/`clear` to drop them. `list` and `show` take --chat <id> to read an earlier chat's changes instead.",
@@ -35669,13 +35839,13 @@ registerSchema({
35669
35839
  chat: { type: "string", description: CHAT_READ_ARG.description, required: false }
35670
35840
  }
35671
35841
  });
35672
- var draftCommand3 = defineCommand119({
35842
+ var draftCommand3 = defineCommand120({
35673
35843
  meta: {
35674
35844
  name: "draft",
35675
35845
  description: "List, show, amend, remove or clear the Google Analytics changes staged on this chat. `list` and `show` take --chat <id> to read an earlier chat's changes instead."
35676
35846
  },
35677
35847
  subCommands: {
35678
- list: defineCommand119({
35848
+ list: defineCommand120({
35679
35849
  meta: { name: "list", description: "Review everything staged on this chat (--json for the raw envelope)" },
35680
35850
  args: {
35681
35851
  json: { type: "boolean", description: "Print the raw JSON envelope", required: false },
@@ -35685,7 +35855,7 @@ var draftCommand3 = defineCommand119({
35685
35855
  await draftList(args.json === true, args.chat);
35686
35856
  }
35687
35857
  }),
35688
- show: defineCommand119({
35858
+ show: defineCommand120({
35689
35859
  meta: {
35690
35860
  name: "show",
35691
35861
  description: "Print the full staged payload for one change, alongside how the property looks today \u2014 the receipt to verify it before the chat completes (never truncated)."
@@ -35702,7 +35872,7 @@ var draftCommand3 = defineCommand119({
35702
35872
  );
35703
35873
  }
35704
35874
  }),
35705
- amend: defineCommand119({
35875
+ amend: defineCommand120({
35706
35876
  meta: {
35707
35877
  name: "amend",
35708
35878
  description: "Update a staged change in place \u2014 merges a JSON patch into its payload (objects deep-merge, null deletes a key, arrays/scalars replace) and re-runs every check. Use this instead of remove + re-create."
@@ -35719,7 +35889,7 @@ var draftCommand3 = defineCommand119({
35719
35889
  });
35720
35890
  }
35721
35891
  }),
35722
- remove: defineCommand119({
35892
+ remove: defineCommand120({
35723
35893
  meta: { name: "remove", description: "Remove one staged change" },
35724
35894
  args: { ref: { type: "positional", description: "Staged ref or target id", required: false } },
35725
35895
  run: async ({ args }) => {
@@ -35728,7 +35898,7 @@ var draftCommand3 = defineCommand119({
35728
35898
  });
35729
35899
  }
35730
35900
  }),
35731
- clear: defineCommand119({
35901
+ clear: defineCommand120({
35732
35902
  meta: { name: "clear", description: "Discard all Google Analytics changes staged on this chat" },
35733
35903
  run: async () => {
35734
35904
  await draftAction2("/api/ga4/draft/clear", {});
@@ -35738,7 +35908,7 @@ var draftCommand3 = defineCommand119({
35738
35908
  });
35739
35909
 
35740
35910
  // src/commands/ga4/properties.ts
35741
- import { defineCommand as defineCommand120 } from "citty";
35911
+ import { defineCommand as defineCommand121 } from "citty";
35742
35912
  registerSchema({
35743
35913
  command: "ga4.properties",
35744
35914
  description: "List the GA4 properties this company connected \u2014 there can be several, and every one of them is yours to query. Returns the property IDs the query and audit commands take. Run this first to find property IDs.",
@@ -35754,7 +35924,7 @@ function propertyHints(properties) {
35754
35924
  resources: properties.map((property) => ({ id: property.externalId, label: property.name }))
35755
35925
  });
35756
35926
  }
35757
- var propertiesCommand = defineCommand120({
35927
+ var propertiesCommand = defineCommand121({
35758
35928
  meta: {
35759
35929
  name: "properties",
35760
35930
  description: `List accessible GA4 properties.
@@ -35804,7 +35974,7 @@ Examples:
35804
35974
  // src/commands/ga4/query.ts
35805
35975
  import { appendFileSync as appendFileSync2, existsSync as existsSync7, readFileSync as readFileSync13, writeFileSync as writeFileSync4 } from "fs";
35806
35976
  import { resolve as resolve2 } from "path";
35807
- import { defineCommand as defineCommand121 } from "citty";
35977
+ import { defineCommand as defineCommand122 } from "citty";
35808
35978
 
35809
35979
  // src/commands/ga4/presets.ts
35810
35980
  var GA4_PRESETS = [
@@ -35939,7 +36109,7 @@ function handleError3(err) {
35939
36109
  });
35940
36110
  process.exit(1);
35941
36111
  }
35942
- var queryCommand2 = defineCommand121({
36112
+ var queryCommand2 = defineCommand122({
35943
36113
  meta: {
35944
36114
  name: "query",
35945
36115
  description: `Run GA4 Data API reports. Preset-first with free-form escape hatch.
@@ -36010,7 +36180,7 @@ Free-form (escape hatch):
36010
36180
  });
36011
36181
 
36012
36182
  // src/commands/ga4/write-commands.ts
36013
- import { defineCommand as defineCommand122 } from "citty";
36183
+ import { defineCommand as defineCommand123 } from "citty";
36014
36184
  var PROPERTY_ARG_DESCRIPTION = "GA4 property id (optional only when one property is connected \u2014 run `baker ga4 properties`)";
36015
36185
  var propertyArg = { type: "string", description: PROPERTY_ARG_DESCRIPTION, required: false };
36016
36186
  function createJsonDescription(noun) {
@@ -36028,7 +36198,7 @@ registerSchema({
36028
36198
  "property-id": { type: "string", description: PROPERTY_ARG_DESCRIPTION, required: false }
36029
36199
  }
36030
36200
  });
36031
- var keyEventCommand = defineCommand122({
36201
+ var keyEventCommand = defineCommand123({
36032
36202
  meta: {
36033
36203
  name: "key-event",
36034
36204
  description: `Stage key-event (conversion) changes
@@ -36041,7 +36211,7 @@ Examples:
36041
36211
  baker ga4 key-event delete 4185`
36042
36212
  },
36043
36213
  subCommands: {
36044
- create: defineCommand122({
36214
+ create: defineCommand123({
36045
36215
  meta: {
36046
36216
  name: "create",
36047
36217
  description: "Stage a new key event. Needs eventName and countingMethod (ONCE_PER_EVENT or ONCE_PER_SESSION); defaultValue sets a monetary value when the event does not send one."
@@ -36062,7 +36232,7 @@ Examples:
36062
36232
  );
36063
36233
  }
36064
36234
  }),
36065
- update: defineCommand122({
36235
+ update: defineCommand123({
36066
36236
  meta: {
36067
36237
  name: "update",
36068
36238
  description: "Stage a change to an existing key event (pass its id). Only countingMethod and defaultValue can change \u2014 Google will not rename a key event."
@@ -36082,7 +36252,7 @@ Examples:
36082
36252
  });
36083
36253
  }
36084
36254
  }),
36085
- delete: defineCommand122({
36255
+ delete: defineCommand123({
36086
36256
  meta: {
36087
36257
  name: "delete",
36088
36258
  description: "Stage removing a key event, so the event stops counting as a conversion. The event itself keeps being collected. Irreversible once the chat completes \u2014 confirm with the user first."
@@ -36130,7 +36300,7 @@ for (const { kind, noun, createHint } of DEFINITIONS) {
36130
36300
  }
36131
36301
  function definitionCommand(definition) {
36132
36302
  const { command, kind, noun, example } = definition;
36133
- return defineCommand122({
36303
+ return defineCommand123({
36134
36304
  meta: {
36135
36305
  name: command,
36136
36306
  description: `Stage ${noun} changes
@@ -36142,7 +36312,7 @@ Examples:
36142
36312
  baker ga4 ${command} archive 12`
36143
36313
  },
36144
36314
  subCommands: {
36145
- create: defineCommand122({
36315
+ create: defineCommand123({
36146
36316
  meta: { name: "create", description: `Stage a new ${noun} (or a JSON array of them, staged together)` },
36147
36317
  args: {
36148
36318
  json: { type: "string", description: createJsonDescription(noun), required: false },
@@ -36160,7 +36330,7 @@ Examples:
36160
36330
  );
36161
36331
  }
36162
36332
  }),
36163
- update: defineCommand122({
36333
+ update: defineCommand123({
36164
36334
  meta: {
36165
36335
  name: "update",
36166
36336
  description: `Stage a change to an existing ${noun} (pass its id). The parameter name and scope cannot change \u2014 archive and recreate for that.`
@@ -36180,7 +36350,7 @@ Examples:
36180
36350
  });
36181
36351
  }
36182
36352
  }),
36183
- archive: defineCommand122({
36353
+ archive: defineCommand123({
36184
36354
  meta: {
36185
36355
  name: "archive",
36186
36356
  description: `Stage archiving a ${noun} (pass its id). It stops collecting and leaves reporting; past data stays. Irreversible once the chat completes \u2014 confirm with the user first.`
@@ -36225,7 +36395,7 @@ var dataStreamArg = {
36225
36395
  function withStream(args) {
36226
36396
  return typeof args["data-stream"] === "string" ? { dataStream: args["data-stream"] } : {};
36227
36397
  }
36228
- var customEventCommand = defineCommand122({
36398
+ var customEventCommand = defineCommand123({
36229
36399
  meta: {
36230
36400
  name: "custom-event",
36231
36401
  description: `Stage custom events \u2014 new events built from events the site already sends
@@ -36236,7 +36406,7 @@ Examples:
36236
36406
  baker ga4 custom-event delete 7`
36237
36407
  },
36238
36408
  subCommands: {
36239
- create: defineCommand122({
36409
+ create: defineCommand123({
36240
36410
  meta: {
36241
36411
  name: "create",
36242
36412
  description: "Stage a new custom event. Conditions match the source event: use field `event_name` to match the event itself, or any parameter name to match its value."
@@ -36259,7 +36429,7 @@ Examples:
36259
36429
  );
36260
36430
  }
36261
36431
  }),
36262
- update: defineCommand122({
36432
+ update: defineCommand123({
36263
36433
  meta: { name: "update", description: "Stage a change to an existing custom event (pass its id)" },
36264
36434
  args: {
36265
36435
  id: { type: "positional", description: "Custom event rule id", required: false },
@@ -36278,7 +36448,7 @@ Examples:
36278
36448
  });
36279
36449
  }
36280
36450
  }),
36281
- delete: defineCommand122({
36451
+ delete: defineCommand123({
36282
36452
  meta: {
36283
36453
  name: "delete",
36284
36454
  description: "Stage removing a custom event (pass its id). The event stops being created from then on; data already collected under it stays. Confirm with the user first."
@@ -36333,7 +36503,7 @@ function booleanArg(input, flag) {
36333
36503
  if (input === "false") return false;
36334
36504
  return failValidation3(`--${flag} must be true or false`);
36335
36505
  }
36336
- var dataRetentionCommand = defineCommand122({
36506
+ var dataRetentionCommand = defineCommand123({
36337
36507
  meta: {
36338
36508
  name: "data-retention",
36339
36509
  description: `Stage a change to how long Google Analytics keeps data
@@ -36343,7 +36513,7 @@ Examples:
36343
36513
  baker ga4 data-retention set --event-data 14 --user-data 14 --reset-on-activity true`
36344
36514
  },
36345
36515
  subCommands: {
36346
- set: defineCommand122({
36516
+ set: defineCommand123({
36347
36517
  meta: { name: "set", description: "Stage the retention window (months)" },
36348
36518
  args: {
36349
36519
  "event-data": { type: "string", description: "2, 14, 26, 38 or 50", required: false },
@@ -36379,7 +36549,7 @@ registerSchema({
36379
36549
  "property-id": { type: "string", description: PROPERTY_ARG_DESCRIPTION, required: false }
36380
36550
  }
36381
36551
  });
36382
- var audienceCommand = defineCommand122({
36552
+ var audienceCommand = defineCommand123({
36383
36553
  meta: {
36384
36554
  name: "audience",
36385
36555
  description: `Stage audience changes (saved groups of users, for retargeting and analysis)
@@ -36411,7 +36581,7 @@ Examples:
36411
36581
  baker ga4 audience archive 8813`
36412
36582
  },
36413
36583
  subCommands: {
36414
- create: defineCommand122({
36584
+ create: defineCommand123({
36415
36585
  meta: { name: "create", description: createJsonDescription("audience") },
36416
36586
  args: {
36417
36587
  json: { type: "string", description: createJsonDescription("audience"), required: false },
@@ -36429,7 +36599,7 @@ Examples:
36429
36599
  );
36430
36600
  }
36431
36601
  }),
36432
- update: defineCommand122({
36602
+ update: defineCommand123({
36433
36603
  meta: {
36434
36604
  name: "update",
36435
36605
  description: "Stage a new name or description (pass the audience id). Who is in it cannot be changed."
@@ -36450,7 +36620,7 @@ Examples:
36450
36620
  });
36451
36621
  }
36452
36622
  }),
36453
- archive: defineCommand122({
36623
+ archive: defineCommand123({
36454
36624
  meta: {
36455
36625
  name: "archive",
36456
36626
  description: "Stage retiring an audience. Cannot be undone \u2014 the users it gathered do not come back."
@@ -36479,7 +36649,7 @@ registerSchema({
36479
36649
  "property-id": { type: "string", description: PROPERTY_ARG_DESCRIPTION, required: false }
36480
36650
  }
36481
36651
  });
36482
- var propertyCommand = defineCommand122({
36652
+ var propertyCommand = defineCommand123({
36483
36653
  meta: {
36484
36654
  name: "property",
36485
36655
  description: `Stage a change to the property's own settings
@@ -36489,7 +36659,7 @@ Examples:
36489
36659
  baker ga4 property set --name "Acme \u2014 Web" --industry REAL_ESTATE`
36490
36660
  },
36491
36661
  subCommands: {
36492
- set: defineCommand122({
36662
+ set: defineCommand123({
36493
36663
  meta: { name: "set", description: "Stage the property name and/or industry category" },
36494
36664
  args: {
36495
36665
  name: { type: "string", description: "New property display name", required: false },
@@ -36529,7 +36699,7 @@ registerSchema({
36529
36699
  "property-id": { type: "string", description: PROPERTY_ARG_DESCRIPTION, required: false }
36530
36700
  }
36531
36701
  });
36532
- var adsLinkCommand = defineCommand122({
36702
+ var adsLinkCommand = defineCommand123({
36533
36703
  meta: {
36534
36704
  name: "ads-link",
36535
36705
  description: `Stage linking this property to a Google Ads account
@@ -36544,7 +36714,7 @@ Examples:
36544
36714
  baker ga4 ads-link delete L1234`
36545
36715
  },
36546
36716
  subCommands: {
36547
- create: defineCommand122({
36717
+ create: defineCommand123({
36548
36718
  meta: { name: "create", description: "Stage linking the property to a Google Ads account" },
36549
36719
  args: {
36550
36720
  "customer-id": { type: "string", description: "Google Ads customer id (123-456-7890)", required: false },
@@ -36563,7 +36733,7 @@ Examples:
36563
36733
  await stageOp3({ kind: "ga4.googleAdsLink.create", payload, ...withProperty(args) });
36564
36734
  }
36565
36735
  }),
36566
- update: defineCommand122({
36736
+ update: defineCommand123({
36567
36737
  meta: {
36568
36738
  name: "update",
36569
36739
  description: "Stage the ads-personalisation setting on an existing link (pass its id)"
@@ -36585,7 +36755,7 @@ Examples:
36585
36755
  });
36586
36756
  }
36587
36757
  }),
36588
- delete: defineCommand122({
36758
+ delete: defineCommand123({
36589
36759
  meta: {
36590
36760
  name: "delete",
36591
36761
  description: "Stage unlinking. Stops conversion imports and strands every audience on this property."
@@ -36636,7 +36806,7 @@ var MEASUREMENT_FLAGS = {
36636
36806
  "page-changes": "pageChangesEnabled",
36637
36807
  "form-interactions": "formInteractionsEnabled"
36638
36808
  };
36639
- var enhancedMeasurementCommand = defineCommand122({
36809
+ var enhancedMeasurementCommand = defineCommand123({
36640
36810
  meta: {
36641
36811
  name: "enhanced-measurement",
36642
36812
  description: `Stage the automatic events GA4 collects on a website stream
@@ -36650,7 +36820,7 @@ Examples:
36650
36820
  baker ga4 enhanced-measurement set --video false`
36651
36821
  },
36652
36822
  subCommands: {
36653
- set: defineCommand122({
36823
+ set: defineCommand123({
36654
36824
  meta: { name: "set", description: "Stage which automatic events the website stream collects" },
36655
36825
  args: {
36656
36826
  scrolls: { type: "string", description: "true|false", required: false },
@@ -36693,7 +36863,7 @@ Examples:
36693
36863
  });
36694
36864
 
36695
36865
  // src/commands/ga4/index.ts
36696
- var ga4Command = defineCommand123({
36866
+ var ga4Command = defineCommand124({
36697
36867
  meta: {
36698
36868
  name: "ga4",
36699
36869
  description: `Google Analytics 4. Report on a property, audit its config, and change what it measures.
@@ -36742,12 +36912,12 @@ Full guide: __tooling__/docs/tools/baker/ga4.md`
36742
36912
  });
36743
36913
 
36744
36914
  // src/commands/gsc/index.ts
36745
- import { defineCommand as defineCommand127 } from "citty";
36915
+ import { defineCommand as defineCommand128 } from "citty";
36746
36916
 
36747
36917
  // src/commands/gsc/query.ts
36748
36918
  import { appendFileSync as appendFileSync3, existsSync as existsSync8, readFileSync as readFileSync14, writeFileSync as writeFileSync5 } from "fs";
36749
36919
  import { resolve as resolve3 } from "path";
36750
- import { defineCommand as defineCommand124 } from "citty";
36920
+ import { defineCommand as defineCommand125 } from "citty";
36751
36921
 
36752
36922
  // src/commands/gsc/presets.ts
36753
36923
  var GSC_PRESETS = [
@@ -36941,7 +37111,7 @@ function handleError4(err) {
36941
37111
  });
36942
37112
  process.exit(1);
36943
37113
  }
36944
- var queryCommand3 = defineCommand124({
37114
+ var queryCommand3 = defineCommand125({
36945
37115
  meta: {
36946
37116
  name: "query",
36947
37117
  description: `Run GSC Search Analytics queries. Preset-first with free-form escape hatch.
@@ -37019,7 +37189,7 @@ Free-form (escape hatch):
37019
37189
  });
37020
37190
 
37021
37191
  // src/commands/gsc/sitemaps.ts
37022
- import { defineCommand as defineCommand125 } from "citty";
37192
+ import { defineCommand as defineCommand126 } from "citty";
37023
37193
  registerSchema({
37024
37194
  command: "gsc.sitemaps",
37025
37195
  description: "List sitemaps for a Search Console site. Check sitemap health and errors.",
@@ -37028,7 +37198,7 @@ registerSchema({
37028
37198
  "no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
37029
37199
  }
37030
37200
  });
37031
- var sitemapsCommand = defineCommand125({
37201
+ var sitemapsCommand = defineCommand126({
37032
37202
  meta: {
37033
37203
  name: "sitemaps",
37034
37204
  description: `List sitemaps for a site. Check health and errors.
@@ -37081,7 +37251,7 @@ Examples:
37081
37251
  });
37082
37252
 
37083
37253
  // src/commands/gsc/sites.ts
37084
- import { defineCommand as defineCommand126 } from "citty";
37254
+ import { defineCommand as defineCommand127 } from "citty";
37085
37255
  registerSchema({
37086
37256
  command: "gsc.sites",
37087
37257
  description: "List the Search Console sites this company connected \u2014 there can be several, and every one of them is yours to query. Returns the site URLs the query and sitemaps commands take.",
@@ -37097,7 +37267,7 @@ function siteHints(sites) {
37097
37267
  resources: sites.map((site) => ({ id: site.siteUrl, label: site.permissionLevel }))
37098
37268
  });
37099
37269
  }
37100
- var sitesCommand = defineCommand126({
37270
+ var sitesCommand = defineCommand127({
37101
37271
  meta: {
37102
37272
  name: "sites",
37103
37273
  description: `List verified Search Console sites.
@@ -37145,7 +37315,7 @@ Examples:
37145
37315
  });
37146
37316
 
37147
37317
  // src/commands/gsc/index.ts
37148
- var gscCommand = defineCommand127({
37318
+ var gscCommand = defineCommand128({
37149
37319
  meta: {
37150
37320
  name: "gsc",
37151
37321
  description: `Google Search Console commands. PPC-SEO arbitrage, brand halo analysis, negative keyword discovery.
@@ -37169,7 +37339,7 @@ Full guide: __tooling__/docs/tools/baker/gsc.md`
37169
37339
  });
37170
37340
 
37171
37341
  // src/commands/history/index.ts
37172
- import { defineCommand as defineCommand128 } from "citty";
37342
+ import { defineCommand as defineCommand129 } from "citty";
37173
37343
  registerSchema({
37174
37344
  command: "history.list",
37175
37345
  description: "Start here: unified account history (audit log) \u2014 everything that changed on this account, newest first: publishes, chat lifecycle, backlog actions, team changes, setup links, tags, schedules, ad-platform writes, followed advertisers, media, creatives, reports, domains, and integrations. Use it to see what happened recently before planning work. Compact by default; add --full for raw metadata per entry.",
@@ -37224,7 +37394,7 @@ function parseBoundedInt2(raw, name, min, max) {
37224
37394
  }
37225
37395
  return value;
37226
37396
  }
37227
- var listCommand13 = defineCommand128({
37397
+ var listCommand13 = defineCommand129({
37228
37398
  meta: {
37229
37399
  name: "list",
37230
37400
  description: "List recent account changes (unified audit log), newest first."
@@ -37270,7 +37440,7 @@ var listCommand13 = defineCommand128({
37270
37440
  }
37271
37441
  }
37272
37442
  });
37273
- var historyCommand = defineCommand128({
37443
+ var historyCommand = defineCommand129({
37274
37444
  meta: {
37275
37445
  name: "history",
37276
37446
  description: `Unified account history (audit log): what changed, who did it, and when.
@@ -37280,7 +37450,7 @@ Full guide: __tooling__/docs/tools/baker/history.md`
37280
37450
  });
37281
37451
 
37282
37452
  // src/commands/hubspot/index.ts
37283
- import { defineCommand as defineCommand129 } from "citty";
37453
+ import { defineCommand as defineCommand130 } from "citty";
37284
37454
  var EMBED_TYPES = ["legacy", "v4", "unknown"];
37285
37455
  function failNotConnected(err) {
37286
37456
  if (err instanceof ApiError && err.code === "FORBIDDEN" && err.message.includes(HUBSPOT_MISSING_GRANT_MARKER)) {
@@ -37434,7 +37604,7 @@ registerSchema({
37434
37604
  }
37435
37605
  }
37436
37606
  });
37437
- var formsListCommand = defineCommand129({
37607
+ var formsListCommand = defineCommand130({
37438
37608
  meta: {
37439
37609
  name: "list",
37440
37610
  description: "List HubSpot forms with embed type and post-submit action. Example: baker hubspot forms list --redirecting-only"
@@ -37479,7 +37649,7 @@ var formsListCommand = defineCommand129({
37479
37649
  }
37480
37650
  }
37481
37651
  });
37482
- var formsViewCommand = defineCommand129({
37652
+ var formsViewCommand = defineCommand130({
37483
37653
  meta: {
37484
37654
  name: "view",
37485
37655
  description: "Show one HubSpot form's fields and configuration. Example: baker hubspot forms view 1a2b3c"
@@ -37531,7 +37701,7 @@ var formsViewCommand = defineCommand129({
37531
37701
  }
37532
37702
  }
37533
37703
  });
37534
- var meetingsListCommand = defineCommand129({
37704
+ var meetingsListCommand = defineCommand130({
37535
37705
  meta: {
37536
37706
  name: "list",
37537
37707
  description: "List HubSpot meeting links (calendars). Example: baker hubspot meetings list"
@@ -37573,7 +37743,7 @@ var meetingsListCommand = defineCommand129({
37573
37743
  }
37574
37744
  }
37575
37745
  });
37576
- var meetingsViewCommand = defineCommand129({
37746
+ var meetingsViewCommand = defineCommand130({
37577
37747
  meta: {
37578
37748
  name: "view",
37579
37749
  description: "Show one HubSpot meeting link's booking fields. Example: baker hubspot meetings view discovery-call"
@@ -37617,7 +37787,7 @@ var meetingsViewCommand = defineCommand129({
37617
37787
  }
37618
37788
  }
37619
37789
  });
37620
- var formsSubmissionsCommand = defineCommand129({
37790
+ var formsSubmissionsCommand = defineCommand130({
37621
37791
  meta: {
37622
37792
  name: "submissions",
37623
37793
  description: "How many leads a form received, and when. Example: baker hubspot forms submissions <formId> --days 30"
@@ -37666,7 +37836,7 @@ var formsSubmissionsCommand = defineCommand129({
37666
37836
  }
37667
37837
  }
37668
37838
  });
37669
- var workflowsListCommand = defineCommand129({
37839
+ var workflowsListCommand = defineCommand130({
37670
37840
  meta: {
37671
37841
  name: "list",
37672
37842
  description: "List HubSpot workflows. Example: baker hubspot workflows list --enabled-only"
@@ -37731,7 +37901,7 @@ function workflowViewHints(workflow) {
37731
37901
  }
37732
37902
  return hints;
37733
37903
  }
37734
- var workflowsViewCommand = defineCommand129({
37904
+ var workflowsViewCommand = defineCommand130({
37735
37905
  meta: {
37736
37906
  name: "view",
37737
37907
  description: "Read one workflow's real configuration \u2014 enrolment, branches in order, and what each step writes. Example: baker hubspot workflows view 121183594 --full"
@@ -37758,7 +37928,7 @@ var workflowsViewCommand = defineCommand129({
37758
37928
  }
37759
37929
  }
37760
37930
  });
37761
- var pipelinesListCommand = defineCommand129({
37931
+ var pipelinesListCommand = defineCommand130({
37762
37932
  meta: {
37763
37933
  name: "list",
37764
37934
  description: "List HubSpot deal pipelines and their stages. Example: baker hubspot pipelines list"
@@ -37775,7 +37945,7 @@ var pipelinesListCommand = defineCommand129({
37775
37945
  }
37776
37946
  }
37777
37947
  });
37778
- var contactsSummaryCommand = defineCommand129({
37948
+ var contactsSummaryCommand = defineCommand130({
37779
37949
  meta: {
37780
37950
  name: "summary",
37781
37951
  description: "Whether recent leads are being worked, as counts. Example: baker hubspot contacts summary --days 30"
@@ -37845,7 +38015,7 @@ function contactLookupHints(data) {
37845
38015
  }
37846
38016
  return hints;
37847
38017
  }
37848
- var contactsLookupCommand = defineCommand129({
38018
+ var contactsLookupCommand = defineCommand130({
37849
38019
  meta: {
37850
38020
  name: "lookup",
37851
38021
  description: "Find one contact by email and see whether it was worked. Example: baker hubspot contacts lookup a@b.com"
@@ -37869,27 +38039,27 @@ var contactsLookupCommand = defineCommand129({
37869
38039
  }
37870
38040
  }
37871
38041
  });
37872
- var contactsCommand = defineCommand129({
38042
+ var contactsCommand = defineCommand130({
37873
38043
  meta: { name: "contacts", description: "Contacts on the connected HubSpot account." },
37874
38044
  subCommands: { summary: contactsSummaryCommand, lookup: contactsLookupCommand }
37875
38045
  });
37876
- var formsCommand = defineCommand129({
38046
+ var formsCommand = defineCommand130({
37877
38047
  meta: { name: "forms", description: "HubSpot forms on the connected account." },
37878
38048
  subCommands: { list: formsListCommand, view: formsViewCommand, submissions: formsSubmissionsCommand }
37879
38049
  });
37880
- var workflowsCommand = defineCommand129({
38050
+ var workflowsCommand = defineCommand130({
37881
38051
  meta: { name: "workflows", description: "HubSpot workflows on the connected account." },
37882
38052
  subCommands: { list: workflowsListCommand, view: workflowsViewCommand }
37883
38053
  });
37884
- var pipelinesCommand = defineCommand129({
38054
+ var pipelinesCommand = defineCommand130({
37885
38055
  meta: { name: "pipelines", description: "HubSpot deal pipelines on the connected account." },
37886
38056
  subCommands: { list: pipelinesListCommand }
37887
38057
  });
37888
- var meetingsCommand = defineCommand129({
38058
+ var meetingsCommand = defineCommand130({
37889
38059
  meta: { name: "meetings", description: "HubSpot meeting links (calendars) on the connected account." },
37890
38060
  subCommands: { list: meetingsListCommand, view: meetingsViewCommand }
37891
38061
  });
37892
- var hubspotCommand = defineCommand129({
38062
+ var hubspotCommand = defineCommand130({
37893
38063
  meta: {
37894
38064
  name: "hubspot",
37895
38065
  description: `Read the connected HubSpot account \u2014 forms, the leads they received, workflows, deal pipelines and
@@ -37927,10 +38097,10 @@ Full guide: __tooling__/docs/tools/baker/hubspot.md`
37927
38097
  });
37928
38098
 
37929
38099
  // src/commands/images/index.ts
37930
- import { defineCommand as defineCommand155 } from "citty";
38100
+ import { defineCommand as defineCommand156 } from "citty";
37931
38101
 
37932
38102
  // src/commands/images/crop.ts
37933
- import { defineCommand as defineCommand130 } from "citty";
38103
+ import { defineCommand as defineCommand131 } from "citty";
37934
38104
 
37935
38105
  // src/lib/image/crop-sprite.ts
37936
38106
  import sharp from "sharp";
@@ -37945,7 +38115,7 @@ function cropSprite(input, region) {
37945
38115
 
37946
38116
  // src/lib/image/io.ts
37947
38117
  import { randomBytes } from "crypto";
37948
- import { glob as fsGlob, readFile as readFile20, rename, stat as stat4, writeFile as writeFile11 } from "fs/promises";
38118
+ import { glob as fsGlob, readFile as readFile21, rename, stat as stat4, writeFile as writeFile12 } from "fs/promises";
37949
38119
  import { dirname as dirname2, extname as extname2, join as join5, resolve as resolve4 } from "path";
37950
38120
  var REMOTE_RE = /^https?:\/\//i;
37951
38121
  var GLOB_RE = /[*?[\]{}]/;
@@ -37978,11 +38148,11 @@ async function readImageBuffer(pathOrUrl) {
37978
38148
  const { buffer } = await fetchExternalBytes(pathOrUrl, { maxBytes: MAX_REMOTE_IMAGE_BYTES });
37979
38149
  return buffer;
37980
38150
  }
37981
- return readFile20(pathOrUrl);
38151
+ return readFile21(pathOrUrl);
37982
38152
  }
37983
- async function isDirectory(path38) {
38153
+ async function isDirectory(path39) {
37984
38154
  try {
37985
- const s = await stat4(path38);
38155
+ const s = await stat4(path39);
37986
38156
  return s.isDirectory();
37987
38157
  } catch {
37988
38158
  return false;
@@ -38001,7 +38171,7 @@ async function atomicWrite(targetPath, data) {
38001
38171
  const absolute = resolve4(targetPath);
38002
38172
  const dir = dirname2(absolute);
38003
38173
  const tmp = join5(dir, `.baker-image-${randomBytes(8).toString("hex")}.tmp`);
38004
- await writeFile11(tmp, data);
38174
+ await writeFile12(tmp, data);
38005
38175
  await rename(tmp, absolute);
38006
38176
  }
38007
38177
 
@@ -38052,7 +38222,7 @@ function emitError2(err) {
38052
38222
  }
38053
38223
  process.exit(1);
38054
38224
  }
38055
- var cropCommand = defineCommand130({
38225
+ var cropCommand = defineCommand131({
38056
38226
  meta: {
38057
38227
  name: "crop",
38058
38228
  description: "Crop a rectangular region from an image.\n\nExample: baker images crop sprite.png --x 0 --y 0 --width 64 --height 64 --output icon.png"
@@ -38088,7 +38258,7 @@ var cropCommand = defineCommand130({
38088
38258
  });
38089
38259
 
38090
38260
  // src/commands/images/delete.ts
38091
- import { defineCommand as defineCommand131 } from "citty";
38261
+ import { defineCommand as defineCommand132 } from "citty";
38092
38262
  registerSchema({
38093
38263
  command: "images.delete",
38094
38264
  description: "Delete an image by ID",
@@ -38102,7 +38272,7 @@ registerSchema({
38102
38272
  }
38103
38273
  }
38104
38274
  });
38105
- var deleteCommand2 = defineCommand131({
38275
+ var deleteCommand2 = defineCommand132({
38106
38276
  meta: {
38107
38277
  name: "delete",
38108
38278
  description: "Delete an image by ID. Use --dry-run to preview. Example: baker images delete j571abc123 --dry-run"
@@ -38143,7 +38313,7 @@ var deleteCommand2 = defineCommand131({
38143
38313
  });
38144
38314
 
38145
38315
  // src/commands/images/dimensions.ts
38146
- import { defineCommand as defineCommand132 } from "citty";
38316
+ import { defineCommand as defineCommand133 } from "citty";
38147
38317
 
38148
38318
  // src/lib/image/dimensions.ts
38149
38319
  import { imageSize } from "image-size";
@@ -38172,7 +38342,7 @@ registerSchema({
38172
38342
  }
38173
38343
  }
38174
38344
  });
38175
- var dimensionsCommand = defineCommand132({
38345
+ var dimensionsCommand = defineCommand133({
38176
38346
  meta: {
38177
38347
  name: "dimensions",
38178
38348
  description: "Read image dimensions without decoding the full file.\n\nExample: baker images dimensions ./logo.png\nExample: baker images dimensions https://acme.com/hero.png"
@@ -38228,7 +38398,7 @@ var dimensionsCommand = defineCommand132({
38228
38398
  });
38229
38399
 
38230
38400
  // src/commands/images/download.ts
38231
- import { defineCommand as defineCommand133 } from "citty";
38401
+ import { defineCommand as defineCommand134 } from "citty";
38232
38402
 
38233
38403
  // src/commands/images/downloadPaths.ts
38234
38404
  import { basename as basename2, extname as extname3, join as join6 } from "path";
@@ -38284,13 +38454,13 @@ function resolveDownloadPath({ baseName, extension, out, outIsDirectory: outIsDi
38284
38454
  }
38285
38455
  function disambiguate(paths) {
38286
38456
  const taken = /* @__PURE__ */ new Set();
38287
- return paths.map((path38) => {
38288
- if (!taken.has(path38)) {
38289
- taken.add(path38);
38290
- return path38;
38457
+ return paths.map((path39) => {
38458
+ if (!taken.has(path39)) {
38459
+ taken.add(path39);
38460
+ return path39;
38291
38461
  }
38292
- const ext = extname3(path38);
38293
- const stem = path38.slice(0, path38.length - ext.length);
38462
+ const ext = extname3(path39);
38463
+ const stem = path39.slice(0, path39.length - ext.length);
38294
38464
  let n = 2;
38295
38465
  while (taken.has(`${stem}-${n}${ext}`)) n += 1;
38296
38466
  const unique = `${stem}-${n}${ext}`;
@@ -38417,10 +38587,10 @@ async function runDownloads(plan) {
38417
38587
  const paths = disambiguate(fetched.map((item) => item.path));
38418
38588
  const downloaded = [];
38419
38589
  for (const [index, item] of fetched.entries()) {
38420
- const path38 = paths[index] ?? item.path;
38590
+ const path39 = paths[index] ?? item.path;
38421
38591
  try {
38422
- await atomicWrite(path38, item.buffer);
38423
- downloaded.push({ input: item.input, output: path38, bytes: item.buffer.length, contentType: item.contentType });
38592
+ await atomicWrite(path39, item.buffer);
38593
+ downloaded.push({ input: item.input, output: path39, bytes: item.buffer.length, contentType: item.contentType });
38424
38594
  } catch (err) {
38425
38595
  failed.push({ input: item.input, error: failureMessage(err, "Write failed") });
38426
38596
  }
@@ -38453,7 +38623,7 @@ function emitError3(err) {
38453
38623
  writeJson({ ok: false, error: { code: "INTERNAL_ERROR", message: "Unexpected error" } });
38454
38624
  process.exit(1);
38455
38625
  }
38456
- var downloadCommand = defineCommand133({
38626
+ var downloadCommand = defineCommand134({
38457
38627
  meta: {
38458
38628
  name: "download",
38459
38629
  description: "Download image URLs and/or library images to local files \u2014 the missing first half of `source \u2192 download \u2192 normalize \u2192 place`. Never use `curl` for this.\n\nExamples:\n baker images download https://media.withbaker.com/\u2026/logo.webp\n baker images download j57abc123 j57def456 --out src/pages/pricing/_images/\n baker images download https://\u2026/hero.png --out ./hero.png"
@@ -38486,7 +38656,7 @@ var downloadCommand = defineCommand133({
38486
38656
  });
38487
38657
 
38488
38658
  // src/commands/images/extract.ts
38489
- import { defineCommand as defineCommand134 } from "citty";
38659
+ import { defineCommand as defineCommand135 } from "citty";
38490
38660
 
38491
38661
  // src/commands/images/autoIngest.ts
38492
38662
  var AUTO_INGEST_MAX = {
@@ -38533,7 +38703,7 @@ registerSchema({
38533
38703
  }
38534
38704
  }
38535
38705
  });
38536
- var extractCommand = defineCommand134({
38706
+ var extractCommand = defineCommand135({
38537
38707
  meta: {
38538
38708
  name: "extract",
38539
38709
  description: "Pull every image from a single URL via Firecrawl. ~$0.001/scrape. Cap auto-ingest at 20.\n\nExample: baker images extract https://stripe.com --auto-ingest 5"
@@ -38588,7 +38758,7 @@ var extractCommand = defineCommand134({
38588
38758
  });
38589
38759
 
38590
38760
  // src/commands/images/find.ts
38591
- import { defineCommand as defineCommand135 } from "citty";
38761
+ import { defineCommand as defineCommand136 } from "citty";
38592
38762
 
38593
38763
  // src/commands/images/providerHits.ts
38594
38764
  function asRecord3(value) {
@@ -38726,7 +38896,7 @@ registerSchema({
38726
38896
  full: { type: "boolean", description: "Include full metadata", required: false, default: false }
38727
38897
  }
38728
38898
  });
38729
- var findCommand = defineCommand135({
38899
+ var findCommand = defineCommand136({
38730
38900
  meta: {
38731
38901
  name: "find",
38732
38902
  description: "Library-first fanout image search. Opt in to providers with --sources. `--fallback` short-circuits to externals only when library is thin. With --auto-ingest, ingested external hits return Baker-owned URLs.\n\nExample: baker images find 'office' --sources library,magnific --limit 20"
@@ -38799,7 +38969,7 @@ var findCommand = defineCommand135({
38799
38969
  });
38800
38970
 
38801
38971
  // src/commands/images/get.ts
38802
- import { defineCommand as defineCommand136 } from "citty";
38972
+ import { defineCommand as defineCommand137 } from "citty";
38803
38973
  registerSchema({
38804
38974
  command: "images.get",
38805
38975
  description: "Get a single image by ID",
@@ -38807,7 +38977,7 @@ registerSchema({
38807
38977
  id: { type: "string", description: "Image ID", required: true }
38808
38978
  }
38809
38979
  });
38810
- var getCommand3 = defineCommand136({
38980
+ var getCommand3 = defineCommand137({
38811
38981
  meta: { name: "get", description: "Get a single image by ID. Example: baker images get j571abc123" },
38812
38982
  args: {
38813
38983
  id: { type: "positional", description: "Image ID", required: false },
@@ -38843,7 +39013,7 @@ var getCommand3 = defineCommand136({
38843
39013
  });
38844
39014
 
38845
39015
  // src/commands/images/gif.ts
38846
- import { defineCommand as defineCommand137 } from "citty";
39016
+ import { defineCommand as defineCommand138 } from "citty";
38847
39017
  registerSchema({
38848
39018
  command: "images.gif",
38849
39019
  description: "Search Giphy for GIFs / reaction memes (paid social creative).",
@@ -38875,7 +39045,7 @@ registerSchema({
38875
39045
  }
38876
39046
  }
38877
39047
  });
38878
- var gifCommand = defineCommand137({
39048
+ var gifCommand = defineCommand138({
38879
39049
  meta: {
38880
39050
  name: "gif",
38881
39051
  description: "Search Giphy for GIFs / reaction memes \u2014 built for paid-social creative (Meta, TikTok, LinkedIn, X). Free API. Each hit carries WebP + GIF + MP4 URLs in providerMeta so you can pick the right format per platform.\n\nExample: baker images gif 'this is fine' --limit 10\nExample: baker images gif 'office reaction' --rating pg --auto-ingest 2\nExample: baker images gif --trending --limit 25"
@@ -38922,7 +39092,7 @@ var gifCommand = defineCommand137({
38922
39092
  });
38923
39093
 
38924
39094
  // src/commands/images/google.ts
38925
- import { defineCommand as defineCommand138 } from "citty";
39095
+ import { defineCommand as defineCommand139 } from "citty";
38926
39096
  var GOOGLE_ERROR_FIX = {
38927
39097
  action: "use_different_resource",
38928
39098
  explanation: "Generate the asset instead of retrying Google. Google is the last-resort image provider. Run `baker studio generate` to make the asset. Never sleep-and-retry this command \u2014 the CLI already backs off on rate limits. If no image can be sourced, continue the rest of the task with a placeholder rather than aborting it."
@@ -38962,7 +39132,7 @@ registerSchema({
38962
39132
  }
38963
39133
  }
38964
39134
  });
38965
- var googleCommand2 = defineCommand138({
39135
+ var googleCommand2 = defineCommand139({
38966
39136
  meta: {
38967
39137
  name: "google",
38968
39138
  description: "Google Images via the official Custom Search JSON API ($0.005/query, free 100/day). \u26A0 Source unverified \u2014 watermarks, low-res, mislabeled results are common. Use as last resort. With --auto-ingest, ingested hits return Baker-owned URLs.\n\nExample: baker images google 'industrial workshop' --type photo --size large --limit 20"
@@ -39035,7 +39205,7 @@ var googleCommand2 = defineCommand138({
39035
39205
  });
39036
39206
 
39037
39207
  // src/commands/images/group.ts
39038
- import { defineCommand as defineCommand139 } from "citty";
39208
+ import { defineCommand as defineCommand140 } from "citty";
39039
39209
 
39040
39210
  // src/commands/mediaGroup.ts
39041
39211
  async function runMediaGroupLookup(input) {
@@ -39088,7 +39258,7 @@ registerSchema({
39088
39258
  "group-key": { type: "string", description: "The set key directly, when you already have it", required: false }
39089
39259
  }
39090
39260
  });
39091
- var groupCommand = defineCommand139({
39261
+ var groupCommand = defineCommand140({
39092
39262
  meta: {
39093
39263
  name: "group",
39094
39264
  description: "List every asset that arrived in the same set \u2014 the slides of one Instagram carousel, the images off one scraped page. Start here whenever a hit looks like part of a sequence: carousel slides are authored to be read in order and usually only make sense together. Takes an image or a video id, since one carousel can contain both. Example: baker images group <imageId>"
@@ -39108,7 +39278,7 @@ var groupCommand = defineCommand139({
39108
39278
  });
39109
39279
 
39110
39280
  // src/commands/images/icon.ts
39111
- import { defineCommand as defineCommand140 } from "citty";
39281
+ import { defineCommand as defineCommand141 } from "citty";
39112
39282
 
39113
39283
  // src/commands/images/brandVerification.ts
39114
39284
  function brandToken(domain) {
@@ -39205,7 +39375,7 @@ registerSchema({
39205
39375
  full: { type: "boolean", description: "Include full metadata", required: false, default: false }
39206
39376
  }
39207
39377
  });
39208
- var iconCommand = defineCommand140({
39378
+ var iconCommand = defineCommand141({
39209
39379
  meta: {
39210
39380
  name: "icon",
39211
39381
  description: "Icon via Iconify (simple-icons, logos, lucide, devicon, heroicons, tabler, phosphor, material-symbols, \u2026). Free CDN, no API key.\n\nExample: baker images icon react --set devicon\nExample: baker images icon lucide:check --color '#0a0a0a'"
@@ -39275,7 +39445,7 @@ var iconCommand = defineCommand140({
39275
39445
  });
39276
39446
 
39277
39447
  // src/commands/images/ingest.ts
39278
- import { defineCommand as defineCommand141 } from "citty";
39448
+ import { defineCommand as defineCommand142 } from "citty";
39279
39449
  var SOURCE_VALUES = imageSourceSchema.options.join(" | ");
39280
39450
  registerSchema({
39281
39451
  command: "images.ingest",
@@ -39290,7 +39460,7 @@ registerSchema({
39290
39460
  fields: { type: "string", description: "Comma-separated field names to include", required: false }
39291
39461
  }
39292
39462
  });
39293
- var ingestCommand = defineCommand141({
39463
+ var ingestCommand = defineCommand142({
39294
39464
  meta: {
39295
39465
  name: "ingest",
39296
39466
  description: "Download a remote URL and store it in the library. Hash-deduped on bytes + externalId.\n\nExample: baker images ingest https://img.freepik.com/free-photo/xyz.jpg --source magnific --external-id 12345"
@@ -39359,7 +39529,7 @@ var ingestCommand = defineCommand141({
39359
39529
  });
39360
39530
 
39361
39531
  // src/commands/images/layerize.ts
39362
- import { defineCommand as defineCommand142 } from "citty";
39532
+ import { defineCommand as defineCommand143 } from "citty";
39363
39533
  registerSchema({
39364
39534
  command: "images.layerize",
39365
39535
  description: "Split a library image into editable layers: transparent PNG cutouts for each element, plus any headline recovered as EDITABLE TEXT with its font, size, colour and position. Waits for completion by default. Costs credits.",
@@ -39423,7 +39593,7 @@ async function pollUntilSettled(imageId, maxWait) {
39423
39593
  }
39424
39594
  return null;
39425
39595
  }
39426
- var layerizeCommand = defineCommand142({
39596
+ var layerizeCommand = defineCommand143({
39427
39597
  meta: {
39428
39598
  name: "layerize",
39429
39599
  description: "Split a library image into editable layers \u2014 transparent cutouts per element, plus any baked-in headline recovered as editable text with its typography.\n\nStart here: baker images layerize j571abc123def\nExample: baker images layerize j571abc123def --full\nExample: baker images layerize j571abc123def --instructions 'keep the product and its shadow together'"
@@ -39492,7 +39662,7 @@ registerSchema({
39492
39662
  full: { type: "boolean", description: "Include geometry and typography for every layer", required: false }
39493
39663
  }
39494
39664
  });
39495
- var layersCommand = defineCommand142({
39665
+ var layersCommand = defineCommand143({
39496
39666
  meta: {
39497
39667
  name: "layers",
39498
39668
  description: "Read the layers of an image that has already been split. Free \u2014 no provider call.\n\nStart here: baker images layers j571abc123def\nExample: baker images layers j571abc123def --full"
@@ -39532,7 +39702,7 @@ var layersCommand = defineCommand142({
39532
39702
  });
39533
39703
 
39534
39704
  // src/commands/images/library.ts
39535
- import { defineCommand as defineCommand143 } from "citty";
39705
+ import { defineCommand as defineCommand144 } from "citty";
39536
39706
  registerSchema({
39537
39707
  command: "images.library",
39538
39708
  description: "Search the company image library. Returns only ready images.",
@@ -39558,7 +39728,7 @@ registerSchema({
39558
39728
  }
39559
39729
  }
39560
39730
  });
39561
- var libraryCommand = defineCommand143({
39731
+ var libraryCommand = defineCommand144({
39562
39732
  meta: {
39563
39733
  name: "library",
39564
39734
  description: "Search the company image library (hybrid BM25 + vector + Cohere rerank). Use this BEFORE any external provider.\n\nExample: baker images library 'hero banner' --aspect-ratio 16:9 --source magnific"
@@ -39621,7 +39791,7 @@ var libraryCommand = defineCommand143({
39621
39791
  });
39622
39792
 
39623
39793
  // src/commands/images/logo.ts
39624
- import { defineCommand as defineCommand144 } from "citty";
39794
+ import { defineCommand as defineCommand145 } from "citty";
39625
39795
  registerSchema({
39626
39796
  command: "images.logo",
39627
39797
  description: "Brand logo lookup via Brandfetch. Auto-ingests by default. Returns `brandMatch` \u2014 Brandfetch's own verdict on whose brand the domain is (confirmed | mismatch | unverified). Branch on it: `mismatch` means the mark is another company's, so do not place it. `confirmed` verifies the record, not the artwork \u2014 read the ingested row back to check the mark itself.",
@@ -39649,7 +39819,7 @@ registerSchema({
39649
39819
  full: { type: "boolean", description: "Include full metadata", required: false, default: false }
39650
39820
  }
39651
39821
  });
39652
- var logoCommand = defineCommand144({
39822
+ var logoCommand = defineCommand145({
39653
39823
  meta: {
39654
39824
  name: "logo",
39655
39825
  description: "Brand logo via Brandfetch. Returns up to 5 variants (icon, light/dark logo, light/dark symbol) plus `brandMatch`. Auto-ingests the first variant.\n\nStart here: check `brandMatch.verdict`.\n mismatch \u2192 the mark belongs to `brandMatch.name`, a different company. Do not place it.\n unverified \u2192 nothing confirmed whose logo this is. Treat as unchecked.\n confirmed \u2192 Brandfetch has this brand's record. That verifies the record, NOT the artwork \u2014 a confirmed domain has served another company's logo before.\n\n\u26A0 Whatever the verdict, read the ingested row back with `baker images get <imageId>` and check `textInImage`/`subject` before placing it. That is the only check that looks at the mark.\n\nExample: baker images logo stripe.com --variant logo"
@@ -39722,7 +39892,7 @@ var logoCommand = defineCommand144({
39722
39892
  });
39723
39893
 
39724
39894
  // src/commands/images/normalize.ts
39725
- import { defineCommand as defineCommand145 } from "citty";
39895
+ import { defineCommand as defineCommand146 } from "citty";
39726
39896
 
39727
39897
  // src/lib/image/color-changer.ts
39728
39898
  import quantize from "quantize";
@@ -40454,7 +40624,7 @@ function coerceRawArgs(args) {
40454
40624
  "dry-run": bool(args["dry-run"])
40455
40625
  };
40456
40626
  }
40457
- var normalizeCommand2 = defineCommand145({
40627
+ var normalizeCommand2 = defineCommand146({
40458
40628
  meta: {
40459
40629
  name: "normalize",
40460
40630
  description: `Normalize logos / images: declarative recolor + bg removal + trim + resize. Operates on local files; writes in-place by default.
@@ -40509,7 +40679,7 @@ Examples:
40509
40679
  });
40510
40680
 
40511
40681
  // src/commands/images/pinterest.ts
40512
- import { defineCommand as defineCommand146 } from "citty";
40682
+ import { defineCommand as defineCommand147 } from "citty";
40513
40683
  registerSchema({
40514
40684
  command: "images.pinterest",
40515
40685
  description: "Pinterest image search via ScrapeCreators. Reference-grade real-world photography, product styling, interiors, fashion, food, and aesthetic mood boards. Inspect before placing \u2014 Pinterest is unverified, trademark-bearing web content.",
@@ -40529,7 +40699,7 @@ registerSchema({
40529
40699
  }
40530
40700
  }
40531
40701
  });
40532
- var pinterestCommand = defineCommand146({
40702
+ var pinterestCommand = defineCommand147({
40533
40703
  meta: {
40534
40704
  name: "pinterest",
40535
40705
  description: "Pinterest image search via ScrapeCreators ($0.00188/request). Best for photo-realistic reference imagery \u2014 lifestyle, interiors, fashion, food, product styling, and mood boards to brief AI generation against. \u26A0 Unverified, trademark-bearing web content \u2014 inspect and respect rights before placing on a customer page. Browse first; auto-ingest only the pins you commit to.\n\nExamples:\n baker images pinterest 'scandinavian living room'\n baker images pinterest 'minimalist skincare product photography' --limit 20\n baker images pinterest 'cozy coffee shop interior' --auto-ingest 2 --context 'Mood reference for hero photography'"
@@ -40586,7 +40756,7 @@ var pinterestCommand = defineCommand146({
40586
40756
  });
40587
40757
 
40588
40758
  // src/commands/images/screenshot.ts
40589
- import { defineCommand as defineCommand147 } from "citty";
40759
+ import { defineCommand as defineCommand148 } from "citty";
40590
40760
  registerSchema({
40591
40761
  command: "images.screenshot",
40592
40762
  description: "Capture a website screenshot via ScreenshotOne. Auto-ingests on success.",
@@ -40605,7 +40775,7 @@ registerSchema({
40605
40775
  full: { type: "boolean", description: "Include full metadata", required: false, default: false }
40606
40776
  }
40607
40777
  });
40608
- var screenshotCommand = defineCommand147({
40778
+ var screenshotCommand = defineCommand148({
40609
40779
  meta: {
40610
40780
  name: "screenshot",
40611
40781
  description: "Screenshot a URL via ScreenshotOne. $0.009/capture. Auto-ingests to library.\n\nExample: baker images screenshot https://stripe.com --full-page"
@@ -40669,7 +40839,7 @@ var screenshotCommand = defineCommand147({
40669
40839
  });
40670
40840
 
40671
40841
  // src/commands/images/search.ts
40672
- import { defineCommand as defineCommand148 } from "citty";
40842
+ import { defineCommand as defineCommand149 } from "citty";
40673
40843
  registerSchema({
40674
40844
  command: "images.search",
40675
40845
  description: "Search images by text query. Only returns ready images.",
@@ -40685,7 +40855,7 @@ registerSchema({
40685
40855
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
40686
40856
  }
40687
40857
  });
40688
- var searchCommand = defineCommand148({
40858
+ var searchCommand = defineCommand149({
40689
40859
  meta: {
40690
40860
  name: "search",
40691
40861
  description: "Semantic search images by text query. Uses hybrid BM25 + vector + reranking. Example: baker images search 'hero banner' --aspect-ratio 16:9 --tags logo"
@@ -40745,7 +40915,7 @@ var searchCommand = defineCommand148({
40745
40915
  });
40746
40916
 
40747
40917
  // src/commands/images/sticker.ts
40748
- import { defineCommand as defineCommand149 } from "citty";
40918
+ import { defineCommand as defineCommand150 } from "citty";
40749
40919
  registerSchema({
40750
40920
  command: "images.sticker",
40751
40921
  description: "Search Giphy stickers \u2014 transparent-background overlays for ad creative.",
@@ -40777,7 +40947,7 @@ registerSchema({
40777
40947
  }
40778
40948
  }
40779
40949
  });
40780
- var stickerCommand = defineCommand149({
40950
+ var stickerCommand = defineCommand150({
40781
40951
  meta: {
40782
40952
  name: "sticker",
40783
40953
  description: "Search Giphy's sticker corpus \u2014 transparent-background WebPs / GIFs ideal for overlaying on ad creative (Meta, TikTok, Stories). Same Giphy free API as `baker images gif`; results carry WebP + GIF + MP4 URLs in providerMeta.\n\nExample: baker images sticker 'thumbs up' --limit 10\nExample: baker images sticker celebration --rating g --auto-ingest 3\nExample: baker images sticker --trending --limit 25"
@@ -40824,7 +40994,7 @@ var stickerCommand = defineCommand149({
40824
40994
  });
40825
40995
 
40826
40996
  // src/commands/images/stock.ts
40827
- import { defineCommand as defineCommand150 } from "citty";
40997
+ import { defineCommand as defineCommand151 } from "citty";
40828
40998
  var STOCK_ERROR_FIX = {
40829
40999
  action: "use_different_resource",
40830
41000
  explanation: "Switch provider instead of retrying stock search. Stock search is one of several image sources. Run `baker images find <query> --sources library,pinterest,google` (`--sources` is required \u2014 `find` alone searches the library only) or `baker studio generate` to make the asset. Never sleep-and-retry this command \u2014 the CLI already backs off on rate limits. If no image can be sourced, continue the rest of the task with a placeholder rather than aborting it."
@@ -40901,7 +41071,7 @@ function buildStockRequest(query, args) {
40901
41071
  if (args.context) body.descriptionContext = args.context;
40902
41072
  return body;
40903
41073
  }
40904
- var stockCommand = defineCommand150({
41074
+ var stockCommand = defineCommand151({
40905
41075
  meta: {
40906
41076
  name: "stock",
40907
41077
  description: "Stock search via Magnific \u2014 Freepik's developer API (~250M assets: photos, vectors, illustrations, icons, PSDs). $0.002/req. With --auto-ingest, ingested hits return Baker-owned URLs.\n\nExamples:\n baker images stock 'minimalist office'\n baker images stock 'flat office workers' --type vector\n baker images stock 'hero photo of a kitchen' --type photo --orientation landscape --ai exclude\n baker images stock 'brand pattern' --color '#0a0a0a' --license freemium --auto-ingest 2"
@@ -40974,7 +41144,7 @@ var stockCommand = defineCommand150({
40974
41144
  });
40975
41145
 
40976
41146
  // src/lib/tags-command.ts
40977
- import { defineCommand as defineCommand151 } from "citty";
41147
+ import { defineCommand as defineCommand152 } from "citty";
40978
41148
  function makeTagsCommand(command, label, endpoint) {
40979
41149
  registerSchema({
40980
41150
  command: `${command}.tags`,
@@ -40983,7 +41153,7 @@ function makeTagsCommand(command, label, endpoint) {
40983
41153
  output: { type: "string", description: "Output format: md|json", required: false, default: "md" }
40984
41154
  }
40985
41155
  });
40986
- return defineCommand151({
41156
+ return defineCommand152({
40987
41157
  meta: {
40988
41158
  name: "tags",
40989
41159
  description: `List the available ${label} tag names (defaults + company custom tags). Use before filtering with --tags. Example: baker ${command} tags`
@@ -41019,7 +41189,7 @@ function makeTagsCommand(command, label, endpoint) {
41019
41189
  var tagsCommand3 = makeTagsCommand("images", "image", "/api/images/tags");
41020
41190
 
41021
41191
  // src/commands/images/upload.ts
41022
- import { defineCommand as defineCommand152 } from "citty";
41192
+ import { defineCommand as defineCommand153 } from "citty";
41023
41193
  registerSchema({
41024
41194
  command: "images.upload",
41025
41195
  description: "Upload an image to the library \u2014 local file path or remote http(s) URL.",
@@ -41057,7 +41227,7 @@ registerSchema({
41057
41227
  function isRemoteUrl2(value) {
41058
41228
  return /^https?:\/\//i.test(value);
41059
41229
  }
41060
- var uploadCommand = defineCommand152({
41230
+ var uploadCommand = defineCommand153({
41061
41231
  meta: {
41062
41232
  name: "upload",
41063
41233
  description: "Upload an image to the library \u2014 accepts a local file path OR a remote http(s) URL.\n\nLocal: reads bytes, sends to /api/images/upload, content-type auto-detected from extension.\nRemote: dispatches to /api/images/ingest with hash-dedup on bytes + externalId.\n\nExamples:\n baker images upload ./logo.png --source uploaded\n baker images upload ./cert.png --context 'ISO 27001 badge \u2014 enterprise tier'\n baker images upload https://acme.com/hero.png --source firecrawl --context 'Acme competitor pricing hero'"
@@ -41150,7 +41320,7 @@ async function uploadLocal(target, args) {
41150
41320
  }
41151
41321
 
41152
41322
  // src/commands/images/upscale.ts
41153
- import { defineCommand as defineCommand153 } from "citty";
41323
+ import { defineCommand as defineCommand154 } from "citty";
41154
41324
  registerSchema({
41155
41325
  command: "images.upscale",
41156
41326
  description: "Upscale a library image via the backend (Replicate, cost-tracked). Waits for completion by default. The image must be status 'ready' and raster (not SVG/AVIF).",
@@ -41165,7 +41335,7 @@ registerSchema({
41165
41335
  }
41166
41336
  });
41167
41337
  var POLL_INTERVAL_MS4 = 1500;
41168
- var upscaleCommand = defineCommand153({
41338
+ var upscaleCommand = defineCommand154({
41169
41339
  meta: {
41170
41340
  name: "upscale",
41171
41341
  description: "Upscale a library image via the Convex backend (Replicate, cost-tracked at $0.05/image). Waits for completion by default.\n\nExample: baker images upscale j571abc123def\nExample: baker images upscale j571abc123def --max-wait 0 # fire-and-forget"
@@ -41220,7 +41390,7 @@ var upscaleCommand = defineCommand153({
41220
41390
  });
41221
41391
 
41222
41392
  // src/commands/images/use.ts
41223
- import { defineCommand as defineCommand154 } from "citty";
41393
+ import { defineCommand as defineCommand155 } from "citty";
41224
41394
  registerSchema({
41225
41395
  command: "images.use",
41226
41396
  description: "Ingest a URL and wait for the library record to be ready.",
@@ -41249,7 +41419,7 @@ function emitReady(ingestResult, doc, args) {
41249
41419
  args.full === true
41250
41420
  );
41251
41421
  }
41252
- var useCommand = defineCommand154({
41422
+ var useCommand = defineCommand155({
41253
41423
  meta: {
41254
41424
  name: "use",
41255
41425
  description: "Sugar over `ingest`: download \u2192 store \u2192 wait until describe + embed complete \u2192 return ready library record.\n\nExample: baker images use https://cdn.example.com/hero.png --source uploaded"
@@ -41298,7 +41468,7 @@ var useCommand = defineCommand154({
41298
41468
  });
41299
41469
 
41300
41470
  // src/commands/images/index.ts
41301
- var imagesCommand = defineCommand155({
41471
+ var imagesCommand = defineCommand156({
41302
41472
  meta: {
41303
41473
  name: "images",
41304
41474
  description: `Find, source, and normalize images. Subcommands route by provider so cost + license are explicit.
@@ -41376,12 +41546,12 @@ Full guide: __tooling__/docs/tools/baker/images.md`
41376
41546
  });
41377
41547
 
41378
41548
  // src/commands/landing/index.ts
41379
- import { defineCommand as defineCommand166 } from "citty";
41549
+ import { defineCommand as defineCommand167 } from "citty";
41380
41550
 
41381
41551
  // src/commands/landing/critique.ts
41382
41552
  import { readdir as readdir9, stat as stat6 } from "fs/promises";
41383
- import path28 from "path";
41384
- import { defineCommand as defineCommand156 } from "citty";
41553
+ import path29 from "path";
41554
+ import { defineCommand as defineCommand157 } from "citty";
41385
41555
 
41386
41556
  // src/engine/landing/lib/constants.ts
41387
41557
  var OVERUSED_FONTS = /* @__PURE__ */ new Set([
@@ -42347,40 +42517,40 @@ function describeCounts(findings) {
42347
42517
  }
42348
42518
 
42349
42519
  // src/commands/landing/snapshot.ts
42350
- import { mkdir as mkdir8, rename as rename2, writeFile as writeFile12 } from "fs/promises";
42351
- import path26 from "path";
42520
+ import { mkdir as mkdir9, rename as rename2, writeFile as writeFile13 } from "fs/promises";
42521
+ import path27 from "path";
42352
42522
  var CRITIC_VERSION = "2";
42353
42523
  function critiqueCacheDir(projectRoot) {
42354
- return path26.join(projectRoot, ".cache", "landing-critique");
42524
+ return path27.join(projectRoot, ".cache", "landing-critique");
42355
42525
  }
42356
42526
  function snapshotPath(projectRoot, slug) {
42357
- return path26.join(critiqueCacheDir(projectRoot), `${slug}.json`);
42527
+ return path27.join(critiqueCacheDir(projectRoot), `${slug}.json`);
42358
42528
  }
42359
42529
  async function writeCritiqueSnapshot(projectRoot, snapshot) {
42360
- await mkdir8(critiqueCacheDir(projectRoot), { recursive: true });
42530
+ await mkdir9(critiqueCacheDir(projectRoot), { recursive: true });
42361
42531
  const dest = snapshotPath(projectRoot, snapshot.slug);
42362
42532
  const tmp = `${dest}.tmp`;
42363
- await writeFile12(tmp, `${JSON.stringify(snapshot, null, 2)}
42533
+ await writeFile13(tmp, `${JSON.stringify(snapshot, null, 2)}
42364
42534
  `, "utf8");
42365
42535
  await rename2(tmp, dest);
42366
42536
  }
42367
42537
 
42368
42538
  // src/commands/landing/source-version.ts
42369
- import { readdir as readdir8, readFile as readFile21, stat as stat5 } from "fs/promises";
42370
- import path27 from "path";
42539
+ import { readdir as readdir8, readFile as readFile22, stat as stat5 } from "fs/promises";
42540
+ import path28 from "path";
42371
42541
  async function landingSourceRelPaths(landingDir) {
42372
42542
  const rel = [];
42373
- if (await isFile(path27.join(landingDir, "index.astro"))) rel.push("index.astro");
42374
- const componentsDir = path27.join(landingDir, "_components");
42543
+ if (await isFile(path28.join(landingDir, "index.astro"))) rel.push("index.astro");
42544
+ const componentsDir = path28.join(landingDir, "_components");
42375
42545
  for (const abs of await walkAstro(componentsDir)) {
42376
- rel.push(path27.relative(landingDir, abs).split(path27.sep).join("/"));
42546
+ rel.push(path28.relative(landingDir, abs).split(path28.sep).join("/"));
42377
42547
  }
42378
42548
  return rel.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
42379
42549
  }
42380
42550
  async function readLandingSources(landingDir) {
42381
42551
  const rel = await landingSourceRelPaths(landingDir);
42382
42552
  const out = [];
42383
- for (const r of rel) out.push({ path: r, text: await readFile21(path27.join(landingDir, r), "utf8") });
42553
+ for (const r of rel) out.push({ path: r, text: await readFile22(path28.join(landingDir, r), "utf8") });
42384
42554
  return out;
42385
42555
  }
42386
42556
  async function computeLandingSourceSha(landingDir) {
@@ -42389,7 +42559,7 @@ async function computeLandingSourceSha(landingDir) {
42389
42559
  for (const r of rel) {
42390
42560
  let bytes;
42391
42561
  try {
42392
- bytes = await readFile21(path27.join(landingDir, r));
42562
+ bytes = await readFile22(path28.join(landingDir, r));
42393
42563
  } catch {
42394
42564
  bytes = Buffer.alloc(0);
42395
42565
  }
@@ -42413,7 +42583,7 @@ async function walkAstro(dir) {
42413
42583
  }
42414
42584
  const out = [];
42415
42585
  for (const entry of entries) {
42416
- const abs = path27.join(dir, entry.name);
42586
+ const abs = path28.join(dir, entry.name);
42417
42587
  if (entry.isDirectory()) out.push(...await walkAstro(abs));
42418
42588
  else if (entry.isFile() && entry.name.endsWith(".astro")) out.push(abs);
42419
42589
  }
@@ -42450,7 +42620,7 @@ function fail5(code, message, fix) {
42450
42620
  );
42451
42621
  process.exit(2);
42452
42622
  }
42453
- var critiqueCommand2 = defineCommand156({
42623
+ var critiqueCommand2 = defineCommand157({
42454
42624
  meta: {
42455
42625
  name: "critique",
42456
42626
  description: "Start here: `baker landing critique <slug>` after building or editing a landing. Deterministic design-quality critic (ADVISORY \u2014 findings never fail it). 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 publishing requires \u2014 run it before finishing a landing."
@@ -42474,7 +42644,7 @@ var critiqueCommand2 = defineCommand156({
42474
42644
  { availableSlugs: await listLandingSlugs(projectRoot) }
42475
42645
  );
42476
42646
  }
42477
- if (!await isDir(path28.resolve(projectRoot, "src", "pages", slug))) {
42647
+ if (!await isDir(path29.resolve(projectRoot, "src", "pages", slug))) {
42478
42648
  fail5("NOT_FOUND", `No landing at src/pages/${slug}/`, {
42479
42649
  availableSlugs: await listLandingSlugs(projectRoot)
42480
42650
  });
@@ -42513,7 +42683,7 @@ var critiqueCommand2 = defineCommand156({
42513
42683
  }
42514
42684
  });
42515
42685
  async function critiqueOne(projectRoot, slug, brand) {
42516
- const landingDir = path28.resolve(projectRoot, "src", "pages", slug);
42686
+ const landingDir = path29.resolve(projectRoot, "src", "pages", slug);
42517
42687
  const [sources, sourceSha] = await Promise.all([readLandingSources(landingDir), computeLandingSourceSha(landingDir)]);
42518
42688
  const report = critiqueLanding({ slug, sources, brand });
42519
42689
  let snapshotFailed = false;
@@ -42533,7 +42703,7 @@ async function critiqueOne(projectRoot, slug, brand) {
42533
42703
  }
42534
42704
  async function listLandingSlugs(projectRoot) {
42535
42705
  try {
42536
- const entries = await readdir9(path28.join(projectRoot, "src", "pages"), { withFileTypes: true });
42706
+ const entries = await readdir9(path29.join(projectRoot, "src", "pages"), { withFileTypes: true });
42537
42707
  return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).map((e) => e.name).sort();
42538
42708
  } catch {
42539
42709
  return [];
@@ -42559,10 +42729,10 @@ async function isDir(p) {
42559
42729
  }
42560
42730
 
42561
42731
  // src/commands/landing/inspiration/index.ts
42562
- import { defineCommand as defineCommand165 } from "citty";
42732
+ import { defineCommand as defineCommand166 } from "citty";
42563
42733
 
42564
42734
  // src/commands/landing/inspiration/add.ts
42565
- import { defineCommand as defineCommand157 } from "citty";
42735
+ import { defineCommand as defineCommand158 } from "citty";
42566
42736
 
42567
42737
  // src/commands/landing/inspiration/shared.ts
42568
42738
  var INSPIRATION_HINTS = {
@@ -42644,7 +42814,7 @@ registerSchema({
42644
42814
  note: { type: "string", description: "Why this page is worth keeping", required: false }
42645
42815
  }
42646
42816
  });
42647
- var addCommand = defineCommand157({
42817
+ var addCommand = defineCommand158({
42648
42818
  meta: {
42649
42819
  name: "add",
42650
42820
  description: "Add someone else's landing page to the reference library. Example: baker landing inspiration add https://linear.app --note 'the client likes this density'"
@@ -42684,9 +42854,9 @@ var addCommand = defineCommand157({
42684
42854
  });
42685
42855
 
42686
42856
  // src/commands/landing/inspiration/code.ts
42687
- import { mkdir as mkdir9, writeFile as writeFile13 } from "fs/promises";
42688
- import path29 from "path";
42689
- import { defineCommand as defineCommand158 } from "citty";
42857
+ import { mkdir as mkdir10, writeFile as writeFile14 } from "fs/promises";
42858
+ import path30 from "path";
42859
+ import { defineCommand as defineCommand159 } from "citty";
42690
42860
  registerSchema({
42691
42861
  command: "landing.inspiration.code",
42692
42862
  description: "Write a reference section's standalone HTML+CSS to .baker/inspiration/<id>/ so you can read how it is built. Reference only \u2014 the structure is the lesson, the words are not yours to reuse.",
@@ -42695,7 +42865,7 @@ registerSchema({
42695
42865
  full: { type: "boolean", description: "Print the markup inline as well as writing it", required: false }
42696
42866
  }
42697
42867
  });
42698
- var codeCommand = defineCommand158({
42868
+ var codeCommand = defineCommand159({
42699
42869
  meta: {
42700
42870
  name: "code",
42701
42871
  description: "Write one reference section's standalone markup to disk. Example: baker landing inspiration code k57abc\u2026 \u2014 read it for structure, then build your own."
@@ -42708,10 +42878,10 @@ var codeCommand = defineCommand158({
42708
42878
  try {
42709
42879
  const id = args.id;
42710
42880
  const data = await apiGet("/api/landing-inspiration/section-code", { id });
42711
- const dir = path29.join(process.cwd(), ".baker", "inspiration", id);
42712
- await mkdir9(dir, { recursive: true });
42713
- const file = path29.join(dir, "section.html");
42714
- await writeFile13(file, data.html);
42881
+ const dir = path30.join(process.cwd(), ".baker", "inspiration", id);
42882
+ await mkdir10(dir, { recursive: true });
42883
+ const file = path30.join(dir, "section.html");
42884
+ await writeFile14(file, data.html);
42715
42885
  const hints = [INSPIRATION_HINTS.structureNotCopy, INSPIRATION_HINTS.adapt];
42716
42886
  const fidelity = fidelityHint(data.fidelity);
42717
42887
  if (fidelity) hints.push(fidelity);
@@ -42720,7 +42890,7 @@ var codeCommand = defineCommand158({
42720
42890
  ok: true,
42721
42891
  data: {
42722
42892
  id,
42723
- file: path29.relative(process.cwd(), file),
42893
+ file: path30.relative(process.cwd(), file),
42724
42894
  bytes: data.html.length,
42725
42895
  fidelity: data.fidelity,
42726
42896
  reproduction_notes: data.reproductionNotes,
@@ -42741,7 +42911,7 @@ var codeCommand = defineCommand158({
42741
42911
  });
42742
42912
 
42743
42913
  // src/commands/landing/inspiration/favorites.ts
42744
- import { defineCommand as defineCommand159 } from "citty";
42914
+ import { defineCommand as defineCommand160 } from "citty";
42745
42915
  registerSchema({
42746
42916
  command: "landing.inspiration.favorites",
42747
42917
  description: "List the reference sections this company has saved. This is what `search` looks at by default, so it is the client's own taste profile \u2014 read it before proposing a direction.",
@@ -42755,7 +42925,7 @@ registerSchema({
42755
42925
  }
42756
42926
  }
42757
42927
  });
42758
- var favoritesCommand = defineCommand159({
42928
+ var favoritesCommand = defineCommand160({
42759
42929
  meta: {
42760
42930
  name: "favorites",
42761
42931
  description: "List this company's saved reference sections. Example: baker landing inspiration favorites --type hero,pricing"
@@ -42835,7 +43005,7 @@ registerSchema({
42835
43005
  note: { type: "string", description: "Why this is worth keeping", required: false }
42836
43006
  }
42837
43007
  });
42838
- var favoriteCommand = defineCommand159({
43008
+ var favoriteCommand = defineCommand160({
42839
43009
  meta: {
42840
43010
  name: "favorite",
42841
43011
  description: "Save a reference section to this company. Example: baker landing inspiration favorite k57abc\u2026"
@@ -42873,7 +43043,7 @@ registerSchema({
42873
43043
  page: { type: "boolean", description: "Treat the id as a page rather than a section", required: false }
42874
43044
  }
42875
43045
  });
42876
- var unfavoriteCommand = defineCommand159({
43046
+ var unfavoriteCommand = defineCommand160({
42877
43047
  meta: {
42878
43048
  name: "unfavorite",
42879
43049
  description: "Remove a reference section from this company's saved set. Example: baker landing inspiration unfavorite k57abc\u2026"
@@ -42895,7 +43065,7 @@ var unfavoriteCommand = defineCommand159({
42895
43065
  });
42896
43066
 
42897
43067
  // src/commands/landing/inspiration/page.ts
42898
- import { defineCommand as defineCommand160 } from "citty";
43068
+ import { defineCommand as defineCommand161 } from "citty";
42899
43069
  registerSchema({
42900
43070
  command: "landing.inspiration.page",
42901
43071
  description: "Show a whole reference page as a sequence: every section top to bottom with its type and the idea behind it. This is the view to use when the question is how a good page is ORDERED rather than what one section looks like.",
@@ -42912,7 +43082,7 @@ registerSchema({
42912
43082
  }
42913
43083
  }
42914
43084
  });
42915
- var pageCommand2 = defineCommand160({
43085
+ var pageCommand2 = defineCommand161({
42916
43086
  meta: {
42917
43087
  name: "page",
42918
43088
  description: "Show how a reference page sequences its sections. Example: baker landing inspiration page j91xyz\u2026 \u2014 the blueprint, not the pixels."
@@ -42975,7 +43145,7 @@ var pageCommand2 = defineCommand160({
42975
43145
  });
42976
43146
 
42977
43147
  // src/commands/landing/inspiration/scrape.ts
42978
- import { defineCommand as defineCommand161 } from "citty";
43148
+ import { defineCommand as defineCommand162 } from "citty";
42979
43149
 
42980
43150
  // src/engine/landing-library/proxyFailure.ts
42981
43151
  var PROXY_STATUS = 407;
@@ -43151,8 +43321,8 @@ function classifyCaptureFailure(error) {
43151
43321
  }
43152
43322
 
43153
43323
  // src/engine/landing-library/run.ts
43154
- import { mkdir as mkdir10, writeFile as writeFile15 } from "fs/promises";
43155
- import path31 from "path";
43324
+ import { mkdir as mkdir11, writeFile as writeFile16 } from "fs/promises";
43325
+ import path32 from "path";
43156
43326
 
43157
43327
  // ../proxy/src/preflight.ts
43158
43328
  import http from "http";
@@ -44567,11 +44737,11 @@ async function renderBundleToPng(browser, html, viewportWidth, options = {}) {
44567
44737
  }
44568
44738
 
44569
44739
  // src/engine/landing-library/report.ts
44570
- import { writeFile as writeFile14 } from "fs/promises";
44571
- import path30 from "path";
44740
+ import { writeFile as writeFile15 } from "fs/promises";
44741
+ import path31 from "path";
44572
44742
  async function writeCaptureReport(manifest, outDir) {
44573
- const file = path30.join(outDir, "report.html");
44574
- await writeFile14(file, renderReport(manifest));
44743
+ const file = path31.join(outDir, "report.html");
44744
+ await writeFile15(file, renderReport(manifest));
44575
44745
  return file;
44576
44746
  }
44577
44747
  function escapeHtml3(value) {
@@ -44749,32 +44919,32 @@ async function reproducePage(args) {
44749
44919
  const { browser, page, outDir, pageUrl, livePageShot } = args;
44750
44920
  const built = await buildSectionBundle(page, "body", pageUrl).catch(() => null);
44751
44921
  if (!built) return { bundle: null, fidelity: null };
44752
- await writeFile15(path31.join(outDir, "page.html"), built.html);
44922
+ await writeFile16(path32.join(outDir, "page.html"), built.html);
44753
44923
  const rendered = await renderBundleToPng(browser, built.html, DESKTOP_VIEWPORT.width, {
44754
44924
  wholePage: true,
44755
44925
  timeoutMs: 6e4
44756
44926
  });
44757
44927
  if (!rendered || !livePageShot) return { bundle: "page.html", fidelity: null };
44758
- await writeFile15(path31.join(outDir, "page-rendered.png"), rendered);
44928
+ await writeFile16(path32.join(outDir, "page-rendered.png"), rendered);
44759
44929
  const { score, note } = await scoreFidelity(livePageShot, rendered);
44760
44930
  return { bundle: "page.html", fidelity: score, ...note ? { fidelityNote: note } : {} };
44761
44931
  }
44762
44932
  async function captureOneSection(args) {
44763
44933
  const { browser, page, candidate, sectionsDir, outDir, pageUrl, withCode } = args;
44764
- const dir = path31.join(sectionsDir, String(candidate.index).padStart(2, "0"));
44765
- await mkdir10(dir, { recursive: true });
44934
+ const dir = path32.join(sectionsDir, String(candidate.index).padStart(2, "0"));
44935
+ await mkdir11(dir, { recursive: true });
44766
44936
  const desktop = await captureSection(page, candidate);
44767
- if (desktop) await writeFile15(path31.join(dir, "desktop.png"), desktop);
44937
+ if (desktop) await writeFile16(path32.join(dir, "desktop.png"), desktop);
44768
44938
  const visualHash = desktop ? await perceptualHash(desktop) : null;
44769
44939
  const motion = await collectMotion(page, candidate.selector);
44770
44940
  const built = withCode ? await buildSectionBundle(page, candidate.selector, pageUrl) : null;
44771
44941
  let fidelity = null;
44772
44942
  let fidelityNote;
44773
44943
  if (built) {
44774
- await writeFile15(path31.join(dir, "section.html"), built.html);
44944
+ await writeFile16(path32.join(dir, "section.html"), built.html);
44775
44945
  const rendered = await renderBundleToPng(browser, built.html, DESKTOP_VIEWPORT.width);
44776
44946
  if (rendered && desktop) {
44777
- await writeFile15(path31.join(dir, "section-rendered.png"), rendered);
44947
+ await writeFile16(path32.join(dir, "section-rendered.png"), rendered);
44778
44948
  const result = await scoreFidelity(desktop, rendered);
44779
44949
  fidelity = result.score;
44780
44950
  fidelityNote = result.note;
@@ -44782,9 +44952,9 @@ async function captureOneSection(args) {
44782
44952
  }
44783
44953
  return {
44784
44954
  ...candidate,
44785
- desktopShot: desktop ? path31.relative(outDir, path31.join(dir, "desktop.png")) : null,
44955
+ desktopShot: desktop ? path32.relative(outDir, path32.join(dir, "desktop.png")) : null,
44786
44956
  mobileShot: null,
44787
- bundle: built ? path31.relative(outDir, path31.join(dir, "section.html")) : null,
44957
+ bundle: built ? path32.relative(outDir, path32.join(dir, "section.html")) : null,
44788
44958
  fidelity,
44789
44959
  ...fidelityNote ? { fidelityNote } : {},
44790
44960
  ...built ? { cssStats: built.stats } : {},
@@ -44803,9 +44973,9 @@ async function captureMobileShots(args) {
44803
44973
  for (const section of sections) {
44804
44974
  const shot = await captureSectionOnMobile(mobile.page, section);
44805
44975
  if (!shot) continue;
44806
- const file = path31.join(sectionsDir, String(section.index).padStart(2, "0"), "mobile.png");
44807
- await writeFile15(file, shot);
44808
- section.mobileShot = path31.relative(outDir, file);
44976
+ const file = path32.join(sectionsDir, String(section.index).padStart(2, "0"), "mobile.png");
44977
+ await writeFile16(file, shot);
44978
+ section.mobileShot = path32.relative(outDir, file);
44809
44979
  }
44810
44980
  } finally {
44811
44981
  await mobile.context.close();
@@ -44820,10 +44990,10 @@ async function captureMotionTakes(args) {
44820
44990
  const filmOne = async (section) => {
44821
44991
  const take = await captureMotionTake(browser, pageUrl, section.selector).catch(() => null);
44822
44992
  if (!take) return;
44823
- const dir = path31.join(sectionsDir, String(section.index).padStart(2, "0"));
44824
- const file = path31.join(dir, "motion-filmstrip.png");
44825
- await writeFile15(file, take.filmstrip);
44826
- section.motionFilmstrip = path31.relative(outDir, file);
44993
+ const dir = path32.join(sectionsDir, String(section.index).padStart(2, "0"));
44994
+ const file = path32.join(dir, "motion-filmstrip.png");
44995
+ await writeFile16(file, take.filmstrip);
44996
+ section.motionFilmstrip = path32.relative(outDir, file);
44827
44997
  log(` [${section.index}] ${section.motion.summary}`);
44828
44998
  };
44829
44999
  const queue = [...moving];
@@ -44880,7 +45050,7 @@ async function captureAlternateViews(args) {
44880
45050
  async function reproduceWholePage(args) {
44881
45051
  const { browser, page, outDir, pageUrl, withCode, log } = args;
44882
45052
  const fullPage = await page.screenshot({ type: "png", fullPage: true }).catch(() => null);
44883
- if (fullPage) await writeFile15(path31.join(outDir, "full-page.png"), fullPage);
45053
+ if (fullPage) await writeFile16(path32.join(outDir, "full-page.png"), fullPage);
44884
45054
  if (!withCode) return { bundle: null, fidelity: null };
44885
45055
  const reproduction = await reproducePage({ browser, page, outDir, pageUrl, livePageShot: fullPage });
44886
45056
  log(`page reproduction: ${reproduction.fidelity === null ? "unavailable" : reproduction.fidelity.toFixed(2)}`);
@@ -44951,7 +45121,7 @@ async function openViaLadder(args) {
44951
45121
  async function scrapeLanding(options) {
44952
45122
  const timeoutMs = options.timeoutMs ?? 45e3;
44953
45123
  const log = options.onProgress ?? (() => void 0);
44954
- const sectionsDir = path31.join(options.outDir, "sections");
45124
+ const sectionsDir = path32.join(options.outDir, "sections");
44955
45125
  const nonPublic = refuseNonPublicUrl(options.url);
44956
45126
  if (nonPublic) {
44957
45127
  throw new BlockedPageError({
@@ -44971,7 +45141,7 @@ async function scrapeLanding(options) {
44971
45141
  const withMotion = options.motion !== false && !escalated;
44972
45142
  const renderBrowser = options.code === false ? null : await launchBrowser();
44973
45143
  try {
44974
- await mkdir10(sectionsDir, { recursive: true });
45144
+ await mkdir11(sectionsDir, { recursive: true });
44975
45145
  const sections = await captureSections({
44976
45146
  browser: renderBrowser ?? browser,
44977
45147
  page,
@@ -45013,7 +45183,7 @@ async function scrapeLanding(options) {
45013
45183
  security: prepared.security,
45014
45184
  captureTier: tier
45015
45185
  };
45016
- await writeFile15(path31.join(options.outDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
45186
+ await writeFile16(path32.join(options.outDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
45017
45187
  `);
45018
45188
  if (options.report !== false) {
45019
45189
  const reportPath = await writeCaptureReport(manifest, options.outDir);
@@ -45028,28 +45198,28 @@ async function scrapeLanding(options) {
45028
45198
 
45029
45199
  // src/commands/landing/inspiration/captureOut.ts
45030
45200
  import { existsSync as existsSync9 } from "fs";
45031
- import path32 from "path";
45201
+ import path33 from "path";
45032
45202
  var SCRATCH_DIR = ".baker";
45033
45203
  function isWithin(parent, target) {
45034
- const relative = path32.relative(parent, target);
45035
- return relative === "" || !relative.startsWith("..") && !path32.isAbsolute(relative);
45204
+ const relative = path33.relative(parent, target);
45205
+ return relative === "" || !relative.startsWith("..") && !path33.isAbsolute(relative);
45036
45206
  }
45037
45207
  function findRepoRoot(from) {
45038
- let dir = path32.resolve(from);
45208
+ let dir = path33.resolve(from);
45039
45209
  for (; ; ) {
45040
- if (existsSync9(path32.join(dir, ".git"))) return dir;
45041
- const parent = path32.dirname(dir);
45210
+ if (existsSync9(path33.join(dir, ".git"))) return dir;
45211
+ const parent = path33.dirname(dir);
45042
45212
  if (parent === dir) return null;
45043
45213
  dir = parent;
45044
45214
  }
45045
45215
  }
45046
45216
  function checkCaptureOut(out, options) {
45047
45217
  const { cwd, repoRoot } = options;
45048
- const resolved = path32.resolve(cwd, out);
45218
+ const resolved = path33.resolve(cwd, out);
45049
45219
  if (repoRoot === null || !isWithin(repoRoot, resolved)) return { ok: true };
45050
- const scratch = path32.join(repoRoot, SCRATCH_DIR);
45220
+ const scratch = path33.join(repoRoot, SCRATCH_DIR);
45051
45221
  if (isWithin(scratch, resolved)) return { ok: true };
45052
- const suggestion = path32.posix.join(SCRATCH_DIR, "teardowns", path32.basename(resolved) || "capture");
45222
+ const suggestion = path33.posix.join(SCRATCH_DIR, "teardowns", path33.basename(resolved) || "capture");
45053
45223
  return {
45054
45224
  ok: false,
45055
45225
  error: {
@@ -45115,7 +45285,7 @@ registerSchema({
45115
45285
  report: { type: "boolean", description: "Write report.html. `--no-report` to skip", required: false }
45116
45286
  }
45117
45287
  });
45118
- var scrapeCommand = defineCommand161({
45288
+ var scrapeCommand = defineCommand162({
45119
45289
  meta: {
45120
45290
  name: "scrape",
45121
45291
  description: "Capture a landing page to a directory, now. Example: baker landing inspiration scrape https://linear.app --out .baker/inspiration/linear.app"
@@ -45223,12 +45393,12 @@ var scrapeCommand = defineCommand161({
45223
45393
  });
45224
45394
 
45225
45395
  // src/commands/landing/inspiration/search.ts
45226
- import path34 from "path";
45227
- import { defineCommand as defineCommand162 } from "citty";
45396
+ import path35 from "path";
45397
+ import { defineCommand as defineCommand163 } from "citty";
45228
45398
 
45229
45399
  // src/commands/landing/inspiration/shot.ts
45230
- import { mkdir as mkdir11, writeFile as writeFile16 } from "fs/promises";
45231
- import path33 from "path";
45400
+ import { mkdir as mkdir12, writeFile as writeFile17 } from "fs/promises";
45401
+ import path34 from "path";
45232
45402
  import sharp6 from "sharp";
45233
45403
  var READABLE_SHOT = {
45234
45404
  maxWidth: 1440,
@@ -45254,9 +45424,9 @@ async function downloadReadableShot(url, file) {
45254
45424
  const response = await fetch(url);
45255
45425
  if (!response.ok) return null;
45256
45426
  const shot = await toReadableShot(Buffer.from(await response.arrayBuffer()));
45257
- await mkdir11(path33.dirname(file), { recursive: true });
45258
- await writeFile16(file, shot);
45259
- return path33.relative(process.cwd(), file);
45427
+ await mkdir12(path34.dirname(file), { recursive: true });
45428
+ await writeFile17(file, shot);
45429
+ return path34.relative(process.cwd(), file);
45260
45430
  } catch {
45261
45431
  return null;
45262
45432
  }
@@ -45348,20 +45518,20 @@ function buildSearchBody(args) {
45348
45518
  return body;
45349
45519
  }
45350
45520
  async function downloadShots(results) {
45351
- const dir = path34.join(process.cwd(), ".baker", "inspiration");
45521
+ const dir = path35.join(process.cwd(), ".baker", "inspiration");
45352
45522
  const saved = /* @__PURE__ */ new Map();
45353
45523
  await Promise.all(
45354
45524
  results.map(async (result) => {
45355
45525
  const file = await downloadReadableShot(
45356
45526
  result.desktopShotUrl,
45357
- path34.join(dir, `${result.id}.${READABLE_SHOT.extension}`)
45527
+ path35.join(dir, `${result.id}.${READABLE_SHOT.extension}`)
45358
45528
  );
45359
45529
  if (file) saved.set(result.id, file);
45360
45530
  })
45361
45531
  );
45362
45532
  return saved;
45363
45533
  }
45364
- var searchCommand2 = defineCommand162({
45534
+ var searchCommand2 = defineCommand163({
45365
45535
  meta: {
45366
45536
  name: "search",
45367
45537
  description: "Search real landing-page sections for reference. Example: baker landing inspiration search 'dark developer hero with a terminal' --register dev-tool-minimal --scope all"
@@ -45459,7 +45629,7 @@ var searchCommand2 = defineCommand162({
45459
45629
  });
45460
45630
 
45461
45631
  // src/commands/landing/inspiration/sequences.ts
45462
- import { defineCommand as defineCommand163 } from "citty";
45632
+ import { defineCommand as defineCommand164 } from "citty";
45463
45633
  var COMPACT_TRANSITIONS = 12;
45464
45634
  var COMPACT_ENDS = 5;
45465
45635
  var MAX_PAGES = 50;
@@ -45533,7 +45703,7 @@ function sequencesHints(data, { scope, full }) {
45533
45703
  if (scope === "favorites") hints.push(...favoritesScopeHints(data.favoritesHealth, data.pagesReturned));
45534
45704
  return hints;
45535
45705
  }
45536
- var sequencesCommand = defineCommand163({
45706
+ var sequencesCommand = defineCommand164({
45537
45707
  meta: {
45538
45708
  name: "sequences",
45539
45709
  description: "What section follows what, across many real pages at once. Example: baker landing inspiration sequences 'developer tool pricing page' --scope all \u2014 the evidence for how to order a page you are about to build."
@@ -45582,8 +45752,8 @@ var sequencesCommand = defineCommand163({
45582
45752
  });
45583
45753
 
45584
45754
  // src/commands/landing/inspiration/view.ts
45585
- import path35 from "path";
45586
- import { defineCommand as defineCommand164 } from "citty";
45755
+ import path36 from "path";
45756
+ import { defineCommand as defineCommand165 } from "citty";
45587
45757
  registerSchema({
45588
45758
  command: "landing.inspiration.view",
45589
45759
  description: "Everything known about one section: composition, motion, design tokens, the copy it uses, why it works, and what must change to make it yours. Downloads the desktop and mobile screenshots plus the motion filmstrip so you can look at them.",
@@ -45596,7 +45766,7 @@ registerSchema({
45596
45766
  }
45597
45767
  }
45598
45768
  });
45599
- var viewCommand2 = defineCommand164({
45769
+ var viewCommand2 = defineCommand165({
45600
45770
  meta: {
45601
45771
  name: "view",
45602
45772
  description: "Full detail for one reference section. Example: baker landing inspiration view k57abc\u2026 \u2014 read the screenshots it saves before you build."
@@ -45615,12 +45785,12 @@ var viewCommand2 = defineCommand164({
45615
45785
  const id = args.id;
45616
45786
  const data = await apiGet("/api/landing-inspiration/section", { id });
45617
45787
  const section = data.section;
45618
- const dir = path35.join(process.cwd(), ".baker", "inspiration", id);
45788
+ const dir = path36.join(process.cwd(), ".baker", "inspiration", id);
45619
45789
  const ext = READABLE_SHOT.extension;
45620
45790
  const [desktop, mobile, filmstrip] = await Promise.all([
45621
- downloadReadableShot(section.desktopShotUrl, path35.join(dir, `desktop.${ext}`)),
45622
- downloadReadableShot(section.mobileShotUrl, path35.join(dir, `mobile.${ext}`)),
45623
- downloadReadableShot(section.motionFilmstripUrl, path35.join(dir, `motion-filmstrip.${ext}`))
45791
+ downloadReadableShot(section.desktopShotUrl, path36.join(dir, `desktop.${ext}`)),
45792
+ downloadReadableShot(section.mobileShotUrl, path36.join(dir, `mobile.${ext}`)),
45793
+ downloadReadableShot(section.motionFilmstripUrl, path36.join(dir, `motion-filmstrip.${ext}`))
45624
45794
  ]);
45625
45795
  const full = args.full;
45626
45796
  const hints = [INSPIRATION_HINTS.structureNotCopy, INSPIRATION_HINTS.adapt];
@@ -45680,7 +45850,7 @@ var viewCommand2 = defineCommand164({
45680
45850
  });
45681
45851
 
45682
45852
  // src/commands/landing/inspiration/index.ts
45683
- var inspirationCommand = defineCommand165({
45853
+ var inspirationCommand = defineCommand166({
45684
45854
  meta: {
45685
45855
  name: "inspiration",
45686
45856
  description: `Reference library of real landing-page sections \u2014 look at how good pages actually solve a problem before you design one.
@@ -45725,7 +45895,7 @@ Full guide: __tooling__/docs/tools/baker/landing.md`
45725
45895
  });
45726
45896
 
45727
45897
  // src/commands/landing/index.ts
45728
- var landingCommand = defineCommand166({
45898
+ var landingCommand = defineCommand167({
45729
45899
  meta: {
45730
45900
  name: "landing",
45731
45901
  description: `Design-quality tools for landing pages (src/pages/<slug>/).
@@ -45743,7 +45913,7 @@ Subcommands:
45743
45913
  });
45744
45914
 
45745
45915
  // src/commands/mcp/index.ts
45746
- import { defineCommand as defineCommand167 } from "citty";
45916
+ import { defineCommand as defineCommand168 } from "citty";
45747
45917
 
45748
45918
  // src/commands/mcp/platforms.ts
45749
45919
  function readsKey(label) {
@@ -45812,7 +45982,7 @@ registerSchema({
45812
45982
  description: "List everything this chat can reach: managed integrations (Attio, Slack, Gmail, Google Sheets, \u2026), custom MCP servers, and the platforms the company signed in to (HubSpot, Google Ads, GA4, Search Console, Tag Manager) which you read through their own `baker` commands. Start here when the user mentions an external tool or platform.",
45813
45983
  args: {}
45814
45984
  });
45815
- var connectedCommand = defineCommand167({
45985
+ var connectedCommand = defineCommand168({
45816
45986
  meta: {
45817
45987
  name: "connected",
45818
45988
  description: `Everything this chat can reach \u2014 managed integrations, custom MCP servers, and connected platforms.
@@ -45871,7 +46041,7 @@ registerSchema({
45871
46041
  description: "List the custom MCP servers this company's chats see (org + company + your own user scope).",
45872
46042
  args: {}
45873
46043
  });
45874
- var listCommand14 = defineCommand167({
46044
+ var listCommand14 = defineCommand168({
45875
46045
  meta: { name: "list", description: "List custom MCP servers visible to this company's chats." },
45876
46046
  run: async () => {
45877
46047
  try {
@@ -45908,7 +46078,7 @@ registerSchema({
45908
46078
  header: { type: "string", description: 'Auth header "Key: Value" (repeatable)', required: false }
45909
46079
  }
45910
46080
  });
45911
- var addCommand2 = defineCommand167({
46081
+ var addCommand2 = defineCommand168({
45912
46082
  meta: {
45913
46083
  name: "add",
45914
46084
  description: `Register a custom MCP server. Tools appear as mcp__<name>__* on the NEXT message.
@@ -45960,7 +46130,7 @@ registerSchema({
45960
46130
  description: "Remove a company custom MCP server by name.",
45961
46131
  args: { name: { type: "string", description: "Server name to remove", required: true } }
45962
46132
  });
45963
- var removeCommand4 = defineCommand167({
46133
+ var removeCommand4 = defineCommand168({
45964
46134
  meta: {
45965
46135
  name: "remove",
45966
46136
  description: `Remove a company custom MCP server by name.
@@ -45982,7 +46152,7 @@ Example:
45982
46152
  }
45983
46153
  }
45984
46154
  });
45985
- var mcpCommand = defineCommand167({
46155
+ var mcpCommand = defineCommand168({
45986
46156
  meta: {
45987
46157
  name: "mcp",
45988
46158
  description: `Third-party tools for this company \u2014 see what's connected, register custom HTTPS MCP endpoints.
@@ -46008,10 +46178,10 @@ Full guide: __tooling__/docs/tools/baker/mcp.md`
46008
46178
  });
46009
46179
 
46010
46180
  // src/commands/research/index.ts
46011
- import { defineCommand as defineCommand179 } from "citty";
46181
+ import { defineCommand as defineCommand180 } from "citty";
46012
46182
 
46013
46183
  // src/commands/research/advertisers.ts
46014
- import { defineCommand as defineCommand168 } from "citty";
46184
+ import { defineCommand as defineCommand169 } from "citty";
46015
46185
 
46016
46186
  // src/commands/research/hints.ts
46017
46187
  var AD_COPY_ROUTE = 'This returns competing DOMAINS and their SERP economics \u2014 no ad copy, no headlines, no creative. For the actual copy of a competitor\'s ads: `baker winning-ads advertisers "<brand>"` \u2192 `baker winning-ads search "<brief>" --advertiser-id <id>` \u2192 `baker winning-ads content <adId>` (`primary_text` / `headline` / `cta`, plus the spoken transcript and on-screen text for video). That corpus is Meta and LinkedIn only \u2014 Google SERP ad copy is not available through any Baker command, so do not keep querying for it here.';
@@ -46194,7 +46364,7 @@ var FIELDS3 = {
46194
46364
  etv: "Estimated traffic value (USD)",
46195
46365
  visibility: "SERP visibility score (0-1)"
46196
46366
  };
46197
- var advertisersCommand = defineCommand168({
46367
+ var advertisersCommand = defineCommand169({
46198
46368
  meta: {
46199
46369
  name: "advertisers",
46200
46370
  description: `Domains competing for a keyword in Google SERPs, with position, relevance, traffic value and visibility. Returns NO ad copy \u2014 for a competitor's headlines and body copy use \`baker winning-ads content <adId>\` (Meta/LinkedIn only).
@@ -46250,7 +46420,7 @@ Examples:
46250
46420
  });
46251
46421
 
46252
46422
  // src/commands/research/autocomplete.ts
46253
- import { defineCommand as defineCommand169 } from "citty";
46423
+ import { defineCommand as defineCommand170 } from "citty";
46254
46424
  registerSchema({
46255
46425
  command: "research.autocomplete",
46256
46426
  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).",
@@ -46273,7 +46443,7 @@ registerSchema({
46273
46443
  var FIELDS4 = {
46274
46444
  suggestion: "Autocomplete suggestion from Google"
46275
46445
  };
46276
- var autocompleteCommand = defineCommand169({
46446
+ var autocompleteCommand = defineCommand170({
46277
46447
  meta: {
46278
46448
  name: "autocomplete",
46279
46449
  description: `Get Google Autocomplete suggestions for keyword expansion.
@@ -46328,7 +46498,7 @@ Examples:
46328
46498
  });
46329
46499
 
46330
46500
  // src/commands/research/countries.ts
46331
- import { defineCommand as defineCommand170 } from "citty";
46501
+ import { defineCommand as defineCommand171 } from "citty";
46332
46502
  registerSchema({
46333
46503
  command: "research.countries",
46334
46504
  description: "List all supported country codes for --location flag in research commands.",
@@ -46385,7 +46555,7 @@ var FIELDS5 = {
46385
46555
  code: "Country code to pass as --location",
46386
46556
  name: "Country name"
46387
46557
  };
46388
- var countriesCommand = defineCommand170({
46558
+ var countriesCommand = defineCommand171({
46389
46559
  meta: {
46390
46560
  name: "countries",
46391
46561
  description: "List all supported country codes for --location flag."
@@ -46396,7 +46566,7 @@ var countriesCommand = defineCommand170({
46396
46566
  });
46397
46567
 
46398
46568
  // src/commands/research/fetch.ts
46399
- import { defineCommand as defineCommand171 } from "citty";
46569
+ import { defineCommand as defineCommand172 } from "citty";
46400
46570
  var CONTENT_PREVIEW_CHARS = 2e4;
46401
46571
  var TIMEOUT_MS = 18e4;
46402
46572
  registerSchema({
@@ -46469,7 +46639,7 @@ function fetchFix(code) {
46469
46639
  explanation: "This read failed. Don't build a retry ladder around it \u2014 finish the rest of the job with what you can reach and name this page as a gap."
46470
46640
  };
46471
46641
  }
46472
- var fetchCommand = defineCommand171({
46642
+ var fetchCommand = defineCommand172({
46473
46643
  meta: {
46474
46644
  name: "fetch",
46475
46645
  description: `Read a page an ordinary web fetch could not. Bot walls and JavaScript-rendered pages are resolved for you \u2014 you never have to retry, wait, or drive a browser yourself.
@@ -46546,7 +46716,7 @@ Examples:
46546
46716
  });
46547
46717
 
46548
46718
  // src/commands/research/intent.ts
46549
- import { defineCommand as defineCommand172 } from "citty";
46719
+ import { defineCommand as defineCommand173 } from "citty";
46550
46720
  registerSchema({
46551
46721
  command: "research.intent",
46552
46722
  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.",
@@ -46569,7 +46739,7 @@ var FIELDS7 = {
46569
46739
  intent: "Primary Google Search intent: informational, navigational, commercial, transactional",
46570
46740
  probability: "Confidence score 0.0-1.0"
46571
46741
  };
46572
- var intentCommand = defineCommand172({
46742
+ var intentCommand = defineCommand173({
46573
46743
  meta: {
46574
46744
  name: "intent",
46575
46745
  description: `Classify Google Search intent for keywords. Returns intent type and confidence.
@@ -46617,7 +46787,7 @@ Examples:
46617
46787
  });
46618
46788
 
46619
46789
  // src/commands/research/keyword-gap.ts
46620
- import { defineCommand as defineCommand173 } from "citty";
46790
+ import { defineCommand as defineCommand174 } from "citty";
46621
46791
  registerSchema({
46622
46792
  command: "research.keyword-gap",
46623
46793
  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.",
@@ -46646,7 +46816,7 @@ var FIELDS8 = {
46646
46816
  cpc: "Cost per click USD",
46647
46817
  their_position: "Competitor's ranking position"
46648
46818
  };
46649
- var keywordGapCommand = defineCommand173({
46819
+ var keywordGapCommand = defineCommand174({
46650
46820
  meta: {
46651
46821
  name: "keyword-gap",
46652
46822
  description: `Find keywords a competitor has that you don't. Supports pagination via --offset.
@@ -46721,7 +46891,7 @@ Examples:
46721
46891
  });
46722
46892
 
46723
46893
  // src/commands/research/keywords-for-site.ts
46724
- import { defineCommand as defineCommand174 } from "citty";
46894
+ import { defineCommand as defineCommand175 } from "citty";
46725
46895
  registerSchema({
46726
46896
  command: "research.keywords-for-site",
46727
46897
  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.",
@@ -46754,7 +46924,7 @@ var FIELDS9 = {
46754
46924
  competition: "LOW, MEDIUM, or HIGH",
46755
46925
  competition_index: "Competition score 0-100"
46756
46926
  };
46757
- var keywordsForSiteCommand = defineCommand174({
46927
+ var keywordsForSiteCommand = defineCommand175({
46758
46928
  meta: {
46759
46929
  name: "keywords-for-site",
46760
46930
  description: `Get keywords a competitor targets in Google. Use --type to filter paid/organic.
@@ -46816,7 +46986,7 @@ Examples:
46816
46986
  });
46817
46987
 
46818
46988
  // src/commands/research/languages.ts
46819
- import { defineCommand as defineCommand175 } from "citty";
46989
+ import { defineCommand as defineCommand176 } from "citty";
46820
46990
  registerSchema({
46821
46991
  command: "research.languages",
46822
46992
  description: "List all supported language codes for --language flag in research commands.",
@@ -46846,7 +47016,7 @@ var FIELDS10 = {
46846
47016
  code: "Language code to pass as --language",
46847
47017
  name: "Language name (also accepted by --language)"
46848
47018
  };
46849
- var languagesCommand2 = defineCommand175({
47019
+ var languagesCommand2 = defineCommand176({
46850
47020
  meta: {
46851
47021
  name: "languages",
46852
47022
  description: "List all supported language codes for --language flag."
@@ -46857,7 +47027,7 @@ var languagesCommand2 = defineCommand175({
46857
47027
  });
46858
47028
 
46859
47029
  // src/commands/research/lighthouse.ts
46860
- import { defineCommand as defineCommand176 } from "citty";
47030
+ import { defineCommand as defineCommand177 } from "citty";
46861
47031
  registerSchema({
46862
47032
  command: "research.lighthouse",
46863
47033
  description: "Landing page performance audit. Returns metrics that affect Google Ads Quality Score and CPC.",
@@ -46876,7 +47046,7 @@ var FIELDS11 = {
46876
47046
  speed_index_ms: "Speed Index in ms (good: < 3400)",
46877
47047
  interactive_ms: "Time to Interactive in ms (good: < 3800)"
46878
47048
  };
46879
- var lighthouseCommand = defineCommand176({
47049
+ var lighthouseCommand = defineCommand177({
46880
47050
  meta: {
46881
47051
  name: "lighthouse",
46882
47052
  description: `Landing page performance audit. Metrics affecting Google Ads Quality Score.
@@ -46914,7 +47084,7 @@ Examples:
46914
47084
  });
46915
47085
 
46916
47086
  // src/commands/research/relevant-pages.ts
46917
- import { defineCommand as defineCommand177 } from "citty";
47087
+ import { defineCommand as defineCommand178 } from "citty";
46918
47088
  registerSchema({
46919
47089
  command: "research.relevant-pages",
46920
47090
  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).",
@@ -46940,7 +47110,7 @@ var FIELDS12 = {
46940
47110
  keywords: "Total organic keywords the page ranks for",
46941
47111
  top_10: "Keywords in positions 1-10"
46942
47112
  };
46943
- var relevantPagesCommand = defineCommand177({
47113
+ var relevantPagesCommand = defineCommand178({
46944
47114
  meta: {
46945
47115
  name: "relevant-pages",
46946
47116
  description: `Get the top pages of a competitor domain with traffic data.
@@ -46987,7 +47157,7 @@ Examples:
46987
47157
  });
46988
47158
 
46989
47159
  // src/commands/research/web.ts
46990
- import { defineCommand as defineCommand178 } from "citty";
47160
+ import { defineCommand as defineCommand179 } from "citty";
46991
47161
  registerSchema({
46992
47162
  command: "research.web",
46993
47163
  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).",
@@ -47038,7 +47208,7 @@ async function runDeepResearch(question) {
47038
47208
  }
47039
47209
  throw new Error("Deep research timed out");
47040
47210
  }
47041
- var webCommand = defineCommand178({
47211
+ var webCommand = defineCommand179({
47042
47212
  meta: {
47043
47213
  name: "web",
47044
47214
  description: `Search the web with AI to answer any open-ended marketing question. Uses live internet data via Google Search.
@@ -47100,7 +47270,7 @@ Examples:
47100
47270
  });
47101
47271
 
47102
47272
  // src/commands/research/index.ts
47103
- var researchCommand = defineCommand179({
47273
+ var researchCommand = defineCommand180({
47104
47274
  meta: {
47105
47275
  name: "research",
47106
47276
  description: `Competitive intelligence and AI-powered research commands.
@@ -47144,10 +47314,10 @@ Full guide: __tooling__/docs/tools/baker/research.md`
47144
47314
  });
47145
47315
 
47146
47316
  // src/commands/scheduled-actions/index.ts
47147
- import { defineCommand as defineCommand187 } from "citty";
47317
+ import { defineCommand as defineCommand188 } from "citty";
47148
47318
 
47149
47319
  // src/commands/scheduled-actions/create.ts
47150
- import { defineCommand as defineCommand180 } from "citty";
47320
+ import { defineCommand as defineCommand181 } from "citty";
47151
47321
 
47152
47322
  // src/commands/scheduled-actions/shared.ts
47153
47323
  var TEMP_SCHEDULED_ACTION_PREFIX = "temp_sched_";
@@ -47294,7 +47464,7 @@ registerSchema({
47294
47464
  }
47295
47465
  }
47296
47466
  });
47297
- var createCommand3 = defineCommand180({
47467
+ var createCommand3 = defineCommand181({
47298
47468
  meta: {
47299
47469
  name: "create",
47300
47470
  description: 'Stage a scheduled action. Example: baker scheduled-actions create --name "Weekly report" --description "..." --cron "0 9 * * MON"'
@@ -47353,7 +47523,7 @@ var createCommand3 = defineCommand180({
47353
47523
  });
47354
47524
 
47355
47525
  // src/commands/scheduled-actions/delete.ts
47356
- import { defineCommand as defineCommand181 } from "citty";
47526
+ import { defineCommand as defineCommand182 } from "citty";
47357
47527
  registerSchema({
47358
47528
  command: "scheduled-actions.delete",
47359
47529
  description: "Stage deletion of a published scheduled action or cancellation of a temp_sched_* draft creation.",
@@ -47361,7 +47531,7 @@ registerSchema({
47361
47531
  id: { type: "string", description: "Published scheduled action ID or temp_sched_* draft ID", required: true }
47362
47532
  }
47363
47533
  });
47364
- var deleteCommand3 = defineCommand181({
47534
+ var deleteCommand3 = defineCommand182({
47365
47535
  meta: {
47366
47536
  name: "delete",
47367
47537
  description: "Stage scheduled action deletion. Example: baker scheduled-actions delete <id-or-temp_sched_id>"
@@ -47390,7 +47560,7 @@ var deleteCommand3 = defineCommand181({
47390
47560
  });
47391
47561
 
47392
47562
  // src/commands/scheduled-actions/get.ts
47393
- import { defineCommand as defineCommand182 } from "citty";
47563
+ import { defineCommand as defineCommand183 } from "citty";
47394
47564
  registerSchema({
47395
47565
  command: "scheduled-actions.get",
47396
47566
  description: "Get a published scheduled action or a temp_sched_* draft-created scheduled action.",
@@ -47399,7 +47569,7 @@ registerSchema({
47399
47569
  chat: { type: "string", description: CHAT_READ_ARG.description, required: false }
47400
47570
  }
47401
47571
  });
47402
- var getCommand4 = defineCommand182({
47572
+ var getCommand4 = defineCommand183({
47403
47573
  meta: {
47404
47574
  name: "get",
47405
47575
  description: "Get a scheduled action. Example: baker scheduled-actions get <id-or-temp_sched_id>"
@@ -47438,7 +47608,7 @@ var getCommand4 = defineCommand182({
47438
47608
  });
47439
47609
 
47440
47610
  // src/commands/scheduled-actions/list.ts
47441
- import { defineCommand as defineCommand183 } from "citty";
47611
+ import { defineCommand as defineCommand184 } from "citty";
47442
47612
  registerSchema({
47443
47613
  command: "scheduled-actions.list",
47444
47614
  description: "List published scheduled actions. Includes draft state when BAKER_CHAT_ID is set, or --chat <id> to read an earlier chat's staged schedules instead.",
@@ -47446,7 +47616,7 @@ registerSchema({
47446
47616
  chat: { type: "string", description: CHAT_READ_ARG.description, required: false }
47447
47617
  }
47448
47618
  });
47449
- var listCommand15 = defineCommand183({
47619
+ var listCommand15 = defineCommand184({
47450
47620
  meta: {
47451
47621
  name: "list",
47452
47622
  description: "List scheduled actions. Includes staged draft ops when BAKER_CHAT_ID is set, or --chat <id> to read an earlier chat's staged schedules instead."
@@ -47469,9 +47639,9 @@ var listCommand15 = defineCommand183({
47469
47639
  });
47470
47640
 
47471
47641
  // src/commands/scheduled-actions/templates.ts
47472
- import { readFile as readFile22 } from "fs/promises";
47473
- import path36 from "path";
47474
- import { defineCommand as defineCommand184 } from "citty";
47642
+ import { readFile as readFile23 } from "fs/promises";
47643
+ import path37 from "path";
47644
+ import { defineCommand as defineCommand185 } from "citty";
47475
47645
  registerSchema({
47476
47646
  command: "scheduled-actions.templates",
47477
47647
  description: "The recipes available to this company: the ones Baker ships plus the ones they wrote themselves, each with its id, what it produces and how often it is meant to run. Read this before proposing a company's automation, so every recipe you name is one that exists. Also how a company gets a recipe of its own \u2014 save a brief, prove it runs, then publish it.",
@@ -47526,7 +47696,7 @@ registerSchema({
47526
47696
  }
47527
47697
  }
47528
47698
  });
47529
- var templatesCommand = defineCommand184({
47699
+ var templatesCommand = defineCommand185({
47530
47700
  meta: {
47531
47701
  name: "templates",
47532
47702
  description: `The recipes this company can run \u2014 Baker's own plus theirs, with what each one produces.
@@ -47573,7 +47743,7 @@ Full guide: __tooling__/docs/tools/baker/scheduled-actions.md`
47573
47743
  }
47574
47744
  if (save.length > 0) {
47575
47745
  const briefFile = flag("brief-file");
47576
- const brief = briefFile.length > 0 ? await readFile22(path36.resolve(briefFile), "utf8") : flag("brief");
47746
+ const brief = briefFile.length > 0 ? await readFile23(path37.resolve(briefFile), "utf8") : flag("brief");
47577
47747
  if (brief.trim().length === 0) {
47578
47748
  failValidation4("--brief-file (preferred) or --brief is required: the brief is the recipe.");
47579
47749
  }
@@ -47618,7 +47788,7 @@ Full guide: __tooling__/docs/tools/baker/scheduled-actions.md`
47618
47788
  });
47619
47789
 
47620
47790
  // src/commands/scheduled-actions/trigger.ts
47621
- import { defineCommand as defineCommand185 } from "citty";
47791
+ import { defineCommand as defineCommand186 } from "citty";
47622
47792
  registerSchema({
47623
47793
  command: "scheduled-actions.trigger",
47624
47794
  description: "Immediately trigger a published scheduled action. Does not require BAKER_CHAT_ID and rejects temp_sched_* IDs.",
@@ -47626,7 +47796,7 @@ registerSchema({
47626
47796
  id: { type: "string", description: "Published scheduled action ID", required: true }
47627
47797
  }
47628
47798
  });
47629
- var triggerCommand = defineCommand185({
47799
+ var triggerCommand = defineCommand186({
47630
47800
  meta: {
47631
47801
  name: "trigger",
47632
47802
  description: "Immediately trigger a published scheduled action. Example: baker scheduled-actions trigger <id>"
@@ -47663,7 +47833,7 @@ var triggerCommand = defineCommand185({
47663
47833
  });
47664
47834
 
47665
47835
  // src/commands/scheduled-actions/update.ts
47666
- import { defineCommand as defineCommand186 } from "citty";
47836
+ import { defineCommand as defineCommand187 } from "citty";
47667
47837
  registerSchema({
47668
47838
  command: "scheduled-actions.update",
47669
47839
  description: "Stage an update to a published scheduled action or temp_sched_* draft-created scheduled action.",
@@ -47694,7 +47864,7 @@ registerSchema({
47694
47864
  prompt: { type: "string", description: "Replacement additional spawned-agent instructions", required: false }
47695
47865
  }
47696
47866
  });
47697
- var updateCommand3 = defineCommand186({
47867
+ var updateCommand3 = defineCommand187({
47698
47868
  meta: {
47699
47869
  name: "update",
47700
47870
  description: "Stage a scheduled action update. Examples: baker scheduled-actions update <id> --enabled false | baker scheduled-actions update <id> --mode publish"
@@ -47772,7 +47942,7 @@ var updateCommand3 = defineCommand186({
47772
47942
  });
47773
47943
 
47774
47944
  // src/commands/scheduled-actions/index.ts
47775
- var scheduledActionsCommand = defineCommand187({
47945
+ var scheduledActionsCommand = defineCommand188({
47776
47946
  meta: {
47777
47947
  name: "scheduled-actions",
47778
47948
  description: `Manage Scheduled Actions. Subcommands: list, get, create, update, delete, trigger, templates.
@@ -47801,14 +47971,14 @@ Full guide: __tooling__/docs/tools/baker/scheduled-actions.md`
47801
47971
  });
47802
47972
 
47803
47973
  // src/commands/schema.ts
47804
- import { defineCommand as defineCommand188 } from "citty";
47974
+ import { defineCommand as defineCommand189 } from "citty";
47805
47975
  function narrowToFamily(commandName, available) {
47806
47976
  const segments = commandName.split(".");
47807
47977
  const prefix = segments[0] === "ads" && segments[1] ? `ads.${segments[1]}.` : `${segments[0]}.`;
47808
47978
  const siblings = available.filter((name) => name.startsWith(prefix));
47809
47979
  return siblings.length > 0 ? siblings : available;
47810
47980
  }
47811
- var schemaCommand2 = defineCommand188({
47981
+ var schemaCommand2 = defineCommand189({
47812
47982
  meta: {
47813
47983
  name: "schema",
47814
47984
  description: "Inspect command argument schemas (for AI agent introspection). Lists all commands if no argument given. Example: baker schema images.search"
@@ -47852,10 +48022,10 @@ var schemaCommand2 = defineCommand188({
47852
48022
  });
47853
48023
 
47854
48024
  // src/commands/studio/index.ts
47855
- import { defineCommand as defineCommand197 } from "citty";
48025
+ import { defineCommand as defineCommand198 } from "citty";
47856
48026
 
47857
48027
  // src/commands/studio/animate.ts
47858
- import { defineCommand as defineCommand189 } from "citty";
48028
+ import { defineCommand as defineCommand190 } from "citty";
47859
48029
 
47860
48030
  // src/commands/studio/batch.ts
47861
48031
  function projectBatch(generation, full) {
@@ -48044,7 +48214,7 @@ function parseImageRefs(spec) {
48044
48214
  }
48045
48215
  var defaultDeps = {
48046
48216
  ingest: (url) => apiPost("/api/images/ingest", { url, source: "uploaded" }),
48047
- upload: (path38) => uploadLocalImage({ file: path38, contentType: detectImageContentType(path38), source: "uploaded" })
48217
+ upload: (path39) => uploadLocalImage({ file: path39, contentType: detectImageContentType(path39), source: "uploaded" })
48048
48218
  };
48049
48219
  async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
48050
48220
  const refs = parseImageRefs(spec);
@@ -48064,10 +48234,10 @@ async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
48064
48234
  }
48065
48235
  return { imageIds, added };
48066
48236
  }
48067
- function uploadFailure(path38) {
48237
+ function uploadFailure(path39) {
48068
48238
  return (error) => {
48069
48239
  if (error instanceof ApiError) throw error;
48070
- throw new ApiError("VALIDATION_ERROR", `Could not read "${path38}" as an image.`);
48240
+ throw new ApiError("VALIDATION_ERROR", `Could not read "${path39}" as an image.`);
48071
48241
  };
48072
48242
  }
48073
48243
 
@@ -48090,7 +48260,7 @@ registerSchema({
48090
48260
  },
48091
48261
  image: {
48092
48262
  type: "string",
48093
- description: "Comma-separated source images: library image ids, local file paths, and/or image URLs. Required unless --from text. With the default --from frame this must be exactly ONE image (the opening frame) \u2014 several are refused rather than silently reduced to the first. With --from references, up to 9 on Seedance 2.0 and 30 on Seedance 2.5.",
48263
+ description: "Comma-separated source images: library image ids, local file paths, and/or image URLs. Required unless --from text or --avatar (the avatar is the source). With the default --from frame this must be exactly ONE image (the opening frame) \u2014 several are refused rather than silently reduced to the first. With --from references, up to 9 on Seedance 2.0 and 30 on Seedance 2.5.",
48094
48264
  required: false
48095
48265
  },
48096
48266
  from: {
@@ -48306,13 +48476,18 @@ function costHintsFor(body) {
48306
48476
  }
48307
48477
  return hints;
48308
48478
  }
48309
- var animateCommand = defineCommand189({
48479
+ var animateCommand = defineCommand190({
48310
48480
  meta: {
48311
48481
  name: "animate",
48312
- description: "Render a clip. With an image the look is already fixed, so the prompt describes MOVEMENT \u2014 what the camera does, what the subject does, in what order. With --from text there is no image and the prompt is the whole shot.\n\nA rendered clip is NOT usable anywhere until you keep it: `baker studio keep <id> --slot N` is what puts it in the video library. Takes nobody keeps are never ingested, which is what makes a rejected batch cheap.\n\nFor anything longer than 15 seconds, or to build on footage that already exists, use --model bytedance/seedance-2.5: it renders 4-30s and is the only model that reads an existing clip or an existing soundtrack.\n\nExamples:\n baker studio animate 'slow push in, model turns to camera and smiles' --image j57abc123def456ghi789\n baker studio animate 'handheld drift right, steam rising from the cup' --image './out/hero.png' --duration 6 --quality 1080p\n baker studio animate 'product rotates once on a turntable' --image j57abc\u2026,j57def\u2026 --from references\n baker studio animate 'she keeps walking, camera stays with her, then she stops and looks up' --image j57abc\u2026 --from references --from-clip j57batch\u2026:0 --model bytedance/seedance-2.5 --duration 20\n baker studio animate 'hold on the product, then a slow push-in' --from references --from-video j57vid\u2026 --model bytedance/seedance-2.5\n baker studio animate 'slow drone pull-back over a solar farm at golden hour, no people' --from text --model bytedance/seedance-2.5 --duration 12"
48482
+ description: "Render a clip. With an image the look is already fixed, so the prompt describes MOVEMENT \u2014 what the camera does, what the subject does, in what order. With --from text there is no image and the prompt is the whole shot.\n\nA rendered clip is NOT usable anywhere until you keep it: `baker studio keep <id> --slot N` is what puts it in the video library. Takes nobody keeps are never ingested, which is what makes a rejected batch cheap.\n\nFor anything longer than 15 seconds, or to build on footage that already exists, use --model bytedance/seedance-2.5: it renders 4-30s and is the only model that reads an existing clip or an existing soundtrack.\n\nExamples:\n baker studio animate 'slow push in, model turns to camera and smiles' --image j57abc123def456ghi789\n baker studio animate 'she looks to camera and says: \"Hola, soy Elena\"' --avatar elena --quality 720p --aspect-ratio 9:16\n baker studio animate 'handheld drift right, steam rising from the cup' --image './out/hero.png' --duration 6 --quality 1080p\n baker studio animate 'product rotates once on a turntable' --image j57abc\u2026,j57def\u2026 --from references\n baker studio animate 'she keeps walking, camera stays with her, then she stops and looks up' --image j57abc\u2026 --from references --from-clip j57batch\u2026:0 --model bytedance/seedance-2.5 --duration 20\n baker studio animate 'hold on the product, then a slow push-in' --from references --from-video j57vid\u2026 --model bytedance/seedance-2.5\n baker studio animate 'slow drone pull-back over a solar farm at golden hour, no people' --from text --model bytedance/seedance-2.5 --duration 12"
48313
48483
  },
48314
48484
  args: {
48315
48485
  prompt: { type: "positional", description: "What MOVES", required: false },
48486
+ avatar: {
48487
+ type: "string",
48488
+ description: "Cast an avatar by short name \u2014 grounds the clip on their identity sheet AND gives it their voice",
48489
+ required: false
48490
+ },
48316
48491
  image: {
48317
48492
  type: "string",
48318
48493
  description: "Comma-separated library image ids, local file paths, and/or image URLs",
@@ -48377,13 +48552,13 @@ var animateCommand = defineCommand189({
48377
48552
  writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message: "A prompt is required" } });
48378
48553
  process.exit(1);
48379
48554
  }
48380
- if (!args.image && args.from !== "text") {
48555
+ if (!args.image && !args.avatar && args.from !== "text") {
48381
48556
  writeJson({
48382
48557
  ok: false,
48383
48558
  error: {
48384
48559
  code: "VALIDATION_ERROR",
48385
- message: "--image is required unless you pass --from text: a clip normally animates a still, so it needs the image to start from",
48386
- fix: "Pass --image <id|path|url>, or --from text to render from the prompt alone."
48560
+ message: "--image is required unless you pass --from text or --avatar: a clip normally animates a still, so it needs the image to start from",
48561
+ fix: "Pass --image <id|path|url>, --avatar <handle> to build the shot around one of your avatars, or --from text to render from the prompt alone."
48387
48562
  }
48388
48563
  });
48389
48564
  process.exit(1);
@@ -48425,7 +48600,7 @@ var animateCommand = defineCommand189({
48425
48600
  });
48426
48601
 
48427
48602
  // src/commands/studio/generate.ts
48428
- import { defineCommand as defineCommand190 } from "citty";
48603
+ import { defineCommand as defineCommand191 } from "citty";
48429
48604
  var MODEL_LIST2 = IMAGE_MODEL_IDS;
48430
48605
  var DEFAULT_MAX_WAIT_MS2 = 24e4;
48431
48606
  registerSchema({
@@ -48561,12 +48736,17 @@ function buildGenerateBody(args, prompt) {
48561
48736
  }
48562
48737
  return body;
48563
48738
  }
48564
- var generateCommand = defineCommand190({
48739
+ var generateCommand = defineCommand191({
48565
48740
  meta: {
48566
48741
  name: "generate",
48567
48742
  description: "Start here to make an image. Renders 1-8 takes of one brief, ingests each into the media library as it lands, and shows the batch in the dashboard Studio next to the ones the client ran.\n\nModel choice: google/gemini-3.1-flash-image-preview (default \u2014 fast, best at editing a reference and at extreme ratios), google/gemini-3-pro-image-preview (highest fidelity, slower), openai/gpt-image-2 (photoreal and the cleanest in-image text \u2014 no --image-size, no 4:5 / 5:4), recraft/recraft-v4.1-pro-vector (vector/flat marks with palette control).\n\n--reference is the biggest quality lever there is: a real logo, product shot, Pinterest pin or sandbox screenshot beats any amount of adjectives.\n\nExamples:\n baker studio generate 'matte black bottle on wet marble, hard studio light, 35mm' --aspect-ratio 3:2 --count 3\n baker studio generate 'this bottle on a sunlit kitchen counter' --reference './src/brand/product.png,https://\u2026/kitchen.jpg'\n baker studio generate 'founder-style selfie, kitchen background, natural light' --skill ugc-selfie-hook\n baker studio generate 'flat geometric mascot, brand palette' --model recraft/recraft-v4.1-pro-vector --rgb-colors '[[10,10,10],[255,80,0]]'"
48568
48743
  },
48569
48744
  args: {
48745
+ avatar: {
48746
+ type: "string",
48747
+ description: "Cast an avatar by short name \u2014 grounds the image on their identity sheet",
48748
+ required: false
48749
+ },
48570
48750
  prompt: { type: "positional", description: "What to generate", required: false },
48571
48751
  model: { type: "string", description: "Model id (default google/gemini-3.1-flash-image-preview)", required: false },
48572
48752
  "aspect-ratio": { type: "string", description: "Aspect ratio (default 1:1)", required: false },
@@ -48639,7 +48819,7 @@ var generateCommand = defineCommand190({
48639
48819
  });
48640
48820
 
48641
48821
  // src/commands/studio/get.ts
48642
- import { defineCommand as defineCommand191 } from "citty";
48822
+ import { defineCommand as defineCommand192 } from "citty";
48643
48823
  registerSchema({
48644
48824
  command: "studio.get",
48645
48825
  description: "Read one Studio batch: every take, where it lives, and why a take is missing. This is how you pick up a batch that was still rendering when the start command returned.",
@@ -48648,7 +48828,7 @@ registerSchema({
48648
48828
  full: { type: "boolean", description: "Include settings, references and attribution", required: false }
48649
48829
  }
48650
48830
  });
48651
- var getCommand5 = defineCommand191({
48831
+ var getCommand5 = defineCommand192({
48652
48832
  meta: {
48653
48833
  name: "get",
48654
48834
  description: "Read one Studio batch \u2014 the takes, their urls, whether each is in the library, and the reason for any that failed.\n\nExample: baker studio get j57abc123def456ghi789\nExample: baker studio get j57abc123def456ghi789 --full"
@@ -48677,7 +48857,7 @@ var getCommand5 = defineCommand191({
48677
48857
  });
48678
48858
 
48679
48859
  // src/commands/studio/improve.ts
48680
- import { defineCommand as defineCommand192 } from "citty";
48860
+ import { defineCommand as defineCommand193 } from "citty";
48681
48861
  var DESCRIPTION = "Sharpen a rough brief into directed art direction \u2014 the same rewrite the client gets from the wand in the Studio prompt bar. Reach for it when you are relaying the CLIENT's own words and want them shaped without substituting your voice; when you are writing the art direction yourself, just write it, because you will do a better job than this does.";
48682
48862
  registerSchema({
48683
48863
  command: "studio.improve",
@@ -48702,7 +48882,7 @@ registerSchema({
48702
48882
  }
48703
48883
  }
48704
48884
  });
48705
- var improveCommand = defineCommand192({
48885
+ var improveCommand = defineCommand193({
48706
48886
  meta: {
48707
48887
  name: "improve",
48708
48888
  description: `${DESCRIPTION}
@@ -48746,7 +48926,7 @@ Examples:
48746
48926
  });
48747
48927
 
48748
48928
  // src/commands/studio/keep.ts
48749
- import { defineCommand as defineCommand193 } from "citty";
48929
+ import { defineCommand as defineCommand194 } from "citty";
48750
48930
  registerSchema({
48751
48931
  command: "studio.keep",
48752
48932
  description: "Mark one take as the keeper. For an image this stars it, so the client reviewing the batch sees which one you used. For a CLIP it is the step that puts it in the video library \u2014 until then the clip cannot be used in a canvas, a landing, or an ad.",
@@ -48756,7 +48936,7 @@ registerSchema({
48756
48936
  undo: { type: "boolean", description: "Un-star an image, or take a kept clip back out", required: false }
48757
48937
  }
48758
48938
  });
48759
- var keepCommand = defineCommand193({
48939
+ var keepCommand = defineCommand194({
48760
48940
  meta: {
48761
48941
  name: "keep",
48762
48942
  description: "Mark one take as the keeper. An image gets starred (it was already in the library); a clip gets INGESTED into the video library, which is what makes it usable anywhere else.\n\nExample: baker studio keep j57abc123def456ghi789 --slot 2\nExample: baker studio keep j57abc123def456ghi789 --slot 2 --undo"
@@ -48807,7 +48987,7 @@ var keepCommand = defineCommand193({
48807
48987
  });
48808
48988
 
48809
48989
  // src/commands/studio/list.ts
48810
- import { defineCommand as defineCommand194 } from "citty";
48990
+ import { defineCommand as defineCommand195 } from "citty";
48811
48991
  registerSchema({
48812
48992
  command: "studio.list",
48813
48993
  description: "Recent Studio batches for THIS conversation, newest first \u2014 what you have already generated, so you re-use a take instead of paying for it twice. `--all` widens it to everything the company generated, including what people ran themselves in the dashboard.",
@@ -48818,7 +48998,7 @@ registerSchema({
48818
48998
  full: { type: "boolean", description: "Include settings, references and attribution", required: false }
48819
48999
  }
48820
49000
  });
48821
- var listCommand16 = defineCommand194({
49001
+ var listCommand16 = defineCommand195({
48822
49002
  meta: {
48823
49003
  name: "list",
48824
49004
  description: "Recent Studio batches, newest first. Scoped to this conversation unless you pass --all.\n\nExample: baker studio list\nExample: baker studio list --kind video --limit 5\nExample: baker studio list --all # includes batches the client ran in the dashboard"
@@ -48852,7 +49032,7 @@ var listCommand16 = defineCommand194({
48852
49032
  });
48853
49033
 
48854
49034
  // src/commands/studio/models.ts
48855
- import { defineCommand as defineCommand195 } from "citty";
49035
+ import { defineCommand as defineCommand196 } from "citty";
48856
49036
  var DESCRIPTION2 = "What each Studio model actually accepts: its shapes, resolutions, clip lengths, prompt character cap, how many reference images it takes, and which knobs it has. Read this before a batch you care about \u2014 the models disagree far more than they look like they do, and a setting the chosen model does not have is REFUSED, not ignored.";
48857
49037
  registerSchema({
48858
49038
  command: "studio.models",
@@ -48939,7 +49119,7 @@ function buildModelCards(kind, model) {
48939
49119
  const selected = model ? ids.filter((id) => id === model) : ids;
48940
49120
  return selected.map((id) => build(id));
48941
49121
  }
48942
- var modelsCommand = defineCommand195({
49122
+ var modelsCommand = defineCommand196({
48943
49123
  meta: {
48944
49124
  name: "models",
48945
49125
  description: `${DESCRIPTION2}
@@ -48986,13 +49166,13 @@ Examples:
48986
49166
  });
48987
49167
 
48988
49168
  // src/commands/studio/skills.ts
48989
- import { defineCommand as defineCommand196 } from "citty";
49169
+ import { defineCommand as defineCommand197 } from "citty";
48990
49170
  registerSchema({
48991
49171
  command: "studio.skills",
48992
49172
  description: "The craft directions `studio generate --skill <id>` accepts. Each one carries directed art direction plus the model, shape and take count it wants, so you pick a look by name instead of writing the boilerplate yourself.",
48993
49173
  args: {}
48994
49174
  });
48995
- var skillsCommand = defineCommand196({
49175
+ var skillsCommand = defineCommand197({
48996
49176
  meta: {
48997
49177
  name: "skills",
48998
49178
  description: "List the craft directions available to `baker studio generate --skill <id>` \u2014 what each one is for, whether it wants a reference image, and the model/shape/count it defaults to.\n\nExample: baker studio skills"
@@ -49016,7 +49196,7 @@ var skillsCommand = defineCommand196({
49016
49196
  });
49017
49197
 
49018
49198
  // src/commands/studio/index.ts
49019
- var studioCommand = defineCommand197({
49199
+ var studioCommand = defineCommand198({
49020
49200
  meta: {
49021
49201
  name: "studio",
49022
49202
  description: `Make new imagery and clips. Every batch is recorded and shows up in the dashboard Studio for the client to review, labelled with this conversation.
@@ -49059,10 +49239,10 @@ Full guide: __tooling__/docs/tools/baker/studio.md`
49059
49239
  });
49060
49240
 
49061
49241
  // src/commands/tag-manager/index.ts
49062
- import { defineCommand as defineCommand201 } from "citty";
49242
+ import { defineCommand as defineCommand202 } from "citty";
49063
49243
 
49064
49244
  // src/commands/tag-manager/draft.ts
49065
- import { defineCommand as defineCommand198 } from "citty";
49245
+ import { defineCommand as defineCommand199 } from "citty";
49066
49246
 
49067
49247
  // src/commands/tag-manager/shared.ts
49068
49248
  import { readFileSync as readFileSync15 } from "fs";
@@ -49129,10 +49309,10 @@ async function stageOp4(op) {
49129
49309
  handleError5(err);
49130
49310
  }
49131
49311
  }
49132
- async function draftAction3(path38, body, chat) {
49312
+ async function draftAction3(path39, body, chat) {
49133
49313
  const chatId = resolveChatId(chat);
49134
49314
  try {
49135
- const data = await apiPost(path38, { chatId, ...body });
49315
+ const data = await apiPost(path39, { chatId, ...body });
49136
49316
  writeJsonEnvelope({ ok: true, data });
49137
49317
  return data;
49138
49318
  } catch (err) {
@@ -49185,13 +49365,13 @@ registerSchema({
49185
49365
  chat: { type: "string", description: CHAT_READ_ARG.description, required: false }
49186
49366
  }
49187
49367
  });
49188
- var draftCommand4 = defineCommand198({
49368
+ var draftCommand4 = defineCommand199({
49189
49369
  meta: {
49190
49370
  name: "draft",
49191
49371
  description: "List, show, amend, remove, or clear staged Tag Manager changes for this chat. `list` and `show` take --chat <id> to read an earlier chat's changes instead."
49192
49372
  },
49193
49373
  subCommands: {
49194
- list: defineCommand198({
49374
+ list: defineCommand199({
49195
49375
  meta: {
49196
49376
  name: "list",
49197
49377
  description: "Review everything staged on this chat (--json for the raw envelope)"
@@ -49204,7 +49384,7 @@ var draftCommand4 = defineCommand198({
49204
49384
  await draftList2(args.json === true, args.chat);
49205
49385
  }
49206
49386
  }),
49207
- show: defineCommand198({
49387
+ show: defineCommand199({
49208
49388
  meta: {
49209
49389
  name: "show",
49210
49390
  description: "Print the full staged payload for one change \u2014 the receipt to verify it looks right before publish (never truncated)."
@@ -49221,7 +49401,7 @@ var draftCommand4 = defineCommand198({
49221
49401
  );
49222
49402
  }
49223
49403
  }),
49224
- amend: defineCommand198({
49404
+ amend: defineCommand199({
49225
49405
  meta: {
49226
49406
  name: "amend",
49227
49407
  description: "Update a staged change in place \u2014 merges a JSON patch into its payload (objects deep-merge, null deletes a key, arrays/scalars replace) and re-validates. Use this instead of remove + re-create."
@@ -49238,7 +49418,7 @@ var draftCommand4 = defineCommand198({
49238
49418
  });
49239
49419
  }
49240
49420
  }),
49241
- remove: defineCommand198({
49421
+ remove: defineCommand199({
49242
49422
  meta: { name: "remove", description: "Remove one staged change (cascades to anything depending on it)" },
49243
49423
  args: { ref: { type: "positional", description: "Staged ref (gtm_temp_*) or target", required: false } },
49244
49424
  run: async ({ args }) => {
@@ -49247,7 +49427,7 @@ var draftCommand4 = defineCommand198({
49247
49427
  });
49248
49428
  }
49249
49429
  }),
49250
- clear: defineCommand198({
49430
+ clear: defineCommand199({
49251
49431
  meta: { name: "clear", description: "Discard all Tag Manager changes staged on this chat" },
49252
49432
  run: async () => {
49253
49433
  await draftAction3("/api/tag-manager/draft/clear", {});
@@ -49257,7 +49437,7 @@ var draftCommand4 = defineCommand198({
49257
49437
  });
49258
49438
 
49259
49439
  // src/commands/tag-manager/read.ts
49260
- import { defineCommand as defineCommand199 } from "citty";
49440
+ import { defineCommand as defineCommand200 } from "citty";
49261
49441
  registerSchema({
49262
49442
  command: "tagManager.containers",
49263
49443
  description: "List the Google Tag Manager containers this company's connection can reach. Every container the company connected is flagged `connected: true` \u2014 there can be several, and Baker may read and change all of them. Start here to confirm which containers you are managing.",
@@ -49298,7 +49478,7 @@ function containersHints(containers) {
49298
49478
  }))
49299
49479
  });
49300
49480
  }
49301
- var containersCommand = defineCommand199({
49481
+ var containersCommand = defineCommand200({
49302
49482
  meta: {
49303
49483
  name: "containers",
49304
49484
  description: `List Tag Manager containers reachable by this company's connection.
@@ -49315,7 +49495,7 @@ Start here:
49315
49495
  }
49316
49496
  }
49317
49497
  });
49318
- var readCommand = defineCommand199({
49498
+ var readCommand = defineCommand200({
49319
49499
  meta: {
49320
49500
  name: "read",
49321
49501
  description: `Read the current contents of the Tag Manager container \u2014 always do this before staging changes.
@@ -49357,7 +49537,7 @@ Examples:
49357
49537
  });
49358
49538
 
49359
49539
  // src/commands/tag-manager/write-commands.ts
49360
- import { defineCommand as defineCommand200 } from "citty";
49540
+ import { defineCommand as defineCommand201 } from "citty";
49361
49541
  var CONTAINER_ARG_DESCRIPTION = "Numeric container id (optional only when one container is connected \u2014 run `baker tag-manager containers`)";
49362
49542
  var ENTITIES = [
49363
49543
  {
@@ -49413,10 +49593,10 @@ for (const { entity, noun, createHint } of ENTITIES) {
49413
49593
  });
49414
49594
  }
49415
49595
  function entityCommand(entity, noun, example) {
49416
- return defineCommand200({
49596
+ return defineCommand201({
49417
49597
  meta: { name: entity, description: `Stage ${noun} changes on this chat's Tag Manager draft` },
49418
49598
  subCommands: {
49419
- create: defineCommand200({
49599
+ create: defineCommand201({
49420
49600
  meta: {
49421
49601
  name: "create",
49422
49602
  description: `Stage a new ${noun}
@@ -49438,7 +49618,7 @@ Examples:
49438
49618
  });
49439
49619
  }
49440
49620
  }),
49441
- update: defineCommand200({
49621
+ update: defineCommand201({
49442
49622
  meta: {
49443
49623
  name: "update",
49444
49624
  description: `Stage an update to an existing ${noun} (pass its id or path)`
@@ -49458,7 +49638,7 @@ Examples:
49458
49638
  });
49459
49639
  }
49460
49640
  }),
49461
- delete: defineCommand200({
49641
+ delete: defineCommand201({
49462
49642
  meta: { name: "delete", description: `Stage the deletion of a ${noun} (pass its id or path)` },
49463
49643
  args: {
49464
49644
  id: { type: "positional", description: `${noun} id or path`, required: false },
@@ -49499,7 +49679,7 @@ function builtinTypes(args) {
49499
49679
  }
49500
49680
  return raw.split(",").map((entry) => entry.trim());
49501
49681
  }
49502
- var builtinCommand = defineCommand200({
49682
+ var builtinCommand = defineCommand201({
49503
49683
  meta: {
49504
49684
  name: "builtin",
49505
49685
  description: `Enable or disable built-in variables
@@ -49509,7 +49689,7 @@ Examples:
49509
49689
  baker tag-manager builtin disable --types formId`
49510
49690
  },
49511
49691
  subCommands: {
49512
- enable: defineCommand200({
49692
+ enable: defineCommand201({
49513
49693
  meta: { name: "enable", description: "Stage enabling built-in variables" },
49514
49694
  args: {
49515
49695
  types: { type: "string", description: "Comma-separated types", required: false },
@@ -49523,7 +49703,7 @@ Examples:
49523
49703
  });
49524
49704
  }
49525
49705
  }),
49526
- disable: defineCommand200({
49706
+ disable: defineCommand201({
49527
49707
  meta: { name: "disable", description: "Stage disabling built-in variables" },
49528
49708
  args: {
49529
49709
  types: { type: "string", description: "Comma-separated types", required: false },
@@ -49541,7 +49721,7 @@ Examples:
49541
49721
  });
49542
49722
 
49543
49723
  // src/commands/tag-manager/index.ts
49544
- var tagManagerCommand = defineCommand201({
49724
+ var tagManagerCommand = defineCommand202({
49545
49725
  meta: {
49546
49726
  name: "tag-manager",
49547
49727
  description: `Read and change what lives inside the client's Google Tag Manager container \u2014 tags, triggers, variables, folders and built-in variables.
@@ -49578,7 +49758,7 @@ Full guide: __tooling__/docs/tools/baker/tag-manager.md`
49578
49758
  });
49579
49759
 
49580
49760
  // src/commands/tags/index.ts
49581
- import { defineCommand as defineCommand202 } from "citty";
49761
+ import { defineCommand as defineCommand203 } from "citty";
49582
49762
 
49583
49763
  // src/commands/tags/shared.ts
49584
49764
  function failApi3(err) {
@@ -49647,7 +49827,7 @@ async function listTags(json) {
49647
49827
  var listArgs9 = {
49648
49828
  json: { type: "boolean", description: "Print the raw JSON envelope instead of the readable list" }
49649
49829
  };
49650
- var listCommand17 = defineCommand202({
49830
+ var listCommand17 = defineCommand203({
49651
49831
  meta: {
49652
49832
  name: "list",
49653
49833
  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"
@@ -49666,7 +49846,7 @@ async function listDraft3(chat) {
49666
49846
  failApi3(err);
49667
49847
  }
49668
49848
  }
49669
- var draftCommand5 = defineCommand202({
49849
+ var draftCommand5 = defineCommand203({
49670
49850
  meta: {
49671
49851
  name: "draft",
49672
49852
  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). Takes --chat <id> to read an earlier chat's staged changes instead."
@@ -49676,7 +49856,7 @@ var draftCommand5 = defineCommand202({
49676
49856
  await listDraft3(args.chat);
49677
49857
  }
49678
49858
  });
49679
- var tagsCommand4 = defineCommand202({
49859
+ var tagsCommand4 = defineCommand203({
49680
49860
  meta: {
49681
49861
  name: "tags",
49682
49862
  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.
@@ -49705,10 +49885,10 @@ Full guide: __tooling__/docs/tools/baker/tags.md`
49705
49885
  });
49706
49886
 
49707
49887
  // src/commands/testimonials/index.ts
49708
- import { defineCommand as defineCommand206 } from "citty";
49888
+ import { defineCommand as defineCommand207 } from "citty";
49709
49889
 
49710
49890
  // src/commands/testimonials/get.ts
49711
- import { defineCommand as defineCommand203 } from "citty";
49891
+ import { defineCommand as defineCommand204 } from "citty";
49712
49892
  registerSchema({
49713
49893
  command: "testimonials.get",
49714
49894
  description: "Get a single testimonial by ID",
@@ -49716,7 +49896,7 @@ registerSchema({
49716
49896
  id: { type: "string", description: "Testimonial ID", required: true }
49717
49897
  }
49718
49898
  });
49719
- var getCommand6 = defineCommand203({
49899
+ var getCommand6 = defineCommand204({
49720
49900
  meta: { name: "get", description: "Get a single testimonial by ID. Example: baker testimonials get j571abc123" },
49721
49901
  args: {
49722
49902
  id: { type: "positional", description: "Testimonial ID", required: false },
@@ -49753,7 +49933,7 @@ var getCommand6 = defineCommand203({
49753
49933
  });
49754
49934
 
49755
49935
  // src/commands/testimonials/list.ts
49756
- import { defineCommand as defineCommand204 } from "citty";
49936
+ import { defineCommand as defineCommand205 } from "citty";
49757
49937
 
49758
49938
  // src/commands/testimonials/emptyCorpusHints.ts
49759
49939
  function resolveEmptyReason({
@@ -49888,7 +50068,7 @@ function buildListParams(args) {
49888
50068
  }
49889
50069
  return params;
49890
50070
  }
49891
- var listCommand18 = defineCommand204({
50071
+ var listCommand18 = defineCommand205({
49892
50072
  meta: {
49893
50073
  name: "list",
49894
50074
  description: "List testimonials with optional filters. Example: baker testimonials list --source google --sentiment positive"
@@ -49940,7 +50120,7 @@ var listCommand18 = defineCommand204({
49940
50120
  });
49941
50121
 
49942
50122
  // src/commands/testimonials/search.ts
49943
- import { defineCommand as defineCommand205 } from "citty";
50123
+ import { defineCommand as defineCommand206 } from "citty";
49944
50124
  var FILTER_FLAGS2 = ["source", "rating-min", "rating-max", "status", "sentiment", "language", "tags"];
49945
50125
  function languageBiasHint(results, requestedLanguage) {
49946
50126
  if (requestedLanguage) {
@@ -50019,7 +50199,7 @@ function buildSearchRequest(query, args) {
50019
50199
  }
50020
50200
  return body;
50021
50201
  }
50022
- var searchCommand3 = defineCommand205({
50202
+ var searchCommand3 = defineCommand206({
50023
50203
  meta: {
50024
50204
  name: "search",
50025
50205
  description: "Semantic search testimonials by text query. Uses hybrid BM25 + vector + reranking. Example: baker testimonials search 'great service' --rating-min 4"
@@ -50083,7 +50263,7 @@ var searchCommand3 = defineCommand205({
50083
50263
  var tagsCommand5 = makeTagsCommand("testimonials", "testimonial", "/api/testimonials/tags");
50084
50264
 
50085
50265
  // src/commands/testimonials/index.ts
50086
- var testimonialsCommand = defineCommand206({
50266
+ var testimonialsCommand = defineCommand207({
50087
50267
  meta: {
50088
50268
  name: "testimonials",
50089
50269
  description: `Find and browse testimonials in Baker. Subcommands: search, get, list, tags.
@@ -50105,10 +50285,10 @@ Full guide: __tooling__/docs/tools/baker/testimonials.md`
50105
50285
  });
50106
50286
 
50107
50287
  // src/commands/videos/index.ts
50108
- import { defineCommand as defineCommand213 } from "citty";
50288
+ import { defineCommand as defineCommand214 } from "citty";
50109
50289
 
50110
50290
  // src/commands/videos/delete.ts
50111
- import { defineCommand as defineCommand207 } from "citty";
50291
+ import { defineCommand as defineCommand208 } from "citty";
50112
50292
  registerSchema({
50113
50293
  command: "videos.delete",
50114
50294
  description: "Delete a video by ID",
@@ -50122,7 +50302,7 @@ registerSchema({
50122
50302
  }
50123
50303
  }
50124
50304
  });
50125
- var deleteCommand4 = defineCommand207({
50305
+ var deleteCommand4 = defineCommand208({
50126
50306
  meta: {
50127
50307
  name: "delete",
50128
50308
  description: "Delete a video by ID. Use --dry-run to preview. Example: baker videos delete j571abc123 --dry-run"
@@ -50163,7 +50343,7 @@ var deleteCommand4 = defineCommand207({
50163
50343
  });
50164
50344
 
50165
50345
  // src/commands/videos/get.ts
50166
- import { defineCommand as defineCommand208 } from "citty";
50346
+ import { defineCommand as defineCommand209 } from "citty";
50167
50347
  registerSchema({
50168
50348
  command: "videos.get",
50169
50349
  description: "Get a single video by ID",
@@ -50171,7 +50351,7 @@ registerSchema({
50171
50351
  id: { type: "string", description: "Video ID", required: true }
50172
50352
  }
50173
50353
  });
50174
- var getCommand7 = defineCommand208({
50354
+ var getCommand7 = defineCommand209({
50175
50355
  meta: { name: "get", description: "Get a single video by ID. Example: baker videos get j571abc123" },
50176
50356
  args: {
50177
50357
  id: { type: "positional", description: "Video ID", required: false },
@@ -50208,7 +50388,7 @@ var getCommand7 = defineCommand208({
50208
50388
  });
50209
50389
 
50210
50390
  // src/commands/videos/group.ts
50211
- import { defineCommand as defineCommand209 } from "citty";
50391
+ import { defineCommand as defineCommand210 } from "citty";
50212
50392
  registerSchema({
50213
50393
  command: "videos.group",
50214
50394
  description: "List every clip and image that arrived in the same set as this video (carousel slides, one page)",
@@ -50217,7 +50397,7 @@ registerSchema({
50217
50397
  "group-key": { type: "string", description: "The set key directly, when you already have it", required: false }
50218
50398
  }
50219
50399
  });
50220
- var groupCommand2 = defineCommand209({
50400
+ var groupCommand2 = defineCommand210({
50221
50401
  meta: {
50222
50402
  name: "group",
50223
50403
  description: "List every asset that arrived in the same set as this clip \u2014 the other slides of the Instagram post it came from, stills included. A carousel is authored to be read in order, so a clip pulled out of one is usually missing half its meaning. Example: baker videos group <videoId>"
@@ -50239,8 +50419,8 @@ var groupCommand2 = defineCommand209({
50239
50419
  // src/commands/videos/ingest.ts
50240
50420
  import { mkdtemp as mkdtemp2, rm as rm7, stat as stat7 } from "fs/promises";
50241
50421
  import { tmpdir as tmpdir3 } from "os";
50242
- import path37 from "path";
50243
- import { defineCommand as defineCommand210 } from "citty";
50422
+ import path38 from "path";
50423
+ import { defineCommand as defineCommand211 } from "citty";
50244
50424
 
50245
50425
  // src/lib/streamUpload.ts
50246
50426
  import { createHash as createHash2 } from "crypto";
@@ -50404,7 +50584,7 @@ registerSchema({
50404
50584
  "dry-run": { type: "boolean", description: "Preview the operation without executing", required: false }
50405
50585
  }
50406
50586
  });
50407
- var ingestCommand2 = defineCommand210({
50587
+ var ingestCommand2 = defineCommand211({
50408
50588
  meta: {
50409
50589
  name: "ingest",
50410
50590
  description: "Add a video to the library from a URL. A direct file URL is handed straight to Baker, which fetches it. A page URL (YouTube, TikTok, Vimeo, Instagram) is downloaded here first, then uploaded \u2014 and a direct URL that Baker cannot fetch falls back to that same path automatically.\n\nExample: baker videos ingest https://www.youtube.com/watch?v=abc123"
@@ -50590,7 +50770,7 @@ function ingestUrl(args) {
50590
50770
  }
50591
50771
  async function downloadThenIngest(args, country) {
50592
50772
  const vimeoCookie = captureVimeoCookie();
50593
- const workDir = await mkdtemp2(path37.join(tmpdir3(), "videos-ingest-"));
50773
+ const workDir = await mkdtemp2(path38.join(tmpdir3(), "videos-ingest-"));
50594
50774
  try {
50595
50775
  const probe = await probeYtDlp({ url: args.url, country, vimeoCookie, cookieDir: workDir });
50596
50776
  if (isAudioOnly(probe.info)) {
@@ -50666,7 +50846,7 @@ async function uploadToAssetStore(filePath, sizeBytes) {
50666
50846
  }
50667
50847
 
50668
50848
  // src/commands/videos/search.ts
50669
- import { defineCommand as defineCommand211 } from "citty";
50849
+ import { defineCommand as defineCommand212 } from "citty";
50670
50850
  registerSchema({
50671
50851
  command: "videos.search",
50672
50852
  description: "Search videos by text query. Only returns ready videos.",
@@ -50676,7 +50856,7 @@ registerSchema({
50676
50856
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
50677
50857
  }
50678
50858
  });
50679
- var searchCommand4 = defineCommand211({
50859
+ var searchCommand4 = defineCommand212({
50680
50860
  meta: {
50681
50861
  name: "search",
50682
50862
  description: "Semantic search videos by text query. Uses hybrid BM25 + vector + reranking. Example: baker videos search 'product demo' --tags tutorial"
@@ -50726,9 +50906,9 @@ var searchCommand4 = defineCommand211({
50726
50906
  var tagsCommand6 = makeTagsCommand("videos", "video", "/api/videos/tags");
50727
50907
 
50728
50908
  // src/commands/videos/upload.ts
50729
- import { readFile as readFile23, stat as stat8 } from "fs/promises";
50909
+ import { readFile as readFile24, stat as stat8 } from "fs/promises";
50730
50910
  import { basename as basename3, extname as extname4 } from "path";
50731
- import { defineCommand as defineCommand212 } from "citty";
50911
+ import { defineCommand as defineCommand213 } from "citty";
50732
50912
  var MIME_MAP = {
50733
50913
  ".mp4": "video/mp4",
50734
50914
  ".mov": "video/quicktime",
@@ -50770,7 +50950,7 @@ function detectContentType(filePath) {
50770
50950
  function isRemoteUrl3(value) {
50771
50951
  return /^https?:\/\//i.test(value);
50772
50952
  }
50773
- var uploadCommand2 = defineCommand212({
50953
+ var uploadCommand2 = defineCommand213({
50774
50954
  meta: {
50775
50955
  name: "upload",
50776
50956
  description: "Upload a video to Baker \u2014 accepts a local file path OR a remote http(s) URL.\n\nLocal: auto-detects content type and uploads via Mux direct upload.\nRemote: hands off to `videos ingest` (direct fetch, or download-then-upload for a YouTube/TikTok/Vimeo page).\n\nExamples:\n baker videos upload ./demo.mp4\n baker videos upload https://www.youtube.com/watch?v=abc123"
@@ -50823,7 +51003,7 @@ var uploadCommand2 = defineCommand212({
50823
51003
  originalFilename,
50824
51004
  descriptionContext
50825
51005
  });
50826
- const fileBuffer = await readFile23(filePath);
51006
+ const fileBuffer = await readFile24(filePath);
50827
51007
  const uploadResponse = await fetch(uploadUrl, {
50828
51008
  method: "PUT",
50829
51009
  headers: { "Content-Type": contentType },
@@ -50854,7 +51034,7 @@ var uploadCommand2 = defineCommand212({
50854
51034
  });
50855
51035
 
50856
51036
  // src/commands/videos/index.ts
50857
- var videosCommand = defineCommand213({
51037
+ var videosCommand = defineCommand214({
50858
51038
  meta: {
50859
51039
  name: "videos",
50860
51040
  description: `Find and manage videos in Baker. Subcommands: search, get, upload, ingest, delete, tags.
@@ -50881,10 +51061,10 @@ Full guide: __tooling__/docs/tools/baker/videos.md`
50881
51061
  });
50882
51062
 
50883
51063
  // src/commands/winning-ads/index.ts
50884
- import { defineCommand as defineCommand226 } from "citty";
51064
+ import { defineCommand as defineCommand227 } from "citty";
50885
51065
 
50886
51066
  // src/commands/winning-ads/advertisers.ts
50887
- import { defineCommand as defineCommand214 } from "citty";
51067
+ import { defineCommand as defineCommand215 } from "citty";
50888
51068
 
50889
51069
  // src/commands/winning-ads/shared.ts
50890
51070
  function splitList2(value) {
@@ -50937,7 +51117,7 @@ function advertiserNormalizer(record, full) {
50937
51117
  last_synced_at: record.last_synced_at ?? null
50938
51118
  };
50939
51119
  }
50940
- var advertisersCommand2 = defineCommand214({
51120
+ var advertisersCommand2 = defineCommand215({
50941
51121
  meta: {
50942
51122
  name: "advertisers",
50943
51123
  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'
@@ -50995,7 +51175,7 @@ var advertisersCommand2 = defineCommand214({
50995
51175
  });
50996
51176
 
50997
51177
  // src/commands/winning-ads/brief.ts
50998
- import { defineCommand as defineCommand215 } from "citty";
51178
+ import { defineCommand as defineCommand216 } from "citty";
50999
51179
  registerSchema({
51000
51180
  command: "winning-ads.brief",
51001
51181
  description: "Generate a creative brief grounded in strategically-similar winning ads. Optionally describe the target creative with --dna (JSON) and steer with --notes.",
@@ -51041,7 +51221,7 @@ function parseDna(raw) {
51041
51221
  }
51042
51222
  return parsed;
51043
51223
  }
51044
- var briefCommand = defineCommand215({
51224
+ var briefCommand = defineCommand216({
51045
51225
  meta: {
51046
51226
  name: "brief",
51047
51227
  description: `Generate a creative brief from winning references. Example: baker winning-ads brief --dna '{"angle":"cost savings"}' --notes "B2B, LinkedIn video" --k 8`
@@ -51077,7 +51257,7 @@ var briefCommand = defineCommand215({
51077
51257
  });
51078
51258
 
51079
51259
  // src/commands/winning-ads/content.ts
51080
- import { defineCommand as defineCommand216 } from "citty";
51260
+ import { defineCommand as defineCommand217 } from "citty";
51081
51261
  registerSchema({
51082
51262
  command: "winning-ads.content",
51083
51263
  description: "Read what's INSIDE one winning ad: the spoken transcript, the on-screen text, and the ad copy. Use this after `search`/`winners`/`feed` return a shortlist \u2014 pass an ad_id to understand a reference before reproducing it. Add --full for speech, pacing, and soundtrack detail. Video ads carry the transcript/on-screen text; static ads carry only the copy.",
@@ -51090,7 +51270,7 @@ registerSchema({
51090
51270
  }
51091
51271
  }
51092
51272
  });
51093
- var contentCommand = defineCommand216({
51273
+ var contentCommand = defineCommand217({
51094
51274
  meta: {
51095
51275
  name: "content",
51096
51276
  description: "Read the transcript + on-screen text + copy of one winning ad. Example: baker winning-ads content adg_123 --platform meta --full --output md"
@@ -51139,7 +51319,7 @@ var contentCommand = defineCommand216({
51139
51319
  });
51140
51320
 
51141
51321
  // src/commands/winning-ads/feed.ts
51142
- import { defineCommand as defineCommand217 } from "citty";
51322
+ import { defineCommand as defineCommand218 } from "citty";
51143
51323
  function buildFeedParams(input) {
51144
51324
  const params = {};
51145
51325
  const advertiser = splitList2(input.advertiser);
@@ -51191,7 +51371,7 @@ registerSchema({
51191
51371
  format: { type: "string", description: "Comma-separated formats to include (e.g. static,video)", required: false }
51192
51372
  }
51193
51373
  });
51194
- var feedCommand = defineCommand217({
51374
+ var feedCommand = defineCommand218({
51195
51375
  meta: {
51196
51376
  name: "feed",
51197
51377
  description: "Winners across every brand you follow (browse, then trim per advertiser). Example: baker winning-ads feed --per-advertiser 5 --output md"
@@ -51276,7 +51456,7 @@ var feedCommand = defineCommand217({
51276
51456
  });
51277
51457
 
51278
51458
  // src/commands/winning-ads/follow.ts
51279
- import { defineCommand as defineCommand218 } from "citty";
51459
+ import { defineCommand as defineCommand219 } from "citty";
51280
51460
  var PLATFORMS = ["meta", "linkedin"];
51281
51461
  registerSchema({
51282
51462
  command: "winning-ads.follow",
@@ -51291,7 +51471,7 @@ registerSchema({
51291
51471
  label: { type: "string", description: "Optional display label (defaults to the resolved name)", required: false }
51292
51472
  }
51293
51473
  });
51294
- var followCommand = defineCommand218({
51474
+ var followCommand = defineCommand219({
51295
51475
  meta: {
51296
51476
  name: "follow",
51297
51477
  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'
@@ -51338,7 +51518,7 @@ var followCommand = defineCommand218({
51338
51518
  });
51339
51519
 
51340
51520
  // src/commands/winning-ads/follow-competitors.ts
51341
- import { defineCommand as defineCommand219 } from "citty";
51521
+ import { defineCommand as defineCommand220 } from "citty";
51342
51522
  var PLATFORMS2 = ["meta", "linkedin"];
51343
51523
  var BATCH_TIMEOUT_MS = 3e5;
51344
51524
  function buildFollowBatchBody(input) {
@@ -51371,7 +51551,7 @@ registerSchema({
51371
51551
  }
51372
51552
  }
51373
51553
  });
51374
- var followCompetitorsCommand = defineCommand219({
51554
+ var followCompetitorsCommand = defineCommand220({
51375
51555
  meta: {
51376
51556
  name: "follow-competitors",
51377
51557
  description: 'Follow many brands at once by domain \u2014 add every competitor in one call. Example: baker winning-ads follow-competitors "deel.com,notion.so,hubspot.com"'
@@ -51446,7 +51626,7 @@ var followCompetitorsCommand = defineCommand219({
51446
51626
  });
51447
51627
 
51448
51628
  // src/commands/winning-ads/following.ts
51449
- import { defineCommand as defineCommand220 } from "citty";
51629
+ import { defineCommand as defineCommand221 } from "citty";
51450
51630
  registerSchema({
51451
51631
  command: "winning-ads.following",
51452
51632
  description: "List the brands you follow in your ad-dna library, with each one's status (ready vs still adding) and cached ad counts. A brand still adding has counts that are a lie in progress; one with `discovery_failed` has counts that are short because we couldn't finish looking, which is not the same as it running no ads.",
@@ -51500,7 +51680,7 @@ function followingNormalizer(record, full) {
51500
51680
  platforms: Array.isArray(record.platforms) ? record.platforms : []
51501
51681
  };
51502
51682
  }
51503
- var followingCommand = defineCommand220({
51683
+ var followingCommand = defineCommand221({
51504
51684
  meta: {
51505
51685
  name: "following",
51506
51686
  description: "List brands you follow, with status (ready / adding\u2026) and cached counts. A brand's counts are only final once it is ready. Example: baker winning-ads following --output md"
@@ -51536,7 +51716,7 @@ var followingCommand = defineCommand220({
51536
51716
  });
51537
51717
 
51538
51718
  // src/commands/winning-ads/patterns.ts
51539
- import { defineCommand as defineCommand221 } from "citty";
51719
+ import { defineCommand as defineCommand222 } from "citty";
51540
51720
  registerSchema({
51541
51721
  command: "winning-ads.patterns",
51542
51722
  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.",
@@ -51575,7 +51755,7 @@ function discriminatorRow(record) {
51575
51755
  top_values_duds: Array.isArray(record.top_values_b) ? record.top_values_b.join(", ") : ""
51576
51756
  };
51577
51757
  }
51578
- var patternsCommand = defineCommand221({
51758
+ var patternsCommand = defineCommand222({
51579
51759
  meta: {
51580
51760
  name: "patterns",
51581
51761
  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"
@@ -51631,7 +51811,7 @@ var patternsCommand = defineCommand221({
51631
51811
  });
51632
51812
 
51633
51813
  // src/commands/winning-ads/search.ts
51634
- import { defineCommand as defineCommand222 } from "citty";
51814
+ import { defineCommand as defineCommand223 } from "citty";
51635
51815
  registerSchema({
51636
51816
  command: "winning-ads.search",
51637
51817
  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.",
@@ -51739,7 +51919,7 @@ function buildSearchBody2(args) {
51739
51919
  }
51740
51920
  return body;
51741
51921
  }
51742
- var searchCommand5 = defineCommand222({
51922
+ var searchCommand5 = defineCommand223({
51743
51923
  meta: {
51744
51924
  name: "search",
51745
51925
  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"
@@ -51854,7 +52034,7 @@ var searchCommand5 = defineCommand222({
51854
52034
  });
51855
52035
 
51856
52036
  // src/commands/winning-ads/seeds.ts
51857
- import { defineCommand as defineCommand223 } from "citty";
52037
+ import { defineCommand as defineCommand224 } from "citty";
51858
52038
  function leanRow(r) {
51859
52039
  return {
51860
52040
  key: r.key,
@@ -51882,7 +52062,7 @@ function makeSeedCommand(opts) {
51882
52062
  limit: { type: "number", description: "Max keys 1-100 (default 20)", required: false, default: 20 }
51883
52063
  }
51884
52064
  });
51885
- return defineCommand223({
52065
+ return defineCommand224({
51886
52066
  meta: { name: opts.name, description: opts.description },
51887
52067
  args: {
51888
52068
  platform: { type: "string", description: "Single platform to segment on", required: false },
@@ -51931,7 +52111,7 @@ var formatsCommand = makeSeedCommand({
51931
52111
  });
51932
52112
 
51933
52113
  // src/commands/winning-ads/unfollow.ts
51934
- import { defineCommand as defineCommand224 } from "citty";
52114
+ import { defineCommand as defineCommand225 } from "citty";
51935
52115
  registerSchema({
51936
52116
  command: "winning-ads.unfollow",
51937
52117
  description: "Stop following a brand \u2014 removes it from your ad-dna library by advertiser id.",
@@ -51939,7 +52119,7 @@ registerSchema({
51939
52119
  advertiser: { type: "string", description: "Advertiser id to unfollow", required: true }
51940
52120
  }
51941
52121
  });
51942
- var unfollowCommand = defineCommand224({
52122
+ var unfollowCommand = defineCommand225({
51943
52123
  meta: {
51944
52124
  name: "unfollow",
51945
52125
  description: "Stop following a brand by advertiser id. Example: baker winning-ads unfollow adv_123"
@@ -51960,7 +52140,7 @@ var unfollowCommand = defineCommand224({
51960
52140
  });
51961
52141
 
51962
52142
  // src/commands/winning-ads/winners.ts
51963
- import { defineCommand as defineCommand225 } from "citty";
52143
+ import { defineCommand as defineCommand226 } from "citty";
51964
52144
  registerSchema({
51965
52145
  command: "winning-ads.winners",
51966
52146
  description: "Top winning ads for one advertiser id (from `advertisers` or `following`). Returns lean winner cards; add --full for DNA + longevity.",
@@ -51970,7 +52150,7 @@ registerSchema({
51970
52150
  platform: { type: "string", description: "Filter to a single platform: meta|linkedin", required: false }
51971
52151
  }
51972
52152
  });
51973
- var winnersCommand = defineCommand225({
52153
+ var winnersCommand = defineCommand226({
51974
52154
  meta: {
51975
52155
  name: "winners",
51976
52156
  description: "Top winning ads for a specific advertiser id. Example: baker winning-ads winners adv_123 --top 15 --output md"
@@ -52020,7 +52200,7 @@ var winnersCommand = defineCommand225({
52020
52200
  });
52021
52201
 
52022
52202
  // src/commands/winning-ads/index.ts
52023
- var winningAdsCommand = defineCommand226({
52203
+ var winningAdsCommand = defineCommand227({
52024
52204
  meta: {
52025
52205
  name: "winning-ads",
52026
52206
  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.
@@ -52210,7 +52390,7 @@ function unknownFlagEnvelope(unknown, commandPath, suggestion) {
52210
52390
  };
52211
52391
  }
52212
52392
  function commandPathOf(root, argv) {
52213
- const path38 = [];
52393
+ const path39 = [];
52214
52394
  let command = root;
52215
52395
  for (const token of argv) {
52216
52396
  if (token === "--" || token.startsWith("-")) {
@@ -52221,10 +52401,10 @@ function commandPathOf(root, argv) {
52221
52401
  if (next === void 0 || typeof next !== "object") {
52222
52402
  break;
52223
52403
  }
52224
- path38.push(token);
52404
+ path39.push(token);
52225
52405
  command = next;
52226
52406
  }
52227
- return path38.join(" ");
52407
+ return path39.join(" ");
52228
52408
  }
52229
52409
  function refuseUnknownFlags(root, argv) {
52230
52410
  const unknown = findUnknownFlags(root, argv);
@@ -52258,7 +52438,7 @@ function getCliVersion() {
52258
52438
  }
52259
52439
 
52260
52440
  // src/cli.ts
52261
- var main = defineCommand227({
52441
+ var main = defineCommand228({
52262
52442
  meta: {
52263
52443
  name: "baker",
52264
52444
  version: getCliVersion(),