@koda-sl/baker-cli 0.306.0-dev.2b6124eb2 → 0.306.0-dev.bb28562ac

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
@@ -67,7 +67,7 @@ import {
67
67
  validateCanvasDeep,
68
68
  withoutCastName,
69
69
  ytDlpBlockSignal
70
- } from "./chunk-UBNLCVGN.js";
70
+ } from "./chunk-3TEVIAAV.js";
71
71
  import {
72
72
  csvOrJson,
73
73
  daysAgoIso,
@@ -120,7 +120,7 @@ import {
120
120
  } from "./chunk-WFWU3CHS.js";
121
121
 
122
122
  // src/cli.ts
123
- import { defineCommand as defineCommand238, runMain } from "citty";
123
+ import { defineCommand as defineCommand240, runMain } from "citty";
124
124
 
125
125
  // src/cache-flag.ts
126
126
  var NO_CACHE_ARG = {
@@ -1438,6 +1438,7 @@ var TEMP_REF_PREFIX = "li_temp_";
1438
1438
  var TEMP_REF_REGEX = /^li_temp_[A-Za-z0-9_-]{4,}$/;
1439
1439
  var NUMERIC_ID_REGEX = /^\d+$/;
1440
1440
  var URN_REGEX = /^urn:li:[a-zA-Z]+:.+$/;
1441
+ var CONVERSION_URN_REGEX = /^urn:lla:llaPartnerConversion:\d+$/;
1441
1442
  var SPONSORED_ACCOUNT_URN_REGEX = /^urn:li:sponsoredAccount:\d+$/;
1442
1443
  var IMAGE_URN_REGEX = /^urn:li:image:.+$/;
1443
1444
  var VIDEO_URN_REGEX = /^urn:li:video:.+$/;
@@ -2017,12 +2018,44 @@ var LINKEDIN_DRAFT_OP_KINDS = [
2017
2018
  "audience.uploadList",
2018
2019
  "conversion.create",
2019
2020
  "conversion.update",
2021
+ "conversion.associateCampaign",
2022
+ "conversion.dissociateCampaign",
2020
2023
  "leadForm.create",
2021
2024
  "leadForm.update"
2022
2025
  ];
2023
2026
  var linkedinDraftOpKindSchema = z3.enum(LINKEDIN_DRAFT_OP_KINDS);
2024
2027
  var accountIdSchema = z3.string().regex(NUMERIC_ID_REGEX, "accountId must be the bare numeric ad account id");
2025
2028
  var updateTargetSchema = z3.union([z3.string().regex(URN_REGEX), z3.string().regex(NUMERIC_ID_REGEX), tempRefSchema]);
2029
+ var conversionCampaignTargetSchema = z3.union([
2030
+ // LinkedIn's own namespace for a conversion rule is `urn:lla:` — it is what
2031
+ // the `campaignConversions` key and the CAPI payload use, and what LinkedIn's
2032
+ // docs show — while `conversions list` prints Baker's `urn:li:` spelling of
2033
+ // the same rule. `URN_REGEX` is urn:li: only, so accept the other one here
2034
+ // rather than have the agent translate between two names for one rule.
2035
+ z3.string().regex(CONVERSION_URN_REGEX),
2036
+ z3.string().regex(URN_REGEX),
2037
+ z3.string().regex(NUMERIC_ID_REGEX),
2038
+ tempRefSchema
2039
+ ]);
2040
+ var conversionCampaignSchema = z3.object({
2041
+ campaign: z3.union([z3.string().regex(URN_REGEX), z3.string().regex(NUMERIC_ID_REGEX), tempRefSchema])
2042
+ });
2043
+ function conversionCampaignOp(kind) {
2044
+ return z3.object({
2045
+ kind: z3.literal(kind),
2046
+ accountId: accountIdSchema,
2047
+ target: conversionCampaignTargetSchema,
2048
+ payload: conversionCampaignSchema
2049
+ }).superRefine((op, ctx) => {
2050
+ if (TEMP_REF_REGEX.test(op.target)) {
2051
+ ctx.addIssue({
2052
+ code: "custom",
2053
+ path: ["target"],
2054
+ message: "a conversion rule staged in this same chat has no id yet, so it cannot be linked to a campaign \u2014 stage the rule with associateAllCampaigns, or link it once this chat has published"
2055
+ });
2056
+ }
2057
+ });
2058
+ }
2026
2059
  function createOp(kind, payload) {
2027
2060
  return z3.object({ kind: z3.literal(kind), accountId: accountIdSchema, payload });
2028
2061
  }
@@ -2050,6 +2083,8 @@ var linkedinDraftOpInputSchema = z3.discriminatedUnion("kind", [
2050
2083
  createOp("audience.uploadList", audienceUploadListSchema),
2051
2084
  createOp("conversion.create", conversionCreateSchema),
2052
2085
  updateOp("conversion.update", conversionUpdateSchema),
2086
+ conversionCampaignOp("conversion.associateCampaign"),
2087
+ conversionCampaignOp("conversion.dissociateCampaign"),
2053
2088
  createOp("leadForm.create", leadFormCreateSchema),
2054
2089
  updateOp("leadForm.update", leadFormUpdateSchema)
2055
2090
  ]);
@@ -6185,6 +6220,24 @@ var imagesGroupResponseSchema = z19.object({
6185
6220
  });
6186
6221
  var imagesDeleteRequestSchema = z19.object({ id: z19.string().min(1, "Missing image ID") });
6187
6222
  var imagesDeleteResponseSchema = z19.object({ ok: z19.literal(true) });
6223
+ var imagesDescribeRequestSchema = z19.object({
6224
+ id: z19.string().min(1, "Missing image ID"),
6225
+ name: z19.string().min(1).optional(),
6226
+ description: z19.string().min(1).optional(),
6227
+ tags: z19.array(z19.string()).optional(),
6228
+ /** Return the whole library row alongside the three fields that changed. */
6229
+ full: z19.boolean().optional()
6230
+ }).refine((body) => body.name !== void 0 || body.description !== void 0 || body.tags !== void 0, {
6231
+ message: "Pass at least one of name, description or tags"
6232
+ });
6233
+ var imagesDescribeResponseSchema = z19.object({
6234
+ id: z19.string(),
6235
+ name: z19.string(),
6236
+ description: z19.string(),
6237
+ tags: z19.array(z19.string()),
6238
+ /** Present only when the request asked for it. */
6239
+ image: imagesGetResponseSchema.optional()
6240
+ });
6188
6241
  var imagesUpscaleRequestSchema = z19.object({ imageId: z19.string().min(1, "Missing image ID") });
6189
6242
  var imagesUpscaleResponseSchema = z19.object({
6190
6243
  imageId: z19.string(),
@@ -7112,6 +7165,24 @@ var videosIngestResponseSchema = z25.object({
7112
7165
  videoId: z25.string(),
7113
7166
  deduped: z25.boolean()
7114
7167
  });
7168
+ var videosDescribeRequestSchema = z25.object({
7169
+ id: z25.string().min(1, "Missing video ID"),
7170
+ name: z25.string().min(1).optional(),
7171
+ description: z25.string().min(1).optional(),
7172
+ tags: z25.array(z25.string()).optional(),
7173
+ /** Return the whole library row alongside the three fields that changed. */
7174
+ full: z25.boolean().optional()
7175
+ }).refine((body) => body.name !== void 0 || body.description !== void 0 || body.tags !== void 0, {
7176
+ message: "Pass at least one of name, description or tags"
7177
+ });
7178
+ var videosDescribeResponseSchema = z25.object({
7179
+ id: z25.string(),
7180
+ name: z25.string(),
7181
+ description: z25.string(),
7182
+ tags: z25.array(z25.string()),
7183
+ /** Present only when the request asked for it. */
7184
+ video: videosGetResponseSchema.optional()
7185
+ });
7115
7186
  var videosDeleteRequestSchema = z25.object({ id: z25.string().min(1, "Missing video ID") });
7116
7187
  var videosDeleteResponseSchema = z25.object({ ok: z25.literal(true) });
7117
7188
 
@@ -7337,13 +7408,29 @@ var SURFACE_RULES = [
7337
7408
  /\bhotjar\b/i,
7338
7409
  /\bgtm\s+snippet\b/i,
7339
7410
  /\bcapi\b/i,
7340
- /\b(?:install|add|remove|swap|instalar|colocar)\b[\s\S]*\b(?:snippet|script|tag)\b/i
7411
+ /\b(?:install|add|remove|swap|instalar|colocar|poner|anadir|agregar)\b[\s\S]*\b(?:snippet|script|tags?|etiquetas?|pixel)\b/i,
7412
+ // The product's own word for this object, in both languages. A tracking
7413
+ // script named in Spanish ("el código de seguimiento de HubSpot", "una
7414
+ // etiqueta tipo hubspot") matched none of the signals above, so the one
7415
+ // Task that most needed the redirect — file it, or show the form? — was
7416
+ // filed. `tag-manager` is evaluated first and still claims the container.
7417
+ /\btracking\s+(?:code|script|snippet|tag)\b/i,
7418
+ /\bcodigos?\s+de\s+seguimiento\b/i,
7419
+ /\bscripts?\s+de\s+seguimiento\b/i,
7420
+ /\betiquetas?\s+(?:de\s+seguimiento|de\s+medicion|tipo\s+\w+)\b/i
7341
7421
  ],
7342
7422
  // Two vetoes. A structured snippet is a Google Ads extension, not a script
7343
7423
  // on the page — `ads-write` claims it above, and this stops both rules
7344
7424
  // being able to answer. Server-side delivery is infrastructure nobody here
7345
7425
  // can provision, the same call `tag-manager` already makes for sGTM.
7346
- except: [/\bstructured\s+snippets?\b/i, /\bserver-?\s?side\b/i, /\bhosting\b/i],
7426
+ except: [
7427
+ /\bstructured\s+snippets?\b/i,
7428
+ /\bserver-?\s?side\b/i,
7429
+ /\bhosting\b/i,
7430
+ // A label on a campaign is an ad-platform object, and "etiqueta" is the
7431
+ // Spanish for both it and the script this rule owns.
7432
+ /\b(?:labels?|etiquetas?)\s+(?:de|en|a|to|on)\s+(?:las?\s+|los\s+|the\s+)?(?:campanas?|campaigns?|anuncios?|ads?|grupos?\s+de\s+anuncios|ad\s*groups?)\b/i
7433
+ ],
7347
7434
  hint: "This is a tag or script on the site \u2014 it goes through the `request_tag_input` approval form in this chat, which also collects any secret values (guide: `__tooling__/docs/tools/baker/tags.md`). Show the form instead of filing a Task."
7348
7435
  },
7349
7436
  {
@@ -13365,10 +13452,52 @@ function mappingCommandFor(platform, proposed) {
13365
13452
 
13366
13453
  // ../api/src/analytics/websiteTag.ts
13367
13454
  import { z as z28 } from "zod";
13455
+ var websiteTagRequestSchema = z28.object({
13456
+ measure: z28.enum(["campaigns", "outcomes", "all"]).optional()
13457
+ });
13458
+ var analyticsSitesRequestSchema = z28.object({
13459
+ add: z28.array(z28.string()).optional(),
13460
+ remove: z28.array(z28.string()).optional()
13461
+ });
13462
+ var analyticsSitesResponseSchema = z28.object({
13463
+ /** Every declared website after the change, which is the answer to a bare read. */
13464
+ origins: z28.array(z28.string()),
13465
+ /** Normalized and actually new — an entry already present is reported in neither list. */
13466
+ added: z28.array(z28.string()),
13467
+ removed: z28.array(z28.string()),
13468
+ /** Still-undeclared domains this company owns. See {@link suggestedPixelOrigins}. */
13469
+ suggestedOrigins: z28.array(z28.string())
13470
+ });
13368
13471
  var websiteTagResponseSchema = z28.object({
13369
13472
  host: z28.string(),
13370
13473
  siteKey: z28.string(),
13371
13474
  origins: z28.array(z28.string()),
13475
+ /** Echoed back, so a caller that asked for nothing can see what it got. */
13476
+ measure: z28.enum(["campaigns", "outcomes", "all"]),
13477
+ /**
13478
+ * Whether `campaigns` scope can recognise anybody here — see
13479
+ * {@link campaignsScopeCanMatch}. `false` means the default measures nobody
13480
+ * on this company's install, however correctly the tag is pasted.
13481
+ */
13482
+ campaignsCanMatch: z28.boolean(),
13483
+ /**
13484
+ * Whether `host` is one of the company's own domains rather than Baker's.
13485
+ *
13486
+ * Worth answering because it is the difference between an install a content
13487
+ * blocker removes and one it has no list entry for — the pixel takes its
13488
+ * collect endpoint from its own `src`, so this decides the fate of the event
13489
+ * stream and not just the script load. See {@link pixelHost}.
13490
+ */
13491
+ firstParty: z28.boolean(),
13492
+ /**
13493
+ * Websites this company plainly owns and has not declared — the registrable
13494
+ * domains of its own landing domains. See {@link suggestedPixelOrigins}.
13495
+ *
13496
+ * A suggestion for somebody to accept, never a scope already granted: nothing
13497
+ * in Baker reads this list, and `baker analytics sites --add` is what acts on
13498
+ * it.
13499
+ */
13500
+ suggestedOrigins: z28.array(z28.string()),
13372
13501
  tag: z28.string(),
13373
13502
  tagManagerTag: z28.string(),
13374
13503
  brief: z28.string()
@@ -13586,6 +13715,7 @@ var analyticsPresetSchema = z29.enum([
13586
13715
  "stream"
13587
13716
  ]);
13588
13717
  var ANALYTICS_PRESETS = analyticsPresetSchema.options;
13718
+ var ANALYTICS_EVENT_DOORS = ["pages", "website", "server"];
13589
13719
  var analyticsQueryRequestSchema = z29.object({
13590
13720
  preset: analyticsPresetSchema,
13591
13721
  /** Lookback in days. Ignored when `startDate` is given. */
@@ -13644,9 +13774,7 @@ var analyticsQueryRequestSchema = z29.object({
13644
13774
  */
13645
13775
  adPlatform: z29.enum(AD_PLATFORMS).optional(),
13646
13776
  /**
13647
- * Read only what one collector produced: `site` for everything Baker
13648
- * measured itself, `systems` for what a client's own server posted to
13649
- * `POST /v1/events`.
13777
+ * Read only what came in by one door. See {@link ANALYTICS_EVENT_DOORS}.
13650
13778
  *
13651
13779
  * Omit it for all of it, which is the default and the honest superset.
13652
13780
  * Only the reads where the split is a question somebody asks honour it —
@@ -13655,7 +13783,7 @@ var analyticsQueryRequestSchema = z29.object({
13655
13783
  * ingest row carries no `session_id`, deliberately, so there is no visit
13656
13784
  * for it to be counted in.
13657
13785
  */
13658
- eventOrigin: z29.enum(["site", "systems"]).optional(),
13786
+ eventOrigin: z29.enum(ANALYTICS_EVENT_DOORS).optional(),
13659
13787
  /**
13660
13788
  * `stream` only: keep just one kind of event, named by the key
13661
13789
  * `conversions` hands back in its catalogue.
@@ -13889,9 +14017,27 @@ var analyticsLandingRowSchema = z29.object({
13889
14017
  /** Sessions that BEGAN here. The honest denominator for this page's rate. */
13890
14018
  entrances: z29.number().int().nonnegative(),
13891
14019
  visitors: z29.number().int().nonnegative(),
14020
+ /**
14021
+ * Conversions that fired ON this landing — rarely the number to show.
14022
+ *
14023
+ * A landing's outcome usually happens somewhere else: the client's checkout,
14024
+ * their shop, a CRM days later. None of those rows carries a landing id, so
14025
+ * this reads 0 for a page whose visits convert reliably.
14026
+ */
13892
14027
  conversions: z29.number().int().nonnegative(),
13893
14028
  /** Visits that converted on this page at least once. */
13894
14029
  convertingSessions: z29.number().int().nonnegative(),
14030
+ /**
14031
+ * Conversions the attribution model credited to this landing — what the page
14032
+ * is bought for, and what {@link conversionRate} is built from.
14033
+ *
14034
+ * Last non-direct click over a 90-day lookback, resolved per person, so it
14035
+ * includes the sale a returning visitor made next week and the deal a CRM
14036
+ * posted with no cookie at all.
14037
+ */
14038
+ creditedConversions: z29.number().int().nonnegative(),
14039
+ /** People whose credited touch arrived here and who then converted. */
14040
+ creditedVisitors: z29.number().int().nonnegative(),
13895
14041
  outboundClicks: z29.number().int().nonnegative(),
13896
14042
  /**
13897
14043
  * Converting visits per entrance. Null when nobody entered here at all.
@@ -14016,9 +14162,25 @@ var analyticsPageRowSchema = z29.object({
14016
14162
  pageViews: z29.number().int().nonnegative(),
14017
14163
  /** Sessions that began on this page — its value as a landing page. */
14018
14164
  entrances: z29.number().int().nonnegative(),
14165
+ /**
14166
+ * Conversions that fired ON this page — "which page converts".
14167
+ *
14168
+ * Almost never the number to put beside Entrances. A landing page's job is to
14169
+ * start a visit that converts, and the conversion is usually somewhere else:
14170
+ * the shop, the checkout, a CRM a week later. Read alone this makes every
14171
+ * landing page of a real funnel report zero.
14172
+ */
14019
14173
  conversions: z29.number().int().nonnegative(),
14020
14174
  /** Visits that converted on this page at least once. */
14021
14175
  convertingSessions: z29.number().int().nonnegative(),
14176
+ /**
14177
+ * Conversions the attribution model credited to this page — last non-direct
14178
+ * click, 90-day lookback, resolved per person. The number a landing-page
14179
+ * table means by "conversions", and the one the rate is built from.
14180
+ */
14181
+ creditedConversions: z29.number().int().nonnegative(),
14182
+ /** People whose credited touch arrived here and who then converted. */
14183
+ creditedVisitors: z29.number().int().nonnegative(),
14022
14184
  /** Converting visits per entrance, 0–1. Null when nobody landed here. */
14023
14185
  conversionRate: z29.number().min(0).max(1).nullable(),
14024
14186
  avgEngagementMs: z29.number().nonnegative().nullable()
@@ -14437,6 +14599,17 @@ var analyticsTimelineRowSchema = z29.object({
14437
14599
  eventType: z29.string(),
14438
14600
  /** `edge`, `beacon`, `server` — where the row was written from. */
14439
14601
  source: z29.string(),
14602
+ /**
14603
+ * `baker`, `external`, `export` — where the page was, which `source` does
14604
+ * not say. The two together are what decide the row's door; see
14605
+ * `ANALYTICS_EVENT_DOORS`.
14606
+ *
14607
+ * Defaulted rather than required: the column arrived after the corpus did
14608
+ * and carries no default of its own, so a row older than it reads `""`, and
14609
+ * that has to mean "Baker's, as everything was then" rather than fail the
14610
+ * whole row's parse.
14611
+ */
14612
+ deployment: z29.string().default(""),
14440
14613
  environment: z29.string(),
14441
14614
  /** The build the page was published from. */
14442
14615
  release: z29.string(),
@@ -14776,6 +14949,13 @@ var analyticsVisitorRowSchema = z29.object({
14776
14949
  identityHash: z29.string(),
14777
14950
  identityKind: z29.string()
14778
14951
  });
14952
+ var analyticsMeasureScopeSchema = z29.object({
14953
+ scope: z29.enum(["campaigns", "outcomes", "all", "unknown"]),
14954
+ /** Every event the tag on the client's own site sent in this window. */
14955
+ websiteEvents: z29.number(),
14956
+ /** Browser ids it minted off-Baker. Any at all means the tag is on `all`. */
14957
+ websiteNewVisitors: z29.number()
14958
+ });
14779
14959
  var analyticsQueryDataSchema = z29.object({
14780
14960
  preset: analyticsPresetSchema,
14781
14961
  window: analyticsWindowSchema,
@@ -14792,6 +14972,7 @@ var analyticsQueryDataSchema = z29.object({
14792
14972
  triggers: z29.array(analyticsTriggerRowSchema).optional(),
14793
14973
  releases: z29.array(analyticsReleaseRowSchema).optional(),
14794
14974
  paid: z29.array(analyticsPaidRowSchema).optional(),
14975
+ measureScope: analyticsMeasureScopeSchema.optional(),
14795
14976
  deliveries: z29.array(analyticsDeliveryRowSchema).optional(),
14796
14977
  submissions: z29.array(analyticsSubmissionRowSchema).optional(),
14797
14978
  geo: z29.array(analyticsGeoRowSchema).optional(),
@@ -18229,6 +18410,24 @@ registerSchema({
18229
18410
  file: { type: "string", description: "JSON file with fields to change", required: false }
18230
18411
  }
18231
18412
  });
18413
+ registerSchema({
18414
+ command: "ads.linkedin.conversions.associate-campaign",
18415
+ description: "Count an existing conversion rule for one ad set. Which ad sets a rule counts for is a separate LinkedIn record, not a field on the rule \u2014 `conversions update` cannot change it, and `--associate-all-campaigns` exists only on create. Staged until publish.",
18416
+ args: {
18417
+ id: { type: "positional", description: "Conversion rule id or URN", required: true },
18418
+ ...writeAccountArgs,
18419
+ campaign: { type: "string", description: "Ad set (campaign) id or URN", required: true }
18420
+ }
18421
+ });
18422
+ registerSchema({
18423
+ command: "ads.linkedin.conversions.dissociate-campaign",
18424
+ description: "Stop counting an existing conversion rule for one ad set. Staged until publish.",
18425
+ args: {
18426
+ id: { type: "positional", description: "Conversion rule id or URN", required: true },
18427
+ ...writeAccountArgs,
18428
+ campaign: { type: "string", description: "Ad set (campaign) id or URN", required: true }
18429
+ }
18430
+ });
18232
18431
  registerSchema({
18233
18432
  command: "ads.linkedin.lead-forms.create",
18234
18433
  description: "Stage a new Lead Gen Form from a JSON file: {name, headline \u226460, description? \u2264160, privacyPolicyUrl, questions[] \u226412 (playbook: \u22644 for completion), thankYou?, legalDisclaimer?}. Staged until publish.",
@@ -19033,6 +19232,57 @@ Example: baker ads linkedin conversions update 104988516 --post-click-window 30
19033
19232
  });
19034
19233
  }
19035
19234
  });
19235
+ var CONVERSION_CAMPAIGN_EXPLAINER = `Which ad sets a conversion rule counts for is a separate LinkedIn record, not a field on the rule \u2014 \`conversions update\` cannot change it, and \`--associate-all-campaigns\` only exists on create.`;
19236
+ function conversionCampaignArgs() {
19237
+ return {
19238
+ id: { type: "positional", description: "Conversion rule id or URN", required: true },
19239
+ ...accountArgs,
19240
+ campaign: { type: "string", description: "Ad set (campaign) id or URN", required: true }
19241
+ };
19242
+ }
19243
+ function requireCampaignArg(args) {
19244
+ const campaign = args.campaign;
19245
+ if (typeof campaign !== "string" || campaign.length === 0) {
19246
+ failWriteValidation2("--campaign is required \u2014 pass the ad set id or URN this conversion should count for");
19247
+ }
19248
+ return campaign;
19249
+ }
19250
+ var conversionsAssociateCommand = defineCommand33({
19251
+ meta: {
19252
+ name: "associate-campaign",
19253
+ description: `Count an existing conversion rule for one ad set. ${STAGED_NOTE}
19254
+ ${CONVERSION_CAMPAIGN_EXPLAINER}
19255
+ Example: baker ads linkedin conversions associate-campaign 104988516 --campaign 337643194`
19256
+ },
19257
+ args: conversionCampaignArgs(),
19258
+ run: async ({ args }) => {
19259
+ const accountId = bareAccountId(args);
19260
+ await stageOp({
19261
+ kind: "conversion.associateCampaign",
19262
+ accountId,
19263
+ target: requireTarget2(args, "conversion rule"),
19264
+ payload: { campaign: requireCampaignArg(args) }
19265
+ });
19266
+ }
19267
+ });
19268
+ var conversionsDissociateCommand = defineCommand33({
19269
+ meta: {
19270
+ name: "dissociate-campaign",
19271
+ description: `Stop counting an existing conversion rule for one ad set. ${STAGED_NOTE}
19272
+ ${CONVERSION_CAMPAIGN_EXPLAINER}
19273
+ Example: baker ads linkedin conversions dissociate-campaign 104988516 --campaign 337643194`
19274
+ },
19275
+ args: conversionCampaignArgs(),
19276
+ run: async ({ args }) => {
19277
+ const accountId = bareAccountId(args);
19278
+ await stageOp({
19279
+ kind: "conversion.dissociateCampaign",
19280
+ accountId,
19281
+ target: requireTarget2(args, "conversion rule"),
19282
+ payload: { campaign: requireCampaignArg(args) }
19283
+ });
19284
+ }
19285
+ });
19036
19286
  var leadFormsCreateCommand = defineCommand33({
19037
19287
  meta: {
19038
19288
  name: "create",
@@ -20165,13 +20415,17 @@ var conversionsCommand2 = defineCommand43({
20165
20415
  Subcommands:
20166
20416
  list \u2014 every rule on the account
20167
20417
  health \u2014 playbook \xA707 5-point health check
20168
- create / update \u2014 stage a conversion-rule write (applies on chat publish)`
20418
+ create / update \u2014 stage a conversion-rule write (applies on chat publish)
20419
+ associate-campaign / dissociate-campaign
20420
+ \u2014 which ad sets a rule counts for (its own LinkedIn record, not a field on the rule)`
20169
20421
  },
20170
20422
  subCommands: {
20171
20423
  list: listCmd,
20172
20424
  health: healthCmd,
20173
20425
  create: conversionsCreateCommand,
20174
- update: conversionsUpdateCommand
20426
+ update: conversionsUpdateCommand,
20427
+ "associate-campaign": conversionsAssociateCommand,
20428
+ "dissociate-campaign": conversionsDissociateCommand
20175
20429
  }
20176
20430
  });
20177
20431
 
@@ -27994,7 +28248,7 @@ one marked "Already counted", so a retry is visible without being counted.
27994
28248
 
27995
28249
  The same holds **across connections**, which is the reason to use it. If the page also
27996
28250
  reports this outcome with Baker's website tag, pass one string as \`event_id\` here and as
27997
- \`eventId\` in \`window.baker.conversion(name, { eventId })\`: the browser event and the
28251
+ \`eventId\` in \`window.baker.track(name, { eventId })\`: the browser event and the
27998
28252
  server event are then one event and count once, whichever arrives first and whether or not
27999
28253
  the other ever does. Report an important outcome from both \u2014 a browser can be blocked, a
28000
28254
  server cannot \u2014 and always pair them with an id. Send no id and the two are two conversions,
@@ -28727,7 +28981,7 @@ function adHierarchyHints(data, platform) {
28727
28981
  }
28728
28982
  function sessionlessHint(data, origin) {
28729
28983
  if (data.totals.pageViews === 0 || data.totals.sessions > 0) return [];
28730
- if (origin === "systems") {
28984
+ if (origin === "server") {
28731
28985
  return [
28732
28986
  "No sessions, because this report is scoped to events a client's own server posted and those carry no visit \u2014 a session would be an invented one. Every per-visitor rate here is null for the same reason; read the counts, and drop --origin for anything about behaviour."
28733
28987
  ];
@@ -28736,8 +28990,23 @@ function sessionlessHint(data, origin) {
28736
28990
  "Pages were served but no sessions were counted \u2014 visitors are declining analytics consent, so traffic is measurable and per-visitor behaviour is not. Say so rather than reporting zero visitors."
28737
28991
  ];
28738
28992
  }
28993
+ function measureScopeHint(data) {
28994
+ const scope = data.measureScope?.scope;
28995
+ if (scope === "all") {
28996
+ return [
28997
+ "This company's website tag measures EVERY visitor, not only the ones Baker's campaigns brought. So visits, views and conversions here describe the whole business \u2014 organic, email and direct included. Do not report these as what the campaigns produced; use the per-campaign and per-landing credit for that."
28998
+ ];
28999
+ }
29000
+ if (scope === "outcomes") {
29001
+ return [
29002
+ "This company's website tag reports outcomes only, so no browsing on their own site is measured. Visits and views cover Baker pages alone, and a drop-off between a Baker page and a sale is invisible rather than absent."
29003
+ ];
29004
+ }
29005
+ return [];
29006
+ }
28739
29007
  function buildAnalyticsHints(data, platform, origin) {
28740
29008
  const hints2 = [];
29009
+ hints2.push(...measureScopeHint(data));
28741
29010
  for (const funnel of data.funnels ?? []) {
28742
29011
  const worst = funnel.worstStep;
28743
29012
  if (worst && worst.dropRate !== null && worst.dropRate >= SEVERE_STEP_DROP) {
@@ -29083,6 +29352,73 @@ function formatHint(data, format) {
29083
29352
  ];
29084
29353
  }
29085
29354
 
29355
+ // src/commands/analytics/websiteTagHints.ts
29356
+ function asTyped(origin) {
29357
+ return origin.replace(/^https?:\/\//, "");
29358
+ }
29359
+ function suggestionHint(suggested) {
29360
+ if (suggested.length === 0) return [];
29361
+ const commands = suggested.map((origin) => `baker analytics sites --add ${asTyped(origin)}`).join(" \xB7 ");
29362
+ return [
29363
+ `Baker publishes landing pages on ${suggested.map(asTyped).join(", ")} for this company, and no declared website covers ${suggested.length === 1 ? "it" : "them"}. Declaring the domain covers every subdomain of it \u2014 the marketing site, the shop, the booking page, and the ones that do not exist yet: ${commands}`
29364
+ ];
29365
+ }
29366
+ function websiteTagHints(response) {
29367
+ const hints2 = [];
29368
+ if (response.origins.length === 0) {
29369
+ hints2.push(
29370
+ "NO WEBSITE DECLARED \u2014 this tag will be ignored everywhere it is installed, silently, with no failing request to find. Declare the site before installing anything: baker analytics sites --add <the client's domain>. It takes effect at once and needs no republish."
29371
+ );
29372
+ }
29373
+ hints2.push(...suggestionHint(response.suggestedOrigins));
29374
+ if (response.firstParty) {
29375
+ hints2.push(
29376
+ `This tag loads from ${response.host} \u2014 the client's own domain, not Baker's \u2014 so the script and every event it sends travel first-party, which a content blocker has no list entry for. The trade is a dependency: if that domain is ever removed from Baker, this tag 404s and measurement stops. Re-run this command after any domain change.`
29377
+ );
29378
+ }
29379
+ if (response.measure === "all") {
29380
+ hints2.push(
29381
+ "This tag measures the client's WHOLE WEBSITE \u2014 every visitor and every page view, not only the people your campaigns brought. Their reports will describe their business rather than their campaigns, and outcomes their site produces from organic, email and direct traffic will land in their conversion numbers. Only correct if somebody asked for that; otherwise re-run without --measure."
29382
+ );
29383
+ }
29384
+ if (response.measure === "campaigns" && !response.campaignsCanMatch) {
29385
+ hints2.push(
29386
+ "THIS TAG WILL MEASURE NOBODY \u2014 and will look installed. `campaigns` scope recognises a visitor by a first-party cookie, and a browser does not carry one between two different registrable domains. This company's landings are not on a subdomain of the site this tag goes on, so no visitor can ever match. Two fixes: put the landings on a subdomain of the client's own domain (a custom domain in Baker), or install with --measure outcomes, which measures no browsing and reports the outcomes the site tells Baker about."
29387
+ );
29388
+ } else if (response.measure === "campaigns") {
29389
+ hints2.push(
29390
+ "This tag measures only visitors who have been on one of this company's Baker landing pages \u2014 their whole journey through the site, to the outcome. Anybody else is not measured at all. Use --measure all only if the client asked for their whole website measured."
29391
+ );
29392
+ }
29393
+ hints2.push(
29394
+ 'Reporting an outcome is not the same as counting it. After a `window.baker.track("name")` call is live, name it with: baker analytics conversions --event page:<name> --name "<what to call it>". That applies immediately and counts everything already collected.'
29395
+ );
29396
+ return hints2;
29397
+ }
29398
+ function analyticsSitesHints(response) {
29399
+ const hints2 = [];
29400
+ if (response.origins.length === 0) {
29401
+ hints2.push(
29402
+ "No website is declared, so this company's measurement tag is ignored wherever it is installed \u2014 silently, with no failing request to find. Add one with: baker analytics sites --add <the client's domain>"
29403
+ );
29404
+ }
29405
+ hints2.push(...suggestionHint(response.suggestedOrigins));
29406
+ if (response.added.length > 0) {
29407
+ hints2.push(
29408
+ `Live now. A tag already installed on ${response.added.map(asTyped).join(", ")} starts being accepted immediately \u2014 there is no new tag to paste and nothing to publish. Only events sent from here on are kept; the ones refused before this were never stored and cannot be recovered.`
29409
+ );
29410
+ }
29411
+ if (response.removed.length > 0) {
29412
+ hints2.push(
29413
+ `Measurement from ${response.removed.map(asTyped).join(", ")} will stop at once, with no error on that site \u2014 a tag still installed there goes quiet. Everything already collected from it is kept.`
29414
+ );
29415
+ }
29416
+ hints2.push(
29417
+ "One entry covers everything under it: example.com answers for www., go. and shop.eu. It never widens upward, so declaring go.example.com leaves example.com uncovered \u2014 declare the domain, not the subdomain you are installing on today."
29418
+ );
29419
+ return hints2;
29420
+ }
29421
+
29086
29422
  // src/commands/analytics/index.ts
29087
29423
  var SHARED_ARGS = {
29088
29424
  days: { type: "string", description: "Lookback window in days (default: 30)", required: false },
@@ -29140,7 +29476,7 @@ var SHARED_ARGS = {
29140
29476
  },
29141
29477
  origin: {
29142
29478
  type: "string",
29143
- description: "Read one collector only: `site` for what Baker measured on its own pages, `systems` for what the client's own server posted to POST /v1/events. Omit it for both, which is the default and the honest superset. Reach for it when a number moved and nothing on the pages changed \u2014 a CRM replaying history lands in the same totals as a visitor, and this is the only thing that separates them. Honoured by overview, traffic, events and conversions; every session-scoped report ignores it, because a server-sent event carries no visit to be counted in",
29479
+ description: "Read one door only: `pages` for a browser on a page Baker publishes, `website` for a browser on the client's OWN website (the measurement tag), `server` for what their server posted to POST /v1/events. Omit it for all three, which is the default and the honest superset. Reach for it when a number moved and nothing on the pages changed \u2014 a CRM replaying history and a tag on the client's own site both land in the same totals as a landing page visitor, and this is the only thing that separates them. Honoured by overview, traffic, events and conversions; every session-scoped report ignores it, because a server-sent event carries no visit to be counted in",
29144
29480
  required: false
29145
29481
  },
29146
29482
  output: {
@@ -29205,7 +29541,7 @@ function requestBody(args, options) {
29205
29541
  Object.entries(candidates).filter(([key, value]) => value !== void 0 && (value !== "" || key === "adValue"))
29206
29542
  );
29207
29543
  }
29208
- var EVENT_ORIGINS = ["site", "systems"];
29544
+ var EVENT_ORIGINS = ANALYTICS_EVENT_DOORS;
29209
29545
  async function runPreset(args, options) {
29210
29546
  const origin = args.origin === void 0 ? "" : String(args.origin);
29211
29547
  if (origin !== "" && !EVENT_ORIGINS.includes(origin)) {
@@ -29213,7 +29549,7 @@ async function runPreset(args, options) {
29213
29549
  ok: false,
29214
29550
  error: {
29215
29551
  code: "VALIDATION_ERROR",
29216
- message: `Unknown --origin "${origin}". Use one of: ${EVENT_ORIGINS.join(", ")}, or omit it for both.`
29552
+ message: `Unknown --origin "${origin}". Use one of: ${EVENT_ORIGINS.join(", ")}, or omit it for all three.`
29217
29553
  }
29218
29554
  });
29219
29555
  process.exit(1);
@@ -29811,7 +30147,13 @@ var websiteTagCommand = (() => {
29811
30147
  registerSchema({
29812
30148
  command: "analytics.website-tag",
29813
30149
  description: "The measurement tag for a website Baker does not publish, with this company's key in it",
29814
- args: {}
30150
+ args: {
30151
+ measure: {
30152
+ type: "string",
30153
+ required: false,
30154
+ description: 'Whose activity to measure: "campaigns" (default), "outcomes" or "all"'
30155
+ }
30156
+ }
29815
30157
  });
29816
30158
  return defineCommand95({
29817
30159
  meta: {
@@ -29823,16 +30165,81 @@ Reach for it whenever measurement has to reach a page Baker did not build. What
29823
30165
  Three things worth knowing before you install it:
29824
30166
  the origins list \u2014 the key is IGNORED from any origin not on it, silently. An empty list means the tag will measure nothing.
29825
30167
  Tag Manager is usually the way in \u2014 a Custom HTML tag on All Pages. If the client has a container connected, you can stage that yourself (baker tag-manager).
29826
- in a container, use tagManagerTag \u2014 the response carries two tags. \`tag\` is for a page somebody can edit; \`tagManagerTag\` is the one to stage, because a container rebuilds a Custom HTML tag's script element from its src alone and drops the key, so \`tag\` measures nothing there, published and silent. Check it on the page afterwards: window.baker in the browser console.
30168
+ in a container, use tagManagerTag \u2014 the response carries two tags. \`tag\` is for a page somebody can edit; \`tagManagerTag\` is the one to stage, because a container rebuilds a Custom HTML tag's script element from its src alone and drops the key, so \`tag\` measures nothing there, published and silent. Check it on the page afterwards: window.baker in the browser console.
30169
+ --measure decides the BILL \u2014 the tag goes on every page whatever you do; this decides whose activity is measured. Leave it alone unless the client asked for their whole website.
30170
+
30171
+ --measure campaigns (default) people your Baker campaigns brought to the site. Their whole journey, through to the outcome. Nobody else is measured at all.
30172
+ --measure outcomes nobody's browsing. Only what the site reports with window.baker.track(...), and forms it wired to Baker.
30173
+ --measure all every visitor and every page view of the whole website. Their reports then describe their business, not their campaigns, and outcomes from organic, email and direct land in their conversion numbers. A decision, never a default.
29827
30174
 
29828
30175
  Examples:
29829
30176
  baker analytics website-tag \u2014 the tag, the origins and the install brief
29830
- baker analytics events --origin site \u2014 after it is live, what that website is reporting`
30177
+ baker analytics website-tag --measure all \u2014 measure their whole website, because they asked for that
30178
+ baker analytics events --origin website \u2014 after it is live, what that website is reporting`
29831
30179
  },
29832
- args: {},
29833
- run: async () => {
30180
+ args: {
30181
+ measure: {
30182
+ type: "string",
30183
+ description: 'Whose activity to measure: "campaigns" (default), "outcomes" or "all". Widening is a decision \u2014 see above.'
30184
+ }
30185
+ },
30186
+ run: async (ctx) => {
30187
+ try {
30188
+ const measure = typeof ctx.args.measure === "string" ? ctx.args.measure : void 0;
30189
+ const envelope = await apiPost(
30190
+ "/api/analytics/website-tag",
30191
+ measure ? { measure } : {}
30192
+ );
30193
+ writeJsonEnvelope({ ...envelope, hints: websiteTagHints(envelope.data) });
30194
+ } catch (err) {
30195
+ handleError(err);
30196
+ }
30197
+ }
30198
+ });
30199
+ })();
30200
+ var sitesCommand = (() => {
30201
+ registerSchema({
30202
+ command: "analytics.sites",
30203
+ description: "The websites this company's measurement tag is accepted from, and adding or removing one",
30204
+ args: {
30205
+ add: { type: "string", required: false, description: "Website address to accept measurement from" },
30206
+ remove: { type: "string", required: false, description: "Website address to stop accepting measurement from" }
30207
+ }
30208
+ });
30209
+ return defineCommand95({
30210
+ meta: {
30211
+ name: "sites",
30212
+ description: `Which websites this company's measurement tag is accepted from \u2014 and the command that changes it.
30213
+
30214
+ The site key ships in the source of every page it is pasted on, so this list is the whole of what stops a stranger who read it appending forged conversions to these numbers. A tag on a website that is NOT on this list is ignored, silently, with no failing request anywhere. That is the single most common reason an install measures nothing.
30215
+
30216
+ One entry covers the estate under it: example.com answers for www.example.com, go.example.com and shop.eu.example.com. It never widens upward \u2014 declaring go.example.com does NOT cover example.com \u2014 so declare the DOMAIN, not the subdomain you happen to be installing on today.
30217
+
30218
+ Start here:
30219
+ baker analytics sites \u2014 what is declared now, and what is worth adding
30220
+ baker analytics sites --add example.com \u2014 accept measurement from that site and everything under it
30221
+ baker analytics sites --remove old-site.com
30222
+
30223
+ Examples:
30224
+ baker analytics website-tag \u2014 get the tag; if its origins are empty, come here first
30225
+ baker analytics sites --add foodforjoe.es \u2014 one entry covering www., go. and shop.`
30226
+ },
30227
+ args: {
30228
+ add: {
30229
+ type: "string",
30230
+ description: "Website address to accept measurement from, e.g. example.com \u2014 covers every subdomain of it. Repeat for several"
30231
+ },
30232
+ remove: { type: "string", description: "Website address to stop accepting measurement from" }
30233
+ },
30234
+ run: async (ctx) => {
29834
30235
  try {
29835
- writeJsonEnvelope(await apiPost("/api/analytics/website-tag", {}));
30236
+ const raw = ctx.rawArgs;
30237
+ const body = {
30238
+ add: repeatedValues(raw, "add", ctx.args.add),
30239
+ remove: repeatedValues(raw, "remove", ctx.args.remove)
30240
+ };
30241
+ const envelope = await apiPost("/api/analytics/sites", body);
30242
+ writeJsonEnvelope({ ...envelope, hints: analyticsSitesHints(envelope.data) });
29836
30243
  } catch (err) {
29837
30244
  handleError(err);
29838
30245
  }
@@ -29882,7 +30289,7 @@ Examples:
29882
30289
  baker analytics website-tag \u2014 the tag for a website Baker does not publish, key included
29883
30290
  baker analytics sending-events \u2014 the contract for a client's server to post its own events
29884
30291
  baker analytics arrivals --outcome rejected \u2014 what their server posted that we refused, and why
29885
- baker analytics events --origin systems \u2014 the events their systems reported, not ours
30292
+ baker analytics events --origin server \u2014 the events their systems reported, not ours
29886
30293
  Full guide: __tooling__/docs/tools/baker/analytics.md`
29887
30294
  },
29888
30295
  subCommands: {
@@ -29909,6 +30316,7 @@ Full guide: __tooling__/docs/tools/baker/analytics.md`
29909
30316
  arrivals: arrivalsCommand,
29910
30317
  "sending-events": sendingEventsCommand,
29911
30318
  "website-tag": websiteTagCommand,
30319
+ sites: sitesCommand,
29912
30320
  presets: presetsCommand
29913
30321
  }
29914
30322
  });
@@ -34187,8 +34595,12 @@ function foldUnshownPhrases(phrases, keepRenderedVoice) {
34187
34595
  const claimed = /* @__PURE__ */ new Set();
34188
34596
  const kept = [];
34189
34597
  for (const phrase of phrases) {
34598
+ if (!phrase.presenterShown) {
34599
+ kept.push(phrase);
34600
+ continue;
34601
+ }
34190
34602
  const available = phrase.shownScenes.filter((scene) => !claimed.has(scene));
34191
- if (phrase.presenterShown && available.length > 0) {
34603
+ if (available.length > 0) {
34192
34604
  for (const scene of available) claimed.add(scene);
34193
34605
  kept.push(phrase);
34194
34606
  continue;
@@ -46323,7 +46735,7 @@ function siteHints(sites) {
46323
46735
  resources: sites.map((site) => ({ id: site.siteUrl, label: site.permissionLevel }))
46324
46736
  });
46325
46737
  }
46326
- var sitesCommand = defineCommand135({
46738
+ var sitesCommand2 = defineCommand135({
46327
46739
  meta: {
46328
46740
  name: "sites",
46329
46741
  description: `List verified Search Console sites.
@@ -46388,7 +46800,7 @@ Examples:
46388
46800
  Full guide: __tooling__/docs/tools/baker/gsc.md`
46389
46801
  },
46390
46802
  subCommands: {
46391
- sites: sitesCommand,
46803
+ sites: sitesCommand2,
46392
46804
  query: queryCommand3,
46393
46805
  sitemaps: sitemapsCommand
46394
46806
  }
@@ -47170,7 +47582,7 @@ Full guide: __tooling__/docs/tools/baker/hubspot.md`
47170
47582
  });
47171
47583
 
47172
47584
  // src/commands/images/index.ts
47173
- import { defineCommand as defineCommand164 } from "citty";
47585
+ import { defineCommand as defineCommand165 } from "citty";
47174
47586
 
47175
47587
  // src/commands/images/crop.ts
47176
47588
  import { defineCommand as defineCommand139 } from "citty";
@@ -47385,9 +47797,118 @@ var deleteCommand2 = defineCommand140({
47385
47797
  }
47386
47798
  });
47387
47799
 
47388
- // src/commands/images/dimensions.ts
47800
+ // src/commands/images/describe.ts
47389
47801
  import { defineCommand as defineCommand141 } from "citty";
47390
47802
 
47803
+ // src/lib/describeArgs.ts
47804
+ function describeTags(rawArgs, parsed) {
47805
+ const given = repeatedValues(rawArgs, "tags", parsed);
47806
+ if (given.length === 0) return void 0;
47807
+ return given.flatMap((chunk) => chunk.split(",").map((tag) => tag.trim())).filter(Boolean);
47808
+ }
47809
+ function describeHints(noun, body) {
47810
+ const search = noun === "images" ? "baker images library" : "baker videos search";
47811
+ const hints2 = [];
47812
+ if (body.description !== void 0) {
47813
+ hints2.push(
47814
+ `The semantic index rebuilds in the background \u2014 a \`${search}\` run right now may still rank on the old description.`
47815
+ );
47816
+ }
47817
+ if (body.tags !== void 0) {
47818
+ hints2.push(`--tags replaced the whole tag set. Run \`baker ${noun} get <id>\` first if you meant to add to it.`);
47819
+ }
47820
+ return hints2.length > 0 ? hints2 : void 0;
47821
+ }
47822
+ function describeFields(args, rawArgs) {
47823
+ const fields = {};
47824
+ if (args.description) fields.description = args.description;
47825
+ if (args.name) fields.name = args.name;
47826
+ const tags = describeTags(rawArgs, args.tags);
47827
+ if (tags !== void 0) fields.tags = tags;
47828
+ return fields;
47829
+ }
47830
+ function describeErrorFix(code, noun) {
47831
+ if (code !== "CONFLICT") return void 0;
47832
+ return `Poll \`baker ${noun} get <id>\` until \`status\` reads "ready", then run this again. Do not retry immediately \u2014 the answer will not change until the analysis lands.`;
47833
+ }
47834
+
47835
+ // src/commands/images/describe.ts
47836
+ var DESCRIPTION_HELP = "What the image actually shows, in the words someone would search for it by. This is what `baker images library` retrieves on.";
47837
+ registerSchema({
47838
+ command: "images.describe",
47839
+ description: "Correct a library image's stored name, description or tags. Start here when an image's description is wrong \u2014 it is what semantic search reads.",
47840
+ args: {
47841
+ id: { type: "string", description: "Image ID", required: true },
47842
+ description: { type: "string", description: DESCRIPTION_HELP, required: false },
47843
+ name: { type: "string", description: "Short human label for the image", required: false },
47844
+ tags: {
47845
+ type: "string",
47846
+ description: "Tags for the image \u2014 repeatable (`--tags a --tags b`) or one comma list. **Replaces** the existing set",
47847
+ required: false
47848
+ },
47849
+ full: { type: "boolean", description: "Return the whole library row, not just what changed", required: false }
47850
+ }
47851
+ });
47852
+ var describeCommand = defineCommand141({
47853
+ meta: {
47854
+ name: "describe",
47855
+ description: `Correct what the library says an image is. Only the fields you pass change; the rest keep their current values.
47856
+
47857
+ Start here when a search keeps missing an image you know is there, or when the AI description got the subject wrong.
47858
+
47859
+ Example: baker images describe j571abc123 --description "Founder speaking on stage at SaaStr, blue backdrop" --tags event,team`
47860
+ },
47861
+ args: {
47862
+ id: { type: "positional", description: "Image ID", required: false },
47863
+ "image-id": { type: "string", description: "Image ID (alternative to positional)", required: false },
47864
+ description: { type: "string", description: DESCRIPTION_HELP, required: false },
47865
+ name: { type: "string", description: "Short human label for the image", required: false },
47866
+ tags: {
47867
+ type: "string",
47868
+ description: "Tags for the image \u2014 repeatable (`--tags a --tags b`) or one comma list. **Replaces** the existing set",
47869
+ required: false
47870
+ },
47871
+ full: { type: "boolean", description: "Return the whole library row", required: false, default: false }
47872
+ },
47873
+ run: async ({ args, rawArgs }) => {
47874
+ const id = args.id || args["image-id"];
47875
+ if (!id) {
47876
+ writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message: "Image ID is required" } });
47877
+ process.exit(1);
47878
+ }
47879
+ const fields = describeFields(args, rawArgs);
47880
+ if (fields.name === void 0 && fields.description === void 0 && fields.tags === void 0) {
47881
+ writeJson({
47882
+ ok: false,
47883
+ error: {
47884
+ code: "VALIDATION_ERROR",
47885
+ message: "Nothing to change",
47886
+ fix: "Pass at least one of --description, --name or --tags."
47887
+ }
47888
+ });
47889
+ process.exit(1);
47890
+ }
47891
+ try {
47892
+ validateConvexId(id);
47893
+ const body = { id, ...fields };
47894
+ if (args.full) body.full = true;
47895
+ const data = await apiPost("/api/images/describe", body);
47896
+ writeJson({ ok: true, data, hints: describeHints("images", fields) });
47897
+ } catch (err) {
47898
+ if (err instanceof ApiError) {
47899
+ const fix = describeErrorFix(err.code, "images");
47900
+ writeJson({ ok: false, error: { code: err.code, message: err.message, ...fix ? { fix } : {} } });
47901
+ process.exit(1);
47902
+ }
47903
+ writeJson({ ok: false, error: { code: "INTERNAL_ERROR", message: "Unexpected error" } });
47904
+ process.exit(1);
47905
+ }
47906
+ }
47907
+ });
47908
+
47909
+ // src/commands/images/dimensions.ts
47910
+ import { defineCommand as defineCommand142 } from "citty";
47911
+
47391
47912
  // src/lib/image/dimensions.ts
47392
47913
  import { imageSize } from "image-size";
47393
47914
  function getDimensionsFromBuffer(buffer) {
@@ -47415,7 +47936,7 @@ registerSchema({
47415
47936
  }
47416
47937
  }
47417
47938
  });
47418
- var dimensionsCommand = defineCommand141({
47939
+ var dimensionsCommand = defineCommand142({
47419
47940
  meta: {
47420
47941
  name: "dimensions",
47421
47942
  description: "Read image dimensions without decoding the full file.\n\nExample: baker images dimensions ./logo.png\nExample: baker images dimensions https://acme.com/hero.png"
@@ -47471,7 +47992,7 @@ var dimensionsCommand = defineCommand141({
47471
47992
  });
47472
47993
 
47473
47994
  // src/commands/images/download.ts
47474
- import { defineCommand as defineCommand142 } from "citty";
47995
+ import { defineCommand as defineCommand143 } from "citty";
47475
47996
 
47476
47997
  // src/commands/images/downloadPaths.ts
47477
47998
  import { basename as basename2, extname as extname3, join as join6 } from "path";
@@ -47712,7 +48233,7 @@ function emitError3(err) {
47712
48233
  writeJson({ ok: false, error: { code: "INTERNAL_ERROR", message: "Unexpected error" } });
47713
48234
  process.exit(1);
47714
48235
  }
47715
- var downloadCommand = defineCommand142({
48236
+ var downloadCommand = defineCommand143({
47716
48237
  meta: {
47717
48238
  name: "download",
47718
48239
  description: "Download image URLs and/or library images to local files \u2014 the missing first half of `source \u2192 download \u2192 normalize \u2192 place`. Never use `curl` for this.\n\nExamples:\n baker images download https://media.withbaker.com/\u2026/logo.webp\n baker images download j57abc123 j57def456 --out src/pages/pricing/_images/\n baker images download https://\u2026/hero.png --out ./hero.png"
@@ -47745,7 +48266,7 @@ var downloadCommand = defineCommand142({
47745
48266
  });
47746
48267
 
47747
48268
  // src/commands/images/extract.ts
47748
- import { defineCommand as defineCommand143 } from "citty";
48269
+ import { defineCommand as defineCommand144 } from "citty";
47749
48270
 
47750
48271
  // src/commands/images/autoIngest.ts
47751
48272
  var AUTO_INGEST_MAX = {
@@ -47792,7 +48313,7 @@ registerSchema({
47792
48313
  }
47793
48314
  }
47794
48315
  });
47795
- var extractCommand = defineCommand143({
48316
+ var extractCommand = defineCommand144({
47796
48317
  meta: {
47797
48318
  name: "extract",
47798
48319
  description: "Pull every image from a single URL via Firecrawl. ~$0.001/scrape. Cap auto-ingest at 20.\n\nExample: baker images extract https://stripe.com --auto-ingest 5"
@@ -47847,7 +48368,7 @@ var extractCommand = defineCommand143({
47847
48368
  });
47848
48369
 
47849
48370
  // src/commands/images/find.ts
47850
- import { defineCommand as defineCommand144 } from "citty";
48371
+ import { defineCommand as defineCommand145 } from "citty";
47851
48372
 
47852
48373
  // src/commands/images/providerHits.ts
47853
48374
  function asRecord3(value) {
@@ -47985,7 +48506,7 @@ registerSchema({
47985
48506
  full: { type: "boolean", description: "Include full metadata", required: false, default: false }
47986
48507
  }
47987
48508
  });
47988
- var findCommand = defineCommand144({
48509
+ var findCommand = defineCommand145({
47989
48510
  meta: {
47990
48511
  name: "find",
47991
48512
  description: "Library-first fanout image search. Opt in to providers with --sources. `--fallback` short-circuits to externals only when library is thin. With --auto-ingest, ingested external hits return Baker-owned URLs.\n\nExample: baker images find 'office' --sources library,pexels --limit 20"
@@ -48058,7 +48579,7 @@ var findCommand = defineCommand144({
48058
48579
  });
48059
48580
 
48060
48581
  // src/commands/images/get.ts
48061
- import { defineCommand as defineCommand145 } from "citty";
48582
+ import { defineCommand as defineCommand146 } from "citty";
48062
48583
  registerSchema({
48063
48584
  command: "images.get",
48064
48585
  description: "Get a single image by ID",
@@ -48066,7 +48587,7 @@ registerSchema({
48066
48587
  id: { type: "string", description: "Image ID", required: true }
48067
48588
  }
48068
48589
  });
48069
- var getCommand3 = defineCommand145({
48590
+ var getCommand3 = defineCommand146({
48070
48591
  meta: { name: "get", description: "Get a single image by ID. Example: baker images get j571abc123" },
48071
48592
  args: {
48072
48593
  id: { type: "positional", description: "Image ID", required: false },
@@ -48102,7 +48623,7 @@ var getCommand3 = defineCommand145({
48102
48623
  });
48103
48624
 
48104
48625
  // src/commands/images/gif.ts
48105
- import { defineCommand as defineCommand146 } from "citty";
48626
+ import { defineCommand as defineCommand147 } from "citty";
48106
48627
  registerSchema({
48107
48628
  command: "images.gif",
48108
48629
  description: "Search Giphy for GIFs / reaction memes (paid social creative).",
@@ -48134,7 +48655,7 @@ registerSchema({
48134
48655
  }
48135
48656
  }
48136
48657
  });
48137
- var gifCommand = defineCommand146({
48658
+ var gifCommand = defineCommand147({
48138
48659
  meta: {
48139
48660
  name: "gif",
48140
48661
  description: "Search Giphy for GIFs / reaction memes \u2014 built for paid-social creative (Meta, TikTok, LinkedIn, X). Free API. Each hit carries WebP + GIF + MP4 URLs in providerMeta so you can pick the right format per platform.\n\nExample: baker images gif 'this is fine' --limit 10\nExample: baker images gif 'office reaction' --rating pg --auto-ingest 2\nExample: baker images gif --trending --limit 25"
@@ -48181,7 +48702,7 @@ var gifCommand = defineCommand146({
48181
48702
  });
48182
48703
 
48183
48704
  // src/commands/images/google.ts
48184
- import { defineCommand as defineCommand147 } from "citty";
48705
+ import { defineCommand as defineCommand148 } from "citty";
48185
48706
  var GOOGLE_ERROR_FIX = {
48186
48707
  action: "use_different_resource",
48187
48708
  explanation: "Generate the asset instead of retrying Google. Google is the last-resort image provider. Run `baker studio generate` to make the asset. Never sleep-and-retry this command \u2014 the CLI already backs off on rate limits. If no image can be sourced, continue the rest of the task with a placeholder rather than aborting it."
@@ -48221,7 +48742,7 @@ registerSchema({
48221
48742
  }
48222
48743
  }
48223
48744
  });
48224
- var googleCommand2 = defineCommand147({
48745
+ var googleCommand2 = defineCommand148({
48225
48746
  meta: {
48226
48747
  name: "google",
48227
48748
  description: "Google Images via the official Custom Search JSON API ($0.005/query, free 100/day). \u26A0 Source unverified \u2014 watermarks, low-res, mislabeled results are common. Use as last resort. With --auto-ingest, ingested hits return Baker-owned URLs.\n\nExample: baker images google 'industrial workshop' --type photo --size large --limit 20"
@@ -48294,7 +48815,7 @@ var googleCommand2 = defineCommand147({
48294
48815
  });
48295
48816
 
48296
48817
  // src/commands/images/group.ts
48297
- import { defineCommand as defineCommand148 } from "citty";
48818
+ import { defineCommand as defineCommand149 } from "citty";
48298
48819
 
48299
48820
  // src/commands/mediaGroup.ts
48300
48821
  async function runMediaGroupLookup(input) {
@@ -48347,7 +48868,7 @@ registerSchema({
48347
48868
  "group-key": { type: "string", description: "The set key directly, when you already have it", required: false }
48348
48869
  }
48349
48870
  });
48350
- var groupCommand = defineCommand148({
48871
+ var groupCommand = defineCommand149({
48351
48872
  meta: {
48352
48873
  name: "group",
48353
48874
  description: "List every asset that arrived in the same set \u2014 the slides of one Instagram carousel, the images off one scraped page. Start here whenever a hit looks like part of a sequence: carousel slides are authored to be read in order and usually only make sense together. Takes an image or a video id, since one carousel can contain both. Example: baker images group <imageId>"
@@ -48367,7 +48888,7 @@ var groupCommand = defineCommand148({
48367
48888
  });
48368
48889
 
48369
48890
  // src/commands/images/icon.ts
48370
- import { defineCommand as defineCommand149 } from "citty";
48891
+ import { defineCommand as defineCommand150 } from "citty";
48371
48892
 
48372
48893
  // src/commands/images/brandVerification.ts
48373
48894
  function brandToken(domain) {
@@ -48464,7 +48985,7 @@ registerSchema({
48464
48985
  full: { type: "boolean", description: "Include full metadata", required: false, default: false }
48465
48986
  }
48466
48987
  });
48467
- var iconCommand = defineCommand149({
48988
+ var iconCommand = defineCommand150({
48468
48989
  meta: {
48469
48990
  name: "icon",
48470
48991
  description: "Icon via Iconify (simple-icons, logos, lucide, devicon, heroicons, tabler, phosphor, material-symbols, \u2026). Free CDN, no API key.\n\nExample: baker images icon react --set devicon\nExample: baker images icon lucide:check --color '#0a0a0a'"
@@ -48534,7 +49055,7 @@ var iconCommand = defineCommand149({
48534
49055
  });
48535
49056
 
48536
49057
  // src/commands/images/ingest.ts
48537
- import { defineCommand as defineCommand150 } from "citty";
49058
+ import { defineCommand as defineCommand151 } from "citty";
48538
49059
  var SOURCE_VALUES = imageSourceSchema.options.join(" | ");
48539
49060
  registerSchema({
48540
49061
  command: "images.ingest",
@@ -48549,7 +49070,7 @@ registerSchema({
48549
49070
  fields: { type: "string", description: "Comma-separated field names to include", required: false }
48550
49071
  }
48551
49072
  });
48552
- var ingestCommand = defineCommand150({
49073
+ var ingestCommand = defineCommand151({
48553
49074
  meta: {
48554
49075
  name: "ingest",
48555
49076
  description: "Download a remote URL and store it in the library. Hash-deduped on bytes + externalId.\n\nExample: baker images ingest https://images.pexels.com/photos/13219418/pexels-photo-13219418.jpeg --source pexels --external-id 13219418"
@@ -48618,7 +49139,7 @@ var ingestCommand = defineCommand150({
48618
49139
  });
48619
49140
 
48620
49141
  // src/commands/images/layerize.ts
48621
- import { defineCommand as defineCommand151 } from "citty";
49142
+ import { defineCommand as defineCommand152 } from "citty";
48622
49143
  registerSchema({
48623
49144
  command: "images.layerize",
48624
49145
  description: "Split a library image into editable layers: transparent PNG cutouts for each element, plus any headline recovered as EDITABLE TEXT with its font, size, colour and position. Waits for completion by default. Costs credits.",
@@ -48682,7 +49203,7 @@ async function pollUntilSettled(imageId, maxWait) {
48682
49203
  }
48683
49204
  return null;
48684
49205
  }
48685
- var layerizeCommand = defineCommand151({
49206
+ var layerizeCommand = defineCommand152({
48686
49207
  meta: {
48687
49208
  name: "layerize",
48688
49209
  description: "Split a library image into editable layers \u2014 transparent cutouts per element, plus any baked-in headline recovered as editable text with its typography.\n\nStart here: baker images layerize j571abc123def\nExample: baker images layerize j571abc123def --full\nExample: baker images layerize j571abc123def --instructions 'keep the product and its shadow together'"
@@ -48751,7 +49272,7 @@ registerSchema({
48751
49272
  full: { type: "boolean", description: "Include geometry and typography for every layer", required: false }
48752
49273
  }
48753
49274
  });
48754
- var layersCommand = defineCommand151({
49275
+ var layersCommand = defineCommand152({
48755
49276
  meta: {
48756
49277
  name: "layers",
48757
49278
  description: "Read the layers of an image that has already been split. Free \u2014 no provider call.\n\nStart here: baker images layers j571abc123def\nExample: baker images layers j571abc123def --full"
@@ -48791,7 +49312,7 @@ var layersCommand = defineCommand151({
48791
49312
  });
48792
49313
 
48793
49314
  // src/commands/images/library.ts
48794
- import { defineCommand as defineCommand152 } from "citty";
49315
+ import { defineCommand as defineCommand153 } from "citty";
48795
49316
  registerSchema({
48796
49317
  command: "images.library",
48797
49318
  description: "Search the company image library. Returns only ready images.",
@@ -48817,7 +49338,7 @@ registerSchema({
48817
49338
  }
48818
49339
  }
48819
49340
  });
48820
- var libraryCommand = defineCommand152({
49341
+ var libraryCommand = defineCommand153({
48821
49342
  meta: {
48822
49343
  name: "library",
48823
49344
  description: "Search the company image library (hybrid BM25 + vector + Cohere rerank). Use this BEFORE any external provider.\n\nExample: baker images library 'hero banner' --aspect-ratio 16:9 --source magnific"
@@ -48880,7 +49401,7 @@ var libraryCommand = defineCommand152({
48880
49401
  });
48881
49402
 
48882
49403
  // src/commands/images/logo.ts
48883
- import { defineCommand as defineCommand153 } from "citty";
49404
+ import { defineCommand as defineCommand154 } from "citty";
48884
49405
  registerSchema({
48885
49406
  command: "images.logo",
48886
49407
  description: "Brand logo lookup via Brandfetch. Auto-ingests by default. Returns `brandMatch` \u2014 Brandfetch's own verdict on whose brand the domain is (confirmed | mismatch | unverified). Branch on it: `mismatch` means the mark is another company's, so do not place it. `confirmed` verifies the record, not the artwork \u2014 read the ingested row back to check the mark itself.",
@@ -48908,7 +49429,7 @@ registerSchema({
48908
49429
  full: { type: "boolean", description: "Include full metadata", required: false, default: false }
48909
49430
  }
48910
49431
  });
48911
- var logoCommand = defineCommand153({
49432
+ var logoCommand = defineCommand154({
48912
49433
  meta: {
48913
49434
  name: "logo",
48914
49435
  description: "Brand logo via Brandfetch. Returns up to 5 variants (icon, light/dark logo, light/dark symbol) plus `brandMatch`. Auto-ingests the first variant.\n\nStart here: check `brandMatch.verdict`.\n mismatch \u2192 the mark belongs to `brandMatch.name`, a different company. Do not place it.\n unverified \u2192 nothing confirmed whose logo this is. Treat as unchecked.\n confirmed \u2192 Brandfetch has this brand's record. That verifies the record, NOT the artwork \u2014 a confirmed domain has served another company's logo before.\n\n\u26A0 Whatever the verdict, read the ingested row back with `baker images get <imageId>` and check `textInImage`/`subject` before placing it. That is the only check that looks at the mark.\n\nExample: baker images logo stripe.com --variant logo"
@@ -48981,7 +49502,7 @@ var logoCommand = defineCommand153({
48981
49502
  });
48982
49503
 
48983
49504
  // src/commands/images/normalize.ts
48984
- import { defineCommand as defineCommand154 } from "citty";
49505
+ import { defineCommand as defineCommand155 } from "citty";
48985
49506
 
48986
49507
  // src/lib/image/color-changer.ts
48987
49508
  import quantize from "quantize";
@@ -49713,7 +50234,7 @@ function coerceRawArgs(args) {
49713
50234
  "dry-run": bool(args["dry-run"])
49714
50235
  };
49715
50236
  }
49716
- var normalizeCommand2 = defineCommand154({
50237
+ var normalizeCommand2 = defineCommand155({
49717
50238
  meta: {
49718
50239
  name: "normalize",
49719
50240
  description: `Normalize logos / images: declarative recolor + bg removal + trim + resize. Operates on local files; writes in-place by default.
@@ -49768,7 +50289,7 @@ Examples:
49768
50289
  });
49769
50290
 
49770
50291
  // src/commands/images/pinterest.ts
49771
- import { defineCommand as defineCommand155 } from "citty";
50292
+ import { defineCommand as defineCommand156 } from "citty";
49772
50293
  registerSchema({
49773
50294
  command: "images.pinterest",
49774
50295
  description: "Pinterest image search via ScrapeCreators. Reference-grade real-world photography, product styling, interiors, fashion, food, and aesthetic mood boards. Inspect before placing \u2014 Pinterest is unverified, trademark-bearing web content.",
@@ -49788,7 +50309,7 @@ registerSchema({
49788
50309
  }
49789
50310
  }
49790
50311
  });
49791
- var pinterestCommand = defineCommand155({
50312
+ var pinterestCommand = defineCommand156({
49792
50313
  meta: {
49793
50314
  name: "pinterest",
49794
50315
  description: "Pinterest image search via ScrapeCreators ($0.00188/request). Best for photo-realistic reference imagery \u2014 lifestyle, interiors, fashion, food, product styling, and mood boards to brief AI generation against. \u26A0 Unverified, trademark-bearing web content \u2014 inspect and respect rights before placing on a customer page. Browse first; auto-ingest only the pins you commit to.\n\nExamples:\n baker images pinterest 'scandinavian living room'\n baker images pinterest 'minimalist skincare product photography' --limit 20\n baker images pinterest 'cozy coffee shop interior' --auto-ingest 2 --context 'Mood reference for hero photography'"
@@ -49845,7 +50366,7 @@ var pinterestCommand = defineCommand155({
49845
50366
  });
49846
50367
 
49847
50368
  // src/commands/images/screenshot.ts
49848
- import { defineCommand as defineCommand156 } from "citty";
50369
+ import { defineCommand as defineCommand157 } from "citty";
49849
50370
  registerSchema({
49850
50371
  command: "images.screenshot",
49851
50372
  description: "Capture a website screenshot via ScreenshotOne. Auto-ingests on success.",
@@ -49864,7 +50385,7 @@ registerSchema({
49864
50385
  full: { type: "boolean", description: "Include full metadata", required: false, default: false }
49865
50386
  }
49866
50387
  });
49867
- var screenshotCommand = defineCommand156({
50388
+ var screenshotCommand = defineCommand157({
49868
50389
  meta: {
49869
50390
  name: "screenshot",
49870
50391
  description: "Screenshot a URL via ScreenshotOne. $0.009/capture. Auto-ingests to library.\n\nExample: baker images screenshot https://stripe.com --full-page"
@@ -49928,7 +50449,7 @@ var screenshotCommand = defineCommand156({
49928
50449
  });
49929
50450
 
49930
50451
  // src/commands/images/search.ts
49931
- import { defineCommand as defineCommand157 } from "citty";
50452
+ import { defineCommand as defineCommand158 } from "citty";
49932
50453
  registerSchema({
49933
50454
  command: "images.search",
49934
50455
  description: "Search images by text query. Only returns ready images.",
@@ -49944,7 +50465,7 @@ registerSchema({
49944
50465
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
49945
50466
  }
49946
50467
  });
49947
- var searchCommand = defineCommand157({
50468
+ var searchCommand = defineCommand158({
49948
50469
  meta: {
49949
50470
  name: "search",
49950
50471
  description: "Semantic search images by text query. Uses hybrid BM25 + vector + reranking. Example: baker images search 'hero banner' --aspect-ratio 16:9 --tags logo"
@@ -50004,7 +50525,7 @@ var searchCommand = defineCommand157({
50004
50525
  });
50005
50526
 
50006
50527
  // src/commands/images/sticker.ts
50007
- import { defineCommand as defineCommand158 } from "citty";
50528
+ import { defineCommand as defineCommand159 } from "citty";
50008
50529
  registerSchema({
50009
50530
  command: "images.sticker",
50010
50531
  description: "Search Giphy stickers \u2014 transparent-background overlays for ad creative.",
@@ -50036,7 +50557,7 @@ registerSchema({
50036
50557
  }
50037
50558
  }
50038
50559
  });
50039
- var stickerCommand = defineCommand158({
50560
+ var stickerCommand = defineCommand159({
50040
50561
  meta: {
50041
50562
  name: "sticker",
50042
50563
  description: "Search Giphy's sticker corpus \u2014 transparent-background WebPs / GIFs ideal for overlaying on ad creative (Meta, TikTok, Stories). Same Giphy free API as `baker images gif`; results carry WebP + GIF + MP4 URLs in providerMeta.\n\nExample: baker images sticker 'thumbs up' --limit 10\nExample: baker images sticker celebration --rating g --auto-ingest 3\nExample: baker images sticker --trending --limit 25"
@@ -50083,7 +50604,7 @@ var stickerCommand = defineCommand158({
50083
50604
  });
50084
50605
 
50085
50606
  // src/commands/images/stock.ts
50086
- import { defineCommand as defineCommand159 } from "citty";
50607
+ import { defineCommand as defineCommand160 } from "citty";
50087
50608
  var STOCK_ERROR_FIX = {
50088
50609
  action: "use_different_resource",
50089
50610
  explanation: "Switch provider instead of retrying stock search. Stock search is one of several image sources. Run `baker images find <query> --sources library,pinterest,google` (`--sources` is required \u2014 `find` alone searches the library only) or `baker studio generate` to make the asset. Never sleep-and-retry this command \u2014 the CLI already backs off on rate limits. If no image can be sourced, continue the rest of the task with a placeholder rather than aborting it."
@@ -50163,7 +50684,7 @@ function partialLibraryHints(errors, hitCount) {
50163
50684
  `These results are partial: ${names} did not answer, so they come from the remaining library alone. Treat a thin result as unproven rather than as "stock does not have this", and report the outage rather than re-running the search.`
50164
50685
  ];
50165
50686
  }
50166
- var stockCommand = defineCommand159({
50687
+ var stockCommand = defineCommand160({
50167
50688
  meta: {
50168
50689
  name: "stock",
50169
50690
  description: "Stock search across the free libraries \u2014 Pexels photographs plus Pixabay photographs, illustrations and vectors. No per-request cost. With --auto-ingest, ingested hits return Baker-owned URLs.\n\nExamples:\n baker images stock 'minimalist office'\n baker images stock 'hero photo of a kitchen' --orientation landscape --size large\n baker images stock 'flat office workers' --type illustration\n baker images stock 'leaf outline mark' --type vector\n baker images stock 'smiling carpenter portrait' --orientation portrait"
@@ -50238,7 +50759,7 @@ var stockCommand = defineCommand159({
50238
50759
  });
50239
50760
 
50240
50761
  // src/lib/tags-command.ts
50241
- import { defineCommand as defineCommand160 } from "citty";
50762
+ import { defineCommand as defineCommand161 } from "citty";
50242
50763
  function makeTagsCommand(command, label, endpoint) {
50243
50764
  registerSchema({
50244
50765
  command: `${command}.tags`,
@@ -50247,7 +50768,7 @@ function makeTagsCommand(command, label, endpoint) {
50247
50768
  output: { type: "string", description: "Output format: md|json", required: false, default: "md" }
50248
50769
  }
50249
50770
  });
50250
- return defineCommand160({
50771
+ return defineCommand161({
50251
50772
  meta: {
50252
50773
  name: "tags",
50253
50774
  description: `List the available ${label} tag names (defaults + company custom tags). Use before filtering with --tags. Example: baker ${command} tags`
@@ -50283,7 +50804,7 @@ function makeTagsCommand(command, label, endpoint) {
50283
50804
  var tagsCommand3 = makeTagsCommand("images", "image", "/api/images/tags");
50284
50805
 
50285
50806
  // src/commands/images/upload.ts
50286
- import { defineCommand as defineCommand161 } from "citty";
50807
+ import { defineCommand as defineCommand162 } from "citty";
50287
50808
  registerSchema({
50288
50809
  command: "images.upload",
50289
50810
  description: "Upload an image to the library \u2014 local file path or remote http(s) URL.",
@@ -50321,7 +50842,7 @@ registerSchema({
50321
50842
  function isRemoteUrl2(value) {
50322
50843
  return /^https?:\/\//i.test(value);
50323
50844
  }
50324
- var uploadCommand = defineCommand161({
50845
+ var uploadCommand = defineCommand162({
50325
50846
  meta: {
50326
50847
  name: "upload",
50327
50848
  description: "Upload an image to the library \u2014 accepts a local file path OR a remote http(s) URL.\n\nLocal: reads bytes, sends to /api/images/upload, content-type auto-detected from extension.\nRemote: dispatches to /api/images/ingest with hash-dedup on bytes + externalId.\n\nExamples:\n baker images upload ./logo.png --source uploaded\n baker images upload ./cert.png --context 'ISO 27001 badge \u2014 enterprise tier'\n baker images upload https://acme.com/hero.png --source firecrawl --context 'Acme competitor pricing hero'"
@@ -50414,7 +50935,7 @@ async function uploadLocal(target, args) {
50414
50935
  }
50415
50936
 
50416
50937
  // src/commands/images/upscale.ts
50417
- import { defineCommand as defineCommand162 } from "citty";
50938
+ import { defineCommand as defineCommand163 } from "citty";
50418
50939
  registerSchema({
50419
50940
  command: "images.upscale",
50420
50941
  description: "Upscale a library image via the backend (Replicate, cost-tracked). Waits for completion by default. The image must be status 'ready' and raster (not SVG/AVIF).",
@@ -50429,7 +50950,7 @@ registerSchema({
50429
50950
  }
50430
50951
  });
50431
50952
  var POLL_INTERVAL_MS4 = 1500;
50432
- var upscaleCommand = defineCommand162({
50953
+ var upscaleCommand = defineCommand163({
50433
50954
  meta: {
50434
50955
  name: "upscale",
50435
50956
  description: "Upscale a library image via the Convex backend (Replicate, cost-tracked at $0.05/image). Waits for completion by default.\n\nExample: baker images upscale j571abc123def\nExample: baker images upscale j571abc123def --max-wait 0 # fire-and-forget"
@@ -50484,7 +51005,7 @@ var upscaleCommand = defineCommand162({
50484
51005
  });
50485
51006
 
50486
51007
  // src/commands/images/use.ts
50487
- import { defineCommand as defineCommand163 } from "citty";
51008
+ import { defineCommand as defineCommand164 } from "citty";
50488
51009
  registerSchema({
50489
51010
  command: "images.use",
50490
51011
  description: "Ingest a URL and wait for the library record to be ready.",
@@ -50513,7 +51034,7 @@ function emitReady(ingestResult, doc, args) {
50513
51034
  args.full === true
50514
51035
  );
50515
51036
  }
50516
- var useCommand = defineCommand163({
51037
+ var useCommand = defineCommand164({
50517
51038
  meta: {
50518
51039
  name: "use",
50519
51040
  description: "Sugar over `ingest`: download \u2192 store \u2192 wait until describe + embed complete \u2192 return ready library record.\n\nExample: baker images use https://cdn.example.com/hero.png --source uploaded"
@@ -50562,7 +51083,7 @@ var useCommand = defineCommand163({
50562
51083
  });
50563
51084
 
50564
51085
  // src/commands/images/index.ts
50565
- var imagesCommand = defineCommand164({
51086
+ var imagesCommand = defineCommand165({
50566
51087
  meta: {
50567
51088
  name: "images",
50568
51089
  description: `Find, source, and normalize images. Subcommands route by provider so cost + license are explicit.
@@ -50591,6 +51112,7 @@ Library writes (run on the Convex backend):
50591
51112
  baker images get <id> Get a single record
50592
51113
  baker images group <id> Every image that arrived in the same set (carousel, page scrape)
50593
51114
  baker images delete <id> Delete a record
51115
+ baker images describe <id> --description "\u2026" Correct a wrong name/description/tags \u2014 what library search reads on
50594
51116
 
50595
51117
  Local transforms (operate on files in the sandbox, before upload):
50596
51118
  baker images download <url|imageId\u2026> [--out \u2026] Remote URL or library image \u2192 local file (never use curl)
@@ -50628,6 +51150,7 @@ Full guide: __tooling__/docs/tools/baker/images.md`
50628
51150
  search: searchCommand,
50629
51151
  upload: uploadCommand,
50630
51152
  delete: deleteCommand2,
51153
+ describe: describeCommand,
50631
51154
  download: downloadCommand,
50632
51155
  normalize: normalizeCommand2,
50633
51156
  crop: cropCommand,
@@ -50640,12 +51163,12 @@ Full guide: __tooling__/docs/tools/baker/images.md`
50640
51163
  });
50641
51164
 
50642
51165
  // src/commands/landing/index.ts
50643
- import { defineCommand as defineCommand176 } from "citty";
51166
+ import { defineCommand as defineCommand177 } from "citty";
50644
51167
 
50645
51168
  // src/commands/landing/critique.ts
50646
51169
  import { readdir as readdir14, readFile as readFile30, stat as stat9 } from "fs/promises";
50647
51170
  import path37 from "path";
50648
- import { defineCommand as defineCommand165 } from "citty";
51171
+ import { defineCommand as defineCommand166 } from "citty";
50649
51172
 
50650
51173
  // src/engine/landing/lib/constants.ts
50651
51174
  var OVERUSED_FONTS = /* @__PURE__ */ new Set([
@@ -51953,7 +52476,7 @@ function fail6(code, message, fix) {
51953
52476
  );
51954
52477
  process.exit(2);
51955
52478
  }
51956
- var critiqueCommand2 = defineCommand165({
52479
+ var critiqueCommand2 = defineCommand166({
51957
52480
  meta: {
51958
52481
  name: "critique",
51959
52482
  description: "Start here: `baker landing critique <slug>` after building or editing a landing. Deterministic design-quality critic (ADVISORY \u2014 findings never fail it). Flags the known AI design tells (gradient text, overused fonts, side-tab borders, cream palettes, buzzword copy, broken images) and positioning integrity (a concession ranking a competitor above the client, a rival named in a comparison) tiered block/warn/advisory, respecting the client's BRAND.md as the allowlist. Also records the critique that publishing requires \u2014 run it before finishing a landing."
@@ -52087,10 +52610,10 @@ async function isDir2(p) {
52087
52610
  }
52088
52611
 
52089
52612
  // src/commands/landing/inspiration/index.ts
52090
- import { defineCommand as defineCommand174 } from "citty";
52613
+ import { defineCommand as defineCommand175 } from "citty";
52091
52614
 
52092
52615
  // src/commands/landing/inspiration/add.ts
52093
- import { defineCommand as defineCommand166 } from "citty";
52616
+ import { defineCommand as defineCommand167 } from "citty";
52094
52617
 
52095
52618
  // src/commands/landing/inspiration/shared.ts
52096
52619
  var INSPIRATION_HINTS = {
@@ -52172,7 +52695,7 @@ registerSchema({
52172
52695
  note: { type: "string", description: "Why this page is worth keeping", required: false }
52173
52696
  }
52174
52697
  });
52175
- var addCommand = defineCommand166({
52698
+ var addCommand = defineCommand167({
52176
52699
  meta: {
52177
52700
  name: "add",
52178
52701
  description: "Add someone else's landing page to the reference library. Example: baker landing inspiration add https://linear.app --note 'the client likes this density'"
@@ -52214,7 +52737,7 @@ var addCommand = defineCommand166({
52214
52737
  // src/commands/landing/inspiration/code.ts
52215
52738
  import { mkdir as mkdir11, writeFile as writeFile15 } from "fs/promises";
52216
52739
  import path38 from "path";
52217
- import { defineCommand as defineCommand167 } from "citty";
52740
+ import { defineCommand as defineCommand168 } from "citty";
52218
52741
  registerSchema({
52219
52742
  command: "landing.inspiration.code",
52220
52743
  description: "Write a reference section's standalone HTML+CSS to .baker/inspiration/<id>/ so you can read how it is built. Reference only \u2014 the structure is the lesson, the words are not yours to reuse.",
@@ -52223,7 +52746,7 @@ registerSchema({
52223
52746
  full: { type: "boolean", description: "Print the markup inline as well as writing it", required: false }
52224
52747
  }
52225
52748
  });
52226
- var codeCommand = defineCommand167({
52749
+ var codeCommand = defineCommand168({
52227
52750
  meta: {
52228
52751
  name: "code",
52229
52752
  description: "Write one reference section's standalone markup to disk. Example: baker landing inspiration code k57abc\u2026 \u2014 read it for structure, then build your own."
@@ -52269,7 +52792,7 @@ var codeCommand = defineCommand167({
52269
52792
  });
52270
52793
 
52271
52794
  // src/commands/landing/inspiration/favorites.ts
52272
- import { defineCommand as defineCommand168 } from "citty";
52795
+ import { defineCommand as defineCommand169 } from "citty";
52273
52796
  registerSchema({
52274
52797
  command: "landing.inspiration.favorites",
52275
52798
  description: "List the reference sections this company has saved. This is what `search` looks at by default, so it is the client's own taste profile \u2014 read it before proposing a direction.",
@@ -52283,7 +52806,7 @@ registerSchema({
52283
52806
  }
52284
52807
  }
52285
52808
  });
52286
- var favoritesCommand = defineCommand168({
52809
+ var favoritesCommand = defineCommand169({
52287
52810
  meta: {
52288
52811
  name: "favorites",
52289
52812
  description: "List this company's saved reference sections. Example: baker landing inspiration favorites --type hero,pricing"
@@ -52363,7 +52886,7 @@ registerSchema({
52363
52886
  note: { type: "string", description: "Why this is worth keeping", required: false }
52364
52887
  }
52365
52888
  });
52366
- var favoriteCommand = defineCommand168({
52889
+ var favoriteCommand = defineCommand169({
52367
52890
  meta: {
52368
52891
  name: "favorite",
52369
52892
  description: "Save a reference section to this company. Example: baker landing inspiration favorite k57abc\u2026"
@@ -52401,7 +52924,7 @@ registerSchema({
52401
52924
  page: { type: "boolean", description: "Treat the id as a page rather than a section", required: false }
52402
52925
  }
52403
52926
  });
52404
- var unfavoriteCommand = defineCommand168({
52927
+ var unfavoriteCommand = defineCommand169({
52405
52928
  meta: {
52406
52929
  name: "unfavorite",
52407
52930
  description: "Remove a reference section from this company's saved set. Example: baker landing inspiration unfavorite k57abc\u2026"
@@ -52423,7 +52946,7 @@ var unfavoriteCommand = defineCommand168({
52423
52946
  });
52424
52947
 
52425
52948
  // src/commands/landing/inspiration/page.ts
52426
- import { defineCommand as defineCommand169 } from "citty";
52949
+ import { defineCommand as defineCommand170 } from "citty";
52427
52950
  registerSchema({
52428
52951
  command: "landing.inspiration.page",
52429
52952
  description: "Show a whole reference page as a sequence: every section top to bottom with its type and the idea behind it. This is the view to use when the question is how a good page is ORDERED rather than what one section looks like.",
@@ -52440,7 +52963,7 @@ registerSchema({
52440
52963
  }
52441
52964
  }
52442
52965
  });
52443
- var pageCommand2 = defineCommand169({
52966
+ var pageCommand2 = defineCommand170({
52444
52967
  meta: {
52445
52968
  name: "page",
52446
52969
  description: "Show how a reference page sequences its sections. Example: baker landing inspiration page j91xyz\u2026 \u2014 the blueprint, not the pixels."
@@ -52503,7 +53026,7 @@ var pageCommand2 = defineCommand169({
52503
53026
  });
52504
53027
 
52505
53028
  // src/commands/landing/inspiration/scrape.ts
52506
- import { defineCommand as defineCommand170 } from "citty";
53029
+ import { defineCommand as defineCommand171 } from "citty";
52507
53030
 
52508
53031
  // src/engine/landing-library/proxyFailure.ts
52509
53032
  var PROXY_STATUS = 407;
@@ -54643,7 +55166,7 @@ registerSchema({
54643
55166
  report: { type: "boolean", description: "Write report.html. `--no-report` to skip", required: false }
54644
55167
  }
54645
55168
  });
54646
- var scrapeCommand = defineCommand170({
55169
+ var scrapeCommand = defineCommand171({
54647
55170
  meta: {
54648
55171
  name: "scrape",
54649
55172
  description: "Capture a landing page to a directory, now. Example: baker landing inspiration scrape https://linear.app --out .baker/inspiration/linear.app"
@@ -54752,7 +55275,7 @@ var scrapeCommand = defineCommand170({
54752
55275
 
54753
55276
  // src/commands/landing/inspiration/search.ts
54754
55277
  import path43 from "path";
54755
- import { defineCommand as defineCommand171 } from "citty";
55278
+ import { defineCommand as defineCommand172 } from "citty";
54756
55279
 
54757
55280
  // src/commands/landing/inspiration/shot.ts
54758
55281
  import { mkdir as mkdir13, writeFile as writeFile18 } from "fs/promises";
@@ -54889,7 +55412,7 @@ async function downloadShots(results) {
54889
55412
  );
54890
55413
  return saved;
54891
55414
  }
54892
- var searchCommand2 = defineCommand171({
55415
+ var searchCommand2 = defineCommand172({
54893
55416
  meta: {
54894
55417
  name: "search",
54895
55418
  description: "Search real landing-page sections for reference. Example: baker landing inspiration search 'dark developer hero with a terminal' --register dev-tool-minimal --scope all"
@@ -54987,7 +55510,7 @@ var searchCommand2 = defineCommand171({
54987
55510
  });
54988
55511
 
54989
55512
  // src/commands/landing/inspiration/sequences.ts
54990
- import { defineCommand as defineCommand172 } from "citty";
55513
+ import { defineCommand as defineCommand173 } from "citty";
54991
55514
  var COMPACT_TRANSITIONS = 12;
54992
55515
  var COMPACT_ENDS = 5;
54993
55516
  var MAX_PAGES = 50;
@@ -55061,7 +55584,7 @@ function sequencesHints(data, { scope, full }) {
55061
55584
  if (scope === "favorites") hints2.push(...favoritesScopeHints(data.favoritesHealth, data.pagesReturned));
55062
55585
  return hints2;
55063
55586
  }
55064
- var sequencesCommand = defineCommand172({
55587
+ var sequencesCommand = defineCommand173({
55065
55588
  meta: {
55066
55589
  name: "sequences",
55067
55590
  description: "What section follows what, across many real pages at once. Example: baker landing inspiration sequences 'developer tool pricing page' --scope all \u2014 the evidence for how to order a page you are about to build."
@@ -55111,7 +55634,7 @@ var sequencesCommand = defineCommand172({
55111
55634
 
55112
55635
  // src/commands/landing/inspiration/view.ts
55113
55636
  import path44 from "path";
55114
- import { defineCommand as defineCommand173 } from "citty";
55637
+ import { defineCommand as defineCommand174 } from "citty";
55115
55638
  registerSchema({
55116
55639
  command: "landing.inspiration.view",
55117
55640
  description: "Everything known about one section: composition, motion, design tokens, the copy it uses, why it works, and what must change to make it yours. Downloads the desktop and mobile screenshots plus the motion filmstrip so you can look at them.",
@@ -55124,7 +55647,7 @@ registerSchema({
55124
55647
  }
55125
55648
  }
55126
55649
  });
55127
- var viewCommand2 = defineCommand173({
55650
+ var viewCommand2 = defineCommand174({
55128
55651
  meta: {
55129
55652
  name: "view",
55130
55653
  description: "Full detail for one reference section. Example: baker landing inspiration view k57abc\u2026 \u2014 read the screenshots it saves before you build."
@@ -55208,7 +55731,7 @@ var viewCommand2 = defineCommand173({
55208
55731
  });
55209
55732
 
55210
55733
  // src/commands/landing/inspiration/index.ts
55211
- var inspirationCommand = defineCommand174({
55734
+ var inspirationCommand = defineCommand175({
55212
55735
  meta: {
55213
55736
  name: "inspiration",
55214
55737
  description: `Reference library of real landing-page sections \u2014 look at how good pages actually solve a problem before you design one.
@@ -55256,7 +55779,7 @@ Full guide: __tooling__/docs/tools/baker/landing.md`
55256
55779
  import { randomUUID as randomUUID2 } from "crypto";
55257
55780
  import { mkdir as mkdir14, readdir as readdir15, readFile as readFile31, stat as stat10, writeFile as writeFile19 } from "fs/promises";
55258
55781
  import path45 from "path";
55259
- import { defineCommand as defineCommand175 } from "citty";
55782
+ import { defineCommand as defineCommand176 } from "citty";
55260
55783
  registerSchema({
55261
55784
  command: "landing.variant",
55262
55785
  description: "Create a new variant of a page, for an A/B test. The variant shares the page's sections instead of copying them, so the only difference between the two is the one you fork \u2014 which is the only way the test measures what you think it measures. Run `baker experiment plan` FIRST: most pages cannot settle most questions.",
@@ -55484,7 +56007,7 @@ async function writeVariant(opts) {
55484
56007
  await writeFile19(path45.join(variantDir, "_images", ".gitkeep"), "", "utf8");
55485
56008
  return written;
55486
56009
  }
55487
- var variantCommand = defineCommand175({
56010
+ var variantCommand = defineCommand176({
55488
56011
  meta: {
55489
56012
  name: "variant",
55490
56013
  description: "Create a new variant of a page, for an A/B test. The variant SHARES the page's sections and forks only the component you name, so the two versions differ by exactly the thing you are testing. It is a version of that page, not a page of its own: it is never indexed, never listed, and has no public URL \u2014 both versions answer at the page's own address. Run `baker experiment plan` first \u2014 most pages do not have the traffic to settle most questions."
@@ -55564,7 +56087,7 @@ var variantCommand = defineCommand175({
55564
56087
  });
55565
56088
 
55566
56089
  // src/commands/landing/index.ts
55567
- var landingCommand = defineCommand176({
56090
+ var landingCommand = defineCommand177({
55568
56091
  meta: {
55569
56092
  name: "landing",
55570
56093
  description: `Design-quality tools for landing pages (src/pages/<slug>/).
@@ -55584,7 +56107,7 @@ Subcommands:
55584
56107
  });
55585
56108
 
55586
56109
  // src/commands/mcp/index.ts
55587
- import { defineCommand as defineCommand177 } from "citty";
56110
+ import { defineCommand as defineCommand178 } from "citty";
55588
56111
 
55589
56112
  // src/commands/mcp/platforms.ts
55590
56113
  function readsKey(label) {
@@ -55653,7 +56176,7 @@ registerSchema({
55653
56176
  description: "List everything this chat can reach: managed integrations (Attio, Slack, Gmail, Google Sheets, \u2026), custom MCP servers, and the platforms the company signed in to (HubSpot, Google Ads, GA4, Search Console, Tag Manager) which you read through their own `baker` commands. Start here when the user mentions an external tool or platform.",
55654
56177
  args: {}
55655
56178
  });
55656
- var connectedCommand = defineCommand177({
56179
+ var connectedCommand = defineCommand178({
55657
56180
  meta: {
55658
56181
  name: "connected",
55659
56182
  description: `Everything this chat can reach \u2014 managed integrations, custom MCP servers, and connected platforms.
@@ -55712,7 +56235,7 @@ registerSchema({
55712
56235
  description: "List the custom MCP servers this company's chats see (org + company + your own user scope).",
55713
56236
  args: {}
55714
56237
  });
55715
- var listCommand15 = defineCommand177({
56238
+ var listCommand15 = defineCommand178({
55716
56239
  meta: { name: "list", description: "List custom MCP servers visible to this company's chats." },
55717
56240
  run: async () => {
55718
56241
  try {
@@ -55749,7 +56272,7 @@ registerSchema({
55749
56272
  header: { type: "string", description: 'Auth header "Key: Value" (repeatable)', required: false }
55750
56273
  }
55751
56274
  });
55752
- var addCommand2 = defineCommand177({
56275
+ var addCommand2 = defineCommand178({
55753
56276
  meta: {
55754
56277
  name: "add",
55755
56278
  description: `Register a custom MCP server. Tools appear as mcp__<name>__* on the NEXT message.
@@ -55801,7 +56324,7 @@ registerSchema({
55801
56324
  description: "Remove a company custom MCP server by name.",
55802
56325
  args: { name: { type: "string", description: "Server name to remove", required: true } }
55803
56326
  });
55804
- var removeCommand5 = defineCommand177({
56327
+ var removeCommand5 = defineCommand178({
55805
56328
  meta: {
55806
56329
  name: "remove",
55807
56330
  description: `Remove a company custom MCP server by name.
@@ -55823,7 +56346,7 @@ Example:
55823
56346
  }
55824
56347
  }
55825
56348
  });
55826
- var mcpCommand = defineCommand177({
56349
+ var mcpCommand = defineCommand178({
55827
56350
  meta: {
55828
56351
  name: "mcp",
55829
56352
  description: `Third-party tools for this company \u2014 see what's connected, register custom HTTPS MCP endpoints.
@@ -55849,10 +56372,10 @@ Full guide: __tooling__/docs/tools/baker/mcp.md`
55849
56372
  });
55850
56373
 
55851
56374
  // src/commands/research/index.ts
55852
- import { defineCommand as defineCommand190 } from "citty";
56375
+ import { defineCommand as defineCommand191 } from "citty";
55853
56376
 
55854
56377
  // src/commands/research/advertisers.ts
55855
- import { defineCommand as defineCommand178 } from "citty";
56378
+ import { defineCommand as defineCommand179 } from "citty";
55856
56379
 
55857
56380
  // src/commands/research/hints.ts
55858
56381
  var AD_COPY_ROUTE = 'This returns competing DOMAINS and their SERP economics \u2014 no ad copy, no headlines, no creative. For the actual copy of a competitor\'s ads: `baker winning-ads advertisers "<brand>"` \u2192 `baker winning-ads search "<brief>" --advertiser-id <id>` \u2192 `baker winning-ads content <adId>` (`primary_text` / `headline` / `cta`, plus the spoken transcript and on-screen text for video). That corpus is Meta and LinkedIn only \u2014 Google SERP ad copy is not available through any Baker command, so do not keep querying for it here.';
@@ -56054,7 +56577,7 @@ var FIELDS3 = {
56054
56577
  etv: "Estimated traffic value (USD)",
56055
56578
  visibility: "SERP visibility score (0-1)"
56056
56579
  };
56057
- var advertisersCommand = defineCommand178({
56580
+ var advertisersCommand = defineCommand179({
56058
56581
  meta: {
56059
56582
  name: "advertisers",
56060
56583
  description: `Domains competing for a keyword in Google SERPs, with position, relevance, traffic value and visibility. Returns NO ad copy \u2014 for a competitor's headlines and body copy use \`baker winning-ads content <adId>\` (Meta/LinkedIn only).
@@ -56110,7 +56633,7 @@ Examples:
56110
56633
  });
56111
56634
 
56112
56635
  // src/commands/research/autocomplete.ts
56113
- import { defineCommand as defineCommand179 } from "citty";
56636
+ import { defineCommand as defineCommand180 } from "citty";
56114
56637
  registerSchema({
56115
56638
  command: "research.autocomplete",
56116
56639
  description: "Get Google Autocomplete suggestions for a seed keyword. Useful for keyword expansion and discovering what people actually search for. IMPORTANT: If --location and --language are omitted, defaults to United States (us) and English (en).",
@@ -56133,7 +56656,7 @@ registerSchema({
56133
56656
  var FIELDS4 = {
56134
56657
  suggestion: "Autocomplete suggestion from Google"
56135
56658
  };
56136
- var autocompleteCommand = defineCommand179({
56659
+ var autocompleteCommand = defineCommand180({
56137
56660
  meta: {
56138
56661
  name: "autocomplete",
56139
56662
  description: `Get Google Autocomplete suggestions for keyword expansion.
@@ -56188,7 +56711,7 @@ Examples:
56188
56711
  });
56189
56712
 
56190
56713
  // src/commands/research/countries.ts
56191
- import { defineCommand as defineCommand180 } from "citty";
56714
+ import { defineCommand as defineCommand181 } from "citty";
56192
56715
  registerSchema({
56193
56716
  command: "research.countries",
56194
56717
  description: "List all supported country codes for --location flag in research commands.",
@@ -56245,7 +56768,7 @@ var FIELDS5 = {
56245
56768
  code: "Country code to pass as --location",
56246
56769
  name: "Country name"
56247
56770
  };
56248
- var countriesCommand = defineCommand180({
56771
+ var countriesCommand = defineCommand181({
56249
56772
  meta: {
56250
56773
  name: "countries",
56251
56774
  description: "List all supported country codes for --location flag."
@@ -56256,7 +56779,7 @@ var countriesCommand = defineCommand180({
56256
56779
  });
56257
56780
 
56258
56781
  // src/commands/research/fetch.ts
56259
- import { defineCommand as defineCommand181 } from "citty";
56782
+ import { defineCommand as defineCommand182 } from "citty";
56260
56783
  var CONTENT_PREVIEW_CHARS = 2e4;
56261
56784
  var TIMEOUT_MS = 18e4;
56262
56785
  registerSchema({
@@ -56329,7 +56852,7 @@ function fetchFix(code) {
56329
56852
  explanation: "This read failed. Don't build a retry ladder around it \u2014 finish the rest of the job with what you can reach and name this page as a gap."
56330
56853
  };
56331
56854
  }
56332
- var fetchCommand = defineCommand181({
56855
+ var fetchCommand = defineCommand182({
56333
56856
  meta: {
56334
56857
  name: "fetch",
56335
56858
  description: `Read a page an ordinary web fetch could not. Bot walls and JavaScript-rendered pages are resolved for you \u2014 you never have to retry, wait, or drive a browser yourself.
@@ -56406,7 +56929,7 @@ Examples:
56406
56929
  });
56407
56930
 
56408
56931
  // src/commands/research/intent.ts
56409
- import { defineCommand as defineCommand182 } from "citty";
56932
+ import { defineCommand as defineCommand183 } from "citty";
56410
56933
  registerSchema({
56411
56934
  command: "research.intent",
56412
56935
  description: "Classify Google Search intent for keywords. Determines if someone searching is looking to buy, research, or navigate. IMPORTANT: If --language is omitted, defaults to English (en). The response includes a query_context object showing which language was used.",
@@ -56429,7 +56952,7 @@ var FIELDS7 = {
56429
56952
  intent: "Primary Google Search intent: informational, navigational, commercial, transactional",
56430
56953
  probability: "Confidence score 0.0-1.0"
56431
56954
  };
56432
- var intentCommand = defineCommand182({
56955
+ var intentCommand = defineCommand183({
56433
56956
  meta: {
56434
56957
  name: "intent",
56435
56958
  description: `Classify Google Search intent for keywords. Returns intent type and confidence.
@@ -56477,7 +57000,7 @@ Examples:
56477
57000
  });
56478
57001
 
56479
57002
  // src/commands/research/keyword-gap.ts
56480
- import { defineCommand as defineCommand183 } from "citty";
57003
+ import { defineCommand as defineCommand184 } from "citty";
56481
57004
  registerSchema({
56482
57005
  command: "research.keyword-gap",
56483
57006
  description: "Find keywords a competitor ranks for (organic or paid) that you don't. Discovers expansion opportunities. IMPORTANT: If --location and --language are omitted, defaults to United States (us) and English (en). The response includes a query_context object showing which location/language were used.",
@@ -56506,7 +57029,7 @@ var FIELDS8 = {
56506
57029
  cpc: "Cost per click USD",
56507
57030
  their_position: "Competitor's ranking position"
56508
57031
  };
56509
- var keywordGapCommand = defineCommand183({
57032
+ var keywordGapCommand = defineCommand184({
56510
57033
  meta: {
56511
57034
  name: "keyword-gap",
56512
57035
  description: `Find keywords a competitor has that you don't. Supports pagination via --offset.
@@ -56581,7 +57104,7 @@ Examples:
56581
57104
  });
56582
57105
 
56583
57106
  // src/commands/research/keyword-metrics.ts
56584
- import { defineCommand as defineCommand184 } from "citty";
57107
+ import { defineCommand as defineCommand185 } from "citty";
56585
57108
 
56586
57109
  // src/commands/research/keyword-metrics-rows.ts
56587
57110
  var KEYWORD_METRICS_SOURCE = "keyword_planner_estimate_via_dataforseo";
@@ -56661,7 +57184,7 @@ registerSchema({
56661
57184
  "no-cache": { type: "boolean", description: "Skip server cache, hit API directly", required: false }
56662
57185
  }
56663
57186
  });
56664
- var keywordMetricsCommand = defineCommand184({
57187
+ var keywordMetricsCommand = defineCommand185({
56665
57188
  meta: {
56666
57189
  name: "keyword-metrics",
56667
57190
  description: `Volume, CPC and competition for keywords you name. No domain required.
@@ -56743,7 +57266,7 @@ Examples:
56743
57266
  });
56744
57267
 
56745
57268
  // src/commands/research/keywords-for-site.ts
56746
- import { defineCommand as defineCommand185 } from "citty";
57269
+ import { defineCommand as defineCommand186 } from "citty";
56747
57270
  registerSchema({
56748
57271
  command: "research.keywords-for-site",
56749
57272
  description: "Get keywords a competitor targets in Google. Use --type paid to see only paid keywords, --type organic for organic only. IMPORTANT: If --location and --language are omitted, defaults to United States (us) and English (en). The response includes a query_context object showing which location/language were used.",
@@ -56776,7 +57299,7 @@ var FIELDS9 = {
56776
57299
  competition: "LOW, MEDIUM, or HIGH",
56777
57300
  competition_index: "Competition score 0-100"
56778
57301
  };
56779
- var keywordsForSiteCommand = defineCommand185({
57302
+ var keywordsForSiteCommand = defineCommand186({
56780
57303
  meta: {
56781
57304
  name: "keywords-for-site",
56782
57305
  description: `Get keywords a competitor targets in Google. Use --type to filter paid/organic.
@@ -56838,7 +57361,7 @@ Examples:
56838
57361
  });
56839
57362
 
56840
57363
  // src/commands/research/languages.ts
56841
- import { defineCommand as defineCommand186 } from "citty";
57364
+ import { defineCommand as defineCommand187 } from "citty";
56842
57365
  registerSchema({
56843
57366
  command: "research.languages",
56844
57367
  description: "List all supported language codes for --language flag in research commands.",
@@ -56868,7 +57391,7 @@ var FIELDS10 = {
56868
57391
  code: "Language code to pass as --language",
56869
57392
  name: "Language name (also accepted by --language)"
56870
57393
  };
56871
- var languagesCommand2 = defineCommand186({
57394
+ var languagesCommand2 = defineCommand187({
56872
57395
  meta: {
56873
57396
  name: "languages",
56874
57397
  description: "List all supported language codes for --language flag."
@@ -56879,7 +57402,7 @@ var languagesCommand2 = defineCommand186({
56879
57402
  });
56880
57403
 
56881
57404
  // src/commands/research/lighthouse.ts
56882
- import { defineCommand as defineCommand187 } from "citty";
57405
+ import { defineCommand as defineCommand188 } from "citty";
56883
57406
  registerSchema({
56884
57407
  command: "research.lighthouse",
56885
57408
  description: "Landing page performance audit. Returns metrics that affect Google Ads Quality Score and CPC.",
@@ -56898,7 +57421,7 @@ var FIELDS11 = {
56898
57421
  speed_index_ms: "Speed Index in ms (good: < 3400)",
56899
57422
  interactive_ms: "Time to Interactive in ms (good: < 3800)"
56900
57423
  };
56901
- var lighthouseCommand = defineCommand187({
57424
+ var lighthouseCommand = defineCommand188({
56902
57425
  meta: {
56903
57426
  name: "lighthouse",
56904
57427
  description: `Landing page performance audit. Metrics affecting Google Ads Quality Score.
@@ -56936,7 +57459,7 @@ Examples:
56936
57459
  });
56937
57460
 
56938
57461
  // src/commands/research/relevant-pages.ts
56939
- import { defineCommand as defineCommand188 } from "citty";
57462
+ import { defineCommand as defineCommand189 } from "citty";
56940
57463
  registerSchema({
56941
57464
  command: "research.relevant-pages",
56942
57465
  description: "Get the top pages of a competitor domain with organic traffic and ranking data. Shows which pages drive the most traffic. IMPORTANT: If --location and --language are omitted, defaults to United States (us) and English (en).",
@@ -56962,7 +57485,7 @@ var FIELDS12 = {
56962
57485
  keywords: "Total organic keywords the page ranks for",
56963
57486
  top_10: "Keywords in positions 1-10"
56964
57487
  };
56965
- var relevantPagesCommand = defineCommand188({
57488
+ var relevantPagesCommand = defineCommand189({
56966
57489
  meta: {
56967
57490
  name: "relevant-pages",
56968
57491
  description: `Get the top pages of a competitor domain with traffic data.
@@ -57009,7 +57532,7 @@ Examples:
57009
57532
  });
57010
57533
 
57011
57534
  // src/commands/research/web.ts
57012
- import { defineCommand as defineCommand189 } from "citty";
57535
+ import { defineCommand as defineCommand190 } from "citty";
57013
57536
  registerSchema({
57014
57537
  command: "research.web",
57015
57538
  description: "Search the web with AI to answer marketing questions \u2014 competitors, ICP, pricing, pain points, market trends. Three depth levels: medium (quick, default), high (thorough), xhigh (exhaustive deep research).",
@@ -57060,7 +57583,7 @@ async function runDeepResearch(question) {
57060
57583
  }
57061
57584
  throw new Error("Deep research timed out");
57062
57585
  }
57063
- var webCommand = defineCommand189({
57586
+ var webCommand = defineCommand190({
57064
57587
  meta: {
57065
57588
  name: "web",
57066
57589
  description: `Search the web with AI to answer any open-ended marketing question. Uses live internet data via Google Search.
@@ -57122,7 +57645,7 @@ Examples:
57122
57645
  });
57123
57646
 
57124
57647
  // src/commands/research/index.ts
57125
- var researchCommand = defineCommand190({
57648
+ var researchCommand = defineCommand191({
57126
57649
  meta: {
57127
57650
  name: "research",
57128
57651
  description: `Competitive intelligence and AI-powered research commands.
@@ -57169,10 +57692,10 @@ Full guide: __tooling__/docs/tools/baker/research.md`
57169
57692
  });
57170
57693
 
57171
57694
  // src/commands/scheduled-actions/index.ts
57172
- import { defineCommand as defineCommand198 } from "citty";
57695
+ import { defineCommand as defineCommand199 } from "citty";
57173
57696
 
57174
57697
  // src/commands/scheduled-actions/create.ts
57175
- import { defineCommand as defineCommand191 } from "citty";
57698
+ import { defineCommand as defineCommand192 } from "citty";
57176
57699
 
57177
57700
  // src/commands/scheduled-actions/shared.ts
57178
57701
  var TEMP_SCHEDULED_ACTION_PREFIX = "temp_sched_";
@@ -57328,7 +57851,7 @@ registerSchema({
57328
57851
  }
57329
57852
  }
57330
57853
  });
57331
- var createCommand3 = defineCommand191({
57854
+ var createCommand3 = defineCommand192({
57332
57855
  meta: {
57333
57856
  name: "create",
57334
57857
  description: 'Stage a scheduled action. Example: baker scheduled-actions create --name "Weekly report" --description "..." --cron "0 9 * * MON"'
@@ -57387,7 +57910,7 @@ var createCommand3 = defineCommand191({
57387
57910
  });
57388
57911
 
57389
57912
  // src/commands/scheduled-actions/delete.ts
57390
- import { defineCommand as defineCommand192 } from "citty";
57913
+ import { defineCommand as defineCommand193 } from "citty";
57391
57914
  registerSchema({
57392
57915
  command: "scheduled-actions.delete",
57393
57916
  description: "Stage deletion of a published scheduled action or cancellation of a temp_sched_* draft creation.",
@@ -57395,7 +57918,7 @@ registerSchema({
57395
57918
  id: { type: "string", description: "Published scheduled action ID or temp_sched_* draft ID", required: true }
57396
57919
  }
57397
57920
  });
57398
- var deleteCommand3 = defineCommand192({
57921
+ var deleteCommand3 = defineCommand193({
57399
57922
  meta: {
57400
57923
  name: "delete",
57401
57924
  description: "Stage scheduled action deletion. Example: baker scheduled-actions delete <id-or-temp_sched_id>"
@@ -57424,7 +57947,7 @@ var deleteCommand3 = defineCommand192({
57424
57947
  });
57425
57948
 
57426
57949
  // src/commands/scheduled-actions/get.ts
57427
- import { defineCommand as defineCommand193 } from "citty";
57950
+ import { defineCommand as defineCommand194 } from "citty";
57428
57951
  registerSchema({
57429
57952
  command: "scheduled-actions.get",
57430
57953
  description: "Get a published scheduled action or a temp_sched_* draft-created scheduled action.",
@@ -57433,7 +57956,7 @@ registerSchema({
57433
57956
  chat: { type: "string", description: CHAT_READ_ARG.description, required: false }
57434
57957
  }
57435
57958
  });
57436
- var getCommand4 = defineCommand193({
57959
+ var getCommand4 = defineCommand194({
57437
57960
  meta: {
57438
57961
  name: "get",
57439
57962
  description: "Get a scheduled action. Example: baker scheduled-actions get <id-or-temp_sched_id>"
@@ -57472,7 +57995,7 @@ var getCommand4 = defineCommand193({
57472
57995
  });
57473
57996
 
57474
57997
  // src/commands/scheduled-actions/list.ts
57475
- import { defineCommand as defineCommand194 } from "citty";
57998
+ import { defineCommand as defineCommand195 } from "citty";
57476
57999
  registerSchema({
57477
58000
  command: "scheduled-actions.list",
57478
58001
  description: "List published scheduled actions. Includes draft state when BAKER_CHAT_ID is set, or --chat <id> to read an earlier chat's staged schedules instead.",
@@ -57480,7 +58003,7 @@ registerSchema({
57480
58003
  chat: { type: "string", description: CHAT_READ_ARG.description, required: false }
57481
58004
  }
57482
58005
  });
57483
- var listCommand16 = defineCommand194({
58006
+ var listCommand16 = defineCommand195({
57484
58007
  meta: {
57485
58008
  name: "list",
57486
58009
  description: "List scheduled actions. Includes staged draft ops when BAKER_CHAT_ID is set, or --chat <id> to read an earlier chat's staged schedules instead."
@@ -57505,7 +58028,7 @@ var listCommand16 = defineCommand194({
57505
58028
  // src/commands/scheduled-actions/templates.ts
57506
58029
  import { readFile as readFile32 } from "fs/promises";
57507
58030
  import path46 from "path";
57508
- import { defineCommand as defineCommand195 } from "citty";
58031
+ import { defineCommand as defineCommand196 } from "citty";
57509
58032
  registerSchema({
57510
58033
  command: "scheduled-actions.templates",
57511
58034
  description: "The recipes available to this company: the ones Baker ships plus the ones they wrote themselves, each with its id, what it produces and how often it is meant to run. Read this before proposing a company's automation, so every recipe you name is one that exists. Also how a company gets a recipe of its own \u2014 save a brief, prove it runs, then publish it.",
@@ -57560,7 +58083,7 @@ registerSchema({
57560
58083
  }
57561
58084
  }
57562
58085
  });
57563
- var templatesCommand = defineCommand195({
58086
+ var templatesCommand = defineCommand196({
57564
58087
  meta: {
57565
58088
  name: "templates",
57566
58089
  description: `The recipes this company can run \u2014 Baker's own plus theirs, with what each one produces.
@@ -57652,7 +58175,7 @@ Full guide: __tooling__/docs/tools/baker/scheduled-actions.md`
57652
58175
  });
57653
58176
 
57654
58177
  // src/commands/scheduled-actions/trigger.ts
57655
- import { defineCommand as defineCommand196 } from "citty";
58178
+ import { defineCommand as defineCommand197 } from "citty";
57656
58179
  registerSchema({
57657
58180
  command: "scheduled-actions.trigger",
57658
58181
  description: "Immediately trigger a published scheduled action. Does not require BAKER_CHAT_ID and rejects temp_sched_* IDs.",
@@ -57660,7 +58183,7 @@ registerSchema({
57660
58183
  id: { type: "string", description: "Published scheduled action ID", required: true }
57661
58184
  }
57662
58185
  });
57663
- var triggerCommand = defineCommand196({
58186
+ var triggerCommand = defineCommand197({
57664
58187
  meta: {
57665
58188
  name: "trigger",
57666
58189
  description: "Immediately trigger a published scheduled action. Example: baker scheduled-actions trigger <id>"
@@ -57697,7 +58220,7 @@ var triggerCommand = defineCommand196({
57697
58220
  });
57698
58221
 
57699
58222
  // src/commands/scheduled-actions/update.ts
57700
- import { defineCommand as defineCommand197 } from "citty";
58223
+ import { defineCommand as defineCommand198 } from "citty";
57701
58224
  registerSchema({
57702
58225
  command: "scheduled-actions.update",
57703
58226
  description: "Stage an update to a published scheduled action or temp_sched_* draft-created scheduled action.",
@@ -57728,7 +58251,7 @@ registerSchema({
57728
58251
  prompt: { type: "string", description: "Replacement additional spawned-agent instructions", required: false }
57729
58252
  }
57730
58253
  });
57731
- var updateCommand4 = defineCommand197({
58254
+ var updateCommand4 = defineCommand198({
57732
58255
  meta: {
57733
58256
  name: "update",
57734
58257
  description: "Stage a scheduled action update. Examples: baker scheduled-actions update <id> --enabled false | baker scheduled-actions update <id> --mode publish"
@@ -57806,7 +58329,7 @@ var updateCommand4 = defineCommand197({
57806
58329
  });
57807
58330
 
57808
58331
  // src/commands/scheduled-actions/index.ts
57809
- var scheduledActionsCommand = defineCommand198({
58332
+ var scheduledActionsCommand = defineCommand199({
57810
58333
  meta: {
57811
58334
  name: "scheduled-actions",
57812
58335
  description: `Manage Scheduled Actions. Subcommands: list, get, create, update, delete, trigger, templates.
@@ -57835,14 +58358,14 @@ Full guide: __tooling__/docs/tools/baker/scheduled-actions.md`
57835
58358
  });
57836
58359
 
57837
58360
  // src/commands/schema.ts
57838
- import { defineCommand as defineCommand199 } from "citty";
58361
+ import { defineCommand as defineCommand200 } from "citty";
57839
58362
  function narrowToFamily(commandName, available) {
57840
58363
  const segments = commandName.split(".");
57841
58364
  const prefix = segments[0] === "ads" && segments[1] ? `ads.${segments[1]}.` : `${segments[0]}.`;
57842
58365
  const siblings = available.filter((name) => name.startsWith(prefix));
57843
58366
  return siblings.length > 0 ? siblings : available;
57844
58367
  }
57845
- var schemaCommand2 = defineCommand199({
58368
+ var schemaCommand2 = defineCommand200({
57846
58369
  meta: {
57847
58370
  name: "schema",
57848
58371
  description: "Inspect command argument schemas (for AI agent introspection). Lists all commands if no argument given. Example: baker schema images.search"
@@ -57886,10 +58409,10 @@ var schemaCommand2 = defineCommand199({
57886
58409
  });
57887
58410
 
57888
58411
  // src/commands/studio/index.ts
57889
- import { defineCommand as defineCommand208 } from "citty";
58412
+ import { defineCommand as defineCommand209 } from "citty";
57890
58413
 
57891
58414
  // src/commands/studio/animate.ts
57892
- import { defineCommand as defineCommand200 } from "citty";
58415
+ import { defineCommand as defineCommand201 } from "citty";
57893
58416
 
57894
58417
  // src/commands/studio/batch.ts
57895
58418
  function projectBatch(generation, full) {
@@ -58366,7 +58889,7 @@ function costHintsFor(body) {
58366
58889
  }
58367
58890
  return hints2;
58368
58891
  }
58369
- var animateCommand = defineCommand200({
58892
+ var animateCommand = defineCommand201({
58370
58893
  meta: {
58371
58894
  name: "animate",
58372
58895
  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"
@@ -58490,7 +59013,7 @@ var animateCommand = defineCommand200({
58490
59013
  });
58491
59014
 
58492
59015
  // src/commands/studio/generate.ts
58493
- import { defineCommand as defineCommand201 } from "citty";
59016
+ import { defineCommand as defineCommand202 } from "citty";
58494
59017
  var MODEL_LIST2 = IMAGE_MODEL_IDS;
58495
59018
  var DEFAULT_MAX_WAIT_MS2 = 24e4;
58496
59019
  registerSchema({
@@ -58626,7 +59149,7 @@ function buildGenerateBody(args, prompt) {
58626
59149
  }
58627
59150
  return body;
58628
59151
  }
58629
- var generateCommand = defineCommand201({
59152
+ var generateCommand = defineCommand202({
58630
59153
  meta: {
58631
59154
  name: "generate",
58632
59155
  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]]'"
@@ -58709,7 +59232,7 @@ var generateCommand = defineCommand201({
58709
59232
  });
58710
59233
 
58711
59234
  // src/commands/studio/get.ts
58712
- import { defineCommand as defineCommand202 } from "citty";
59235
+ import { defineCommand as defineCommand203 } from "citty";
58713
59236
  registerSchema({
58714
59237
  command: "studio.get",
58715
59238
  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.",
@@ -58718,7 +59241,7 @@ registerSchema({
58718
59241
  full: { type: "boolean", description: "Include settings, references and attribution", required: false }
58719
59242
  }
58720
59243
  });
58721
- var getCommand5 = defineCommand202({
59244
+ var getCommand5 = defineCommand203({
58722
59245
  meta: {
58723
59246
  name: "get",
58724
59247
  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"
@@ -58747,7 +59270,7 @@ var getCommand5 = defineCommand202({
58747
59270
  });
58748
59271
 
58749
59272
  // src/commands/studio/improve.ts
58750
- import { defineCommand as defineCommand203 } from "citty";
59273
+ import { defineCommand as defineCommand204 } from "citty";
58751
59274
  var DESCRIPTION = "Sharpen a rough brief into directed art direction \u2014 the same rewrite the client gets from the wand in the Studio prompt bar. Reach for it when you are relaying the CLIENT's own words and want them shaped without substituting your voice; when you are writing the art direction yourself, just write it, because you will do a better job than this does.";
58752
59275
  registerSchema({
58753
59276
  command: "studio.improve",
@@ -58772,7 +59295,7 @@ registerSchema({
58772
59295
  }
58773
59296
  }
58774
59297
  });
58775
- var improveCommand = defineCommand203({
59298
+ var improveCommand = defineCommand204({
58776
59299
  meta: {
58777
59300
  name: "improve",
58778
59301
  description: `${DESCRIPTION}
@@ -58816,7 +59339,7 @@ Examples:
58816
59339
  });
58817
59340
 
58818
59341
  // src/commands/studio/keep.ts
58819
- import { defineCommand as defineCommand204 } from "citty";
59342
+ import { defineCommand as defineCommand205 } from "citty";
58820
59343
  registerSchema({
58821
59344
  command: "studio.keep",
58822
59345
  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.",
@@ -58826,7 +59349,7 @@ registerSchema({
58826
59349
  undo: { type: "boolean", description: "Un-star an image, or take a kept clip back out", required: false }
58827
59350
  }
58828
59351
  });
58829
- var keepCommand = defineCommand204({
59352
+ var keepCommand = defineCommand205({
58830
59353
  meta: {
58831
59354
  name: "keep",
58832
59355
  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"
@@ -58877,7 +59400,7 @@ var keepCommand = defineCommand204({
58877
59400
  });
58878
59401
 
58879
59402
  // src/commands/studio/list.ts
58880
- import { defineCommand as defineCommand205 } from "citty";
59403
+ import { defineCommand as defineCommand206 } from "citty";
58881
59404
  registerSchema({
58882
59405
  command: "studio.list",
58883
59406
  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.",
@@ -58888,7 +59411,7 @@ registerSchema({
58888
59411
  full: { type: "boolean", description: "Include settings, references and attribution", required: false }
58889
59412
  }
58890
59413
  });
58891
- var listCommand17 = defineCommand205({
59414
+ var listCommand17 = defineCommand206({
58892
59415
  meta: {
58893
59416
  name: "list",
58894
59417
  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"
@@ -58922,7 +59445,7 @@ var listCommand17 = defineCommand205({
58922
59445
  });
58923
59446
 
58924
59447
  // src/commands/studio/models.ts
58925
- import { defineCommand as defineCommand206 } from "citty";
59448
+ import { defineCommand as defineCommand207 } from "citty";
58926
59449
  var DESCRIPTION2 = "What each Studio model actually accepts: its shapes, resolutions, clip lengths, prompt character cap, how many reference images it takes, and which knobs it has. Read this before a batch you care about \u2014 the models disagree far more than they look like they do, and a setting the chosen model does not have is REFUSED, not ignored.";
58927
59450
  registerSchema({
58928
59451
  command: "studio.models",
@@ -59009,7 +59532,7 @@ function buildModelCards(kind, model) {
59009
59532
  const selected = model ? ids.filter((id) => id === model) : ids;
59010
59533
  return selected.map((id) => build(id));
59011
59534
  }
59012
- var modelsCommand = defineCommand206({
59535
+ var modelsCommand = defineCommand207({
59013
59536
  meta: {
59014
59537
  name: "models",
59015
59538
  description: `${DESCRIPTION2}
@@ -59056,13 +59579,13 @@ Examples:
59056
59579
  });
59057
59580
 
59058
59581
  // src/commands/studio/skills.ts
59059
- import { defineCommand as defineCommand207 } from "citty";
59582
+ import { defineCommand as defineCommand208 } from "citty";
59060
59583
  registerSchema({
59061
59584
  command: "studio.skills",
59062
59585
  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.",
59063
59586
  args: {}
59064
59587
  });
59065
- var skillsCommand = defineCommand207({
59588
+ var skillsCommand = defineCommand208({
59066
59589
  meta: {
59067
59590
  name: "skills",
59068
59591
  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"
@@ -59086,7 +59609,7 @@ var skillsCommand = defineCommand207({
59086
59609
  });
59087
59610
 
59088
59611
  // src/commands/studio/index.ts
59089
- var studioCommand = defineCommand208({
59612
+ var studioCommand = defineCommand209({
59090
59613
  meta: {
59091
59614
  name: "studio",
59092
59615
  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.
@@ -59129,10 +59652,10 @@ Full guide: __tooling__/docs/tools/baker/studio.md`
59129
59652
  });
59130
59653
 
59131
59654
  // src/commands/tag-manager/index.ts
59132
- import { defineCommand as defineCommand212 } from "citty";
59655
+ import { defineCommand as defineCommand213 } from "citty";
59133
59656
 
59134
59657
  // src/commands/tag-manager/draft.ts
59135
- import { defineCommand as defineCommand209 } from "citty";
59658
+ import { defineCommand as defineCommand210 } from "citty";
59136
59659
 
59137
59660
  // src/commands/tag-manager/shared.ts
59138
59661
  import { readFileSync as readFileSync18 } from "fs";
@@ -59255,13 +59778,13 @@ registerSchema({
59255
59778
  chat: { type: "string", description: CHAT_READ_ARG.description, required: false }
59256
59779
  }
59257
59780
  });
59258
- var draftCommand5 = defineCommand209({
59781
+ var draftCommand5 = defineCommand210({
59259
59782
  meta: {
59260
59783
  name: "draft",
59261
59784
  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."
59262
59785
  },
59263
59786
  subCommands: {
59264
- list: defineCommand209({
59787
+ list: defineCommand210({
59265
59788
  meta: {
59266
59789
  name: "list",
59267
59790
  description: "Review everything staged on this chat (--json for the raw envelope)"
@@ -59274,7 +59797,7 @@ var draftCommand5 = defineCommand209({
59274
59797
  await draftList2(args.json === true, args.chat);
59275
59798
  }
59276
59799
  }),
59277
- show: defineCommand209({
59800
+ show: defineCommand210({
59278
59801
  meta: {
59279
59802
  name: "show",
59280
59803
  description: "Print the full staged payload for one change \u2014 the receipt to verify it looks right before publish (never truncated)."
@@ -59291,7 +59814,7 @@ var draftCommand5 = defineCommand209({
59291
59814
  );
59292
59815
  }
59293
59816
  }),
59294
- amend: defineCommand209({
59817
+ amend: defineCommand210({
59295
59818
  meta: {
59296
59819
  name: "amend",
59297
59820
  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."
@@ -59308,7 +59831,7 @@ var draftCommand5 = defineCommand209({
59308
59831
  });
59309
59832
  }
59310
59833
  }),
59311
- remove: defineCommand209({
59834
+ remove: defineCommand210({
59312
59835
  meta: { name: "remove", description: "Remove one staged change (cascades to anything depending on it)" },
59313
59836
  args: { ref: { type: "positional", description: "Staged ref (gtm_temp_*) or target", required: false } },
59314
59837
  run: async ({ args }) => {
@@ -59317,7 +59840,7 @@ var draftCommand5 = defineCommand209({
59317
59840
  });
59318
59841
  }
59319
59842
  }),
59320
- clear: defineCommand209({
59843
+ clear: defineCommand210({
59321
59844
  meta: { name: "clear", description: "Discard all Tag Manager changes staged on this chat" },
59322
59845
  run: async () => {
59323
59846
  await draftAction3("/api/tag-manager/draft/clear", {});
@@ -59327,7 +59850,7 @@ var draftCommand5 = defineCommand209({
59327
59850
  });
59328
59851
 
59329
59852
  // src/commands/tag-manager/read.ts
59330
- import { defineCommand as defineCommand210 } from "citty";
59853
+ import { defineCommand as defineCommand211 } from "citty";
59331
59854
  registerSchema({
59332
59855
  command: "tagManager.containers",
59333
59856
  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.",
@@ -59368,7 +59891,7 @@ function containersHints(containers) {
59368
59891
  }))
59369
59892
  });
59370
59893
  }
59371
- var containersCommand = defineCommand210({
59894
+ var containersCommand = defineCommand211({
59372
59895
  meta: {
59373
59896
  name: "containers",
59374
59897
  description: `List Tag Manager containers reachable by this company's connection.
@@ -59385,7 +59908,7 @@ Start here:
59385
59908
  }
59386
59909
  }
59387
59910
  });
59388
- var readCommand = defineCommand210({
59911
+ var readCommand = defineCommand211({
59389
59912
  meta: {
59390
59913
  name: "read",
59391
59914
  description: `Read the current contents of the Tag Manager container \u2014 always do this before staging changes.
@@ -59427,7 +59950,7 @@ Examples:
59427
59950
  });
59428
59951
 
59429
59952
  // src/commands/tag-manager/write-commands.ts
59430
- import { defineCommand as defineCommand211 } from "citty";
59953
+ import { defineCommand as defineCommand212 } from "citty";
59431
59954
  var CONTAINER_ARG_DESCRIPTION = "Numeric container id (optional only when one container is connected \u2014 run `baker tag-manager containers`)";
59432
59955
  var ENTITIES = [
59433
59956
  {
@@ -59483,10 +60006,10 @@ for (const { entity, noun, createHint } of ENTITIES) {
59483
60006
  });
59484
60007
  }
59485
60008
  function entityCommand(entity, noun, example) {
59486
- return defineCommand211({
60009
+ return defineCommand212({
59487
60010
  meta: { name: entity, description: `Stage ${noun} changes on this chat's Tag Manager draft` },
59488
60011
  subCommands: {
59489
- create: defineCommand211({
60012
+ create: defineCommand212({
59490
60013
  meta: {
59491
60014
  name: "create",
59492
60015
  description: `Stage a new ${noun}
@@ -59508,7 +60031,7 @@ Examples:
59508
60031
  });
59509
60032
  }
59510
60033
  }),
59511
- update: defineCommand211({
60034
+ update: defineCommand212({
59512
60035
  meta: {
59513
60036
  name: "update",
59514
60037
  description: `Stage an update to an existing ${noun} (pass its id or path)`
@@ -59528,7 +60051,7 @@ Examples:
59528
60051
  });
59529
60052
  }
59530
60053
  }),
59531
- delete: defineCommand211({
60054
+ delete: defineCommand212({
59532
60055
  meta: { name: "delete", description: `Stage the deletion of a ${noun} (pass its id or path)` },
59533
60056
  args: {
59534
60057
  id: { type: "positional", description: `${noun} id or path`, required: false },
@@ -59569,7 +60092,7 @@ function builtinTypes(args) {
59569
60092
  }
59570
60093
  return raw.split(",").map((entry) => entry.trim());
59571
60094
  }
59572
- var builtinCommand = defineCommand211({
60095
+ var builtinCommand = defineCommand212({
59573
60096
  meta: {
59574
60097
  name: "builtin",
59575
60098
  description: `Enable or disable built-in variables
@@ -59579,7 +60102,7 @@ Examples:
59579
60102
  baker tag-manager builtin disable --types formId`
59580
60103
  },
59581
60104
  subCommands: {
59582
- enable: defineCommand211({
60105
+ enable: defineCommand212({
59583
60106
  meta: { name: "enable", description: "Stage enabling built-in variables" },
59584
60107
  args: {
59585
60108
  types: { type: "string", description: "Comma-separated types", required: false },
@@ -59593,7 +60116,7 @@ Examples:
59593
60116
  });
59594
60117
  }
59595
60118
  }),
59596
- disable: defineCommand211({
60119
+ disable: defineCommand212({
59597
60120
  meta: { name: "disable", description: "Stage disabling built-in variables" },
59598
60121
  args: {
59599
60122
  types: { type: "string", description: "Comma-separated types", required: false },
@@ -59611,7 +60134,7 @@ Examples:
59611
60134
  });
59612
60135
 
59613
60136
  // src/commands/tag-manager/index.ts
59614
- var tagManagerCommand = defineCommand212({
60137
+ var tagManagerCommand = defineCommand213({
59615
60138
  meta: {
59616
60139
  name: "tag-manager",
59617
60140
  description: `Read and change what lives inside the client's Google Tag Manager container \u2014 tags, triggers, variables, folders and built-in variables.
@@ -59648,7 +60171,7 @@ Full guide: __tooling__/docs/tools/baker/tag-manager.md`
59648
60171
  });
59649
60172
 
59650
60173
  // src/commands/tags/index.ts
59651
- import { defineCommand as defineCommand213 } from "citty";
60174
+ import { defineCommand as defineCommand214 } from "citty";
59652
60175
 
59653
60176
  // src/commands/tags/shared.ts
59654
60177
  function failApi3(err) {
@@ -59687,6 +60210,12 @@ function renderEffectiveEntry(entry) {
59687
60210
  return configLines.length > 0 ? `${header}
59688
60211
  ${configLines.join("\n")}` : header;
59689
60212
  }
60213
+ function tagsListHints() {
60214
+ return [
60215
+ "To create, edit or delete a tag, call the `request_tag_input` approval form (tool `mcp__baker_ui__request_tag_input`, server `baker_ui`) \u2014 it is attached to every turn of this chat and collects any secret values itself.",
60216
+ "Do NOT conclude the form is unavailable from your tool list: call it, and report what it returns. And never file a tag change as a Task \u2014 a Task hands the user work this chat can stage in one call."
60217
+ ];
60218
+ }
59690
60219
 
59691
60220
  // src/commands/tags/index.ts
59692
60221
  registerSchema({
@@ -59700,16 +60229,21 @@ async function listTags(json) {
59700
60229
  try {
59701
60230
  const chatId = requireChatId();
59702
60231
  const response = await apiPost("/api/tags/list", { chatId });
60232
+ const hints2 = tagsListHints();
59703
60233
  if (json) {
59704
- writeJson({ ok: true, data: response });
60234
+ writeJson({ ok: true, data: response, hints: hints2 });
59705
60235
  return;
59706
60236
  }
59707
60237
  if (response.tags.length === 0) {
59708
- process.stdout.write("No tags configured. Propose one with the request_tag_input tool (baker_ui).\n");
59709
- return;
60238
+ process.stdout.write("No tags configured.\n");
60239
+ } else {
60240
+ process.stdout.write(`${response.tags.map(renderEffectiveEntry).join("\n")}
60241
+ `);
59710
60242
  }
59711
- process.stdout.write(`${response.tags.map(renderEffectiveEntry).join("\n")}
60243
+ for (const hint of hints2) {
60244
+ process.stderr.write(`${hint}
59712
60245
  `);
60246
+ }
59713
60247
  } catch (err) {
59714
60248
  failApi3(err);
59715
60249
  }
@@ -59717,7 +60251,7 @@ async function listTags(json) {
59717
60251
  var listArgs10 = {
59718
60252
  json: { type: "boolean", description: "Print the raw JSON envelope instead of the readable list" }
59719
60253
  };
59720
- var listCommand18 = defineCommand213({
60254
+ var listCommand18 = defineCommand214({
59721
60255
  meta: {
59722
60256
  name: "list",
59723
60257
  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"
@@ -59731,12 +60265,12 @@ async function listDraft4(chat) {
59731
60265
  try {
59732
60266
  const chatId = resolveChatId(chat);
59733
60267
  const response = await apiPost("/api/tags/draft", { chatId });
59734
- writeJson({ ok: true, data: response });
60268
+ writeJson({ ok: true, data: response, hints: tagsListHints() });
59735
60269
  } catch (err) {
59736
60270
  failApi3(err);
59737
60271
  }
59738
60272
  }
59739
- var draftCommand6 = defineCommand213({
60273
+ var draftCommand6 = defineCommand214({
59740
60274
  meta: {
59741
60275
  name: "draft",
59742
60276
  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."
@@ -59746,7 +60280,7 @@ var draftCommand6 = defineCommand213({
59746
60280
  await listDraft4(args.chat);
59747
60281
  }
59748
60282
  });
59749
- var tagsCommand4 = defineCommand213({
60283
+ var tagsCommand4 = defineCommand214({
59750
60284
  meta: {
59751
60285
  name: "tags",
59752
60286
  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.
@@ -59775,10 +60309,10 @@ Full guide: __tooling__/docs/tools/baker/tags.md`
59775
60309
  });
59776
60310
 
59777
60311
  // src/commands/testimonials/index.ts
59778
- import { defineCommand as defineCommand217 } from "citty";
60312
+ import { defineCommand as defineCommand218 } from "citty";
59779
60313
 
59780
60314
  // src/commands/testimonials/get.ts
59781
- import { defineCommand as defineCommand214 } from "citty";
60315
+ import { defineCommand as defineCommand215 } from "citty";
59782
60316
  registerSchema({
59783
60317
  command: "testimonials.get",
59784
60318
  description: "Get a single testimonial by ID",
@@ -59786,7 +60320,7 @@ registerSchema({
59786
60320
  id: { type: "string", description: "Testimonial ID", required: true }
59787
60321
  }
59788
60322
  });
59789
- var getCommand6 = defineCommand214({
60323
+ var getCommand6 = defineCommand215({
59790
60324
  meta: { name: "get", description: "Get a single testimonial by ID. Example: baker testimonials get j571abc123" },
59791
60325
  args: {
59792
60326
  id: { type: "positional", description: "Testimonial ID", required: false },
@@ -59823,7 +60357,7 @@ var getCommand6 = defineCommand214({
59823
60357
  });
59824
60358
 
59825
60359
  // src/commands/testimonials/list.ts
59826
- import { defineCommand as defineCommand215 } from "citty";
60360
+ import { defineCommand as defineCommand216 } from "citty";
59827
60361
 
59828
60362
  // src/commands/testimonials/emptyCorpusHints.ts
59829
60363
  function resolveEmptyReason({
@@ -59980,7 +60514,7 @@ function buildListParams(args) {
59980
60514
  }
59981
60515
  return params;
59982
60516
  }
59983
- var listCommand19 = defineCommand215({
60517
+ var listCommand19 = defineCommand216({
59984
60518
  meta: {
59985
60519
  name: "list",
59986
60520
  description: "List testimonials with optional filters. Example: baker testimonials list --source google --sentiment positive"
@@ -60040,7 +60574,7 @@ var listCommand19 = defineCommand215({
60040
60574
  });
60041
60575
 
60042
60576
  // src/commands/testimonials/search.ts
60043
- import { defineCommand as defineCommand216 } from "citty";
60577
+ import { defineCommand as defineCommand217 } from "citty";
60044
60578
  var FILTER_FLAGS2 = ["source", "rating-min", "rating-max", "status", "sentiment", "language", "tags"];
60045
60579
  function languageBiasHint(results, requestedLanguage) {
60046
60580
  if (requestedLanguage) {
@@ -60119,7 +60653,7 @@ function buildSearchRequest(query, args) {
60119
60653
  }
60120
60654
  return body;
60121
60655
  }
60122
- var searchCommand3 = defineCommand216({
60656
+ var searchCommand3 = defineCommand217({
60123
60657
  meta: {
60124
60658
  name: "search",
60125
60659
  description: "Semantic search testimonials by text query. Uses hybrid BM25 + vector + reranking. Example: baker testimonials search 'great service' --rating-min 4"
@@ -60183,7 +60717,7 @@ var searchCommand3 = defineCommand216({
60183
60717
  var tagsCommand5 = makeTagsCommand("testimonials", "testimonial", "/api/testimonials/tags");
60184
60718
 
60185
60719
  // src/commands/testimonials/index.ts
60186
- var testimonialsCommand = defineCommand217({
60720
+ var testimonialsCommand = defineCommand218({
60187
60721
  meta: {
60188
60722
  name: "testimonials",
60189
60723
  description: `Find and browse testimonials in Baker. Subcommands: search, get, list, tags.
@@ -60205,10 +60739,10 @@ Full guide: __tooling__/docs/tools/baker/testimonials.md`
60205
60739
  });
60206
60740
 
60207
60741
  // src/commands/videos/index.ts
60208
- import { defineCommand as defineCommand224 } from "citty";
60742
+ import { defineCommand as defineCommand226 } from "citty";
60209
60743
 
60210
60744
  // src/commands/videos/delete.ts
60211
- import { defineCommand as defineCommand218 } from "citty";
60745
+ import { defineCommand as defineCommand219 } from "citty";
60212
60746
  registerSchema({
60213
60747
  command: "videos.delete",
60214
60748
  description: "Delete a video by ID",
@@ -60222,7 +60756,7 @@ registerSchema({
60222
60756
  }
60223
60757
  }
60224
60758
  });
60225
- var deleteCommand4 = defineCommand218({
60759
+ var deleteCommand4 = defineCommand219({
60226
60760
  meta: {
60227
60761
  name: "delete",
60228
60762
  description: "Delete a video by ID. Use --dry-run to preview. Example: baker videos delete j571abc123 --dry-run"
@@ -60262,8 +60796,83 @@ var deleteCommand4 = defineCommand218({
60262
60796
  }
60263
60797
  });
60264
60798
 
60799
+ // src/commands/videos/describe.ts
60800
+ import { defineCommand as defineCommand220 } from "citty";
60801
+ 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.";
60802
+ registerSchema({
60803
+ command: "videos.describe",
60804
+ description: "Correct a library video's stored name, description or tags. Start here when a clip's description is wrong \u2014 it is what semantic search reads.",
60805
+ args: {
60806
+ id: { type: "string", description: "Video ID", required: true },
60807
+ description: { type: "string", description: DESCRIPTION_HELP2, required: false },
60808
+ name: { type: "string", description: "Short human label for the clip", required: false },
60809
+ tags: {
60810
+ type: "string",
60811
+ description: "Tags for the clip \u2014 repeatable (`--tags a --tags b`) or one comma list. **Replaces** the existing set",
60812
+ required: false
60813
+ },
60814
+ full: { type: "boolean", description: "Return the whole library row, not just what changed", required: false }
60815
+ }
60816
+ });
60817
+ var describeCommand2 = defineCommand220({
60818
+ meta: {
60819
+ name: "describe",
60820
+ description: `Correct what the library says a video is. Only the fields you pass change; the rest keep their current values.
60821
+
60822
+ Start here when a search keeps missing a clip you know is there, or when the AI description got the subject wrong.
60823
+
60824
+ Example: baker videos describe j571abc123 --description "Customer explaining how onboarding cut their setup from a week to a day" --tags testimonial`
60825
+ },
60826
+ args: {
60827
+ id: { type: "positional", description: "Video ID", required: false },
60828
+ "video-id": { type: "string", description: "Video ID (alternative to positional)", required: false },
60829
+ description: { type: "string", description: DESCRIPTION_HELP2, required: false },
60830
+ name: { type: "string", description: "Short human label for the clip", required: false },
60831
+ tags: {
60832
+ type: "string",
60833
+ description: "Tags for the clip \u2014 repeatable (`--tags a --tags b`) or one comma list. **Replaces** the existing set",
60834
+ required: false
60835
+ },
60836
+ full: { type: "boolean", description: "Return the whole library row", required: false, default: false }
60837
+ },
60838
+ run: async ({ args, rawArgs }) => {
60839
+ const id = args.id || args["video-id"];
60840
+ if (!id) {
60841
+ writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message: "Video ID is required" } });
60842
+ process.exit(1);
60843
+ }
60844
+ const fields = describeFields(args, rawArgs);
60845
+ if (fields.name === void 0 && fields.description === void 0 && fields.tags === void 0) {
60846
+ writeJson({
60847
+ ok: false,
60848
+ error: {
60849
+ code: "VALIDATION_ERROR",
60850
+ message: "Nothing to change",
60851
+ fix: "Pass at least one of --description, --name or --tags."
60852
+ }
60853
+ });
60854
+ process.exit(1);
60855
+ }
60856
+ try {
60857
+ validateConvexId(id);
60858
+ const body = { id, ...fields };
60859
+ if (args.full) body.full = true;
60860
+ const data = await apiPost("/api/videos/describe", body);
60861
+ writeJson({ ok: true, data, hints: describeHints("videos", fields) });
60862
+ } catch (err) {
60863
+ if (err instanceof ApiError) {
60864
+ const fix = describeErrorFix(err.code, "videos");
60865
+ writeJson({ ok: false, error: { code: err.code, message: err.message, ...fix ? { fix } : {} } });
60866
+ process.exit(1);
60867
+ }
60868
+ writeJson({ ok: false, error: { code: "INTERNAL_ERROR", message: "Unexpected error" } });
60869
+ process.exit(1);
60870
+ }
60871
+ }
60872
+ });
60873
+
60265
60874
  // src/commands/videos/get.ts
60266
- import { defineCommand as defineCommand219 } from "citty";
60875
+ import { defineCommand as defineCommand221 } from "citty";
60267
60876
  registerSchema({
60268
60877
  command: "videos.get",
60269
60878
  description: "Get a single video by ID",
@@ -60271,7 +60880,7 @@ registerSchema({
60271
60880
  id: { type: "string", description: "Video ID", required: true }
60272
60881
  }
60273
60882
  });
60274
- var getCommand7 = defineCommand219({
60883
+ var getCommand7 = defineCommand221({
60275
60884
  meta: { name: "get", description: "Get a single video by ID. Example: baker videos get j571abc123" },
60276
60885
  args: {
60277
60886
  id: { type: "positional", description: "Video ID", required: false },
@@ -60308,7 +60917,7 @@ var getCommand7 = defineCommand219({
60308
60917
  });
60309
60918
 
60310
60919
  // src/commands/videos/group.ts
60311
- import { defineCommand as defineCommand220 } from "citty";
60920
+ import { defineCommand as defineCommand222 } from "citty";
60312
60921
  registerSchema({
60313
60922
  command: "videos.group",
60314
60923
  description: "List every clip and image that arrived in the same set as this video (carousel slides, one page)",
@@ -60317,7 +60926,7 @@ registerSchema({
60317
60926
  "group-key": { type: "string", description: "The set key directly, when you already have it", required: false }
60318
60927
  }
60319
60928
  });
60320
- var groupCommand2 = defineCommand220({
60929
+ var groupCommand2 = defineCommand222({
60321
60930
  meta: {
60322
60931
  name: "group",
60323
60932
  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>"
@@ -60340,7 +60949,7 @@ var groupCommand2 = defineCommand220({
60340
60949
  import { mkdtemp as mkdtemp3, rm as rm9, stat as stat11 } from "fs/promises";
60341
60950
  import { tmpdir as tmpdir4 } from "os";
60342
60951
  import path47 from "path";
60343
- import { defineCommand as defineCommand221 } from "citty";
60952
+ import { defineCommand as defineCommand223 } from "citty";
60344
60953
 
60345
60954
  // src/lib/streamUpload.ts
60346
60955
  import { createHash as createHash2 } from "crypto";
@@ -60504,7 +61113,7 @@ registerSchema({
60504
61113
  "dry-run": { type: "boolean", description: "Preview the operation without executing", required: false }
60505
61114
  }
60506
61115
  });
60507
- var ingestCommand2 = defineCommand221({
61116
+ var ingestCommand2 = defineCommand223({
60508
61117
  meta: {
60509
61118
  name: "ingest",
60510
61119
  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"
@@ -60766,7 +61375,7 @@ async function uploadToAssetStore(filePath, sizeBytes) {
60766
61375
  }
60767
61376
 
60768
61377
  // src/commands/videos/search.ts
60769
- import { defineCommand as defineCommand222 } from "citty";
61378
+ import { defineCommand as defineCommand224 } from "citty";
60770
61379
  registerSchema({
60771
61380
  command: "videos.search",
60772
61381
  description: "Search videos by text query. Only returns ready videos.",
@@ -60776,7 +61385,7 @@ registerSchema({
60776
61385
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
60777
61386
  }
60778
61387
  });
60779
- var searchCommand4 = defineCommand222({
61388
+ var searchCommand4 = defineCommand224({
60780
61389
  meta: {
60781
61390
  name: "search",
60782
61391
  description: "Semantic search videos by text query. Uses hybrid BM25 + vector + reranking. Example: baker videos search 'product demo' --tags tutorial"
@@ -60828,7 +61437,7 @@ var tagsCommand6 = makeTagsCommand("videos", "video", "/api/videos/tags");
60828
61437
  // src/commands/videos/upload.ts
60829
61438
  import { readFile as readFile33, stat as stat12 } from "fs/promises";
60830
61439
  import { basename as basename3, extname as extname4 } from "path";
60831
- import { defineCommand as defineCommand223 } from "citty";
61440
+ import { defineCommand as defineCommand225 } from "citty";
60832
61441
  var MIME_MAP = {
60833
61442
  ".mp4": "video/mp4",
60834
61443
  ".mov": "video/quicktime",
@@ -60870,7 +61479,7 @@ function detectContentType(filePath) {
60870
61479
  function isRemoteUrl3(value) {
60871
61480
  return /^https?:\/\//i.test(value);
60872
61481
  }
60873
- var uploadCommand2 = defineCommand223({
61482
+ var uploadCommand2 = defineCommand225({
60874
61483
  meta: {
60875
61484
  name: "upload",
60876
61485
  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"
@@ -60954,10 +61563,10 @@ var uploadCommand2 = defineCommand223({
60954
61563
  });
60955
61564
 
60956
61565
  // src/commands/videos/index.ts
60957
- var videosCommand = defineCommand224({
61566
+ var videosCommand = defineCommand226({
60958
61567
  meta: {
60959
61568
  name: "videos",
60960
- description: `Find and manage videos in Baker. Subcommands: search, get, upload, ingest, delete, tags.
61569
+ description: `Find and manage videos in Baker. Subcommands: search, get, upload, ingest, describe, delete, tags.
60961
61570
 
60962
61571
  Examples:
60963
61572
  baker videos search "product demo" --limit 5
@@ -60965,6 +61574,7 @@ Examples:
60965
61574
  baker videos get <video-id>
60966
61575
  baker videos upload ./demo.mp4
60967
61576
  baker videos ingest https://www.youtube.com/watch?v=abc123
61577
+ baker videos describe <video-id> --description "Customer testimonial, 30s"
60968
61578
  baker videos delete <video-id> --dry-run
60969
61579
  baker videos tags
60970
61580
  Full guide: __tooling__/docs/tools/baker/videos.md`
@@ -60975,16 +61585,17 @@ Full guide: __tooling__/docs/tools/baker/videos.md`
60975
61585
  search: searchCommand4,
60976
61586
  upload: uploadCommand2,
60977
61587
  ingest: ingestCommand2,
61588
+ describe: describeCommand2,
60978
61589
  delete: deleteCommand4,
60979
61590
  tags: tagsCommand6
60980
61591
  }
60981
61592
  });
60982
61593
 
60983
61594
  // src/commands/winning-ads/index.ts
60984
- import { defineCommand as defineCommand237 } from "citty";
61595
+ import { defineCommand as defineCommand239 } from "citty";
60985
61596
 
60986
61597
  // src/commands/winning-ads/advertisers.ts
60987
- import { defineCommand as defineCommand225 } from "citty";
61598
+ import { defineCommand as defineCommand227 } from "citty";
60988
61599
 
60989
61600
  // src/commands/winning-ads/shared.ts
60990
61601
  function splitList2(value) {
@@ -61037,7 +61648,7 @@ function advertiserNormalizer(record, full) {
61037
61648
  last_synced_at: record.last_synced_at ?? null
61038
61649
  };
61039
61650
  }
61040
- var advertisersCommand2 = defineCommand225({
61651
+ var advertisersCommand2 = defineCommand227({
61041
61652
  meta: {
61042
61653
  name: "advertisers",
61043
61654
  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'
@@ -61095,7 +61706,7 @@ var advertisersCommand2 = defineCommand225({
61095
61706
  });
61096
61707
 
61097
61708
  // src/commands/winning-ads/brief.ts
61098
- import { defineCommand as defineCommand226 } from "citty";
61709
+ import { defineCommand as defineCommand228 } from "citty";
61099
61710
  registerSchema({
61100
61711
  command: "winning-ads.brief",
61101
61712
  description: "Generate a creative brief grounded in strategically-similar winning ads. Optionally describe the target creative with --dna (JSON) and steer with --notes.",
@@ -61141,7 +61752,7 @@ function parseDna(raw) {
61141
61752
  }
61142
61753
  return parsed;
61143
61754
  }
61144
- var briefCommand = defineCommand226({
61755
+ var briefCommand = defineCommand228({
61145
61756
  meta: {
61146
61757
  name: "brief",
61147
61758
  description: `Generate a creative brief from winning references. Example: baker winning-ads brief --dna '{"angle":"cost savings"}' --notes "B2B, LinkedIn video" --k 8`
@@ -61177,7 +61788,7 @@ var briefCommand = defineCommand226({
61177
61788
  });
61178
61789
 
61179
61790
  // src/commands/winning-ads/content.ts
61180
- import { defineCommand as defineCommand227 } from "citty";
61791
+ import { defineCommand as defineCommand229 } from "citty";
61181
61792
  registerSchema({
61182
61793
  command: "winning-ads.content",
61183
61794
  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.",
@@ -61190,7 +61801,7 @@ registerSchema({
61190
61801
  }
61191
61802
  }
61192
61803
  });
61193
- var contentCommand = defineCommand227({
61804
+ var contentCommand = defineCommand229({
61194
61805
  meta: {
61195
61806
  name: "content",
61196
61807
  description: "Read the transcript + on-screen text + copy of one winning ad. Example: baker winning-ads content adg_123 --platform meta --full --output md"
@@ -61239,7 +61850,7 @@ var contentCommand = defineCommand227({
61239
61850
  });
61240
61851
 
61241
61852
  // src/commands/winning-ads/feed.ts
61242
- import { defineCommand as defineCommand228 } from "citty";
61853
+ import { defineCommand as defineCommand230 } from "citty";
61243
61854
  function buildFeedParams(input) {
61244
61855
  const params = {};
61245
61856
  const advertiser = splitList2(input.advertiser);
@@ -61291,7 +61902,7 @@ registerSchema({
61291
61902
  format: { type: "string", description: "Comma-separated formats to include (e.g. static,video)", required: false }
61292
61903
  }
61293
61904
  });
61294
- var feedCommand = defineCommand228({
61905
+ var feedCommand = defineCommand230({
61295
61906
  meta: {
61296
61907
  name: "feed",
61297
61908
  description: "Winners across every brand you follow (browse, then trim per advertiser). Example: baker winning-ads feed --per-advertiser 5 --output md"
@@ -61376,7 +61987,7 @@ var feedCommand = defineCommand228({
61376
61987
  });
61377
61988
 
61378
61989
  // src/commands/winning-ads/follow.ts
61379
- import { defineCommand as defineCommand229 } from "citty";
61990
+ import { defineCommand as defineCommand231 } from "citty";
61380
61991
  var PLATFORMS = adLibraryPlatformSchema.options;
61381
61992
  registerSchema({
61382
61993
  command: "winning-ads.follow",
@@ -61391,7 +62002,7 @@ registerSchema({
61391
62002
  label: { type: "string", description: "Optional display label (defaults to the resolved name)", required: false }
61392
62003
  }
61393
62004
  });
61394
- var followCommand = defineCommand229({
62005
+ var followCommand = defineCommand231({
61395
62006
  meta: {
61396
62007
  name: "follow",
61397
62008
  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'
@@ -61438,7 +62049,7 @@ var followCommand = defineCommand229({
61438
62049
  });
61439
62050
 
61440
62051
  // src/commands/winning-ads/follow-competitors.ts
61441
- import { defineCommand as defineCommand230 } from "citty";
62052
+ import { defineCommand as defineCommand232 } from "citty";
61442
62053
  var PLATFORMS2 = adLibraryPlatformSchema.options;
61443
62054
  var BATCH_TIMEOUT_MS = 3e5;
61444
62055
  function buildFollowBatchBody(input) {
@@ -61471,7 +62082,7 @@ registerSchema({
61471
62082
  }
61472
62083
  }
61473
62084
  });
61474
- var followCompetitorsCommand = defineCommand230({
62085
+ var followCompetitorsCommand = defineCommand232({
61475
62086
  meta: {
61476
62087
  name: "follow-competitors",
61477
62088
  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"'
@@ -61546,7 +62157,7 @@ var followCompetitorsCommand = defineCommand230({
61546
62157
  });
61547
62158
 
61548
62159
  // src/commands/winning-ads/following.ts
61549
- import { defineCommand as defineCommand231 } from "citty";
62160
+ import { defineCommand as defineCommand233 } from "citty";
61550
62161
  registerSchema({
61551
62162
  command: "winning-ads.following",
61552
62163
  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.",
@@ -61600,7 +62211,7 @@ function followingNormalizer(record, full) {
61600
62211
  platforms: Array.isArray(record.platforms) ? record.platforms : []
61601
62212
  };
61602
62213
  }
61603
- var followingCommand = defineCommand231({
62214
+ var followingCommand = defineCommand233({
61604
62215
  meta: {
61605
62216
  name: "following",
61606
62217
  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"
@@ -61636,7 +62247,7 @@ var followingCommand = defineCommand231({
61636
62247
  });
61637
62248
 
61638
62249
  // src/commands/winning-ads/patterns.ts
61639
- import { defineCommand as defineCommand232 } from "citty";
62250
+ import { defineCommand as defineCommand234 } from "citty";
61640
62251
  registerSchema({
61641
62252
  command: "winning-ads.patterns",
61642
62253
  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.",
@@ -61675,7 +62286,7 @@ function discriminatorRow(record) {
61675
62286
  top_values_duds: Array.isArray(record.top_values_b) ? record.top_values_b.join(", ") : ""
61676
62287
  };
61677
62288
  }
61678
- var patternsCommand = defineCommand232({
62289
+ var patternsCommand = defineCommand234({
61679
62290
  meta: {
61680
62291
  name: "patterns",
61681
62292
  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"
@@ -61731,7 +62342,7 @@ var patternsCommand = defineCommand232({
61731
62342
  });
61732
62343
 
61733
62344
  // src/commands/winning-ads/search.ts
61734
- import { defineCommand as defineCommand233 } from "citty";
62345
+ import { defineCommand as defineCommand235 } from "citty";
61735
62346
  registerSchema({
61736
62347
  command: "winning-ads.search",
61737
62348
  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.",
@@ -61839,7 +62450,7 @@ function buildSearchBody2(args) {
61839
62450
  }
61840
62451
  return body;
61841
62452
  }
61842
- var searchCommand5 = defineCommand233({
62453
+ var searchCommand5 = defineCommand235({
61843
62454
  meta: {
61844
62455
  name: "search",
61845
62456
  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"
@@ -61954,7 +62565,7 @@ var searchCommand5 = defineCommand233({
61954
62565
  });
61955
62566
 
61956
62567
  // src/commands/winning-ads/seeds.ts
61957
- import { defineCommand as defineCommand234 } from "citty";
62568
+ import { defineCommand as defineCommand236 } from "citty";
61958
62569
  function leanRow(r) {
61959
62570
  return {
61960
62571
  key: r.key,
@@ -61982,7 +62593,7 @@ function makeSeedCommand(opts) {
61982
62593
  limit: { type: "number", description: "Max keys 1-100 (default 20)", required: false, default: 20 }
61983
62594
  }
61984
62595
  });
61985
- return defineCommand234({
62596
+ return defineCommand236({
61986
62597
  meta: { name: opts.name, description: opts.description },
61987
62598
  args: {
61988
62599
  platform: { type: "string", description: "Single platform to segment on", required: false },
@@ -62031,7 +62642,7 @@ var formatsCommand = makeSeedCommand({
62031
62642
  });
62032
62643
 
62033
62644
  // src/commands/winning-ads/unfollow.ts
62034
- import { defineCommand as defineCommand235 } from "citty";
62645
+ import { defineCommand as defineCommand237 } from "citty";
62035
62646
  registerSchema({
62036
62647
  command: "winning-ads.unfollow",
62037
62648
  description: "Stop following a brand \u2014 removes it from your ad-dna library by advertiser id.",
@@ -62039,7 +62650,7 @@ registerSchema({
62039
62650
  advertiser: { type: "string", description: "Advertiser id to unfollow", required: true }
62040
62651
  }
62041
62652
  });
62042
- var unfollowCommand = defineCommand235({
62653
+ var unfollowCommand = defineCommand237({
62043
62654
  meta: {
62044
62655
  name: "unfollow",
62045
62656
  description: "Stop following a brand by advertiser id. Example: baker winning-ads unfollow adv_123"
@@ -62060,7 +62671,7 @@ var unfollowCommand = defineCommand235({
62060
62671
  });
62061
62672
 
62062
62673
  // src/commands/winning-ads/winners.ts
62063
- import { defineCommand as defineCommand236 } from "citty";
62674
+ import { defineCommand as defineCommand238 } from "citty";
62064
62675
  registerSchema({
62065
62676
  command: "winning-ads.winners",
62066
62677
  description: "Top winning ads for one advertiser id (from `advertisers` or `following`). Returns lean winner cards; add --full for DNA + longevity.",
@@ -62070,7 +62681,7 @@ registerSchema({
62070
62681
  platform: { type: "string", description: "Filter to a single platform: meta|linkedin|tiktok", required: false }
62071
62682
  }
62072
62683
  });
62073
- var winnersCommand = defineCommand236({
62684
+ var winnersCommand = defineCommand238({
62074
62685
  meta: {
62075
62686
  name: "winners",
62076
62687
  description: "Top winning ads for a specific advertiser id. Example: baker winning-ads winners adv_123 --top 15 --output md"
@@ -62120,7 +62731,7 @@ var winnersCommand = defineCommand236({
62120
62731
  });
62121
62732
 
62122
62733
  // src/commands/winning-ads/index.ts
62123
- var winningAdsCommand = defineCommand237({
62734
+ var winningAdsCommand = defineCommand239({
62124
62735
  meta: {
62125
62736
  name: "winning-ads",
62126
62737
  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.
@@ -62358,7 +62969,7 @@ function getCliVersion() {
62358
62969
  }
62359
62970
 
62360
62971
  // src/cli.ts
62361
- var main = defineCommand238({
62972
+ var main = defineCommand240({
62362
62973
  meta: {
62363
62974
  name: "baker",
62364
62975
  version: getCliVersion(),