@koda-sl/baker-cli 0.411.0 → 0.413.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -97,7 +97,7 @@ import {
97
97
  withoutCastName,
98
98
  wordCount,
99
99
  ytDlpBlockSignal
100
- } from "./chunk-KTMFHBN6.js";
100
+ } from "./chunk-4Q7EDLEC.js";
101
101
  import {
102
102
  NAME_CONTAINS_ARG,
103
103
  csvOrJson,
@@ -108,7 +108,7 @@ import {
108
108
  resolveEffectiveStatus,
109
109
  statusFilterHints,
110
110
  todayIso
111
- } from "./chunk-ZIOW64BL.js";
111
+ } from "./chunk-PLH5K6RI.js";
112
112
  import {
113
113
  buildQueryCacheKey,
114
114
  cacheGet,
@@ -124,19 +124,19 @@ import {
124
124
  writeAdsJson,
125
125
  writeAdsOutput,
126
126
  writeJsonEnvelope
127
- } from "./chunk-VKWHVMMD.js";
127
+ } from "./chunk-G7YB37RQ.js";
128
128
  import {
129
129
  ApiError,
130
130
  apiGet,
131
131
  apiGetWithHeaders,
132
132
  apiPost,
133
133
  validateConvexId
134
- } from "./chunk-S5BY45H7.js";
134
+ } from "./chunk-FLERTMEW.js";
135
135
  import {
136
136
  installExitRecord,
137
137
  installStreamTaps,
138
138
  logInvocation
139
- } from "./chunk-ISJOVAJP.js";
139
+ } from "./chunk-EXFQDO6T.js";
140
140
  import {
141
141
  __commonJS,
142
142
  __esm,
@@ -171,7 +171,7 @@ import {
171
171
  winningAdNormalizer,
172
172
  writeJson,
173
173
  writeOutput
174
- } from "./chunk-ISCXDKWB.js";
174
+ } from "./chunk-ASON7WRD.js";
175
175
 
176
176
  // ../../node_modules/.pnpm/i18n-iso-countries@7.14.0/node_modules/i18n-iso-countries/langs/af.json
