@koda-sl/baker-cli 0.380.0-dev.d26778d3f → 0.381.0-dev.3442c3753

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
@@ -133,7 +133,7 @@ import {
133
133
  } from "./chunk-7YNOGIWQ.js";
134
134
 
135
135
  // src/cli.ts
136
- import { defineCommand as defineCommand255, runMain } from "citty";
136
+ import { defineCommand as defineCommand256, runMain } from "citty";
137
137
 
138
138
  // src/cache-flag.ts
139
139
  var NO_CACHE_ARG = {
@@ -5111,6 +5111,20 @@ var FLOW_SIDE_EFFECT_CATALOG = [
5111
5111
  reference: SIDE_EFFECT_REF("httpWebhook")
5112
5112
  },
5113
5113
  crmSideEffect("googleSpreadsheet", "Appends a row to a Google Sheet.", "columnMapping", MAP_FIRST),
5114
+ {
5115
+ kind: "side-effect",
5116
+ type: "shopifyCustomer",
5117
+ summary: "Adds a sign-up to the connected Shopify store with the consent they ticked.",
5118
+ skeleton: {
5119
+ ...SIDE_EFFECT_ENVELOPE,
5120
+ type: "shopifyCustomer",
5121
+ customer: { email: null, phone: null, acceptsEmailMarketing: null, acceptsSmsMarketing: null },
5122
+ offer: null
5123
+ },
5124
+ owners: { customer: "agent", offer: "agent" },
5125
+ gotcha: "Map email to an email field with baker flows map. Email and SMS consent each need their own optional, visible, initially unticked checkbox; SMS also needs phone. No connection secret is needed. An optional offer names a published sign-up offer; read baker shopify signup-offers. Show its code with ShopifySignupCode.",
5126
+ reference: SIDE_EFFECT_REF("shopifyCustomer")
5127
+ },
5114
5128
  crmSideEffect("hubspotForm", "Submits to a HubSpot form endpoint.", "fieldMapping", MAP_FIRST),
5115
5129
  crmSideEffect("pipedrive", "Creates or updates a Pipedrive record.", "fieldMapping", MAP_FIRST),
5116
5130
  crmSideEffect("goHighlevelContact", "Creates or updates a GoHighLevel contact.", "fieldMapping", MAP_FIRST),
@@ -7099,6 +7113,69 @@ var STORE_EVENTS = {
7099
7113
  }
7100
7114
  };
7101
7115
  var STORE_EVENT_NAMES = Object.keys(STORE_EVENTS);
7116
+ var STORE_FIELD_LABELS = {
7117
+ baker_source: "Comes from",
7118
+ store: "Store",
7119
+ title: "Product",
7120
+ handle: "Product address",
7121
+ status: "Status",
7122
+ published: "On the store",
7123
+ price: "Price",
7124
+ price_max: "Highest price",
7125
+ currency: "Currency",
7126
+ collections: "Collections",
7127
+ tags: "Tags",
7128
+ product_type: "Product type",
7129
+ vendor: "Brand",
7130
+ product_url: "Product page",
7131
+ whole_product: "Whole product out of stock",
7132
+ options_out: "Options out of stock",
7133
+ options_out_count: "How many options are out",
7134
+ options_count: "How many options it has",
7135
+ promoted_by: "Ads found",
7136
+ promotions_checked: "Where Baker looked for ads",
7137
+ whole_product_was_out: "Whole product was out of stock",
7138
+ options_back: "Options back in stock",
7139
+ still_out_count: "Options still out of stock",
7140
+ stock_holds: "Ads Baker paused",
7141
+ reason: "Why",
7142
+ changed: "What changed",
7143
+ before_title: "Name before",
7144
+ before_price: "Price before",
7145
+ before_status: "Status before",
7146
+ before_published: "On the store before",
7147
+ price_changes: "Price changes",
7148
+ collection: "Collection",
7149
+ product_count: "Products",
7150
+ before_product_count: "Products before",
7151
+ order_name: "Order",
7152
+ ordered_at: "Ordered at",
7153
+ shopify_source: "Sold through",
7154
+ visitor_from: "Shopper came from",
7155
+ campaign_from: "Campaign",
7156
+ customer: "Customer",
7157
+ country: "Country",
7158
+ accepts_email: "Accepts email",
7159
+ orders: "Orders",
7160
+ account_state: "Account",
7161
+ total: "Total",
7162
+ items_count: "Items",
7163
+ products: "Products",
7164
+ product_handles: "Product addresses",
7165
+ known_customer: "Known customer",
7166
+ discount_codes_count: "Discount codes",
7167
+ started_at: "Started at",
7168
+ last_activity_at: "Last active at",
7169
+ window_hours: "Hours looked at",
7170
+ window_start: "From",
7171
+ window_end: "To",
7172
+ expected_orders: "Usual orders",
7173
+ revenue: "Sales",
7174
+ expected_revenue: "Usual sales",
7175
+ drop_percent: "Drop (%)",
7176
+ baseline_days: "Days compared"
7177
+ };
7178
+ var FIELD_LABELS = new Map(Object.entries(STORE_FIELD_LABELS));
7102
7179
 
7103
7180
  // ../api/src/scheduledAgentRules.ts
7104
7181
  var CONDITION_OPERATORS = {
@@ -8541,9 +8618,9 @@ function parsePriority(value, { allowClear }) {
8541
8618
  `Unknown --priority "${value}". Expected one of: ${ACTION_PRIORITIES.join(", ")}${allowClear ? ", or 'none' to clear" : ""}.`
8542
8619
  );
8543
8620
  }
