@koda-sl/baker-cli 0.244.0 → 0.245.0-dev.75adc1879

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -58,7 +58,7 @@ import {
58
58
  ulid,
59
59
  validateCanvasDeep,
60
60
  ytDlpBlockSignal
61
- } from "./chunk-EKLAHWSF.js";
61
+ } from "./chunk-CMPAHYLB.js";
62
62
  import {
63
63
  csvOrJson,
64
64
  daysAgoIso,
@@ -108,7 +108,7 @@ import {
108
108
  } from "./chunk-DZUVUGEP.js";
109
109
 
110
110
  // src/cli.ts
111
- import { defineCommand as defineCommand227, runMain } from "citty";
111
+ import { defineCommand as defineCommand229, runMain } from "citty";
112
112
 
113
113
  // src/cache-flag.ts
114
114
  var NO_CACHE_ARG = {
@@ -2122,18 +2122,6 @@ var BIDDING_STRATEGY_TYPES = [
2122
2122
  "MANUAL_CPV",
2123
2123
  "PERCENT_CPC"
2124
2124
  ];
2125
- var MERGED_TARGET_STRATEGY_CHANNELS = /* @__PURE__ */ new Set(["SEARCH"]);
2126
- var MERGED_TARGET_CPA_MESSAGE = "Google doesn't accept Target CPA as a bidding strategy of its own on a Search campaign \u2014 the target sits inside Maximize conversions. Choose Maximize conversions and give it the same target CPA; it bids exactly the same.";
2127
- var MERGED_TARGET_ROAS_MESSAGE = "Google doesn't accept Target ROAS as a bidding strategy of its own on a Search campaign \u2014 the target sits inside Maximize conversion value. Choose Maximize conversion value and give it the same target ROAS; it bids exactly the same.";
2128
- function mergedTargetStrategyRefusal(channelType, biddingType) {
2129
- if (channelType === void 0 || !MERGED_TARGET_STRATEGY_CHANNELS.has(channelType)) {
2130
- return void 0;
2131
- }
2132
- if (biddingType === "TARGET_CPA") {
2133
- return MERGED_TARGET_CPA_MESSAGE;
2134
- }
2135
- return biddingType === "TARGET_ROAS" ? MERGED_TARGET_ROAS_MESSAGE : void 0;
2136
- }
2137
2125
  var POSITIVE_GEO_TARGET_TYPES = ["PRESENCE_OR_INTEREST", "PRESENCE"];
2138
2126
  var NEGATIVE_GEO_TARGET_TYPES = ["PRESENCE_OR_INTEREST", "PRESENCE"];
2139
2127
  var BUDGET_DELIVERY_METHODS = ["STANDARD", "ACCELERATED"];
@@ -2858,10 +2846,6 @@ var campaignCreateSchema2 = z4.strictObject({
2858
2846
  if (p.channelType === "PERFORMANCE_MAX" && p.bidding && p.bidding.type === "MANUAL_CPC") {
2859
2847
  ctx.addIssue({ code: "custom", path: ["bidding"], message: "Performance Max does not support Manual CPC" });
2860
2848
  }
2861
- const mergedTarget = mergedTargetStrategyRefusal(p.channelType, p.bidding?.type);
2862
- if (mergedTarget) {
2863
- ctx.addIssue({ code: "custom", path: ["bidding"], message: mergedTarget });
2864
- }
2865
2849
  if (p.startDate && p.endDate && p.endDate <= p.startDate) {
2866
2850
  ctx.addIssue({ code: "custom", path: ["endDate"], message: "endDate must be after startDate" });
2867
2851
  }
@@ -3936,10 +3920,35 @@ var capabilitySurfaceReportSchema = z9.object({
3936
3920
  /** Where to look next: `baker ads google`, `baker ga4`, … */
3937
3921
  commands: z9.string()
3938
3922
  });
3923
+ var capabilityToolSchema = z9.object({
3924
+ /** `microsoft_clarity`, `hubspot`, or the name the user gave a custom server. */
3925
+ key: z9.string(),
3926
+ label: z9.string(),
3927
+ /** `mcp` — tools in the agent's own list. `platform` — signed in, read through the CLI. */
3928
+ kind: z9.enum(["mcp", "platform"]),
3929
+ /** How to actually reach it, in one phrase the agent can act on. */
3930
+ reach: z9.string(),
3931
+ /**
3932
+ * Whether it can be used right now.
3933
+ *
3934
+ * A connection that exists and is expired is worse than one that is absent:
3935
+ * it reads as available and fails at the moment it is needed, which on an
3936
+ * unattended run is a report with a hole in it and no explanation.
3937
+ */
3938
+ ready: z9.boolean(),
3939
+ /** Why it is not ready, or what it is for. Absent when neither needs saying. */
3940
+ note: z9.string().optional()
3941
+ });
3939
3942
  var capabilitiesResponseSchema = z9.object({
3940
3943
  ok: z9.literal(true),
3941
3944
  data: z9.object({
3942
- surfaces: z9.array(capabilitySurfaceReportSchema)
3945
+ surfaces: z9.array(capabilitySurfaceReportSchema),
3946
+ /**
3947
+ * Everything else this company can reach. Shared connections only —
3948
+ * a teammate's private per-user connection belongs to whoever started the
3949
+ * session and is visible in the agent's own tool list, not here.
3950
+ */
3951
+ tools: z9.array(capabilityToolSchema)
3943
3952
  })
3944
3953
  });
3945
3954
 
@@ -3986,6 +3995,8 @@ var chatChangeTypeSchema = z10.enum([
3986
3995
  // what the edge captures and one held until Publish would not measure the
3987
3996
  // campaign it was written for.
3988
3997
  "analytics-mapping",
3998
+ // An A/B test between a landing and its alternative. Staged, not immediate.
3999
+ "experiment",
3989
4000
  "briefs"
3990
4001
  ]);
3991
4002
  var chatChangeActionSchema = z10.enum(["created", "updated", "deleted"]);
@@ -9779,11 +9790,11 @@ function rawTextEntries(value) {
9779
9790
  const values = Array.isArray(value) ? value : typeof value === "string" ? [value] : [];
9780
9791
  return values.filter((v) => typeof v === "string").flatMap((v) => v.split(",")).map((v) => v.trim()).filter(Boolean);
9781
9792
  }
9782
- function rawFileEntries(path40) {
9783
- if (typeof path40 !== "string" || path40.length === 0) {
9793
+ function rawFileEntries(path43) {
9794
+ if (typeof path43 !== "string" || path43.length === 0) {
9784
9795
  return [];
9785
9796
  }
9786
- return readFileSync2(path40, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
9797
+ return readFileSync2(path43, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
9787
9798
  }
9788
9799
  function keywordEntries(args) {
9789
9800
  const defaultMatch = typeof args["match-type"] === "string" ? args["match-type"].toUpperCase() : void 0;
@@ -9806,19 +9817,19 @@ function keywordEntries(args) {
9806
9817
  }
9807
9818
  return entries;
9808
9819
  }
9809
- function loadJsonFileArg(path40) {
9810
- if (typeof path40 !== "string" || path40.length === 0) {
9820
+ function loadJsonFileArg(path43) {
9821
+ if (typeof path43 !== "string" || path43.length === 0) {
9811
9822
  return {};
9812
9823
  }
9813
9824
  try {
9814
- const parsed = JSON.parse(readFileSync2(path40, "utf8"));
9825
+ const parsed = JSON.parse(readFileSync2(path43, "utf8"));
9815
9826
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
9816
- failWriteValidation(`${path40} must contain a JSON object`);
9827
+ failWriteValidation(`${path43} must contain a JSON object`);
9817
9828
  }
9818
9829
  return parsed;
9819
9830
  } catch (err) {
9820
9831
  if (err instanceof SyntaxError) {
9821
- failWriteValidation(`${path40} is not valid JSON: ${err.message}`);
9832
+ failWriteValidation(`${path43} is not valid JSON: ${err.message}`);
9822
9833
  }
9823
9834
  throw err;
9824
9835
  }
@@ -9948,10 +9959,10 @@ async function stageUpdate(kind, customerId, target, payload, hints) {
9948
9959
  async function stageTarget(kind, customerId, target, hints) {
9949
9960
  await stageGoogleOp({ kind, customerId, target }, hints);
9950
9961
  }
9951
- async function draftAction(path40, body, chat) {
9962
+ async function draftAction(path43, body, chat) {
9952
9963
  try {
9953
9964
  const chatId = resolveChatId(chat);
9954
- const response = await apiPost(path40, { chatId, ...body });
9965
+ const response = await apiPost(path43, { chatId, ...body });
9955
9966
  writeJsonEnvelope(response);
9956
9967
  } catch (err) {
9957
9968
  handleGoogleError(err);
@@ -13644,16 +13655,10 @@ var campaignsCommand = defineCommand30({
13644
13655
  "budget-ref": { type: "string", description: "Budget ref (g_temp_*, id, or resource name)" },
13645
13656
  "bidding-strategy": {
13646
13657
  type: "string",
13647
- description: "MANUAL_CPC | MAXIMIZE_CONVERSIONS | MAXIMIZE_CONVERSION_VALUE | TARGET_SPEND | \u2026"
13648
- },
13649
- "target-cpa": {
13650
- type: "string",
13651
- description: "Target CPA (major units). On a Search campaign pair it with --bidding-strategy MAXIMIZE_CONVERSIONS \u2014 Google keeps the target inside that strategy and rejects a standalone TARGET_CPA."
13652
- },
13653
- "target-roas": {
13654
- type: "string",
13655
- description: "Target ROAS (e.g. 4.0). On a Search campaign pair it with --bidding-strategy MAXIMIZE_CONVERSION_VALUE \u2014 Google keeps the target inside that strategy and rejects a standalone TARGET_ROAS."
13658
+ description: "MANUAL_CPC | MAXIMIZE_CONVERSIONS | TARGET_CPA | TARGET_ROAS | \u2026"
13656
13659
  },
13660
+ "target-cpa": { type: "string", description: "Target CPA (major units) for TARGET_CPA" },
13661
+ "target-roas": { type: "string", description: "Target ROAS (e.g. 4.0) for TARGET_ROAS" },
13657
13662
  ...maxCpcArg,
13658
13663
  ...finalUrlSuffixArg,
13659
13664
  ...urlOptionsArgs,
@@ -13700,18 +13705,9 @@ var campaignsCommand = defineCommand30({
13700
13705
  ...fileArg,
13701
13706
  name: { type: "string" },
13702
13707
  "budget-ref": { type: "string" },
13703
- "bidding-strategy": {
13704
- type: "string",
13705
- description: "MANUAL_CPC | MAXIMIZE_CONVERSIONS | MAXIMIZE_CONVERSION_VALUE | TARGET_SPEND | \u2026"
13706
- },
13707
- "target-cpa": {
13708
- type: "string",
13709
- description: "Target CPA (major units). On a Search campaign pair it with --bidding-strategy MAXIMIZE_CONVERSIONS \u2014 Google keeps the target inside that strategy and rejects a standalone TARGET_CPA."
13710
- },
13711
- "target-roas": {
13712
- type: "string",
13713
- description: "Target ROAS (e.g. 4.0). On a Search campaign pair it with --bidding-strategy MAXIMIZE_CONVERSION_VALUE \u2014 Google keeps the target inside that strategy and rejects a standalone TARGET_ROAS."
13714
- },
13708
+ "bidding-strategy": { type: "string" },
13709
+ "target-cpa": { type: "string" },
13710
+ "target-roas": { type: "string" },
13715
13711
  ...maxCpcArg,
13716
13712
  ...finalUrlSuffixArg,
13717
13713
  ...urlOptionsArgs,
@@ -14456,8 +14452,7 @@ function requireAudienceLevel(value) {
14456
14452
  return level;
14457
14453
  }
14458
14454
  var AUDIENCE_ATTACH_HINTS = [
14459
- 'Read back what the parent ended up with before you report: `baker ads google query "SELECT campaign.id, campaign.targeting_setting.target_restrictions FROM campaign"` (or the ad_group equivalent). A missing AUDIENCE row means TARGETING \u2014 Google does not serialize an empty setting, so absence is a value, not a gap.',
14460
- "Staging checks the list is big enough to serve on this campaign's network (1,000 users on search, 100 on display) and refuses a targeting attach that is not. It does NOT check delivery afterwards, and Google reports no error when an audience stops a campaign serving \u2014 after publishing, read the campaign's impressions for the hour that follows and say what you saw."
14455
+ 'Read back what the parent ended up with before you report: `baker ads google query "SELECT campaign.id, campaign.targeting_setting.target_restrictions FROM campaign"` (or the ad_group equivalent). A missing AUDIENCE row means TARGETING \u2014 Google does not serialize an empty setting, so absence is a value, not a gap.'
14461
14456
  ];
14462
14457
  var audiencesAttachCommand = defineCommand30({
14463
14458
  meta: {
@@ -14916,19 +14911,19 @@ function failWriteValidation2(message) {
14916
14911
  writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
14917
14912
  process.exit(1);
14918
14913
  }
14919
- function loadJsonFileArg2(path40) {
14920
- if (typeof path40 !== "string" || path40.length === 0) {
14914
+ function loadJsonFileArg2(path43) {
14915
+ if (typeof path43 !== "string" || path43.length === 0) {
14921
14916
  return {};
14922
14917
  }
14923
14918
  try {
14924
- const parsed = JSON.parse(readFileSync4(path40, "utf8"));
14919
+ const parsed = JSON.parse(readFileSync4(path43, "utf8"));
14925
14920
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
14926
- failWriteValidation2(`${path40} must contain a JSON object`);
14921
+ failWriteValidation2(`${path43} must contain a JSON object`);
14927
14922
  }
14928
14923
  return parsed;
14929
14924
  } catch (err) {
14930
14925
  if (err instanceof SyntaxError) {
14931
- failWriteValidation2(`${path40} is not valid JSON: ${err.message}`);
14926
+ failWriteValidation2(`${path43} is not valid JSON: ${err.message}`);
14932
14927
  }
14933
14928
  throw err;
14934
14929
  }
@@ -15013,15 +15008,15 @@ function parseLocaleFlag(value) {
15013
15008
  }
15014
15009
  return { language: match[1], country: match[2].toUpperCase() };
15015
15010
  }
15016
- function loadTargetingFileArg(path40) {
15017
- if (typeof path40 !== "string" || path40.length === 0) {
15011
+ function loadTargetingFileArg(path43) {
15012
+ if (typeof path43 !== "string" || path43.length === 0) {
15018
15013
  return void 0;
15019
15014
  }
15020
- const parsed = loadJsonFileArg2(path40);
15015
+ const parsed = loadJsonFileArg2(path43);
15021
15016
  const criteria = parsed.targetingCriteria ?? parsed;
15022
15017
  if (!criteria.include) {
15023
15018
  failWriteValidation2(
15024
- `${path40} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
15019
+ `${path43} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
15025
15020
  );
15026
15021
  }
15027
15022
  return criteria;
@@ -15056,14 +15051,14 @@ function parseCsvLine(line) {
15056
15051
  cells.push(current);
15057
15052
  return cells.map((cell2) => cell2.trim());
15058
15053
  }
15059
- function parseListFileArg(path40, maxRows) {
15060
- if (typeof path40 !== "string" || path40.length === 0) {
15054
+ function parseListFileArg(path43, maxRows) {
15055
+ if (typeof path43 !== "string" || path43.length === 0) {
15061
15056
  return void 0;
15062
15057
  }
15063
- const raw = readFileSync4(path40, "utf8");
15058
+ const raw = readFileSync4(path43, "utf8");
15064
15059
  const lines = raw.split(/\r?\n/).filter((line) => line.trim().length > 0);
15065
15060
  if (lines.length < 2) {
15066
- failWriteValidation2(`${path40} needs a header row and at least one data row`);
15061
+ failWriteValidation2(`${path43} needs a header row and at least one data row`);
15067
15062
  }
15068
15063
  const columns = parseCsvLine(lines[0]).map((column) => column.trim());
15069
15064
  const rows = [];
@@ -15082,7 +15077,7 @@ function parseListFileArg(path40, maxRows) {
15082
15077
  }
15083
15078
  }
15084
15079
  if (rows.length > maxRows) {
15085
- failWriteValidation2(`${path40} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
15080
+ failWriteValidation2(`${path43} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
15086
15081
  }
15087
15082
  return { columns, rows };
15088
15083
  }
@@ -15178,11 +15173,11 @@ function readPositionals(args) {
15178
15173
  function splitIdList(raw) {
15179
15174
  return raw.split(",").map((id) => id.trim()).filter(Boolean);
15180
15175
  }
15181
- function idsFileEntries(path40) {
15182
- if (typeof path40 !== "string" || path40.length === 0) {
15176
+ function idsFileEntries(path43) {
15177
+ if (typeof path43 !== "string" || path43.length === 0) {
15183
15178
  return [];
15184
15179
  }
15185
- return readFileSync4(path40, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).flatMap(splitIdList);
15180
+ return readFileSync4(path43, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).flatMap(splitIdList);
15186
15181
  }
15187
15182
  function requireTargets(args, entity) {
15188
15183
  const positionals = readPositionals(args);
@@ -17803,9 +17798,9 @@ function compactRow(row) {
17803
17798
  ...destination.postUrn ? { postUrn: destination.postUrn } : {}
17804
17799
  };
17805
17800
  }
17806
- function readPath(row, path40) {
17801
+ function readPath(row, path43) {
17807
17802
  let current = row;
17808
- for (const segment of path40.split(".")) {
17803
+ for (const segment of path43.split(".")) {
17809
17804
  const record = asRecord2(current);
17810
17805
  if (!record) return void 0;
17811
17806
  current = record[segment];
@@ -17815,10 +17810,10 @@ function readPath(row, path40) {
17815
17810
  function projectFields(rows, paths) {
17816
17811
  return rows.map((row) => {
17817
17812
  const projected = {};
17818
- for (const path40 of paths) {
17819
- const value = readPath(row, path40);
17813
+ for (const path43 of paths) {
17814
+ const value = readPath(row, path43);
17820
17815
  if (value !== void 0) {
17821
- projected[path40] = value;
17816
+ projected[path43] = value;
17822
17817
  }
17823
17818
  }
17824
17819
  return projected;
@@ -19118,11 +19113,11 @@ var updateStatusSchema = z25.enum(UPDATE_STATUSES);
19118
19113
  function currencyMinimums2(currencyCode) {
19119
19114
  return CURRENCY_MINIMUMS2[currencyCode] ?? DEFAULT_CURRENCY_MINIMUM2;
19120
19115
  }
19121
- function validateDailyBudgetFloor(money, ctx, path40) {
19116
+ function validateDailyBudgetFloor(money, ctx, path43) {
19122
19117
  if (money?.currencyCode) {
19123
19118
  const min = currencyMinimums2(money.currencyCode).dailyBudgetMin;
19124
19119
  if (Number(money.amount) < min) {
19125
- ctx.addIssue({ code: "custom", path: path40, message: `below the ${min} ${money.currencyCode} daily minimum` });
19120
+ ctx.addIssue({ code: "custom", path: path43, message: `below the ${min} ${money.currencyCode} daily minimum` });
19126
19121
  }
19127
19122
  }
19128
19123
  }
@@ -19791,19 +19786,19 @@ function failWriteValidation3(message) {
19791
19786
  writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
19792
19787
  process.exit(1);
19793
19788
  }
19794
- function loadJsonFileArg3(path40) {
19795
- if (typeof path40 !== "string" || path40.length === 0) {
19789
+ function loadJsonFileArg3(path43) {
19790
+ if (typeof path43 !== "string" || path43.length === 0) {
19796
19791
  return {};
19797
19792
  }
19798
19793
  try {
19799
- const parsed = JSON.parse(readFileSync8(path40, "utf8"));
19794
+ const parsed = JSON.parse(readFileSync8(path43, "utf8"));
19800
19795
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
19801
- failWriteValidation3(`${path40} must contain a JSON object`);
19796
+ failWriteValidation3(`${path43} must contain a JSON object`);
19802
19797
  }
19803
19798
  return parsed;
19804
19799
  } catch (err) {
19805
19800
  if (err instanceof SyntaxError) {
19806
- failWriteValidation3(`${path40} is not valid JSON: ${err.message}`);
19801
+ failWriteValidation3(`${path43} is not valid JSON: ${err.message}`);
19807
19802
  }
19808
19803
  throw err;
19809
19804
  }
@@ -24401,11 +24396,11 @@ function unwrap(response) {
24401
24396
  }
24402
24397
  return response.data;
24403
24398
  }
24404
- async function readAvatars(path40, params) {
24405
- return unwrap(await apiGet(path40, params));
24399
+ async function readAvatars(path43, params) {
24400
+ return unwrap(await apiGet(path43, params));
24406
24401
  }
24407
- async function writeAvatars(path40, body) {
24408
- return unwrap(await apiPost(path40, body));
24402
+ async function writeAvatars(path43, body) {
24403
+ return unwrap(await apiPost(path43, body));
24409
24404
  }
24410
24405
 
24411
24406
  // src/commands/avatars/create.ts
@@ -25239,12 +25234,12 @@ function missingFontFiles(urls, available) {
25239
25234
  function planFontAdoption(sources, families) {
25240
25235
  const wanted = new Map(families.map((family) => [normalizeFamily(family), family]));
25241
25236
  const byFamily = /* @__PURE__ */ new Map();
25242
- for (const { path: path40, source } of sources) {
25243
- const dir = posix.dirname(path40);
25237
+ for (const { path: path43, source } of sources) {
25238
+ const dir = posix.dirname(path43);
25244
25239
  for (const face of declaredFontFaces(source)) {
25245
25240
  if (!wanted.has(face.family)) continue;
25246
25241
  const perFile = byFamily.get(face.family) ?? /* @__PURE__ */ new Map();
25247
- perFile.set(path40, [...perFile.get(path40) ?? [], rebaseFontFaceSrc(face.block, dir)]);
25242
+ perFile.set(path43, [...perFile.get(path43) ?? [], rebaseFontFaceSrc(face.block, dir)]);
25248
25243
  byFamily.set(face.family, perFile);
25249
25244
  }
25250
25245
  }
@@ -26268,10 +26263,10 @@ import { defineCommand as defineCommand100 } from "citty";
26268
26263
  import { existsSync as existsSync3, realpathSync } from "fs";
26269
26264
  import { writeFile as writeFile2 } from "fs/promises";
26270
26265
  import path5 from "path";
26271
- function findWorkspaceRoot(startDir, exists = existsSync3, maxDepth = 12) {
26266
+ function findWorkspaceRoot(startDir, exists2 = existsSync3, maxDepth = 12) {
26272
26267
  let dir = path5.resolve(startDir);
26273
26268
  for (let i = 0; i < maxDepth; i++) {
26274
- if (exists(path5.join(dir, "package.json"))) return dir;
26269
+ if (exists2(path5.join(dir, "package.json"))) return dir;
26275
26270
  const parent = path5.dirname(dir);
26276
26271
  if (parent === dir) break;
26277
26272
  dir = parent;
@@ -30218,10 +30213,10 @@ function runDirsToPrune(entries, keep, currentRunId) {
30218
30213
  return runs.slice(0, Math.max(0, runs.length - keep));
30219
30214
  }
30220
30215
  async function pruneOldRuns(outputsDir, keep, currentRunId, log) {
30221
- const { readdir: readdir10 } = await import("fs/promises");
30216
+ const { readdir: readdir11 } = await import("fs/promises");
30222
30217
  let entries;
30223
30218
  try {
30224
- entries = await readdir10(outputsDir);
30219
+ entries = await readdir11(outputsDir);
30225
30220
  } catch {
30226
30221
  return;
30227
30222
  }
@@ -32013,12 +32008,12 @@ async function detectSceneCutsPySceneDetect(filePath, opts = {}) {
32013
32008
  // src/commands/canvas/composition-path.ts
32014
32009
  import { existsSync as existsSync4 } from "fs";
32015
32010
  import path21 from "path";
32016
- function resolveShippedCanvasDir(name, startDir, exists = existsSync4, maxDepth = 8) {
32011
+ function resolveShippedCanvasDir(name, startDir, exists2 = existsSync4, maxDepth = 8) {
32017
32012
  const rel = path21.join("canvas", name);
32018
32013
  let dir = startDir;
32019
32014
  for (let i = 0; i < maxDepth; i++) {
32020
32015
  const candidate = path21.join(dir, rel);
32021
- if (exists(path21.join(candidate, "meta.json"))) return candidate;
32016
+ if (exists2(path21.join(candidate, "meta.json"))) return candidate;
32022
32017
  const parent = path21.dirname(dir);
32023
32018
  if (parent === dir) break;
32024
32019
  dir = parent;
@@ -32844,7 +32839,7 @@ registerSchema({
32844
32839
  }
32845
32840
  }
32846
32841
  });
32847
- function capabilityHints(surfaces) {
32842
+ function capabilityHints(surfaces, tools = []) {
32848
32843
  const hints = [];
32849
32844
  const immediate = surfaces.filter((surface) => surface.writeMode === "applies-at-publish");
32850
32845
  if (immediate.length > 0) {
@@ -32867,6 +32862,18 @@ function capabilityHints(surfaces) {
32867
32862
  "Naming one surface (`baker capabilities google-ads`) adds `ops` \u2014 the exact fields and accepted values of every change there. Read that instead of guessing a field name."
32868
32863
  );
32869
32864
  }
32865
+ const usable = tools.filter((tool) => tool.ready);
32866
+ if (usable.length > 0) {
32867
+ hints.push(
32868
+ `ALSO AVAILABLE, and not on any surface above: ${usable.map((tool) => tool.label).join(", ")}. These answer WHY a page behaves as it does, which no surface here can. See \`tools[].reach\` for how to call each one.`
32869
+ );
32870
+ }
32871
+ const broken = tools.filter((tool) => !tool.ready);
32872
+ if (broken.length > 0) {
32873
+ hints.push(
32874
+ `CONNECTED BUT UNUSABLE: ${broken.map((tool) => `${tool.label} \u2014 ${tool.note ?? "not reachable"}`).join(" ")} Tell the user rather than planning around it silently.`
32875
+ );
32876
+ }
32870
32877
  return hints;
32871
32878
  }
32872
32879
  var runCapabilities = defineCommand108({
@@ -32876,6 +32883,8 @@ var runCapabilities = defineCommand108({
32876
32883
 
32877
32884
  Reports, per surface: what is connected and with what access, what a write there does when it lands, the entities that can be changed, and \u2014 the part nothing else tells you \u2014 what cannot be changed and why.
32878
32885
 
32886
+ Also returns \`tools\`: everything else this company can reach that is not one of those surfaces \u2014 Microsoft Clarity and any other MCP toolkit, custom servers, HubSpot. This is the one call that answers "what do I actually have".
32887
+
32879
32888
  Examples:
32880
32889
  baker capabilities
32881
32890
  baker capabilities google-ads
@@ -32911,7 +32920,7 @@ Full guide: __tooling__/docs/tools/baker/capabilities.md`
32911
32920
  body.full = true;
32912
32921
  }
32913
32922
  const response = await apiPost("/api/capabilities", body);
32914
- const hints = capabilityHints(response.data.surfaces);
32923
+ const hints = capabilityHints(response.data.surfaces, response.data.tools);
32915
32924
  writeJson({
32916
32925
  ...response,
32917
32926
  meta: { count: response.data.surfaces.length },
@@ -33381,12 +33390,793 @@ Full guide: __tooling__/docs/tools/baker/creatives.md`
33381
33390
  }
33382
33391
  });
33383
33392
 
33393
+ // src/commands/experiment/index.ts
33394
+ import { defineCommand as defineCommand112 } from "citty";
33395
+
33396
+ // src/commands/landing/import-graph.ts
33397
+ import { readdir as readdir8, readFile as readFile20, stat as stat4 } from "fs/promises";
33398
+ import path26 from "path";
33399
+ var SOURCE_EXT = /\.(astro|ts|tsx|js|jsx|mjs|cjs|vue|svelte)$/;
33400
+ var RESOLVE_CANDIDATES = [
33401
+ "",
33402
+ ".astro",
33403
+ ".ts",
33404
+ ".tsx",
33405
+ ".js",
33406
+ ".jsx",
33407
+ ".mjs",
33408
+ ".cjs",
33409
+ "/index.astro",
33410
+ "/index.ts",
33411
+ "/index.js"
33412
+ ];
33413
+ var IMPORT_RE = /(?<![A-Za-z0-9_$])(?:import|export)\s*(?:[\s\S]*?\bfrom\s*)?["']([^"']+)["']/g;
33414
+ var DYNAMIC_IMPORT_RE = /\bimport\s*\(\s*["']([^"']+)["']\s*\)/g;
33415
+ async function isFile(p) {
33416
+ try {
33417
+ return (await stat4(p)).isFile();
33418
+ } catch {
33419
+ return false;
33420
+ }
33421
+ }
33422
+ async function walkFiles(dir) {
33423
+ let entries;
33424
+ try {
33425
+ entries = await readdir8(dir, { withFileTypes: true });
33426
+ } catch {
33427
+ return [];
33428
+ }
33429
+ const out = [];
33430
+ for (const entry of entries) {
33431
+ const full = path26.join(dir, entry.name);
33432
+ if (entry.isDirectory()) {
33433
+ if (entry.name === "node_modules" || entry.name === "dist") continue;
33434
+ out.push(...await walkFiles(full));
33435
+ } else if (SOURCE_EXT.test(entry.name)) {
33436
+ out.push(full);
33437
+ }
33438
+ }
33439
+ return out;
33440
+ }
33441
+ async function resolveSpecifier(specifier, fromFile, root) {
33442
+ const bare = specifier.split("?")[0] ?? "";
33443
+ if (bare.includes("*")) return null;
33444
+ let base;
33445
+ if (bare.startsWith("/")) base = path26.join(root, bare.slice(1));
33446
+ else if (bare.startsWith(".")) base = path26.resolve(path26.dirname(fromFile), bare);
33447
+ else return null;
33448
+ for (const suffix of RESOLVE_CANDIDATES) {
33449
+ const candidate = base + suffix;
33450
+ if (SOURCE_EXT.test(candidate) && await isFile(candidate)) return candidate;
33451
+ }
33452
+ return null;
33453
+ }
33454
+ async function importedFiles(text2, file, root) {
33455
+ const out = [];
33456
+ for (const re2 of [IMPORT_RE, DYNAMIC_IMPORT_RE]) {
33457
+ for (const match of text2.matchAll(re2)) {
33458
+ const spec = match[1];
33459
+ if (spec === void 0) continue;
33460
+ const target = await resolveSpecifier(spec, file, root);
33461
+ if (target !== null) out.push(target);
33462
+ }
33463
+ }
33464
+ return out;
33465
+ }
33466
+ async function landingGraphFiles(root, slug) {
33467
+ const seen = /* @__PURE__ */ new Set();
33468
+ const queue = await walkFiles(path26.join(root, "src/pages", slug));
33469
+ while (queue.length > 0) {
33470
+ const file = queue.pop();
33471
+ if (file === void 0 || seen.has(file)) continue;
33472
+ let text2;
33473
+ try {
33474
+ text2 = await readFile20(file, "utf8");
33475
+ } catch {
33476
+ continue;
33477
+ }
33478
+ seen.add(file);
33479
+ for (const target of await importedFiles(text2, file, root)) {
33480
+ if (!seen.has(target)) queue.push(target);
33481
+ }
33482
+ }
33483
+ return new Set([...seen].map((abs) => path26.relative(root, abs).split(path26.sep).join("/")));
33484
+ }
33485
+
33486
+ // src/commands/experiment/composition.ts
33487
+ function sectionsUnder(files, slug) {
33488
+ const prefix = `src/pages/${slug}/`;
33489
+ return files.filter((file) => file.startsWith(prefix) && !file.slice(prefix.length).startsWith("index.")).sort();
33490
+ }
33491
+ function compositionOf(controlSlug, variantSlug, variantGraph) {
33492
+ return {
33493
+ forked: sectionsUnder(variantGraph, variantSlug),
33494
+ shared: sectionsUnder(variantGraph, controlSlug)
33495
+ };
33496
+ }
33497
+ async function readComposition(root, controlSlug, variantSlug) {
33498
+ try {
33499
+ const graph = await landingGraphFiles(root, variantSlug);
33500
+ return compositionOf(controlSlug, variantSlug, [...graph]);
33501
+ } catch {
33502
+ return void 0;
33503
+ }
33504
+ }
33505
+
33506
+ // src/commands/experiment/goal.ts
33507
+ function parseEventGoal(argument) {
33508
+ const [name, filter] = argument.split("/");
33509
+ if (!name) return { error: "Name the event: --goal event:request_demo" };
33510
+ if (!filter) return { kind: "custom_event", name };
33511
+ const separator = filter.indexOf("=");
33512
+ if (separator === -1) {
33513
+ return { error: `Write the property as key=value: --goal event:${name}/section=pricing` };
33514
+ }
33515
+ return { kind: "custom_event", name, property: filter.slice(0, separator), value: filter.slice(separator + 1) };
33516
+ }
33517
+ function parseGoal(raw) {
33518
+ if (raw === void 0 || raw === "") return void 0;
33519
+ const separator = raw.indexOf(":");
33520
+ const kind = separator === -1 ? raw : raw.slice(0, separator);
33521
+ const argument = separator === -1 ? "" : raw.slice(separator + 1);
33522
+ if (kind === "leads") return argument ? { kind: "conversion", flowSlug: argument } : { kind: "conversion" };
33523
+ if (kind === "click") return argument ? { kind: "outbound_click", targetHost: argument } : { kind: "outbound_click" };
33524
+ if (kind === "event") return parseEventGoal(argument);
33525
+ return {
33526
+ error: `Unknown goal "${raw}". Use leads, leads:<form>, event:<name>, event:<name>/<key>=<value>, click, or click:<host>`
33527
+ };
33528
+ }
33529
+
33530
+ // src/commands/experiment/hints.ts
33531
+ var RUNNING_HINT = {
33532
+ keep_running: (row) => `${row.experimentId} cannot yet tell the two pages apart \u2014 do not read its numbers as a result, and do not end it hoping for one. ${describeWait(row)}`,
33533
+ winner: (row) => `${row.experimentId} has a winner. Run \`baker experiment finish --id ${row.experimentId}\` to send all the traffic to it. That only re-routes between two pages already published \u2014 it does not edit either.`,
33534
+ no_difference: (row) => `${row.experimentId} measured the two pages as alike \u2014 it collected enough traffic to see the change it was looking for, and there was none. Finish it (the page you have stays live) and test a bigger change \u2014 a headline, an offer, the shape of the page, not a button colour.`,
33535
+ stopped_early_harmful: (row) => `${row.experimentId} is doing damage. Finish it now \u2014 traffic goes back to the original page.`,
33536
+ // Unreachable in practice — only `finish --abandon` produces it, and that
33537
+ // leaves the test finished. The Record is exhaustive by type so a verdict
33538
+ // added upstream fails to compile here rather than printing nothing.
33539
+ abandoned: (row) => `${row.experimentId} was stopped before it concluded and found nothing.`,
33540
+ invalid: (row) => `${row.experimentId} cannot be read: ${row.invalidReason === "crossover" ? "too many visitors saw both versions" : "the traffic did not split evenly"}. Finish it and start again rather than reporting anything from it.`
33541
+ };
33542
+ function describeWait(row) {
33543
+ if (row.willNotConclude) {
33544
+ return "This page's traffic will not settle it at any point, so abandon it with `--abandon` and test a bigger change.";
33545
+ }
33546
+ if (row.earliestDecisionAt === null) {
33547
+ return "This page has produced no visitors yet, so there is nothing to project a finish date from.";
33548
+ }
33549
+ return `Earliest it could say anything: ${new Date(row.earliestDecisionAt).toISOString().slice(0, 10)}.`;
33550
+ }
33551
+ function buildStatusHints(experiments) {
33552
+ const hints = experiments.filter((row) => row.status === "running").map((row) => RUNNING_HINT[row.verdict](row));
33553
+ if (experiments.length === 0) {
33554
+ hints.push(
33555
+ "No tests yet. `baker experiment plan --landing <slug> --variant <slug>` says whether a page has enough traffic for one before you build the alternative."
33556
+ );
33557
+ }
33558
+ return hints;
33559
+ }
33560
+ function asLift(value) {
33561
+ return `+${Math.round(value * 100)}%`;
33562
+ }
33563
+ function windowHint(days, detectable) {
33564
+ if (days === void 0) return null;
33565
+ if (detectable === null || detectable === void 0) {
33566
+ return `In ${days} days this page would not gather enough visitors to settle anything, at any size of change. The constraint is the traffic, not the test.`;
33567
+ }
33568
+ return `In ${days} days this page could only settle a change of ${asLift(detectable)} or more. Design the alternative to be at least that different \u2014 a smaller change needs traffic these ${days} days will not deliver, and the test would end saying nothing.`;
33569
+ }
33570
+ function buildPlanHints(plan, days) {
33571
+ const window2 = windowHint(days, plan.detectableLiftInDays);
33572
+ if (!plan.canRun) {
33573
+ return [
33574
+ plan.reason ?? "This test cannot run on this page.",
33575
+ ...window2 ? [window2] : [],
33576
+ "A test that cannot conclude is worse than no test \u2014 it still produces a number, and somebody acts on it."
33577
+ ];
33578
+ }
33579
+ const hints = [
33580
+ `This test measures ${plan.goalLabel}. That is fixed when it starts and cannot be changed later.`,
33581
+ // Fires at the exact moment the hypothesis gets invented, whether or not
33582
+ // the agent read the family doc. `plan` says a question CAN be settled here
33583
+ // and says nothing about which question is worth asking — and a well-run
33584
+ // test of a bad idea costs three weeks and returns `no_difference`.
33585
+ "Now decide WHAT to change, from evidence rather than taste. These numbers say which page and which step; they cannot say why. A second opinion answers why \u2014 Microsoft Clarity for what visitors actually do on the page, `baker gsc` for what they searched, `baker ads <platform>` for what the ad promised, `baker ga4` for the client's own measurement. `baker capabilities` lists everything this client has in one call \u2014 the surfaces, and under `tools` everything else that is reachable. Message match \u2014 what the ad or the search promised against what the page opens with \u2014 is the cheapest hypothesis worth testing and is wrong surprisingly often.",
33586
+ // The base always works, so a missing integration is never a reason to
33587
+ // stop. Naming what it would have shown is worth more to the client than
33588
+ // either a silent gap or a run that waited.
33589
+ "If none of that is connected, this page's own drop-off is enough to test on \u2014 say which source you lacked and what it would have told you, then run the test anyway. A test never waits on an integration."
33590
+ ];
33591
+ if (plan.estimatedDays !== null && plan.estimatedDays > 60) {
33592
+ hints.push(
33593
+ `At this page's traffic it would take about ${plan.estimatedDays} days. Consider testing a bigger change, which needs less traffic to detect.`
33594
+ );
33595
+ }
33596
+ if (window2) hints.push(window2);
33597
+ return hints;
33598
+ }
33599
+
33600
+ // ../api/src/experiments/composition.ts
33601
+ import { z as z29 } from "zod";
33602
+ var experimentCompositionSchema = z29.object({
33603
+ /** Components the variant owns its own copy of — the thing under test. */
33604
+ forked: z29.array(z29.string().max(300)).max(50),
33605
+ /** Components both pages render from one file, so an edit reaches both arms. */
33606
+ shared: z29.array(z29.string().max(300)).max(200)
33607
+ });
33608
+
33609
+ // ../api/src/experiments/goal.ts
33610
+ import { z as z30 } from "zod";
33611
+ var eventNameSchema = z30.string().min(1).max(120);
33612
+ var experimentGoalSchema = z30.discriminatedUnion("kind", [
33613
+ z30.object({
33614
+ kind: z30.literal("conversion"),
33615
+ /**
33616
+ * Narrow to one Form, by the slug that is its identity.
33617
+ *
33618
+ * Absent means every marked conversion on the page counts, which is the
33619
+ * right default for a landing with one Form and the wrong one for a page
33620
+ * carrying both a newsletter signup and a demo request.
33621
+ */
33622
+ flowSlug: z30.string().max(120).optional()
33623
+ }),
33624
+ z30.object({
33625
+ kind: z30.literal("custom_event"),
33626
+ name: eventNameSchema,
33627
+ /**
33628
+ * Narrow to one value of one of the event's own properties.
33629
+ *
33630
+ * `section_view` on its own is every section; `section_view` where
33631
+ * `section = pricing` is the one that matters. Without this a page would
33632
+ * have to encode the answer into the event name to be testable against it,
33633
+ * which is precisely what the properties map exists to stop.
33634
+ */
33635
+ property: z30.string().max(64).optional(),
33636
+ value: z30.string().max(255).optional()
33637
+ }),
33638
+ z30.object({
33639
+ kind: z30.literal("outbound_click"),
33640
+ /**
33641
+ * The destination host, as the row records it. Absent means any exit.
33642
+ *
33643
+ * A host rather than a full URL because that is what `event_name` carries
33644
+ * on an `outbound_click` row — `target_url` has the rest, and matching on
33645
+ * it would make the goal depend on query parameters a campaign rewrites.
33646
+ */
33647
+ targetHost: z30.string().max(255).optional()
33648
+ })
33649
+ ]);
33650
+
33651
+ // ../api/src/experiments/hypothesis.ts
33652
+ import { z as z31 } from "zod";
33653
+ var experimentEvidenceSourceSchema = z31.enum([
33654
+ "baker_analytics",
33655
+ "clarity",
33656
+ "ads",
33657
+ "search_console",
33658
+ "research",
33659
+ "inspiration",
33660
+ "client",
33661
+ "hunch"
33662
+ ]);
33663
+ var experimentEvidenceSchema = z31.object({
33664
+ source: experimentEvidenceSourceSchema,
33665
+ note: z31.string().min(1).max(200),
33666
+ /**
33667
+ * Where to go and look. Not a `z.url()` — a report inside Baker is reached by
33668
+ * a relative path, and rejecting those would push every in-product citation
33669
+ * into the note where nothing can link it.
33670
+ */
33671
+ href: z31.string().max(500).optional()
33672
+ });
33673
+ var experimentHypothesisSchema = z31.object({
33674
+ /**
33675
+ * The observation. The one genuinely new field, and the reason for the rest.
33676
+ *
33677
+ * Without it a lost test teaches nothing, because there is no belief for the
33678
+ * result to be evidence against — which is how a testing programme ends up as
33679
+ * a list of tests instead of a list of things learned.
33680
+ */
33681
+ because: z31.string().min(1).max(300),
33682
+ /**
33683
+ * The change, in the client's words.
33684
+ *
33685
+ * The machine-checkable truth lives in `plan.composition.forked`; this is what
33686
+ * a person reads. The two are allowed to differ in wording and cannot differ
33687
+ * in fact, because the brief shows both.
33688
+ */
33689
+ change: z31.string().min(1).max(200),
33690
+ /** Which way the goal is expected to move. See the note at the top of this file. */
33691
+ expect: z31.enum(["increase", "decrease"]).default("increase"),
33692
+ evidence: z31.array(experimentEvidenceSchema).max(5).optional()
33693
+ });
33694
+ var experimentHypothesisOutcomeSchema = z31.enum([
33695
+ "supported",
33696
+ "refuted_opposite",
33697
+ "refuted_no_effect",
33698
+ "unanswered"
33699
+ ]);
33700
+
33701
+ // ../api/src/experiments/wire.ts
33702
+ import { z as z32 } from "zod";
33703
+ var slugSchema = z32.string().min(1).max(120).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "Use a page slug, like `oferta`");
33704
+ var experimentPlanRequestSchema = z32.object({
33705
+ landingSlug: slugSchema,
33706
+ variantSlug: slugSchema,
33707
+ goal: experimentGoalSchema.optional(),
33708
+ /**
33709
+ * The smallest lift worth detecting, relative to the baseline. `0.2` is +20%.
33710
+ *
33711
+ * The single most consequential number a caller supplies, and the one it is
33712
+ * most tempting to set small. A test sized for +5% on a 3% page needs a
33713
+ * quarter of a million visitors per arm; `plan` will say so rather than
33714
+ * running it.
33715
+ */
33716
+ minDetectableRelativeLift: z32.number().gt(0).max(10).optional(),
33717
+ /**
33718
+ * "I have this many days — what could I even see?"
33719
+ *
33720
+ * Read-only, and deliberately not a setting. It does NOT shorten the test:
33721
+ * the finish line is a number of visitors fixed before the test starts, and a
33722
+ * horizon that can be moved is not a horizon. What it does is answer the
33723
+ * question a refusal leaves hanging — `plan` says a +20% lift needs three
33724
+ * weeks this page will not deliver, and this says what those weeks WOULD
33725
+ * settle, which is a change somebody can actually go and make.
33726
+ */
33727
+ days: z32.number().int().gt(0).max(365).optional()
33728
+ });
33729
+ var experimentStartRequestSchema = experimentPlanRequestSchema.extend({
33730
+ hypothesis: experimentHypothesisSchema,
33731
+ /**
33732
+ * What the two arms are made of, read off the import graph by the caller at
33733
+ * the moment the test starts.
33734
+ *
33735
+ * Optional on the wire because the backend cannot compute it — only the
33736
+ * Company Workspace has the files — and a workspace that cannot answer must
33737
+ * not be blocked from running a test. The brief degrades to naming the two
33738
+ * pages instead of naming the section under test.
33739
+ */
33740
+ composition: experimentCompositionSchema.optional()
33741
+ });
33742
+ var experimentFinishRequestSchema = z32.object({
33743
+ experimentId: z32.string().min(1).max(64),
33744
+ /**
33745
+ * Stop the test, keep the page that is live, and record no result.
33746
+ *
33747
+ * The only way a person or an agent can end a test that is still
33748
+ * `keep_running`, and it is deliberately its own field rather than a
33749
+ * `survivor` they get to pick: an abandoned test has no winner, keeps the
33750
+ * control, and is recorded as `abandoned` so it can never be reported later
33751
+ * as a result. Without the separate flag, "stop this" and "the variant won"
33752
+ * would be the same call.
33753
+ *
33754
+ * **It overrides the verdict, and does not defer to it.** The only outcome it
33755
+ * can actually disagree with is a variant win — every other one keeps the
33756
+ * control anyway — so reading it as "apply only when the numbers have nothing
33757
+ * to say" meant that abandoning a won test handed all the traffic to the
33758
+ * variant instead of keeping the original: the opposite of what this field
33759
+ * promises, on live visitors, from a flag whose whole purpose is to be the
33760
+ * conservative choice. The response reports `discardedVerdict` when it did
33761
+ * override one, because throwing away a real finding should be said out loud.
33762
+ */
33763
+ abandon: z32.boolean().optional()
33764
+ });
33765
+ var experimentStatusRequestSchema = z32.object({
33766
+ experimentId: z32.string().max(64).optional(),
33767
+ full: z32.boolean().optional()
33768
+ });
33769
+ var experimentPlanResponseSchema = z32.object({
33770
+ canRun: z32.boolean(),
33771
+ /** Present when `canRun` is false. Product language, ready to show. */
33772
+ reason: z32.string().optional(),
33773
+ baselineRate: z32.number(),
33774
+ minDetectableRelativeLift: z32.number(),
33775
+ /** `null` when no amount of traffic could settle the hypothesis. */
33776
+ requiredPerVariant: z32.number().nullable(),
33777
+ /** Visitors a day the page has been getting, per arm once split. */
33778
+ dailyVisitorsPerVariant: z32.number(),
33779
+ /** `null` when the page has no traffic to project from. */
33780
+ estimatedDays: z32.number().nullable(),
33781
+ goal: experimentGoalSchema,
33782
+ goalLabel: z32.string(),
33783
+ /**
33784
+ * The smallest lift `days` of this page's traffic could detect, when `days`
33785
+ * was asked for. `null` when it was not, or when no lift at all would be
33786
+ * detectable in that window.
33787
+ */
33788
+ detectableLiftInDays: z32.number().nullable().optional()
33789
+ });
33790
+ var experimentVerdictSchema = z32.enum([
33791
+ "keep_running",
33792
+ "winner",
33793
+ "no_difference",
33794
+ "invalid",
33795
+ "stopped_early_harmful",
33796
+ /**
33797
+ * Stopped by a person before it concluded. Never produced by the stopping
33798
+ * rule — only by `finish --abandon`.
33799
+ *
33800
+ * Its own verdict rather than folded into `no_difference`, because they are
33801
+ * opposite claims: one says the two versions were measured and found alike,
33802
+ * the other says nobody ever found out. Collapsing them would manufacture the
33803
+ * exact finding this feature exists to refuse.
33804
+ */
33805
+ "abandoned"
33806
+ ]);
33807
+ var experimentArmSchema = z32.object({
33808
+ slug: z32.string(),
33809
+ visitors: z32.number(),
33810
+ conversions: z32.number(),
33811
+ rate: z32.number()
33812
+ });
33813
+ var experimentStatusRowSchema = z32.object({
33814
+ experimentId: z32.string(),
33815
+ landingSlug: z32.string(),
33816
+ variantSlug: z32.string(),
33817
+ hypothesis: experimentHypothesisSchema,
33818
+ /**
33819
+ * The hypothesis as one sentence, built from its parts plus the goal and the
33820
+ * lift on this same row. Rendered backend-side so what a client reads in chat
33821
+ * and what they read on the dashboard can never be two different sentences.
33822
+ */
33823
+ hypothesisStatement: z32.string(),
33824
+ /**
33825
+ * What the test found out about the belief — derived from `verdict` and
33826
+ * `winner`, never entered.
33827
+ *
33828
+ * Its own field because the verdict and the outcome come apart in exactly the
33829
+ * case worth catching: a `winner` the *control* won is not a win, it is the
33830
+ * change having made things worse.
33831
+ */
33832
+ hypothesisOutcome: experimentHypothesisOutcomeSchema,
33833
+ /** What the two arms differ by. Absent for a test started before this was recorded. */
33834
+ composition: experimentCompositionSchema.optional(),
33835
+ /**
33836
+ * The finish line this test was sized for. On the row rather than only inside
33837
+ * the plan because the claim is not readable without it — "leads should go up"
33838
+ * is not a hypothesis, "leads should go up by at least 20%" is.
33839
+ */
33840
+ minDetectableRelativeLift: z32.number(),
33841
+ goalLabel: z32.string(),
33842
+ status: z32.enum(["running", "finished"]),
33843
+ startedAt: z32.number(),
33844
+ finishedAt: z32.number().nullable(),
33845
+ verdict: experimentVerdictSchema,
33846
+ /** Set only on `winner`. Which page to keep. */
33847
+ winner: z32.enum(["control", "variant"]).nullable(),
33848
+ /** Set only on `invalid`. Why the numbers cannot be read. */
33849
+ invalidReason: z32.enum(["sample_ratio_mismatch", "crossover"]).nullable(),
33850
+ /** What a person should be told, in one sentence, in product language. */
33851
+ summary: z32.string(),
33852
+ control: experimentArmSchema,
33853
+ variant: experimentArmSchema,
33854
+ /**
33855
+ * How close this test is to being able to say anything at all, 0 to 1.
33856
+ *
33857
+ * Not progress toward a planned sample — there is no longer one to progress
33858
+ * toward. It is the traffic behind the test as a share of the traffic its own
33859
+ * measured variance says it needs, so it moves when the page gets noisier or
33860
+ * steadier rather than only when visitors arrive.
33861
+ */
33862
+ precision: z32.number(),
33863
+ /** When the test could first say something. `null` with no traffic to project from. */
33864
+ earliestDecisionAt: z32.number().nullable(),
33865
+ /**
33866
+ * True when no amount of this page's traffic would settle the comparison.
33867
+ * A different thing from a page that is merely slow, and the reason to
33868
+ * abandon a test rather than leave it running.
33869
+ */
33870
+ willNotConclude: z32.boolean(),
33871
+ /** Printing only. Never a reason to act — `verdict` is. */
33872
+ evidence: z32.object({
33873
+ relativeLift: z32.number(),
33874
+ probabilityVariantBetter: z32.number(),
33875
+ controlInterval: z32.tuple([z32.number(), z32.number()]),
33876
+ variantInterval: z32.tuple([z32.number(), z32.number()]),
33877
+ /** The anytime-valid interval on the relative lift — what the verdict is read from. */
33878
+ liftInterval: z32.tuple([z32.number(), z32.number()])
33879
+ }).optional()
33880
+ });
33881
+ var experimentStatusResponseSchema = z32.object({
33882
+ experiments: z32.array(experimentStatusRowSchema)
33883
+ });
33884
+
33885
+ // src/commands/experiment/hypothesis.ts
33886
+ var SOURCES = experimentEvidenceSourceSchema.options.join(", ");
33887
+ function parseOne(raw) {
33888
+ const separator = raw.indexOf(":");
33889
+ if (separator === -1) {
33890
+ return { error: `Write the evidence as source:note \u2014 for example \`${raw}:what you saw\`. Sources: ${SOURCES}` };
33891
+ }
33892
+ const source = experimentEvidenceSourceSchema.safeParse(raw.slice(0, separator).trim());
33893
+ if (!source.success) {
33894
+ return { error: `\u201C${raw.slice(0, separator).trim()}\u201D is not one of the evidence sources. Use one of: ${SOURCES}` };
33895
+ }
33896
+ const rest = raw.slice(separator + 1).trim();
33897
+ const pipe = rest.lastIndexOf("|");
33898
+ const note = (pipe === -1 ? rest : rest.slice(0, pipe)).trim();
33899
+ const href = pipe === -1 ? void 0 : rest.slice(pipe + 1).trim();
33900
+ if (note === "") {
33901
+ return { error: `Say what ${source.data} showed: --evidence "${source.data}:what you saw"` };
33902
+ }
33903
+ return href ? { source: source.data, note, href } : { source: source.data, note };
33904
+ }
33905
+ function parseEvidence(raw) {
33906
+ if (raw === void 0) return void 0;
33907
+ const entries = (Array.isArray(raw) ? raw : [raw]).filter((entry) => entry !== "");
33908
+ if (entries.length === 0) return void 0;
33909
+ const parsed = [];
33910
+ for (const entry of entries) {
33911
+ const one = parseOne(entry);
33912
+ if ("error" in one) return one;
33913
+ parsed.push(one);
33914
+ }
33915
+ return parsed;
33916
+ }
33917
+
33918
+ // src/commands/experiment/index.ts
33919
+ var GOAL_ARG = {
33920
+ type: "string",
33921
+ description: "What counts as success, and it is fixed for the life of the test. `leads` (default \u2014 whatever this company already marks as a conversion), `leads:<form>`, `event:<name>` for an event the page declares itself (see `baker analytics events`), `event:<name>/<key>=<value>` to narrow it, `click` or `click:<host>` for leaving the site. Pick the thing the business actually wants; a test that measures the wrong outcome can have the variant win and the revenue fall.",
33922
+ required: false
33923
+ };
33924
+ var LIFT_ARG = {
33925
+ type: "string",
33926
+ description: "The smallest improvement worth detecting, relative \u2014 0.2 is +20% (default). The single most consequential number here: halving it roughly quadruples the traffic needed. Ask for the smallest lift that would actually change what the client does, not the smallest one you would like to see.",
33927
+ required: false
33928
+ };
33929
+ var DAYS_ARG = {
33930
+ type: "string",
33931
+ description: "How many days you have, if that is the constraint. This does NOT shorten the test \u2014 the finish line is a visitor count fixed before it starts \u2014 it answers the other question: what a window that long could settle on this page's traffic. Use it when `plan` refuses and you need to know what WOULD fit.",
33932
+ required: false
33933
+ };
33934
+ var BECAUSE_ARG = {
33935
+ type: "string",
33936
+ description: "What you SAW that makes this worth testing \u2014 the observation, not the change. \u201C60% of visitors never scroll past the hero\u201D, \u201CCPL doubled after the new headline\u201D. This is the field that makes a losing test worth something: without it there is no belief for the result to be evidence against, and the next test is no better informed than this one.",
33937
+ required: true
33938
+ };
33939
+ var CHANGE_ARG = {
33940
+ type: "string",
33941
+ description: "What is different about the alternative version, in the client's words \u2014 \u201Cput the booking form in the hero\u201D. One thing. Say it as the reader of the report would.",
33942
+ required: true
33943
+ };
33944
+ var EXPECT_ARG = {
33945
+ type: "string",
33946
+ description: "Which way you expect the goal to move: `increase` (default) or `decrease`. This is what lets Baker report whether your hypothesis HELD, rather than only which page won \u2014 a test the original page wins is not a win, it is the change having made things worse, and that is the most useful thing a test can tell you.",
33947
+ required: false
33948
+ };
33949
+ var EVIDENCE_ARG = {
33950
+ type: "string",
33951
+ description: "Where the observation came from, as `source:what it showed`, optionally `|link`. Repeatable. Sources: baker_analytics, clarity, ads, search_console, research, inspiration, client, hunch. Use `hunch` honestly when there is no data \u2014 a stated guess is worth more than a guess dressed as analytics.",
33952
+ required: false
33953
+ };
33954
+ function fail5(message) {
33955
+ writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
33956
+ process.exit(1);
33957
+ }
33958
+ function reportApiError(error) {
33959
+ if (error instanceof ApiError) {
33960
+ writeJsonEnvelope({ ok: false, error: { code: error.code ?? "ERROR", message: error.message } });
33961
+ process.exit(1);
33962
+ }
33963
+ throw error;
33964
+ }
33965
+ registerSchema({
33966
+ command: "experiment.plan",
33967
+ description: "Whether a page has enough traffic to settle a question, before anything is built",
33968
+ args: {
33969
+ landing: { type: "string", description: "The page under test, by slug", required: true },
33970
+ variant: { type: "string", description: "The alternative page's slug", required: true },
33971
+ goal: GOAL_ARG,
33972
+ lift: LIFT_ARG,
33973
+ days: DAYS_ARG
33974
+ }
33975
+ });
33976
+ var planCommand = defineCommand112({
33977
+ meta: {
33978
+ name: "plan",
33979
+ description: "Start here, BEFORE building the alternative page. Says whether this page gets enough traffic to settle the question, how long it would take, and whether the thing you want to measure has ever happened on it. Most landing pages cannot run most tests \u2014 finding that out now costs one call, and finding it out later costs three weeks and a decision made on noise."
33980
+ },
33981
+ args: {
33982
+ landing: { type: "string", description: "The page under test, by slug", required: true },
33983
+ variant: { type: "string", description: "The alternative page's slug", required: true },
33984
+ goal: GOAL_ARG,
33985
+ lift: LIFT_ARG,
33986
+ days: DAYS_ARG
33987
+ },
33988
+ run: async ({ args }) => {
33989
+ const goal = parseGoal(args.goal ? String(args.goal) : void 0);
33990
+ if (goal && "error" in goal) fail5(goal.error);
33991
+ try {
33992
+ const response = await apiPost("/api/experiments/plan", {
33993
+ landingSlug: String(args.landing),
33994
+ variantSlug: String(args.variant),
33995
+ ...goal ? { goal } : {},
33996
+ ...args.lift ? { minDetectableRelativeLift: Number(args.lift) } : {},
33997
+ ...args.days ? { days: Number(args.days) } : {}
33998
+ });
33999
+ writeJsonEnvelope({
34000
+ ok: true,
34001
+ data: response.data,
34002
+ hints: buildPlanHints(response.data, args.days ? Number(args.days) : void 0)
34003
+ });
34004
+ } catch (error) {
34005
+ reportApiError(error);
34006
+ }
34007
+ }
34008
+ });
34009
+ registerSchema({
34010
+ command: "experiment.start",
34011
+ description: "Stage an A/B test between a page and its alternative",
34012
+ args: {
34013
+ landing: { type: "string", description: "The page under test, by slug", required: true },
34014
+ variant: { type: "string", description: "The alternative page's slug", required: true },
34015
+ because: BECAUSE_ARG,
34016
+ change: CHANGE_ARG,
34017
+ expect: EXPECT_ARG,
34018
+ evidence: EVIDENCE_ARG,
34019
+ goal: GOAL_ARG,
34020
+ lift: LIFT_ARG
34021
+ }
34022
+ });
34023
+ var startCommand = defineCommand112({
34024
+ meta: {
34025
+ name: "start",
34026
+ description: "Stage a test between the page and its alternative. Both are ordinary published pages; visitors are split 50/50 at the edge, so there is no flicker and nothing for an ad blocker to suppress. Nothing goes live until the session is published \u2014 the person publishing is the review of the alternative page. Re-runs `plan` first and refuses on the same grounds."
34027
+ },
34028
+ args: {
34029
+ landing: { type: "string", description: "The page under test, by slug", required: true },
34030
+ variant: { type: "string", description: "The alternative page's slug", required: true },
34031
+ because: BECAUSE_ARG,
34032
+ change: CHANGE_ARG,
34033
+ expect: EXPECT_ARG,
34034
+ evidence: EVIDENCE_ARG,
34035
+ goal: GOAL_ARG,
34036
+ lift: LIFT_ARG
34037
+ },
34038
+ run: async ({ args }) => {
34039
+ const goal = parseGoal(args.goal ? String(args.goal) : void 0);
34040
+ if (goal && "error" in goal) fail5(goal.error);
34041
+ const expect = args.expect === void 0 ? "increase" : String(args.expect);
34042
+ if (expect !== "increase" && expect !== "decrease") {
34043
+ fail5("`--expect` is either `increase` or `decrease` \u2014 which way should the goal move if you are right?");
34044
+ }
34045
+ const evidence = parseEvidence(args.evidence);
34046
+ if (evidence && "error" in evidence) fail5(evidence.error);
34047
+ const composition = await readComposition(process.cwd(), String(args.landing), String(args.variant));
34048
+ try {
34049
+ const response = await apiPost("/api/experiments/start", {
34050
+ landingSlug: String(args.landing),
34051
+ variantSlug: String(args.variant),
34052
+ hypothesis: {
34053
+ because: String(args.because),
34054
+ change: String(args.change),
34055
+ expect,
34056
+ ...evidence ? { evidence } : {}
34057
+ },
34058
+ ...composition ? { composition } : {},
34059
+ ...goal ? { goal } : {},
34060
+ ...args.lift ? { minDetectableRelativeLift: Number(args.lift) } : {}
34061
+ });
34062
+ writeJsonEnvelope({
34063
+ ok: true,
34064
+ data: response.data,
34065
+ hints: [
34066
+ // The one composition worth interrupting for: `baker landing variant`
34067
+ // with no `--fork` succeeds and produces a page that renders exactly
34068
+ // the control, so the test would run for weeks against itself.
34069
+ ...composition && composition.forked.length === 0 ? [
34070
+ `\u201C${String(args.variant)}\u201D does not have its own copy of any section \u2014 it renders exactly the same page as \u201C${String(args.landing)}\u201D, so this test cannot find anything. Fork the section you want to test (\`baker landing variant ${String(args.landing)} ${String(args.variant)} --fork Hero.astro\`) and edit only that file.`
34071
+ ] : [],
34072
+ "Staged. The split starts when this session is published \u2014 publishing is the review of the alternative page.",
34073
+ "Check it as often as you like \u2014 `baker experiment status` is safe to read at any moment and holds its error rate however often you ask. What is never safe is reading the numbers instead of the verdict: `keep_running` means this test cannot yet tell the two pages apart, whatever the rates happen to say today."
34074
+ ]
34075
+ });
34076
+ } catch (error) {
34077
+ reportApiError(error);
34078
+ }
34079
+ }
34080
+ });
34081
+ registerSchema({
34082
+ command: "experiment.status",
34083
+ description: "The verdict on every A/B test, running and finished",
34084
+ args: {
34085
+ id: { type: "string", description: "One test, by its id", required: false },
34086
+ full: { type: "boolean", description: "Include the posteriors behind the verdict", required: false }
34087
+ }
34088
+ });
34089
+ var statusCommand4 = defineCommand112({
34090
+ meta: {
34091
+ name: "status",
34092
+ description: "The verdict on each test. Read `verdict` and nothing else to decide: keep_running | winner | no_difference | invalid | stopped_early_harmful. `summary` is one sentence you can show the client. The numbers under `--full` are for printing, never for deciding \u2014 a test that says keep_running has not finished, however good its numbers look."
34093
+ },
34094
+ args: {
34095
+ id: { type: "string", description: "One test, by its id", required: false },
34096
+ full: { type: "boolean", description: "Include the posteriors behind the verdict", required: false }
34097
+ },
34098
+ run: async ({ args }) => {
34099
+ try {
34100
+ const response = await apiPost("/api/experiments/status", {
34101
+ ...args.id ? { experimentId: String(args.id) } : {},
34102
+ ...args.full === true ? { full: true } : {}
34103
+ });
34104
+ writeJsonEnvelope({
34105
+ ok: true,
34106
+ data: response.data,
34107
+ hints: buildStatusHints(response.data.experiments)
34108
+ });
34109
+ } catch (error) {
34110
+ reportApiError(error);
34111
+ }
34112
+ }
34113
+ });
34114
+ registerSchema({
34115
+ command: "experiment.finish",
34116
+ description: "End a test and send all its traffic to the surviving page",
34117
+ args: {
34118
+ id: { type: "string", description: "The test, by its id", required: true },
34119
+ abandon: {
34120
+ type: "boolean",
34121
+ description: "Stop the test, keep the original page, and record no result. The only way to end a test that has not concluded \u2014 and it OVERRIDES a verdict if there is one, throwing that finding away. Do not pass it by reflex.",
34122
+ required: false
34123
+ }
34124
+ }
34125
+ });
34126
+ var finishCommand = defineCommand112({
34127
+ meta: {
34128
+ name: "finish",
34129
+ description: "End a test and send all its traffic to whichever page survived. Only a test whose verdict has concluded can be finished with a result \u2014 a `keep_running` test can only be `--abandon`ed, which keeps the original page and records no finding. Promotion is a traffic change over two pages that were both already published and reviewed; it does not edit either page."
34130
+ },
34131
+ args: {
34132
+ id: { type: "string", description: "The test, by its id", required: true },
34133
+ abandon: {
34134
+ type: "boolean",
34135
+ description: "Stop the test, keep the original page, and record no result. The only way to end a test that has not concluded \u2014 and it OVERRIDES a verdict if there is one, throwing that finding away. Do not pass it by reflex.",
34136
+ required: false
34137
+ }
34138
+ },
34139
+ run: async ({ args }) => {
34140
+ try {
34141
+ const response = await apiPost("/api/experiments/finish", {
34142
+ experimentId: String(args.id),
34143
+ ...args.abandon === true ? { abandon: true } : {}
34144
+ });
34145
+ writeJsonEnvelope({
34146
+ ok: true,
34147
+ data: response.data,
34148
+ hints: [
34149
+ `Staged. All the traffic goes to the ${response.data.survivor === "variant" ? "alternative" : "original"} page when this session is published.`,
34150
+ ...response.data.discardedVerdict ? [
34151
+ `You abandoned a test that HAD concluded (${response.data.discardedVerdict}). The original page keeps the traffic and no result is recorded \u2014 the finding is gone. If that was not what you meant, run this again without --abandon.`
34152
+ ] : [],
34153
+ "If the alternative won and you want it to become the page itself, that is an ordinary page edit afterwards \u2014 copy its content over and delete the alternative."
34154
+ ]
34155
+ });
34156
+ } catch (error) {
34157
+ reportApiError(error);
34158
+ }
34159
+ }
34160
+ });
34161
+ var experimentCommand = defineCommand112({
34162
+ meta: {
34163
+ name: "experiment",
34164
+ description: "Run an A/B test between a landing page and an alternative version of it, and be told which one is better. Always `plan` first: most pages do not get enough traffic to settle most questions, and a test that cannot conclude still produces a number somebody will act on."
34165
+ },
34166
+ subCommands: {
34167
+ plan: planCommand,
34168
+ start: startCommand,
34169
+ status: statusCommand4,
34170
+ finish: finishCommand
34171
+ }
34172
+ });
34173
+
33384
34174
  // src/commands/flows/index.ts
33385
- import { defineCommand as defineCommand116 } from "citty";
34175
+ import { defineCommand as defineCommand117 } from "citty";
33386
34176
 
33387
34177
  // src/commands/flows/add.ts
33388
34178
  import { randomUUID } from "crypto";
33389
- import { defineCommand as defineCommand112 } from "citty";
34179
+ import { defineCommand as defineCommand113 } from "citty";
33390
34180
 
33391
34181
  // src/commands/flows/shared.ts
33392
34182
  import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync9, writeFileSync as writeFileSync3 } from "fs";
@@ -33439,12 +34229,12 @@ function collectSideEffects(tree) {
33439
34229
  );
33440
34230
  }
33441
34231
  function readFlowTree(slug) {
33442
- const path40 = join3(flowsDir(), slug, "_data.json");
33443
- if (!existsSync5(path40)) {
34232
+ const path43 = join3(flowsDir(), slug, "_data.json");
34233
+ if (!existsSync5(path43)) {
33444
34234
  failLocal(`No form "${slug}". Run "baker flows list" to see the forms in this workspace.`);
33445
34235
  }
33446
34236
  try {
33447
- return JSON.parse(readFileSync9(path40, "utf-8"));
34237
+ return JSON.parse(readFileSync9(path43, "utf-8"));
33448
34238
  } catch (error) {
33449
34239
  failLocal(`Could not read form "${slug}": ${error instanceof Error ? error.message : String(error)}`);
33450
34240
  }
@@ -33596,7 +34386,7 @@ registerSchema({
33596
34386
  parent: { type: "string", description: "Parent node id. Omit to append to the root's children.", required: false }
33597
34387
  }
33598
34388
  });
33599
- var addNodeCommand = defineCommand112({
34389
+ var addNodeCommand = defineCommand113({
33600
34390
  meta: {
33601
34391
  name: "add-node",
33602
34392
  description: 'Add a step to a form, with the skeleton its type requires. Example: baker flows add-node contact --type customForm --name "Contact Info"'
@@ -33692,7 +34482,7 @@ registerSchema({
33692
34482
  }
33693
34483
  }
33694
34484
  });
33695
- var addSideEffectCommand = defineCommand112({
34485
+ var addSideEffectCommand = defineCommand113({
33696
34486
  meta: {
33697
34487
  name: "add-side-effect",
33698
34488
  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"
@@ -33774,7 +34564,7 @@ var addSideEffectCommand = defineCommand112({
33774
34564
 
33775
34565
  // src/commands/flows/map.ts
33776
34566
  import { readFileSync as readFileSync11 } from "fs";
33777
- import { defineCommand as defineCommand113 } from "citty";
34567
+ import { defineCommand as defineCommand114 } from "citty";
33778
34568
 
33779
34569
  // src/commands/flows/value-expression.ts
33780
34570
  import { existsSync as existsSync6, readFileSync as readFileSync10 } from "fs";
@@ -33836,10 +34626,10 @@ function parseValueExpression(raw) {
33836
34626
  return parts.map(parsePart);
33837
34627
  }
33838
34628
  function trackingFieldIds() {
33839
- const path40 = join4(flowsDir(), "..", "tracking.ts");
33840
- if (!existsSync6(path40)) return null;
34629
+ const path43 = join4(flowsDir(), "..", "tracking.ts");
34630
+ if (!existsSync6(path43)) return null;
33841
34631
  try {
33842
- const source = readFileSync10(path40, "utf-8");
34632
+ const source = readFileSync10(path43, "utf-8");
33843
34633
  const block2 = source.match(/TRACKING_FIELD_IDS\s*=\s*\[([\s\S]*?)\]\s*as const/)?.[1];
33844
34634
  if (!block2) return null;
33845
34635
  const ids = [...block2.matchAll(/"(tracking\.[a-z0-9_]+)"/g)].map((match) => match[1]);
@@ -34391,13 +35181,13 @@ function specsFromFile(parsed) {
34391
35181
  return `${destField}${type}=${entry?.value ?? ""}`;
34392
35182
  });
34393
35183
  }
34394
- function readSpecFile(path40) {
35184
+ function readSpecFile(path43) {
34395
35185
  let raw;
34396
35186
  try {
34397
- raw = path40 === "-" ? readFileSync11(0, "utf-8") : readFileSync11(path40, "utf-8");
35187
+ raw = path43 === "-" ? readFileSync11(0, "utf-8") : readFileSync11(path43, "utf-8");
34398
35188
  } catch (error) {
34399
35189
  refuse(
34400
- `Could not read ${path40 === "-" ? "the mapping from stdin" : `"${path40}"`}: ${error instanceof Error ? error.message : String(error)}`
35190
+ `Could not read ${path43 === "-" ? "the mapping from stdin" : `"${path43}"`}: ${error instanceof Error ? error.message : String(error)}`
34401
35191
  );
34402
35192
  }
34403
35193
  let parsed;
@@ -34405,7 +35195,7 @@ function readSpecFile(path40) {
34405
35195
  parsed = JSON.parse(raw);
34406
35196
  } catch (error) {
34407
35197
  refuse(
34408
- `${path40 === "-" ? "stdin" : `"${path40}"`} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
35198
+ `${path43 === "-" ? "stdin" : `"${path43}"`} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
34409
35199
  'Expected { "map": { "<destField>": "<value>", \u2026 } }'
34410
35200
  );
34411
35201
  }
@@ -34547,7 +35337,7 @@ async function mapOptions(args, fromFile, into) {
34547
35337
  destinationFields: hubspotForm ? await hubspotFieldCatalog(hubspotForm) : void 0
34548
35338
  };
34549
35339
  }
34550
- var mapCommand2 = defineCommand113({
35340
+ var mapCommand2 = defineCommand114({
34551
35341
  meta: {
34552
35342
  name: "map",
34553
35343
  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"
@@ -34645,7 +35435,7 @@ var mapCommand2 = defineCommand113({
34645
35435
  });
34646
35436
 
34647
35437
  // src/commands/flows/normalize.ts
34648
- import { defineCommand as defineCommand114 } from "citty";
35438
+ import { defineCommand as defineCommand115 } from "citty";
34649
35439
  var OPTIONAL_STRINGS = ["oauthProviderId"];
34650
35440
  var ARRAY_FIELDS = [
34651
35441
  "fieldMapping",
@@ -34659,18 +35449,18 @@ var ARRAY_FIELDS = [
34659
35449
  "tagIds"
34660
35450
  ];
34661
35451
  var ARRAY_OWNERS = ["", "body"];
34662
- function dropUnsetOptionals(sideEffect, path40) {
35452
+ function dropUnsetOptionals(sideEffect, path43) {
34663
35453
  return OPTIONAL_STRINGS.flatMap((key) => {
34664
35454
  if (!(key in sideEffect) || sideEffect[key] !== null && sideEffect[key] !== "") return [];
34665
35455
  delete sideEffect[key];
34666
- return [{ path: path40, change: `dropped \`${key}\` (an optional string is absent, never null)` }];
35456
+ return [{ path: path43, change: `dropped \`${key}\` (an optional string is absent, never null)` }];
34667
35457
  });
34668
35458
  }
34669
- function fillNulledArrays(target, prefix, path40) {
35459
+ function fillNulledArrays(target, prefix, path43) {
34670
35460
  return ARRAY_FIELDS.flatMap((key) => {
34671
35461
  if (!(key in target) || target[key] !== null) return [];
34672
35462
  target[key] = [];
34673
- return [{ path: path40, change: `\`${prefix}${key}: null\` \u2192 \`[]\`` }];
35463
+ return [{ path: path43, change: `\`${prefix}${key}: null\` \u2192 \`[]\`` }];
34674
35464
  });
34675
35465
  }
34676
35466
  function sideEffectsOf(node) {
@@ -34680,13 +35470,13 @@ function sideEffectsOf(node) {
34680
35470
  );
34681
35471
  }
34682
35472
  function normalizeSideEffect(sideEffect, where) {
34683
- const path40 = `${where} \u2192 ${String(sideEffect.id ?? "side effect")}`;
35473
+ const path43 = `${where} \u2192 ${String(sideEffect.id ?? "side effect")}`;
34684
35474
  const arrays = ARRAY_OWNERS.flatMap((owner) => {
34685
35475
  const target = owner ? sideEffect[owner] : sideEffect;
34686
35476
  if (!target || typeof target !== "object") return [];
34687
- return fillNulledArrays(target, owner ? `${owner}.` : "", path40);
35477
+ return fillNulledArrays(target, owner ? `${owner}.` : "", path43);
34688
35478
  });
34689
- return [...dropUnsetOptionals(sideEffect, path40), ...arrays];
35479
+ return [...dropUnsetOptionals(sideEffect, path43), ...arrays];
34690
35480
  }
34691
35481
  function normalizeFlowTree(tree) {
34692
35482
  const changes = [];
@@ -34714,7 +35504,7 @@ registerSchema({
34714
35504
  }
34715
35505
  }
34716
35506
  });
34717
- var normalizeCommand = defineCommand114({
35507
+ var normalizeCommand = defineCommand115({
34718
35508
  meta: {
34719
35509
  name: "normalize",
34720
35510
  description: "Repair the shapes a form file is allowed to get slightly wrong. Example: baker flows normalize contact --write"
@@ -34745,7 +35535,7 @@ var normalizeCommand = defineCommand114({
34745
35535
  });
34746
35536
 
34747
35537
  // src/commands/flows/schema.ts
34748
- import { defineCommand as defineCommand115 } from "citty";
35538
+ import { defineCommand as defineCommand116 } from "citty";
34749
35539
  registerSchema({
34750
35540
  command: "flows.schema",
34751
35541
  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.",
@@ -34770,7 +35560,7 @@ function triggerHints(triggers) {
34770
35560
  `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(", ")}.` : "")
34771
35561
  ];
34772
35562
  }
34773
- var schemaCommand = defineCommand115({
35563
+ var schemaCommand = defineCommand116({
34774
35564
  meta: {
34775
35565
  name: "schema",
34776
35566
  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)"
@@ -34904,7 +35694,7 @@ function displayName(slug) {
34904
35694
  const name = tree.displayName;
34905
35695
  return typeof name === "string" && name.trim() ? name.trim() : slug;
34906
35696
  }
34907
- var listCommand12 = defineCommand116({
35697
+ var listCommand12 = defineCommand117({
34908
35698
  meta: {
34909
35699
  name: "list",
34910
35700
  description: "List Forms in this workspace with the count of confidential fields still needing setup. Example: baker flows list"
@@ -34925,7 +35715,7 @@ var listCommand12 = defineCommand116({
34925
35715
  });
34926
35716
  }
34927
35717
  });
34928
- var showCommand3 = defineCommand116({
35718
+ var showCommand3 = defineCommand117({
34929
35719
  meta: {
34930
35720
  name: "show",
34931
35721
  description: "Show a Form's confidential fields and their configuration status (never secret values). Example: baker flows show contact"
@@ -34953,7 +35743,7 @@ var showCommand3 = defineCommand116({
34953
35743
  });
34954
35744
  }
34955
35745
  });
34956
- var flowsCommand = defineCommand116({
35746
+ var flowsCommand = defineCommand117({
34957
35747
  meta: {
34958
35748
  name: "flows",
34959
35749
  description: `Read and shape this workspace's Forms (flows).
@@ -34987,10 +35777,10 @@ Full guide: __tooling__/docs/tools/baker/flows.md`
34987
35777
  });
34988
35778
 
34989
35779
  // src/commands/ga4/index.ts
34990
- import { defineCommand as defineCommand123 } from "citty";
35780
+ import { defineCommand as defineCommand124 } from "citty";
34991
35781
 
34992
35782
  // src/commands/ga4/audit.ts
34993
- import { defineCommand as defineCommand117 } from "citty";
35783
+ import { defineCommand as defineCommand118 } from "citty";
34994
35784
 
34995
35785
  // src/commands/ga4/resolve.ts
34996
35786
  async function fetchProperties(useCache = true) {
@@ -35056,7 +35846,7 @@ registerSchema({
35056
35846
  "no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
35057
35847
  }
35058
35848
  });
35059
- var auditCommand2 = defineCommand117({
35849
+ var auditCommand2 = defineCommand118({
35060
35850
  meta: {
35061
35851
  name: "audit",
35062
35852
  description: `Run all GA4 admin health checks. Returns property config with playbook warnings.
@@ -35111,7 +35901,7 @@ Examples:
35111
35901
  });
35112
35902
 
35113
35903
  // src/commands/ga4/config.ts
35114
- import { defineCommand as defineCommand118 } from "citty";
35904
+ import { defineCommand as defineCommand119 } from "citty";
35115
35905
 
35116
35906
  // src/commands/ga4/shared.ts
35117
35907
  import { readFileSync as readFileSync12 } from "fs";
@@ -35224,10 +36014,10 @@ async function stageOps(ops) {
35224
36014
  handleError2(err);
35225
36015
  }
35226
36016
  }
35227
- async function draftAction2(path40, body, chat) {
36017
+ async function draftAction2(path43, body, chat) {
35228
36018
  const chatId = resolveChatId(chat);
35229
36019
  try {
35230
- const data = await apiPost(path40, { chatId, ...body });
36020
+ const data = await apiPost(path43, { chatId, ...body });
35231
36021
  writeJsonEnvelope({ ok: true, data });
35232
36022
  return data;
35233
36023
  } catch (err) {
@@ -35307,7 +36097,7 @@ function configHints(config) {
35307
36097
  }
35308
36098
  return hints;
35309
36099
  }
35310
- var configCommand = defineCommand118({
36100
+ var configCommand = defineCommand119({
35311
36101
  meta: {
35312
36102
  name: "config",
35313
36103
  description: `Read how the property is configured to measure \u2014 key events, custom definitions, custom events, retention
@@ -35339,7 +36129,7 @@ Examples:
35339
36129
  });
35340
36130
 
35341
36131
  // src/commands/ga4/draft.ts
35342
- import { defineCommand as defineCommand119 } from "citty";
36132
+ import { defineCommand as defineCommand120 } from "citty";
35343
36133
  registerSchema({
35344
36134
  command: "ga4.draft",
35345
36135
  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.",
@@ -35348,13 +36138,13 @@ registerSchema({
35348
36138
  chat: { type: "string", description: CHAT_READ_ARG.description, required: false }
35349
36139
  }
35350
36140
  });
35351
- var draftCommand3 = defineCommand119({
36141
+ var draftCommand3 = defineCommand120({
35352
36142
  meta: {
35353
36143
  name: "draft",
35354
36144
  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."
35355
36145
  },
35356
36146
  subCommands: {
35357
- list: defineCommand119({
36147
+ list: defineCommand120({
35358
36148
  meta: { name: "list", description: "Review everything staged on this chat (--json for the raw envelope)" },
35359
36149
  args: {
35360
36150
  json: { type: "boolean", description: "Print the raw JSON envelope", required: false },
@@ -35364,7 +36154,7 @@ var draftCommand3 = defineCommand119({
35364
36154
  await draftList(args.json === true, args.chat);
35365
36155
  }
35366
36156
  }),
35367
- show: defineCommand119({
36157
+ show: defineCommand120({
35368
36158
  meta: {
35369
36159
  name: "show",
35370
36160
  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)."
@@ -35381,7 +36171,7 @@ var draftCommand3 = defineCommand119({
35381
36171
  );
35382
36172
  }
35383
36173
  }),
35384
- amend: defineCommand119({
36174
+ amend: defineCommand120({
35385
36175
  meta: {
35386
36176
  name: "amend",
35387
36177
  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."
@@ -35398,7 +36188,7 @@ var draftCommand3 = defineCommand119({
35398
36188
  });
35399
36189
  }
35400
36190
  }),
35401
- remove: defineCommand119({
36191
+ remove: defineCommand120({
35402
36192
  meta: { name: "remove", description: "Remove one staged change" },
35403
36193
  args: { ref: { type: "positional", description: "Staged ref or target id", required: false } },
35404
36194
  run: async ({ args }) => {
@@ -35407,7 +36197,7 @@ var draftCommand3 = defineCommand119({
35407
36197
  });
35408
36198
  }
35409
36199
  }),
35410
- clear: defineCommand119({
36200
+ clear: defineCommand120({
35411
36201
  meta: { name: "clear", description: "Discard all Google Analytics changes staged on this chat" },
35412
36202
  run: async () => {
35413
36203
  await draftAction2("/api/ga4/draft/clear", {});
@@ -35417,7 +36207,7 @@ var draftCommand3 = defineCommand119({
35417
36207
  });
35418
36208
 
35419
36209
  // src/commands/ga4/properties.ts
35420
- import { defineCommand as defineCommand120 } from "citty";
36210
+ import { defineCommand as defineCommand121 } from "citty";
35421
36211
  registerSchema({
35422
36212
  command: "ga4.properties",
35423
36213
  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.",
@@ -35433,7 +36223,7 @@ function propertyHints(properties) {
35433
36223
  resources: properties.map((property) => ({ id: property.externalId, label: property.name }))
35434
36224
  });
35435
36225
  }
35436
- var propertiesCommand = defineCommand120({
36226
+ var propertiesCommand = defineCommand121({
35437
36227
  meta: {
35438
36228
  name: "properties",
35439
36229
  description: `List accessible GA4 properties.
@@ -35483,7 +36273,7 @@ Examples:
35483
36273
  // src/commands/ga4/query.ts
35484
36274
  import { appendFileSync as appendFileSync2, existsSync as existsSync7, readFileSync as readFileSync13, writeFileSync as writeFileSync4 } from "fs";
35485
36275
  import { resolve as resolve2 } from "path";
35486
- import { defineCommand as defineCommand121 } from "citty";
36276
+ import { defineCommand as defineCommand122 } from "citty";
35487
36277
 
35488
36278
  // src/commands/ga4/presets.ts
35489
36279
  var GA4_PRESETS = [
@@ -35618,7 +36408,7 @@ function handleError3(err) {
35618
36408
  });
35619
36409
  process.exit(1);
35620
36410
  }
35621
- var queryCommand2 = defineCommand121({
36411
+ var queryCommand2 = defineCommand122({
35622
36412
  meta: {
35623
36413
  name: "query",
35624
36414
  description: `Run GA4 Data API reports. Preset-first with free-form escape hatch.
@@ -35689,7 +36479,7 @@ Free-form (escape hatch):
35689
36479
  });
35690
36480
 
35691
36481
  // src/commands/ga4/write-commands.ts
35692
- import { defineCommand as defineCommand122 } from "citty";
36482
+ import { defineCommand as defineCommand123 } from "citty";
35693
36483
  var PROPERTY_ARG_DESCRIPTION = "GA4 property id (optional only when one property is connected \u2014 run `baker ga4 properties`)";
35694
36484
  var propertyArg = { type: "string", description: PROPERTY_ARG_DESCRIPTION, required: false };
35695
36485
  function createJsonDescription(noun) {
@@ -35707,7 +36497,7 @@ registerSchema({
35707
36497
  "property-id": { type: "string", description: PROPERTY_ARG_DESCRIPTION, required: false }
35708
36498
  }
35709
36499
  });
35710
- var keyEventCommand = defineCommand122({
36500
+ var keyEventCommand = defineCommand123({
35711
36501
  meta: {
35712
36502
  name: "key-event",
35713
36503
  description: `Stage key-event (conversion) changes
@@ -35720,7 +36510,7 @@ Examples:
35720
36510
  baker ga4 key-event delete 4185`
35721
36511
  },
35722
36512
  subCommands: {
35723
- create: defineCommand122({
36513
+ create: defineCommand123({
35724
36514
  meta: {
35725
36515
  name: "create",
35726
36516
  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."
@@ -35741,7 +36531,7 @@ Examples:
35741
36531
  );
35742
36532
  }
35743
36533
  }),
35744
- update: defineCommand122({
36534
+ update: defineCommand123({
35745
36535
  meta: {
35746
36536
  name: "update",
35747
36537
  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."
@@ -35761,7 +36551,7 @@ Examples:
35761
36551
  });
35762
36552
  }
35763
36553
  }),
35764
- delete: defineCommand122({
36554
+ delete: defineCommand123({
35765
36555
  meta: {
35766
36556
  name: "delete",
35767
36557
  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."
@@ -35809,7 +36599,7 @@ for (const { kind, noun, createHint } of DEFINITIONS) {
35809
36599
  }
35810
36600
  function definitionCommand(definition) {
35811
36601
  const { command, kind, noun, example } = definition;
35812
- return defineCommand122({
36602
+ return defineCommand123({
35813
36603
  meta: {
35814
36604
  name: command,
35815
36605
  description: `Stage ${noun} changes
@@ -35821,7 +36611,7 @@ Examples:
35821
36611
  baker ga4 ${command} archive 12`
35822
36612
  },
35823
36613
  subCommands: {
35824
- create: defineCommand122({
36614
+ create: defineCommand123({
35825
36615
  meta: { name: "create", description: `Stage a new ${noun} (or a JSON array of them, staged together)` },
35826
36616
  args: {
35827
36617
  json: { type: "string", description: createJsonDescription(noun), required: false },
@@ -35839,7 +36629,7 @@ Examples:
35839
36629
  );
35840
36630
  }
35841
36631
  }),
35842
- update: defineCommand122({
36632
+ update: defineCommand123({
35843
36633
  meta: {
35844
36634
  name: "update",
35845
36635
  description: `Stage a change to an existing ${noun} (pass its id). The parameter name and scope cannot change \u2014 archive and recreate for that.`
@@ -35859,7 +36649,7 @@ Examples:
35859
36649
  });
35860
36650
  }
35861
36651
  }),
35862
- archive: defineCommand122({
36652
+ archive: defineCommand123({
35863
36653
  meta: {
35864
36654
  name: "archive",
35865
36655
  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.`
@@ -35904,7 +36694,7 @@ var dataStreamArg = {
35904
36694
  function withStream(args) {
35905
36695
  return typeof args["data-stream"] === "string" ? { dataStream: args["data-stream"] } : {};
35906
36696
  }
35907
- var customEventCommand = defineCommand122({
36697
+ var customEventCommand = defineCommand123({
35908
36698
  meta: {
35909
36699
  name: "custom-event",
35910
36700
  description: `Stage custom events \u2014 new events built from events the site already sends
@@ -35915,7 +36705,7 @@ Examples:
35915
36705
  baker ga4 custom-event delete 7`
35916
36706
  },
35917
36707
  subCommands: {
35918
- create: defineCommand122({
36708
+ create: defineCommand123({
35919
36709
  meta: {
35920
36710
  name: "create",
35921
36711
  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."
@@ -35938,7 +36728,7 @@ Examples:
35938
36728
  );
35939
36729
  }
35940
36730
  }),
35941
- update: defineCommand122({
36731
+ update: defineCommand123({
35942
36732
  meta: { name: "update", description: "Stage a change to an existing custom event (pass its id)" },
35943
36733
  args: {
35944
36734
  id: { type: "positional", description: "Custom event rule id", required: false },
@@ -35957,7 +36747,7 @@ Examples:
35957
36747
  });
35958
36748
  }
35959
36749
  }),
35960
- delete: defineCommand122({
36750
+ delete: defineCommand123({
35961
36751
  meta: {
35962
36752
  name: "delete",
35963
36753
  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."
@@ -36012,7 +36802,7 @@ function booleanArg(input, flag) {
36012
36802
  if (input === "false") return false;
36013
36803
  return failValidation3(`--${flag} must be true or false`);
36014
36804
  }
36015
- var dataRetentionCommand = defineCommand122({
36805
+ var dataRetentionCommand = defineCommand123({
36016
36806
  meta: {
36017
36807
  name: "data-retention",
36018
36808
  description: `Stage a change to how long Google Analytics keeps data
@@ -36022,7 +36812,7 @@ Examples:
36022
36812
  baker ga4 data-retention set --event-data 14 --user-data 14 --reset-on-activity true`
36023
36813
  },
36024
36814
  subCommands: {
36025
- set: defineCommand122({
36815
+ set: defineCommand123({
36026
36816
  meta: { name: "set", description: "Stage the retention window (months)" },
36027
36817
  args: {
36028
36818
  "event-data": { type: "string", description: "2, 14, 26, 38 or 50", required: false },
@@ -36058,7 +36848,7 @@ registerSchema({
36058
36848
  "property-id": { type: "string", description: PROPERTY_ARG_DESCRIPTION, required: false }
36059
36849
  }
36060
36850
  });
36061
- var audienceCommand = defineCommand122({
36851
+ var audienceCommand = defineCommand123({
36062
36852
  meta: {
36063
36853
  name: "audience",
36064
36854
  description: `Stage audience changes (saved groups of users, for retargeting and analysis)
@@ -36090,7 +36880,7 @@ Examples:
36090
36880
  baker ga4 audience archive 8813`
36091
36881
  },
36092
36882
  subCommands: {
36093
- create: defineCommand122({
36883
+ create: defineCommand123({
36094
36884
  meta: { name: "create", description: createJsonDescription("audience") },
36095
36885
  args: {
36096
36886
  json: { type: "string", description: createJsonDescription("audience"), required: false },
@@ -36108,7 +36898,7 @@ Examples:
36108
36898
  );
36109
36899
  }
36110
36900
  }),
36111
- update: defineCommand122({
36901
+ update: defineCommand123({
36112
36902
  meta: {
36113
36903
  name: "update",
36114
36904
  description: "Stage a new name or description (pass the audience id). Who is in it cannot be changed."
@@ -36129,7 +36919,7 @@ Examples:
36129
36919
  });
36130
36920
  }
36131
36921
  }),
36132
- archive: defineCommand122({
36922
+ archive: defineCommand123({
36133
36923
  meta: {
36134
36924
  name: "archive",
36135
36925
  description: "Stage retiring an audience. Cannot be undone \u2014 the users it gathered do not come back."
@@ -36158,7 +36948,7 @@ registerSchema({
36158
36948
  "property-id": { type: "string", description: PROPERTY_ARG_DESCRIPTION, required: false }
36159
36949
  }
36160
36950
  });
36161
- var propertyCommand = defineCommand122({
36951
+ var propertyCommand = defineCommand123({
36162
36952
  meta: {
36163
36953
  name: "property",
36164
36954
  description: `Stage a change to the property's own settings
@@ -36168,7 +36958,7 @@ Examples:
36168
36958
  baker ga4 property set --name "Acme \u2014 Web" --industry REAL_ESTATE`
36169
36959
  },
36170
36960
  subCommands: {
36171
- set: defineCommand122({
36961
+ set: defineCommand123({
36172
36962
  meta: { name: "set", description: "Stage the property name and/or industry category" },
36173
36963
  args: {
36174
36964
  name: { type: "string", description: "New property display name", required: false },
@@ -36208,7 +36998,7 @@ registerSchema({
36208
36998
  "property-id": { type: "string", description: PROPERTY_ARG_DESCRIPTION, required: false }
36209
36999
  }
36210
37000
  });
36211
- var adsLinkCommand = defineCommand122({
37001
+ var adsLinkCommand = defineCommand123({
36212
37002
  meta: {
36213
37003
  name: "ads-link",
36214
37004
  description: `Stage linking this property to a Google Ads account
@@ -36223,7 +37013,7 @@ Examples:
36223
37013
  baker ga4 ads-link delete L1234`
36224
37014
  },
36225
37015
  subCommands: {
36226
- create: defineCommand122({
37016
+ create: defineCommand123({
36227
37017
  meta: { name: "create", description: "Stage linking the property to a Google Ads account" },
36228
37018
  args: {
36229
37019
  "customer-id": { type: "string", description: "Google Ads customer id (123-456-7890)", required: false },
@@ -36242,7 +37032,7 @@ Examples:
36242
37032
  await stageOp3({ kind: "ga4.googleAdsLink.create", payload, ...withProperty(args) });
36243
37033
  }
36244
37034
  }),
36245
- update: defineCommand122({
37035
+ update: defineCommand123({
36246
37036
  meta: {
36247
37037
  name: "update",
36248
37038
  description: "Stage the ads-personalisation setting on an existing link (pass its id)"
@@ -36264,7 +37054,7 @@ Examples:
36264
37054
  });
36265
37055
  }
36266
37056
  }),
36267
- delete: defineCommand122({
37057
+ delete: defineCommand123({
36268
37058
  meta: {
36269
37059
  name: "delete",
36270
37060
  description: "Stage unlinking. Stops conversion imports and strands every audience on this property."
@@ -36315,7 +37105,7 @@ var MEASUREMENT_FLAGS = {
36315
37105
  "page-changes": "pageChangesEnabled",
36316
37106
  "form-interactions": "formInteractionsEnabled"
36317
37107
  };
36318
- var enhancedMeasurementCommand = defineCommand122({
37108
+ var enhancedMeasurementCommand = defineCommand123({
36319
37109
  meta: {
36320
37110
  name: "enhanced-measurement",
36321
37111
  description: `Stage the automatic events GA4 collects on a website stream
@@ -36329,7 +37119,7 @@ Examples:
36329
37119
  baker ga4 enhanced-measurement set --video false`
36330
37120
  },
36331
37121
  subCommands: {
36332
- set: defineCommand122({
37122
+ set: defineCommand123({
36333
37123
  meta: { name: "set", description: "Stage which automatic events the website stream collects" },
36334
37124
  args: {
36335
37125
  scrolls: { type: "string", description: "true|false", required: false },
@@ -36372,7 +37162,7 @@ Examples:
36372
37162
  });
36373
37163
 
36374
37164
  // src/commands/ga4/index.ts
36375
- var ga4Command = defineCommand123({
37165
+ var ga4Command = defineCommand124({
36376
37166
  meta: {
36377
37167
  name: "ga4",
36378
37168
  description: `Google Analytics 4. Report on a property, audit its config, and change what it measures.
@@ -36421,12 +37211,12 @@ Full guide: __tooling__/docs/tools/baker/ga4.md`
36421
37211
  });
36422
37212
 
36423
37213
  // src/commands/gsc/index.ts
36424
- import { defineCommand as defineCommand127 } from "citty";
37214
+ import { defineCommand as defineCommand128 } from "citty";
36425
37215
 
36426
37216
  // src/commands/gsc/query.ts
36427
37217
  import { appendFileSync as appendFileSync3, existsSync as existsSync8, readFileSync as readFileSync14, writeFileSync as writeFileSync5 } from "fs";
36428
37218
  import { resolve as resolve3 } from "path";
36429
- import { defineCommand as defineCommand124 } from "citty";
37219
+ import { defineCommand as defineCommand125 } from "citty";
36430
37220
 
36431
37221
  // src/commands/gsc/presets.ts
36432
37222
  var GSC_PRESETS = [
@@ -36620,7 +37410,7 @@ function handleError4(err) {
36620
37410
  });
36621
37411
  process.exit(1);
36622
37412
  }
36623
- var queryCommand3 = defineCommand124({
37413
+ var queryCommand3 = defineCommand125({
36624
37414
  meta: {
36625
37415
  name: "query",
36626
37416
  description: `Run GSC Search Analytics queries. Preset-first with free-form escape hatch.
@@ -36698,7 +37488,7 @@ Free-form (escape hatch):
36698
37488
  });
36699
37489
 
36700
37490
  // src/commands/gsc/sitemaps.ts
36701
- import { defineCommand as defineCommand125 } from "citty";
37491
+ import { defineCommand as defineCommand126 } from "citty";
36702
37492
  registerSchema({
36703
37493
  command: "gsc.sitemaps",
36704
37494
  description: "List sitemaps for a Search Console site. Check sitemap health and errors.",
@@ -36707,7 +37497,7 @@ registerSchema({
36707
37497
  "no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
36708
37498
  }
36709
37499
  });
36710
- var sitemapsCommand = defineCommand125({
37500
+ var sitemapsCommand = defineCommand126({
36711
37501
  meta: {
36712
37502
  name: "sitemaps",
36713
37503
  description: `List sitemaps for a site. Check health and errors.
@@ -36760,7 +37550,7 @@ Examples:
36760
37550
  });
36761
37551
 
36762
37552
  // src/commands/gsc/sites.ts
36763
- import { defineCommand as defineCommand126 } from "citty";
37553
+ import { defineCommand as defineCommand127 } from "citty";
36764
37554
  registerSchema({
36765
37555
  command: "gsc.sites",
36766
37556
  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.",
@@ -36776,7 +37566,7 @@ function siteHints(sites) {
36776
37566
  resources: sites.map((site) => ({ id: site.siteUrl, label: site.permissionLevel }))
36777
37567
  });
36778
37568
  }
36779
- var sitesCommand = defineCommand126({
37569
+ var sitesCommand = defineCommand127({
36780
37570
  meta: {
36781
37571
  name: "sites",
36782
37572
  description: `List verified Search Console sites.
@@ -36824,7 +37614,7 @@ Examples:
36824
37614
  });
36825
37615
 
36826
37616
  // src/commands/gsc/index.ts
36827
- var gscCommand = defineCommand127({
37617
+ var gscCommand = defineCommand128({
36828
37618
  meta: {
36829
37619
  name: "gsc",
36830
37620
  description: `Google Search Console commands. PPC-SEO arbitrage, brand halo analysis, negative keyword discovery.
@@ -36848,7 +37638,7 @@ Full guide: __tooling__/docs/tools/baker/gsc.md`
36848
37638
  });
36849
37639
 
36850
37640
  // src/commands/history/index.ts
36851
- import { defineCommand as defineCommand128 } from "citty";
37641
+ import { defineCommand as defineCommand129 } from "citty";
36852
37642
  registerSchema({
36853
37643
  command: "history.list",
36854
37644
  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.",
@@ -36903,7 +37693,7 @@ function parseBoundedInt2(raw, name, min, max) {
36903
37693
  }
36904
37694
  return value;
36905
37695
  }
36906
- var listCommand13 = defineCommand128({
37696
+ var listCommand13 = defineCommand129({
36907
37697
  meta: {
36908
37698
  name: "list",
36909
37699
  description: "List recent account changes (unified audit log), newest first."
@@ -36949,7 +37739,7 @@ var listCommand13 = defineCommand128({
36949
37739
  }
36950
37740
  }
36951
37741
  });
36952
- var historyCommand = defineCommand128({
37742
+ var historyCommand = defineCommand129({
36953
37743
  meta: {
36954
37744
  name: "history",
36955
37745
  description: `Unified account history (audit log): what changed, who did it, and when.
@@ -36959,7 +37749,7 @@ Full guide: __tooling__/docs/tools/baker/history.md`
36959
37749
  });
36960
37750
 
36961
37751
  // src/commands/hubspot/index.ts
36962
- import { defineCommand as defineCommand129 } from "citty";
37752
+ import { defineCommand as defineCommand130 } from "citty";
36963
37753
  var EMBED_TYPES = ["legacy", "v4", "unknown"];
36964
37754
  function failNotConnected(err) {
36965
37755
  if (err instanceof ApiError && err.code === "FORBIDDEN" && err.message.includes(HUBSPOT_MISSING_GRANT_MARKER)) {
@@ -37113,7 +37903,7 @@ registerSchema({
37113
37903
  }
37114
37904
  }
37115
37905
  });
37116
- var formsListCommand = defineCommand129({
37906
+ var formsListCommand = defineCommand130({
37117
37907
  meta: {
37118
37908
  name: "list",
37119
37909
  description: "List HubSpot forms with embed type and post-submit action. Example: baker hubspot forms list --redirecting-only"
@@ -37158,7 +37948,7 @@ var formsListCommand = defineCommand129({
37158
37948
  }
37159
37949
  }
37160
37950
  });
37161
- var formsViewCommand = defineCommand129({
37951
+ var formsViewCommand = defineCommand130({
37162
37952
  meta: {
37163
37953
  name: "view",
37164
37954
  description: "Show one HubSpot form's fields and configuration. Example: baker hubspot forms view 1a2b3c"
@@ -37210,7 +38000,7 @@ var formsViewCommand = defineCommand129({
37210
38000
  }
37211
38001
  }
37212
38002
  });
37213
- var meetingsListCommand = defineCommand129({
38003
+ var meetingsListCommand = defineCommand130({
37214
38004
  meta: {
37215
38005
  name: "list",
37216
38006
  description: "List HubSpot meeting links (calendars). Example: baker hubspot meetings list"
@@ -37252,7 +38042,7 @@ var meetingsListCommand = defineCommand129({
37252
38042
  }
37253
38043
  }
37254
38044
  });
37255
- var meetingsViewCommand = defineCommand129({
38045
+ var meetingsViewCommand = defineCommand130({
37256
38046
  meta: {
37257
38047
  name: "view",
37258
38048
  description: "Show one HubSpot meeting link's booking fields. Example: baker hubspot meetings view discovery-call"
@@ -37296,7 +38086,7 @@ var meetingsViewCommand = defineCommand129({
37296
38086
  }
37297
38087
  }
37298
38088
  });
37299
- var formsSubmissionsCommand = defineCommand129({
38089
+ var formsSubmissionsCommand = defineCommand130({
37300
38090
  meta: {
37301
38091
  name: "submissions",
37302
38092
  description: "How many leads a form received, and when. Example: baker hubspot forms submissions <formId> --days 30"
@@ -37345,7 +38135,7 @@ var formsSubmissionsCommand = defineCommand129({
37345
38135
  }
37346
38136
  }
37347
38137
  });
37348
- var workflowsListCommand = defineCommand129({
38138
+ var workflowsListCommand = defineCommand130({
37349
38139
  meta: {
37350
38140
  name: "list",
37351
38141
  description: "List HubSpot workflows. Example: baker hubspot workflows list --enabled-only"
@@ -37410,7 +38200,7 @@ function workflowViewHints(workflow) {
37410
38200
  }
37411
38201
  return hints;
37412
38202
  }
37413
- var workflowsViewCommand = defineCommand129({
38203
+ var workflowsViewCommand = defineCommand130({
37414
38204
  meta: {
37415
38205
  name: "view",
37416
38206
  description: "Read one workflow's real configuration \u2014 enrolment, branches in order, and what each step writes. Example: baker hubspot workflows view 121183594 --full"
@@ -37437,7 +38227,7 @@ var workflowsViewCommand = defineCommand129({
37437
38227
  }
37438
38228
  }
37439
38229
  });
37440
- var pipelinesListCommand = defineCommand129({
38230
+ var pipelinesListCommand = defineCommand130({
37441
38231
  meta: {
37442
38232
  name: "list",
37443
38233
  description: "List HubSpot deal pipelines and their stages. Example: baker hubspot pipelines list"
@@ -37454,7 +38244,7 @@ var pipelinesListCommand = defineCommand129({
37454
38244
  }
37455
38245
  }
37456
38246
  });
37457
- var contactsSummaryCommand = defineCommand129({
38247
+ var contactsSummaryCommand = defineCommand130({
37458
38248
  meta: {
37459
38249
  name: "summary",
37460
38250
  description: "Whether recent leads are being worked, as counts. Example: baker hubspot contacts summary --days 30"
@@ -37524,7 +38314,7 @@ function contactLookupHints(data) {
37524
38314
  }
37525
38315
  return hints;
37526
38316
  }
37527
- var contactsLookupCommand = defineCommand129({
38317
+ var contactsLookupCommand = defineCommand130({
37528
38318
  meta: {
37529
38319
  name: "lookup",
37530
38320
  description: "Find one contact by email and see whether it was worked. Example: baker hubspot contacts lookup a@b.com"
@@ -37548,27 +38338,27 @@ var contactsLookupCommand = defineCommand129({
37548
38338
  }
37549
38339
  }
37550
38340
  });
37551
- var contactsCommand = defineCommand129({
38341
+ var contactsCommand = defineCommand130({
37552
38342
  meta: { name: "contacts", description: "Contacts on the connected HubSpot account." },
37553
38343
  subCommands: { summary: contactsSummaryCommand, lookup: contactsLookupCommand }
37554
38344
  });
37555
- var formsCommand = defineCommand129({
38345
+ var formsCommand = defineCommand130({
37556
38346
  meta: { name: "forms", description: "HubSpot forms on the connected account." },
37557
38347
  subCommands: { list: formsListCommand, view: formsViewCommand, submissions: formsSubmissionsCommand }
37558
38348
  });
37559
- var workflowsCommand = defineCommand129({
38349
+ var workflowsCommand = defineCommand130({
37560
38350
  meta: { name: "workflows", description: "HubSpot workflows on the connected account." },
37561
38351
  subCommands: { list: workflowsListCommand, view: workflowsViewCommand }
37562
38352
  });
37563
- var pipelinesCommand = defineCommand129({
38353
+ var pipelinesCommand = defineCommand130({
37564
38354
  meta: { name: "pipelines", description: "HubSpot deal pipelines on the connected account." },
37565
38355
  subCommands: { list: pipelinesListCommand }
37566
38356
  });
37567
- var meetingsCommand = defineCommand129({
38357
+ var meetingsCommand = defineCommand130({
37568
38358
  meta: { name: "meetings", description: "HubSpot meeting links (calendars) on the connected account." },
37569
38359
  subCommands: { list: meetingsListCommand, view: meetingsViewCommand }
37570
38360
  });
37571
- var hubspotCommand = defineCommand129({
38361
+ var hubspotCommand = defineCommand130({
37572
38362
  meta: {
37573
38363
  name: "hubspot",
37574
38364
  description: `Read the connected HubSpot account \u2014 forms, the leads they received, workflows, deal pipelines and
@@ -37606,10 +38396,10 @@ Full guide: __tooling__/docs/tools/baker/hubspot.md`
37606
38396
  });
37607
38397
 
37608
38398
  // src/commands/images/index.ts
37609
- import { defineCommand as defineCommand155 } from "citty";
38399
+ import { defineCommand as defineCommand156 } from "citty";
37610
38400
 
37611
38401
  // src/commands/images/crop.ts
37612
- import { defineCommand as defineCommand130 } from "citty";
38402
+ import { defineCommand as defineCommand131 } from "citty";
37613
38403
 
37614
38404
  // src/lib/image/crop-sprite.ts
37615
38405
  import sharp from "sharp";
@@ -37624,7 +38414,7 @@ function cropSprite(input, region) {
37624
38414
 
37625
38415
  // src/lib/image/io.ts
37626
38416
  import { randomBytes } from "crypto";
37627
- import { glob as fsGlob, readFile as readFile20, rename, stat as stat4, writeFile as writeFile11 } from "fs/promises";
38417
+ import { glob as fsGlob, readFile as readFile21, rename, stat as stat5, writeFile as writeFile11 } from "fs/promises";
37628
38418
  import { dirname as dirname2, extname as extname2, join as join5, resolve as resolve4 } from "path";
37629
38419
  var REMOTE_RE = /^https?:\/\//i;
37630
38420
  var GLOB_RE = /[*?[\]{}]/;
@@ -37657,11 +38447,11 @@ async function readImageBuffer(pathOrUrl) {
37657
38447
  const { buffer } = await fetchExternalBytes(pathOrUrl, { maxBytes: MAX_REMOTE_IMAGE_BYTES });
37658
38448
  return buffer;
37659
38449
  }
37660
- return readFile20(pathOrUrl);
38450
+ return readFile21(pathOrUrl);
37661
38451
  }
37662
- async function isDirectory(path40) {
38452
+ async function isDirectory(path43) {
37663
38453
  try {
37664
- const s = await stat4(path40);
38454
+ const s = await stat5(path43);
37665
38455
  return s.isDirectory();
37666
38456
  } catch {
37667
38457
  return false;
@@ -37731,7 +38521,7 @@ function emitError2(err) {
37731
38521
  }
37732
38522
  process.exit(1);
37733
38523
  }
37734
- var cropCommand = defineCommand130({
38524
+ var cropCommand = defineCommand131({
37735
38525
  meta: {
37736
38526
  name: "crop",
37737
38527
  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"
@@ -37767,7 +38557,7 @@ var cropCommand = defineCommand130({
37767
38557
  });
37768
38558
 
37769
38559
  // src/commands/images/delete.ts
37770
- import { defineCommand as defineCommand131 } from "citty";
38560
+ import { defineCommand as defineCommand132 } from "citty";
37771
38561
  registerSchema({
37772
38562
  command: "images.delete",
37773
38563
  description: "Delete an image by ID",
@@ -37781,7 +38571,7 @@ registerSchema({
37781
38571
  }
37782
38572
  }
37783
38573
  });
37784
- var deleteCommand2 = defineCommand131({
38574
+ var deleteCommand2 = defineCommand132({
37785
38575
  meta: {
37786
38576
  name: "delete",
37787
38577
  description: "Delete an image by ID. Use --dry-run to preview. Example: baker images delete j571abc123 --dry-run"
@@ -37822,7 +38612,7 @@ var deleteCommand2 = defineCommand131({
37822
38612
  });
37823
38613
 
37824
38614
  // src/commands/images/dimensions.ts
37825
- import { defineCommand as defineCommand132 } from "citty";
38615
+ import { defineCommand as defineCommand133 } from "citty";
37826
38616
 
37827
38617
  // src/lib/image/dimensions.ts
37828
38618
  import { imageSize } from "image-size";
@@ -37851,7 +38641,7 @@ registerSchema({
37851
38641
  }
37852
38642
  }
37853
38643
  });
37854
- var dimensionsCommand = defineCommand132({
38644
+ var dimensionsCommand = defineCommand133({
37855
38645
  meta: {
37856
38646
  name: "dimensions",
37857
38647
  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"
@@ -37907,7 +38697,7 @@ var dimensionsCommand = defineCommand132({
37907
38697
  });
37908
38698
 
37909
38699
  // src/commands/images/download.ts
37910
- import { defineCommand as defineCommand133 } from "citty";
38700
+ import { defineCommand as defineCommand134 } from "citty";
37911
38701
 
37912
38702
  // src/commands/images/downloadPaths.ts
37913
38703
  import { basename as basename2, extname as extname3, join as join6 } from "path";
@@ -37963,13 +38753,13 @@ function resolveDownloadPath({ baseName, extension, out, outIsDirectory: outIsDi
37963
38753
  }
37964
38754
  function disambiguate(paths) {
37965
38755
  const taken = /* @__PURE__ */ new Set();
37966
- return paths.map((path40) => {
37967
- if (!taken.has(path40)) {
37968
- taken.add(path40);
37969
- return path40;
38756
+ return paths.map((path43) => {
38757
+ if (!taken.has(path43)) {
38758
+ taken.add(path43);
38759
+ return path43;
37970
38760
  }
37971
- const ext = extname3(path40);
37972
- const stem = path40.slice(0, path40.length - ext.length);
38761
+ const ext = extname3(path43);
38762
+ const stem = path43.slice(0, path43.length - ext.length);
37973
38763
  let n = 2;
37974
38764
  while (taken.has(`${stem}-${n}${ext}`)) n += 1;
37975
38765
  const unique = `${stem}-${n}${ext}`;
@@ -38096,10 +38886,10 @@ async function runDownloads(plan) {
38096
38886
  const paths = disambiguate(fetched.map((item) => item.path));
38097
38887
  const downloaded = [];
38098
38888
  for (const [index, item] of fetched.entries()) {
38099
- const path40 = paths[index] ?? item.path;
38889
+ const path43 = paths[index] ?? item.path;
38100
38890
  try {
38101
- await atomicWrite(path40, item.buffer);
38102
- downloaded.push({ input: item.input, output: path40, bytes: item.buffer.length, contentType: item.contentType });
38891
+ await atomicWrite(path43, item.buffer);
38892
+ downloaded.push({ input: item.input, output: path43, bytes: item.buffer.length, contentType: item.contentType });
38103
38893
  } catch (err) {
38104
38894
  failed.push({ input: item.input, error: failureMessage(err, "Write failed") });
38105
38895
  }
@@ -38132,7 +38922,7 @@ function emitError3(err) {
38132
38922
  writeJson({ ok: false, error: { code: "INTERNAL_ERROR", message: "Unexpected error" } });
38133
38923
  process.exit(1);
38134
38924
  }
38135
- var downloadCommand = defineCommand133({
38925
+ var downloadCommand = defineCommand134({
38136
38926
  meta: {
38137
38927
  name: "download",
38138
38928
  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"
@@ -38165,7 +38955,7 @@ var downloadCommand = defineCommand133({
38165
38955
  });
38166
38956
 
38167
38957
  // src/commands/images/extract.ts
38168
- import { defineCommand as defineCommand134 } from "citty";
38958
+ import { defineCommand as defineCommand135 } from "citty";
38169
38959
 
38170
38960
  // src/commands/images/autoIngest.ts
38171
38961
  var AUTO_INGEST_MAX = {
@@ -38212,7 +39002,7 @@ registerSchema({
38212
39002
  }
38213
39003
  }
38214
39004
  });
38215
- var extractCommand = defineCommand134({
39005
+ var extractCommand = defineCommand135({
38216
39006
  meta: {
38217
39007
  name: "extract",
38218
39008
  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"
@@ -38267,7 +39057,7 @@ var extractCommand = defineCommand134({
38267
39057
  });
38268
39058
 
38269
39059
  // src/commands/images/find.ts
38270
- import { defineCommand as defineCommand135 } from "citty";
39060
+ import { defineCommand as defineCommand136 } from "citty";
38271
39061
 
38272
39062
  // src/commands/images/providerHits.ts
38273
39063
  function asRecord3(value) {
@@ -38405,7 +39195,7 @@ registerSchema({
38405
39195
  full: { type: "boolean", description: "Include full metadata", required: false, default: false }
38406
39196
  }
38407
39197
  });
38408
- var findCommand = defineCommand135({
39198
+ var findCommand = defineCommand136({
38409
39199
  meta: {
38410
39200
  name: "find",
38411
39201
  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"
@@ -38478,7 +39268,7 @@ var findCommand = defineCommand135({
38478
39268
  });
38479
39269
 
38480
39270
  // src/commands/images/get.ts
38481
- import { defineCommand as defineCommand136 } from "citty";
39271
+ import { defineCommand as defineCommand137 } from "citty";
38482
39272
  registerSchema({
38483
39273
  command: "images.get",
38484
39274
  description: "Get a single image by ID",
@@ -38486,7 +39276,7 @@ registerSchema({
38486
39276
  id: { type: "string", description: "Image ID", required: true }
38487
39277
  }
38488
39278
  });
38489
- var getCommand3 = defineCommand136({
39279
+ var getCommand3 = defineCommand137({
38490
39280
  meta: { name: "get", description: "Get a single image by ID. Example: baker images get j571abc123" },
38491
39281
  args: {
38492
39282
  id: { type: "positional", description: "Image ID", required: false },
@@ -38522,7 +39312,7 @@ var getCommand3 = defineCommand136({
38522
39312
  });
38523
39313
 
38524
39314
  // src/commands/images/gif.ts
38525
- import { defineCommand as defineCommand137 } from "citty";
39315
+ import { defineCommand as defineCommand138 } from "citty";
38526
39316
  registerSchema({
38527
39317
  command: "images.gif",
38528
39318
  description: "Search Giphy for GIFs / reaction memes (paid social creative).",
@@ -38554,7 +39344,7 @@ registerSchema({
38554
39344
  }
38555
39345
  }
38556
39346
  });
38557
- var gifCommand = defineCommand137({
39347
+ var gifCommand = defineCommand138({
38558
39348
  meta: {
38559
39349
  name: "gif",
38560
39350
  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"
@@ -38601,7 +39391,7 @@ var gifCommand = defineCommand137({
38601
39391
  });
38602
39392
 
38603
39393
  // src/commands/images/google.ts
38604
- import { defineCommand as defineCommand138 } from "citty";
39394
+ import { defineCommand as defineCommand139 } from "citty";
38605
39395
  var GOOGLE_ERROR_FIX = {
38606
39396
  action: "use_different_resource",
38607
39397
  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."
@@ -38641,7 +39431,7 @@ registerSchema({
38641
39431
  }
38642
39432
  }
38643
39433
  });
38644
- var googleCommand2 = defineCommand138({
39434
+ var googleCommand2 = defineCommand139({
38645
39435
  meta: {
38646
39436
  name: "google",
38647
39437
  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"
@@ -38714,7 +39504,7 @@ var googleCommand2 = defineCommand138({
38714
39504
  });
38715
39505
 
38716
39506
  // src/commands/images/group.ts
38717
- import { defineCommand as defineCommand139 } from "citty";
39507
+ import { defineCommand as defineCommand140 } from "citty";
38718
39508
 
38719
39509
  // src/commands/mediaGroup.ts
38720
39510
  async function runMediaGroupLookup(input) {
@@ -38767,7 +39557,7 @@ registerSchema({
38767
39557
  "group-key": { type: "string", description: "The set key directly, when you already have it", required: false }
38768
39558
  }
38769
39559
  });
38770
- var groupCommand = defineCommand139({
39560
+ var groupCommand = defineCommand140({
38771
39561
  meta: {
38772
39562
  name: "group",
38773
39563
  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>"
@@ -38787,7 +39577,7 @@ var groupCommand = defineCommand139({
38787
39577
  });
38788
39578
 
38789
39579
  // src/commands/images/icon.ts
38790
- import { defineCommand as defineCommand140 } from "citty";
39580
+ import { defineCommand as defineCommand141 } from "citty";
38791
39581
 
38792
39582
  // src/commands/images/brandVerification.ts
38793
39583
  function brandToken(domain) {
@@ -38884,7 +39674,7 @@ registerSchema({
38884
39674
  full: { type: "boolean", description: "Include full metadata", required: false, default: false }
38885
39675
  }
38886
39676
  });
38887
- var iconCommand = defineCommand140({
39677
+ var iconCommand = defineCommand141({
38888
39678
  meta: {
38889
39679
  name: "icon",
38890
39680
  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'"
@@ -38954,7 +39744,7 @@ var iconCommand = defineCommand140({
38954
39744
  });
38955
39745
 
38956
39746
  // src/commands/images/ingest.ts
38957
- import { defineCommand as defineCommand141 } from "citty";
39747
+ import { defineCommand as defineCommand142 } from "citty";
38958
39748
  var SOURCE_VALUES = imageSourceSchema.options.join(" | ");
38959
39749
  registerSchema({
38960
39750
  command: "images.ingest",
@@ -38969,7 +39759,7 @@ registerSchema({
38969
39759
  fields: { type: "string", description: "Comma-separated field names to include", required: false }
38970
39760
  }
38971
39761
  });
38972
- var ingestCommand = defineCommand141({
39762
+ var ingestCommand = defineCommand142({
38973
39763
  meta: {
38974
39764
  name: "ingest",
38975
39765
  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"
@@ -39038,7 +39828,7 @@ var ingestCommand = defineCommand141({
39038
39828
  });
39039
39829
 
39040
39830
  // src/commands/images/layerize.ts
39041
- import { defineCommand as defineCommand142 } from "citty";
39831
+ import { defineCommand as defineCommand143 } from "citty";
39042
39832
  registerSchema({
39043
39833
  command: "images.layerize",
39044
39834
  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.",
@@ -39102,7 +39892,7 @@ async function pollUntilSettled(imageId, maxWait) {
39102
39892
  }
39103
39893
  return null;
39104
39894
  }
39105
- var layerizeCommand = defineCommand142({
39895
+ var layerizeCommand = defineCommand143({
39106
39896
  meta: {
39107
39897
  name: "layerize",
39108
39898
  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'"
@@ -39171,7 +39961,7 @@ registerSchema({
39171
39961
  full: { type: "boolean", description: "Include geometry and typography for every layer", required: false }
39172
39962
  }
39173
39963
  });
39174
- var layersCommand = defineCommand142({
39964
+ var layersCommand = defineCommand143({
39175
39965
  meta: {
39176
39966
  name: "layers",
39177
39967
  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"
@@ -39211,7 +40001,7 @@ var layersCommand = defineCommand142({
39211
40001
  });
39212
40002
 
39213
40003
  // src/commands/images/library.ts
39214
- import { defineCommand as defineCommand143 } from "citty";
40004
+ import { defineCommand as defineCommand144 } from "citty";
39215
40005
  registerSchema({
39216
40006
  command: "images.library",
39217
40007
  description: "Search the company image library. Returns only ready images.",
@@ -39237,7 +40027,7 @@ registerSchema({
39237
40027
  }
39238
40028
  }
39239
40029
  });
39240
- var libraryCommand = defineCommand143({
40030
+ var libraryCommand = defineCommand144({
39241
40031
  meta: {
39242
40032
  name: "library",
39243
40033
  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"
@@ -39300,7 +40090,7 @@ var libraryCommand = defineCommand143({
39300
40090
  });
39301
40091
 
39302
40092
  // src/commands/images/logo.ts
39303
- import { defineCommand as defineCommand144 } from "citty";
40093
+ import { defineCommand as defineCommand145 } from "citty";
39304
40094
  registerSchema({
39305
40095
  command: "images.logo",
39306
40096
  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.",
@@ -39328,7 +40118,7 @@ registerSchema({
39328
40118
  full: { type: "boolean", description: "Include full metadata", required: false, default: false }
39329
40119
  }
39330
40120
  });
39331
- var logoCommand = defineCommand144({
40121
+ var logoCommand = defineCommand145({
39332
40122
  meta: {
39333
40123
  name: "logo",
39334
40124
  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"
@@ -39401,7 +40191,7 @@ var logoCommand = defineCommand144({
39401
40191
  });
39402
40192
 
39403
40193
  // src/commands/images/normalize.ts
39404
- import { defineCommand as defineCommand145 } from "citty";
40194
+ import { defineCommand as defineCommand146 } from "citty";
39405
40195
 
39406
40196
  // src/lib/image/color-changer.ts
39407
40197
  import quantize from "quantize";
@@ -40133,7 +40923,7 @@ function coerceRawArgs(args) {
40133
40923
  "dry-run": bool(args["dry-run"])
40134
40924
  };
40135
40925
  }
40136
- var normalizeCommand2 = defineCommand145({
40926
+ var normalizeCommand2 = defineCommand146({
40137
40927
  meta: {
40138
40928
  name: "normalize",
40139
40929
  description: `Normalize logos / images: declarative recolor + bg removal + trim + resize. Operates on local files; writes in-place by default.
@@ -40188,7 +40978,7 @@ Examples:
40188
40978
  });
40189
40979
 
40190
40980
  // src/commands/images/pinterest.ts
40191
- import { defineCommand as defineCommand146 } from "citty";
40981
+ import { defineCommand as defineCommand147 } from "citty";
40192
40982
  registerSchema({
40193
40983
  command: "images.pinterest",
40194
40984
  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.",
@@ -40208,7 +40998,7 @@ registerSchema({
40208
40998
  }
40209
40999
  }
40210
41000
  });
40211
- var pinterestCommand = defineCommand146({
41001
+ var pinterestCommand = defineCommand147({
40212
41002
  meta: {
40213
41003
  name: "pinterest",
40214
41004
  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'"
@@ -40265,7 +41055,7 @@ var pinterestCommand = defineCommand146({
40265
41055
  });
40266
41056
 
40267
41057
  // src/commands/images/screenshot.ts
40268
- import { defineCommand as defineCommand147 } from "citty";
41058
+ import { defineCommand as defineCommand148 } from "citty";
40269
41059
  registerSchema({
40270
41060
  command: "images.screenshot",
40271
41061
  description: "Capture a website screenshot via ScreenshotOne. Auto-ingests on success.",
@@ -40284,7 +41074,7 @@ registerSchema({
40284
41074
  full: { type: "boolean", description: "Include full metadata", required: false, default: false }
40285
41075
  }
40286
41076
  });
40287
- var screenshotCommand = defineCommand147({
41077
+ var screenshotCommand = defineCommand148({
40288
41078
  meta: {
40289
41079
  name: "screenshot",
40290
41080
  description: "Screenshot a URL via ScreenshotOne. $0.009/capture. Auto-ingests to library.\n\nExample: baker images screenshot https://stripe.com --full-page"
@@ -40348,7 +41138,7 @@ var screenshotCommand = defineCommand147({
40348
41138
  });
40349
41139
 
40350
41140
  // src/commands/images/search.ts
40351
- import { defineCommand as defineCommand148 } from "citty";
41141
+ import { defineCommand as defineCommand149 } from "citty";
40352
41142
  registerSchema({
40353
41143
  command: "images.search",
40354
41144
  description: "Search images by text query. Only returns ready images.",
@@ -40364,7 +41154,7 @@ registerSchema({
40364
41154
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
40365
41155
  }
40366
41156
  });
40367
- var searchCommand = defineCommand148({
41157
+ var searchCommand = defineCommand149({
40368
41158
  meta: {
40369
41159
  name: "search",
40370
41160
  description: "Semantic search images by text query. Uses hybrid BM25 + vector + reranking. Example: baker images search 'hero banner' --aspect-ratio 16:9 --tags logo"
@@ -40424,7 +41214,7 @@ var searchCommand = defineCommand148({
40424
41214
  });
40425
41215
 
40426
41216
  // src/commands/images/sticker.ts
40427
- import { defineCommand as defineCommand149 } from "citty";
41217
+ import { defineCommand as defineCommand150 } from "citty";
40428
41218
  registerSchema({
40429
41219
  command: "images.sticker",
40430
41220
  description: "Search Giphy stickers \u2014 transparent-background overlays for ad creative.",
@@ -40456,7 +41246,7 @@ registerSchema({
40456
41246
  }
40457
41247
  }
40458
41248
  });
40459
- var stickerCommand = defineCommand149({
41249
+ var stickerCommand = defineCommand150({
40460
41250
  meta: {
40461
41251
  name: "sticker",
40462
41252
  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"
@@ -40503,7 +41293,7 @@ var stickerCommand = defineCommand149({
40503
41293
  });
40504
41294
 
40505
41295
  // src/commands/images/stock.ts
40506
- import { defineCommand as defineCommand150 } from "citty";
41296
+ import { defineCommand as defineCommand151 } from "citty";
40507
41297
  var STOCK_ERROR_FIX = {
40508
41298
  action: "use_different_resource",
40509
41299
  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."
@@ -40580,7 +41370,7 @@ function buildStockRequest(query, args) {
40580
41370
  if (args.context) body.descriptionContext = args.context;
40581
41371
  return body;
40582
41372
  }
40583
- var stockCommand = defineCommand150({
41373
+ var stockCommand = defineCommand151({
40584
41374
  meta: {
40585
41375
  name: "stock",
40586
41376
  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"
@@ -40653,7 +41443,7 @@ var stockCommand = defineCommand150({
40653
41443
  });
40654
41444
 
40655
41445
  // src/lib/tags-command.ts
40656
- import { defineCommand as defineCommand151 } from "citty";
41446
+ import { defineCommand as defineCommand152 } from "citty";
40657
41447
  function makeTagsCommand(command, label, endpoint) {
40658
41448
  registerSchema({
40659
41449
  command: `${command}.tags`,
@@ -40662,7 +41452,7 @@ function makeTagsCommand(command, label, endpoint) {
40662
41452
  output: { type: "string", description: "Output format: md|json", required: false, default: "md" }
40663
41453
  }
40664
41454
  });
40665
- return defineCommand151({
41455
+ return defineCommand152({
40666
41456
  meta: {
40667
41457
  name: "tags",
40668
41458
  description: `List the available ${label} tag names (defaults + company custom tags). Use before filtering with --tags. Example: baker ${command} tags`
@@ -40698,7 +41488,7 @@ function makeTagsCommand(command, label, endpoint) {
40698
41488
  var tagsCommand3 = makeTagsCommand("images", "image", "/api/images/tags");
40699
41489
 
40700
41490
  // src/commands/images/upload.ts
40701
- import { defineCommand as defineCommand152 } from "citty";
41491
+ import { defineCommand as defineCommand153 } from "citty";
40702
41492
  registerSchema({
40703
41493
  command: "images.upload",
40704
41494
  description: "Upload an image to the library \u2014 local file path or remote http(s) URL.",
@@ -40736,7 +41526,7 @@ registerSchema({
40736
41526
  function isRemoteUrl2(value) {
40737
41527
  return /^https?:\/\//i.test(value);
40738
41528
  }
40739
- var uploadCommand = defineCommand152({
41529
+ var uploadCommand = defineCommand153({
40740
41530
  meta: {
40741
41531
  name: "upload",
40742
41532
  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'"
@@ -40829,7 +41619,7 @@ async function uploadLocal(target, args) {
40829
41619
  }
40830
41620
 
40831
41621
  // src/commands/images/upscale.ts
40832
- import { defineCommand as defineCommand153 } from "citty";
41622
+ import { defineCommand as defineCommand154 } from "citty";
40833
41623
  registerSchema({
40834
41624
  command: "images.upscale",
40835
41625
  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).",
@@ -40844,7 +41634,7 @@ registerSchema({
40844
41634
  }
40845
41635
  });
40846
41636
  var POLL_INTERVAL_MS4 = 1500;
40847
- var upscaleCommand = defineCommand153({
41637
+ var upscaleCommand = defineCommand154({
40848
41638
  meta: {
40849
41639
  name: "upscale",
40850
41640
  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"
@@ -40899,7 +41689,7 @@ var upscaleCommand = defineCommand153({
40899
41689
  });
40900
41690
 
40901
41691
  // src/commands/images/use.ts
40902
- import { defineCommand as defineCommand154 } from "citty";
41692
+ import { defineCommand as defineCommand155 } from "citty";
40903
41693
  registerSchema({
40904
41694
  command: "images.use",
40905
41695
  description: "Ingest a URL and wait for the library record to be ready.",
@@ -40928,7 +41718,7 @@ function emitReady(ingestResult, doc, args) {
40928
41718
  args.full === true
40929
41719
  );
40930
41720
  }
40931
- var useCommand = defineCommand154({
41721
+ var useCommand = defineCommand155({
40932
41722
  meta: {
40933
41723
  name: "use",
40934
41724
  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"
@@ -40977,7 +41767,7 @@ var useCommand = defineCommand154({
40977
41767
  });
40978
41768
 
40979
41769
  // src/commands/images/index.ts
40980
- var imagesCommand = defineCommand155({
41770
+ var imagesCommand = defineCommand156({
40981
41771
  meta: {
40982
41772
  name: "images",
40983
41773
  description: `Find, source, and normalize images. Subcommands route by provider so cost + license are explicit.
@@ -41055,12 +41845,12 @@ Full guide: __tooling__/docs/tools/baker/images.md`
41055
41845
  });
41056
41846
 
41057
41847
  // src/commands/landing/index.ts
41058
- import { defineCommand as defineCommand166 } from "citty";
41848
+ import { defineCommand as defineCommand168 } from "citty";
41059
41849
 
41060
41850
  // src/commands/landing/critique.ts
41061
41851
  import { readdir as readdir9, stat as stat6 } from "fs/promises";
41062
- import path29 from "path";
41063
- import { defineCommand as defineCommand156 } from "citty";
41852
+ import path30 from "path";
41853
+ import { defineCommand as defineCommand157 } from "citty";
41064
41854
 
41065
41855
  // src/engine/landing/lib/constants.ts
41066
41856
  var OVERUSED_FONTS = /* @__PURE__ */ new Set([
@@ -42099,13 +42889,13 @@ function describeCounts(findings) {
42099
42889
  }
42100
42890
 
42101
42891
  // src/engine/landing/lib/referenceStore.ts
42102
- import { mkdir as mkdir8, readFile as readFile21, writeFile as writeFile12 } from "fs/promises";
42103
- import path26 from "path";
42892
+ import { mkdir as mkdir8, readFile as readFile22, writeFile as writeFile12 } from "fs/promises";
42893
+ import path27 from "path";
42104
42894
  var REFERENCES_FILE = ".cache/inspiration-refs.json";
42105
42895
  var REFERENCE_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
42106
42896
  async function readReferences(projectRoot) {
42107
42897
  try {
42108
- const raw = await readFile21(path26.join(projectRoot, REFERENCES_FILE), "utf8");
42898
+ const raw = await readFile22(path27.join(projectRoot, REFERENCES_FILE), "utf8");
42109
42899
  const parsed = JSON.parse(raw);
42110
42900
  if (!Array.isArray(parsed)) return [];
42111
42901
  const cutoff = Date.now() - REFERENCE_TTL_MS;
@@ -42129,8 +42919,8 @@ async function recordReferences(projectRoot, references) {
42129
42919
  const existing = await readReferences(projectRoot);
42130
42920
  const replaced = new Set(references.map((reference) => reference.sectionId));
42131
42921
  const merged = [...existing.filter((entry) => !replaced.has(entry.sectionId)), ...references];
42132
- const file = path26.join(projectRoot, REFERENCES_FILE);
42133
- await mkdir8(path26.dirname(file), { recursive: true });
42922
+ const file = path27.join(projectRoot, REFERENCES_FILE);
42923
+ await mkdir8(path27.dirname(file), { recursive: true });
42134
42924
  await writeFile12(file, `${JSON.stringify(merged, null, 2)}
42135
42925
  `);
42136
42926
  return true;
@@ -42141,13 +42931,13 @@ async function recordReferences(projectRoot, references) {
42141
42931
 
42142
42932
  // src/commands/landing/snapshot.ts
42143
42933
  import { mkdir as mkdir9, rename as rename2, writeFile as writeFile13 } from "fs/promises";
42144
- import path27 from "path";
42934
+ import path28 from "path";
42145
42935
  var CRITIC_VERSION = "2";
42146
42936
  function critiqueCacheDir(projectRoot) {
42147
- return path27.join(projectRoot, ".cache", "landing-critique");
42937
+ return path28.join(projectRoot, ".cache", "landing-critique");
42148
42938
  }
42149
42939
  function snapshotPath(projectRoot, slug) {
42150
- return path27.join(critiqueCacheDir(projectRoot), `${slug}.json`);
42940
+ return path28.join(critiqueCacheDir(projectRoot), `${slug}.json`);
42151
42941
  }
42152
42942
  async function writeCritiqueSnapshot(projectRoot, snapshot) {
42153
42943
  await mkdir9(critiqueCacheDir(projectRoot), { recursive: true });
@@ -42159,30 +42949,26 @@ async function writeCritiqueSnapshot(projectRoot, snapshot) {
42159
42949
  }
42160
42950
 
42161
42951
  // src/commands/landing/source-version.ts
42162
- import { readdir as readdir8, readFile as readFile22, stat as stat5 } from "fs/promises";
42163
- import path28 from "path";
42164
- async function landingSourceRelPaths(landingDir) {
42165
- const rel = [];
42166
- if (await isFile(path28.join(landingDir, "index.astro"))) rel.push("index.astro");
42167
- const componentsDir = path28.join(landingDir, "_components");
42168
- for (const abs of await walkAstro(componentsDir)) {
42169
- rel.push(path28.relative(landingDir, abs).split(path28.sep).join("/"));
42170
- }
42171
- return rel.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
42952
+ import { readFile as readFile23 } from "fs/promises";
42953
+ import path29 from "path";
42954
+ var CRITIQUED_ROOTS = ["src/pages/", "src/components/"];
42955
+ async function landingSourceRelPaths(root, slug) {
42956
+ const files = await landingGraphFiles(root, slug);
42957
+ return [...files].filter((rel) => rel.endsWith(".astro") && CRITIQUED_ROOTS.some((r) => rel.startsWith(r))).sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
42172
42958
  }
42173
- async function readLandingSources(landingDir) {
42174
- const rel = await landingSourceRelPaths(landingDir);
42959
+ async function readLandingSources(root, slug) {
42960
+ const rel = await landingSourceRelPaths(root, slug);
42175
42961
  const out = [];
42176
- for (const r of rel) out.push({ path: r, text: await readFile22(path28.join(landingDir, r), "utf8") });
42962
+ for (const r of rel) out.push({ path: r, text: await readFile23(path29.join(root, r), "utf8") });
42177
42963
  return out;
42178
42964
  }
42179
- async function computeLandingSourceSha(landingDir) {
42180
- const rel = await landingSourceRelPaths(landingDir);
42965
+ async function computeLandingSourceSha(root, slug) {
42966
+ const rel = await landingSourceRelPaths(root, slug);
42181
42967
  const parts = [];
42182
42968
  for (const r of rel) {
42183
42969
  let bytes;
42184
42970
  try {
42185
- bytes = await readFile22(path28.join(landingDir, r));
42971
+ bytes = await readFile23(path29.join(root, r));
42186
42972
  } catch {
42187
42973
  bytes = Buffer.alloc(0);
42188
42974
  }
@@ -42190,28 +42976,6 @@ async function computeLandingSourceSha(landingDir) {
42190
42976
  }
42191
42977
  return sha256Hex(Buffer.concat(parts));
42192
42978
  }
42193
- async function isFile(p) {
42194
- try {
42195
- return (await stat5(p)).isFile();
42196
- } catch {
42197
- return false;
42198
- }
42199
- }
42200
- async function walkAstro(dir) {
42201
- let entries;
42202
- try {
42203
- entries = await readdir8(dir, { withFileTypes: true });
42204
- } catch {
42205
- return [];
42206
- }
42207
- const out = [];
42208
- for (const entry of entries) {
42209
- const abs = path28.join(dir, entry.name);
42210
- if (entry.isDirectory()) out.push(...await walkAstro(abs));
42211
- else if (entry.isFile() && entry.name.endsWith(".astro")) out.push(abs);
42212
- }
42213
- return out;
42214
- }
42215
42979
 
42216
42980
  // src/commands/landing/critique.ts
42217
42981
  registerSchema({
@@ -42236,14 +43000,14 @@ function parseCritiqueSlugs(args) {
42236
43000
  const all = [args.slug, ...rest].filter((s) => typeof s === "string" && s.length > 0);
42237
43001
  return [...new Set(all.map(String))];
42238
43002
  }
42239
- function fail5(code, message, fix) {
43003
+ function fail6(code, message, fix) {
42240
43004
  process.stderr.write(
42241
43005
  `${JSON.stringify({ ok: false, error: { code, message, ...fix ? { fix } : {} } }, null, 2)}
42242
43006
  `
42243
43007
  );
42244
43008
  process.exit(2);
42245
43009
  }
42246
- var critiqueCommand2 = defineCommand156({
43010
+ var critiqueCommand2 = defineCommand157({
42247
43011
  meta: {
42248
43012
  name: "critique",
42249
43013
  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."
@@ -42261,14 +43025,14 @@ var critiqueCommand2 = defineCommand156({
42261
43025
  const projectRoot = process.cwd();
42262
43026
  for (const slug of slugs) {
42263
43027
  if (!SLUG_RE.test(slug) || slug.includes("..")) {
42264
- fail5(
43028
+ fail6(
42265
43029
  "INVALID_SLUG",
42266
43030
  `"${slug}" is not a landing slug \u2014 use the folder name directly under src/pages/ (letters, digits, dashes; not a path, not a _-private folder).`,
42267
43031
  { availableSlugs: await listLandingSlugs(projectRoot) }
42268
43032
  );
42269
43033
  }
42270
- if (!await isDir(path29.resolve(projectRoot, "src", "pages", slug))) {
42271
- fail5("NOT_FOUND", `No landing at src/pages/${slug}/`, {
43034
+ if (!await isDir(path30.resolve(projectRoot, "src", "pages", slug))) {
43035
+ fail6("NOT_FOUND", `No landing at src/pages/${slug}/`, {
42272
43036
  availableSlugs: await listLandingSlugs(projectRoot)
42273
43037
  });
42274
43038
  }
@@ -42306,8 +43070,10 @@ var critiqueCommand2 = defineCommand156({
42306
43070
  }
42307
43071
  });
42308
43072
  async function critiqueOne(projectRoot, slug, brand, references) {
42309
- const landingDir = path29.resolve(projectRoot, "src", "pages", slug);
42310
- const [sources, sourceSha] = await Promise.all([readLandingSources(landingDir), computeLandingSourceSha(landingDir)]);
43073
+ const [sources, sourceSha] = await Promise.all([
43074
+ readLandingSources(projectRoot, slug),
43075
+ computeLandingSourceSha(projectRoot, slug)
43076
+ ]);
42311
43077
  const report = critiqueLanding({ slug, sources, brand, references });
42312
43078
  let snapshotFailed = false;
42313
43079
  try {
@@ -42326,7 +43092,7 @@ async function critiqueOne(projectRoot, slug, brand, references) {
42326
43092
  }
42327
43093
  async function listLandingSlugs(projectRoot) {
42328
43094
  try {
42329
- const entries = await readdir9(path29.join(projectRoot, "src", "pages"), { withFileTypes: true });
43095
+ const entries = await readdir9(path30.join(projectRoot, "src", "pages"), { withFileTypes: true });
42330
43096
  return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).map((e) => e.name).sort();
42331
43097
  } catch {
42332
43098
  return [];
@@ -42352,10 +43118,10 @@ async function isDir(p) {
42352
43118
  }
42353
43119
 
42354
43120
  // src/commands/landing/inspiration/index.ts
42355
- import { defineCommand as defineCommand165 } from "citty";
43121
+ import { defineCommand as defineCommand166 } from "citty";
42356
43122
 
42357
43123
  // src/commands/landing/inspiration/add.ts
42358
- import { defineCommand as defineCommand157 } from "citty";
43124
+ import { defineCommand as defineCommand158 } from "citty";
42359
43125
 
42360
43126
  // src/commands/landing/inspiration/shared.ts
42361
43127
  var INSPIRATION_HINTS = {
@@ -42437,7 +43203,7 @@ registerSchema({
42437
43203
  note: { type: "string", description: "Why this page is worth keeping", required: false }
42438
43204
  }
42439
43205
  });
42440
- var addCommand = defineCommand157({
43206
+ var addCommand = defineCommand158({
42441
43207
  meta: {
42442
43208
  name: "add",
42443
43209
  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'"
@@ -42478,8 +43244,8 @@ var addCommand = defineCommand157({
42478
43244
 
42479
43245
  // src/commands/landing/inspiration/code.ts
42480
43246
  import { mkdir as mkdir10, writeFile as writeFile14 } from "fs/promises";
42481
- import path30 from "path";
42482
- import { defineCommand as defineCommand158 } from "citty";
43247
+ import path31 from "path";
43248
+ import { defineCommand as defineCommand159 } from "citty";
42483
43249
  registerSchema({
42484
43250
  command: "landing.inspiration.code",
42485
43251
  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. Consulting a section records it, and `baker landing critique` blocks a publish that ships its copy verbatim.",
@@ -42488,7 +43254,7 @@ registerSchema({
42488
43254
  full: { type: "boolean", description: "Print the markup inline as well as writing it", required: false }
42489
43255
  }
42490
43256
  });
42491
- var codeCommand = defineCommand158({
43257
+ var codeCommand = defineCommand159({
42492
43258
  meta: {
42493
43259
  name: "code",
42494
43260
  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."
@@ -42501,9 +43267,9 @@ var codeCommand = defineCommand158({
42501
43267
  try {
42502
43268
  const id = args.id;
42503
43269
  const data = await apiGet("/api/landing-inspiration/section-code", { id });
42504
- const dir = path30.join(process.cwd(), ".baker", "inspiration", id);
43270
+ const dir = path31.join(process.cwd(), ".baker", "inspiration", id);
42505
43271
  await mkdir10(dir, { recursive: true });
42506
- const file = path30.join(dir, "section.html");
43272
+ const file = path31.join(dir, "section.html");
42507
43273
  await writeFile14(file, data.html);
42508
43274
  const recorded = await recordReference(process.cwd(), {
42509
43275
  sectionId: id,
@@ -42524,7 +43290,7 @@ var codeCommand = defineCommand158({
42524
43290
  ok: true,
42525
43291
  data: {
42526
43292
  id,
42527
- file: path30.relative(process.cwd(), file),
43293
+ file: path31.relative(process.cwd(), file),
42528
43294
  bytes: data.html.length,
42529
43295
  fidelity: data.fidelity,
42530
43296
  reproduction_notes: data.reproductionNotes,
@@ -42545,7 +43311,7 @@ var codeCommand = defineCommand158({
42545
43311
  });
42546
43312
 
42547
43313
  // src/commands/landing/inspiration/favorites.ts
42548
- import { defineCommand as defineCommand159 } from "citty";
43314
+ import { defineCommand as defineCommand160 } from "citty";
42549
43315
  registerSchema({
42550
43316
  command: "landing.inspiration.favorites",
42551
43317
  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.",
@@ -42559,7 +43325,7 @@ registerSchema({
42559
43325
  }
42560
43326
  }
42561
43327
  });
42562
- var favoritesCommand = defineCommand159({
43328
+ var favoritesCommand = defineCommand160({
42563
43329
  meta: {
42564
43330
  name: "favorites",
42565
43331
  description: "List this company's saved reference sections. Example: baker landing inspiration favorites --type hero,pricing"
@@ -42639,7 +43405,7 @@ registerSchema({
42639
43405
  note: { type: "string", description: "Why this is worth keeping", required: false }
42640
43406
  }
42641
43407
  });
42642
- var favoriteCommand = defineCommand159({
43408
+ var favoriteCommand = defineCommand160({
42643
43409
  meta: {
42644
43410
  name: "favorite",
42645
43411
  description: "Save a reference section to this company. Example: baker landing inspiration favorite k57abc\u2026"
@@ -42677,7 +43443,7 @@ registerSchema({
42677
43443
  page: { type: "boolean", description: "Treat the id as a page rather than a section", required: false }
42678
43444
  }
42679
43445
  });
42680
- var unfavoriteCommand = defineCommand159({
43446
+ var unfavoriteCommand = defineCommand160({
42681
43447
  meta: {
42682
43448
  name: "unfavorite",
42683
43449
  description: "Remove a reference section from this company's saved set. Example: baker landing inspiration unfavorite k57abc\u2026"
@@ -42699,7 +43465,7 @@ var unfavoriteCommand = defineCommand159({
42699
43465
  });
42700
43466
 
42701
43467
  // src/commands/landing/inspiration/page.ts
42702
- import { defineCommand as defineCommand160 } from "citty";
43468
+ import { defineCommand as defineCommand161 } from "citty";
42703
43469
  registerSchema({
42704
43470
  command: "landing.inspiration.page",
42705
43471
  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.",
@@ -42716,7 +43482,7 @@ registerSchema({
42716
43482
  }
42717
43483
  }
42718
43484
  });
42719
- var pageCommand2 = defineCommand160({
43485
+ var pageCommand2 = defineCommand161({
42720
43486
  meta: {
42721
43487
  name: "page",
42722
43488
  description: "Show how a reference page sequences its sections. Example: baker landing inspiration page j91xyz\u2026 \u2014 the blueprint, not the pixels."
@@ -42779,9 +43545,9 @@ var pageCommand2 = defineCommand160({
42779
43545
  });
42780
43546
 
42781
43547
  // src/commands/landing/inspiration/scrape.ts
42782
- import { readFile as readFile23 } from "fs/promises";
42783
- import path34 from "path";
42784
- import { defineCommand as defineCommand161 } from "citty";
43548
+ import { readFile as readFile24 } from "fs/promises";
43549
+ import path35 from "path";
43550
+ import { defineCommand as defineCommand162 } from "citty";
42785
43551
 
42786
43552
  // src/engine/landing/lib/capturedReferences.ts
42787
43553
  var MAX_STRINGS_PER_SECTION = 40;
@@ -42973,7 +43739,7 @@ function classifyCaptureFailure(error) {
42973
43739
 
42974
43740
  // src/engine/landing-library/run.ts
42975
43741
  import { mkdir as mkdir11, writeFile as writeFile16 } from "fs/promises";
42976
- import path32 from "path";
43742
+ import path33 from "path";
42977
43743
 
42978
43744
  // ../proxy/src/preflight.ts
42979
43745
  import http from "http";
@@ -44389,9 +45155,9 @@ async function renderBundleToPng(browser, html, viewportWidth, options = {}) {
44389
45155
 
44390
45156
  // src/engine/landing-library/report.ts
44391
45157
  import { writeFile as writeFile15 } from "fs/promises";
44392
- import path31 from "path";
45158
+ import path32 from "path";
44393
45159
  async function writeCaptureReport(manifest, outDir) {
44394
- const file = path31.join(outDir, "report.html");
45160
+ const file = path32.join(outDir, "report.html");
44395
45161
  await writeFile15(file, renderReport(manifest));
44396
45162
  return file;
44397
45163
  }
@@ -44570,32 +45336,32 @@ async function reproducePage(args) {
44570
45336
  const { browser, page, outDir, pageUrl, livePageShot } = args;
44571
45337
  const built = await buildSectionBundle(page, "body", pageUrl).catch(() => null);
44572
45338
  if (!built) return { bundle: null, fidelity: null };
44573
- await writeFile16(path32.join(outDir, "page.html"), built.html);
45339
+ await writeFile16(path33.join(outDir, "page.html"), built.html);
44574
45340
  const rendered = await renderBundleToPng(browser, built.html, DESKTOP_VIEWPORT.width, {
44575
45341
  wholePage: true,
44576
45342
  timeoutMs: 6e4
44577
45343
  });
44578
45344
  if (!rendered || !livePageShot) return { bundle: "page.html", fidelity: null };
44579
- await writeFile16(path32.join(outDir, "page-rendered.png"), rendered);
45345
+ await writeFile16(path33.join(outDir, "page-rendered.png"), rendered);
44580
45346
  const { score, note } = await scoreFidelity(livePageShot, rendered);
44581
45347
  return { bundle: "page.html", fidelity: score, ...note ? { fidelityNote: note } : {} };
44582
45348
  }
44583
45349
  async function captureOneSection(args) {
44584
45350
  const { browser, page, candidate, sectionsDir, outDir, pageUrl, withCode } = args;
44585
- const dir = path32.join(sectionsDir, String(candidate.index).padStart(2, "0"));
45351
+ const dir = path33.join(sectionsDir, String(candidate.index).padStart(2, "0"));
44586
45352
  await mkdir11(dir, { recursive: true });
44587
45353
  const desktop = await captureSection(page, candidate);
44588
- if (desktop) await writeFile16(path32.join(dir, "desktop.png"), desktop);
45354
+ if (desktop) await writeFile16(path33.join(dir, "desktop.png"), desktop);
44589
45355
  const visualHash = desktop ? await perceptualHash(desktop) : null;
44590
45356
  const motion = await collectMotion(page, candidate.selector);
44591
45357
  const built = withCode ? await buildSectionBundle(page, candidate.selector, pageUrl) : null;
44592
45358
  let fidelity = null;
44593
45359
  let fidelityNote;
44594
45360
  if (built) {
44595
- await writeFile16(path32.join(dir, "section.html"), built.html);
45361
+ await writeFile16(path33.join(dir, "section.html"), built.html);
44596
45362
  const rendered = await renderBundleToPng(browser, built.html, DESKTOP_VIEWPORT.width);
44597
45363
  if (rendered && desktop) {
44598
- await writeFile16(path32.join(dir, "section-rendered.png"), rendered);
45364
+ await writeFile16(path33.join(dir, "section-rendered.png"), rendered);
44599
45365
  const result = await scoreFidelity(desktop, rendered);
44600
45366
  fidelity = result.score;
44601
45367
  fidelityNote = result.note;
@@ -44603,9 +45369,9 @@ async function captureOneSection(args) {
44603
45369
  }
44604
45370
  return {
44605
45371
  ...candidate,
44606
- desktopShot: desktop ? path32.relative(outDir, path32.join(dir, "desktop.png")) : null,
45372
+ desktopShot: desktop ? path33.relative(outDir, path33.join(dir, "desktop.png")) : null,
44607
45373
  mobileShot: null,
44608
- bundle: built ? path32.relative(outDir, path32.join(dir, "section.html")) : null,
45374
+ bundle: built ? path33.relative(outDir, path33.join(dir, "section.html")) : null,
44609
45375
  fidelity,
44610
45376
  ...fidelityNote ? { fidelityNote } : {},
44611
45377
  ...built ? { cssStats: built.stats } : {},
@@ -44624,9 +45390,9 @@ async function captureMobileShots(args) {
44624
45390
  for (const section of sections) {
44625
45391
  const shot = await captureSectionOnMobile(mobile.page, section);
44626
45392
  if (!shot) continue;
44627
- const file = path32.join(sectionsDir, String(section.index).padStart(2, "0"), "mobile.png");
45393
+ const file = path33.join(sectionsDir, String(section.index).padStart(2, "0"), "mobile.png");
44628
45394
  await writeFile16(file, shot);
44629
- section.mobileShot = path32.relative(outDir, file);
45395
+ section.mobileShot = path33.relative(outDir, file);
44630
45396
  }
44631
45397
  } finally {
44632
45398
  await mobile.context.close();
@@ -44641,10 +45407,10 @@ async function captureMotionTakes(args) {
44641
45407
  const filmOne = async (section) => {
44642
45408
  const take = await captureMotionTake(browser, pageUrl, section.selector).catch(() => null);
44643
45409
  if (!take) return;
44644
- const dir = path32.join(sectionsDir, String(section.index).padStart(2, "0"));
44645
- const file = path32.join(dir, "motion-filmstrip.png");
45410
+ const dir = path33.join(sectionsDir, String(section.index).padStart(2, "0"));
45411
+ const file = path33.join(dir, "motion-filmstrip.png");
44646
45412
  await writeFile16(file, take.filmstrip);
44647
- section.motionFilmstrip = path32.relative(outDir, file);
45413
+ section.motionFilmstrip = path33.relative(outDir, file);
44648
45414
  log(` [${section.index}] ${section.motion.summary}`);
44649
45415
  };
44650
45416
  const queue = [...moving];
@@ -44701,7 +45467,7 @@ async function captureAlternateViews(args) {
44701
45467
  async function reproduceWholePage(args) {
44702
45468
  const { browser, page, outDir, pageUrl, withCode, log } = args;
44703
45469
  const fullPage = await page.screenshot({ type: "png", fullPage: true }).catch(() => null);
44704
- if (fullPage) await writeFile16(path32.join(outDir, "full-page.png"), fullPage);
45470
+ if (fullPage) await writeFile16(path33.join(outDir, "full-page.png"), fullPage);
44705
45471
  if (!withCode) return { bundle: null, fidelity: null };
44706
45472
  const reproduction = await reproducePage({ browser, page, outDir, pageUrl, livePageShot: fullPage });
44707
45473
  log(`page reproduction: ${reproduction.fidelity === null ? "unavailable" : reproduction.fidelity.toFixed(2)}`);
@@ -44772,7 +45538,7 @@ async function openViaLadder(args) {
44772
45538
  async function scrapeLanding(options) {
44773
45539
  const timeoutMs = options.timeoutMs ?? 45e3;
44774
45540
  const log = options.onProgress ?? (() => void 0);
44775
- const sectionsDir = path32.join(options.outDir, "sections");
45541
+ const sectionsDir = path33.join(options.outDir, "sections");
44776
45542
  const nonPublic = refuseNonPublicUrl(options.url);
44777
45543
  if (nonPublic) {
44778
45544
  throw new BlockedPageError({
@@ -44834,7 +45600,7 @@ async function scrapeLanding(options) {
44834
45600
  security: prepared.security,
44835
45601
  captureTier: tier
44836
45602
  };
44837
- await writeFile16(path32.join(options.outDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
45603
+ await writeFile16(path33.join(options.outDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
44838
45604
  `);
44839
45605
  if (options.report !== false) {
44840
45606
  const reportPath = await writeCaptureReport(manifest, options.outDir);
@@ -44849,28 +45615,28 @@ async function scrapeLanding(options) {
44849
45615
 
44850
45616
  // src/commands/landing/inspiration/captureOut.ts
44851
45617
  import { existsSync as existsSync9 } from "fs";
44852
- import path33 from "path";
45618
+ import path34 from "path";
44853
45619
  var SCRATCH_DIR = ".baker";
44854
45620
  function isWithin(parent, target) {
44855
- const relative = path33.relative(parent, target);
44856
- return relative === "" || !relative.startsWith("..") && !path33.isAbsolute(relative);
45621
+ const relative = path34.relative(parent, target);
45622
+ return relative === "" || !relative.startsWith("..") && !path34.isAbsolute(relative);
44857
45623
  }
44858
45624
  function findRepoRoot(from) {
44859
- let dir = path33.resolve(from);
45625
+ let dir = path34.resolve(from);
44860
45626
  for (; ; ) {
44861
- if (existsSync9(path33.join(dir, ".git"))) return dir;
44862
- const parent = path33.dirname(dir);
45627
+ if (existsSync9(path34.join(dir, ".git"))) return dir;
45628
+ const parent = path34.dirname(dir);
44863
45629
  if (parent === dir) return null;
44864
45630
  dir = parent;
44865
45631
  }
44866
45632
  }
44867
45633
  function checkCaptureOut(out, options) {
44868
45634
  const { cwd, repoRoot } = options;
44869
- const resolved = path33.resolve(cwd, out);
45635
+ const resolved = path34.resolve(cwd, out);
44870
45636
  if (repoRoot === null || !isWithin(repoRoot, resolved)) return { ok: true };
44871
- const scratch = path33.join(repoRoot, SCRATCH_DIR);
45637
+ const scratch = path34.join(repoRoot, SCRATCH_DIR);
44872
45638
  if (isWithin(scratch, resolved)) return { ok: true };
44873
- const suggestion = path33.posix.join(SCRATCH_DIR, "teardowns", path33.basename(resolved) || "capture");
45639
+ const suggestion = path34.posix.join(SCRATCH_DIR, "teardowns", path34.basename(resolved) || "capture");
44874
45640
  return {
44875
45641
  ok: false,
44876
45642
  error: {
@@ -44899,7 +45665,7 @@ async function recordCapture(manifest, outDir) {
44899
45665
  for (const section of manifest.sections) {
44900
45666
  if (!section.bundle) continue;
44901
45667
  try {
44902
- const markup = await readFile23(path34.join(outDir, section.bundle), "utf8");
45668
+ const markup = await readFile24(path35.join(outDir, section.bundle), "utf8");
44903
45669
  const copyStrings = capturedCopyStrings(markup);
44904
45670
  if (copyStrings.length === 0) continue;
44905
45671
  references.push({
@@ -44960,7 +45726,7 @@ registerSchema({
44960
45726
  report: { type: "boolean", description: "Write report.html. `--no-report` to skip", required: false }
44961
45727
  }
44962
45728
  });
44963
- var scrapeCommand = defineCommand161({
45729
+ var scrapeCommand = defineCommand162({
44964
45730
  meta: {
44965
45731
  name: "scrape",
44966
45732
  description: "Capture a landing page to a directory, now. Example: baker landing inspiration scrape https://linear.app --out .baker/inspiration/linear.app"
@@ -45069,12 +45835,12 @@ var scrapeCommand = defineCommand161({
45069
45835
  });
45070
45836
 
45071
45837
  // src/commands/landing/inspiration/search.ts
45072
- import path36 from "path";
45073
- import { defineCommand as defineCommand162 } from "citty";
45838
+ import path37 from "path";
45839
+ import { defineCommand as defineCommand163 } from "citty";
45074
45840
 
45075
45841
  // src/commands/landing/inspiration/shot.ts
45076
45842
  import { mkdir as mkdir12, writeFile as writeFile17 } from "fs/promises";
45077
- import path35 from "path";
45843
+ import path36 from "path";
45078
45844
  import sharp6 from "sharp";
45079
45845
  var READABLE_SHOT = {
45080
45846
  maxWidth: 1440,
@@ -45100,9 +45866,9 @@ async function downloadReadableShot(url, file) {
45100
45866
  const response = await fetch(url);
45101
45867
  if (!response.ok) return null;
45102
45868
  const shot = await toReadableShot(Buffer.from(await response.arrayBuffer()));
45103
- await mkdir12(path35.dirname(file), { recursive: true });
45869
+ await mkdir12(path36.dirname(file), { recursive: true });
45104
45870
  await writeFile17(file, shot);
45105
- return path35.relative(process.cwd(), file);
45871
+ return path36.relative(process.cwd(), file);
45106
45872
  } catch {
45107
45873
  return null;
45108
45874
  }
@@ -45194,20 +45960,20 @@ function buildSearchBody(args) {
45194
45960
  return body;
45195
45961
  }
45196
45962
  async function downloadShots(results) {
45197
- const dir = path36.join(process.cwd(), ".baker", "inspiration");
45963
+ const dir = path37.join(process.cwd(), ".baker", "inspiration");
45198
45964
  const saved = /* @__PURE__ */ new Map();
45199
45965
  await Promise.all(
45200
45966
  results.map(async (result) => {
45201
45967
  const file = await downloadReadableShot(
45202
45968
  result.desktopShotUrl,
45203
- path36.join(dir, `${result.id}.${READABLE_SHOT.extension}`)
45969
+ path37.join(dir, `${result.id}.${READABLE_SHOT.extension}`)
45204
45970
  );
45205
45971
  if (file) saved.set(result.id, file);
45206
45972
  })
45207
45973
  );
45208
45974
  return saved;
45209
45975
  }
45210
- var searchCommand2 = defineCommand162({
45976
+ var searchCommand2 = defineCommand163({
45211
45977
  meta: {
45212
45978
  name: "search",
45213
45979
  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"
@@ -45305,7 +46071,7 @@ var searchCommand2 = defineCommand162({
45305
46071
  });
45306
46072
 
45307
46073
  // src/commands/landing/inspiration/sequences.ts
45308
- import { defineCommand as defineCommand163 } from "citty";
46074
+ import { defineCommand as defineCommand164 } from "citty";
45309
46075
  var COMPACT_TRANSITIONS = 12;
45310
46076
  var COMPACT_ENDS = 5;
45311
46077
  var MAX_PAGES = 50;
@@ -45379,7 +46145,7 @@ function sequencesHints(data, { scope, full }) {
45379
46145
  if (scope === "favorites") hints.push(...favoritesScopeHints(data.favoritesHealth, data.pagesReturned));
45380
46146
  return hints;
45381
46147
  }
45382
- var sequencesCommand = defineCommand163({
46148
+ var sequencesCommand = defineCommand164({
45383
46149
  meta: {
45384
46150
  name: "sequences",
45385
46151
  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."
@@ -45428,8 +46194,8 @@ var sequencesCommand = defineCommand163({
45428
46194
  });
45429
46195
 
45430
46196
  // src/commands/landing/inspiration/view.ts
45431
- import path37 from "path";
45432
- import { defineCommand as defineCommand164 } from "citty";
46197
+ import path38 from "path";
46198
+ import { defineCommand as defineCommand165 } from "citty";
45433
46199
  registerSchema({
45434
46200
  command: "landing.inspiration.view",
45435
46201
  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.",
@@ -45442,7 +46208,7 @@ registerSchema({
45442
46208
  }
45443
46209
  }
45444
46210
  });
45445
- var viewCommand2 = defineCommand164({
46211
+ var viewCommand2 = defineCommand165({
45446
46212
  meta: {
45447
46213
  name: "view",
45448
46214
  description: "Full detail for one reference section. Example: baker landing inspiration view k57abc\u2026 \u2014 read the screenshots it saves before you build."
@@ -45461,12 +46227,12 @@ var viewCommand2 = defineCommand164({
45461
46227
  const id = args.id;
45462
46228
  const data = await apiGet("/api/landing-inspiration/section", { id });
45463
46229
  const section = data.section;
45464
- const dir = path37.join(process.cwd(), ".baker", "inspiration", id);
46230
+ const dir = path38.join(process.cwd(), ".baker", "inspiration", id);
45465
46231
  const ext = READABLE_SHOT.extension;
45466
46232
  const [desktop, mobile, filmstrip] = await Promise.all([
45467
- downloadReadableShot(section.desktopShotUrl, path37.join(dir, `desktop.${ext}`)),
45468
- downloadReadableShot(section.mobileShotUrl, path37.join(dir, `mobile.${ext}`)),
45469
- downloadReadableShot(section.motionFilmstripUrl, path37.join(dir, `motion-filmstrip.${ext}`))
46233
+ downloadReadableShot(section.desktopShotUrl, path38.join(dir, `desktop.${ext}`)),
46234
+ downloadReadableShot(section.mobileShotUrl, path38.join(dir, `mobile.${ext}`)),
46235
+ downloadReadableShot(section.motionFilmstripUrl, path38.join(dir, `motion-filmstrip.${ext}`))
45470
46236
  ]);
45471
46237
  const full = args.full;
45472
46238
  const hints = [INSPIRATION_HINTS.structureNotCopy, INSPIRATION_HINTS.adapt];
@@ -45526,7 +46292,7 @@ var viewCommand2 = defineCommand164({
45526
46292
  });
45527
46293
 
45528
46294
  // src/commands/landing/inspiration/index.ts
45529
- var inspirationCommand = defineCommand165({
46295
+ var inspirationCommand = defineCommand166({
45530
46296
  meta: {
45531
46297
  name: "inspiration",
45532
46298
  description: `Reference library of real landing-page sections \u2014 look at how good pages actually solve a problem before you design one.
@@ -45570,8 +46336,310 @@ Full guide: __tooling__/docs/tools/baker/landing.md`
45570
46336
  }
45571
46337
  });
45572
46338
 
46339
+ // src/commands/landing/variant.ts
46340
+ import { randomUUID as randomUUID2 } from "crypto";
46341
+ import { mkdir as mkdir13, readdir as readdir10, readFile as readFile25, stat as stat7, writeFile as writeFile18 } from "fs/promises";
46342
+ import path40 from "path";
46343
+ import { defineCommand as defineCommand167 } from "citty";
46344
+
46345
+ // src/commands/landing/repoint.ts
46346
+ import path39 from "path";
46347
+ var QUOTED_RELATIVE = /(['"])(\.\.?\/[^'"]*)\1/g;
46348
+ function isInside3(abs, dir) {
46349
+ return abs === dir || abs.startsWith(dir + path39.sep);
46350
+ }
46351
+ function toSpecifier(from, to) {
46352
+ const rel = path39.relative(from, to).split(path39.sep).join("/");
46353
+ return rel.startsWith(".") ? rel : `./${rel}`;
46354
+ }
46355
+ function repointSpecifier(spec, opts) {
46356
+ if (!spec.startsWith(".")) return spec;
46357
+ const abs = path39.resolve(opts.fromDir, spec);
46358
+ if (opts.forkedTargets?.has(abs)) {
46359
+ const withinControl = path39.relative(opts.controlDir, abs);
46360
+ return toSpecifier(opts.toDir, path39.resolve(opts.variantDir, withinControl));
46361
+ }
46362
+ if (!isInside3(abs, opts.controlDir)) return spec;
46363
+ return toSpecifier(opts.toDir, abs);
46364
+ }
46365
+ function repointFile(text2, opts) {
46366
+ return text2.replace(QUOTED_RELATIVE, (_match, quote, spec) => {
46367
+ return `${quote}${repointSpecifier(spec, opts)}${quote}`;
46368
+ });
46369
+ }
46370
+
46371
+ // src/commands/landing/variant.ts
46372
+ registerSchema({
46373
+ command: "landing.variant",
46374
+ description: "Create the alternative version of a landing for an A/B test. The new page shares the control's sections instead of copying them, so the only difference between the two is the one you fork \u2014 which is the only way the test measures what you think it measures. Run `baker experiment plan` FIRST: most pages cannot settle most questions.",
46375
+ args: {
46376
+ control: { type: "string", description: "The page under test, by slug", required: true },
46377
+ slug: { type: "string", description: "Slug for the alternative page (convention: <control>-b)", required: true },
46378
+ fork: {
46379
+ type: "string",
46380
+ description: "The component the variant owns its own copy of, named as it appears under _components/ (`Hero.astro`, or `hero/Card.astro`). Repeat for several. Everything else is shared with the control, so a later edit to the control reaches both arms and cannot skew the test.",
46381
+ required: false
46382
+ },
46383
+ because: {
46384
+ type: "string",
46385
+ description: "What you SAW that makes this worth building \u2014 the observation, not the change. Required here, before the page exists, because a reason written afterwards is a caption for a page rather than the reason for one.",
46386
+ required: true
46387
+ },
46388
+ change: {
46389
+ type: "string",
46390
+ description: "What is different about this version, in the client's words \u2014 \u201Cput the booking form in the hero\u201D.",
46391
+ required: true
46392
+ }
46393
+ }
46394
+ });
46395
+ var SLUG_RE2 = /^[a-z0-9][a-z0-9-]*$/;
46396
+ function fail7(code, message, fix) {
46397
+ process.stderr.write(
46398
+ `${JSON.stringify({ ok: false, error: { code, message, ...fix ? { fix } : {} } }, null, 2)}
46399
+ `
46400
+ );
46401
+ process.exit(2);
46402
+ }
46403
+ async function isDir2(p) {
46404
+ try {
46405
+ return (await stat7(p)).isDirectory();
46406
+ } catch {
46407
+ return false;
46408
+ }
46409
+ }
46410
+ async function exists(p) {
46411
+ try {
46412
+ await stat7(p);
46413
+ return true;
46414
+ } catch {
46415
+ return false;
46416
+ }
46417
+ }
46418
+ async function listLandingSlugs2(root) {
46419
+ try {
46420
+ const entries = await readdir10(path40.resolve(root, "src", "pages"), { withFileTypes: true });
46421
+ return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).map((e) => e.name).sort();
46422
+ } catch {
46423
+ return [];
46424
+ }
46425
+ }
46426
+ async function listComponents(componentsDir, prefix = "") {
46427
+ let entries;
46428
+ try {
46429
+ entries = await readdir10(componentsDir, { withFileTypes: true });
46430
+ } catch {
46431
+ return [];
46432
+ }
46433
+ const out = [];
46434
+ for (const entry of entries) {
46435
+ const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
46436
+ if (entry.isDirectory()) out.push(...await listComponents(path40.join(componentsDir, entry.name), rel));
46437
+ else if (entry.name.endsWith(".astro")) out.push(rel);
46438
+ }
46439
+ return out.sort();
46440
+ }
46441
+ function resolveForkName(requested, available) {
46442
+ const cleaned = requested.replace(/^\.?\/?(_components\/)?/, "");
46443
+ for (const candidate of [cleaned, `${cleaned}.astro`]) {
46444
+ if (available.includes(candidate)) return candidate;
46445
+ }
46446
+ const basename4 = (p) => p.slice(p.lastIndexOf("/") + 1);
46447
+ const matches = available.filter((a) => basename4(a) === cleaned || basename4(a) === `${cleaned}.astro`);
46448
+ return matches.length === 1 ? matches[0] ?? null : null;
46449
+ }
46450
+ function parseForks(fork) {
46451
+ const list = Array.isArray(fork) ? fork : fork === void 0 || fork === null ? [] : [fork];
46452
+ const names = list.filter((s) => typeof s === "string").flatMap((s) => s.split(",")).map((s) => s.trim()).filter((s) => s.length > 0);
46453
+ return [...new Set(names)];
46454
+ }
46455
+ function buildVariantDefinition(controlDefinition, opts) {
46456
+ let out = controlDefinition;
46457
+ out = out.replace(/^internalId:.*$/m, `internalId: "${opts.internalId}"`);
46458
+ out = out.replace(/^internalTitle:\s*(.*)$/m, (_m, title) => `internalTitle: ${title.trim()} (variant)`);
46459
+ const shared = opts.forks.length > 0 ? opts.forks.join(", ") : "nothing yet";
46460
+ const note = [
46461
+ "",
46462
+ `## A/B test \u2014 alternative version of \`${opts.controlSlug}\``,
46463
+ "",
46464
+ `This page is the alternative arm of a test against \`${opts.controlSlug}\`. Visitors are split at the edge and the address bar keeps saying \`/${opts.controlSlug}/\`, so both arms are the same page seen by different people.`,
46465
+ "",
46466
+ `**Only this differs:** ${shared}. Every other section is imported from \`${opts.controlSlug}\`, on purpose \u2014 an edit to the control reaches both arms, so the test keeps measuring the one difference rather than a drift between two copies.`,
46467
+ ...opts.because ? ["", `**Because:** ${opts.because}`] : [],
46468
+ ...opts.change ? ["", `**We changed:** ${opts.change}`] : [],
46469
+ "",
46470
+ `Run \`baker experiment start --landing ${opts.controlSlug} --variant ${opts.variantSlug} --because "\u2026" --change "\u2026"\` to begin, and read the result with \`baker experiment status\`.`,
46471
+ ""
46472
+ ].join("\n");
46473
+ return `${out.trimEnd()}
46474
+ ${note}`;
46475
+ }
46476
+ async function resolveTargets(projectRoot, opts) {
46477
+ const { controlSlug, variantSlug } = opts;
46478
+ for (const [label, slug] of [
46479
+ ["control", controlSlug],
46480
+ ["slug", variantSlug]
46481
+ ]) {
46482
+ if (!SLUG_RE2.test(slug)) {
46483
+ fail7(
46484
+ "INVALID_SLUG",
46485
+ `"${slug}" is not a landing slug \u2014 use the folder name directly under src/pages/ (lowercase letters, digits and dashes).`,
46486
+ { argument: label, availableSlugs: await listLandingSlugs2(projectRoot) }
46487
+ );
46488
+ }
46489
+ }
46490
+ if (controlSlug === variantSlug) {
46491
+ fail7("INVALID_SLUG", `The alternative needs its own slug \u2014 try \`${controlSlug}-b\`.`);
46492
+ }
46493
+ const controlDir = path40.resolve(projectRoot, "src", "pages", controlSlug);
46494
+ const variantDir = path40.resolve(projectRoot, "src", "pages", variantSlug);
46495
+ if (!await isDir2(controlDir)) {
46496
+ fail7("NOT_FOUND", `No landing at src/pages/${controlSlug}/`, {
46497
+ availableSlugs: await listLandingSlugs2(projectRoot)
46498
+ });
46499
+ }
46500
+ if (await exists(variantDir)) {
46501
+ fail7(
46502
+ "ALREADY_EXISTS",
46503
+ `src/pages/${variantSlug}/ already exists \u2014 pick another slug, or delete that page if it was a false start.`
46504
+ );
46505
+ }
46506
+ if (!await exists(path40.join(controlDir, "index.astro"))) {
46507
+ fail7("NOT_FOUND", `src/pages/${controlSlug}/index.astro is missing, so there is no page to make a variant of.`);
46508
+ }
46509
+ const available = await listComponents(path40.join(controlDir, "_components"));
46510
+ const forks = [];
46511
+ for (const name of parseForks(opts.fork)) {
46512
+ const resolved = resolveForkName(name, available);
46513
+ if (resolved === null) {
46514
+ fail7("NOT_FOUND", `"${name}" is not a component of ${controlSlug}.`, {
46515
+ availableComponents: available,
46516
+ hint: "Name it as it appears under _components/ \u2014 `Hero.astro`, or `hero/Card.astro`."
46517
+ });
46518
+ }
46519
+ forks.push(resolved);
46520
+ }
46521
+ return { controlDir, variantDir, available, forks };
46522
+ }
46523
+ async function writeVariant(opts) {
46524
+ const { controlDir, variantDir, controlSlug, variantSlug, forks } = opts;
46525
+ const forkedTargets = new Set(forks.map((f) => path40.join(controlDir, "_components", f)));
46526
+ const written = [];
46527
+ const indexText = await readFile25(path40.join(controlDir, "index.astro"), "utf8");
46528
+ await mkdir13(variantDir, { recursive: true });
46529
+ await writeFile18(
46530
+ path40.join(variantDir, "index.astro"),
46531
+ repointFile(indexText, { fromDir: controlDir, toDir: variantDir, controlDir, variantDir, forkedTargets }),
46532
+ "utf8"
46533
+ );
46534
+ written.push(`src/pages/${variantSlug}/index.astro`);
46535
+ for (const fork of forks) {
46536
+ const fromFile = path40.join(controlDir, "_components", fork);
46537
+ const toFile = path40.join(variantDir, "_components", fork);
46538
+ const text2 = await readFile25(fromFile, "utf8");
46539
+ await mkdir13(path40.dirname(toFile), { recursive: true });
46540
+ await writeFile18(
46541
+ toFile,
46542
+ repointFile(text2, {
46543
+ fromDir: path40.dirname(fromFile),
46544
+ toDir: path40.dirname(toFile),
46545
+ controlDir,
46546
+ variantDir,
46547
+ forkedTargets
46548
+ }),
46549
+ "utf8"
46550
+ );
46551
+ written.push(`src/pages/${variantSlug}/_components/${fork}`);
46552
+ }
46553
+ const controlDefinitionPath = path40.join(controlDir, "_definition.md");
46554
+ if (await exists(controlDefinitionPath)) {
46555
+ const definition = buildVariantDefinition(await readFile25(controlDefinitionPath, "utf8"), {
46556
+ internalId: randomUUID2().replace(/-/g, "").slice(0, 8),
46557
+ variantSlug,
46558
+ controlSlug,
46559
+ forks,
46560
+ ...opts.because ? { because: opts.because } : {},
46561
+ ...opts.change ? { change: opts.change } : {}
46562
+ });
46563
+ await writeFile18(path40.join(variantDir, "_definition.md"), definition, "utf8");
46564
+ written.push(`src/pages/${variantSlug}/_definition.md`);
46565
+ }
46566
+ await mkdir13(path40.join(variantDir, "_images"), { recursive: true });
46567
+ await writeFile18(path40.join(variantDir, "_images", ".gitkeep"), "", "utf8");
46568
+ return written;
46569
+ }
46570
+ var variantCommand = defineCommand167({
46571
+ meta: {
46572
+ name: "variant",
46573
+ description: "Create the alternative version of a landing for an A/B test. The new page SHARES the control's sections and forks only the component you name, so the two arms differ by exactly the thing you are testing. Run `baker experiment plan` first \u2014 most pages do not have the traffic to settle most questions."
46574
+ },
46575
+ args: {
46576
+ control: { type: "positional", required: true, description: "The page under test, by slug" },
46577
+ slug: {
46578
+ type: "positional",
46579
+ required: true,
46580
+ description: "Slug for the alternative page (convention: <control>-b)"
46581
+ },
46582
+ fork: {
46583
+ type: "string",
46584
+ description: "Component the variant owns its own copy of (`Hero.astro`). Repeat for several."
46585
+ },
46586
+ because: {
46587
+ type: "string",
46588
+ required: true,
46589
+ description: "What you SAW that makes this worth building \u2014 the observation, not the change. Required here, before the page exists, because a reason written afterwards is a caption for a page rather than the reason for one, and a test with no stated belief teaches nothing whichever way it lands. Recorded in the page's notes and passed to `baker experiment start`."
46590
+ },
46591
+ change: {
46592
+ type: "string",
46593
+ required: true,
46594
+ description: "What is different about this version, in the client's words \u2014 \u201Cput the booking form in the hero\u201D."
46595
+ }
46596
+ },
46597
+ async run({ args }) {
46598
+ const projectRoot = process.cwd();
46599
+ const controlSlug = String(args.control);
46600
+ const variantSlug = String(args.slug);
46601
+ const { controlDir, variantDir, available, forks } = await resolveTargets(projectRoot, {
46602
+ controlSlug,
46603
+ variantSlug,
46604
+ fork: args.fork
46605
+ });
46606
+ const written = await writeVariant({
46607
+ controlDir,
46608
+ variantDir,
46609
+ controlSlug,
46610
+ variantSlug,
46611
+ forks,
46612
+ because: String(args.because),
46613
+ change: String(args.change)
46614
+ });
46615
+ const hints = [
46616
+ forks.length === 0 ? `MISSING --fork: ${variantSlug} currently renders exactly ${controlSlug}, so a test between them cannot conclude anything. Re-run with --fork <Component>, or copy the one section your change touches into src/pages/${variantSlug}/_components/ and repoint that import.` : `Edit ONLY src/pages/${variantSlug}/_components/${forks.join(", ")} \u2014 every other section is shared with ${controlSlug}, and editing a shared one changes both arms at once.`,
46617
+ `Preview both at http://localhost:4321/${controlSlug}/ and http://localhost:4321/${variantSlug}/ and check they differ only where you meant.`,
46618
+ `Then: baker experiment start --landing ${controlSlug} --variant ${variantSlug} --because "${String(args.because)}" --change "${String(args.change)}". Nothing is split until the session is published \u2014 the person publishing is the review of this page.`
46619
+ ];
46620
+ process.stdout.write(
46621
+ `${JSON.stringify(
46622
+ {
46623
+ ok: true,
46624
+ data: {
46625
+ control: controlSlug,
46626
+ slug: variantSlug,
46627
+ forked: forks,
46628
+ shared: available.filter((c) => !forks.includes(c)),
46629
+ written
46630
+ },
46631
+ hints
46632
+ },
46633
+ null,
46634
+ 2
46635
+ )}
46636
+ `
46637
+ );
46638
+ }
46639
+ });
46640
+
45573
46641
  // src/commands/landing/index.ts
45574
- var landingCommand = defineCommand166({
46642
+ var landingCommand = defineCommand168({
45575
46643
  meta: {
45576
46644
  name: "landing",
45577
46645
  description: `Design-quality tools for landing pages (src/pages/<slug>/).
@@ -45580,16 +46648,18 @@ Start here: \`baker landing critique <slug>\` after building or editing a landin
45580
46648
 
45581
46649
  Subcommands:
45582
46650
  baker landing inspiration \u2014 reference library of real landing-page sections; search it during research, BEFORE writing the Direction Contract. Inspiration only: take the mechanism, never the words.
46651
+ baker landing variant <control> <slug> \u2014 the alternative version of a page for an A/B test: shares the control's sections, forks only the component you name, so the two arms differ by exactly your hypothesis. Run \`baker experiment plan\` first.
45583
46652
  baker landing critique <slug> \u2014 deterministic design-quality critic (advisory): flags the known AI 'slop' tells (gradient text, overused fonts, side-tab borders, cream palettes, buzzword copy, broken images) tiered block/warn/advisory, respecting the client's BRAND.md. Records the critique the publish quality gate requires \u2014 run it before finishing a landing.`
45584
46653
  },
45585
46654
  subCommands: {
45586
46655
  inspiration: inspirationCommand,
45587
- critique: critiqueCommand2
46656
+ critique: critiqueCommand2,
46657
+ variant: variantCommand
45588
46658
  }
45589
46659
  });
45590
46660
 
45591
46661
  // src/commands/mcp/index.ts
45592
- import { defineCommand as defineCommand167 } from "citty";
46662
+ import { defineCommand as defineCommand169 } from "citty";
45593
46663
 
45594
46664
  // src/commands/mcp/platforms.ts
45595
46665
  function readsKey(label) {
@@ -45644,7 +46714,7 @@ function parseHeaders(raw) {
45644
46714
  }
45645
46715
  return Object.keys(headers).length > 0 ? headers : void 0;
45646
46716
  }
45647
- function fail6(err) {
46717
+ function fail8(err) {
45648
46718
  if (err instanceof ApiError) {
45649
46719
  writeJson({ ok: false, error: { code: err.code, message: err.message } });
45650
46720
  process.exit(1);
@@ -45658,7 +46728,7 @@ registerSchema({
45658
46728
  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.",
45659
46729
  args: {}
45660
46730
  });
45661
- var connectedCommand = defineCommand167({
46731
+ var connectedCommand = defineCommand169({
45662
46732
  meta: {
45663
46733
  name: "connected",
45664
46734
  description: `Everything this chat can reach \u2014 managed integrations, custom MCP servers, and connected platforms.
@@ -45708,7 +46778,7 @@ A tool or platform the user names that is NOT listed anywhere here is simply not
45708
46778
  hints
45709
46779
  });
45710
46780
  } catch (err) {
45711
- fail6(err);
46781
+ fail8(err);
45712
46782
  }
45713
46783
  }
45714
46784
  });
@@ -45717,7 +46787,7 @@ registerSchema({
45717
46787
  description: "List the custom MCP servers this company's chats see (org + company + your own user scope).",
45718
46788
  args: {}
45719
46789
  });
45720
- var listCommand14 = defineCommand167({
46790
+ var listCommand14 = defineCommand169({
45721
46791
  meta: { name: "list", description: "List custom MCP servers visible to this company's chats." },
45722
46792
  run: async () => {
45723
46793
  try {
@@ -45736,7 +46806,7 @@ var listCommand14 = defineCommand167({
45736
46806
  } : {}
45737
46807
  });
45738
46808
  } catch (err) {
45739
- fail6(err);
46809
+ fail8(err);
45740
46810
  }
45741
46811
  }
45742
46812
  });
@@ -45754,7 +46824,7 @@ registerSchema({
45754
46824
  header: { type: "string", description: 'Auth header "Key: Value" (repeatable)', required: false }
45755
46825
  }
45756
46826
  });
45757
- var addCommand2 = defineCommand167({
46827
+ var addCommand2 = defineCommand169({
45758
46828
  meta: {
45759
46829
  name: "add",
45760
46830
  description: `Register a custom MCP server. Tools appear as mcp__<name>__* on the NEXT message.
@@ -45797,7 +46867,7 @@ Examples:
45797
46867
  ]
45798
46868
  });
45799
46869
  } catch (err) {
45800
- fail6(err);
46870
+ fail8(err);
45801
46871
  }
45802
46872
  }
45803
46873
  });
@@ -45806,7 +46876,7 @@ registerSchema({
45806
46876
  description: "Remove a company custom MCP server by name.",
45807
46877
  args: { name: { type: "string", description: "Server name to remove", required: true } }
45808
46878
  });
45809
- var removeCommand4 = defineCommand167({
46879
+ var removeCommand4 = defineCommand169({
45810
46880
  meta: {
45811
46881
  name: "remove",
45812
46882
  description: `Remove a company custom MCP server by name.
@@ -45824,11 +46894,11 @@ Example:
45824
46894
  });
45825
46895
  writeJson({ ok: true, data });
45826
46896
  } catch (err) {
45827
- fail6(err);
46897
+ fail8(err);
45828
46898
  }
45829
46899
  }
45830
46900
  });
45831
- var mcpCommand = defineCommand167({
46901
+ var mcpCommand = defineCommand169({
45832
46902
  meta: {
45833
46903
  name: "mcp",
45834
46904
  description: `Third-party tools for this company \u2014 see what's connected, register custom HTTPS MCP endpoints.
@@ -45854,10 +46924,10 @@ Full guide: __tooling__/docs/tools/baker/mcp.md`
45854
46924
  });
45855
46925
 
45856
46926
  // src/commands/research/index.ts
45857
- import { defineCommand as defineCommand179 } from "citty";
46927
+ import { defineCommand as defineCommand181 } from "citty";
45858
46928
 
45859
46929
  // src/commands/research/advertisers.ts
45860
- import { defineCommand as defineCommand168 } from "citty";
46930
+ import { defineCommand as defineCommand170 } from "citty";
45861
46931
 
45862
46932
  // src/commands/research/hints.ts
45863
46933
  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.';
@@ -46040,7 +47110,7 @@ var FIELDS3 = {
46040
47110
  etv: "Estimated traffic value (USD)",
46041
47111
  visibility: "SERP visibility score (0-1)"
46042
47112
  };
46043
- var advertisersCommand = defineCommand168({
47113
+ var advertisersCommand = defineCommand170({
46044
47114
  meta: {
46045
47115
  name: "advertisers",
46046
47116
  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).
@@ -46096,7 +47166,7 @@ Examples:
46096
47166
  });
46097
47167
 
46098
47168
  // src/commands/research/autocomplete.ts
46099
- import { defineCommand as defineCommand169 } from "citty";
47169
+ import { defineCommand as defineCommand171 } from "citty";
46100
47170
  registerSchema({
46101
47171
  command: "research.autocomplete",
46102
47172
  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).",
@@ -46119,7 +47189,7 @@ registerSchema({
46119
47189
  var FIELDS4 = {
46120
47190
  suggestion: "Autocomplete suggestion from Google"
46121
47191
  };
46122
- var autocompleteCommand = defineCommand169({
47192
+ var autocompleteCommand = defineCommand171({
46123
47193
  meta: {
46124
47194
  name: "autocomplete",
46125
47195
  description: `Get Google Autocomplete suggestions for keyword expansion.
@@ -46174,7 +47244,7 @@ Examples:
46174
47244
  });
46175
47245
 
46176
47246
  // src/commands/research/countries.ts
46177
- import { defineCommand as defineCommand170 } from "citty";
47247
+ import { defineCommand as defineCommand172 } from "citty";
46178
47248
  registerSchema({
46179
47249
  command: "research.countries",
46180
47250
  description: "List all supported country codes for --location flag in research commands.",
@@ -46231,7 +47301,7 @@ var FIELDS5 = {
46231
47301
  code: "Country code to pass as --location",
46232
47302
  name: "Country name"
46233
47303
  };
46234
- var countriesCommand = defineCommand170({
47304
+ var countriesCommand = defineCommand172({
46235
47305
  meta: {
46236
47306
  name: "countries",
46237
47307
  description: "List all supported country codes for --location flag."
@@ -46242,7 +47312,7 @@ var countriesCommand = defineCommand170({
46242
47312
  });
46243
47313
 
46244
47314
  // src/commands/research/fetch.ts
46245
- import { defineCommand as defineCommand171 } from "citty";
47315
+ import { defineCommand as defineCommand173 } from "citty";
46246
47316
  var CONTENT_PREVIEW_CHARS = 2e4;
46247
47317
  var TIMEOUT_MS = 18e4;
46248
47318
  registerSchema({
@@ -46315,7 +47385,7 @@ function fetchFix(code) {
46315
47385
  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."
46316
47386
  };
46317
47387
  }
46318
- var fetchCommand = defineCommand171({
47388
+ var fetchCommand = defineCommand173({
46319
47389
  meta: {
46320
47390
  name: "fetch",
46321
47391
  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.
@@ -46392,7 +47462,7 @@ Examples:
46392
47462
  });
46393
47463
 
46394
47464
  // src/commands/research/intent.ts
46395
- import { defineCommand as defineCommand172 } from "citty";
47465
+ import { defineCommand as defineCommand174 } from "citty";
46396
47466
  registerSchema({
46397
47467
  command: "research.intent",
46398
47468
  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.",
@@ -46415,7 +47485,7 @@ var FIELDS7 = {
46415
47485
  intent: "Primary Google Search intent: informational, navigational, commercial, transactional",
46416
47486
  probability: "Confidence score 0.0-1.0"
46417
47487
  };
46418
- var intentCommand = defineCommand172({
47488
+ var intentCommand = defineCommand174({
46419
47489
  meta: {
46420
47490
  name: "intent",
46421
47491
  description: `Classify Google Search intent for keywords. Returns intent type and confidence.
@@ -46463,7 +47533,7 @@ Examples:
46463
47533
  });
46464
47534
 
46465
47535
  // src/commands/research/keyword-gap.ts
46466
- import { defineCommand as defineCommand173 } from "citty";
47536
+ import { defineCommand as defineCommand175 } from "citty";
46467
47537
  registerSchema({
46468
47538
  command: "research.keyword-gap",
46469
47539
  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.",
@@ -46492,7 +47562,7 @@ var FIELDS8 = {
46492
47562
  cpc: "Cost per click USD",
46493
47563
  their_position: "Competitor's ranking position"
46494
47564
  };
46495
- var keywordGapCommand = defineCommand173({
47565
+ var keywordGapCommand = defineCommand175({
46496
47566
  meta: {
46497
47567
  name: "keyword-gap",
46498
47568
  description: `Find keywords a competitor has that you don't. Supports pagination via --offset.
@@ -46567,7 +47637,7 @@ Examples:
46567
47637
  });
46568
47638
 
46569
47639
  // src/commands/research/keywords-for-site.ts
46570
- import { defineCommand as defineCommand174 } from "citty";
47640
+ import { defineCommand as defineCommand176 } from "citty";
46571
47641
  registerSchema({
46572
47642
  command: "research.keywords-for-site",
46573
47643
  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.",
@@ -46600,7 +47670,7 @@ var FIELDS9 = {
46600
47670
  competition: "LOW, MEDIUM, or HIGH",
46601
47671
  competition_index: "Competition score 0-100"
46602
47672
  };
46603
- var keywordsForSiteCommand = defineCommand174({
47673
+ var keywordsForSiteCommand = defineCommand176({
46604
47674
  meta: {
46605
47675
  name: "keywords-for-site",
46606
47676
  description: `Get keywords a competitor targets in Google. Use --type to filter paid/organic.
@@ -46662,7 +47732,7 @@ Examples:
46662
47732
  });
46663
47733
 
46664
47734
  // src/commands/research/languages.ts
46665
- import { defineCommand as defineCommand175 } from "citty";
47735
+ import { defineCommand as defineCommand177 } from "citty";
46666
47736
  registerSchema({
46667
47737
  command: "research.languages",
46668
47738
  description: "List all supported language codes for --language flag in research commands.",
@@ -46692,7 +47762,7 @@ var FIELDS10 = {
46692
47762
  code: "Language code to pass as --language",
46693
47763
  name: "Language name (also accepted by --language)"
46694
47764
  };
46695
- var languagesCommand2 = defineCommand175({
47765
+ var languagesCommand2 = defineCommand177({
46696
47766
  meta: {
46697
47767
  name: "languages",
46698
47768
  description: "List all supported language codes for --language flag."
@@ -46703,7 +47773,7 @@ var languagesCommand2 = defineCommand175({
46703
47773
  });
46704
47774
 
46705
47775
  // src/commands/research/lighthouse.ts
46706
- import { defineCommand as defineCommand176 } from "citty";
47776
+ import { defineCommand as defineCommand178 } from "citty";
46707
47777
  registerSchema({
46708
47778
  command: "research.lighthouse",
46709
47779
  description: "Landing page performance audit. Returns metrics that affect Google Ads Quality Score and CPC.",
@@ -46722,7 +47792,7 @@ var FIELDS11 = {
46722
47792
  speed_index_ms: "Speed Index in ms (good: < 3400)",
46723
47793
  interactive_ms: "Time to Interactive in ms (good: < 3800)"
46724
47794
  };
46725
- var lighthouseCommand = defineCommand176({
47795
+ var lighthouseCommand = defineCommand178({
46726
47796
  meta: {
46727
47797
  name: "lighthouse",
46728
47798
  description: `Landing page performance audit. Metrics affecting Google Ads Quality Score.
@@ -46760,7 +47830,7 @@ Examples:
46760
47830
  });
46761
47831
 
46762
47832
  // src/commands/research/relevant-pages.ts
46763
- import { defineCommand as defineCommand177 } from "citty";
47833
+ import { defineCommand as defineCommand179 } from "citty";
46764
47834
  registerSchema({
46765
47835
  command: "research.relevant-pages",
46766
47836
  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).",
@@ -46786,7 +47856,7 @@ var FIELDS12 = {
46786
47856
  keywords: "Total organic keywords the page ranks for",
46787
47857
  top_10: "Keywords in positions 1-10"
46788
47858
  };
46789
- var relevantPagesCommand = defineCommand177({
47859
+ var relevantPagesCommand = defineCommand179({
46790
47860
  meta: {
46791
47861
  name: "relevant-pages",
46792
47862
  description: `Get the top pages of a competitor domain with traffic data.
@@ -46833,7 +47903,7 @@ Examples:
46833
47903
  });
46834
47904
 
46835
47905
  // src/commands/research/web.ts
46836
- import { defineCommand as defineCommand178 } from "citty";
47906
+ import { defineCommand as defineCommand180 } from "citty";
46837
47907
  registerSchema({
46838
47908
  command: "research.web",
46839
47909
  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).",
@@ -46884,7 +47954,7 @@ async function runDeepResearch(question) {
46884
47954
  }
46885
47955
  throw new Error("Deep research timed out");
46886
47956
  }
46887
- var webCommand = defineCommand178({
47957
+ var webCommand = defineCommand180({
46888
47958
  meta: {
46889
47959
  name: "web",
46890
47960
  description: `Search the web with AI to answer any open-ended marketing question. Uses live internet data via Google Search.
@@ -46946,7 +48016,7 @@ Examples:
46946
48016
  });
46947
48017
 
46948
48018
  // src/commands/research/index.ts
46949
- var researchCommand = defineCommand179({
48019
+ var researchCommand = defineCommand181({
46950
48020
  meta: {
46951
48021
  name: "research",
46952
48022
  description: `Competitive intelligence and AI-powered research commands.
@@ -46990,10 +48060,10 @@ Full guide: __tooling__/docs/tools/baker/research.md`
46990
48060
  });
46991
48061
 
46992
48062
  // src/commands/scheduled-actions/index.ts
46993
- import { defineCommand as defineCommand187 } from "citty";
48063
+ import { defineCommand as defineCommand189 } from "citty";
46994
48064
 
46995
48065
  // src/commands/scheduled-actions/create.ts
46996
- import { defineCommand as defineCommand180 } from "citty";
48066
+ import { defineCommand as defineCommand182 } from "citty";
46997
48067
 
46998
48068
  // src/commands/scheduled-actions/shared.ts
46999
48069
  var TEMP_SCHEDULED_ACTION_PREFIX = "temp_sched_";
@@ -47140,7 +48210,7 @@ registerSchema({
47140
48210
  }
47141
48211
  }
47142
48212
  });
47143
- var createCommand3 = defineCommand180({
48213
+ var createCommand3 = defineCommand182({
47144
48214
  meta: {
47145
48215
  name: "create",
47146
48216
  description: 'Stage a scheduled action. Example: baker scheduled-actions create --name "Weekly report" --description "..." --cron "0 9 * * MON"'
@@ -47199,7 +48269,7 @@ var createCommand3 = defineCommand180({
47199
48269
  });
47200
48270
 
47201
48271
  // src/commands/scheduled-actions/delete.ts
47202
- import { defineCommand as defineCommand181 } from "citty";
48272
+ import { defineCommand as defineCommand183 } from "citty";
47203
48273
  registerSchema({
47204
48274
  command: "scheduled-actions.delete",
47205
48275
  description: "Stage deletion of a published scheduled action or cancellation of a temp_sched_* draft creation.",
@@ -47207,7 +48277,7 @@ registerSchema({
47207
48277
  id: { type: "string", description: "Published scheduled action ID or temp_sched_* draft ID", required: true }
47208
48278
  }
47209
48279
  });
47210
- var deleteCommand3 = defineCommand181({
48280
+ var deleteCommand3 = defineCommand183({
47211
48281
  meta: {
47212
48282
  name: "delete",
47213
48283
  description: "Stage scheduled action deletion. Example: baker scheduled-actions delete <id-or-temp_sched_id>"
@@ -47236,7 +48306,7 @@ var deleteCommand3 = defineCommand181({
47236
48306
  });
47237
48307
 
47238
48308
  // src/commands/scheduled-actions/get.ts
47239
- import { defineCommand as defineCommand182 } from "citty";
48309
+ import { defineCommand as defineCommand184 } from "citty";
47240
48310
  registerSchema({
47241
48311
  command: "scheduled-actions.get",
47242
48312
  description: "Get a published scheduled action or a temp_sched_* draft-created scheduled action.",
@@ -47245,7 +48315,7 @@ registerSchema({
47245
48315
  chat: { type: "string", description: CHAT_READ_ARG.description, required: false }
47246
48316
  }
47247
48317
  });
47248
- var getCommand4 = defineCommand182({
48318
+ var getCommand4 = defineCommand184({
47249
48319
  meta: {
47250
48320
  name: "get",
47251
48321
  description: "Get a scheduled action. Example: baker scheduled-actions get <id-or-temp_sched_id>"
@@ -47284,7 +48354,7 @@ var getCommand4 = defineCommand182({
47284
48354
  });
47285
48355
 
47286
48356
  // src/commands/scheduled-actions/list.ts
47287
- import { defineCommand as defineCommand183 } from "citty";
48357
+ import { defineCommand as defineCommand185 } from "citty";
47288
48358
  registerSchema({
47289
48359
  command: "scheduled-actions.list",
47290
48360
  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.",
@@ -47292,7 +48362,7 @@ registerSchema({
47292
48362
  chat: { type: "string", description: CHAT_READ_ARG.description, required: false }
47293
48363
  }
47294
48364
  });
47295
- var listCommand15 = defineCommand183({
48365
+ var listCommand15 = defineCommand185({
47296
48366
  meta: {
47297
48367
  name: "list",
47298
48368
  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."
@@ -47315,9 +48385,9 @@ var listCommand15 = defineCommand183({
47315
48385
  });
47316
48386
 
47317
48387
  // src/commands/scheduled-actions/templates.ts
47318
- import { readFile as readFile24 } from "fs/promises";
47319
- import path38 from "path";
47320
- import { defineCommand as defineCommand184 } from "citty";
48388
+ import { readFile as readFile26 } from "fs/promises";
48389
+ import path41 from "path";
48390
+ import { defineCommand as defineCommand186 } from "citty";
47321
48391
  registerSchema({
47322
48392
  command: "scheduled-actions.templates",
47323
48393
  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.",
@@ -47372,7 +48442,7 @@ registerSchema({
47372
48442
  }
47373
48443
  }
47374
48444
  });
47375
- var templatesCommand = defineCommand184({
48445
+ var templatesCommand = defineCommand186({
47376
48446
  meta: {
47377
48447
  name: "templates",
47378
48448
  description: `The recipes this company can run \u2014 Baker's own plus theirs, with what each one produces.
@@ -47419,7 +48489,7 @@ Full guide: __tooling__/docs/tools/baker/scheduled-actions.md`
47419
48489
  }
47420
48490
  if (save.length > 0) {
47421
48491
  const briefFile = flag("brief-file");
47422
- const brief = briefFile.length > 0 ? await readFile24(path38.resolve(briefFile), "utf8") : flag("brief");
48492
+ const brief = briefFile.length > 0 ? await readFile26(path41.resolve(briefFile), "utf8") : flag("brief");
47423
48493
  if (brief.trim().length === 0) {
47424
48494
  failValidation4("--brief-file (preferred) or --brief is required: the brief is the recipe.");
47425
48495
  }
@@ -47464,7 +48534,7 @@ Full guide: __tooling__/docs/tools/baker/scheduled-actions.md`
47464
48534
  });
47465
48535
 
47466
48536
  // src/commands/scheduled-actions/trigger.ts
47467
- import { defineCommand as defineCommand185 } from "citty";
48537
+ import { defineCommand as defineCommand187 } from "citty";
47468
48538
  registerSchema({
47469
48539
  command: "scheduled-actions.trigger",
47470
48540
  description: "Immediately trigger a published scheduled action. Does not require BAKER_CHAT_ID and rejects temp_sched_* IDs.",
@@ -47472,7 +48542,7 @@ registerSchema({
47472
48542
  id: { type: "string", description: "Published scheduled action ID", required: true }
47473
48543
  }
47474
48544
  });
47475
- var triggerCommand = defineCommand185({
48545
+ var triggerCommand = defineCommand187({
47476
48546
  meta: {
47477
48547
  name: "trigger",
47478
48548
  description: "Immediately trigger a published scheduled action. Example: baker scheduled-actions trigger <id>"
@@ -47509,7 +48579,7 @@ var triggerCommand = defineCommand185({
47509
48579
  });
47510
48580
 
47511
48581
  // src/commands/scheduled-actions/update.ts
47512
- import { defineCommand as defineCommand186 } from "citty";
48582
+ import { defineCommand as defineCommand188 } from "citty";
47513
48583
  registerSchema({
47514
48584
  command: "scheduled-actions.update",
47515
48585
  description: "Stage an update to a published scheduled action or temp_sched_* draft-created scheduled action.",
@@ -47540,7 +48610,7 @@ registerSchema({
47540
48610
  prompt: { type: "string", description: "Replacement additional spawned-agent instructions", required: false }
47541
48611
  }
47542
48612
  });
47543
- var updateCommand3 = defineCommand186({
48613
+ var updateCommand3 = defineCommand188({
47544
48614
  meta: {
47545
48615
  name: "update",
47546
48616
  description: "Stage a scheduled action update. Examples: baker scheduled-actions update <id> --enabled false | baker scheduled-actions update <id> --mode publish"
@@ -47618,7 +48688,7 @@ var updateCommand3 = defineCommand186({
47618
48688
  });
47619
48689
 
47620
48690
  // src/commands/scheduled-actions/index.ts
47621
- var scheduledActionsCommand = defineCommand187({
48691
+ var scheduledActionsCommand = defineCommand189({
47622
48692
  meta: {
47623
48693
  name: "scheduled-actions",
47624
48694
  description: `Manage Scheduled Actions. Subcommands: list, get, create, update, delete, trigger, templates.
@@ -47647,14 +48717,14 @@ Full guide: __tooling__/docs/tools/baker/scheduled-actions.md`
47647
48717
  });
47648
48718
 
47649
48719
  // src/commands/schema.ts
47650
- import { defineCommand as defineCommand188 } from "citty";
48720
+ import { defineCommand as defineCommand190 } from "citty";
47651
48721
  function narrowToFamily(commandName, available) {
47652
48722
  const segments = commandName.split(".");
47653
48723
  const prefix = segments[0] === "ads" && segments[1] ? `ads.${segments[1]}.` : `${segments[0]}.`;
47654
48724
  const siblings = available.filter((name) => name.startsWith(prefix));
47655
48725
  return siblings.length > 0 ? siblings : available;
47656
48726
  }
47657
- var schemaCommand2 = defineCommand188({
48727
+ var schemaCommand2 = defineCommand190({
47658
48728
  meta: {
47659
48729
  name: "schema",
47660
48730
  description: "Inspect command argument schemas (for AI agent introspection). Lists all commands if no argument given. Example: baker schema images.search"
@@ -47698,10 +48768,10 @@ var schemaCommand2 = defineCommand188({
47698
48768
  });
47699
48769
 
47700
48770
  // src/commands/studio/index.ts
47701
- import { defineCommand as defineCommand197 } from "citty";
48771
+ import { defineCommand as defineCommand199 } from "citty";
47702
48772
 
47703
48773
  // src/commands/studio/animate.ts
47704
- import { defineCommand as defineCommand189 } from "citty";
48774
+ import { defineCommand as defineCommand191 } from "citty";
47705
48775
 
47706
48776
  // src/commands/studio/batch.ts
47707
48777
  function projectBatch(generation, full) {
@@ -47890,7 +48960,7 @@ function parseImageRefs(spec) {
47890
48960
  }
47891
48961
  var defaultDeps = {
47892
48962
  ingest: (url) => apiPost("/api/images/ingest", { url, source: "uploaded" }),
47893
- upload: (path40) => uploadLocalImage({ file: path40, contentType: detectImageContentType(path40), source: "uploaded" })
48963
+ upload: (path43) => uploadLocalImage({ file: path43, contentType: detectImageContentType(path43), source: "uploaded" })
47894
48964
  };
47895
48965
  async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
47896
48966
  const refs = parseImageRefs(spec);
@@ -47910,10 +48980,10 @@ async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
47910
48980
  }
47911
48981
  return { imageIds, added };
47912
48982
  }
47913
- function uploadFailure(path40) {
48983
+ function uploadFailure(path43) {
47914
48984
  return (error) => {
47915
48985
  if (error instanceof ApiError) throw error;
47916
- throw new ApiError("VALIDATION_ERROR", `Could not read "${path40}" as an image.`);
48986
+ throw new ApiError("VALIDATION_ERROR", `Could not read "${path43}" as an image.`);
47917
48987
  };
47918
48988
  }
47919
48989
 
@@ -48152,7 +49222,7 @@ function costHintsFor(body) {
48152
49222
  }
48153
49223
  return hints;
48154
49224
  }
48155
- var animateCommand = defineCommand189({
49225
+ var animateCommand = defineCommand191({
48156
49226
  meta: {
48157
49227
  name: "animate",
48158
49228
  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"
@@ -48271,7 +49341,7 @@ var animateCommand = defineCommand189({
48271
49341
  });
48272
49342
 
48273
49343
  // src/commands/studio/generate.ts
48274
- import { defineCommand as defineCommand190 } from "citty";
49344
+ import { defineCommand as defineCommand192 } from "citty";
48275
49345
  var MODEL_LIST2 = IMAGE_MODEL_IDS;
48276
49346
  var DEFAULT_MAX_WAIT_MS2 = 24e4;
48277
49347
  registerSchema({
@@ -48407,7 +49477,7 @@ function buildGenerateBody(args, prompt) {
48407
49477
  }
48408
49478
  return body;
48409
49479
  }
48410
- var generateCommand = defineCommand190({
49480
+ var generateCommand = defineCommand192({
48411
49481
  meta: {
48412
49482
  name: "generate",
48413
49483
  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]]'"
@@ -48485,7 +49555,7 @@ var generateCommand = defineCommand190({
48485
49555
  });
48486
49556
 
48487
49557
  // src/commands/studio/get.ts
48488
- import { defineCommand as defineCommand191 } from "citty";
49558
+ import { defineCommand as defineCommand193 } from "citty";
48489
49559
  registerSchema({
48490
49560
  command: "studio.get",
48491
49561
  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.",
@@ -48494,7 +49564,7 @@ registerSchema({
48494
49564
  full: { type: "boolean", description: "Include settings, references and attribution", required: false }
48495
49565
  }
48496
49566
  });
48497
- var getCommand5 = defineCommand191({
49567
+ var getCommand5 = defineCommand193({
48498
49568
  meta: {
48499
49569
  name: "get",
48500
49570
  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"
@@ -48523,7 +49593,7 @@ var getCommand5 = defineCommand191({
48523
49593
  });
48524
49594
 
48525
49595
  // src/commands/studio/improve.ts
48526
- import { defineCommand as defineCommand192 } from "citty";
49596
+ import { defineCommand as defineCommand194 } from "citty";
48527
49597
  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.";
48528
49598
  registerSchema({
48529
49599
  command: "studio.improve",
@@ -48548,7 +49618,7 @@ registerSchema({
48548
49618
  }
48549
49619
  }
48550
49620
  });
48551
- var improveCommand = defineCommand192({
49621
+ var improveCommand = defineCommand194({
48552
49622
  meta: {
48553
49623
  name: "improve",
48554
49624
  description: `${DESCRIPTION}
@@ -48592,7 +49662,7 @@ Examples:
48592
49662
  });
48593
49663
 
48594
49664
  // src/commands/studio/keep.ts
48595
- import { defineCommand as defineCommand193 } from "citty";
49665
+ import { defineCommand as defineCommand195 } from "citty";
48596
49666
  registerSchema({
48597
49667
  command: "studio.keep",
48598
49668
  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.",
@@ -48602,7 +49672,7 @@ registerSchema({
48602
49672
  undo: { type: "boolean", description: "Un-star an image, or take a kept clip back out", required: false }
48603
49673
  }
48604
49674
  });
48605
- var keepCommand = defineCommand193({
49675
+ var keepCommand = defineCommand195({
48606
49676
  meta: {
48607
49677
  name: "keep",
48608
49678
  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"
@@ -48653,7 +49723,7 @@ var keepCommand = defineCommand193({
48653
49723
  });
48654
49724
 
48655
49725
  // src/commands/studio/list.ts
48656
- import { defineCommand as defineCommand194 } from "citty";
49726
+ import { defineCommand as defineCommand196 } from "citty";
48657
49727
  registerSchema({
48658
49728
  command: "studio.list",
48659
49729
  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.",
@@ -48664,7 +49734,7 @@ registerSchema({
48664
49734
  full: { type: "boolean", description: "Include settings, references and attribution", required: false }
48665
49735
  }
48666
49736
  });
48667
- var listCommand16 = defineCommand194({
49737
+ var listCommand16 = defineCommand196({
48668
49738
  meta: {
48669
49739
  name: "list",
48670
49740
  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"
@@ -48698,7 +49768,7 @@ var listCommand16 = defineCommand194({
48698
49768
  });
48699
49769
 
48700
49770
  // src/commands/studio/models.ts
48701
- import { defineCommand as defineCommand195 } from "citty";
49771
+ import { defineCommand as defineCommand197 } from "citty";
48702
49772
  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.";
48703
49773
  registerSchema({
48704
49774
  command: "studio.models",
@@ -48785,7 +49855,7 @@ function buildModelCards(kind, model) {
48785
49855
  const selected = model ? ids.filter((id) => id === model) : ids;
48786
49856
  return selected.map((id) => build(id));
48787
49857
  }
48788
- var modelsCommand = defineCommand195({
49858
+ var modelsCommand = defineCommand197({
48789
49859
  meta: {
48790
49860
  name: "models",
48791
49861
  description: `${DESCRIPTION2}
@@ -48832,13 +49902,13 @@ Examples:
48832
49902
  });
48833
49903
 
48834
49904
  // src/commands/studio/skills.ts
48835
- import { defineCommand as defineCommand196 } from "citty";
49905
+ import { defineCommand as defineCommand198 } from "citty";
48836
49906
  registerSchema({
48837
49907
  command: "studio.skills",
48838
49908
  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.",
48839
49909
  args: {}
48840
49910
  });
48841
- var skillsCommand = defineCommand196({
49911
+ var skillsCommand = defineCommand198({
48842
49912
  meta: {
48843
49913
  name: "skills",
48844
49914
  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"
@@ -48862,7 +49932,7 @@ var skillsCommand = defineCommand196({
48862
49932
  });
48863
49933
 
48864
49934
  // src/commands/studio/index.ts
48865
- var studioCommand = defineCommand197({
49935
+ var studioCommand = defineCommand199({
48866
49936
  meta: {
48867
49937
  name: "studio",
48868
49938
  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.
@@ -48905,10 +49975,10 @@ Full guide: __tooling__/docs/tools/baker/studio.md`
48905
49975
  });
48906
49976
 
48907
49977
  // src/commands/tag-manager/index.ts
48908
- import { defineCommand as defineCommand201 } from "citty";
49978
+ import { defineCommand as defineCommand203 } from "citty";
48909
49979
 
48910
49980
  // src/commands/tag-manager/draft.ts
48911
- import { defineCommand as defineCommand198 } from "citty";
49981
+ import { defineCommand as defineCommand200 } from "citty";
48912
49982
 
48913
49983
  // src/commands/tag-manager/shared.ts
48914
49984
  import { readFileSync as readFileSync15 } from "fs";
@@ -48975,10 +50045,10 @@ async function stageOp4(op) {
48975
50045
  handleError5(err);
48976
50046
  }
48977
50047
  }
48978
- async function draftAction3(path40, body, chat) {
50048
+ async function draftAction3(path43, body, chat) {
48979
50049
  const chatId = resolveChatId(chat);
48980
50050
  try {
48981
- const data = await apiPost(path40, { chatId, ...body });
50051
+ const data = await apiPost(path43, { chatId, ...body });
48982
50052
  writeJsonEnvelope({ ok: true, data });
48983
50053
  return data;
48984
50054
  } catch (err) {
@@ -49031,13 +50101,13 @@ registerSchema({
49031
50101
  chat: { type: "string", description: CHAT_READ_ARG.description, required: false }
49032
50102
  }
49033
50103
  });
49034
- var draftCommand4 = defineCommand198({
50104
+ var draftCommand4 = defineCommand200({
49035
50105
  meta: {
49036
50106
  name: "draft",
49037
50107
  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."
49038
50108
  },
49039
50109
  subCommands: {
49040
- list: defineCommand198({
50110
+ list: defineCommand200({
49041
50111
  meta: {
49042
50112
  name: "list",
49043
50113
  description: "Review everything staged on this chat (--json for the raw envelope)"
@@ -49050,7 +50120,7 @@ var draftCommand4 = defineCommand198({
49050
50120
  await draftList2(args.json === true, args.chat);
49051
50121
  }
49052
50122
  }),
49053
- show: defineCommand198({
50123
+ show: defineCommand200({
49054
50124
  meta: {
49055
50125
  name: "show",
49056
50126
  description: "Print the full staged payload for one change \u2014 the receipt to verify it looks right before publish (never truncated)."
@@ -49067,7 +50137,7 @@ var draftCommand4 = defineCommand198({
49067
50137
  );
49068
50138
  }
49069
50139
  }),
49070
- amend: defineCommand198({
50140
+ amend: defineCommand200({
49071
50141
  meta: {
49072
50142
  name: "amend",
49073
50143
  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."
@@ -49084,7 +50154,7 @@ var draftCommand4 = defineCommand198({
49084
50154
  });
49085
50155
  }
49086
50156
  }),
49087
- remove: defineCommand198({
50157
+ remove: defineCommand200({
49088
50158
  meta: { name: "remove", description: "Remove one staged change (cascades to anything depending on it)" },
49089
50159
  args: { ref: { type: "positional", description: "Staged ref (gtm_temp_*) or target", required: false } },
49090
50160
  run: async ({ args }) => {
@@ -49093,7 +50163,7 @@ var draftCommand4 = defineCommand198({
49093
50163
  });
49094
50164
  }
49095
50165
  }),
49096
- clear: defineCommand198({
50166
+ clear: defineCommand200({
49097
50167
  meta: { name: "clear", description: "Discard all Tag Manager changes staged on this chat" },
49098
50168
  run: async () => {
49099
50169
  await draftAction3("/api/tag-manager/draft/clear", {});
@@ -49103,7 +50173,7 @@ var draftCommand4 = defineCommand198({
49103
50173
  });
49104
50174
 
49105
50175
  // src/commands/tag-manager/read.ts
49106
- import { defineCommand as defineCommand199 } from "citty";
50176
+ import { defineCommand as defineCommand201 } from "citty";
49107
50177
  registerSchema({
49108
50178
  command: "tagManager.containers",
49109
50179
  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.",
@@ -49144,7 +50214,7 @@ function containersHints(containers) {
49144
50214
  }))
49145
50215
  });
49146
50216
  }
49147
- var containersCommand = defineCommand199({
50217
+ var containersCommand = defineCommand201({
49148
50218
  meta: {
49149
50219
  name: "containers",
49150
50220
  description: `List Tag Manager containers reachable by this company's connection.
@@ -49161,7 +50231,7 @@ Start here:
49161
50231
  }
49162
50232
  }
49163
50233
  });
49164
- var readCommand = defineCommand199({
50234
+ var readCommand = defineCommand201({
49165
50235
  meta: {
49166
50236
  name: "read",
49167
50237
  description: `Read the current contents of the Tag Manager container \u2014 always do this before staging changes.
@@ -49203,7 +50273,7 @@ Examples:
49203
50273
  });
49204
50274
 
49205
50275
  // src/commands/tag-manager/write-commands.ts
49206
- import { defineCommand as defineCommand200 } from "citty";
50276
+ import { defineCommand as defineCommand202 } from "citty";
49207
50277
  var CONTAINER_ARG_DESCRIPTION = "Numeric container id (optional only when one container is connected \u2014 run `baker tag-manager containers`)";
49208
50278
  var ENTITIES = [
49209
50279
  {
@@ -49259,10 +50329,10 @@ for (const { entity, noun, createHint } of ENTITIES) {
49259
50329
  });
49260
50330
  }
49261
50331
  function entityCommand(entity, noun, example) {
49262
- return defineCommand200({
50332
+ return defineCommand202({
49263
50333
  meta: { name: entity, description: `Stage ${noun} changes on this chat's Tag Manager draft` },
49264
50334
  subCommands: {
49265
- create: defineCommand200({
50335
+ create: defineCommand202({
49266
50336
  meta: {
49267
50337
  name: "create",
49268
50338
  description: `Stage a new ${noun}
@@ -49284,7 +50354,7 @@ Examples:
49284
50354
  });
49285
50355
  }
49286
50356
  }),
49287
- update: defineCommand200({
50357
+ update: defineCommand202({
49288
50358
  meta: {
49289
50359
  name: "update",
49290
50360
  description: `Stage an update to an existing ${noun} (pass its id or path)`
@@ -49304,7 +50374,7 @@ Examples:
49304
50374
  });
49305
50375
  }
49306
50376
  }),
49307
- delete: defineCommand200({
50377
+ delete: defineCommand202({
49308
50378
  meta: { name: "delete", description: `Stage the deletion of a ${noun} (pass its id or path)` },
49309
50379
  args: {
49310
50380
  id: { type: "positional", description: `${noun} id or path`, required: false },
@@ -49345,7 +50415,7 @@ function builtinTypes(args) {
49345
50415
  }
49346
50416
  return raw.split(",").map((entry) => entry.trim());
49347
50417
  }
49348
- var builtinCommand = defineCommand200({
50418
+ var builtinCommand = defineCommand202({
49349
50419
  meta: {
49350
50420
  name: "builtin",
49351
50421
  description: `Enable or disable built-in variables
@@ -49355,7 +50425,7 @@ Examples:
49355
50425
  baker tag-manager builtin disable --types formId`
49356
50426
  },
49357
50427
  subCommands: {
49358
- enable: defineCommand200({
50428
+ enable: defineCommand202({
49359
50429
  meta: { name: "enable", description: "Stage enabling built-in variables" },
49360
50430
  args: {
49361
50431
  types: { type: "string", description: "Comma-separated types", required: false },
@@ -49369,7 +50439,7 @@ Examples:
49369
50439
  });
49370
50440
  }
49371
50441
  }),
49372
- disable: defineCommand200({
50442
+ disable: defineCommand202({
49373
50443
  meta: { name: "disable", description: "Stage disabling built-in variables" },
49374
50444
  args: {
49375
50445
  types: { type: "string", description: "Comma-separated types", required: false },
@@ -49387,7 +50457,7 @@ Examples:
49387
50457
  });
49388
50458
 
49389
50459
  // src/commands/tag-manager/index.ts
49390
- var tagManagerCommand = defineCommand201({
50460
+ var tagManagerCommand = defineCommand203({
49391
50461
  meta: {
49392
50462
  name: "tag-manager",
49393
50463
  description: `Read and change what lives inside the client's Google Tag Manager container \u2014 tags, triggers, variables, folders and built-in variables.
@@ -49424,7 +50494,7 @@ Full guide: __tooling__/docs/tools/baker/tag-manager.md`
49424
50494
  });
49425
50495
 
49426
50496
  // src/commands/tags/index.ts
49427
- import { defineCommand as defineCommand202 } from "citty";
50497
+ import { defineCommand as defineCommand204 } from "citty";
49428
50498
 
49429
50499
  // src/commands/tags/shared.ts
49430
50500
  function failApi3(err) {
@@ -49493,7 +50563,7 @@ async function listTags(json) {
49493
50563
  var listArgs9 = {
49494
50564
  json: { type: "boolean", description: "Print the raw JSON envelope instead of the readable list" }
49495
50565
  };
49496
- var listCommand17 = defineCommand202({
50566
+ var listCommand17 = defineCommand204({
49497
50567
  meta: {
49498
50568
  name: "list",
49499
50569
  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"
@@ -49512,7 +50582,7 @@ async function listDraft3(chat) {
49512
50582
  failApi3(err);
49513
50583
  }
49514
50584
  }
49515
- var draftCommand5 = defineCommand202({
50585
+ var draftCommand5 = defineCommand204({
49516
50586
  meta: {
49517
50587
  name: "draft",
49518
50588
  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."
@@ -49522,7 +50592,7 @@ var draftCommand5 = defineCommand202({
49522
50592
  await listDraft3(args.chat);
49523
50593
  }
49524
50594
  });
49525
- var tagsCommand4 = defineCommand202({
50595
+ var tagsCommand4 = defineCommand204({
49526
50596
  meta: {
49527
50597
  name: "tags",
49528
50598
  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.
@@ -49551,10 +50621,10 @@ Full guide: __tooling__/docs/tools/baker/tags.md`
49551
50621
  });
49552
50622
 
49553
50623
  // src/commands/testimonials/index.ts
49554
- import { defineCommand as defineCommand206 } from "citty";
50624
+ import { defineCommand as defineCommand208 } from "citty";
49555
50625
 
49556
50626
  // src/commands/testimonials/get.ts
49557
- import { defineCommand as defineCommand203 } from "citty";
50627
+ import { defineCommand as defineCommand205 } from "citty";
49558
50628
  registerSchema({
49559
50629
  command: "testimonials.get",
49560
50630
  description: "Get a single testimonial by ID",
@@ -49562,7 +50632,7 @@ registerSchema({
49562
50632
  id: { type: "string", description: "Testimonial ID", required: true }
49563
50633
  }
49564
50634
  });
49565
- var getCommand6 = defineCommand203({
50635
+ var getCommand6 = defineCommand205({
49566
50636
  meta: { name: "get", description: "Get a single testimonial by ID. Example: baker testimonials get j571abc123" },
49567
50637
  args: {
49568
50638
  id: { type: "positional", description: "Testimonial ID", required: false },
@@ -49599,7 +50669,7 @@ var getCommand6 = defineCommand203({
49599
50669
  });
49600
50670
 
49601
50671
  // src/commands/testimonials/list.ts
49602
- import { defineCommand as defineCommand204 } from "citty";
50672
+ import { defineCommand as defineCommand206 } from "citty";
49603
50673
 
49604
50674
  // src/commands/testimonials/emptyCorpusHints.ts
49605
50675
  function resolveEmptyReason({
@@ -49734,7 +50804,7 @@ function buildListParams(args) {
49734
50804
  }
49735
50805
  return params;
49736
50806
  }
49737
- var listCommand18 = defineCommand204({
50807
+ var listCommand18 = defineCommand206({
49738
50808
  meta: {
49739
50809
  name: "list",
49740
50810
  description: "List testimonials with optional filters. Example: baker testimonials list --source google --sentiment positive"
@@ -49786,7 +50856,7 @@ var listCommand18 = defineCommand204({
49786
50856
  });
49787
50857
 
49788
50858
  // src/commands/testimonials/search.ts
49789
- import { defineCommand as defineCommand205 } from "citty";
50859
+ import { defineCommand as defineCommand207 } from "citty";
49790
50860
  var FILTER_FLAGS2 = ["source", "rating-min", "rating-max", "status", "sentiment", "language", "tags"];
49791
50861
  function languageBiasHint(results, requestedLanguage) {
49792
50862
  if (requestedLanguage) {
@@ -49865,7 +50935,7 @@ function buildSearchRequest(query, args) {
49865
50935
  }
49866
50936
  return body;
49867
50937
  }
49868
- var searchCommand3 = defineCommand205({
50938
+ var searchCommand3 = defineCommand207({
49869
50939
  meta: {
49870
50940
  name: "search",
49871
50941
  description: "Semantic search testimonials by text query. Uses hybrid BM25 + vector + reranking. Example: baker testimonials search 'great service' --rating-min 4"
@@ -49929,7 +50999,7 @@ var searchCommand3 = defineCommand205({
49929
50999
  var tagsCommand5 = makeTagsCommand("testimonials", "testimonial", "/api/testimonials/tags");
49930
51000
 
49931
51001
  // src/commands/testimonials/index.ts
49932
- var testimonialsCommand = defineCommand206({
51002
+ var testimonialsCommand = defineCommand208({
49933
51003
  meta: {
49934
51004
  name: "testimonials",
49935
51005
  description: `Find and browse testimonials in Baker. Subcommands: search, get, list, tags.
@@ -49951,10 +51021,10 @@ Full guide: __tooling__/docs/tools/baker/testimonials.md`
49951
51021
  });
49952
51022
 
49953
51023
  // src/commands/videos/index.ts
49954
- import { defineCommand as defineCommand213 } from "citty";
51024
+ import { defineCommand as defineCommand215 } from "citty";
49955
51025
 
49956
51026
  // src/commands/videos/delete.ts
49957
- import { defineCommand as defineCommand207 } from "citty";
51027
+ import { defineCommand as defineCommand209 } from "citty";
49958
51028
  registerSchema({
49959
51029
  command: "videos.delete",
49960
51030
  description: "Delete a video by ID",
@@ -49968,7 +51038,7 @@ registerSchema({
49968
51038
  }
49969
51039
  }
49970
51040
  });
49971
- var deleteCommand4 = defineCommand207({
51041
+ var deleteCommand4 = defineCommand209({
49972
51042
  meta: {
49973
51043
  name: "delete",
49974
51044
  description: "Delete a video by ID. Use --dry-run to preview. Example: baker videos delete j571abc123 --dry-run"
@@ -50009,7 +51079,7 @@ var deleteCommand4 = defineCommand207({
50009
51079
  });
50010
51080
 
50011
51081
  // src/commands/videos/get.ts
50012
- import { defineCommand as defineCommand208 } from "citty";
51082
+ import { defineCommand as defineCommand210 } from "citty";
50013
51083
  registerSchema({
50014
51084
  command: "videos.get",
50015
51085
  description: "Get a single video by ID",
@@ -50017,7 +51087,7 @@ registerSchema({
50017
51087
  id: { type: "string", description: "Video ID", required: true }
50018
51088
  }
50019
51089
  });
50020
- var getCommand7 = defineCommand208({
51090
+ var getCommand7 = defineCommand210({
50021
51091
  meta: { name: "get", description: "Get a single video by ID. Example: baker videos get j571abc123" },
50022
51092
  args: {
50023
51093
  id: { type: "positional", description: "Video ID", required: false },
@@ -50054,7 +51124,7 @@ var getCommand7 = defineCommand208({
50054
51124
  });
50055
51125
 
50056
51126
  // src/commands/videos/group.ts
50057
- import { defineCommand as defineCommand209 } from "citty";
51127
+ import { defineCommand as defineCommand211 } from "citty";
50058
51128
  registerSchema({
50059
51129
  command: "videos.group",
50060
51130
  description: "List every clip and image that arrived in the same set as this video (carousel slides, one page)",
@@ -50063,7 +51133,7 @@ registerSchema({
50063
51133
  "group-key": { type: "string", description: "The set key directly, when you already have it", required: false }
50064
51134
  }
50065
51135
  });
50066
- var groupCommand2 = defineCommand209({
51136
+ var groupCommand2 = defineCommand211({
50067
51137
  meta: {
50068
51138
  name: "group",
50069
51139
  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>"
@@ -50083,10 +51153,10 @@ var groupCommand2 = defineCommand209({
50083
51153
  });
50084
51154
 
50085
51155
  // src/commands/videos/ingest.ts
50086
- import { mkdtemp as mkdtemp2, rm as rm7, stat as stat7 } from "fs/promises";
51156
+ import { mkdtemp as mkdtemp2, rm as rm7, stat as stat8 } from "fs/promises";
50087
51157
  import { tmpdir as tmpdir3 } from "os";
50088
- import path39 from "path";
50089
- import { defineCommand as defineCommand210 } from "citty";
51158
+ import path42 from "path";
51159
+ import { defineCommand as defineCommand212 } from "citty";
50090
51160
 
50091
51161
  // src/lib/streamUpload.ts
50092
51162
  import { createHash as createHash2 } from "crypto";
@@ -50250,7 +51320,7 @@ registerSchema({
50250
51320
  "dry-run": { type: "boolean", description: "Preview the operation without executing", required: false }
50251
51321
  }
50252
51322
  });
50253
- var ingestCommand2 = defineCommand210({
51323
+ var ingestCommand2 = defineCommand212({
50254
51324
  meta: {
50255
51325
  name: "ingest",
50256
51326
  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"
@@ -50366,35 +51436,35 @@ async function ingestByRoute(args, direct, country) {
50366
51436
  function reportYtDlpFailure(err) {
50367
51437
  const detail = `${err.stderrTail} ${err.message}`;
50368
51438
  if (isProxyFailure(ytDlpBlockSignal(detail))) {
50369
- fail7(
51439
+ fail9(
50370
51440
  "Couldn't download that video: our connection to the internet was refused.",
50371
51441
  "This is Baker's egress proxy, not the link \u2014 its credentials look wrong or expired. The link is probably fine. Report it; retrying won't help until the proxy is fixed."
50372
51442
  );
50373
51443
  }
50374
51444
  if (isDrmProtected(detail)) {
50375
- fail7(
51445
+ fail9(
50376
51446
  "That video is copy-protected, so it can't be added to the library.",
50377
51447
  "The publisher encrypted this one against downloading. Nothing will change that \u2014 not retrying, not a different region. Ask whoever owns the video for the original file and add it with `baker videos upload <file>`."
50378
51448
  );
50379
51449
  }
50380
51450
  if (isVimeoAuthFailure(detail)) {
50381
- fail7(
51451
+ fail9(
50382
51452
  "Vimeo wouldn't hand over that video \u2014 Baker isn't signed in to Vimeo.",
50383
51453
  "Vimeo now refuses anonymous downloads entirely, so this needs a signed-in session and the one Baker holds has either expired or was never set. Nothing is wrong with the link. Report it so the Vimeo cookie can be refreshed; meanwhile download the file and use `baker videos upload <file>`."
50384
51454
  );
50385
51455
  }
50386
51456
  if (isRegionBlocked(detail)) {
50387
- fail7(
51457
+ fail9(
50388
51458
  `That video isn't available in the region we're fetching from. ${err.stderrTail || err.message}`,
50389
51459
  "Re-run with `--country <two-letter code>` for a country where the video plays \u2014 e.g. `--country US`. Pick the one the video belongs to (the uploader's country is the usual answer); each attempt costs a paid connection, so choose rather than work through the list."
50390
51460
  );
50391
51461
  }
50392
- fail7(
51462
+ fail9(
50393
51463
  `Couldn't download that video. ${err.stderrTail || err.message}`,
50394
51464
  isToolingOrBlockFailure(detail) ? "The site wouldn't hand over the video \u2014 usually the downloader being out of date against a site that changed, or the request being blocked. Retrying won't help, and the link is probably fine. Report it so the tool can be updated; meanwhile download the file and use `baker videos upload <file>`." : "Check the link is public and playable. Private or age-restricted videos can't be downloaded \u2014 download the file yourself and use `baker videos upload <file>`."
50395
51465
  );
50396
51466
  }
50397
- function fail7(message, fix, code = "INGEST_FAILED") {
51467
+ function fail9(message, fix, code = "INGEST_FAILED") {
50398
51468
  writeJson({ ok: false, error: { code, message, fix } });
50399
51469
  process.exit(1);
50400
51470
  }
@@ -50406,13 +51476,13 @@ function reportIngestFailure(err) {
50406
51476
  if (err instanceof YtDlpError) reportYtDlpFailure(err);
50407
51477
  const detail = err instanceof Error ? err.message : String(err);
50408
51478
  if (detail.includes("timed out after")) {
50409
- fail7(
51479
+ fail9(
50410
51480
  "That video took too long to download and was stopped part-way.",
50411
51481
  "Long or slow-serving videos can outrun the download window. Try a shorter clip, or download this one yourself and use `baker videos upload <file>`.",
50412
51482
  "INTERNAL_ERROR"
50413
51483
  );
50414
51484
  }
50415
- fail7(
51485
+ fail9(
50416
51486
  `Couldn't add that video. ${detail}`,
50417
51487
  "This is a fault on Baker's side, not a problem with the link. Report it.",
50418
51488
  "INTERNAL_ERROR"
@@ -50436,7 +51506,7 @@ function ingestUrl(args) {
50436
51506
  }
50437
51507
  async function downloadThenIngest(args, country) {
50438
51508
  const vimeoCookie = captureVimeoCookie();
50439
- const workDir = await mkdtemp2(path39.join(tmpdir3(), "videos-ingest-"));
51509
+ const workDir = await mkdtemp2(path42.join(tmpdir3(), "videos-ingest-"));
50440
51510
  try {
50441
51511
  const probe = await probeYtDlp({ url: args.url, country, vimeoCookie, cookieDir: workDir });
50442
51512
  if (isAudioOnly(probe.info)) {
@@ -50467,7 +51537,7 @@ async function downloadThenIngest(args, country) {
50467
51537
  // we allow to fetch it cannot drift apart.
50468
51538
  timeoutMs: downloadTimeoutMs(durationSeconds(probe.info))
50469
51539
  });
50470
- const stats = await stat7(filePath);
51540
+ const stats = await stat8(filePath);
50471
51541
  if (stats.size > MAX_VIDEO_INGEST_BYTES) {
50472
51542
  throw new ApiError(
50473
51543
  "VALIDATION_ERROR",
@@ -50512,7 +51582,7 @@ async function uploadToAssetStore(filePath, sizeBytes) {
50512
51582
  }
50513
51583
 
50514
51584
  // src/commands/videos/search.ts
50515
- import { defineCommand as defineCommand211 } from "citty";
51585
+ import { defineCommand as defineCommand213 } from "citty";
50516
51586
  registerSchema({
50517
51587
  command: "videos.search",
50518
51588
  description: "Search videos by text query. Only returns ready videos.",
@@ -50522,7 +51592,7 @@ registerSchema({
50522
51592
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
50523
51593
  }
50524
51594
  });
50525
- var searchCommand4 = defineCommand211({
51595
+ var searchCommand4 = defineCommand213({
50526
51596
  meta: {
50527
51597
  name: "search",
50528
51598
  description: "Semantic search videos by text query. Uses hybrid BM25 + vector + reranking. Example: baker videos search 'product demo' --tags tutorial"
@@ -50572,9 +51642,9 @@ var searchCommand4 = defineCommand211({
50572
51642
  var tagsCommand6 = makeTagsCommand("videos", "video", "/api/videos/tags");
50573
51643
 
50574
51644
  // src/commands/videos/upload.ts
50575
- import { readFile as readFile25, stat as stat8 } from "fs/promises";
51645
+ import { readFile as readFile27, stat as stat9 } from "fs/promises";
50576
51646
  import { basename as basename3, extname as extname4 } from "path";
50577
- import { defineCommand as defineCommand212 } from "citty";
51647
+ import { defineCommand as defineCommand214 } from "citty";
50578
51648
  var MIME_MAP = {
50579
51649
  ".mp4": "video/mp4",
50580
51650
  ".mov": "video/quicktime",
@@ -50616,7 +51686,7 @@ function detectContentType(filePath) {
50616
51686
  function isRemoteUrl3(value) {
50617
51687
  return /^https?:\/\//i.test(value);
50618
51688
  }
50619
- var uploadCommand2 = defineCommand212({
51689
+ var uploadCommand2 = defineCommand214({
50620
51690
  meta: {
50621
51691
  name: "upload",
50622
51692
  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"
@@ -50656,7 +51726,7 @@ var uploadCommand2 = defineCommand212({
50656
51726
  const originalFilename = basename3(filePath);
50657
51727
  const descriptionContext = args.context;
50658
51728
  if (args["dry-run"]) {
50659
- const fileStats = await stat8(filePath);
51729
+ const fileStats = await stat9(filePath);
50660
51730
  writeJson({
50661
51731
  ok: true,
50662
51732
  dryRun: true,
@@ -50669,7 +51739,7 @@ var uploadCommand2 = defineCommand212({
50669
51739
  originalFilename,
50670
51740
  descriptionContext
50671
51741
  });
50672
- const fileBuffer = await readFile25(filePath);
51742
+ const fileBuffer = await readFile27(filePath);
50673
51743
  const uploadResponse = await fetch(uploadUrl, {
50674
51744
  method: "PUT",
50675
51745
  headers: { "Content-Type": contentType },
@@ -50700,7 +51770,7 @@ var uploadCommand2 = defineCommand212({
50700
51770
  });
50701
51771
 
50702
51772
  // src/commands/videos/index.ts
50703
- var videosCommand = defineCommand213({
51773
+ var videosCommand = defineCommand215({
50704
51774
  meta: {
50705
51775
  name: "videos",
50706
51776
  description: `Find and manage videos in Baker. Subcommands: search, get, upload, ingest, delete, tags.
@@ -50727,10 +51797,10 @@ Full guide: __tooling__/docs/tools/baker/videos.md`
50727
51797
  });
50728
51798
 
50729
51799
  // src/commands/winning-ads/index.ts
50730
- import { defineCommand as defineCommand226 } from "citty";
51800
+ import { defineCommand as defineCommand228 } from "citty";
50731
51801
 
50732
51802
  // src/commands/winning-ads/advertisers.ts
50733
- import { defineCommand as defineCommand214 } from "citty";
51803
+ import { defineCommand as defineCommand216 } from "citty";
50734
51804
 
50735
51805
  // src/commands/winning-ads/shared.ts
50736
51806
  function splitList2(value) {
@@ -50783,7 +51853,7 @@ function advertiserNormalizer(record, full) {
50783
51853
  last_synced_at: record.last_synced_at ?? null
50784
51854
  };
50785
51855
  }
50786
- var advertisersCommand2 = defineCommand214({
51856
+ var advertisersCommand2 = defineCommand216({
50787
51857
  meta: {
50788
51858
  name: "advertisers",
50789
51859
  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'
@@ -50841,7 +51911,7 @@ var advertisersCommand2 = defineCommand214({
50841
51911
  });
50842
51912
 
50843
51913
  // src/commands/winning-ads/brief.ts
50844
- import { defineCommand as defineCommand215 } from "citty";
51914
+ import { defineCommand as defineCommand217 } from "citty";
50845
51915
  registerSchema({
50846
51916
  command: "winning-ads.brief",
50847
51917
  description: "Generate a creative brief grounded in strategically-similar winning ads. Optionally describe the target creative with --dna (JSON) and steer with --notes.",
@@ -50887,7 +51957,7 @@ function parseDna(raw) {
50887
51957
  }
50888
51958
  return parsed;
50889
51959
  }
50890
- var briefCommand = defineCommand215({
51960
+ var briefCommand = defineCommand217({
50891
51961
  meta: {
50892
51962
  name: "brief",
50893
51963
  description: `Generate a creative brief from winning references. Example: baker winning-ads brief --dna '{"angle":"cost savings"}' --notes "B2B, LinkedIn video" --k 8`
@@ -50923,7 +51993,7 @@ var briefCommand = defineCommand215({
50923
51993
  });
50924
51994
 
50925
51995
  // src/commands/winning-ads/content.ts
50926
- import { defineCommand as defineCommand216 } from "citty";
51996
+ import { defineCommand as defineCommand218 } from "citty";
50927
51997
  registerSchema({
50928
51998
  command: "winning-ads.content",
50929
51999
  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.",
@@ -50936,7 +52006,7 @@ registerSchema({
50936
52006
  }
50937
52007
  }
50938
52008
  });
50939
- var contentCommand = defineCommand216({
52009
+ var contentCommand = defineCommand218({
50940
52010
  meta: {
50941
52011
  name: "content",
50942
52012
  description: "Read the transcript + on-screen text + copy of one winning ad. Example: baker winning-ads content adg_123 --platform meta --full --output md"
@@ -50985,7 +52055,7 @@ var contentCommand = defineCommand216({
50985
52055
  });
50986
52056
 
50987
52057
  // src/commands/winning-ads/feed.ts
50988
- import { defineCommand as defineCommand217 } from "citty";
52058
+ import { defineCommand as defineCommand219 } from "citty";
50989
52059
  function buildFeedParams(input) {
50990
52060
  const params = {};
50991
52061
  const advertiser = splitList2(input.advertiser);
@@ -51037,7 +52107,7 @@ registerSchema({
51037
52107
  format: { type: "string", description: "Comma-separated formats to include (e.g. static,video)", required: false }
51038
52108
  }
51039
52109
  });
51040
- var feedCommand = defineCommand217({
52110
+ var feedCommand = defineCommand219({
51041
52111
  meta: {
51042
52112
  name: "feed",
51043
52113
  description: "Winners across every brand you follow (browse, then trim per advertiser). Example: baker winning-ads feed --per-advertiser 5 --output md"
@@ -51122,7 +52192,7 @@ var feedCommand = defineCommand217({
51122
52192
  });
51123
52193
 
51124
52194
  // src/commands/winning-ads/follow.ts
51125
- import { defineCommand as defineCommand218 } from "citty";
52195
+ import { defineCommand as defineCommand220 } from "citty";
51126
52196
  var PLATFORMS = ["meta", "linkedin"];
51127
52197
  registerSchema({
51128
52198
  command: "winning-ads.follow",
@@ -51137,7 +52207,7 @@ registerSchema({
51137
52207
  label: { type: "string", description: "Optional display label (defaults to the resolved name)", required: false }
51138
52208
  }
51139
52209
  });
51140
- var followCommand = defineCommand218({
52210
+ var followCommand = defineCommand220({
51141
52211
  meta: {
51142
52212
  name: "follow",
51143
52213
  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'
@@ -51184,7 +52254,7 @@ var followCommand = defineCommand218({
51184
52254
  });
51185
52255
 
51186
52256
  // src/commands/winning-ads/follow-competitors.ts
51187
- import { defineCommand as defineCommand219 } from "citty";
52257
+ import { defineCommand as defineCommand221 } from "citty";
51188
52258
  var PLATFORMS2 = ["meta", "linkedin"];
51189
52259
  var BATCH_TIMEOUT_MS = 3e5;
51190
52260
  function buildFollowBatchBody(input) {
@@ -51217,7 +52287,7 @@ registerSchema({
51217
52287
  }
51218
52288
  }
51219
52289
  });
51220
- var followCompetitorsCommand = defineCommand219({
52290
+ var followCompetitorsCommand = defineCommand221({
51221
52291
  meta: {
51222
52292
  name: "follow-competitors",
51223
52293
  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"'
@@ -51292,7 +52362,7 @@ var followCompetitorsCommand = defineCommand219({
51292
52362
  });
51293
52363
 
51294
52364
  // src/commands/winning-ads/following.ts
51295
- import { defineCommand as defineCommand220 } from "citty";
52365
+ import { defineCommand as defineCommand222 } from "citty";
51296
52366
  registerSchema({
51297
52367
  command: "winning-ads.following",
51298
52368
  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.",
@@ -51346,7 +52416,7 @@ function followingNormalizer(record, full) {
51346
52416
  platforms: Array.isArray(record.platforms) ? record.platforms : []
51347
52417
  };
51348
52418
  }
51349
- var followingCommand = defineCommand220({
52419
+ var followingCommand = defineCommand222({
51350
52420
  meta: {
51351
52421
  name: "following",
51352
52422
  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"
@@ -51382,7 +52452,7 @@ var followingCommand = defineCommand220({
51382
52452
  });
51383
52453
 
51384
52454
  // src/commands/winning-ads/patterns.ts
51385
- import { defineCommand as defineCommand221 } from "citty";
52455
+ import { defineCommand as defineCommand223 } from "citty";
51386
52456
  registerSchema({
51387
52457
  command: "winning-ads.patterns",
51388
52458
  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.",
@@ -51421,7 +52491,7 @@ function discriminatorRow(record) {
51421
52491
  top_values_duds: Array.isArray(record.top_values_b) ? record.top_values_b.join(", ") : ""
51422
52492
  };
51423
52493
  }
51424
- var patternsCommand = defineCommand221({
52494
+ var patternsCommand = defineCommand223({
51425
52495
  meta: {
51426
52496
  name: "patterns",
51427
52497
  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"
@@ -51477,7 +52547,7 @@ var patternsCommand = defineCommand221({
51477
52547
  });
51478
52548
 
51479
52549
  // src/commands/winning-ads/search.ts
51480
- import { defineCommand as defineCommand222 } from "citty";
52550
+ import { defineCommand as defineCommand224 } from "citty";
51481
52551
  registerSchema({
51482
52552
  command: "winning-ads.search",
51483
52553
  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.",
@@ -51585,7 +52655,7 @@ function buildSearchBody2(args) {
51585
52655
  }
51586
52656
  return body;
51587
52657
  }
51588
- var searchCommand5 = defineCommand222({
52658
+ var searchCommand5 = defineCommand224({
51589
52659
  meta: {
51590
52660
  name: "search",
51591
52661
  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"
@@ -51700,7 +52770,7 @@ var searchCommand5 = defineCommand222({
51700
52770
  });
51701
52771
 
51702
52772
  // src/commands/winning-ads/seeds.ts
51703
- import { defineCommand as defineCommand223 } from "citty";
52773
+ import { defineCommand as defineCommand225 } from "citty";
51704
52774
  function leanRow(r) {
51705
52775
  return {
51706
52776
  key: r.key,
@@ -51728,7 +52798,7 @@ function makeSeedCommand(opts) {
51728
52798
  limit: { type: "number", description: "Max keys 1-100 (default 20)", required: false, default: 20 }
51729
52799
  }
51730
52800
  });
51731
- return defineCommand223({
52801
+ return defineCommand225({
51732
52802
  meta: { name: opts.name, description: opts.description },
51733
52803
  args: {
51734
52804
  platform: { type: "string", description: "Single platform to segment on", required: false },
@@ -51777,7 +52847,7 @@ var formatsCommand = makeSeedCommand({
51777
52847
  });
51778
52848
 
51779
52849
  // src/commands/winning-ads/unfollow.ts
51780
- import { defineCommand as defineCommand224 } from "citty";
52850
+ import { defineCommand as defineCommand226 } from "citty";
51781
52851
  registerSchema({
51782
52852
  command: "winning-ads.unfollow",
51783
52853
  description: "Stop following a brand \u2014 removes it from your ad-dna library by advertiser id.",
@@ -51785,7 +52855,7 @@ registerSchema({
51785
52855
  advertiser: { type: "string", description: "Advertiser id to unfollow", required: true }
51786
52856
  }
51787
52857
  });
51788
- var unfollowCommand = defineCommand224({
52858
+ var unfollowCommand = defineCommand226({
51789
52859
  meta: {
51790
52860
  name: "unfollow",
51791
52861
  description: "Stop following a brand by advertiser id. Example: baker winning-ads unfollow adv_123"
@@ -51806,7 +52876,7 @@ var unfollowCommand = defineCommand224({
51806
52876
  });
51807
52877
 
51808
52878
  // src/commands/winning-ads/winners.ts
51809
- import { defineCommand as defineCommand225 } from "citty";
52879
+ import { defineCommand as defineCommand227 } from "citty";
51810
52880
  registerSchema({
51811
52881
  command: "winning-ads.winners",
51812
52882
  description: "Top winning ads for one advertiser id (from `advertisers` or `following`). Returns lean winner cards; add --full for DNA + longevity.",
@@ -51816,7 +52886,7 @@ registerSchema({
51816
52886
  platform: { type: "string", description: "Filter to a single platform: meta|linkedin", required: false }
51817
52887
  }
51818
52888
  });
51819
- var winnersCommand = defineCommand225({
52889
+ var winnersCommand = defineCommand227({
51820
52890
  meta: {
51821
52891
  name: "winners",
51822
52892
  description: "Top winning ads for a specific advertiser id. Example: baker winning-ads winners adv_123 --top 15 --output md"
@@ -51866,7 +52936,7 @@ var winnersCommand = defineCommand225({
51866
52936
  });
51867
52937
 
51868
52938
  // src/commands/winning-ads/index.ts
51869
- var winningAdsCommand = defineCommand226({
52939
+ var winningAdsCommand = defineCommand228({
51870
52940
  meta: {
51871
52941
  name: "winning-ads",
51872
52942
  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.
@@ -52056,7 +53126,7 @@ function unknownFlagEnvelope(unknown, commandPath, suggestion) {
52056
53126
  };
52057
53127
  }
52058
53128
  function commandPathOf(root, argv) {
52059
- const path40 = [];
53129
+ const path43 = [];
52060
53130
  let command = root;
52061
53131
  for (const token of argv) {
52062
53132
  if (token === "--" || token.startsWith("-")) {
@@ -52067,10 +53137,10 @@ function commandPathOf(root, argv) {
52067
53137
  if (next === void 0 || typeof next !== "object") {
52068
53138
  break;
52069
53139
  }
52070
- path40.push(token);
53140
+ path43.push(token);
52071
53141
  command = next;
52072
53142
  }
52073
- return path40.join(" ");
53143
+ return path43.join(" ");
52074
53144
  }
52075
53145
  function refuseUnknownFlags(root, argv) {
52076
53146
  const unknown = findUnknownFlags(root, argv);
@@ -52104,7 +53174,7 @@ function getCliVersion() {
52104
53174
  }
52105
53175
 
52106
53176
  // src/cli.ts
52107
- var main = defineCommand227({
53177
+ var main = defineCommand229({
52108
53178
  meta: {
52109
53179
  name: "baker",
52110
53180
  version: getCliVersion(),
@@ -52123,6 +53193,7 @@ Introspection: Run 'baker schema <command>' to inspect argument schemas.`
52123
53193
  ads: adsCommand2,
52124
53194
  brand: brandCommand,
52125
53195
  analytics: analyticsCommand2,
53196
+ experiment: experimentCommand,
52126
53197
  ga4: ga4Command,
52127
53198
  gsc: gscCommand,
52128
53199
  research: researchCommand,