177
177
  var require_af = __commonJS({
@@ -21715,7 +21715,7 @@ var require_entry_node = __commonJS({
21715
21715
  });
21716
21716
 
21717
21717
  // src/cli.ts
21718
- import { defineCommand as defineCommand279, runMain } from "citty";
21718
+ import { defineCommand as defineCommand285, runMain } from "citty";
21719
21719
 
21720
21720
  // src/cache-flag.ts
21721
21721
  var NO_CACHE_ARG = {
@@ -26265,6 +26265,9 @@ var chatChangeTypeSchema = z13.enum([
26265
26265
  // A connected social account feeding the image and video libraries, keyed
26266
26266
  // on `platform:handle`. Immediate — see convex/mediaSources/mutations.ts.
26267
26267
  "media-source",
26268
+ // A review site listing feeding the testimonials library, keyed on the
26269
+ // source's id. Immediate — see convex/testimonials/mutations.ts.
26270
+ "testimonial-source",
26268
26271
  // A landing page saved to the reference library, keyed on the page.
26269
26272
  "landing-reference",
26270
26273
  // One Studio batch — the takes of one brief, as a set. See convex/lib/validators.ts.
@@ -28445,6 +28448,13 @@ var imageSourceSchema = z25.enum([
28445
28448
  "website",
28446
28449
  "google_testimonial",
28447
28450
  "trustpilot_testimonial",
28451
+ "yelp_testimonial",
28452
+ "tripadvisor_testimonial",
28453
+ "g2_testimonial",
28454
+ "capterra_testimonial",
28455
+ "amazon_testimonial",
28456
+ "app_store_testimonial",
28457
+ "google_play_testimonial",
28448
28458
  "instagram",
28449
28459
  "tiktok",
28450
28460
  "linkedin",
@@ -30262,7 +30272,17 @@ var tagChangeToolResultSchema = z33.object({
30262
30272
 
30263
30273
  // ../api/src/testimonials.ts
30264
30274
  import { z as z34 } from "zod";
30265
- var testimonialSourceTypeSchema = z34.enum(["google", "trustpilot"]);
30275
+ var testimonialSourceTypeSchema = z34.enum([
30276
+ "google",
30277
+ "trustpilot",
30278
+ "yelp",
30279
+ "tripadvisor",
30280
+ "g2",
30281
+ "capterra",
30282
+ "amazon",
30283
+ "app_store",
30284
+ "google_play"
30285
+ ]);
30266
30286
  var testimonialStatusSchema = z34.enum(["pending", "processing", "ready", "error"]);
30267
30287
  var testimonialSentimentSchema = z34.enum(["positive", "neutral", "negative"]);
30268
30288
  var testimonialDocSchema = z34.object({
@@ -30330,6 +30350,35 @@ var testimonialsOutscraperWebhookResponseSchema = z34.object({
30330
30350
  ok: z34.literal(true),
30331
30351
  note: z34.string().optional()
30332
30352
  });
30353
+ var testimonialSourceRefreshIntervalSchema = z34.enum(["never", "daily", "weekly", "monthly"]);
30354
+ var testimonialSourceSummarySchema = z34.object({
30355
+ id: z34.string(),
30356
+ platform: testimonialSourceTypeSchema,
30357
+ platformLabel: z34.string(),
30358
+ label: z34.string(),
30359
+ pageUrl: z34.string().optional(),
30360
+ status: z34.enum(["importing", "idle", "error"]),
30361
+ reviews: z34.number(),
30362
+ refreshInterval: z34.string(),
30363
+ lastSyncedAt: z34.number().optional(),
30364
+ error: z34.string().optional(),
30365
+ createdAt: z34.number()
30366
+ });
30367
+ var testimonialSourcesAddRequestSchema = z34.object({
30368
+ /** The review page's link; for Google Maps also the business name. */
30369
+ input: z34.string().trim().min(1, "Pass the review page's link, or a business name for Google Maps"),
30370
+ /** Needed only to force a site: a business name is Google, a link names its own site. */
30371
+ platform: testimonialSourceTypeSchema.optional(),
30372
+ /** Auto-sync cadence. Omitted = never, like the dashboard. */
30373
+ every: testimonialSourceRefreshIntervalSchema.optional()
30374
+ });
30375
+ var sourceIdSchema = z34.string().trim().min(1, "Pass a source id from `baker testimonials sources list`");
30376
+ var testimonialSourcesSyncRequestSchema = z34.object({ id: sourceIdSchema });
30377
+ var testimonialSourcesRemoveRequestSchema = z34.object({ id: sourceIdSchema });
30378
+ var testimonialSourcesScheduleRequestSchema = z34.object({
30379
+ id: sourceIdSchema,
30380
+ every: testimonialSourceRefreshIntervalSchema
30381
+ });
30333
30382
 
30334
30383
  // ../api/src/videos.ts
30335
30384
  import { z as z35 } from "zod";
@@ -30389,7 +30438,11 @@ var videoAnalysisSchema = z35.object({
30389
30438
  shotStyle: z35.string(),
30390
30439
  audioStyle: z35.string(),
30391
30440
  hook: z35.string(),
30392
- usageNotes: z35.string()
30441
+ usageNotes: z35.string(),
30442
+ // Optional because rows analysed before analysis version 2 do not carry them.
30443
+ format: z35.string().optional(),
30444
+ tone: z35.string().optional(),
30445
+ productClaims: z35.array(z35.string()).optional()
30393
30446
  });
30394
30447
  var videoDocSchema = z35.object({
30395
30448
  _id: z35.string(),
@@ -53578,10 +53631,10 @@ Example: baker ads meta ads retag 120252158038400247 --url-tags "utm_source={{si
53578
53631
  }
53579
53632
  },
53580
53633
  run: async ({ args: args8 }) => {
53581
- const { apiPost: apiPost2 } = await import("./client-AY6TPFQ4.js");
53582
- const { requireChatId: requireChatId2 } = await import("./env-CWEGTJF3.js");
53583
- const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-EPF3HHJ4.js");
53584
- const { handleMetaError: handleMetaError2 } = await import("./shared-53G34I3U.js");
53634
+ const { apiPost: apiPost2 } = await import("./client-22ED7NUR.js");
53635
+ const { requireChatId: requireChatId2 } = await import("./env-E565GJBV.js");
53636
+ const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-F6NKGZ6E.js");
53637
+ const { handleMetaError: handleMetaError2 } = await import("./shared-YSUGIUS4.js");
53585
53638
  try {
53586
53639
  const accountId = bareAccountId2(args8);
53587
53640
  const urlTags = typeof args8["url-tags"] === "string" ? args8["url-tags"] : void 0;
@@ -53732,10 +53785,10 @@ function duplicateCommand2(entity, label) {
53732
53785
  replace: { type: "boolean", description: "Pause the original once the copy publishes" }
53733
53786
  },
53734
53787
  run: async ({ args: args8 }) => {
53735
- const { apiPost: apiPost2 } = await import("./client-AY6TPFQ4.js");
53736
- const { requireChatId: requireChatId2 } = await import("./env-CWEGTJF3.js");
53737
- const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-EPF3HHJ4.js");
53738
- const { handleMetaError: handleMetaError2 } = await import("./shared-53G34I3U.js");
53788
+ const { apiPost: apiPost2 } = await import("./client-22ED7NUR.js");
53789
+ const { requireChatId: requireChatId2 } = await import("./env-E565GJBV.js");
53790
+ const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-F6NKGZ6E.js");
53791
+ const { handleMetaError: handleMetaError2 } = await import("./shared-YSUGIUS4.js");
53739
53792
  try {
53740
53793
  const accountId = bareAccountId2(args8);
53741
53794
  const chatId = requireChatId2();
@@ -60290,7 +60343,7 @@ var SOURCE_KINDS = ["webhook", "hubspot"];
60290
60343
  var SOURCE_STATUSES = ["learning", "counting", "paused"];
60291
60344
  var SOURCE_NAME_MAX = 80;
60292
60345
  var sourceNameSchema = z49.string().trim().min(1).max(SOURCE_NAME_MAX).refine((name) => validateEventName(name) === null, REJECTION_MESSAGES.invalid_name);
60293
- var sourceIdSchema = z49.string().min(1);
60346
+ var sourceIdSchema2 = z49.string().min(1);
60294
60347
  var sourceMappingSchema = z49.record(z49.string(), z49.string().min(1)).superRefine((mapping, ctx) => {
60295
60348
  for (const row2 of Object.keys(mapping)) {
60296
60349
  const problem = mappingRowProblem(row2);
@@ -60388,7 +60441,7 @@ var sourcesRequestSchema = z49.strictObject({
60388
60441
  }).optional(),
60389
60442
  /** Change what a HubSpot event watches. It counts from now on under the new trigger; the rows stay. The object cannot change: the rows read it. */
60390
60443
  setTrigger: z49.strictObject({
60391
- sourceId: sourceIdSchema,
60444
+ sourceId: sourceIdSchema2,
60392
60445
  trigger: hubspotTriggerInputSchema,
60393
60446
  contacts: hubspotDealContactsSchema.optional()
60394
60447
  }).optional(),
@@ -60396,7 +60449,7 @@ var sourcesRequestSchema = z49.strictObject({
60396
60449
  preview: hubspotPreviewRequestSchema.optional(),
60397
60450
  /** Change where a HubSpot-read source takes its amount from; no `amountProperty` clears it. Sugar over `map`. */
60398
60451
  setAmount: z49.strictObject({
60399
- sourceId: sourceIdSchema,
60452
+ sourceId: sourceIdSchema2,
60400
60453
  amountProperty: z49.string().trim().min(1).optional(),
60401
60454
  currency: currencyCodeSchema.optional()
60402
60455
  }).optional(),
@@ -60410,7 +60463,7 @@ var sourcesRequestSchema = z49.strictObject({
60410
60463
  * has a timestamp for, so nothing is invented. Dry unless `write`.
60411
60464
  */
60412
60465
  backfill: z49.strictObject({
60413
- sourceId: sourceIdSchema,
60466
+ sourceId: sourceIdSchema2,
60414
60467
  /** ISO instant, or Unix milliseconds. Bounded by the 400 days the store keeps. */
60415
60468
  since: z49.union([z49.string().trim().min(1), z49.number()]),
60416
60469
  /**
@@ -60429,10 +60482,10 @@ var sourcesRequestSchema = z49.strictObject({
60429
60482
  /** Without it, nothing is counted and the answer says what would be. */
60430
60483
  write: z49.boolean().optional()
60431
60484
  }).optional(),
60432
- map: z49.strictObject({ sourceId: sourceIdSchema, mapping: sourceMappingSchema }).optional(),
60433
- pause: z49.strictObject({ sourceId: sourceIdSchema }).optional(),
60434
- resume: z49.strictObject({ sourceId: sourceIdSchema }).optional(),
60435
- remove: z49.strictObject({ sourceId: sourceIdSchema }).optional()
60485
+ map: z49.strictObject({ sourceId: sourceIdSchema2, mapping: sourceMappingSchema }).optional(),
60486
+ pause: z49.strictObject({ sourceId: sourceIdSchema2 }).optional(),
60487
+ resume: z49.strictObject({ sourceId: sourceIdSchema2 }).optional(),
60488
+ remove: z49.strictObject({ sourceId: sourceIdSchema2 }).optional()
60436
60489
  });
60437
60490
  var sourceRefusalSchema = z49.strictObject({
60438
60491
  at: z49.number(),
@@ -97334,7 +97387,7 @@ Full guide: __tooling__/docs/tools/baker/tags.md`
97334
97387
  });
97335
97388
 
97336
97389
  // src/commands/testimonials/index.ts
97337
- import { defineCommand as defineCommand258 } from "citty";
97390
+ import { defineCommand as defineCommand264 } from "citty";
97338
97391
 
97339
97392
  // src/commands/testimonials/get.ts
97340
97393
  import { defineCommand as defineCommand255 } from "citty";
@@ -97398,7 +97451,7 @@ function resolveEmptyReason({
97398
97451
  }
97399
97452
  return sourceCount === 0 ? "no_corpus_connected" : "corpus_empty";
97400
97453
  }
97401
- var NO_CORPUS_EXIT = 'Testimonials are ingested from the client\'s Google/Trustpilot reviews \u2014 an agent cannot write one \u2014 so no rewording will find quotes. Stop searching and source proof another way: a result or case study the client already published, a named-customer logo (`baker images logo`), or a founder claim attributed as a claim. Otherwise ship without a testimonial block and file the gap: `baker actions create --name "Collect customer quotes for <page>" --priority medium --tags <slug>`. Never invent, paraphrase-into-existence, or pad with unrelated praise.';
97454
+ var NO_CORPUS_EXIT = "Testimonials are ingested from the client's public review pages \u2014 an agent cannot write one \u2014 so no rewording will find quotes. If the user named the client's own review page (Google Maps, Trustpilot, Yelp, Tripadvisor, G2, Capterra, Amazon, App Store, Google Play), add it with `baker testimonials sources add <link>` \u2014 its reviews arrive over the next minutes, not in this turn. Stop searching and source proof another way: a result or case study the client already published, a named-customer logo (`baker images logo`), or a founder claim attributed as a claim. Otherwise ship without a testimonial block and file the gap: `baker actions create --name \"Collect customer quotes for <page>\" --priority medium --tags <slug>`. Never invent, paraphrase-into-existence, or pad with unrelated praise.";
97402
97455
  function emptyReasonDetail(reason, sourceCount, activeFilters) {
97403
97456
  if (reason === "filtered_out") {
97404
97457
  return `No rows matched, and ${activeFilters.join(", ")} narrowed this read. The corpus was not checked \u2014 drop every filter to see whether the library itself is empty.`;
@@ -97490,9 +97543,9 @@ registerSchema({
97490
97543
  args: {
97491
97544
  source: {
97492
97545
  type: "string",
97493
- description: "Filter by source: google|trustpilot",
97546
+ description: `Filter by source: ${testimonialSourceTypeSchema.options.join("|")}`,
97494
97547
  required: false,
97495
- enum: ["google", "trustpilot"]
97548
+ enum: [...testimonialSourceTypeSchema.options]
97496
97549
  },
97497
97550
  "rating-min": { type: "number", description: "Minimum star rating (1-5)", required: false },
97498
97551
  "rating-max": { type: "number", description: "Maximum star rating (1-5)", required: false },
@@ -97545,7 +97598,11 @@ var listCommand22 = defineCommand256({
97545
97598
  description: "List testimonials with optional filters. Example: baker testimonials list --source google --sentiment positive"
97546
97599
  },
97547
97600
  args: {
97548
- source: { type: "string", description: "Filter by source: google|trustpilot", required: false },
97601
+ source: {
97602
+ type: "string",
97603
+ description: `Filter by source: ${testimonialSourceTypeSchema.options.join("|")}`,
97604
+ required: false
97605
+ },
97549
97606
  "rating-min": { type: "string", description: "Minimum star rating (1-5)", required: false },
97550
97607
  "rating-max": { type: "string", description: "Maximum star rating (1-5)", required: false },
97551
97608
  status: { type: "string", description: "Filter by status: pending|processing|ready|error", required: false },
@@ -97628,9 +97685,9 @@ registerSchema({
97628
97685
  limit: { type: "number", description: "Maximum results to return", required: false, default: 10 },
97629
97686
  source: {
97630
97687
  type: "string",
97631
- description: "Filter by source: google|trustpilot",
97688
+ description: `Filter by source: ${testimonialSourceTypeSchema.options.join("|")}`,
97632
97689
  required: false,
97633
- enum: ["google", "trustpilot"]
97690
+ enum: [...testimonialSourceTypeSchema.options]
97634
97691
  },
97635
97692
  "rating-min": { type: "number", description: "Minimum star rating (1-5)", required: false },
97636
97693
  "rating-max": { type: "number", description: "Maximum star rating (1-5)", required: false },
@@ -97686,7 +97743,11 @@ var searchCommand4 = defineCommand257({
97686
97743
  args: {
97687
97744
  query: { type: "positional", description: "Search query text", required: false },
97688
97745
  limit: { type: "string", description: "Max results (default 10)", required: false },
97689
- source: { type: "string", description: "Filter by source: google|trustpilot", required: false },
97746
+ source: {
97747
+ type: "string",
97748
+ description: `Filter by source: ${testimonialSourceTypeSchema.options.join("|")}`,
97749
+ required: false
97750
+ },
97690
97751
  "rating-min": { type: "string", description: "Minimum star rating (1-5)", required: false },
97691
97752
  "rating-max": { type: "string", description: "Maximum star rating (1-5)", required: false },
97692
97753
  status: { type: "string", description: "Filter by status: pending|processing|ready|error", required: false },
@@ -97738,14 +97799,276 @@ var searchCommand4 = defineCommand257({
97738
97799
  }
97739
97800
  });
97740
97801
 
97802
+ // src/commands/testimonials/sources/index.ts
97803
+ import { defineCommand as defineCommand263 } from "citty";
97804
+
97805
+ // src/commands/testimonials/sources/add.ts
97806
+ import { defineCommand as defineCommand258 } from "citty";
97807
+
97808
+ // src/commands/testimonials/sources/shared.ts
97809
+ function reportError3(err) {
97810
+ if (err instanceof ApiError) {
97811
+ writeJson({ ok: false, error: { code: err.code, message: err.message } });
97812
+ process.exit(1);
97813
+ }
97814
+ writeJson({ ok: false, error: { code: "INTERNAL_ERROR", message: "Unexpected error" } });
97815
+ process.exit(1);
97816
+ }
97817
+ function reportInvalid(issue) {
97818
+ writeJson({
97819
+ ok: false,
97820
+ error: {
97821
+ code: "VALIDATION_ERROR",
97822
+ message: issue ? `${issue.path.map(String).join(".") || "input"}: ${issue.message}` : "Invalid request"
97823
+ }
97824
+ });
97825
+ process.exit(1);
97826
+ }
97827
+ function compactSource2(source) {
97828
+ return {
97829
+ id: source.id,
97830
+ platform: source.platform,
97831
+ label: source.label,
97832
+ ...source.pageUrl ? { url: source.pageUrl } : {},
97833
+ status: source.status,
97834
+ reviews: source.reviews,
97835
+ autoSync: source.refreshInterval,
97836
+ ...source.lastSyncedAt ? { lastSyncedAt: new Date(source.lastSyncedAt).toISOString() } : {},
97837
+ ...source.error ? { error: source.error } : {}
97838
+ };
97839
+ }
97840
+
97841
+ // src/commands/testimonials/sources/add.ts
97842
+ var SITES = testimonialSourceTypeSchema.options.join("|");
97843
+ registerSchema({
97844
+ command: "testimonials.sources.add",
97845
+ description: "Add a review page as a testimonial source \u2014 Google Maps, Trustpilot, Yelp, Tripadvisor, G2, Capterra, Amazon, App Store or Google Play. Pass the page's link (it names its own site), or a business name for Google Maps. Its reviews (up to 700) are imported, tagged and made searchable in the background over the next minutes. Each imported review costs a little, so add only the client's own listings the user asked for. Applies immediately and shows on the chat as an already-applied change. Adding a listing that is already a source is a no-op that starts a fresh import.",
97846
+ args: {
97847
+ input: {
97848
+ type: "string",
97849
+ description: "The review page's link, or a business name (Google Maps only)",
97850
+ required: true
97851
+ },
97852
+ platform: {
97853
+ type: "string",
97854
+ description: `Only to force a site: ${SITES}. A bare domain with trustpilot is that company's Trustpilot page.`,
97855
+ required: false
97856
+ },
97857
+ every: {
97858
+ type: "string",
97859
+ description: `Auto-sync cadence: ${testimonialSourceRefreshIntervalSchema.options.join("|")} (default never)`,
97860
+ required: false
97861
+ }
97862
+ }
97863
+ });
97864
+ function addSourceHints(input, { created, source }) {
97865
+ const hints2 = [
97866
+ created ? `Added ${source.label} on ${source.platformLabel}. Its reviews are importing in the background \u2014 they become searchable with \`baker testimonials search\` as each one is analysed, usually within a few minutes. Don't wait on them in this turn; check with \`baker testimonials sources list\`.` : `${source.label} on ${source.platformLabel} was already a source; a fresh import was started if one wasn't running.`
97867
+ ];
97868
+ if (created && source.platform === "google" && !/^https?:\/\/|\.[a-z]{2,}\//i.test(input.trim())) {
97869
+ hints2.push(
97870
+ `Matched the name to ${source.label} on Google Maps (${source.pageUrl ?? "no link"}). If that is not the client's business, run \`baker testimonials sources remove ${source.id}\` at once and add the listing's Google Maps link instead.`
97871
+ );
97872
+ }
97873
+ if (source.refreshInterval === "never") {
97874
+ hints2.push(
97875
+ `Auto-sync is off. If the user wants new reviews to keep arriving: \`baker testimonials sources schedule ${source.id} --every monthly\`.`
97876
+ );
97877
+ }
97878
+ return hints2;
97879
+ }
97880
+ var addCommand4 = defineCommand258({
97881
+ meta: {
97882
+ name: "add",
97883
+ description: `Add a review page as a testimonial source. Start here with the page's link; for Google Maps a business name also works.
97884
+
97885
+ Examples:
97886
+ baker testimonials sources add https://www.yelp.com/biz/acme-bakery-madrid
97887
+ baker testimonials sources add https://www.g2.com/products/acme --every monthly
97888
+ baker testimonials sources add "Acme Bakery Madrid" # Google Maps, found by name
97889
+ baker testimonials sources add acme.com --platform trustpilot`
97890
+ },
97891
+ args: {
97892
+ input: { type: "positional", description: "Review page link, or a business name for Google Maps", required: true },
97893
+ platform: { type: "string", description: `Force a site: ${SITES}`, required: false },
97894
+ every: { type: "string", description: "Auto-sync: never|daily|weekly|monthly", required: false }
97895
+ },
97896
+ run: async ({ args: args8 }) => {
97897
+ const parsed = testimonialSourcesAddRequestSchema.safeParse({
97898
+ input: String(args8.input ?? ""),
97899
+ platform: args8.platform ? String(args8.platform).toLowerCase().replace(/[\s-]/g, "_") : void 0,
97900
+ every: args8.every ? String(args8.every).toLowerCase() : void 0
97901
+ });
97902
+ if (!parsed.success) reportInvalid(parsed.error.issues[0]);
97903
+ try {
97904
+ const data = await apiPost("/api/testimonials/sources/add", parsed.data, {
97905
+ timeoutMs: 12e4
97906
+ });
97907
+ const hints2 = addSourceHints(parsed.data.input, data);
97908
+ writeJson({ ok: true, data: { created: data.created, source: compactSource2(data.source) }, hints: hints2 });
97909
+ } catch (err) {
97910
+ reportError3(err);
97911
+ }
97912
+ }
97913
+ });
97914
+
97915
+ // src/commands/testimonials/sources/list.ts
97916
+ import { defineCommand as defineCommand259 } from "citty";
97917
+ registerSchema({
97918
+ command: "testimonials.sources.list",
97919
+ description: "List the review pages feeding the testimonials library: each one's id, site, import status and how many reviews it has brought in.",
97920
+ args: {}
97921
+ });
97922
+ function listSourceHints(sources) {
97923
+ if (sources.length === 0) {
97924
+ return [
97925
+ "No review sources yet. If the client has a public review page (Google Maps, Trustpilot, Yelp, Tripadvisor, G2, Capterra, Amazon, App Store, Google Play), add it with `baker testimonials sources add <link>`."
97926
+ ];
97927
+ }
97928
+ const hints2 = sources.filter((source) => source.status === "error").map(
97929
+ (source) => `${source.label} on ${source.platformLabel} did not import: ${source.error ?? "the last import failed"} Retry once with \`baker testimonials sources sync ${source.id}\`; if it fails again, tell the user in plain words and carry on with the other sources.`
97930
+ );
97931
+ if (sources.some((source) => source.status === "importing")) {
97932
+ hints2.push("Some sources are still importing \u2014 their review counts will grow over the next minutes.");
97933
+ }
97934
+ return hints2;
97935
+ }
97936
+ var listCommand23 = defineCommand259({
97937
+ meta: {
97938
+ name: "list",
97939
+ description: "List testimonial sources (id, site, import status, reviews imported).\n\nExample: baker testimonials sources list"
97940
+ },
97941
+ args: {},
97942
+ run: async () => {
97943
+ try {
97944
+ const sources = await apiGet("/api/testimonials/sources/list");
97945
+ const hints2 = listSourceHints(sources);
97946
+ writeJson({ ok: true, data: sources.map(compactSource2), hints: hints2.length > 0 ? hints2 : void 0 });
97947
+ } catch (err) {
97948
+ reportError3(err);
97949
+ }
97950
+ }
97951
+ });
97952
+
97953
+ // src/commands/testimonials/sources/remove.ts
97954
+ import { defineCommand as defineCommand260 } from "citty";
97955
+ registerSchema({
97956
+ command: "testimonials.sources.remove",
97957
+ description: "Remove a review source AND every testimonial it imported (and their pictures). Applies immediately and cannot be undone \u2014 only when the user asked for it.",
97958
+ args: { id: { type: "string", description: "Source id from `testimonials sources list`", required: true } }
97959
+ });
97960
+ var removeCommand8 = defineCommand260({
97961
+ meta: {
97962
+ name: "remove",
97963
+ description: "Remove a review source and all its testimonials (cannot be undone).\n\nExample: baker testimonials sources remove <source-id>"
97964
+ },
97965
+ args: { id: { type: "positional", description: "Source id from `testimonials sources list`", required: true } },
97966
+ run: async ({ args: args8 }) => {
97967
+ const parsed = testimonialSourcesRemoveRequestSchema.safeParse({ id: String(args8.id ?? "") });
97968
+ if (!parsed.success) reportInvalid(parsed.error.issues[0]);
97969
+ try {
97970
+ const data = await apiPost("/api/testimonials/sources/remove", parsed.data);
97971
+ writeJson({ ok: true, data });
97972
+ } catch (err) {
97973
+ reportError3(err);
97974
+ }
97975
+ }
97976
+ });
97977
+
97978
+ // src/commands/testimonials/sources/schedule.ts
97979
+ import { defineCommand as defineCommand261 } from "citty";
97980
+ var INTERVALS = testimonialSourceRefreshIntervalSchema.options.join("|");
97981
+ registerSchema({
97982
+ command: "testimonials.sources.schedule",
97983
+ description: "Set how often a review source imports its new reviews on its own.",
97984
+ args: {
97985
+ id: { type: "string", description: "Source id from `testimonials sources list`", required: true },
97986
+ every: { type: "string", description: `Auto-sync cadence: ${INTERVALS}`, required: true }
97987
+ }
97988
+ });
97989
+ var scheduleCommand2 = defineCommand261({
97990
+ meta: {
97991
+ name: "schedule",
97992
+ description: "Set a review source's auto-sync cadence.\n\nExample: baker testimonials sources schedule <source-id> --every monthly"
97993
+ },
97994
+ args: {
97995
+ id: { type: "positional", description: "Source id from `testimonials sources list`", required: true },
97996
+ every: { type: "string", description: INTERVALS, required: true }
97997
+ },
97998
+ run: async ({ args: args8 }) => {
97999
+ const parsed = testimonialSourcesScheduleRequestSchema.safeParse({
98000
+ id: String(args8.id ?? ""),
98001
+ every: String(args8.every ?? "").toLowerCase()
98002
+ });
98003
+ if (!parsed.success) reportInvalid(parsed.error.issues[0]);
98004
+ try {
98005
+ const data = await apiPost("/api/testimonials/sources/schedule", parsed.data);
98006
+ writeJson({ ok: true, data: { id: data.id, autoSync: parsed.data.every } });
98007
+ } catch (err) {
98008
+ reportError3(err);
98009
+ }
98010
+ }
98011
+ });
98012
+
98013
+ // src/commands/testimonials/sources/sync.ts
98014
+ import { defineCommand as defineCommand262 } from "citty";
98015
+ registerSchema({
98016
+ command: "testimonials.sources.sync",
98017
+ description: "Import a review source's new reviews now. Only reviews newer than the last import are read where the site allows it.",
98018
+ args: { id: { type: "string", description: "Source id from `testimonials sources list`", required: true } }
98019
+ });
98020
+ var syncCommand2 = defineCommand262({
98021
+ meta: {
98022
+ name: "sync",
98023
+ description: "Import a review source's new reviews now.\n\nExample: baker testimonials sources sync <source-id>"
98024
+ },
98025
+ args: { id: { type: "positional", description: "Source id from `testimonials sources list`", required: true } },
98026
+ run: async ({ args: args8 }) => {
98027
+ const parsed = testimonialSourcesSyncRequestSchema.safeParse({ id: String(args8.id ?? "") });
98028
+ if (!parsed.success) reportInvalid(parsed.error.issues[0]);
98029
+ try {
98030
+ const data = await apiPost("/api/testimonials/sources/sync", parsed.data);
98031
+ writeJson({
98032
+ ok: true,
98033
+ data,
98034
+ hints: data.started ? void 0 : ["An import was already running for this source; nothing new was started."]
98035
+ });
98036
+ } catch (err) {
98037
+ reportError3(err);
98038
+ }
98039
+ }
98040
+ });
98041
+
98042
+ // src/commands/testimonials/sources/index.ts
98043
+ var sourcesCommand2 = defineCommand263({
98044
+ meta: {
98045
+ name: "sources",
98046
+ description: `The review pages the testimonials come from: Google Maps, Trustpilot, Yelp, Tripadvisor, G2, Capterra, Amazon, App Store, Google Play. Subcommands: add, list, sync, schedule, remove.
98047
+
98048
+ Examples:
98049
+ baker testimonials sources add https://www.trustpilot.com/review/acme.com
98050
+ baker testimonials sources list
98051
+ baker testimonials sources sync <source-id>
98052
+ baker testimonials sources schedule <source-id> --every monthly
98053
+ baker testimonials sources remove <source-id>`
98054
+ },
98055
+ subCommands: {
98056
+ add: addCommand4,
98057
+ list: listCommand23,
98058
+ sync: syncCommand2,
98059
+ schedule: scheduleCommand2,
98060
+ remove: removeCommand8
98061
+ }
98062
+ });
98063
+
97741
98064
  // src/commands/testimonials/tags.ts
97742
98065
  var tagsCommand5 = makeTagsCommand("testimonials", "testimonial", "/api/testimonials/tags");
97743
98066
 
97744
98067
  // src/commands/testimonials/index.ts
97745
- var testimonialsCommand = defineCommand258({
98068
+ var testimonialsCommand = defineCommand264({
97746
98069
  meta: {
97747
98070
  name: "testimonials",
97748
- description: `Find and browse testimonials in Baker. Subcommands: search, get, list, tags.
98071
+ description: `Find and browse testimonials in Baker, and manage the review pages they come from. Subcommands: search, get, list, tags, sources.
97749
98072
 
97750
98073
  Examples:
97751
98074
  baker testimonials search "great customer service" --limit 5
@@ -97753,21 +98076,23 @@ Examples:
97753
98076
  baker testimonials get <testimonial-id>
97754
98077
  baker testimonials list --source google --sentiment positive
97755
98078
  baker testimonials tags
98079
+ baker testimonials sources add https://www.yelp.com/biz/acme-bakery-madrid
97756
98080
  Full guide: __tooling__/docs/tools/baker/testimonials.md`
97757
98081
  },
97758
98082
  subCommands: {
97759
98083
  get: getCommand6,
97760
98084
  search: searchCommand4,
97761
98085
  list: listCommand22,
97762
- tags: tagsCommand5
98086
+ tags: tagsCommand5,
98087
+ sources: sourcesCommand2
97763
98088
  }
97764
98089
  });
97765
98090
 
97766
98091
  // src/commands/videos/index.ts
97767
- import { defineCommand as defineCommand267 } from "citty";
98092
+ import { defineCommand as defineCommand273 } from "citty";
97768
98093
 
97769
98094
  // src/commands/videos/delete.ts
97770
- import { defineCommand as defineCommand259 } from "citty";
98095
+ import { defineCommand as defineCommand265 } from "citty";
97771
98096
  registerSchema({
97772
98097
  command: "videos.delete",
97773
98098
  description: "Delete a video by ID",
@@ -97781,7 +98106,7 @@ registerSchema({
97781
98106
  }
97782
98107
  }
97783
98108
  });
97784
- var deleteCommand4 = defineCommand259({
98109
+ var deleteCommand4 = defineCommand265({
97785
98110
  meta: {
97786
98111
  name: "delete",
97787
98112
  description: "Delete a video by ID. Use --dry-run to preview. Example: baker videos delete j571abc123 --dry-run"
@@ -97822,7 +98147,7 @@ var deleteCommand4 = defineCommand259({
97822
98147
  });
97823
98148
 
97824
98149
  // src/commands/videos/describe.ts
97825
- import { defineCommand as defineCommand260 } from "citty";
98150
+ import { defineCommand as defineCommand266 } from "citty";
97826
98151
  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.";
97827
98152
  registerSchema({
97828
98153
  command: "videos.describe",
@@ -97839,7 +98164,7 @@ registerSchema({
97839
98164
  full: { type: "boolean", description: "Return the whole library row, not just what changed", required: false }
97840
98165
  }
97841
98166
  });
97842
- var describeCommand2 = defineCommand260({
98167
+ var describeCommand2 = defineCommand266({
97843
98168
  meta: {
97844
98169
  name: "describe",
97845
98170
  description: `Correct what the library says a video is. Only the fields you pass change; the rest keep their current values.
@@ -97901,7 +98226,7 @@ import { constants } from "fs";
97901
98226
  import { copyFile as copyFile2, mkdir as mkdir17, mkdtemp as mkdtemp3, rm as rm10 } from "fs/promises";
97902
98227
  import { tmpdir as tmpdir4 } from "os";
97903
98228
  import path56 from "path";
97904
- import { defineCommand as defineCommand261 } from "citty";
98229
+ import { defineCommand as defineCommand267 } from "citty";
97905
98230
 
97906
98231
  // src/commands/videos/videoPlatforms.ts
97907
98232
  var PLATFORM_DOMAINS = {
@@ -98039,7 +98364,7 @@ function positiveInt(value2) {
98039
98364
  const parsed = typeof value2 === "string" ? Number.parseInt(value2, 10) : Number.NaN;
98040
98365
  return Number.isInteger(parsed) && parsed > 0 ? parsed : void 0;
98041
98366
  }
98042
- var fetchCommand2 = defineCommand261({
98367
+ var fetchCommand2 = defineCommand267({
98043
98368
  meta: {
98044
98369
  name: "fetch",
98045
98370
  description: "Download a video page to a local .mp4 without adding it to the library. Same downloader as `ingest` (proxy fallback, duration check).\n\nExample: baker videos fetch https://www.youtube.com/watch?v=abc123 --out ./clips"
@@ -98125,7 +98450,7 @@ async function placeInto(outDir, source, stem) {
98125
98450
  }
98126
98451
 
98127
98452
  // src/commands/videos/get.ts
98128
- import { defineCommand as defineCommand262 } from "citty";
98453
+ import { defineCommand as defineCommand268 } from "citty";
98129
98454
  registerSchema({
98130
98455
  command: "videos.get",
98131
98456
  description: "Get a single video by ID",
@@ -98133,7 +98458,7 @@ registerSchema({
98133
98458
  id: { type: "string", description: "Video ID", required: true }
98134
98459
  }
98135
98460
  });
98136
- var getCommand7 = defineCommand262({
98461
+ var getCommand7 = defineCommand268({
98137
98462
  meta: { name: "get", description: "Get a single video by ID. Example: baker videos get j571abc123" },
98138
98463
  args: {
98139
98464
  id: { type: "positional", description: "Video ID", required: false },
@@ -98170,7 +98495,7 @@ var getCommand7 = defineCommand262({
98170
98495
  });
98171
98496
 
98172
98497
  // src/commands/videos/group.ts
98173
- import { defineCommand as defineCommand263 } from "citty";
98498
+ import { defineCommand as defineCommand269 } from "citty";
98174
98499
  registerSchema({
98175
98500
  command: "videos.group",
98176
98501
  description: "List every clip and image that arrived in the same set as this video (carousel slides, one page)",
@@ -98179,7 +98504,7 @@ registerSchema({
98179
98504
  "group-key": { type: "string", description: "The set key directly, when you already have it", required: false }
98180
98505
  }
98181
98506
  });
98182
- var groupCommand2 = defineCommand263({
98507
+ var groupCommand2 = defineCommand269({
98183
98508
  meta: {
98184
98509
  name: "group",
98185
98510
  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>"
@@ -98202,7 +98527,7 @@ var groupCommand2 = defineCommand263({
98202
98527
  import { mkdtemp as mkdtemp4, rm as rm11, stat as stat12 } from "fs/promises";
98203
98528
  import { tmpdir as tmpdir5 } from "os";
98204
98529
  import path57 from "path";
98205
- import { defineCommand as defineCommand264 } from "citty";
98530
+ import { defineCommand as defineCommand270 } from "citty";
98206
98531
 
98207
98532
  // src/lib/streamUpload.ts
98208
98533
  import { createHash as createHash3 } from "crypto";
@@ -98255,7 +98580,7 @@ registerSchema({
98255
98580
  "dry-run": { type: "boolean", description: "Preview the operation without executing", required: false }
98256
98581
  }
98257
98582
  });
98258
- var ingestCommand2 = defineCommand264({
98583
+ var ingestCommand2 = defineCommand270({
98259
98584
  meta: {
98260
98585
  name: "ingest",
98261
98586
  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"
@@ -98517,7 +98842,7 @@ async function uploadToAssetStore(filePath, sizeBytes) {
98517
98842
  }
98518
98843
 
98519
98844
  // src/commands/videos/search.ts
98520
- import { defineCommand as defineCommand265 } from "citty";
98845
+ import { defineCommand as defineCommand271 } from "citty";
98521
98846
  registerSchema({
98522
98847
  command: "videos.search",
98523
98848
  description: "Search videos by text query. Only returns ready videos.",
@@ -98527,7 +98852,23 @@ registerSchema({
98527
98852
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
98528
98853
  }
98529
98854
  });
98530
- var searchCommand5 = defineCommand265({
98855
+ function searchHints(results) {
98856
+ const hints2 = [];
98857
+ const parody = results.filter((r) => r.analysis?.tone === "parody").map((r) => r._id);
98858
+ if (parody.length > 0) {
98859
+ hints2.push(
98860
+ `tone: parody \u2014 ${parody.join(", ")}. These play the offer for laughs: fine for a social post, wrong as proof or on a landing page.`
98861
+ );
98862
+ }
98863
+ const unchecked = results.filter((r) => r.analysis?.tone === void 0).map((r) => r._id);
98864
+ if (unchecked.length > 0) {
98865
+ hints2.push(
98866
+ `No format/tone/productClaims yet \u2014 ${unchecked.join(", ")}. Before putting one on a page, read its transcript, scenes and people (\`baker videos get <id> --full\`) and judge it yourself: is it about the offer, is it sincere, and who is speaking?`
98867
+ );
98868
+ }
98869
+ return hints2.length > 0 ? hints2 : void 0;
98870
+ }
98871
+ var searchCommand5 = defineCommand271({
98531
98872
  meta: {
98532
98873
  name: "search",
98533
98874
  description: "Semantic search videos by text query. Uses hybrid BM25 + vector + reranking. Example: baker videos search 'product demo' --tags tutorial"
@@ -98556,7 +98897,7 @@ var searchCommand5 = defineCommand265({
98556
98897
  }
98557
98898
  const data = await apiPost("/api/videos/search", body);
98558
98899
  writeOutput(
98559
- { ok: true, data },
98900
+ { ok: true, data, hints: searchHints(data) },
98560
98901
  args8.output || "json",
98561
98902
  args8.fields ? args8.fields.split(",") : void 0,
98562
98903
  args8.full,
@@ -98579,7 +98920,7 @@ var tagsCommand6 = makeTagsCommand("videos", "video", "/api/videos/tags");
98579
98920
  // src/commands/videos/upload.ts
98580
98921
  import { stat as stat13 } from "fs/promises";
98581
98922
  import { basename as basename4 } from "path";
98582
- import { defineCommand as defineCommand266 } from "citty";
98923
+ import { defineCommand as defineCommand272 } from "citty";
98583
98924
  registerSchema({
98584
98925
  command: "videos.upload",
98585
98926
  description: "Upload a video to Baker \u2014 a local file (via Mux direct upload) or a remote http(s) URL.",
@@ -98606,7 +98947,7 @@ registerSchema({
98606
98947
  function isRemoteUrl3(value2) {
98607
98948
  return /^https?:\/\//i.test(value2);
98608
98949
  }
98609
- var uploadCommand2 = defineCommand266({
98950
+ var uploadCommand2 = defineCommand272({
98610
98951
  meta: {
98611
98952
  name: "upload",
98612
98953
  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"
@@ -98675,7 +99016,7 @@ var uploadCommand2 = defineCommand266({
98675
99016
  });
98676
99017
 
98677
99018
  // src/commands/videos/index.ts
98678
- var videosCommand = defineCommand267({
99019
+ var videosCommand = defineCommand273({
98679
99020
  meta: {
98680
99021
  name: "videos",
98681
99022
  description: `Find and manage videos in Baker. Subcommands: search, get, upload, ingest, fetch, describe, delete, tags.
@@ -98706,10 +99047,10 @@ Full guide: __tooling__/docs/tools/baker/videos.md`
98706
99047
  });
98707
99048
 
98708
99049
  // src/commands/winning-ads/index.ts
98709
- import { defineCommand as defineCommand278 } from "citty";
99050
+ import { defineCommand as defineCommand284 } from "citty";
98710
99051
 
98711
99052
  // src/commands/winning-ads/advertisers.ts
98712
- import { defineCommand as defineCommand268 } from "citty";
99053
+ import { defineCommand as defineCommand274 } from "citty";
98713
99054
 
98714
99055
  // src/commands/winning-ads/shared.ts
98715
99056
  function splitList4(value2) {
@@ -98718,7 +99059,7 @@ function splitList4(value2) {
98718
99059
  }
98719
99060
  return value2.split(",").map((v) => v.trim()).filter(Boolean);
98720
99061
  }
98721
- function reportError3(err) {
99062
+ function reportError4(err) {
98722
99063
  if (err instanceof ApiError) {
98723
99064
  writeJson({ ok: false, error: { code: err.code, message: err.message } });
98724
99065
  process.exit(1);
@@ -98762,7 +99103,7 @@ function advertiserNormalizer(record, full) {
98762
99103
  last_synced_at: record.last_synced_at ?? null
98763
99104
  };
98764
99105
  }
98765
- var advertisersCommand2 = defineCommand268({
99106
+ var advertisersCommand2 = defineCommand274({
98766
99107
  meta: {
98767
99108
  name: "advertisers",
98768
99109
  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'
@@ -98814,13 +99155,13 @@ var advertisersCommand2 = defineCommand268({
98814
99155
  advertiserNormalizer
98815
99156
  );
98816
99157
  } catch (err) {
98817
- reportError3(err);
99158
+ reportError4(err);
98818
99159
  }
98819
99160
  }
98820
99161
  });
98821
99162
 
98822
99163
  // src/commands/winning-ads/content.ts
98823
- import { defineCommand as defineCommand269 } from "citty";
99164
+ import { defineCommand as defineCommand275 } from "citty";
98824
99165
  registerSchema({
98825
99166
  command: "winning-ads.content",
98826
99167
  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. Static and carousel ads return the text drawn into the image with the job each piece does (headline, benefit, cta\u2026), the card it sits on, and their format_pattern. Add --full for how it is built (composition, colours, CTA, markers), how it persuades (awareness, mechanism, pains, desires, proof, objections), what it offers (offer type, price, guarantee, urgency), plus speech, pacing and soundtrack for video.",
@@ -98833,7 +99174,7 @@ registerSchema({
98833
99174
  }
98834
99175
  }
98835
99176
  });
98836
- var contentCommand = defineCommand269({
99177
+ var contentCommand = defineCommand275({
98837
99178
  meta: {
98838
99179
  name: "content",
98839
99180
  description: "Read the transcript + on-screen text + copy of one winning ad; --full adds its build, persuasion and offer. Example: baker winning-ads content adg_123 --platform meta --full --output md"
@@ -98876,13 +99217,13 @@ var contentCommand = defineCommand269({
98876
99217
  adContentNormalizer
98877
99218
  );
98878
99219
  } catch (err) {
98879
- reportError3(err);
99220
+ reportError4(err);
98880
99221
  }
98881
99222
  }
98882
99223
  });
98883
99224
 
98884
99225
  // src/commands/winning-ads/feed.ts
98885
- import { defineCommand as defineCommand270 } from "citty";
99226
+ import { defineCommand as defineCommand276 } from "citty";
98886
99227
  function buildFeedParams(input) {
98887
99228
  const params = {};
98888
99229
  const advertiser = splitList4(input.advertiser);
@@ -98934,7 +99275,7 @@ registerSchema({
98934
99275
  format: { type: "string", description: "Comma-separated formats to include (e.g. static,video)", required: false }
98935
99276
  }
98936
99277
  });
98937
- var feedCommand2 = defineCommand270({
99278
+ var feedCommand2 = defineCommand276({
98938
99279
  meta: {
98939
99280
  name: "feed",
98940
99281
  description: "Winners across every brand you follow (browse, then trim per advertiser). Example: baker winning-ads feed --per-advertiser 5 --output md"
@@ -99013,13 +99354,13 @@ var feedCommand2 = defineCommand270({
99013
99354
  `);
99014
99355
  }
99015
99356
  } catch (err) {
99016
- reportError3(err);
99357
+ reportError4(err);
99017
99358
  }
99018
99359
  }
99019
99360
  });
99020
99361
 
99021
99362
  // src/commands/winning-ads/follow.ts
99022
- import { defineCommand as defineCommand271 } from "citty";
99363
+ import { defineCommand as defineCommand277 } from "citty";
99023
99364
  var PLATFORMS = adLibraryPlatformSchema.options;
99024
99365
  registerSchema({
99025
99366
  command: "winning-ads.follow",
@@ -99034,7 +99375,7 @@ registerSchema({
99034
99375
  label: { type: "string", description: "Optional display label (defaults to the resolved name)", required: false }
99035
99376
  }
99036
99377
  });
99037
- var followCommand = defineCommand271({
99378
+ var followCommand = defineCommand277({
99038
99379
  meta: {
99039
99380
  name: "follow",
99040
99381
  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'
@@ -99075,13 +99416,13 @@ var followCommand = defineCommand271({
99075
99416
  }
99076
99417
  writeJson({ ok: true, data, hints: hints2 });
99077
99418
  } catch (err) {
99078
- reportError3(err);
99419
+ reportError4(err);
99079
99420
  }
99080
99421
  }
99081
99422
  });
99082
99423
 
99083
99424
  // src/commands/winning-ads/follow-competitors.ts
99084
- import { defineCommand as defineCommand272 } from "citty";
99425
+ import { defineCommand as defineCommand278 } from "citty";
99085
99426
  var PLATFORMS2 = adLibraryPlatformSchema.options;
99086
99427
  var BATCH_TIMEOUT_MS = 3e5;
99087
99428
  function buildFollowBatchBody(input) {
@@ -99114,7 +99455,7 @@ registerSchema({
99114
99455
  }
99115
99456
  }
99116
99457
  });
99117
- var followCompetitorsCommand = defineCommand272({
99458
+ var followCompetitorsCommand = defineCommand278({
99118
99459
  meta: {
99119
99460
  name: "follow-competitors",
99120
99461
  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"'
@@ -99183,13 +99524,13 @@ var followCompetitorsCommand = defineCommand272({
99183
99524
  }
99184
99525
  writeJson({ ok: true, data, hints: hints2.length > 0 ? hints2 : void 0 });
99185
99526
  } catch (err) {
99186
- reportError3(err);
99527
+ reportError4(err);
99187
99528
  }
99188
99529
  }
99189
99530
  });
99190
99531
 
99191
99532
  // src/commands/winning-ads/following.ts
99192
- import { defineCommand as defineCommand273 } from "citty";
99533
+ import { defineCommand as defineCommand279 } from "citty";
99193
99534
  registerSchema({
99194
99535
  command: "winning-ads.following",
99195
99536
  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.",
@@ -99243,7 +99584,7 @@ function followingNormalizer(record, full) {
99243
99584
  platforms: Array.isArray(record.platforms) ? record.platforms : []
99244
99585
  };
99245
99586
  }
99246
- var followingCommand = defineCommand273({
99587
+ var followingCommand = defineCommand279({
99247
99588
  meta: {
99248
99589
  name: "following",
99249
99590
  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"
@@ -99273,13 +99614,13 @@ var followingCommand = defineCommand273({
99273
99614
  followingNormalizer
99274
99615
  );
99275
99616
  } catch (err) {
99276
- reportError3(err);
99617
+ reportError4(err);
99277
99618
  }
99278
99619
  }
99279
99620
  });
99280
99621
 
99281
99622
  // src/commands/winning-ads/search.ts
99282
- import { defineCommand as defineCommand274 } from "citty";
99623
+ import { defineCommand as defineCommand280 } from "citty";
99283
99624
  registerSchema({
99284
99625
  command: "winning-ads.search",
99285
99626
  description: "Search the ad-dna corpus of scored winning ads, the company's followed brands first. Returns a lean shortlist (advertiser, summary, scores, days_active/active/winner_category as proof, media_url, and visual/container/mechanism when known) to pick a reference to reproduce. Each search is a paid reranker + judge call: one strong search, at most one adjusted retry. A value outside a closed list (--platform, --format, --winner-category, --awareness, --format-pattern, --sort) is refused with error.fix.validValues.",
@@ -99420,7 +99761,7 @@ function buildSearchBody2(args8) {
99420
99761
  }
99421
99762
  return body;
99422
99763
  }
99423
- function searchHints(data) {
99764
+ function searchHints2(data) {
99424
99765
  const hints2 = [];
99425
99766
  if ((data.following_count ?? 0) === 0) {
99426
99767
  hints2.push(
@@ -99465,7 +99806,7 @@ function reportSearchError(err) {
99465
99806
  writeJson({ ok: false, error: { code: err.code, message: err.message, fix } });
99466
99807
  process.exit(1);
99467
99808
  }
99468
- reportError3(err);
99809
+ reportError4(err);
99469
99810
  }
99470
99811
  var FLAG_FOR_FIELD = {
99471
99812
  limit: "--limit",
@@ -99535,7 +99876,7 @@ function searchRejection(args8, body) {
99535
99876
  }
99536
99877
  function writeSearchResult(data, opts) {
99537
99878
  const rawResults = Array.isArray(data?.results) ? data.results : [];
99538
- const hints2 = searchHints(data ?? {});
99879
+ const hints2 = searchHints2(data ?? {});
99539
99880
  const withHints2 = hints2.length > 0 ? { hints: hints2 } : {};
99540
99881
  if (opts.output === "json") {
99541
99882
  const results = rawResults.map((r) => winningAdNormalizer(r, opts.full));
@@ -99557,7 +99898,7 @@ function writeSearchResult(data, opts) {
99557
99898
  writeOutput({ ok: true, data: rawResults, ...withHints2 }, opts.output, opts.fields, opts.full, winningAdNormalizer);
99558
99899
  }
99559
99900
  var SEARCH_TIMEOUT_MS = 12e4;
99560
- var searchCommand6 = defineCommand274({
99901
+ var searchCommand6 = defineCommand280({
99561
99902
  meta: {
99562
99903
  name: "search",
99563
99904
  description: "Search winning reference ads, the company's followed brands first. Start here: baker winning-ads search 'B2B SaaS before/after AI automation' --platform meta --format static --exclude-advertiser adv_123 --output md. A set of references for N concepts in one call: add --diverse --limit N. The best proven ad in one container from any category: --format-pattern comparison_table (no query needed; add --sort longest_running to order by staying power). Each search is a paid reranker + judge call: one strong search, at most one adjusted retry."
@@ -99683,7 +100024,7 @@ var searchCommand6 = defineCommand274({
99683
100024
  });
99684
100025
 
99685
100026
  // src/commands/winning-ads/seeds.ts
99686
- import { defineCommand as defineCommand275 } from "citty";
100027
+ import { defineCommand as defineCommand281 } from "citty";
99687
100028
  function leanRow(r) {
99688
100029
  return {
99689
100030
  key: r.key,
@@ -99711,7 +100052,7 @@ function makeSeedCommand(opts) {
99711
100052
  limit: { type: "number", description: "Max keys 1-100 (default 20)", required: false, default: 20 }
99712
100053
  }
99713
100054
  });
99714
- return defineCommand275({
100055
+ return defineCommand281({
99715
100056
  meta: { name: opts.name, description: opts.description },
99716
100057
  args: {
99717
100058
  platform: { type: "string", description: "Single platform to segment on", required: false },
@@ -99738,7 +100079,7 @@ function makeSeedCommand(opts) {
99738
100079
  }
99739
100080
  writeOutput({ ok: true, data: projected }, output);
99740
100081
  } catch (err) {
99741
- reportError3(err);
100082
+ reportError4(err);
99742
100083
  }
99743
100084
  }
99744
100085
  });
@@ -99760,7 +100101,7 @@ var formatsCommand = makeSeedCommand({
99760
100101
  });
99761
100102
 
99762
100103
  // src/commands/winning-ads/unfollow.ts
99763
- import { defineCommand as defineCommand276 } from "citty";
100104
+ import { defineCommand as defineCommand282 } from "citty";
99764
100105
  registerSchema({
99765
100106
  command: "winning-ads.unfollow",
99766
100107
  description: "Stop following a brand \u2014 removes it from your ad-dna library by advertiser id.",
@@ -99768,7 +100109,7 @@ registerSchema({
99768
100109
  advertiser: { type: "string", description: "Advertiser id to unfollow", required: true }
99769
100110
  }
99770
100111
  });
99771
- var unfollowCommand = defineCommand276({
100112
+ var unfollowCommand = defineCommand282({
99772
100113
  meta: {
99773
100114
  name: "unfollow",
99774
100115
  description: "Stop following a brand by advertiser id. Example: baker winning-ads unfollow adv_123"
@@ -99783,13 +100124,13 @@ var unfollowCommand = defineCommand276({
99783
100124
  });
99784
100125
  writeJson({ ok: true, data });
99785
100126
  } catch (err) {
99786
- reportError3(err);
100127
+ reportError4(err);
99787
100128
  }
99788
100129
  }
99789
100130
  });
99790
100131
 
99791
100132
  // src/commands/winning-ads/winners.ts
99792
- import { defineCommand as defineCommand277 } from "citty";
100133
+ import { defineCommand as defineCommand283 } from "citty";
99793
100134
  registerSchema({
99794
100135
  command: "winning-ads.winners",
99795
100136
  description: "Top winning ads for one advertiser id (from `advertisers` or `following`). Returns lean winner cards; add --full for DNA + longevity.",
@@ -99799,7 +100140,7 @@ registerSchema({
99799
100140
  platform: { type: "string", description: "Filter to a single platform: meta|linkedin|tiktok", required: false }
99800
100141
  }
99801
100142
  });
99802
- var winnersCommand = defineCommand277({
100143
+ var winnersCommand = defineCommand283({
99803
100144
  meta: {
99804
100145
  name: "winners",
99805
100146
  description: "Top winning ads for a specific advertiser id. Example: baker winning-ads winners adv_123 --top 15 --output md"
@@ -99843,13 +100184,13 @@ var winnersCommand = defineCommand277({
99843
100184
  winningAdNormalizer
99844
100185
  );
99845
100186
  } catch (err) {
99846
- reportError3(err);
100187
+ reportError4(err);
99847
100188
  }
99848
100189
  }
99849
100190
  });
99850
100191
 
99851
100192
  // src/commands/winning-ads/index.ts
99852
- var winningAdsCommand = defineCommand278({
100193
+ var winningAdsCommand = defineCommand284({
99853
100194
  meta: {
99854
100195
  name: "winning-ads",
99855
100196
  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.
@@ -99917,7 +100258,7 @@ function getCliVersion() {
99917
100258
  }
99918
100259
 
99919
100260
  // src/cli.ts
99920
- var main = defineCommand279({
100261
+ var main = defineCommand285({
99921
100262
  meta: {
99922
100263
  name: "baker",
99923
100264
  version: getCliVersion(),