8544
- function looksScheduled(name, description) {
8621
+ function looksScheduled(name, description2) {
8545
8622
  const haystack = fold(`${name}
8546
- ${description}`);
8623
+ ${description2}`);
8547
8624
  return SCHEDULE_SIGNALS.some((re2) => re2.test(haystack));
8548
8625
  }
8549
8626
  var CAMPAIGN_ENTITY = String.raw`(?:campaigns?|campanas?|ad\s*sets?|adsets?|ad\s*groups?|adgroups?|ads?|anuncios?|conjuntos?\s+de\s+anuncios|grupos?\s+de\s+anuncios)`;
@@ -8701,10 +8778,10 @@ var SURFACE_RULES = [
8701
8778
  var FAN_OUT_NUDGE_THRESHOLD = 2;
8702
8779
  var FAN_OUT_THRESHOLD = 3;
8703
8780
  var SCHEDULED_HINT = 'This reads as recurring/scheduled work. If it should run on a cadence or a future date, create a Scheduled agent instead \u2014 baker scheduled-agents create --cron "0 9 * * MON" (or --run-at, or --trigger for a reaction rather than a clock; guide: `__tooling__/docs/tools/baker/scheduled-agents.md`). Do not capture "set up a schedule" as a Work Action.';
8704
- function looksExecutable(name, description) {
8781
+ function looksExecutable(name, description2) {
8705
8782
  const foldedName = fold(name);
8706
8783
  const full = `${foldedName}
8707
- ${fold(description)}`;
8784
+ ${fold(description2)}`;
8708
8785
  for (const rule of SURFACE_RULES) {
8709
8786
  const haystack = rule.nameOnly ? foldedName : full;
8710
8787
  if (rule.except?.some((re2) => re2.test(full))) {
@@ -8724,7 +8801,7 @@ ${fold(description)}`;
8724
8801
  }
8725
8802
  function buildCreateHints({
8726
8803
  name,
8727
- description,
8804
+ description: description2,
8728
8805
  tempId,
8729
8806
  tags,
8730
8807
  prioritySet,
@@ -8732,13 +8809,13 @@ function buildCreateHints({
8732
8809
  }) {
8733
8810
  const hints2 = [];
8734
8811
  const expanded = shellExpandedIn(`${name}
8735
- ${description}`);
8812
+ ${description2}`);
8736
8813
  if (expanded) {
8737
8814
  hints2.push(
8738
8815
  `The text says \u201C${expanded}\u201D: the shell replaced a $ amount (like $0) inside double quotes before Baker saw it. Fix it now: baker actions update ${tempId} --description '<the text, with the amounts as you read them>' (and --name '\u2026' if the name has it) \u2014 single quotes keep $ as written.`
8739
8816
  );
8740
8817
  }
8741
- const advisory = advisoryHint(name, description);
8818
+ const advisory = advisoryHint(name, description2);
8742
8819
  if (advisory) {
8743
8820
  hints2.push(
8744
8821
  `${advisory.hint} If nothing actually blocks it: baker actions draft remove ${tempId}, then do the work.`
@@ -8754,7 +8831,7 @@ ${description}`);
8754
8831
  );
8755
8832
  }
8756
8833
  hints2.push(`Link dependencies: baker actions link --blocker <id> --blocked ${tempId}`);
8757
- if (!description) {
8834
+ if (!description2) {
8758
8835
  hints2.push("Add description: baker actions update <tempId> --description '...' (what/why/where/done-when)");
8759
8836
  }
8760
8837
  if (!tags || tags.length === 0) {
@@ -8772,11 +8849,11 @@ ${description}`);
8772
8849
  function shellExpandedIn(text2) {
8773
8850
  return /(?:^|[\s(])((?:\/usr)?\/bin\/(?:ba|z|da|k)?sh|-?(?:ba|z|da|k)sh)\b/.exec(text2)?.[1] ?? null;
8774
8851
  }
8775
- function advisoryHint(name, description) {
8776
- if (looksScheduled(name, description)) {
8852
+ function advisoryHint(name, description2) {
8853
+ if (looksScheduled(name, description2)) {
8777
8854
  return { kind: "scheduled", hint: SCHEDULED_HINT };
8778
8855
  }
8779
- const executable = looksExecutable(name, description);
8856
+ const executable = looksExecutable(name, description2);
8780
8857
  return executable ? { kind: "executable", ...executable } : null;
8781
8858
  }
8782
8859
  var ACTIONS_LIST_DEFAULT_LIMIT = 500;
@@ -8862,9 +8939,9 @@ function parseSkillFrontmatter(md) {
8862
8939
  const lines = extractFrontmatterLines(md);
8863
8940
  if (!lines) return null;
8864
8941
  const name = readField(lines, "name");
8865
- const description = readField(lines, "description");
8866
- if (!name || !description) return null;
8867
- const trimmed = description.length > DESCRIPTION_MAX ? `${description.slice(0, DESCRIPTION_MAX)}\u2026` : description;
8942
+ const description2 = readField(lines, "description");
8943
+ if (!name || !description2) return null;
8944
+ const trimmed = description2.length > DESCRIPTION_MAX ? `${description2.slice(0, DESCRIPTION_MAX)}\u2026` : description2;
8868
8945
  return { name, description: trimmed };
8869
8946
  }
8870
8947
  function findSkillsDir(startDir) {
@@ -14989,6 +15066,7 @@ var DESTINATION_NAMES = {
14989
15066
  googleSpreadsheet: "Google Sheets",
14990
15067
  hubspotForm: "HubSpot Form",
14991
15068
  goHighlevelContact: "GoHighLevel",
15069
+ shopifyCustomer: "Shopify",
14992
15070
  crmble: "Crmble"
14993
15071
  };
14994
15072
  function destinationKey(type) {
@@ -19550,9 +19628,9 @@ var assetGroupsCommand = defineCommand31({
19550
19628
  ])
19551
19629
  }
19552
19630
  });
19553
- function fileCreateCommand(name, kind, description, hints2) {
19631
+ function fileCreateCommand(name, kind, description2, hints2) {
19554
19632
  return defineCommand31({
19555
- meta: { name, description },
19633
+ meta: { name, description: description2 },
19556
19634
  args: { ...customerIdArg, ...fileArg },
19557
19635
  run: async ({ args: args5 }) => {
19558
19636
  const customerId = requireCustomerId(args5);
@@ -37870,8 +37948,8 @@ function castAvatarsOnElements(elementsInput, avatars) {
37870
37948
  if (!avatar) return el;
37871
37949
  next += 1;
37872
37950
  if (el.label) byLabel.set(el.label, avatar);
37873
- const description = avatar.subjectDescription?.trim();
37874
- return { ...el, ...description ? { description } : {}, source_image: avatar.sheetUrl, source_is_sheet: true };
37951
+ const description2 = avatar.subjectDescription?.trim();
37952
+ return { ...el, ...description2 ? { description: description2 } : {}, source_image: avatar.sheetUrl, source_is_sheet: true };
37875
37953
  });
37876
37954
  return cast.map((el) => {
37877
37955
  const avatar = el.same_as ? byLabel.get(el.same_as) : void 0;
@@ -38166,7 +38244,7 @@ function buildFramePrompt(edge, sceneIndex, framePrompt, present2, hasAnchor, mo
38166
38244
  "- ORIGINAL_FRAME \u2014 use ONLY for composition: framing, camera angle, shot size, subject placement, pose, and proportions. IGNORE its text, logo, brand name, colors, AND the identity of every person/animal/object in it \u2014 those come from the labeled reference images above, never from this frame. It is a DIFFERENT brand's footage with DIFFERENT actors, here ONLY to anchor where things sit and how the shot is framed (e.g. a profile/side angle stays a profile/side angle), never who they are or what palette to use."
38167
38245
  ] : []
38168
38246
  ].join("\n");
38169
- const description = framePrompt?.trim() || `the ${edge} frame of scene ${sceneIndex + 1} \u2014 describe the full composition, subjects, setting, action, lighting, and palette here. (Edit this line to change ONLY this frame.)`;
38247
+ const description2 = framePrompt?.trim() || `the ${edge} frame of scene ${sceneIndex + 1} \u2014 describe the full composition, subjects, setting, action, lighting, and palette here. (Edit this line to change ONLY this frame.)`;
38170
38248
  const isHookFrame = sceneIndex === 0 && edge === "start";
38171
38249
  const cleanPlateBlock = [
38172
38250
  "CRITICAL \u2014 RENDER A CLEAN PLATE WITH ZERO TEXT OR GRAPHIC OVERLAYS:",
@@ -38244,7 +38322,7 @@ function buildFramePrompt(edge, sceneIndex, framePrompt, present2, hasAnchor, mo
38244
38322
  ""
38245
38323
  ] : [],
38246
38324
  "FRAME DESCRIPTION (this frame's editable prompt):",
38247
- description,
38325
+ description2,
38248
38326
  "",
38249
38327
  "Render exactly what the FRAME DESCRIPTION and the SHARED AD SPEC specify \u2014 this is the authoritative ad: its cast identity (via the reference images), palette, brand, and intent are law. Keep every recurring element identical to its reference image across all frames. Again: clean plate only \u2014 no rendered text, and no icons/stickers/emojis/badges (those live on the overlay layer).",
38250
38328
  "",
@@ -39100,8 +39178,8 @@ function localizeNumeralsForNative(text2, langCode) {
39100
39178
  if (!langCode || !INTEGER_SPELLERS[langCode]) return text2;
39101
39179
  return text2.replace(/(?<![\w.,-])\d{1,9}(?![\w.,-])/g, (m) => spellNumber(langCode, Number.parseInt(m, 10)));
39102
39180
  }
39103
- function parseVoiceTraits(description) {
39104
- const d = description.toLowerCase();
39181
+ function parseVoiceTraits(description2) {
39182
+ const d = description2.toLowerCase();
39105
39183
  const out = {};
39106
39184
  if (/\b(female|woman|women|girl|lady)\b/.test(d)) out.gender = "female";
39107
39185
  else if (/\b(male|man|men|guy|boy)\b/.test(d)) out.gender = "male";
@@ -39312,11 +39390,11 @@ function makeVoiceFactory(blueprint, canonical2, nodes, voiceLanguage, pinnedVoi
39312
39390
  const existing = bySpeaker.get(speaker);
39313
39391
  if (existing) return existing;
39314
39392
  const id = sanitizeId(`voice_${speaker}`, `voice_${bySpeaker.size}`);
39315
- const description = describe(speaker);
39316
- const traits = parseVoiceTraits(description);
39393
+ const description2 = describe(speaker);
39394
+ const traits = parseVoiceTraits(description2);
39317
39395
  if (voiceLanguage) traits.language = voiceLanguage;
39318
39396
  nodes.push(
39319
- pinnedVoiceId ? { id, type: "voice_select", params: { description, voice_id: pinnedVoiceId, ...traits } } : { id, type: "voice_select", params: { description, ...traits } }
39397
+ pinnedVoiceId ? { id, type: "voice_select", params: { description: description2, voice_id: pinnedVoiceId, ...traits } } : { id, type: "voice_select", params: { description: description2, ...traits } }
39320
39398
  );
39321
39399
  bySpeaker.set(speaker, id);
39322
39400
  return id;
@@ -42608,8 +42686,8 @@ function castAvatarsOnStaticElements(elementsInput, avatars) {
42608
42686
  const avatar = isPerson(el) ? avatars[next] : void 0;
42609
42687
  if (!avatar) return el;
42610
42688
  next += 1;
42611
- const description = avatar.subjectDescription?.trim();
42612
- return { ...el, ...description ? { description } : {}, source_image: avatar.sheetUrl };
42689
+ const description2 = avatar.subjectDescription?.trim();
42690
+ return { ...el, ...description2 ? { description: description2 } : {}, source_image: avatar.sheetUrl };
42613
42691
  });
42614
42692
  }
42615
42693
  function staticAvatarsNotCast(elementsInput, handles) {
@@ -43915,8 +43993,8 @@ function adSpecCastElements(spec, avatar) {
43915
43993
  ...spec.set?.image?.trim() ? { source_image: spec.set.image.trim() } : {}
43916
43994
  });
43917
43995
  }
43918
- const description = (avatar?.subjectDescription ?? spec.cast?.description)?.trim();
43919
- if (!description) return elements;
43996
+ const description2 = (avatar?.subjectDescription ?? spec.cast?.description)?.trim();
43997
+ if (!description2) return elements;
43920
43998
  const scenes = spec.beats.map((beat, i) => ({ beat, i })).filter(({ beat, i }) => beat.cast !== false && !(endCardWanted(spec) && i === lastIndex)).map(({ i }) => i);
43921
43999
  if (scenes.length === 0) return elements;
43922
44000
  const sheet = avatar?.sheetUrl?.trim();
@@ -43926,7 +44004,7 @@ function adSpecCastElements(spec, avatar) {
43926
44004
  // Same id the dialogue names as speaker: that pairing is what lets the engine see
43927
44005
  // the presenter as present in a scene and route it to native audio.
43928
44006
  cast_id: AD_CAST_ID,
43929
- description,
44007
+ description: description2,
43930
44008
  scenes,
43931
44009
  // An avatar's identity sheet IS the turnaround the engine would otherwise render,
43932
44010
  // so it is handed over as one. Left unmarked, the run paid to generate a sheet OF
@@ -48922,6 +49000,9 @@ var ADS_MATCHING = [
48922
49000
  "country"
48923
49001
  ];
48924
49002
  var MAPPING_SLOTS = {
49003
+ shopifyCustomer: [
49004
+ { kind: "record", key: "customer", allowed: ["email", "phone", "acceptsEmailMarketing", "acceptsSmsMarketing"] }
49005
+ ],
48925
49006
  googleSpreadsheet: [{ kind: "list", key: "columnMapping", addressedBy: "column" }],
48926
49007
  hubspotForm: [
48927
49008
  {
@@ -64096,13 +64177,13 @@ var createCommand3 = defineCommand197({
64096
64177
  try {
64097
64178
  refuseAutoPublish(args5);
64098
64179
  const name = args5.name;
64099
- const description = args5.description;
64180
+ const description2 = args5.description;
64100
64181
  const prompt = args5.prompt;
64101
64182
  const template = typeof args5.template === "string" ? args5.template.trim() : "";
64102
64183
  if (template.length === 0 && (!name || name.trim().length === 0)) {
64103
64184
  failValidation4("--name is required. Or pass --template to use a recipe's name.");
64104
64185
  }
64105
- if (template.length === 0 && (!description || description.trim().length === 0)) {
64186
+ if (template.length === 0 && (!description2 || description2.trim().length === 0)) {
64106
64187
  failValidation4("--description is required. Or pass --template to use a recipe's line.");
64107
64188
  }
64108
64189
  if (template.length === 0 && (!prompt || prompt.trim().length === 0)) {
@@ -64113,7 +64194,7 @@ var createCommand3 = defineCommand197({
64113
64194
  const body = {
64114
64195
  chatId: requireChatId(),
64115
64196
  ...name?.trim() ? { name } : {},
64116
- ...description?.trim() ? { description } : {},
64197
+ ...description2?.trim() ? { description: description2 } : {},
64117
64198
  enabled: !args5.disabled,
64118
64199
  ...trigger,
64119
64200
  ...prompt ? { prompt } : {},
@@ -64793,7 +64874,7 @@ var schemaCommand2 = defineCommand208({
64793
64874
  });
64794
64875
 
64795
64876
  // src/commands/shopify/index.ts
64796
- import { defineCommand as defineCommand215 } from "citty";
64877
+ import { defineCommand as defineCommand216 } from "citty";
64797
64878
 
64798
64879
  // src/commands/shopify/buyers.ts
64799
64880
  import { defineCommand as defineCommand209 } from "citty";
@@ -65129,7 +65210,7 @@ var shopifyVariantSchema = z50.object({
65129
65210
  compareAtPrice: shopifyPriceSchema.optional(),
65130
65211
  stock: shopifyOptionStockSchema,
65131
65212
  quantity: z50.number().optional(),
65132
- /** The link for the page's buy button: never build a Shopify link by hand. Absent when the option can't be bought. */
65213
+ /** The option's buy link for ads, emails and messages (a landing places `<ShopifyProduct>` instead): never build a Shopify link by hand. Absent when the option can't be bought. */
65133
65214
  buyUrl: z50.string().optional(),
65134
65215
  buyUrlKind: shopifyBuyDestinationSchema.optional(),
65135
65216
  /** Why there is no buy link. */
@@ -65244,7 +65325,7 @@ var shopifyProductDetailSchema = z50.object({
65244
65325
  handle: z50.string(),
65245
65326
  /** The product's page in Baker — the link to share with the user. */
65246
65327
  url: z50.string(),
65247
- /** The product's page on the online store: to look at, never a buy button's link (that is each option's `buyUrl`). */
65328
+ /** The product's page on the online store: to look at, never a buy button's link (that is each option's `buyUrl`, or `<ShopifyProduct>` on a landing). */
65248
65329
  storeUrl: z50.string().optional(),
65249
65330
  availability: shopifyAvailabilitySchema.optional(),
65250
65331
  status: shopifyProductStatusSchema.optional(),
@@ -65820,7 +65901,8 @@ var shopifyWriteEntitySchema = z52.enum([
65820
65901
  "discountCode",
65821
65902
  "urlRedirect",
65822
65903
  "marketingActivity",
65823
- "webPixel"
65904
+ "webPixel",
65905
+ "signupOffer"
65824
65906
  ]);
65825
65907
  var shopifyDraftOpKindSchema = z52.enum([
65826
65908
  "shopify.product.update",
@@ -65841,7 +65923,9 @@ var shopifyDraftOpKindSchema = z52.enum([
65841
65923
  "shopify.urlRedirect.delete",
65842
65924
  "shopify.marketingActivity.upsert",
65843
65925
  "shopify.marketingActivity.delete",
65844
- "shopify.webPixel.update"
65926
+ "shopify.webPixel.update",
65927
+ "shopify.signupOffer.set",
65928
+ "shopify.signupOffer.end"
65845
65929
  ]);
65846
65930
  var shopifyDecimalSchema = z52.string().regex(/^\d{1,12}(\.\d{1,4})?$/, 'write amounts as a decimal string, e.g. "19.95"');
65847
65931
  var tagSchema = z52.string().trim().min(1).max(255);
@@ -65963,6 +66047,27 @@ var marketingUpsertSchema = z52.object({
65963
66047
  medium: z52.string().trim().min(1).max(255)
65964
66048
  }).strict()
65965
66049
  }).strict();
66050
+ var signupOfferHandleSchema = z52.string().regex(/^[a-z0-9][a-z0-9-]{1,39}$/, "2\u201340 lowercase letters, digits and dashes, e.g. welcome-10");
66051
+ var signupOfferSetSchema = z52.object({
66052
+ /** What the store's staff see in front of each code, and the Store page's row. */
66053
+ name: z52.string().trim().min(1).max(80),
66054
+ value: discountValueSchema,
66055
+ appliesTo: discountAppliesToSchema,
66056
+ minimumSubtotal: shopifyDecimalSchema.optional(),
66057
+ /** Each code's life, from the moment it is given. */
66058
+ validForDays: z52.number().int().min(1).max(365),
66059
+ /** The offer stops giving codes after this; no code outlives it. */
66060
+ endsAt: isoDateTimeSchema.optional(),
66061
+ /** How many codes the offer gives in all. */
66062
+ maxCodes: z52.number().int().min(1).max(1e5).optional(),
66063
+ /** The start of every code (WELCOME-7Q2M9XKD); default: the handle in capitals. */
66064
+ codePrefix: z52.string().regex(/^[A-Z0-9]{2,12}$/, "2\u201312 capital letters or digits, e.g. WELCOME").optional(),
66065
+ combinesWith: z52.object({
66066
+ productDiscounts: z52.boolean().optional(),
66067
+ orderDiscounts: z52.boolean().optional(),
66068
+ shippingDiscounts: z52.boolean().optional()
66069
+ }).strict().optional()
66070
+ }).strict();
65966
66071
  var emptySchema = z52.object({}).strict();
65967
66072
  var opFields = {
65968
66073
  /** Optional caller-chosen ref for a create, so later ops in the batch can name it (`shopify_temp_…`). */
@@ -66008,7 +66113,19 @@ var shopifyDraftOpInputSchema = z52.discriminatedUnion("kind", [
66008
66113
  payload: emptySchema.optional(),
66009
66114
  ...opFields
66010
66115
  }),
66011
- z52.object({ kind: z52.literal("shopify.webPixel.update"), payload: emptySchema.optional(), ...opFields })
66116
+ z52.object({ kind: z52.literal("shopify.webPixel.update"), payload: emptySchema.optional(), ...opFields }),
66117
+ z52.object({
66118
+ kind: z52.literal("shopify.signupOffer.set"),
66119
+ target: signupOfferHandleSchema,
66120
+ payload: signupOfferSetSchema,
66121
+ ...opFields
66122
+ }),
66123
+ z52.object({
66124
+ kind: z52.literal("shopify.signupOffer.end"),
66125
+ target: signupOfferHandleSchema,
66126
+ payload: emptySchema.optional(),
66127
+ ...opFields
66128
+ })
66012
66129
  ]);
66013
66130
  var PAYLOAD_KEYS = new Map(
66014
66131
  shopifyDraftOpInputSchema.options.map((option) => {
@@ -66226,7 +66343,7 @@ var fullArg = {
66226
66343
 
66227
66344
  // src/commands/shopify/hints.ts
66228
66345
  var RECONNECT = 'Baker has lost access to this Shopify store, so these products may be out of date. Say so, and ask the user to reconnect with `request_connection` { platform: "shopify" } \u2014 the card offers Reconnect.';
66229
- var OWN_STOREFRONT = "This store sells through its own storefront, not a Shopify online store: Baker has no store page links for its products \u2014 link to the product pages on the client's own site, and ask the user for them if you don't know them.";
66346
+ var OWN_STOREFRONT = "This store sells through its own storefront, not a Shopify online store: Baker links to its product pages once the website's address is set in Brain \u2192 Store (`buyUrlKind` says where each link goes); until then, link to the product pages on the client's own site, and ask the user for them if you don't know them.";
66230
66347
  function day(iso) {
66231
66348
  return iso ? iso.slice(0, 10) : null;
66232
66349
  }
@@ -66334,17 +66451,17 @@ function stockHintsFor(product) {
66334
66451
  }
66335
66452
  var BUY_NOTES = {
66336
66453
  password_protected: "The store asks for a password before anyone can see it: a shopper following a buy link meets it first \u2014 tell the user before sending traffic.",
66337
- subscription_optional: "It can also be bought by subscription, but a buy link can only add it once \u2014 say so if the page promotes the subscription.",
66338
- own_storefront: "The store sells through its own storefront: buy links go straight to Shopify's checkout.",
66454
+ subscription_optional: "It can also be bought by subscription: on a landing `<ShopifyProduct>` offers the plans; a `buyUrl` elsewhere adds it once.",
66455
+ own_storefront: "The store sells through its own website: `buyUrlKind` says where each link goes \u2014 its product page, or its cart page once its developers confirm it in Brain \u2192 Store.",
66339
66456
  details_pending: "Baker hasn't read this product's details yet (subscriptions among them): its buy links may change.",
66340
- subscription_only: "It's sold only by subscription, which a buy link can't carry: its buy link is the product page, where the shopper picks the plan."
66457
+ subscription_only: "It's sold only by subscription: on a landing `<ShopifyProduct>` offers the plans; a `buyUrl` elsewhere is its product page, where the shopper picks one."
66341
66458
  };
66342
66459
  function buyHints(product) {
66343
66460
  const hints2 = [];
66344
66461
  if (product.variants.some((variant) => variant.buyUrl)) {
66345
66462
  hints2.push(
66346
- `Use each option's \`buyUrl\` for the page's buy button \u2014 never build a Shopify link yourself (it follows the store's setting: ${product.buyDestination.replace("_", " ")}; \`--quantity <n>\` sets how many).`,
66347
- "Keep that `buyUrl` on the button: never `storeUrl` (a page to look at, not a buy link), and never drop or replace the buy button to get a page past its checks. The options to sell are `variants[]` alone \u2014 one buy button per option, each with its own `buyUrl`, never a size or colour picker made up from a size chart or other details. When the shopper must choose on the store, link the option's `productPage` (from `--full`)."
66463
+ `On a landing, place \`<ShopifyProduct product="${product.handle}" />\` (and \`<ShopifyBuyButton product="${product.handle}">\` for a second button): it shows the options, price and stock, and its button hands the shopper to the store. Never put a \`buyUrl\` on a landing yourself, and never build an option picker. See /landing \`.claude/skills/landing/references/verticals/ecommerce.md\`.`,
66464
+ `\`buyUrl\` is for links outside a landing \u2014 an ad, an email, a message: one per option; never \`storeUrl\` (a page to look at), never a Shopify link built yourself (it follows the store's setting: ${product.buyDestination.replace("_", " ")}; \`--quantity <n>\` sets how many).`
66348
66465
  );
66349
66466
  }
66350
66467
  const notes = new Set(product.variants.flatMap((variant) => variant.notes ?? []));
@@ -66811,9 +66928,24 @@ var WARNINGS = "Read the warnings above before telling the user this is ready.";
66811
66928
  var DISCOUNT = "The code works only after the chat is published; don't announce it as live or share it as working yet.";
66812
66929
  var EMPTY_COLLECTION = "The new collection has no products yet: add them with `baker shopify change collection add <collection> <product\u2026>`.";
66813
66930
  var COLLECTION_DELETE = "Its address stops working when it is deleted: keep old links alive with `baker shopify change redirect create --from /collections/<handle> --to <page>` (ask the user where they should land).";
66931
+ function repointedRedirects(ops) {
66932
+ const removed = ops.flatMap(
66933
+ (op) => op.kind === "shopify.urlRedirect.delete" && op.ref.startsWith("urlRedirect:") ? [op.ref.slice("urlRedirect:".length)] : []
66934
+ );
66935
+ return removed.flatMap((path49) => {
66936
+ const made = ops.find(
66937
+ (op) => op.kind === "shopify.urlRedirect.create" && op.summary.startsWith(`Redirect ${path49} to `)
66938
+ );
66939
+ if (!made) return [];
66940
+ const to = made.summary.slice(`Redirect ${path49} to `.length);
66941
+ return [
66942
+ `${path49} still redirects after publish, now to ${to}: tell the user the old link goes there, not that its redirect is removed.`
66943
+ ];
66944
+ });
66945
+ }
66814
66946
  function stageHints(data) {
66815
66947
  if (!data.staged) return [];
66816
- const hints2 = [STAGED];
66948
+ const hints2 = [STAGED, ...repointedRedirects(data.ops)];
66817
66949
  if (data.ops.some((op) => op.warnings.length > 0)) hints2.push(WARNINGS);
66818
66950
  if (data.ops.some((op) => op.kind === "shopify.discountCode.create")) hints2.push(DISCOUNT);
66819
66951
  if (data.ops.some((op) => op.kind === "shopify.collection.delete")) hints2.push(COLLECTION_DELETE);
@@ -67154,18 +67286,18 @@ var collectionUpdate = leaf(
67154
67286
  () => target3(args5, "collection")
67155
67287
  )
67156
67288
  );
67157
- function collectionVerb(verb, kind, description) {
67289
+ function collectionVerb(verb, kind, description2) {
67158
67290
  return leaf(
67159
67291
  `shopify.change.collection.${verb}`,
67160
- { name: verb, description },
67292
+ { name: verb, description: description2 },
67161
67293
  { collection: TARGET("Collection id, handle, link or a shopify_temp_ ref staged in this chat") },
67162
67294
  async (args5) => await stage([{ kind, target: target3(args5, "collection") }])
67163
67295
  );
67164
67296
  }
67165
- function membership(verb, kind, description) {
67297
+ function membership(verb, kind, description2) {
67166
67298
  return leaf(
67167
67299
  `shopify.change.collection.${verb}`,
67168
- { name: verb, description },
67300
+ { name: verb, description: description2 },
67169
67301
  {
67170
67302
  collection: TARGET("Collection id, handle, link or a shopify_temp_ ref"),
67171
67303
  products: TARGET("Products (ids, handles or links), space separated")
@@ -67215,6 +67347,75 @@ function discountPayload(args5) {
67215
67347
  combinesWith: combinesOf(args5.combines)
67216
67348
  });
67217
67349
  }
67350
+ function signupOfferFlagsProblem(args5) {
67351
+ if (typeof args5.name !== "string" || args5.name.trim() === "")
67352
+ return 'Add --name "<what shoppers and store staff see, e.g. 10% off>": every sign-up offer needs a name.';
67353
+ if (args5["valid-days"] === void 0)
67354
+ return "Add --valid-days <1-365>: how many days each code lasts after it is given.";
67355
+ return void 0;
67356
+ }
67357
+ function signupOfferPayload(args5) {
67358
+ return defined({
67359
+ name: args5.name,
67360
+ value: discountValue(args5),
67361
+ appliesTo: appliesToOf(args5),
67362
+ minimumSubtotal: args5.minimum,
67363
+ validForDays: numberOf3(args5["valid-days"]),
67364
+ endsAt: args5.ends,
67365
+ maxCodes: numberOf3(args5["max-codes"]),
67366
+ codePrefix: args5.prefix,
67367
+ combinesWith: combinesOf(args5.combines)
67368
+ });
67369
+ }
67370
+ var signupOfferSet = leaf(
67371
+ "shopify.change.signupOffer.set",
67372
+ {
67373
+ name: "set",
67374
+ description: 'Stage a sign-up offer. Changes money: ask the exact approval question returned by this command. Example: baker shopify change signup-offer set welcome-10 --name "Welcome" --percent 10 --valid-days 14 --max-codes 1000'
67375
+ },
67376
+ {
67377
+ handle: TARGET("Offer handle, e.g. welcome-10"),
67378
+ name: {
67379
+ type: "string",
67380
+ description: 'Offer name shoppers and store staff see (required), e.g. "10% off"',
67381
+ required: false
67382
+ },
67383
+ percent: { type: "string", description: "Percentage off (choose percent or amount)", required: false },
67384
+ amount: { type: "string", description: "Amount off in the store currency", required: false },
67385
+ products: { type: "string", description: "Product handles or ids, comma separated", required: false },
67386
+ collections: { type: "string", description: "Collection handles or ids, comma separated", required: false },
67387
+ minimum: { type: "string", description: "Minimum subtotal", required: false },
67388
+ "valid-days": { type: "string", description: "Each code lasts 1\u2013365 days (required)", required: false },
67389
+ ends: { type: "string", description: "Offer end as ISO date and time; no code outlives it", required: false },
67390
+ "max-codes": { type: "string", description: "Total codes the offer may give, 1\u2013100000", required: false },
67391
+ prefix: { type: "string", description: "Code prefix: 2\u201312 uppercase letters or digits", required: false },
67392
+ combines: {
67393
+ type: "string",
67394
+ description: "Comma-separated product,order,shipping discounts to combine",
67395
+ required: false
67396
+ },
67397
+ ...JSON_ARGS
67398
+ },
67399
+ async (args5) => await stageFor(
67400
+ "shopify.signupOffer.set",
67401
+ args5,
67402
+ () => {
67403
+ const problem = signupOfferFlagsProblem(args5);
67404
+ if (problem) failValidation3(problem);
67405
+ return signupOfferPayload(args5);
67406
+ },
67407
+ () => target3(args5, "offer handle")
67408
+ )
67409
+ );
67410
+ var signupOfferEnd = leaf(
67411
+ "shopify.change.signupOffer.end",
67412
+ {
67413
+ name: "end",
67414
+ description: "End a sign-up offer at Publish. Existing codes keep their terms. Example: baker shopify change signup-offer end welcome-10"
67415
+ },
67416
+ { handle: TARGET("Offer handle") },
67417
+ async (args5) => await stage([{ kind: "shopify.signupOffer.end", target: target3(args5, "offer handle") }])
67418
+ );
67218
67419
  var discountCreate = leaf(
67219
67420
  "shopify.change.discount.create",
67220
67421
  {
@@ -67340,7 +67541,7 @@ var measurementReconnect = leaf(
67340
67541
  {},
67341
67542
  async () => await stage([{ kind: "shopify.webPixel.update" }])
67342
67543
  );
67343
- var group = (name, description, subCommands) => defineCommand211({ meta: { name, description }, subCommands });
67544
+ var group = (name, description2, subCommands) => defineCommand211({ meta: { name, description: description2 }, subCommands });
67344
67545
  var changeCommand = defineCommand211({
67345
67546
  meta: {
67346
67547
  name: "change",
@@ -67357,6 +67558,7 @@ change can be put back with \`baker shopify undo\`. Prices and discounts need th
67357
67558
  baker shopify change measurement reconnect`
67358
67559
  },
67359
67560
  subCommands: {
67561
+ "signup-offer": group("signup-offer", "Stage or end a sign-up offer", { set: signupOfferSet, end: signupOfferEnd }),
67360
67562
  product: group("product", "Change a product's title, description, SEO, tags or type", { update: productUpdate }),
67361
67563
  variant: group("variant", "Change an option's SKU, barcode, weight or price", { update: variantUpdate }),
67362
67564
  field: group("field", "Set or remove a custom field", { set: fieldSet, remove: fieldRemove }),
@@ -67567,12 +67769,46 @@ import { defineCommand as defineCommand213 } from "citty";
67567
67769
  // src/commands/shopify/landingLinks.ts
67568
67770
  import { readdir as readdir16, readFile as readFile33, stat as stat11 } from "fs/promises";
67569
67771
  import path47 from "path";
67772
+ import { parse } from "@astrojs/compiler/sync";
67570
67773
  var PAGES_MAX = 300;
67571
67774
  var FILE_BYTES_MAX = 256 * 1024;
67572
67775
  function escaped(text2) {
67573
67776
  return text2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
67574
67777
  }
67575
- function evidenceIn(text2, keys) {
67778
+ var BUY_COMPONENTS = /* @__PURE__ */ new Set(["ShopifyProduct", "ShopifyBuyButton"]);
67779
+ var STRING_LITERAL = /^\s*(["'`])([^"'`]*)\1\s*$/;
67780
+ var ITEM_PRODUCT = /\bproduct\s*:\s*(["'`])([^"'`]*)\1/g;
67781
+ function namedBy(attribute) {
67782
+ if (attribute.name === "product") {
67783
+ if (attribute.kind === "quoted" || attribute.kind === "template-literal") return [attribute.value];
67784
+ if (attribute.kind === "expression") return [STRING_LITERAL.exec(attribute.value)?.[2] ?? ""];
67785
+ }
67786
+ if (attribute.name === "items" && attribute.kind === "expression") {
67787
+ return [...attribute.value.matchAll(ITEM_PRODUCT)].map((match) => match[2] ?? "");
67788
+ }
67789
+ return [];
67790
+ }
67791
+ function componentProducts(node, into) {
67792
+ if (node.type === "component" && BUY_COMPONENTS.has(node.name)) {
67793
+ for (const attribute of node.attributes) into.push(...namedBy(attribute));
67794
+ }
67795
+ if ("children" in node) for (const child of node.children) componentProducts(child, into);
67796
+ return into;
67797
+ }
67798
+ function buyComponentProducts(file) {
67799
+ if (!file.path.endsWith(".astro")) return [];
67800
+ try {
67801
+ return componentProducts(parse(file.text, void 0).ast, []);
67802
+ } catch {
67803
+ return [];
67804
+ }
67805
+ }
67806
+ function namesProduct(file, keys) {
67807
+ const wanted = new Set([keys.handle.toLowerCase(), ...keys.productId ? [keys.productId] : []].filter(Boolean));
67808
+ return buyComponentProducts(file).some((named) => wanted.has(named.trim().toLowerCase()));
67809
+ }
67810
+ function evidenceIn(file, keys) {
67811
+ const { text: text2 } = file;
67576
67812
  const evidence = [];
67577
67813
  if (keys.buyUrls.some((url) => url && text2.includes(url))) evidence.push("buy_link");
67578
67814
  const page = new RegExp(`/products/${escaped(keys.handle)}(?![A-Za-z0-9_-])`, "i");
@@ -67581,19 +67817,20 @@ function evidenceIn(text2, keys) {
67581
67817
  (id) => new RegExp(`(?:/cart/(?:\\d+:\\d+,)*${id}:|variant=${id}(?!\\d))`).test(text2)
67582
67818
  );
67583
67819
  if (option) evidence.push("variant_id");
67820
+ if (namesProduct(file, keys)) evidence.push("buy_button");
67584
67821
  return evidence;
67585
67822
  }
67586
67823
  function landingLinksOf(pages, keys) {
67587
67824
  const bySlug = /* @__PURE__ */ new Map();
67588
67825
  for (const page of pages) {
67589
- const found = new Set(page.files.flatMap((file) => evidenceIn(file.text, keys)));
67826
+ const found = new Set(page.files.flatMap((file) => evidenceIn(file, keys)));
67590
67827
  if (found.size === 0) continue;
67591
67828
  const slug = page.slug.replace(/--\d+$/, "");
67592
67829
  const held = bySlug.get(slug) ?? /* @__PURE__ */ new Set();
67593
67830
  for (const evidence of found) held.add(evidence);
67594
67831
  bySlug.set(slug, held);
67595
67832
  }
67596
- const order = ["buy_link", "product_page", "variant_id"];
67833
+ const order = ["buy_link", "product_page", "variant_id", "buy_button"];
67597
67834
  return [...bySlug].map(([slug, evidence]) => ({ slug, evidence: order.filter((entry) => evidence.has(entry)) }));
67598
67835
  }
67599
67836
  async function isDir4(target4) {
@@ -67848,6 +68085,7 @@ var promotionsCommand = defineCommand213({
67848
68085
  const scan = await readWorkspacePages(process.cwd());
67849
68086
  const linked = scan ? landingLinksOf(scan.pages, {
67850
68087
  handle: product.handle,
68088
+ productId: product.productId,
67851
68089
  variantIds: product.variants.map((variant) => variant.variantId),
67852
68090
  buyUrls: product.variants.flatMap((variant) => variant.buyUrl ? [variant.buyUrl] : [])
67853
68091
  }) : void 0;
@@ -67893,8 +68131,96 @@ var stockHoldsCommand = defineCommand213({
67893
68131
  }
67894
68132
  });
67895
68133
 
67896
- // src/commands/shopify/tracking.ts
68134
+ // ../api/src/shopify/signups.ts
68135
+ import { z as z53 } from "zod";
68136
+ var SIGNUP_CODE_PATTERN = /^[A-Z0-9]{2,12}-[0-9A-HJKMNP-TV-Z]{8}$/;
68137
+ var SIGNUP_STATES = [
68138
+ "waiting",
68139
+ "ready",
68140
+ "used",
68141
+ "expired",
68142
+ "already",
68143
+ "ended",
68144
+ "none",
68145
+ "unavailable"
68146
+ ];
68147
+ var CODE_STATES = /* @__PURE__ */ new Set(["ready", "used", "expired"]);
68148
+ var shopifySignupReplySchema = z53.object({
68149
+ state: z53.enum(SIGNUP_STATES),
68150
+ code: z53.string().regex(SIGNUP_CODE_PATTERN).optional(),
68151
+ endsAt: z53.string().min(1).max(40).optional()
68152
+ }).strict().refine(
68153
+ (reply) => CODE_STATES.has(reply.state) ? reply.code !== void 0 && reply.endsAt !== void 0 : true,
68154
+ "a code state carries the code and when it ends"
68155
+ ).refine(
68156
+ (reply) => CODE_STATES.has(reply.state) ? true : reply.code === void 0 && reply.endsAt === void 0,
68157
+ "only the browser the code was given to is told it"
68158
+ );
68159
+ var shopifySignupResponseSchema = z53.union([
68160
+ z53.object({ ok: z53.literal(true), signup: shopifySignupReplySchema }).passthrough(),
68161
+ z53.object({ error: z53.string(), signup: shopifySignupReplySchema }).passthrough()
68162
+ ]);
68163
+ var signupOfferStatusSchema = z53.enum(["active", "ended", "staged"]);
68164
+ var shopifySignupOfferViewSchema = z53.object({
68165
+ handle: z53.string(),
68166
+ name: z53.string(),
68167
+ /** "10% off everything, each code valid 14 days, at most 1,000 codes". */
68168
+ summary: z53.string(),
68169
+ status: signupOfferStatusSchema,
68170
+ validForDays: z53.number(),
68171
+ endsAt: z53.string().optional(),
68172
+ maxCodes: z53.number().optional(),
68173
+ codesGiven: z53.number()
68174
+ });
68175
+ var shopifySignupOffersResponseSchema = z53.object({
68176
+ /** The company has a store connection Baker can use (connected, not waiting on a reconnect). */
68177
+ connected: z53.boolean(),
68178
+ /** The store allows Baker to add customers (sign-ups work). */
68179
+ canAddCustomers: z53.boolean(),
68180
+ /** The store allows Baker to make discount codes (offers give codes). */
68181
+ canGiveCodes: z53.boolean(),
68182
+ offers: z53.array(shopifySignupOfferViewSchema),
68183
+ /** Sign-ups the store didn't take in the last 7 days: for a missing permission, and for anything else. */
68184
+ refusedLast7Days: z53.object({ permission: z53.number(), other: z53.number() })
68185
+ });
68186
+
68187
+ // src/commands/shopify/signupOffers.ts
67897
68188
  import { defineCommand as defineCommand214 } from "citty";
68189
+ var description = "Start here for landing sign-ups: published and this chat's staged offers, codes given and store permissions. Example: baker shopify signup-offers. Forms name an offer handle; terms are staged with baker shopify change signup-offer set.";
68190
+ registerSchema({ command: "shopify.signupOffers", description, args: {} });
68191
+ var signupOffersCommand = defineCommand214({
68192
+ meta: { name: "signup-offers", description },
68193
+ async run() {
68194
+ try {
68195
+ const data = shopifySignupOffersResponseSchema.parse(
68196
+ await apiGet("/api/shopify/signup-offers", params({ chatId: optionalChatId() }))
68197
+ );
68198
+ const hints2 = [];
68199
+ if (!data.canAddCustomers)
68200
+ hints2.push(
68201
+ 'Sign-ups cannot be added to the store yet: call request_connection with { platform: "shopify", reason: "Add sign-ups from your landing" }. Keep building the page; the Form works once allowed.'
68202
+ );
68203
+ if (!data.canGiveCodes)
68204
+ hints2.push(
68205
+ "Codes need the store to allow discounts: use request_connection for Shopify and keep building the page."
68206
+ );
68207
+ if (data.offers.some((offer) => offer.status === "ended"))
68208
+ hints2.push(
68209
+ "Forms naming ended offers still add customers but give no code: stage a new offer or remove offer from the Form."
68210
+ );
68211
+ if (data.offers.length === 0)
68212
+ hints2.push(
68213
+ "No sign-up offer exists yet. Omit offer for sign-ups without a code, or stage one after the user approves its terms."
68214
+ );
68215
+ writeJson({ ok: true, hints: hints2, data });
68216
+ } catch (error) {
68217
+ fail9(error);
68218
+ }
68219
+ }
68220
+ });
68221
+
68222
+ // src/commands/shopify/tracking.ts
68223
+ import { defineCommand as defineCommand215 } from "citty";
67898
68224
  var audit = leaf(
67899
68225
  "shopify.tracking.audit",
67900
68226
  {
@@ -67911,7 +68237,7 @@ var audit = leaf(
67911
68237
  }
67912
68238
  }
67913
68239
  );
67914
- var trackingCommand2 = defineCommand214({
68240
+ var trackingCommand2 = defineCommand215({
67915
68241
  meta: { name: "tracking", description: "Check how the store is measured: baker shopify tracking audit" },
67916
68242
  subCommands: { audit }
67917
68243
  });
@@ -67954,7 +68280,7 @@ registerSchema({
67954
68280
  });
67955
68281
  registerSchema({
67956
68282
  command: "shopify.product",
67957
- description: "One product of the connected store \u2014 accepts its id, its handle, a link to it on the store, in Baker or in Shopify admin. Returns its options with price, stock and `buyUrl` (the link for a page's buy button, one per option \u2014 never build a Shopify link yourself, never use `storeUrl` for it), prices by country, the options out of stock, its collections, its photos' library imageIds to place on a landing (`baker images download <imageIds> --out src/pages/<page>/_images/`) or pass to a creative, its videos' playbackIds (`<Video playbackId=\u2026/>`), and `contentSummary`: what else Baker keeps about it. Start here for a landing from a product: add `--content` for the formatted description, every custom field by its name (a size chart, care instructions), the rating, subscriptions and translations; `--field <name>` returns one field whole. `--out-of-stock` lists only the options out of stock.",
68283
+ description: "One product of the connected store \u2014 accepts its id, its handle, a link to it on the store, in Baker or in Shopify admin. Returns its options with price, stock and `buyUrl` (each option's buy link for ads, emails and messages; on a landing use `<ShopifyProduct>` instead \u2014 never build a Shopify link yourself, never use `storeUrl` for it), prices by country, the options out of stock, its collections, its photos' library imageIds to place on a landing (`baker images download <imageIds> --out src/pages/<page>/_images/`) or pass to a creative, its videos' playbackIds (`<Video playbackId=\u2026/>`), and `contentSummary`: what else Baker keeps about it. Start here for a landing from a product: add `--content` for the formatted description, every custom field by its name (a size chart, care instructions), the rating, subscriptions and translations; `--field <name>` returns one field whole. `--out-of-stock` lists only the options out of stock.",
67958
68284
  args: {
67959
68285
  ref: { type: "positional", description: "Product id, handle, or a link to the product", required: true },
67960
68286
  "variants-cursor": {
@@ -68044,7 +68370,7 @@ registerSchema({
68044
68370
  }
68045
68371
  }
68046
68372
  });
68047
- var salesCommand = defineCommand215({
68373
+ var salesCommand = defineCommand216({
68048
68374
  meta: {
68049
68375
  name: "sales",
68050
68376
  description: "What the store sold: orders, sales, refunds and net, by day, product, campaign or landing \u2014 or when buyers order (weekday, hour, month). Example: baker shopify sales --days 7 --by campaign"
@@ -68082,7 +68408,7 @@ var salesCommand = defineCommand215({
68082
68408
  }
68083
68409
  }
68084
68410
  });
68085
- var storeCommand = defineCommand215({
68411
+ var storeCommand = defineCommand216({
68086
68412
  meta: {
68087
68413
  name: "store",
68088
68414
  description: "The connected Shopify store: currency, countries, how many products, what is out of stock. Example: baker shopify store"
@@ -68100,7 +68426,7 @@ var storeCommand = defineCommand215({
68100
68426
  }
68101
68427
  }
68102
68428
  });
68103
- var productsCommand = defineCommand215({
68429
+ var productsCommand = defineCommand216({
68104
68430
  meta: {
68105
68431
  name: "products",
68106
68432
  description: 'What the store sells, with price and stock. Example: baker shopify products --search "snowboard" --stock out_of_stock'
@@ -68146,7 +68472,7 @@ var productsCommand = defineCommand215({
68146
68472
  }
68147
68473
  }
68148
68474
  });
68149
- var productCommand = defineCommand215({
68475
+ var productCommand = defineCommand216({
68150
68476
  meta: {
68151
68477
  name: "product",
68152
68478
  description: "One product: options with buy links, prices by country, stock, photo imageIds, video playbackIds. Accepts an id, handle or link. Example: baker shopify product the-complete-snowboard --content"
@@ -68209,7 +68535,7 @@ var productCommand = defineCommand215({
68209
68535
  }
68210
68536
  }
68211
68537
  });
68212
- var collectionsCommand = defineCommand215({
68538
+ var collectionsCommand = defineCommand216({
68213
68539
  meta: {
68214
68540
  name: "collections",
68215
68541
  description: "The store's collections, with the handles --collection takes. Example: baker shopify collections --full"
@@ -68234,7 +68560,7 @@ var collectionsCommand = defineCommand215({
68234
68560
  }
68235
68561
  }
68236
68562
  });
68237
- var stockCommand2 = defineCommand215({
68563
+ var stockCommand2 = defineCommand216({
68238
68564
  meta: {
68239
68565
  name: "stock",
68240
68566
  description: "Every product with options out of stock, naming them. Example: baker shopify stock"
@@ -68258,7 +68584,7 @@ var stockCommand2 = defineCommand215({
68258
68584
  }
68259
68585
  }
68260
68586
  });
68261
- var shopifyCommand = defineCommand215({
68587
+ var shopifyCommand = defineCommand216({
68262
68588
  meta: {
68263
68589
  name: "shopify",
68264
68590
  description: `Read the connected Shopify store as Baker keeps it \u2014 products, prices by country, stock, collections,
@@ -68295,13 +68621,14 @@ Examples:
68295
68621
  Product photos are already in the image library: place them by imageId
68296
68622
  (baker images download <imageIds> --out src/pages/<page>/_images/), never by a cdn.shopify.com link.
68297
68623
  Product videos are in the video library: place them with <Video playbackId="\u2026" />.
68298
- A page's buy button uses the option's buyUrl, one button per option \u2014 never build a Shopify link
68299
- yourself, and never use storeUrl (a page to look at) for it.
68624
+ On a landing, the buy button is <ShopifyProduct product="\u2026"/> \u2014 never a buyUrl or a Shopify link
68625
+ written by hand. buyUrl is for ads, emails and messages; never use storeUrl (a page to look at) for it.
68300
68626
  NO_SHOPIFY_CONNECTION means no store is connected: follow error.fix. SHOPIFY_NOT_AVAILABLE means it cannot be
68301
68627
  connected here yet: carry on without it.
68302
68628
  Full guide: __tooling__/docs/tools/baker/shopify.md`
68303
68629
  },
68304
68630
  subCommands: {
68631
+ "signup-offers": signupOffersCommand,
68305
68632
  store: storeCommand,
68306
68633
  products: productsCommand,
68307
68634
  product: productCommand,
@@ -68325,10 +68652,10 @@ Full guide: __tooling__/docs/tools/baker/shopify.md`
68325
68652
  });
68326
68653
 
68327
68654
  // src/commands/studio/index.ts
68328
- import { defineCommand as defineCommand224 } from "citty";
68655
+ import { defineCommand as defineCommand225 } from "citty";
68329
68656
 
68330
68657
  // src/commands/studio/animate.ts
68331
- import { defineCommand as defineCommand216 } from "citty";
68658
+ import { defineCommand as defineCommand217 } from "citty";
68332
68659
 
68333
68660
  // src/commands/studio/spokenPace.ts
68334
68661
  var WORDS_PER_SECOND3 = SPOKEN_WORDS_PER_SECOND;
@@ -68841,7 +69168,7 @@ async function adByHandHintFor(body) {
68841
69168
  return null;
68842
69169
  }
68843
69170
  }
68844
- var animateCommand = defineCommand216({
69171
+ var animateCommand = defineCommand217({
68845
69172
  meta: {
68846
69173
  name: "animate",
68847
69174
  description: "Render a clip. With an image the look is already fixed, so the prompt describes MOVEMENT \u2014 what the camera does, what the subject does, in what order. With --from text there is no image and the prompt is the whole shot.\n\nA rendered clip is NOT usable anywhere until you keep it: `baker studio keep <id> --slot N` is what puts it in the video library. Takes nobody keeps are never ingested, which is what makes a rejected batch cheap.\n\nFor anything longer than 15 seconds, or to build on footage that already exists, use --model bytedance/seedance-2.5: it renders 4-30s and is the only model that reads an existing clip or an existing soundtrack.\n\nExamples:\n baker studio animate 'slow push in, model turns to camera and smiles' --image j57abc123def456ghi789\n baker studio animate 'she looks to camera and says: \"Hola, soy Elena\"' --avatar elena --quality 720p --aspect-ratio 9:16\n baker studio animate 'handheld drift right, steam rising from the cup' --image './out/hero.png' --duration 6 --quality 1080p\n baker studio animate 'product rotates once on a turntable' --image j57abc\u2026,j57def\u2026 --from references\n baker studio animate 'she keeps walking, camera stays with her, then she stops and looks up' --image j57abc\u2026 --from references --from-clip j57batch\u2026:0 --model bytedance/seedance-2.5 --duration 20\n baker studio animate 'hold on the product, then a slow push-in' --from references --from-video j57vid\u2026 --model bytedance/seedance-2.5\n baker studio animate 'slow drone pull-back over a solar farm at golden hour, no people' --from text --model bytedance/seedance-2.5 --duration 12"
@@ -68966,7 +69293,7 @@ var animateCommand = defineCommand216({
68966
69293
  });
68967
69294
 
68968
69295
  // src/commands/studio/generate.ts
68969
- import { defineCommand as defineCommand217 } from "citty";
69296
+ import { defineCommand as defineCommand218 } from "citty";
68970
69297
  var MODEL_LIST2 = IMAGE_MODEL_IDS;
68971
69298
  var DEFAULT_MAX_WAIT_MS2 = 24e4;
68972
69299
  registerSchema({
@@ -69102,7 +69429,7 @@ function buildGenerateBody(args5, prompt) {
69102
69429
  }
69103
69430
  return body;
69104
69431
  }
69105
- var generateCommand = defineCommand217({
69432
+ var generateCommand = defineCommand218({
69106
69433
  meta: {
69107
69434
  name: "generate",
69108
69435
  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]]'"
@@ -69185,7 +69512,7 @@ var generateCommand = defineCommand217({
69185
69512
  });
69186
69513
 
69187
69514
  // src/commands/studio/get.ts
69188
- import { defineCommand as defineCommand218 } from "citty";
69515
+ import { defineCommand as defineCommand219 } from "citty";
69189
69516
  registerSchema({
69190
69517
  command: "studio.get",
69191
69518
  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.",
@@ -69194,7 +69521,7 @@ registerSchema({
69194
69521
  full: { type: "boolean", description: "Include settings, references and attribution", required: false }
69195
69522
  }
69196
69523
  });
69197
- var getCommand5 = defineCommand218({
69524
+ var getCommand5 = defineCommand219({
69198
69525
  meta: {
69199
69526
  name: "get",
69200
69527
  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"
@@ -69223,7 +69550,7 @@ var getCommand5 = defineCommand218({
69223
69550
  });
69224
69551
 
69225
69552
  // src/commands/studio/improve.ts
69226
- import { defineCommand as defineCommand219 } from "citty";
69553
+ import { defineCommand as defineCommand220 } from "citty";
69227
69554
  var DESCRIPTION2 = "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.";
69228
69555
  registerSchema({
69229
69556
  command: "studio.improve",
@@ -69248,7 +69575,7 @@ registerSchema({
69248
69575
  }
69249
69576
  }
69250
69577
  });
69251
- var improveCommand = defineCommand219({
69578
+ var improveCommand = defineCommand220({
69252
69579
  meta: {
69253
69580
  name: "improve",
69254
69581
  description: `${DESCRIPTION2}
@@ -69292,7 +69619,7 @@ Examples:
69292
69619
  });
69293
69620
 
69294
69621
  // src/commands/studio/keep.ts
69295
- import { defineCommand as defineCommand220 } from "citty";
69622
+ import { defineCommand as defineCommand221 } from "citty";
69296
69623
  registerSchema({
69297
69624
  command: "studio.keep",
69298
69625
  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.",
@@ -69302,7 +69629,7 @@ registerSchema({
69302
69629
  undo: { type: "boolean", description: "Un-star an image, or take a kept clip back out", required: false }
69303
69630
  }
69304
69631
  });
69305
- var keepCommand = defineCommand220({
69632
+ var keepCommand = defineCommand221({
69306
69633
  meta: {
69307
69634
  name: "keep",
69308
69635
  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"
@@ -69353,7 +69680,7 @@ var keepCommand = defineCommand220({
69353
69680
  });
69354
69681
 
69355
69682
  // src/commands/studio/list.ts
69356
- import { defineCommand as defineCommand221 } from "citty";
69683
+ import { defineCommand as defineCommand222 } from "citty";
69357
69684
  registerSchema({
69358
69685
  command: "studio.list",
69359
69686
  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.",
@@ -69364,7 +69691,7 @@ registerSchema({
69364
69691
  full: { type: "boolean", description: "Include settings, references and attribution", required: false }
69365
69692
  }
69366
69693
  });
69367
- var listCommand18 = defineCommand221({
69694
+ var listCommand18 = defineCommand222({
69368
69695
  meta: {
69369
69696
  name: "list",
69370
69697
  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"
@@ -69398,7 +69725,7 @@ var listCommand18 = defineCommand221({
69398
69725
  });
69399
69726
 
69400
69727
  // src/commands/studio/models.ts
69401
- import { defineCommand as defineCommand222 } from "citty";
69728
+ import { defineCommand as defineCommand223 } from "citty";
69402
69729
  var DESCRIPTION3 = "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.";
69403
69730
  registerSchema({
69404
69731
  command: "studio.models",
@@ -69485,7 +69812,7 @@ function buildModelCards(kind, model) {
69485
69812
  const selected = model ? ids.filter((id) => id === model) : ids;
69486
69813
  return selected.map((id) => build(id));
69487
69814
  }
69488
- var modelsCommand = defineCommand222({
69815
+ var modelsCommand = defineCommand223({
69489
69816
  meta: {
69490
69817
  name: "models",
69491
69818
  description: `${DESCRIPTION3}
@@ -69532,13 +69859,13 @@ Examples:
69532
69859
  });
69533
69860
 
69534
69861
  // src/commands/studio/skills.ts
69535
- import { defineCommand as defineCommand223 } from "citty";
69862
+ import { defineCommand as defineCommand224 } from "citty";
69536
69863
  registerSchema({
69537
69864
  command: "studio.skills",
69538
69865
  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.",
69539
69866
  args: {}
69540
69867
  });
69541
- var skillsCommand = defineCommand223({
69868
+ var skillsCommand = defineCommand224({
69542
69869
  meta: {
69543
69870
  name: "skills",
69544
69871
  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"
@@ -69562,7 +69889,7 @@ var skillsCommand = defineCommand223({
69562
69889
  });
69563
69890
 
69564
69891
  // src/commands/studio/index.ts
69565
- var studioCommand = defineCommand224({
69892
+ var studioCommand = defineCommand225({
69566
69893
  meta: {
69567
69894
  name: "studio",
69568
69895
  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.
@@ -69605,10 +69932,10 @@ Full guide: __tooling__/docs/tools/baker/studio.md`
69605
69932
  });
69606
69933
 
69607
69934
  // src/commands/tag-manager/index.ts
69608
- import { defineCommand as defineCommand228 } from "citty";
69935
+ import { defineCommand as defineCommand229 } from "citty";
69609
69936
 
69610
69937
  // src/commands/tag-manager/draft.ts
69611
- import { defineCommand as defineCommand225 } from "citty";
69938
+ import { defineCommand as defineCommand226 } from "citty";
69612
69939
 
69613
69940
  // src/commands/tag-manager/shared.ts
69614
69941
  import { readFileSync as readFileSync20 } from "fs";
@@ -69731,13 +70058,13 @@ registerSchema({
69731
70058
  chat: { type: "string", description: CHAT_READ_ARG.description, required: false }
69732
70059
  }
69733
70060
  });
69734
- var draftCommand6 = defineCommand225({
70061
+ var draftCommand6 = defineCommand226({
69735
70062
  meta: {
69736
70063
  name: "draft",
69737
70064
  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."
69738
70065
  },
69739
70066
  subCommands: {
69740
- list: defineCommand225({
70067
+ list: defineCommand226({
69741
70068
  meta: {
69742
70069
  name: "list",
69743
70070
  description: "Review everything staged on this chat (--json for the raw envelope)"
@@ -69750,7 +70077,7 @@ var draftCommand6 = defineCommand225({
69750
70077
  await draftList2(args5.json === true, args5.chat);
69751
70078
  }
69752
70079
  }),
69753
- show: defineCommand225({
70080
+ show: defineCommand226({
69754
70081
  meta: {
69755
70082
  name: "show",
69756
70083
  description: "Print the full staged payload for one change \u2014 the receipt to verify it looks right before publish (never truncated)."
@@ -69767,7 +70094,7 @@ var draftCommand6 = defineCommand225({
69767
70094
  );
69768
70095
  }
69769
70096
  }),
69770
- amend: defineCommand225({
70097
+ amend: defineCommand226({
69771
70098
  meta: {
69772
70099
  name: "amend",
69773
70100
  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."
@@ -69784,7 +70111,7 @@ var draftCommand6 = defineCommand225({
69784
70111
  });
69785
70112
  }
69786
70113
  }),
69787
- remove: defineCommand225({
70114
+ remove: defineCommand226({
69788
70115
  meta: { name: "remove", description: "Remove one staged change (cascades to anything depending on it)" },
69789
70116
  args: { ref: { type: "positional", description: "Staged ref (gtm_temp_*) or target", required: false } },
69790
70117
  run: async ({ args: args5 }) => {
@@ -69793,7 +70120,7 @@ var draftCommand6 = defineCommand225({
69793
70120
  });
69794
70121
  }
69795
70122
  }),
69796
- clear: defineCommand225({
70123
+ clear: defineCommand226({
69797
70124
  meta: { name: "clear", description: "Discard all Tag Manager changes staged on this chat" },
69798
70125
  run: async () => {
69799
70126
  await draftAction3("/api/tag-manager/draft/clear", {});
@@ -69803,7 +70130,7 @@ var draftCommand6 = defineCommand225({
69803
70130
  });
69804
70131
 
69805
70132
  // src/commands/tag-manager/read.ts
69806
- import { defineCommand as defineCommand226 } from "citty";
70133
+ import { defineCommand as defineCommand227 } from "citty";
69807
70134
  registerSchema({
69808
70135
  command: "tagManager.containers",
69809
70136
  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.",
@@ -69844,7 +70171,7 @@ function containersHints(containers) {
69844
70171
  }))
69845
70172
  });
69846
70173
  }
69847
- var containersCommand = defineCommand226({
70174
+ var containersCommand = defineCommand227({
69848
70175
  meta: {
69849
70176
  name: "containers",
69850
70177
  description: `List Tag Manager containers reachable by this company's connection.
@@ -69861,7 +70188,7 @@ Start here:
69861
70188
  }
69862
70189
  }
69863
70190
  });
69864
- var readCommand = defineCommand226({
70191
+ var readCommand = defineCommand227({
69865
70192
  meta: {
69866
70193
  name: "read",
69867
70194
  description: `Read the current contents of the Tag Manager container \u2014 always do this before staging changes.
@@ -69903,7 +70230,7 @@ Examples:
69903
70230
  });
69904
70231
 
69905
70232
  // src/commands/tag-manager/write-commands.ts
69906
- import { defineCommand as defineCommand227 } from "citty";
70233
+ import { defineCommand as defineCommand228 } from "citty";
69907
70234
  var CONTAINER_ARG_DESCRIPTION = "Numeric container id (optional only when one container is connected \u2014 run `baker tag-manager containers`)";
69908
70235
  var ENTITIES = [
69909
70236
  {
@@ -69959,10 +70286,10 @@ for (const { entity, noun, createHint } of ENTITIES) {
69959
70286
  });
69960
70287
  }
69961
70288
  function entityCommand(entity, noun, example) {
69962
- return defineCommand227({
70289
+ return defineCommand228({
69963
70290
  meta: { name: entity, description: `Stage ${noun} changes on this chat's Tag Manager draft` },
69964
70291
  subCommands: {
69965
- create: defineCommand227({
70292
+ create: defineCommand228({
69966
70293
  meta: {
69967
70294
  name: "create",
69968
70295
  description: `Stage a new ${noun}
@@ -69984,7 +70311,7 @@ Examples:
69984
70311
  });
69985
70312
  }
69986
70313
  }),
69987
- update: defineCommand227({
70314
+ update: defineCommand228({
69988
70315
  meta: {
69989
70316
  name: "update",
69990
70317
  description: `Stage an update to an existing ${noun} (pass its id or path)`
@@ -70004,7 +70331,7 @@ Examples:
70004
70331
  });
70005
70332
  }
70006
70333
  }),
70007
- delete: defineCommand227({
70334
+ delete: defineCommand228({
70008
70335
  meta: { name: "delete", description: `Stage the deletion of a ${noun} (pass its id or path)` },
70009
70336
  args: {
70010
70337
  id: { type: "positional", description: `${noun} id or path`, required: false },
@@ -70045,7 +70372,7 @@ function builtinTypes(args5) {
70045
70372
  }
70046
70373
  return raw.split(",").map((entry) => entry.trim());
70047
70374
  }
70048
- var builtinCommand = defineCommand227({
70375
+ var builtinCommand = defineCommand228({
70049
70376
  meta: {
70050
70377
  name: "builtin",
70051
70378
  description: `Enable or disable built-in variables
@@ -70055,7 +70382,7 @@ Examples:
70055
70382
  baker tag-manager builtin disable --types formId`
70056
70383
  },
70057
70384
  subCommands: {
70058
- enable: defineCommand227({
70385
+ enable: defineCommand228({
70059
70386
  meta: { name: "enable", description: "Stage enabling built-in variables" },
70060
70387
  args: {
70061
70388
  types: { type: "string", description: "Comma-separated types", required: false },
@@ -70069,7 +70396,7 @@ Examples:
70069
70396
  });
70070
70397
  }
70071
70398
  }),
70072
- disable: defineCommand227({
70399
+ disable: defineCommand228({
70073
70400
  meta: { name: "disable", description: "Stage disabling built-in variables" },
70074
70401
  args: {
70075
70402
  types: { type: "string", description: "Comma-separated types", required: false },
@@ -70087,7 +70414,7 @@ Examples:
70087
70414
  });
70088
70415
 
70089
70416
  // src/commands/tag-manager/index.ts
70090
- var tagManagerCommand = defineCommand228({
70417
+ var tagManagerCommand = defineCommand229({
70091
70418
  meta: {
70092
70419
  name: "tag-manager",
70093
70420
  description: `Read and change what lives inside the client's Google Tag Manager container \u2014 tags, triggers, variables, folders and built-in variables.
@@ -70124,7 +70451,7 @@ Full guide: __tooling__/docs/tools/baker/tag-manager.md`
70124
70451
  });
70125
70452
 
70126
70453
  // src/commands/tags/index.ts
70127
- import { defineCommand as defineCommand229 } from "citty";
70454
+ import { defineCommand as defineCommand230 } from "citty";
70128
70455
 
70129
70456
  // src/commands/tags/shared.ts
70130
70457
  function failApi3(err) {
@@ -70204,7 +70531,7 @@ async function listTags(json) {
70204
70531
  var listArgs11 = {
70205
70532
  json: { type: "boolean", description: "Print the raw JSON envelope instead of the readable list" }
70206
70533
  };
70207
- var listCommand19 = defineCommand229({
70534
+ var listCommand19 = defineCommand230({
70208
70535
  meta: {
70209
70536
  name: "list",
70210
70537
  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"
@@ -70223,7 +70550,7 @@ async function listDraft4(chat) {
70223
70550
  failApi3(err);
70224
70551
  }
70225
70552
  }
70226
- var draftCommand7 = defineCommand229({
70553
+ var draftCommand7 = defineCommand230({
70227
70554
  meta: {
70228
70555
  name: "draft",
70229
70556
  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."
@@ -70233,7 +70560,7 @@ var draftCommand7 = defineCommand229({
70233
70560
  await listDraft4(args5.chat);
70234
70561
  }
70235
70562
  });
70236
- var tagsCommand4 = defineCommand229({
70563
+ var tagsCommand4 = defineCommand230({
70237
70564
  meta: {
70238
70565
  name: "tags",
70239
70566
  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.
@@ -70262,10 +70589,10 @@ Full guide: __tooling__/docs/tools/baker/tags.md`
70262
70589
  });
70263
70590
 
70264
70591
  // src/commands/testimonials/index.ts
70265
- import { defineCommand as defineCommand233 } from "citty";
70592
+ import { defineCommand as defineCommand234 } from "citty";
70266
70593
 
70267
70594
  // src/commands/testimonials/get.ts
70268
- import { defineCommand as defineCommand230 } from "citty";
70595
+ import { defineCommand as defineCommand231 } from "citty";
70269
70596
  registerSchema({
70270
70597
  command: "testimonials.get",
70271
70598
  description: "Get a single testimonial by ID",
@@ -70273,7 +70600,7 @@ registerSchema({
70273
70600
  id: { type: "string", description: "Testimonial ID", required: true }
70274
70601
  }
70275
70602
  });
70276
- var getCommand6 = defineCommand230({
70603
+ var getCommand6 = defineCommand231({
70277
70604
  meta: { name: "get", description: "Get a single testimonial by ID. Example: baker testimonials get j571abc123" },
70278
70605
  args: {
70279
70606
  id: { type: "positional", description: "Testimonial ID", required: false },
@@ -70310,7 +70637,7 @@ var getCommand6 = defineCommand230({
70310
70637
  });
70311
70638
 
70312
70639
  // src/commands/testimonials/list.ts
70313
- import { defineCommand as defineCommand231 } from "citty";
70640
+ import { defineCommand as defineCommand232 } from "citty";
70314
70641
 
70315
70642
  // src/commands/testimonials/emptyCorpusHints.ts
70316
70643
  function resolveEmptyReason({
@@ -70467,7 +70794,7 @@ function buildListParams(args5) {
70467
70794
  }
70468
70795
  return params2;
70469
70796
  }
70470
- var listCommand20 = defineCommand231({
70797
+ var listCommand20 = defineCommand232({
70471
70798
  meta: {
70472
70799
  name: "list",
70473
70800
  description: "List testimonials with optional filters. Example: baker testimonials list --source google --sentiment positive"
@@ -70527,7 +70854,7 @@ var listCommand20 = defineCommand231({
70527
70854
  });
70528
70855
 
70529
70856
  // src/commands/testimonials/search.ts
70530
- import { defineCommand as defineCommand232 } from "citty";
70857
+ import { defineCommand as defineCommand233 } from "citty";
70531
70858
  var FILTER_FLAGS2 = ["source", "rating-min", "rating-max", "status", "sentiment", "language", "tags"];
70532
70859
  function languageBiasHint(results, requestedLanguage) {
70533
70860
  if (requestedLanguage) {
@@ -70606,7 +70933,7 @@ function buildSearchRequest(query, args5) {
70606
70933
  }
70607
70934
  return body;
70608
70935
  }
70609
- var searchCommand3 = defineCommand232({
70936
+ var searchCommand3 = defineCommand233({
70610
70937
  meta: {
70611
70938
  name: "search",
70612
70939
  description: "Semantic search testimonials by text query. Uses hybrid BM25 + vector + reranking. Example: baker testimonials search 'great service' --rating-min 4"
@@ -70670,7 +70997,7 @@ var searchCommand3 = defineCommand232({
70670
70997
  var tagsCommand5 = makeTagsCommand("testimonials", "testimonial", "/api/testimonials/tags");
70671
70998
 
70672
70999
  // src/commands/testimonials/index.ts
70673
- var testimonialsCommand = defineCommand233({
71000
+ var testimonialsCommand = defineCommand234({
70674
71001
  meta: {
70675
71002
  name: "testimonials",
70676
71003
  description: `Find and browse testimonials in Baker. Subcommands: search, get, list, tags.
@@ -70692,10 +71019,10 @@ Full guide: __tooling__/docs/tools/baker/testimonials.md`
70692
71019
  });
70693
71020
 
70694
71021
  // src/commands/videos/index.ts
70695
- import { defineCommand as defineCommand241 } from "citty";
71022
+ import { defineCommand as defineCommand242 } from "citty";
70696
71023
 
70697
71024
  // src/commands/videos/delete.ts
70698
- import { defineCommand as defineCommand234 } from "citty";
71025
+ import { defineCommand as defineCommand235 } from "citty";
70699
71026
  registerSchema({
70700
71027
  command: "videos.delete",
70701
71028
  description: "Delete a video by ID",
@@ -70709,7 +71036,7 @@ registerSchema({
70709
71036
  }
70710
71037
  }
70711
71038
  });
70712
- var deleteCommand4 = defineCommand234({
71039
+ var deleteCommand4 = defineCommand235({
70713
71040
  meta: {
70714
71041
  name: "delete",
70715
71042
  description: "Delete a video by ID. Use --dry-run to preview. Example: baker videos delete j571abc123 --dry-run"
@@ -70750,7 +71077,7 @@ var deleteCommand4 = defineCommand234({
70750
71077
  });
70751
71078
 
70752
71079
  // src/commands/videos/describe.ts
70753
- import { defineCommand as defineCommand235 } from "citty";
71080
+ import { defineCommand as defineCommand236 } from "citty";
70754
71081
  var DESCRIPTION_HELP2 = "What the clip actually shows, in the words someone would search for it by. This is what `baker videos search` retrieves on.";
70755
71082
  registerSchema({
70756
71083
  command: "videos.describe",
@@ -70767,7 +71094,7 @@ registerSchema({
70767
71094
  full: { type: "boolean", description: "Return the whole library row, not just what changed", required: false }
70768
71095
  }
70769
71096
  });
70770
- var describeCommand2 = defineCommand235({
71097
+ var describeCommand2 = defineCommand236({
70771
71098
  meta: {
70772
71099
  name: "describe",
70773
71100
  description: `Correct what the library says a video is. Only the fields you pass change; the rest keep their current values.
@@ -70825,7 +71152,7 @@ Example: baker videos describe j571abc123 --description "Customer explaining how
70825
71152
  });
70826
71153
 
70827
71154
  // src/commands/videos/get.ts
70828
- import { defineCommand as defineCommand236 } from "citty";
71155
+ import { defineCommand as defineCommand237 } from "citty";
70829
71156
  registerSchema({
70830
71157
  command: "videos.get",
70831
71158
  description: "Get a single video by ID",
@@ -70833,7 +71160,7 @@ registerSchema({
70833
71160
  id: { type: "string", description: "Video ID", required: true }
70834
71161
  }
70835
71162
  });
70836
- var getCommand7 = defineCommand236({
71163
+ var getCommand7 = defineCommand237({
70837
71164
  meta: { name: "get", description: "Get a single video by ID. Example: baker videos get j571abc123" },
70838
71165
  args: {
70839
71166
  id: { type: "positional", description: "Video ID", required: false },
@@ -70870,7 +71197,7 @@ var getCommand7 = defineCommand236({
70870
71197
  });
70871
71198
 
70872
71199
  // src/commands/videos/group.ts
70873
- import { defineCommand as defineCommand237 } from "citty";
71200
+ import { defineCommand as defineCommand238 } from "citty";
70874
71201
  registerSchema({
70875
71202
  command: "videos.group",
70876
71203
  description: "List every clip and image that arrived in the same set as this video (carousel slides, one page)",
@@ -70879,7 +71206,7 @@ registerSchema({
70879
71206
  "group-key": { type: "string", description: "The set key directly, when you already have it", required: false }
70880
71207
  }
70881
71208
  });
70882
- var groupCommand2 = defineCommand237({
71209
+ var groupCommand2 = defineCommand238({
70883
71210
  meta: {
70884
71211
  name: "group",
70885
71212
  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>"
@@ -70902,7 +71229,7 @@ var groupCommand2 = defineCommand237({
70902
71229
  import { mkdtemp as mkdtemp3, rm as rm9, stat as stat12 } from "fs/promises";
70903
71230
  import { tmpdir as tmpdir4 } from "os";
70904
71231
  import path48 from "path";
70905
- import { defineCommand as defineCommand238 } from "citty";
71232
+ import { defineCommand as defineCommand239 } from "citty";
70906
71233
 
70907
71234
  // src/lib/streamUpload.ts
70908
71235
  import { createHash as createHash2 } from "crypto";
@@ -71066,7 +71393,7 @@ registerSchema({
71066
71393
  "dry-run": { type: "boolean", description: "Preview the operation without executing", required: false }
71067
71394
  }
71068
71395
  });
71069
- var ingestCommand2 = defineCommand238({
71396
+ var ingestCommand2 = defineCommand239({
71070
71397
  meta: {
71071
71398
  name: "ingest",
71072
71399
  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"
@@ -71328,7 +71655,7 @@ async function uploadToAssetStore(filePath, sizeBytes) {
71328
71655
  }
71329
71656
 
71330
71657
  // src/commands/videos/search.ts
71331
- import { defineCommand as defineCommand239 } from "citty";
71658
+ import { defineCommand as defineCommand240 } from "citty";
71332
71659
  registerSchema({
71333
71660
  command: "videos.search",
71334
71661
  description: "Search videos by text query. Only returns ready videos.",
@@ -71338,7 +71665,7 @@ registerSchema({
71338
71665
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
71339
71666
  }
71340
71667
  });
71341
- var searchCommand4 = defineCommand239({
71668
+ var searchCommand4 = defineCommand240({
71342
71669
  meta: {
71343
71670
  name: "search",
71344
71671
  description: "Semantic search videos by text query. Uses hybrid BM25 + vector + reranking. Example: baker videos search 'product demo' --tags tutorial"
@@ -71390,7 +71717,7 @@ var tagsCommand6 = makeTagsCommand("videos", "video", "/api/videos/tags");
71390
71717
  // src/commands/videos/upload.ts
71391
71718
  import { readFile as readFile34, stat as stat13 } from "fs/promises";
71392
71719
  import { basename as basename3, extname as extname4 } from "path";
71393
- import { defineCommand as defineCommand240 } from "citty";
71720
+ import { defineCommand as defineCommand241 } from "citty";
71394
71721
  var MIME_MAP = {
71395
71722
  ".mp4": "video/mp4",
71396
71723
  ".mov": "video/quicktime",
@@ -71432,7 +71759,7 @@ function detectContentType(filePath) {
71432
71759
  function isRemoteUrl3(value) {
71433
71760
  return /^https?:\/\//i.test(value);
71434
71761
  }
71435
- var uploadCommand2 = defineCommand240({
71762
+ var uploadCommand2 = defineCommand241({
71436
71763
  meta: {
71437
71764
  name: "upload",
71438
71765
  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"
@@ -71516,7 +71843,7 @@ var uploadCommand2 = defineCommand240({
71516
71843
  });
71517
71844
 
71518
71845
  // src/commands/videos/index.ts
71519
- var videosCommand = defineCommand241({
71846
+ var videosCommand = defineCommand242({
71520
71847
  meta: {
71521
71848
  name: "videos",
71522
71849
  description: `Find and manage videos in Baker. Subcommands: search, get, upload, ingest, describe, delete, tags.
@@ -71545,10 +71872,10 @@ Full guide: __tooling__/docs/tools/baker/videos.md`
71545
71872
  });
71546
71873
 
71547
71874
  // src/commands/winning-ads/index.ts
71548
- import { defineCommand as defineCommand254 } from "citty";
71875
+ import { defineCommand as defineCommand255 } from "citty";
71549
71876
 
71550
71877
  // src/commands/winning-ads/advertisers.ts
71551
- import { defineCommand as defineCommand242 } from "citty";
71878
+ import { defineCommand as defineCommand243 } from "citty";
71552
71879
 
71553
71880
  // src/commands/winning-ads/shared.ts
71554
71881
  function splitList3(value) {
@@ -71601,7 +71928,7 @@ function advertiserNormalizer(record, full) {
71601
71928
  last_synced_at: record.last_synced_at ?? null
71602
71929
  };
71603
71930
  }
71604
- var advertisersCommand2 = defineCommand242({
71931
+ var advertisersCommand2 = defineCommand243({
71605
71932
  meta: {
71606
71933
  name: "advertisers",
71607
71934
  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'
@@ -71659,7 +71986,7 @@ var advertisersCommand2 = defineCommand242({
71659
71986
  });
71660
71987
 
71661
71988
  // src/commands/winning-ads/brief.ts
71662
- import { defineCommand as defineCommand243 } from "citty";
71989
+ import { defineCommand as defineCommand244 } from "citty";
71663
71990
  registerSchema({
71664
71991
  command: "winning-ads.brief",
71665
71992
  description: "Generate a creative brief grounded in strategically-similar winning ads. Optionally describe the target creative with --dna (JSON) and steer with --notes.",
@@ -71705,7 +72032,7 @@ function parseDna(raw) {
71705
72032
  }
71706
72033
  return parsed;
71707
72034
  }
71708
- var briefCommand = defineCommand243({
72035
+ var briefCommand = defineCommand244({
71709
72036
  meta: {
71710
72037
  name: "brief",
71711
72038
  description: `Generate a creative brief from winning references. Example: baker winning-ads brief --dna '{"angle":"cost savings"}' --notes "B2B, LinkedIn video" --k 8`
@@ -71741,7 +72068,7 @@ var briefCommand = defineCommand243({
71741
72068
  });
71742
72069
 
71743
72070
  // src/commands/winning-ads/content.ts
71744
- import { defineCommand as defineCommand244 } from "citty";
72071
+ import { defineCommand as defineCommand245 } from "citty";
71745
72072
  registerSchema({
71746
72073
  command: "winning-ads.content",
71747
72074
  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.",
@@ -71754,7 +72081,7 @@ registerSchema({
71754
72081
  }
71755
72082
  }
71756
72083
  });
71757
- var contentCommand = defineCommand244({
72084
+ var contentCommand = defineCommand245({
71758
72085
  meta: {
71759
72086
  name: "content",
71760
72087
  description: "Read the transcript + on-screen text + copy of one winning ad. Example: baker winning-ads content adg_123 --platform meta --full --output md"
@@ -71803,7 +72130,7 @@ var contentCommand = defineCommand244({
71803
72130
  });
71804
72131
 
71805
72132
  // src/commands/winning-ads/feed.ts
71806
- import { defineCommand as defineCommand245 } from "citty";
72133
+ import { defineCommand as defineCommand246 } from "citty";
71807
72134
  function buildFeedParams(input) {
71808
72135
  const params2 = {};
71809
72136
  const advertiser = splitList3(input.advertiser);
@@ -71855,7 +72182,7 @@ registerSchema({
71855
72182
  format: { type: "string", description: "Comma-separated formats to include (e.g. static,video)", required: false }
71856
72183
  }
71857
72184
  });
71858
- var feedCommand2 = defineCommand245({
72185
+ var feedCommand2 = defineCommand246({
71859
72186
  meta: {
71860
72187
  name: "feed",
71861
72188
  description: "Winners across every brand you follow (browse, then trim per advertiser). Example: baker winning-ads feed --per-advertiser 5 --output md"
@@ -71940,7 +72267,7 @@ var feedCommand2 = defineCommand245({
71940
72267
  });
71941
72268
 
71942
72269
  // src/commands/winning-ads/follow.ts
71943
- import { defineCommand as defineCommand246 } from "citty";
72270
+ import { defineCommand as defineCommand247 } from "citty";
71944
72271
  var PLATFORMS = adLibraryPlatformSchema.options;
71945
72272
  registerSchema({
71946
72273
  command: "winning-ads.follow",
@@ -71955,7 +72282,7 @@ registerSchema({
71955
72282
  label: { type: "string", description: "Optional display label (defaults to the resolved name)", required: false }
71956
72283
  }
71957
72284
  });
71958
- var followCommand = defineCommand246({
72285
+ var followCommand = defineCommand247({
71959
72286
  meta: {
71960
72287
  name: "follow",
71961
72288
  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 every platform we can resolve. Example: baker winning-ads follow "deel.com" --platform meta'
@@ -72002,7 +72329,7 @@ var followCommand = defineCommand246({
72002
72329
  });
72003
72330
 
72004
72331
  // src/commands/winning-ads/follow-competitors.ts
72005
- import { defineCommand as defineCommand247 } from "citty";
72332
+ import { defineCommand as defineCommand248 } from "citty";
72006
72333
  var PLATFORMS2 = adLibraryPlatformSchema.options;
72007
72334
  var BATCH_TIMEOUT_MS = 3e5;
72008
72335
  function buildFollowBatchBody(input) {
@@ -72035,7 +72362,7 @@ registerSchema({
72035
72362
  }
72036
72363
  }
72037
72364
  });
72038
- var followCompetitorsCommand = defineCommand247({
72365
+ var followCompetitorsCommand = defineCommand248({
72039
72366
  meta: {
72040
72367
  name: "follow-competitors",
72041
72368
  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"'
@@ -72110,7 +72437,7 @@ var followCompetitorsCommand = defineCommand247({
72110
72437
  });
72111
72438
 
72112
72439
  // src/commands/winning-ads/following.ts
72113
- import { defineCommand as defineCommand248 } from "citty";
72440
+ import { defineCommand as defineCommand249 } from "citty";
72114
72441
  registerSchema({
72115
72442
  command: "winning-ads.following",
72116
72443
  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.",
@@ -72164,7 +72491,7 @@ function followingNormalizer(record, full) {
72164
72491
  platforms: Array.isArray(record.platforms) ? record.platforms : []
72165
72492
  };
72166
72493
  }
72167
- var followingCommand = defineCommand248({
72494
+ var followingCommand = defineCommand249({
72168
72495
  meta: {
72169
72496
  name: "following",
72170
72497
  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"
@@ -72200,7 +72527,7 @@ var followingCommand = defineCommand248({
72200
72527
  });
72201
72528
 
72202
72529
  // src/commands/winning-ads/patterns.ts
72203
- import { defineCommand as defineCommand249 } from "citty";
72530
+ import { defineCommand as defineCommand250 } from "citty";
72204
72531
  registerSchema({
72205
72532
  command: "winning-ads.patterns",
72206
72533
  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.",
@@ -72239,7 +72566,7 @@ function discriminatorRow(record) {
72239
72566
  top_values_duds: Array.isArray(record.top_values_b) ? record.top_values_b.join(", ") : ""
72240
72567
  };
72241
72568
  }
72242
- var patternsCommand = defineCommand249({
72569
+ var patternsCommand = defineCommand250({
72243
72570
  meta: {
72244
72571
  name: "patterns",
72245
72572
  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"
@@ -72295,7 +72622,7 @@ var patternsCommand = defineCommand249({
72295
72622
  });
72296
72623
 
72297
72624
  // src/commands/winning-ads/search.ts
72298
- import { defineCommand as defineCommand250 } from "citty";
72625
+ import { defineCommand as defineCommand251 } from "citty";
72299
72626
  registerSchema({
72300
72627
  command: "winning-ads.search",
72301
72628
  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.",
@@ -72433,7 +72760,7 @@ function reportSearchError(err) {
72433
72760
  }
72434
72761
  reportError2(err);
72435
72762
  }
72436
- var searchCommand5 = defineCommand250({
72763
+ var searchCommand5 = defineCommand251({
72437
72764
  meta: {
72438
72765
  name: "search",
72439
72766
  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"
@@ -72553,7 +72880,7 @@ var searchCommand5 = defineCommand250({
72553
72880
  });
72554
72881
 
72555
72882
  // src/commands/winning-ads/seeds.ts
72556
- import { defineCommand as defineCommand251 } from "citty";
72883
+ import { defineCommand as defineCommand252 } from "citty";
72557
72884
  function leanRow(r) {
72558
72885
  return {
72559
72886
  key: r.key,
@@ -72581,7 +72908,7 @@ function makeSeedCommand(opts) {
72581
72908
  limit: { type: "number", description: "Max keys 1-100 (default 20)", required: false, default: 20 }
72582
72909
  }
72583
72910
  });
72584
- return defineCommand251({
72911
+ return defineCommand252({
72585
72912
  meta: { name: opts.name, description: opts.description },
72586
72913
  args: {
72587
72914
  platform: { type: "string", description: "Single platform to segment on", required: false },
@@ -72630,7 +72957,7 @@ var formatsCommand = makeSeedCommand({
72630
72957
  });
72631
72958
 
72632
72959
  // src/commands/winning-ads/unfollow.ts
72633
- import { defineCommand as defineCommand252 } from "citty";
72960
+ import { defineCommand as defineCommand253 } from "citty";
72634
72961
  registerSchema({
72635
72962
  command: "winning-ads.unfollow",
72636
72963
  description: "Stop following a brand \u2014 removes it from your ad-dna library by advertiser id.",
@@ -72638,7 +72965,7 @@ registerSchema({
72638
72965
  advertiser: { type: "string", description: "Advertiser id to unfollow", required: true }
72639
72966
  }
72640
72967
  });
72641
- var unfollowCommand = defineCommand252({
72968
+ var unfollowCommand = defineCommand253({
72642
72969
  meta: {
72643
72970
  name: "unfollow",
72644
72971
  description: "Stop following a brand by advertiser id. Example: baker winning-ads unfollow adv_123"
@@ -72659,7 +72986,7 @@ var unfollowCommand = defineCommand252({
72659
72986
  });
72660
72987
 
72661
72988
  // src/commands/winning-ads/winners.ts
72662
- import { defineCommand as defineCommand253 } from "citty";
72989
+ import { defineCommand as defineCommand254 } from "citty";
72663
72990
  registerSchema({
72664
72991
  command: "winning-ads.winners",
72665
72992
  description: "Top winning ads for one advertiser id (from `advertisers` or `following`). Returns lean winner cards; add --full for DNA + longevity.",
@@ -72669,7 +72996,7 @@ registerSchema({
72669
72996
  platform: { type: "string", description: "Filter to a single platform: meta|linkedin|tiktok", required: false }
72670
72997
  }
72671
72998
  });
72672
- var winnersCommand = defineCommand253({
72999
+ var winnersCommand = defineCommand254({
72673
73000
  meta: {
72674
73001
  name: "winners",
72675
73002
  description: "Top winning ads for a specific advertiser id. Example: baker winning-ads winners adv_123 --top 15 --output md"
@@ -72719,7 +73046,7 @@ var winnersCommand = defineCommand253({
72719
73046
  });
72720
73047
 
72721
73048
  // src/commands/winning-ads/index.ts
72722
- var winningAdsCommand = defineCommand254({
73049
+ var winningAdsCommand = defineCommand255({
72723
73050
  meta: {
72724
73051
  name: "winning-ads",
72725
73052
  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.
@@ -72919,8 +73246,8 @@ function suggestFlag(unknown, command) {
72919
73246
  const name = flag3.slice(2);
72920
73247
  const def = args5[name] ?? args5[camel(name)];
72921
73248
  const described = def && typeof def === "object" ? def.description : void 0;
72922
- const description = typeof described === "string" ? described.split(/(?<!\b(?:e\.g|i\.e|vs))\.\s/)[0]?.trim().replace(/\.$/, "") : "";
72923
- return description ? { flag: flag3, description } : { flag: flag3 };
73249
+ const description2 = typeof described === "string" ? described.split(/(?<!\b(?:e\.g|i\.e|vs))\.\s/)[0]?.trim().replace(/\.$/, "") : "";
73250
+ return description2 ? { flag: flag3, description: description2 } : { flag: flag3 };
72924
73251
  }
72925
73252
  function suggestionText(suggestion) {
72926
73253
  if (!suggestion) {
@@ -73046,7 +73373,7 @@ function getCliVersion() {
73046
73373
  }
73047
73374
 
73048
73375
  // src/cli.ts
73049
- var main = defineCommand255({
73376
+ var main = defineCommand256({
73050
73377
  meta: {
73051
73378
  name: "baker",
73052
73379
  version: getCliVersion(),