@koda-sl/baker-cli 0.147.0 → 0.149.0-dev.183ac88ea

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
@@ -24,6 +24,7 @@ import {
24
24
  imageProfileFor,
25
25
  isPersistedAssetRef,
26
26
  looksLikeHttpUrl,
27
+ nearestSupportedAspectRatio,
27
28
  parseRefExpr,
28
29
  platformFormats,
29
30
  requireCredentialsFromEnv,
@@ -31,10 +32,11 @@ import {
31
32
  sha256Hex,
32
33
  spineInputFlags,
33
34
  spineInputOps,
35
+ supportsParam,
34
36
  toModelSafeImage,
35
37
  ulid,
36
38
  validateCanvasDeep
37
- } from "./chunk-2F5SCTNS.js";
39
+ } from "./chunk-JBDSJZBZ.js";
38
40
  import {
39
41
  csvOrJson,
40
42
  daysAgoIso,
@@ -73,7 +75,7 @@ import {
73
75
  } from "./chunk-RK67WL4O.js";
74
76
 
75
77
  // src/cli.ts
76
- import { defineCommand as defineCommand178, runMain } from "citty";
78
+ import { defineCommand as defineCommand182, runMain } from "citty";
77
79
 
78
80
  // src/commands/actions/index.ts
79
81
  import { defineCommand as defineCommand18 } from "citty";
@@ -2131,6 +2133,7 @@ var chatChangeTypeSchema = z5.enum([
2131
2133
  "linkedin-ads",
2132
2134
  "google-ads",
2133
2135
  "meta-ads",
2136
+ "tag-manager",
2134
2137
  // Immediate (already-applied) library effects — see lib/chatChanges.ts.
2135
2138
  "image",
2136
2139
  "video",
@@ -2707,8 +2710,9 @@ var rgbTriple = z8.tuple([
2707
2710
  z8.number().int().min(0).max(255)
2708
2711
  ]);
2709
2712
  var imageGenerateModelSchema = z8.enum([
2713
+ "openai/gpt-image-2",
2714
+ // Legacy — see the registry entry; kept so pre-switch canvases still run.
2710
2715
  "openai/gpt-5.4-image-2",
2711
- "google/gemini-3.5-flash",
2712
2716
  "google/gemini-3.1-flash-image-preview",
2713
2717
  "google/gemini-3-pro-image-preview",
2714
2718
  "recraft/recraft-v4.1-pro-vector"
@@ -5395,17 +5399,27 @@ var googleDraftStageRequestSchema = z13.object({
5395
5399
  chatId: z13.string(),
5396
5400
  op: googleDraftOpInputSchema
5397
5401
  });
5398
- var googleDraftStageResponseSchema = z13.object({
5399
- staged: z13.literal(true),
5400
- ref: z13.string(),
5401
- kind: googleDraftOpKindSchema,
5402
- mode: googleWriteModeSchema,
5403
- dependsOn: z13.array(z13.string()),
5404
- summary: z13.string(),
5405
- warnings: z13.array(z13.string()),
5406
- /** True when the op amended an already-staged op in place instead of appending a new one. */
5407
- amended: z13.boolean().optional()
5408
- });
5402
+ var googleDraftStageResponseSchema = z13.discriminatedUnion("staged", [
5403
+ z13.object({
5404
+ staged: z13.literal(true),
5405
+ ref: z13.string(),
5406
+ kind: googleDraftOpKindSchema,
5407
+ mode: googleWriteModeSchema,
5408
+ dependsOn: z13.array(z13.string()),
5409
+ summary: z13.string(),
5410
+ warnings: z13.array(z13.string()),
5411
+ /** True when the op amended an already-staged op in place instead of appending a new one. */
5412
+ amended: z13.boolean().optional()
5413
+ }),
5414
+ z13.object({
5415
+ staged: z13.literal(false),
5416
+ noop: z13.literal(true),
5417
+ kind: googleDraftOpKindSchema,
5418
+ mode: googleWriteModeSchema,
5419
+ summary: z13.string(),
5420
+ reason: z13.string()
5421
+ })
5422
+ ]);
5409
5423
  var googleDraftAmendRequestSchema = z13.object({
5410
5424
  chatId: z13.string(),
5411
5425
  ref: z13.string(),
@@ -5432,7 +5446,8 @@ var googleDraftStageBatchResponseSchema = z13.object({
5432
5446
  summary: z13.string(),
5433
5447
  warnings: z13.array(z13.string())
5434
5448
  })
5435
- )
5449
+ ),
5450
+ skipped: z13.array(z13.object({ kind: googleDraftOpKindSchema, summary: z13.string(), reason: z13.string() })).optional()
5436
5451
  });
5437
5452
  var googleDraftOpViewSchema = z13.object({
5438
5453
  ref: z13.string(),
@@ -5691,7 +5706,7 @@ function keywordEntries(args) {
5691
5706
  const seen = /* @__PURE__ */ new Set();
5692
5707
  for (const item of raw) {
5693
5708
  const entry = parseKeywordEntry(item, defaultMatch);
5694
- const key = `${entry.text}\0${entry.matchType}`;
5709
+ const key = JSON.stringify([entry.text, entry.matchType]);
5695
5710
  if (!seen.has(key)) {
5696
5711
  seen.add(key);
5697
5712
  entries.push(entry);
@@ -5758,6 +5773,9 @@ function handleGoogleError(err) {
5758
5773
  });
5759
5774
  process.exit(1);
5760
5775
  }
5776
+ function noopHints(data) {
5777
+ return data.staged ? [] : [`NOT STAGED \u2014 ${data.reason}. Nothing to publish for this change; say so rather than reporting it as done.`];
5778
+ }
5761
5779
  async function stageGoogleOp(raw, hints) {
5762
5780
  const preflight = googleDraftOpInputSchema.safeParse(raw);
5763
5781
  if (!preflight.success) {
@@ -5775,7 +5793,8 @@ async function stageGoogleOp(raw, hints) {
5775
5793
  try {
5776
5794
  const chatId = requireChatId();
5777
5795
  const response = await apiPost("/api/ads/google/draft/stage", { chatId, op: preflight.data });
5778
- writeJsonEnvelope(hints && hints.length > 0 ? { ...response, hints } : response);
5796
+ const allHints = [...noopHints(response.data), ...hints ?? []];
5797
+ writeJsonEnvelope(allHints.length > 0 ? { ...response, hints: allHints } : response);
5779
5798
  } catch (err) {
5780
5799
  handleGoogleError(err);
5781
5800
  }
@@ -5811,7 +5830,14 @@ async function stageGoogleOps(rawOps, hints) {
5811
5830
  try {
5812
5831
  const chatId = requireChatId();
5813
5832
  const response = await apiPost("/api/ads/google/draft/stage-batch", { chatId, ops });
5814
- writeJsonEnvelope(hints && hints.length > 0 ? { ...response, hints } : response);
5833
+ const skipped = response.data.skipped ?? [];
5834
+ const allHints = [
5835
+ ...skipped.length > 0 ? [
5836
+ `${skipped.length} of ${ops.length} op(s) NOT STAGED \u2014 already in the requested state: ${skipped.map((s) => s.reason).join("; ")}`
5837
+ ] : [],
5838
+ ...hints ?? []
5839
+ ];
5840
+ writeJsonEnvelope(allHints.length > 0 ? { ...response, hints: allHints } : response);
5815
5841
  } catch (err) {
5816
5842
  handleGoogleError(err);
5817
5843
  }
@@ -16631,7 +16657,9 @@ function buildFrameRef(edge, url, framePrompt, present2, ctx, nodes) {
16631
16657
  const imageProfile = imageProfileFor(ctx.imageModel);
16632
16658
  const genParams = {
16633
16659
  model: ctx.imageModel,
16634
- image_size: "2K",
16660
+ // gpt-image-2 derives pixel dimensions from the ratio and has no size knob,
16661
+ // so asking for 2K there is an `unknown_param` at validate.
16662
+ ...supportsParam("image_generate", ctx.imageModel, "image_size") ? { image_size: "2K" } : {},
16635
16663
  // Per-model image defaults (gpt-image: quality=high — OpenRouter forwards it; we do
16636
16664
  // NOT send input_fidelity, which gpt-image-2 forces high automatically).
16637
16665
  ...imageProfile?.paramDefaults ?? {},
@@ -16646,7 +16674,7 @@ function buildFrameRef(edge, url, framePrompt, present2, ctx, nodes) {
16646
16674
  ctx.imageModel
16647
16675
  )
16648
16676
  };
16649
- if (ctx.genAr) genParams.aspect_ratio = ctx.genAr;
16677
+ if (ctx.genAr) genParams.aspect_ratio = nearestSupportedAspectRatio("image_generate", ctx.imageModel, ctx.genAr);
16650
16678
  const genId = `s${ctx.sceneIndex}${tag}_${edge}`;
16651
16679
  nodes.push({
16652
16680
  id: genId,
@@ -20373,8 +20401,13 @@ function scaffoldStaticAd(input, elementsInput, opts) {
20373
20401
  inputs: { reference, target_blueprint: "$ref:prompt.asset" },
20374
20402
  params: {
20375
20403
  model: opts.genModel,
20376
- aspect_ratio: baseAspectRatio(blueprint, opts),
20377
- image_size: "2K",
20404
+ // The hero renders at the closest ratio its model actually accepts; the
20405
+ // placement fan-out below adapts it to the exact platform formats, which
20406
+ // is also how a 4:5 Meta feed ad gets made on a model that has no 4:5.
20407
+ aspect_ratio: nearestSupportedAspectRatio("image_generate", opts.genModel, baseAspectRatio(blueprint, opts)),
20408
+ ...supportsParam("image_generate", opts.genModel, "image_size") ? { image_size: "2K" } : {},
20409
+ // Per-model image defaults (gpt-image: quality=high).
20410
+ ...imageProfileFor(opts.genModel)?.paramDefaults ?? {},
20378
20411
  prompt
20379
20412
  }
20380
20413
  });
@@ -20741,7 +20774,7 @@ function resolveModels(args) {
20741
20774
  describeModel: pick("describe-model", "image_describe", "~google/gemini-pro-latest"),
20742
20775
  selectModel: pick("select-model", "text_generate", "~google/gemini-flash-latest"),
20743
20776
  layoutModel: pick("layout-model", "text_generate", "~google/gemini-flash-latest"),
20744
- genModel: pick("gen-model", "image_generate", "openai/gpt-5.4-image-2")
20777
+ genModel: pick("gen-model", "image_generate", "openai/gpt-image-2")
20745
20778
  };
20746
20779
  }
20747
20780
  var PLATFORM_PLACEMENTS = {
@@ -21383,7 +21416,7 @@ function resolveModels2(args) {
21383
21416
  // Default to the strongest image model (matches the static-ad scaffold); the
21384
21417
  // frame generators need the most faithful text/identity reproduction. Override
21385
21418
  // with --image-model for a cheaper/faster pass.
21386
- imageModel: pick("image-model", "image_generate", "openai/gpt-5.4-image-2")
21419
+ imageModel: pick("image-model", "image_generate", "openai/gpt-image-2")
21387
21420
  };
21388
21421
  }
21389
21422
  function hasPhotorealCast(elements) {
@@ -24093,7 +24126,7 @@ async function resolveReferences(spec) {
24093
24126
  var generateCommand = defineCommand114({
24094
24127
  meta: {
24095
24128
  name: "generate",
24096
- description: "Generate an image with AI and store it in the library (cost-tracked per request via OpenRouter usage). Models mirror the canvas: google/gemini-3.1-flash-image-preview (Nano Banana flash \u2014 default, fast, extreme aspect ratios) & google/gemini-3.5-flash (fast), google/gemini-3-pro-image-preview (Nano Banana Pro \u2014 highest fidelity), openai/gpt-5.4-image-2 (photoreal, cleanest in-image text, best for ad/landing reproduction), recraft/recraft-v4.1-pro-vector (vector/SVG-style with palette control). The result is auto-ingested (describe + embed), so the next `baker images library` query finds it. Pass --reference with image URLs and/or local file paths (Pinterest, stock, brand assets, sandbox files) to ground generation in reality.\n\nExamples:\n baker images generate 'a friendly golden retriever sitting in a bright modern living room' --aspect-ratio 16:9\n baker images generate 'hero shot of a matte black water bottle on marble' --model openai/gpt-5.4-image-2 --image-size 2K\n baker images generate 'lifestyle photo matching this mood' --reference 'https://\u2026/ref1.jpg,https://\u2026/ref2.jpg'\n baker images generate 'put this product on a marble countertop, soft daylight' --reference './src/brand/logos/product.png,./refs/kitchen-mood.jpg'\n baker images generate 'flat geometric mascot, brand palette' --model recraft/recraft-v4.1-pro-vector --rgb-colors '[[10,10,10],[255,80,0]]'"
24129
+ description: "Generate an image with AI and store it in the library (cost-tracked per request via OpenRouter usage). Models mirror the canvas: google/gemini-3.1-flash-image-preview (Nano Banana flash \u2014 default, fast, extreme aspect ratios), google/gemini-3-pro-image-preview (Nano Banana Pro \u2014 highest fidelity), openai/gpt-image-2 (photoreal, cleanest in-image text, best for ad/landing reproduction \u2014 no --image-size, and no 4:5 / 5:4), recraft/recraft-v4.1-pro-vector (vector/SVG-style with palette control). The result is auto-ingested (describe + embed), so the next `baker images library` query finds it. Pass --reference with image URLs and/or local file paths (Pinterest, stock, brand assets, sandbox files) to ground generation in reality.\n\nExamples:\n baker images generate 'a friendly golden retriever sitting in a bright modern living room' --aspect-ratio 16:9\n baker images generate 'hero shot of a matte black water bottle on marble' --model openai/gpt-image-2 --aspect-ratio 3:2\n baker images generate 'lifestyle photo matching this mood' --reference 'https://\u2026/ref1.jpg,https://\u2026/ref2.jpg'\n baker images generate 'put this product on a marble countertop, soft daylight' --reference './src/brand/logos/product.png,./refs/kitchen-mood.jpg'\n baker images generate 'flat geometric mascot, brand palette' --model recraft/recraft-v4.1-pro-vector --rgb-colors '[[10,10,10],[255,80,0]]'"
24097
24130
  },
24098
24131
  args: {
24099
24132
  prompt: { type: "positional", description: "What to generate", required: false },
@@ -24266,6 +24299,34 @@ var gifCommand = defineCommand116({
24266
24299
 
24267
24300
  // src/commands/images/google.ts
24268
24301
  import { defineCommand as defineCommand117 } from "citty";
24302
+
24303
+ // src/commands/images/searchHints.ts
24304
+ var FALLBACK = {
24305
+ stock: "Still empty \u2192 `baker images find <query> --sources library,pinterest,google` (one call across the providers stock does not cover \u2014 `--sources` is required, `find` alone searches the library only) or `baker images generate` to make the asset. Do not re-run this search with reshuffled flags.",
24306
+ google: "Still empty \u2192 `baker images generate` to make the asset. Google is the last-resort provider; there is nothing below it to retry."
24307
+ };
24308
+ function emptyResultHints({ provider, hitCount, activeFilters }) {
24309
+ if (hitCount > 0) {
24310
+ return [];
24311
+ }
24312
+ const hints = [];
24313
+ if (activeFilters.length > 0) {
24314
+ hints.push(
24315
+ `No hits. Drop the filters before touching the query \u2014 ${activeFilters.join(", ")} narrowed this search and filter combinations are the usual cause of an empty result.`
24316
+ );
24317
+ }
24318
+ hints.push(FALLBACK[provider]);
24319
+ return hints;
24320
+ }
24321
+ function activeFilterFlags(args, candidates) {
24322
+ return candidates.filter((flag) => args[flag] !== void 0 && args[flag] !== "").map((flag) => `--${flag}`);
24323
+ }
24324
+
24325
+ // src/commands/images/google.ts
24326
+ var GOOGLE_ERROR_FIX = {
24327
+ action: "use_different_resource",
24328
+ explanation: "Generate the asset instead of retrying Google. Google is the last-resort image provider. Run `baker images 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."
24329
+ };
24269
24330
  registerSchema({
24270
24331
  command: "images.google",
24271
24332
  description: "Google Images search via the official Custom Search JSON API. Unverified source \u2014 inspect before placing.",
@@ -24336,10 +24397,18 @@ var googleCommand2 = defineCommand117({
24336
24397
  if (args["auto-ingest"]) body.autoIngest = Number(args["auto-ingest"]);
24337
24398
  if (args.context) body.descriptionContext = args.context;
24338
24399
  const data = await apiPost("/api/images/google", body);
24339
- writeJson({ ok: true, data });
24400
+ const hints = emptyResultHints({
24401
+ provider: "google",
24402
+ hitCount: data.hits.length,
24403
+ activeFilters: activeFilterFlags(args, ["type", "size", "color", "safe"])
24404
+ });
24405
+ writeJson({ ok: true, data, ...hints.length ? { hints } : {} });
24340
24406
  } catch (err) {
24341
24407
  if (err instanceof ApiError) {
24342
- writeJson({ ok: false, error: { code: err.code, message: err.message } });
24408
+ writeJson({
24409
+ ok: false,
24410
+ error: { code: err.code, message: err.message, fix: GOOGLE_ERROR_FIX }
24411
+ });
24343
24412
  process.exit(1);
24344
24413
  }
24345
24414
  writeJson({ ok: false, error: { code: "INTERNAL_ERROR", message: "Unexpected error" } });
@@ -25697,6 +25766,10 @@ var stickerCommand = defineCommand126({
25697
25766
 
25698
25767
  // src/commands/images/stock.ts
25699
25768
  import { defineCommand as defineCommand127 } from "citty";
25769
+ var STOCK_ERROR_FIX = {
25770
+ action: "use_different_resource",
25771
+ 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 images 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."
25772
+ };
25700
25773
  registerSchema({
25701
25774
  command: "images.stock",
25702
25775
  description: "Stock photo, vector illustration, icon-set, and PSD search via Magnific (Freepik's developer API).",
@@ -25754,6 +25827,21 @@ registerSchema({
25754
25827
  }
25755
25828
  }
25756
25829
  });
25830
+ function buildStockRequest(query, args) {
25831
+ const body = { query };
25832
+ if (args.type) body.contentType = args.type;
25833
+ if (args.orientation) body.orientation = args.orientation;
25834
+ if (args.license) body.license = args.license;
25835
+ if (args.color) body.color = args.color;
25836
+ if (args.ai) body.aiGenerated = args.ai;
25837
+ if (args.people) body.people = args.people;
25838
+ if (args.order) body.order = args.order;
25839
+ if (args.limit) body.limit = Number(args.limit);
25840
+ if (args.page) body.page = Number(args.page);
25841
+ if (args["auto-ingest"]) body.autoIngest = Number(args["auto-ingest"]);
25842
+ if (args.context) body.descriptionContext = args.context;
25843
+ return body;
25844
+ }
25757
25845
  var stockCommand = defineCommand127({
25758
25846
  meta: {
25759
25847
  name: "stock",
@@ -25784,23 +25872,23 @@ var stockCommand = defineCommand127({
25784
25872
  writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message: "Query is required" } });
25785
25873
  process.exit(1);
25786
25874
  }
25787
- const body = { query };
25788
- if (args.type) body.contentType = args.type;
25789
- if (args.orientation) body.orientation = args.orientation;
25790
- if (args.license) body.license = args.license;
25791
- if (args.color) body.color = args.color;
25792
- if (args.ai) body.aiGenerated = args.ai;
25793
- if (args.people) body.people = args.people;
25794
- if (args.order) body.order = args.order;
25795
- if (args.limit) body.limit = Number(args.limit);
25796
- if (args.page) body.page = Number(args.page);
25797
- if (args["auto-ingest"]) body.autoIngest = Number(args["auto-ingest"]);
25798
- if (args.context) body.descriptionContext = args.context;
25799
- const data = await apiPost("/api/images/stock", body);
25800
- writeJson({ ok: true, data });
25875
+ const data = await apiPost("/api/images/stock", buildStockRequest(query, args));
25876
+ const hints = emptyResultHints({
25877
+ provider: "stock",
25878
+ hitCount: data.hits.length,
25879
+ activeFilters: activeFilterFlags(args, ["type", "orientation", "license", "color", "ai", "people"])
25880
+ });
25881
+ writeJson({ ok: true, data, ...hints.length ? { hints } : {} });
25801
25882
  } catch (err) {
25802
25883
  if (err instanceof ApiError) {
25803
- writeJson({ ok: false, error: { code: err.code, message: err.message } });
25884
+ writeJson({
25885
+ ok: false,
25886
+ error: {
25887
+ code: err.code,
25888
+ message: err.message,
25889
+ fix: STOCK_ERROR_FIX
25890
+ }
25891
+ });
25804
25892
  process.exit(1);
25805
25893
  }
25806
25894
  writeJson({ ok: false, error: { code: "INTERNAL_ERROR", message: "Unexpected error" } });
@@ -28931,9 +29019,514 @@ var schemaCommand = defineCommand154({
28931
29019
  }
28932
29020
  });
28933
29021
 
28934
- // src/commands/tags/index.ts
29022
+ // src/commands/tag-manager/index.ts
29023
+ import { defineCommand as defineCommand158 } from "citty";
29024
+
29025
+ // src/commands/tag-manager/draft.ts
28935
29026
  import { defineCommand as defineCommand155 } from "citty";
28936
29027
 
29028
+ // src/commands/tag-manager/shared.ts
29029
+ import { readFileSync as readFileSync12 } from "fs";
29030
+ function failValidation3(message) {
29031
+ writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
29032
+ process.exit(1);
29033
+ }
29034
+ function requireTarget4(args, entity) {
29035
+ const positional = Array.isArray(args._) ? args._[0] : void 0;
29036
+ const target = args.id ?? args.target ?? positional;
29037
+ if (typeof target !== "string" || target.length === 0) {
29038
+ failValidation3(`pass the ${entity} id or path as the positional argument`);
29039
+ }
29040
+ return target;
29041
+ }
29042
+ function loadJsonArg(args, flag = "json") {
29043
+ const inline = args[flag];
29044
+ const file = args.file;
29045
+ const raw = typeof file === "string" && file.length > 0 ? readFileSync12(file, "utf8") : typeof inline === "string" && inline.length > 0 ? inline : void 0;
29046
+ if (raw === void 0) {
29047
+ failValidation3(`pass --${flag} with inline JSON or --file with a path to a JSON file`);
29048
+ }
29049
+ try {
29050
+ const parsed = JSON.parse(raw);
29051
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
29052
+ failValidation3(`--${flag} must be a JSON object`);
29053
+ }
29054
+ return parsed;
29055
+ } catch {
29056
+ return failValidation3(`--${flag} is not valid JSON`);
29057
+ }
29058
+ }
29059
+ function handleError3(err) {
29060
+ if (err instanceof ApiError) {
29061
+ const notConnected = err.code === "NOT_FOUND" || err.code === "UNAUTHORIZED";
29062
+ writeJsonEnvelope({
29063
+ ok: false,
29064
+ error: {
29065
+ code: err.code,
29066
+ message: err.message,
29067
+ ...notConnected ? {
29068
+ fix: {
29069
+ action: "ask_user",
29070
+ explanation: "Tag Manager is not connected for this company yet. Ask the user to connect Tag Manager in Settings \u2192 Connections and pick the container to manage, then retry. Continue with the rest of the task in the meantime."
29071
+ }
29072
+ } : {},
29073
+ retryable: false
29074
+ }
29075
+ });
29076
+ process.exit(1);
29077
+ }
29078
+ writeJsonEnvelope({ ok: false, error: { code: "NETWORK_ERROR", message: "Unexpected error" } });
29079
+ process.exit(1);
29080
+ }
29081
+ function stageHints(response) {
29082
+ const hints = [];
29083
+ if (response.mode === "simulated") {
29084
+ hints.push(
29085
+ "Tag Manager writes are SIMULATED for this company \u2014 this change will not reach the real container on publish."
29086
+ );
29087
+ }
29088
+ hints.push(
29089
+ "Staged only. On publish these changes land in a new Tag Manager version that is NOT published \u2014 tell the user to publish that version in Tag Manager to make it live."
29090
+ );
29091
+ return hints;
29092
+ }
29093
+ async function stageOp3(op) {
29094
+ const chatId = requireChatId();
29095
+ try {
29096
+ const data = await apiPost("/api/tag-manager/draft/stage", { chatId, op });
29097
+ writeJsonEnvelope({ ok: true, data, hints: stageHints(data) });
29098
+ } catch (err) {
29099
+ handleError3(err);
29100
+ }
29101
+ }
29102
+ async function draftAction2(path27, body) {
29103
+ const chatId = requireChatId();
29104
+ try {
29105
+ const data = await apiPost(path27, { chatId, ...body });
29106
+ writeJsonEnvelope({ ok: true, data });
29107
+ return data;
29108
+ } catch (err) {
29109
+ handleError3(err);
29110
+ }
29111
+ }
29112
+ function renderDraft(response) {
29113
+ if (response.count === 0) {
29114
+ return "No Tag Manager changes staged on this chat.";
29115
+ }
29116
+ const lines = [
29117
+ `${response.count} staged Tag Manager change(s) \u2014 mode: ${response.mode}`,
29118
+ ...response.ops.map((op) => {
29119
+ const status = op.result ? ` [${op.result.status}]` : "";
29120
+ return ` ${op.ref} ${op.summary}${status}`;
29121
+ })
29122
+ ];
29123
+ if (response.versionId) {
29124
+ lines.push(
29125
+ "",
29126
+ `Applied into Tag Manager version ${response.versionId}. It is NOT published \u2014 publish it in Tag Manager to go live.`
29127
+ );
29128
+ }
29129
+ if (response.compilerError) {
29130
+ lines.push("", `Tag Manager reported a problem building the version: ${response.compilerError}`);
29131
+ }
29132
+ return lines.join("\n");
29133
+ }
29134
+ async function draftList(json) {
29135
+ const chatId = requireChatId();
29136
+ try {
29137
+ const data = await apiPost("/api/tag-manager/draft", { chatId });
29138
+ if (json) {
29139
+ writeJsonEnvelope({ ok: true, data });
29140
+ return;
29141
+ }
29142
+ process.stdout.write(`${renderDraft(data)}
29143
+ `);
29144
+ } catch (err) {
29145
+ handleError3(err);
29146
+ }
29147
+ }
29148
+
29149
+ // src/commands/tag-manager/draft.ts
29150
+ registerSchema({
29151
+ command: "tagManager.draft",
29152
+ description: "Review and undo the Tag Manager changes staged on this chat. Use `list` to see everything staged, `show` to inspect one change in full before publish, `amend` to correct one in place, and `remove`/`clear` to drop them.",
29153
+ args: {
29154
+ json: { type: "boolean", description: "Print the raw JSON envelope instead of the readable list", required: false }
29155
+ }
29156
+ });
29157
+ var draftCommand3 = defineCommand155({
29158
+ meta: {
29159
+ name: "draft",
29160
+ description: "List, show, amend, remove, or clear staged Tag Manager changes for this chat"
29161
+ },
29162
+ subCommands: {
29163
+ list: defineCommand155({
29164
+ meta: {
29165
+ name: "list",
29166
+ description: "Review everything staged on this chat (--json for the raw envelope)"
29167
+ },
29168
+ args: { json: { type: "boolean", description: "Print the raw JSON envelope", required: false } },
29169
+ run: async ({ args }) => {
29170
+ await draftList(args.json === true);
29171
+ }
29172
+ }),
29173
+ show: defineCommand155({
29174
+ meta: {
29175
+ name: "show",
29176
+ description: "Print the full staged payload for one change \u2014 the receipt to verify it looks right before publish (never truncated)."
29177
+ },
29178
+ args: { ref: { type: "positional", description: "Staged ref (gtm_temp_*) or target", required: false } },
29179
+ run: async ({ args }) => {
29180
+ await draftAction2("/api/tag-manager/draft/show", {
29181
+ ref: requireTarget4(args, "change")
29182
+ });
29183
+ }
29184
+ }),
29185
+ amend: defineCommand155({
29186
+ meta: {
29187
+ name: "amend",
29188
+ 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."
29189
+ },
29190
+ args: {
29191
+ ref: { type: "positional", description: "Staged ref (gtm_temp_*) or target", required: false },
29192
+ patch: { type: "string", description: "Inline JSON patch object", required: false },
29193
+ file: { type: "string", description: "JSON file with the patch object", required: false }
29194
+ },
29195
+ run: async ({ args }) => {
29196
+ await draftAction2("/api/tag-manager/draft/amend", {
29197
+ ref: requireTarget4(args, "change"),
29198
+ patch: loadJsonArg(args, "patch")
29199
+ });
29200
+ }
29201
+ }),
29202
+ remove: defineCommand155({
29203
+ meta: { name: "remove", description: "Remove one staged change (cascades to anything depending on it)" },
29204
+ args: { ref: { type: "positional", description: "Staged ref (gtm_temp_*) or target", required: false } },
29205
+ run: async ({ args }) => {
29206
+ await draftAction2("/api/tag-manager/draft/remove", {
29207
+ ref: requireTarget4(args, "change")
29208
+ });
29209
+ }
29210
+ }),
29211
+ clear: defineCommand155({
29212
+ meta: { name: "clear", description: "Discard all Tag Manager changes staged on this chat" },
29213
+ run: async () => {
29214
+ await draftAction2("/api/tag-manager/draft/clear", {});
29215
+ }
29216
+ })
29217
+ }
29218
+ });
29219
+
29220
+ // src/commands/tag-manager/read.ts
29221
+ import { defineCommand as defineCommand156 } from "citty";
29222
+ registerSchema({
29223
+ command: "tagManager.containers",
29224
+ description: "List the Google Tag Manager containers this company's connection can reach. The connected container is flagged. Start here to confirm which container you are managing.",
29225
+ args: {}
29226
+ });
29227
+ registerSchema({
29228
+ command: "tagManager.read",
29229
+ description: "Read what is currently inside a Tag Manager container: tags, triggers, variables, folders and enabled built-in variables. Start here before proposing any change, so you edit what exists instead of duplicating it. Compact by default; pass --full for raw Tag Manager objects.",
29230
+ args: {
29231
+ container: { type: "string", description: "Numeric container id (defaults to the connected one)", required: false },
29232
+ workspace: { type: "string", description: "Workspace id (defaults to the default workspace)", required: false },
29233
+ entities: {
29234
+ type: "string",
29235
+ description: "Comma-separated subset: tag,trigger,variable,folder,builtInVariable",
29236
+ required: false
29237
+ },
29238
+ full: { type: "boolean", description: "Include the raw Tag Manager object for each entity", required: false }
29239
+ }
29240
+ });
29241
+ function fail6(err) {
29242
+ if (err instanceof ApiError) {
29243
+ const notConnected = err.code === "NOT_FOUND" || err.code === "UNAUTHORIZED";
29244
+ writeJsonEnvelope({
29245
+ ok: false,
29246
+ error: {
29247
+ code: err.code,
29248
+ message: err.message,
29249
+ ...notConnected ? {
29250
+ fix: {
29251
+ action: "ask_user",
29252
+ explanation: "Tag Manager is not connected for this company yet. Ask the user to connect Tag Manager in Settings \u2192 Connections and choose the container to manage, then retry. Continue with the rest of the task in the meantime."
29253
+ }
29254
+ } : {},
29255
+ retryable: false
29256
+ }
29257
+ });
29258
+ process.exit(1);
29259
+ }
29260
+ writeJsonEnvelope({ ok: false, error: { code: "NETWORK_ERROR", message: "Unexpected error" } });
29261
+ process.exit(1);
29262
+ }
29263
+ var containersCommand = defineCommand156({
29264
+ meta: {
29265
+ name: "containers",
29266
+ description: `List Tag Manager containers reachable by this company's connection.
29267
+
29268
+ Start here:
29269
+ baker tag-manager containers`
29270
+ },
29271
+ run: async () => {
29272
+ try {
29273
+ const data = await apiGet("/api/tag-manager/containers");
29274
+ writeJsonEnvelope({ ok: true, data });
29275
+ } catch (err) {
29276
+ fail6(err);
29277
+ }
29278
+ }
29279
+ });
29280
+ var readCommand = defineCommand156({
29281
+ meta: {
29282
+ name: "read",
29283
+ description: `Read the current contents of the Tag Manager container \u2014 always do this before staging changes.
29284
+
29285
+ Examples:
29286
+ baker tag-manager read
29287
+ baker tag-manager read --entities tag,trigger
29288
+ baker tag-manager read --full`
29289
+ },
29290
+ args: {
29291
+ container: { type: "string", description: "Numeric container id", required: false },
29292
+ workspace: { type: "string", description: "Workspace id", required: false },
29293
+ entities: { type: "string", description: "Comma-separated entity subset", required: false },
29294
+ full: { type: "boolean", description: "Include raw Tag Manager objects", required: false }
29295
+ },
29296
+ run: async ({ args }) => {
29297
+ const entities = typeof args.entities === "string" && args.entities.length > 0 ? args.entities.split(",").map((entry) => entry.trim()) : void 0;
29298
+ try {
29299
+ const data = await apiPost("/api/tag-manager/read", {
29300
+ containerId: args.container,
29301
+ workspaceId: args.workspace,
29302
+ entities,
29303
+ full: args.full === true
29304
+ });
29305
+ const hints = [];
29306
+ if (data.installedContainerMismatch) {
29307
+ hints.push(data.installedContainerMismatch);
29308
+ }
29309
+ hints.push(
29310
+ "Reference an existing trigger or variable by its numeric id or {{Name}}. To reference something you are staging in this same chat, use its gtm_temp_* ref and the executor resolves it at publish."
29311
+ );
29312
+ writeJsonEnvelope({ ok: true, data, hints });
29313
+ } catch (err) {
29314
+ fail6(err);
29315
+ }
29316
+ }
29317
+ });
29318
+
29319
+ // src/commands/tag-manager/write-commands.ts
29320
+ import { defineCommand as defineCommand157 } from "citty";
29321
+ var ENTITIES = [
29322
+ {
29323
+ entity: "tag",
29324
+ noun: "tag",
29325
+ createHint: 'Payload needs name + type (e.g. "gaawe" for a GA4 event) and usually parameter[] and firingTriggerId[]. Reference a trigger by its numeric id, or by a gtm_temp_* ref if you are staging it in this same chat.'
29326
+ },
29327
+ {
29328
+ entity: "trigger",
29329
+ noun: "trigger",
29330
+ createHint: 'Payload needs name + type (e.g. "pageview", "click", "customEvent"). Stage the trigger BEFORE the tag that fires on it, then reference it by its gtm_temp_* ref.'
29331
+ },
29332
+ {
29333
+ entity: "variable",
29334
+ noun: "variable",
29335
+ createHint: 'Payload needs name + type (e.g. "v" for a data layer variable, "c" for a constant). Other entities reference a variable by NAME as {{Variable Name}}.'
29336
+ },
29337
+ {
29338
+ entity: "folder",
29339
+ noun: "folder",
29340
+ createHint: "Payload needs name. Use folders to keep a large container legible."
29341
+ }
29342
+ ];
29343
+ for (const { entity, noun, createHint } of ENTITIES) {
29344
+ registerSchema({
29345
+ command: `tagManager.${entity}.create`,
29346
+ description: `Stage the creation of a Tag Manager ${noun}. ${createHint} Staged only \u2014 applies on publish into an unpublished container version.`,
29347
+ args: {
29348
+ json: { type: "string", description: `Inline JSON ${noun} definition`, required: false },
29349
+ file: { type: "string", description: `Path to a JSON file with the ${noun} definition`, required: false },
29350
+ container: { type: "string", description: "Numeric container id", required: false }
29351
+ }
29352
+ });
29353
+ registerSchema({
29354
+ command: `tagManager.${entity}.update`,
29355
+ description: `Stage an update to an existing Tag Manager ${noun}. Pass the ${noun} id or path as the positional argument and the changed fields as JSON. Read the container first so you send the fields you mean to change.`,
29356
+ args: {
29357
+ json: { type: "string", description: "Inline JSON with the changed fields", required: false },
29358
+ file: { type: "string", description: "Path to a JSON file with the changed fields", required: false }
29359
+ }
29360
+ });
29361
+ registerSchema({
29362
+ command: `tagManager.${entity}.delete`,
29363
+ description: `Stage the deletion of a Tag Manager ${noun}. Pass its id or path as the positional argument. Deleting is irreversible once the resulting version is published \u2014 confirm with the user first.`,
29364
+ args: {}
29365
+ });
29366
+ }
29367
+ function entityCommand(entity, noun) {
29368
+ return defineCommand157({
29369
+ meta: { name: entity, description: `Stage ${noun} changes on this chat's Tag Manager draft` },
29370
+ subCommands: {
29371
+ create: defineCommand157({
29372
+ meta: {
29373
+ name: "create",
29374
+ description: `Stage a new ${noun}
29375
+
29376
+ Examples:
29377
+ baker tag-manager ${entity} create --json '{"name":"GA4 Config","type":"googtag"}'
29378
+ baker tag-manager ${entity} create --file ./${entity}.json`
29379
+ },
29380
+ args: {
29381
+ json: { type: "string", description: "Inline JSON definition", required: false },
29382
+ file: { type: "string", description: "Path to a JSON file", required: false },
29383
+ container: { type: "string", description: "Numeric container id", required: false }
29384
+ },
29385
+ run: async ({ args }) => {
29386
+ await stageOp3({
29387
+ kind: `tagManager.${entity}.create`,
29388
+ payload: loadJsonArg(args),
29389
+ ...typeof args.container === "string" ? { containerId: args.container } : {}
29390
+ });
29391
+ }
29392
+ }),
29393
+ update: defineCommand157({
29394
+ meta: {
29395
+ name: "update",
29396
+ description: `Stage an update to an existing ${noun} (pass its id or path)`
29397
+ },
29398
+ args: {
29399
+ id: { type: "positional", description: `${noun} id or path`, required: false },
29400
+ json: { type: "string", description: "Inline JSON with changed fields", required: false },
29401
+ file: { type: "string", description: "Path to a JSON file", required: false },
29402
+ container: { type: "string", description: "Numeric container id", required: false }
29403
+ },
29404
+ run: async ({ args }) => {
29405
+ await stageOp3({
29406
+ kind: `tagManager.${entity}.update`,
29407
+ target: requireTarget4(args, noun),
29408
+ payload: loadJsonArg(args),
29409
+ ...typeof args.container === "string" ? { containerId: args.container } : {}
29410
+ });
29411
+ }
29412
+ }),
29413
+ delete: defineCommand157({
29414
+ meta: { name: "delete", description: `Stage the deletion of a ${noun} (pass its id or path)` },
29415
+ args: {
29416
+ id: { type: "positional", description: `${noun} id or path`, required: false },
29417
+ container: { type: "string", description: "Numeric container id", required: false }
29418
+ },
29419
+ run: async ({ args }) => {
29420
+ await stageOp3({
29421
+ kind: `tagManager.${entity}.delete`,
29422
+ target: requireTarget4(args, noun),
29423
+ ...typeof args.container === "string" ? { containerId: args.container } : {}
29424
+ });
29425
+ }
29426
+ })
29427
+ }
29428
+ });
29429
+ }
29430
+ var tagCommand = entityCommand("tag", "tag");
29431
+ var triggerCommand2 = entityCommand("trigger", "trigger");
29432
+ var variableCommand = entityCommand("variable", "variable");
29433
+ var folderCommand = entityCommand("folder", "folder");
29434
+ registerSchema({
29435
+ command: "tagManager.builtin",
29436
+ description: "Enable or disable Tag Manager built-in variables by type (e.g. clickUrl, pageUrl, formId). Built-in variables must be enabled before a trigger or tag can reference them as {{Click URL}}.",
29437
+ args: {
29438
+ types: { type: "string", description: "Comma-separated built-in variable types", required: true }
29439
+ }
29440
+ });
29441
+ function builtinTypes(args) {
29442
+ const raw = args.types;
29443
+ if (typeof raw !== "string" || raw.length === 0) {
29444
+ process.stdout.write(
29445
+ `${JSON.stringify({ ok: false, error: { code: "VALIDATION_ERROR", message: "pass --types with comma-separated built-in variable types" } }, null, 2)}
29446
+ `
29447
+ );
29448
+ process.exit(1);
29449
+ }
29450
+ return raw.split(",").map((entry) => entry.trim());
29451
+ }
29452
+ var builtinCommand = defineCommand157({
29453
+ meta: {
29454
+ name: "builtin",
29455
+ description: `Enable or disable built-in variables
29456
+
29457
+ Examples:
29458
+ baker tag-manager builtin enable --types clickUrl,clickText
29459
+ baker tag-manager builtin disable --types formId`
29460
+ },
29461
+ subCommands: {
29462
+ enable: defineCommand157({
29463
+ meta: { name: "enable", description: "Stage enabling built-in variables" },
29464
+ args: {
29465
+ types: { type: "string", description: "Comma-separated types", required: false },
29466
+ container: { type: "string", description: "Numeric container id", required: false }
29467
+ },
29468
+ run: async ({ args }) => {
29469
+ await stageOp3({
29470
+ kind: "tagManager.builtInVariable.enable",
29471
+ payload: { type: builtinTypes(args) },
29472
+ ...typeof args.container === "string" ? { containerId: args.container } : {}
29473
+ });
29474
+ }
29475
+ }),
29476
+ disable: defineCommand157({
29477
+ meta: { name: "disable", description: "Stage disabling built-in variables" },
29478
+ args: {
29479
+ types: { type: "string", description: "Comma-separated types", required: false },
29480
+ container: { type: "string", description: "Numeric container id", required: false }
29481
+ },
29482
+ run: async ({ args }) => {
29483
+ await stageOp3({
29484
+ kind: "tagManager.builtInVariable.disable",
29485
+ payload: { type: builtinTypes(args) },
29486
+ ...typeof args.container === "string" ? { containerId: args.container } : {}
29487
+ });
29488
+ }
29489
+ })
29490
+ }
29491
+ });
29492
+
29493
+ // src/commands/tag-manager/index.ts
29494
+ var tagManagerCommand = defineCommand158({
29495
+ meta: {
29496
+ name: "tag-manager",
29497
+ description: `Read and change what lives inside the client's Google Tag Manager container \u2014 tags, triggers, variables, folders and built-in variables.
29498
+
29499
+ Start here:
29500
+ baker tag-manager read \u2014 see what the container holds today
29501
+
29502
+ Then stage changes (nothing is sent to Tag Manager until publish):
29503
+ baker tag-manager trigger create --json '{"name":"Quote Submit","type":"customEvent"}'
29504
+ baker tag-manager tag create --json '{"name":"GA4 Quote","type":"gaawe","firingTriggerId":["gtm_temp_trigger_..."]}'
29505
+ baker tag-manager draft list \u2014 review everything staged
29506
+
29507
+ On publish, staged changes are written into a new Tag Manager version that is NOT published.
29508
+ Tell the user to publish that version in Tag Manager to make it live.
29509
+
29510
+ This owns the CONTENTS of the container. Installing the container snippet on the site is a
29511
+ different job \u2014 that is \`baker tags\` with the googleTagManager tag.
29512
+
29513
+ Full guide: __tooling__/docs/tools/baker/tag-manager.md`
29514
+ },
29515
+ subCommands: {
29516
+ containers: containersCommand,
29517
+ read: readCommand,
29518
+ tag: tagCommand,
29519
+ trigger: triggerCommand2,
29520
+ variable: variableCommand,
29521
+ folder: folderCommand,
29522
+ builtin: builtinCommand,
29523
+ draft: draftCommand3
29524
+ }
29525
+ });
29526
+
29527
+ // src/commands/tags/index.ts
29528
+ import { defineCommand as defineCommand159 } from "citty";
29529
+
28937
29530
  // src/commands/tags/shared.ts
28938
29531
  function failApi3(err) {
28939
29532
  if (err instanceof ApiError) {
@@ -28998,7 +29591,7 @@ async function listTags(json) {
28998
29591
  failApi3(err);
28999
29592
  }
29000
29593
  }
29001
- var listCommand10 = defineCommand155({
29594
+ var listCommand10 = defineCommand159({
29002
29595
  meta: {
29003
29596
  name: "list",
29004
29597
  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"
@@ -29017,7 +29610,7 @@ async function listDraft3() {
29017
29610
  failApi3(err);
29018
29611
  }
29019
29612
  }
29020
- var draftCommand3 = defineCommand155({
29613
+ var draftCommand4 = defineCommand159({
29021
29614
  meta: {
29022
29615
  name: "draft",
29023
29616
  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)."
@@ -29026,7 +29619,7 @@ var draftCommand3 = defineCommand155({
29026
29619
  await listDraft3();
29027
29620
  }
29028
29621
  });
29029
- var tagsCommand3 = defineCommand155({
29622
+ var tagsCommand3 = defineCommand159({
29030
29623
  meta: {
29031
29624
  name: "tags",
29032
29625
  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.
@@ -29044,7 +29637,7 @@ Full guide: __tooling__/docs/tools/baker/tags.md`
29044
29637
  },
29045
29638
  subCommands: {
29046
29639
  list: listCommand10,
29047
- draft: draftCommand3
29640
+ draft: draftCommand4
29048
29641
  },
29049
29642
  run: async () => {
29050
29643
  await listTags(false);
@@ -29052,10 +29645,10 @@ Full guide: __tooling__/docs/tools/baker/tags.md`
29052
29645
  });
29053
29646
 
29054
29647
  // src/commands/testimonials/index.ts
29055
- import { defineCommand as defineCommand159 } from "citty";
29648
+ import { defineCommand as defineCommand163 } from "citty";
29056
29649
 
29057
29650
  // src/commands/testimonials/get.ts
29058
- import { defineCommand as defineCommand156 } from "citty";
29651
+ import { defineCommand as defineCommand160 } from "citty";
29059
29652
  registerSchema({
29060
29653
  command: "testimonials.get",
29061
29654
  description: "Get a single testimonial by ID",
@@ -29063,7 +29656,7 @@ registerSchema({
29063
29656
  id: { type: "string", description: "Testimonial ID", required: true }
29064
29657
  }
29065
29658
  });
29066
- var getCommand4 = defineCommand156({
29659
+ var getCommand4 = defineCommand160({
29067
29660
  meta: { name: "get", description: "Get a single testimonial by ID. Example: baker testimonials get j571abc123" },
29068
29661
  args: {
29069
29662
  id: { type: "positional", description: "Testimonial ID", required: false },
@@ -29100,7 +29693,7 @@ var getCommand4 = defineCommand156({
29100
29693
  });
29101
29694
 
29102
29695
  // src/commands/testimonials/list.ts
29103
- import { defineCommand as defineCommand157 } from "citty";
29696
+ import { defineCommand as defineCommand161 } from "citty";
29104
29697
  registerSchema({
29105
29698
  command: "testimonials.list",
29106
29699
  description: "List testimonials with optional filters.",
@@ -29130,7 +29723,7 @@ registerSchema({
29130
29723
  limit: { type: "number", description: "Max results (default 50)", required: false, default: 50 }
29131
29724
  }
29132
29725
  });
29133
- var listCommand11 = defineCommand157({
29726
+ var listCommand11 = defineCommand161({
29134
29727
  meta: {
29135
29728
  name: "list",
29136
29729
  description: "List testimonials with optional filters. Example: baker testimonials list --source google --sentiment positive"
@@ -29179,7 +29772,7 @@ var listCommand11 = defineCommand157({
29179
29772
  });
29180
29773
 
29181
29774
  // src/commands/testimonials/search.ts
29182
- import { defineCommand as defineCommand158 } from "citty";
29775
+ import { defineCommand as defineCommand162 } from "citty";
29183
29776
  function languageBiasHint(results, requestedLanguage) {
29184
29777
  if (requestedLanguage) {
29185
29778
  return null;
@@ -29257,7 +29850,7 @@ function buildSearchRequest(query, args) {
29257
29850
  }
29258
29851
  return body;
29259
29852
  }
29260
- var searchCommand2 = defineCommand158({
29853
+ var searchCommand2 = defineCommand162({
29261
29854
  meta: {
29262
29855
  name: "search",
29263
29856
  description: "Semantic search testimonials by text query. Uses hybrid BM25 + vector + reranking. Example: baker testimonials search 'great service' --rating-min 4"
@@ -29313,7 +29906,7 @@ var searchCommand2 = defineCommand158({
29313
29906
  var tagsCommand4 = makeTagsCommand("testimonials", "testimonial", "/api/testimonials/tags");
29314
29907
 
29315
29908
  // src/commands/testimonials/index.ts
29316
- var testimonialsCommand = defineCommand159({
29909
+ var testimonialsCommand = defineCommand163({
29317
29910
  meta: {
29318
29911
  name: "testimonials",
29319
29912
  description: `Find and browse testimonials in Baker. Subcommands: search, get, list, tags.
@@ -29335,10 +29928,10 @@ Full guide: __tooling__/docs/tools/baker/testimonials.md`
29335
29928
  });
29336
29929
 
29337
29930
  // src/commands/videos/index.ts
29338
- import { defineCommand as defineCommand164 } from "citty";
29931
+ import { defineCommand as defineCommand168 } from "citty";
29339
29932
 
29340
29933
  // src/commands/videos/delete.ts
29341
- import { defineCommand as defineCommand160 } from "citty";
29934
+ import { defineCommand as defineCommand164 } from "citty";
29342
29935
  registerSchema({
29343
29936
  command: "videos.delete",
29344
29937
  description: "Delete a video by ID",
@@ -29352,7 +29945,7 @@ registerSchema({
29352
29945
  }
29353
29946
  }
29354
29947
  });
29355
- var deleteCommand3 = defineCommand160({
29948
+ var deleteCommand3 = defineCommand164({
29356
29949
  meta: {
29357
29950
  name: "delete",
29358
29951
  description: "Delete a video by ID. Use --dry-run to preview. Example: baker videos delete j571abc123 --dry-run"
@@ -29393,7 +29986,7 @@ var deleteCommand3 = defineCommand160({
29393
29986
  });
29394
29987
 
29395
29988
  // src/commands/videos/get.ts
29396
- import { defineCommand as defineCommand161 } from "citty";
29989
+ import { defineCommand as defineCommand165 } from "citty";
29397
29990
  registerSchema({
29398
29991
  command: "videos.get",
29399
29992
  description: "Get a single video by ID",
@@ -29401,7 +29994,7 @@ registerSchema({
29401
29994
  id: { type: "string", description: "Video ID", required: true }
29402
29995
  }
29403
29996
  });
29404
- var getCommand5 = defineCommand161({
29997
+ var getCommand5 = defineCommand165({
29405
29998
  meta: { name: "get", description: "Get a single video by ID. Example: baker videos get j571abc123" },
29406
29999
  args: {
29407
30000
  id: { type: "positional", description: "Video ID", required: false },
@@ -29438,7 +30031,7 @@ var getCommand5 = defineCommand161({
29438
30031
  });
29439
30032
 
29440
30033
  // src/commands/videos/search.ts
29441
- import { defineCommand as defineCommand162 } from "citty";
30034
+ import { defineCommand as defineCommand166 } from "citty";
29442
30035
  registerSchema({
29443
30036
  command: "videos.search",
29444
30037
  description: "Search videos by text query. Only returns ready videos.",
@@ -29448,7 +30041,7 @@ registerSchema({
29448
30041
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
29449
30042
  }
29450
30043
  });
29451
- var searchCommand3 = defineCommand162({
30044
+ var searchCommand3 = defineCommand166({
29452
30045
  meta: {
29453
30046
  name: "search",
29454
30047
  description: "Semantic search videos by text query. Uses hybrid BM25 + vector + reranking. Example: baker videos search 'product demo' --tags tutorial"
@@ -29500,7 +30093,7 @@ var tagsCommand5 = makeTagsCommand("videos", "video", "/api/videos/tags");
29500
30093
  // src/commands/videos/upload.ts
29501
30094
  import { readFile as readFile22, stat as stat7 } from "fs/promises";
29502
30095
  import { extname as extname3 } from "path";
29503
- import { defineCommand as defineCommand163 } from "citty";
30096
+ import { defineCommand as defineCommand167 } from "citty";
29504
30097
  var MIME_MAP = {
29505
30098
  ".mp4": "video/mp4",
29506
30099
  ".mov": "video/quicktime",
@@ -29534,7 +30127,7 @@ function detectContentType(filePath) {
29534
30127
  }
29535
30128
  return mime;
29536
30129
  }
29537
- var uploadCommand2 = defineCommand163({
30130
+ var uploadCommand2 = defineCommand167({
29538
30131
  meta: {
29539
30132
  name: "upload",
29540
30133
  description: "Upload a video file to Baker via Mux direct upload. Auto-detects content type. Example: baker videos upload ./demo.mp4"
@@ -29588,7 +30181,7 @@ var uploadCommand2 = defineCommand163({
29588
30181
  });
29589
30182
 
29590
30183
  // src/commands/videos/index.ts
29591
- var videosCommand = defineCommand164({
30184
+ var videosCommand = defineCommand168({
29592
30185
  meta: {
29593
30186
  name: "videos",
29594
30187
  description: `Find and manage videos in Baker. Subcommands: search, get, upload, delete, tags.
@@ -29612,10 +30205,10 @@ Full guide: __tooling__/docs/tools/baker/videos.md`
29612
30205
  });
29613
30206
 
29614
30207
  // src/commands/winning-ads/index.ts
29615
- import { defineCommand as defineCommand177 } from "citty";
30208
+ import { defineCommand as defineCommand181 } from "citty";
29616
30209
 
29617
30210
  // src/commands/winning-ads/advertisers.ts
29618
- import { defineCommand as defineCommand165 } from "citty";
30211
+ import { defineCommand as defineCommand169 } from "citty";
29619
30212
 
29620
30213
  // src/commands/winning-ads/shared.ts
29621
30214
  function splitList(value) {
@@ -29668,7 +30261,7 @@ function advertiserNormalizer(record, full) {
29668
30261
  last_synced_at: record.last_synced_at ?? null
29669
30262
  };
29670
30263
  }
29671
- var advertisersCommand2 = defineCommand165({
30264
+ var advertisersCommand2 = defineCommand169({
29672
30265
  meta: {
29673
30266
  name: "advertisers",
29674
30267
  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'
@@ -29726,7 +30319,7 @@ var advertisersCommand2 = defineCommand165({
29726
30319
  });
29727
30320
 
29728
30321
  // src/commands/winning-ads/brief.ts
29729
- import { defineCommand as defineCommand166 } from "citty";
30322
+ import { defineCommand as defineCommand170 } from "citty";
29730
30323
  registerSchema({
29731
30324
  command: "winning-ads.brief",
29732
30325
  description: "Generate a creative brief grounded in strategically-similar winning ads. Optionally describe the target creative with --dna (JSON) and steer with --notes.",
@@ -29772,7 +30365,7 @@ function parseDna(raw) {
29772
30365
  }
29773
30366
  return parsed;
29774
30367
  }
29775
- var briefCommand = defineCommand166({
30368
+ var briefCommand = defineCommand170({
29776
30369
  meta: {
29777
30370
  name: "brief",
29778
30371
  description: `Generate a creative brief from winning references. Example: baker winning-ads brief --dna '{"angle":"cost savings"}' --notes "B2B, LinkedIn video" --k 8`
@@ -29808,7 +30401,7 @@ var briefCommand = defineCommand166({
29808
30401
  });
29809
30402
 
29810
30403
  // src/commands/winning-ads/content.ts
29811
- import { defineCommand as defineCommand167 } from "citty";
30404
+ import { defineCommand as defineCommand171 } from "citty";
29812
30405
  registerSchema({
29813
30406
  command: "winning-ads.content",
29814
30407
  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.",
@@ -29821,7 +30414,7 @@ registerSchema({
29821
30414
  }
29822
30415
  }
29823
30416
  });
29824
- var contentCommand = defineCommand167({
30417
+ var contentCommand = defineCommand171({
29825
30418
  meta: {
29826
30419
  name: "content",
29827
30420
  description: "Read the transcript + on-screen text + copy of one winning ad. Example: baker winning-ads content adg_123 --platform meta --full --output md"
@@ -29870,7 +30463,7 @@ var contentCommand = defineCommand167({
29870
30463
  });
29871
30464
 
29872
30465
  // src/commands/winning-ads/feed.ts
29873
- import { defineCommand as defineCommand168 } from "citty";
30466
+ import { defineCommand as defineCommand172 } from "citty";
29874
30467
  function buildFeedParams(input) {
29875
30468
  const params = {};
29876
30469
  const advertiser = splitList(input.advertiser);
@@ -29922,7 +30515,7 @@ registerSchema({
29922
30515
  format: { type: "string", description: "Comma-separated formats to include (e.g. static,video)", required: false }
29923
30516
  }
29924
30517
  });
29925
- var feedCommand = defineCommand168({
30518
+ var feedCommand = defineCommand172({
29926
30519
  meta: {
29927
30520
  name: "feed",
29928
30521
  description: "Winners across every brand you follow (browse, then trim per advertiser). Example: baker winning-ads feed --per-advertiser 5 --output md"
@@ -30007,7 +30600,7 @@ var feedCommand = defineCommand168({
30007
30600
  });
30008
30601
 
30009
30602
  // src/commands/winning-ads/follow.ts
30010
- import { defineCommand as defineCommand169 } from "citty";
30603
+ import { defineCommand as defineCommand173 } from "citty";
30011
30604
  var PLATFORMS = ["meta", "linkedin"];
30012
30605
  registerSchema({
30013
30606
  command: "winning-ads.follow",
@@ -30022,7 +30615,7 @@ registerSchema({
30022
30615
  label: { type: "string", description: "Optional display label (defaults to the resolved name)", required: false }
30023
30616
  }
30024
30617
  });
30025
- var followCommand = defineCommand169({
30618
+ var followCommand = defineCommand173({
30026
30619
  meta: {
30027
30620
  name: "follow",
30028
30621
  description: 'Follow a brand to track ALL its ads \u2014 every platform and country. --platform is how we read your input, not a limit. A domain tracks both Meta + LinkedIn. Example: baker winning-ads follow "deel.com" --platform meta'
@@ -30069,7 +30662,7 @@ var followCommand = defineCommand169({
30069
30662
  });
30070
30663
 
30071
30664
  // src/commands/winning-ads/follow-competitors.ts
30072
- import { defineCommand as defineCommand170 } from "citty";
30665
+ import { defineCommand as defineCommand174 } from "citty";
30073
30666
  var PLATFORMS2 = ["meta", "linkedin"];
30074
30667
  var BATCH_TIMEOUT_MS = 3e5;
30075
30668
  function buildFollowBatchBody(input) {
@@ -30102,7 +30695,7 @@ registerSchema({
30102
30695
  }
30103
30696
  }
30104
30697
  });
30105
- var followCompetitorsCommand = defineCommand170({
30698
+ var followCompetitorsCommand = defineCommand174({
30106
30699
  meta: {
30107
30700
  name: "follow-competitors",
30108
30701
  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"'
@@ -30177,7 +30770,7 @@ var followCompetitorsCommand = defineCommand170({
30177
30770
  });
30178
30771
 
30179
30772
  // src/commands/winning-ads/following.ts
30180
- import { defineCommand as defineCommand171 } from "citty";
30773
+ import { defineCommand as defineCommand175 } from "citty";
30181
30774
  registerSchema({
30182
30775
  command: "winning-ads.following",
30183
30776
  description: "List the brands you follow in your ad-dna library, with each one's status (ready vs still adding) and cached ad counts.",
@@ -30210,7 +30803,7 @@ function followingNormalizer(record, full) {
30210
30803
  platforms: Array.isArray(record.platforms) ? record.platforms : []
30211
30804
  };
30212
30805
  }
30213
- var followingCommand = defineCommand171({
30806
+ var followingCommand = defineCommand175({
30214
30807
  meta: {
30215
30808
  name: "following",
30216
30809
  description: "List brands you follow, with status (ready / adding\u2026) and cached counts. Example: baker winning-ads following --output md"
@@ -30245,7 +30838,7 @@ var followingCommand = defineCommand171({
30245
30838
  });
30246
30839
 
30247
30840
  // src/commands/winning-ads/patterns.ts
30248
- import { defineCommand as defineCommand172 } from "citty";
30841
+ import { defineCommand as defineCommand176 } from "citty";
30249
30842
  registerSchema({
30250
30843
  command: "winning-ads.patterns",
30251
30844
  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.",
@@ -30284,7 +30877,7 @@ function discriminatorRow(record) {
30284
30877
  top_values_duds: Array.isArray(record.top_values_b) ? record.top_values_b.join(", ") : ""
30285
30878
  };
30286
30879
  }
30287
- var patternsCommand = defineCommand172({
30880
+ var patternsCommand = defineCommand176({
30288
30881
  meta: {
30289
30882
  name: "patterns",
30290
30883
  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"
@@ -30340,7 +30933,7 @@ var patternsCommand = defineCommand172({
30340
30933
  });
30341
30934
 
30342
30935
  // src/commands/winning-ads/search.ts
30343
- import { defineCommand as defineCommand173 } from "citty";
30936
+ import { defineCommand as defineCommand177 } from "citty";
30344
30937
  registerSchema({
30345
30938
  command: "winning-ads.search",
30346
30939
  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.",
@@ -30448,7 +31041,7 @@ function buildSearchBody(args) {
30448
31041
  }
30449
31042
  return body;
30450
31043
  }
30451
- var searchCommand4 = defineCommand173({
31044
+ var searchCommand4 = defineCommand177({
30452
31045
  meta: {
30453
31046
  name: "search",
30454
31047
  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"
@@ -30563,7 +31156,7 @@ var searchCommand4 = defineCommand173({
30563
31156
  });
30564
31157
 
30565
31158
  // src/commands/winning-ads/seeds.ts
30566
- import { defineCommand as defineCommand174 } from "citty";
31159
+ import { defineCommand as defineCommand178 } from "citty";
30567
31160
  function leanRow(r) {
30568
31161
  return {
30569
31162
  key: r.key,
@@ -30591,7 +31184,7 @@ function makeSeedCommand(opts) {
30591
31184
  limit: { type: "number", description: "Max keys 1-100 (default 20)", required: false, default: 20 }
30592
31185
  }
30593
31186
  });
30594
- return defineCommand174({
31187
+ return defineCommand178({
30595
31188
  meta: { name: opts.name, description: opts.description },
30596
31189
  args: {
30597
31190
  platform: { type: "string", description: "Single platform to segment on", required: false },
@@ -30640,7 +31233,7 @@ var formatsCommand = makeSeedCommand({
30640
31233
  });
30641
31234
 
30642
31235
  // src/commands/winning-ads/unfollow.ts
30643
- import { defineCommand as defineCommand175 } from "citty";
31236
+ import { defineCommand as defineCommand179 } from "citty";
30644
31237
  registerSchema({
30645
31238
  command: "winning-ads.unfollow",
30646
31239
  description: "Stop following a brand \u2014 removes it from your ad-dna library by advertiser id.",
@@ -30648,7 +31241,7 @@ registerSchema({
30648
31241
  advertiser: { type: "string", description: "Advertiser id to unfollow", required: true }
30649
31242
  }
30650
31243
  });
30651
- var unfollowCommand = defineCommand175({
31244
+ var unfollowCommand = defineCommand179({
30652
31245
  meta: {
30653
31246
  name: "unfollow",
30654
31247
  description: "Stop following a brand by advertiser id. Example: baker winning-ads unfollow adv_123"
@@ -30669,7 +31262,7 @@ var unfollowCommand = defineCommand175({
30669
31262
  });
30670
31263
 
30671
31264
  // src/commands/winning-ads/winners.ts
30672
- import { defineCommand as defineCommand176 } from "citty";
31265
+ import { defineCommand as defineCommand180 } from "citty";
30673
31266
  registerSchema({
30674
31267
  command: "winning-ads.winners",
30675
31268
  description: "Top winning ads for one advertiser id (from `advertisers` or `following`). Returns lean winner cards; add --full for DNA + longevity.",
@@ -30679,7 +31272,7 @@ registerSchema({
30679
31272
  platform: { type: "string", description: "Filter to a single platform: meta|linkedin", required: false }
30680
31273
  }
30681
31274
  });
30682
- var winnersCommand = defineCommand176({
31275
+ var winnersCommand = defineCommand180({
30683
31276
  meta: {
30684
31277
  name: "winners",
30685
31278
  description: "Top winning ads for a specific advertiser id. Example: baker winning-ads winners adv_123 --top 15 --output md"
@@ -30729,7 +31322,7 @@ var winnersCommand = defineCommand176({
30729
31322
  });
30730
31323
 
30731
31324
  // src/commands/winning-ads/index.ts
30732
- var winningAdsCommand = defineCommand177({
31325
+ var winningAdsCommand = defineCommand181({
30733
31326
  meta: {
30734
31327
  name: "winning-ads",
30735
31328
  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.
@@ -30785,7 +31378,7 @@ Full guide: __tooling__/docs/tools/baker/winning-ads.md`
30785
31378
  });
30786
31379
 
30787
31380
  // src/version.ts
30788
- import { readFileSync as readFileSync12 } from "fs";
31381
+ import { readFileSync as readFileSync13 } from "fs";
30789
31382
  function packageJsonUrl() {
30790
31383
  return new URL("../package.json", import.meta.url);
30791
31384
  }
@@ -30797,15 +31390,15 @@ function parsePackageVersion(raw) {
30797
31390
  throw new Error("Invalid CLI package.json: missing version");
30798
31391
  }
30799
31392
  function getCliVersion() {
30800
- return parsePackageVersion(readFileSync12(packageJsonUrl(), "utf8"));
31393
+ return parsePackageVersion(readFileSync13(packageJsonUrl(), "utf8"));
30801
31394
  }
30802
31395
 
30803
31396
  // src/cli.ts
30804
- var main = defineCommand178({
31397
+ var main = defineCommand182({
30805
31398
  meta: {
30806
31399
  name: "baker",
30807
31400
  version: getCliVersion(),
30808
- description: `AI-agent CLI for finding and managing images, videos, testimonials, action items, scheduled actions, marketing tags, account history, and ad platform data in Baker.
31401
+ description: `AI-agent CLI for finding and managing images, videos, testimonials, action items, scheduled actions, marketing tags, Tag Manager containers, account history, and ad platform data in Baker.
30809
31402
 
30810
31403
  Auth: Set BAKER_API_KEY (starts with bk_) and BAKER_API_URL environment variables.
30811
31404
  Chat: Set BAKER_CHAT_ID for action and scheduled-action commands that stage changes against a chat.
@@ -30828,6 +31421,7 @@ Introspection: Run 'baker schema <command>' to inspect argument schemas.`
30828
31421
  testimonials: testimonialsCommand,
30829
31422
  canvas: canvasCommand,
30830
31423
  tags: tagsCommand3,
31424
+ "tag-manager": tagManagerCommand,
30831
31425
  chats: chatsCommand,
30832
31426
  history: historyCommand,
30833
31427
  "winning-ads": winningAdsCommand,