@koda-sl/baker-cli 0.154.0 → 0.155.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -75,7 +75,7 @@ import {
75
75
  } from "./chunk-RK67WL4O.js";
76
76
 
77
77
  // src/cli.ts
78
- import { defineCommand as defineCommand181, runMain } from "citty";
78
+ import { defineCommand as defineCommand185, runMain } from "citty";
79
79
 
80
80
  // src/commands/actions/index.ts
81
81
  import { defineCommand as defineCommand18 } from "citty";
@@ -492,6 +492,141 @@ function looksScheduled(name, description) {
492
492
  ${description}`;
493
493
  return SCHEDULE_SIGNALS.some((re) => re.test(haystack));
494
494
  }
495
+ var SURFACE_RULES = [
496
+ {
497
+ surface: "analysis",
498
+ nameOnly: true,
499
+ signals: [
500
+ /^\s*(?:re-?)?(?:investigate|analyse|analyze|audit|review|document|assess|evaluate|research|map|inventory|examine|diagnose)\b/i
501
+ ],
502
+ hint: "This names the analysis itself \u2014 investigating, auditing and documenting are work you do now, not work you file. Run it in this chat and deliver the finding; a Task is for a fix something genuinely blocks (bar: `__tooling__/docs/tools/baker/actions.md`)."
503
+ },
504
+ {
505
+ surface: "ask-user",
506
+ signals: [
507
+ /\bproduction\s+(?:domain|url|site|hostname)\b/i,
508
+ /\b(?:confirm|check|verify)\s+(?:with|w\/)\s+(?:the\s+)?(?:client|user|customer|team)\b/i,
509
+ /\bmissing\s+(?:the\s+)?(?:value|values|id|ids|url|budget|domain|credentials?|account\s+id|measurement\s+id)\b/i,
510
+ /\b(?:ask|get)\s+(?:the\s+)?(?:client|user)\s+for\b/i
511
+ ],
512
+ hint: "This is a missing input, not blocked work. Ask for it with AskUserQuestion (one question), then do the work in the same turn \u2014 filing the Task AND asking the question is the same request twice."
513
+ },
514
+ {
515
+ surface: "tag-manager",
516
+ signals: [
517
+ /\bgtm\b/i,
518
+ /\btag\s*manager\b/i,
519
+ /\bdata\s*layer\b/i,
520
+ /\bfiring\s+trigger\b/i,
521
+ /\bbuilt-?in\s+variables?\b/i,
522
+ /\bconsent\s+(?:mode|settings?|state)\b/i,
523
+ /\bga4\s+(?:config|configuration|event)\b/i,
524
+ /\bcontainer\s+(?:tag|trigger|variable)s?\b/i,
525
+ /\b(?:conversion|form|submit|submission|click|purchase|lead|custom)\s+event\b/i,
526
+ /\btrack\w*\b[\s\S]*\bevent\b/i
527
+ ],
528
+ except: [/\bserver-?\s?side\b/i, /\bsgtm\b/i, /\bhosting\b/i],
529
+ hint: "This is a change inside the GTM container \u2014 `baker tag-manager` stages it now and it applies at publish (guide: `__tooling__/docs/tools/baker/tag-manager.md`). Stage it instead of filing it, and if several container fixes really are blocked, they belong in ONE Task, not one per finding."
530
+ },
531
+ {
532
+ surface: "site-tags",
533
+ signals: [
534
+ /\bpixel\b/i,
535
+ /\binsight\s+tag\b/i,
536
+ /\bclarity\b/i,
537
+ /\bhotjar\b/i,
538
+ /\bgtm\s+snippet\b/i,
539
+ /\b(?:install|add|remove|swap)\b[\s\S]*\b(?:snippet|script|tag)\b/i
540
+ ],
541
+ 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."
542
+ },
543
+ {
544
+ surface: "ads-write",
545
+ signals: [
546
+ /\b(?:pause|unpause|enable|disable|launch|restructure|duplicate|clone|rebuild)\b[\s\S]*\b(?:campaign|ad\s*set|ad\s*group|adgroup)\b/i,
547
+ /\b(?:tcpa|troas|target\s+cpa|target\s+roas)\b/i,
548
+ /\b(?:daily|campaign|lifetime|ad\s*set)\s+budget\b/i,
549
+ /\b(?:raise|lower|increase|decrease|adjust|change|set|update)\s+(?:the\s+)?budget\b/i,
550
+ /\bbid\s+strateg/i,
551
+ /\bnegative\s+keywords?\b/i,
552
+ /\b(?:lookalike|custom)\s+audience\b/i,
553
+ /\bswap\s+(?:the\s+)?creative\b/i
554
+ ],
555
+ hint: "This is an ad-platform change the write surface covers \u2014 stage it now with `baker ads google|meta|linkedin` and it applies at publish (guides: `__tooling__/docs/tools/baker/ads-google.md`, `__tooling__/docs/tools/baker/ads-meta.md`, `__tooling__/docs/tools/baker/ads-linkedin.md`). Staging is not going live."
556
+ },
557
+ {
558
+ surface: "landing",
559
+ signals: [
560
+ /\b(?:landing\s+page|hero|headline|above\s+the\s+fold)\b/i,
561
+ /\b(?:rewrite|tighten|restyle)\b[\s\S]*\b(?:copy|page|section)\b/i
562
+ ],
563
+ hint: "This is a page change \u2014 build it in this chat with the `/landing` skill. Only file it if something outside the page blocks the work."
564
+ },
565
+ {
566
+ surface: "flow",
567
+ signals: [/\b(?:form|flow)\s+(?:step|steps|branching|logic|redirect|behaviou?r)\b/i, /\bthank\s*you\s+redirect\b/i],
568
+ hint: "This is form behaviour \u2014 change it in this chat with the `/flow-builder` skill. Only file it if something outside the form blocks the work."
569
+ }
570
+ ];
571
+ var FAN_OUT_THRESHOLD = 3;
572
+ var SCHEDULED_HINT = 'This reads as recurring/scheduled work. If it should run on a cadence or a future date, create a Scheduled Action instead \u2014 baker scheduled-actions create --cron "0 9 * * MON" (or --run-at; guide: `__tooling__/docs/tools/baker/scheduled-actions.md`). Do NOT capture "set up a scheduled action" as a Work Action.';
573
+ function looksExecutable(name, description) {
574
+ const full = `${name}
575
+ ${description}`;
576
+ for (const rule of SURFACE_RULES) {
577
+ const haystack = rule.nameOnly ? name : full;
578
+ if (rule.except?.some((re) => re.test(full))) {
579
+ continue;
580
+ }
581
+ if (rule.signals.some((re) => re.test(haystack))) {
582
+ return { surface: rule.surface, hint: rule.hint };
583
+ }
584
+ }
585
+ return null;
586
+ }
587
+ function buildCreateHints({
588
+ name,
589
+ description,
590
+ tempId,
591
+ tags,
592
+ prioritySet,
593
+ draftCreateCount
594
+ }) {
595
+ const hints = [];
596
+ const advisory = advisoryHint(name, description);
597
+ if (advisory) {
598
+ hints.push(
599
+ `${advisory.hint} If nothing actually blocks it: baker actions draft remove ${tempId}, then do the work.`
600
+ );
601
+ }
602
+ if (draftCreateCount >= FAN_OUT_THRESHOLD) {
603
+ hints.push(
604
+ `${draftCreateCount} Tasks are staged in this chat. Re-read them with \`baker actions draft\`: can any be done now with a surface you already have, and do any two touch the same system (same container, same campaign, same page)? Those belong in ONE Task \u2014 fold them together with \`baker actions update\` and drop the extras with \`baker actions draft remove <tempId>\`.`
605
+ );
606
+ }
607
+ hints.push(`Link dependencies: baker actions link --blocker <id> --blocked ${tempId}`);
608
+ if (!description) {
609
+ hints.push("Add description: baker actions update <tempId> --description '...' (what/why/where/done-when)");
610
+ }
611
+ if (!tags || tags.length === 0) {
612
+ hints.push(
613
+ "MISSING --tags. This action is invisible to the backlog's tag filter. Re-run with --tags <slug,...> (`baker actions tags list` for the taxonomy, `baker actions tags create --slug <slug>` to mint) \u2014 or `baker actions update <tempId> --tags <slug,...>`."
614
+ );
615
+ }
616
+ if (!prioritySet) {
617
+ hints.push(
618
+ `MISSING --priority. Without it this action ranks as 'normal' (medium) in the do-first ordering, so urgent/high client work won't surface first. Re-run with --priority ${ACTION_PRIORITIES.join("|")} \u2014 or \`baker actions update <tempId> --priority <level>\`.`
619
+ );
620
+ }
621
+ return hints;
622
+ }
623
+ function advisoryHint(name, description) {
624
+ if (looksScheduled(name, description)) {
625
+ return { kind: "scheduled", hint: SCHEDULED_HINT };
626
+ }
627
+ const executable = looksExecutable(name, description);
628
+ return executable ? { kind: "executable", ...executable } : null;
629
+ }
495
630
 
496
631
  // src/commands/actions/skillCatalog.ts
497
632
  import { existsSync, readdirSync, readFileSync } from "fs";
@@ -811,7 +946,9 @@ var actionsCreateRequestSchema = z.object({
811
946
  tags: z.array(z.string()).optional(),
812
947
  priority: actionPrioritySchema.optional()
813
948
  });
814
- var actionsCreateResponseSchema = okResponse(z.object({ tempId: z.string() }));
949
+ var actionsCreateResponseSchema = okResponse(
950
+ z.object({ tempId: z.string(), draftCreateCount: z.number().optional() })
951
+ );
815
952
  var actionsGetRequestSchema = z.object({ actionId: z.string().min(1) });
816
953
  var actionsGetResponseSchema = okResponse(actionDetailSchema.nullable());
817
954
  var actionsListRequestSchema = z.object({
@@ -2184,6 +2321,7 @@ var chatChangeTypeSchema = z5.enum([
2184
2321
  "linkedin-ads",
2185
2322
  "google-ads",
2186
2323
  "meta-ads",
2324
+ "tag-manager",
2187
2325
  // Immediate (already-applied) library effects — see lib/chatChanges.ts.
2188
2326
  "image",
2189
2327
  "video",
@@ -2521,7 +2659,8 @@ var historyCategorySchema = z7.enum([
2521
2659
  "creative",
2522
2660
  "report",
2523
2661
  "domain",
2524
- "integration"
2662
+ "integration",
2663
+ "tag_manager"
2525
2664
  ]);
2526
2665
  var historyListRequestSchema = z7.object({
2527
2666
  limit: z7.number().int().min(1).max(200).optional(),
@@ -3376,27 +3515,17 @@ var createCommand = defineCommand3({
3376
3515
  ...tags ? { tags } : {},
3377
3516
  ...priority !== void 0 && priority !== null ? { priority } : {}
3378
3517
  });
3379
- const hints = [];
3380
- if (looksScheduled(name, args.description ?? "")) {
3381
- hints.push(
3382
- 'This reads as recurring/scheduled work. If it should run on a cadence or a future date, create a Scheduled Action instead \u2014 baker scheduled-actions create --cron "0 9 * * MON" (or --run-at; guide: __tooling__/docs/tools/baker/scheduled-actions.md). Do NOT capture "set up a scheduled action" as a Work Action.'
3383
- );
3384
- }
3385
- hints.push(`Link dependencies: baker actions link --blocker <id> --blocked ${tempId}`);
3386
- if (!args.description) {
3387
- hints.push("Add description: baker actions update <tempId> --description '...' (what/why/where/done-when)");
3388
- }
3389
- if (!tags || tags.length === 0) {
3390
- hints.push(
3391
- "MISSING --tags. This action is invisible to the backlog's tag filter. Re-run with --tags <slug,...> (`baker actions tags list` for the taxonomy, `baker actions tags create --slug <slug>` to mint) \u2014 or `baker actions update <tempId> --tags <slug,...>`."
3392
- );
3393
- }
3394
- if (priority === void 0) {
3395
- hints.push(
3396
- `MISSING --priority. Without it this action ranks as 'normal' (medium) in the do-first ordering, so urgent/high client work won't surface first. Re-run with --priority ${ACTION_PRIORITIES.join("|")} \u2014 or \`baker actions update <tempId> --priority <level>\`.`
3397
- );
3398
- }
3399
- writeJson({ ...response, hints });
3518
+ writeJson({
3519
+ ...response,
3520
+ hints: buildCreateHints({
3521
+ name,
3522
+ description: args.description ?? "",
3523
+ tempId,
3524
+ tags,
3525
+ prioritySet: priority !== void 0,
3526
+ draftCreateCount: response.data.draftCreateCount ?? 0
3527
+ })
3528
+ });
3400
3529
  } catch (err) {
3401
3530
  failApi(err);
3402
3531
  }
@@ -30234,9 +30363,488 @@ var schemaCommand = defineCommand157({
30234
30363
  }
30235
30364
  });
30236
30365
 
30237
- // src/commands/tags/index.ts
30366
+ // src/commands/tag-manager/index.ts
30367
+ import { defineCommand as defineCommand161 } from "citty";
30368
+
30369
+ // src/commands/tag-manager/draft.ts
30238
30370
  import { defineCommand as defineCommand158 } from "citty";
30239
30371
 
30372
+ // src/commands/tag-manager/shared.ts
30373
+ import { readFileSync as readFileSync12 } from "fs";
30374
+ function failValidation3(message) {
30375
+ writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
30376
+ process.exit(1);
30377
+ }
30378
+ function requireTarget4(args, entity) {
30379
+ const positional = Array.isArray(args._) ? args._[0] : void 0;
30380
+ const target = args.id ?? args.target ?? positional;
30381
+ if (typeof target !== "string" || target.length === 0) {
30382
+ failValidation3(`pass the ${entity} id or path as the positional argument`);
30383
+ }
30384
+ return target;
30385
+ }
30386
+ function loadJsonArg(args, flag = "json") {
30387
+ const inline = args[flag];
30388
+ const file = args.file;
30389
+ const raw = typeof file === "string" && file.length > 0 ? readFileSync12(file, "utf8") : typeof inline === "string" && inline.length > 0 ? inline : void 0;
30390
+ if (raw === void 0) {
30391
+ failValidation3(`pass --${flag} with inline JSON or --file with a path to a JSON file`);
30392
+ }
30393
+ try {
30394
+ const parsed = JSON.parse(raw);
30395
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
30396
+ failValidation3(`--${flag} must be a JSON object`);
30397
+ }
30398
+ return parsed;
30399
+ } catch {
30400
+ return failValidation3(`--${flag} is not valid JSON`);
30401
+ }
30402
+ }
30403
+ var RETRYABLE_CODES = /* @__PURE__ */ new Set(["RATE_LIMITED", "INTERNAL_ERROR", "NETWORK_ERROR", "TIMEOUT"]);
30404
+ function handleError3(err) {
30405
+ if (err instanceof ApiError) {
30406
+ writeJsonEnvelope({
30407
+ ok: false,
30408
+ error: {
30409
+ code: err.code,
30410
+ message: err.message,
30411
+ ...err.code === "UNAUTHORIZED" ? {
30412
+ fix: {
30413
+ action: "ask_user",
30414
+ explanation: 'Tag Manager is not connected for this company yet, or no container has been picked. Do NOT send the user to Settings \u2014 call the `request_connection` tool with { platform: "google-tag-manager", reason } and they connect and pick the container from inside the chat. Carry on with the rest of the task meanwhile.'
30415
+ }
30416
+ } : {},
30417
+ retryable: RETRYABLE_CODES.has(err.code)
30418
+ }
30419
+ });
30420
+ process.exit(1);
30421
+ }
30422
+ writeJsonEnvelope({ ok: false, error: { code: "NETWORK_ERROR", message: "Unexpected error", retryable: true } });
30423
+ process.exit(1);
30424
+ }
30425
+ var STAGE_HINTS = [
30426
+ "Staged only \u2014 nothing has changed in Tag Manager yet. These apply when the chat completes, in dependency order, and close as a Tag Manager version. Whether that version goes live is a per-company setting, so never promise the user their tracking is live; say the changes are ready and will apply when you finish."
30427
+ ];
30428
+ async function stageOp3(op) {
30429
+ const chatId = requireChatId();
30430
+ try {
30431
+ const data = await apiPost("/api/tag-manager/draft/stage", { chatId, op });
30432
+ writeJsonEnvelope({ ok: true, data, hints: STAGE_HINTS });
30433
+ } catch (err) {
30434
+ handleError3(err);
30435
+ }
30436
+ }
30437
+ async function draftAction2(path28, body) {
30438
+ const chatId = requireChatId();
30439
+ try {
30440
+ const data = await apiPost(path28, { chatId, ...body });
30441
+ writeJsonEnvelope({ ok: true, data });
30442
+ return data;
30443
+ } catch (err) {
30444
+ handleError3(err);
30445
+ }
30446
+ }
30447
+ function renderDraft(response) {
30448
+ if (response.count === 0) {
30449
+ return "No Tag Manager changes staged on this chat.";
30450
+ }
30451
+ const lines = [
30452
+ `${response.count} staged Tag Manager change(s)`,
30453
+ ...response.ops.map((op) => {
30454
+ const status = op.result ? ` [${op.result.status}]` : "";
30455
+ return ` ${op.ref} ${op.summary}${status}`;
30456
+ })
30457
+ ];
30458
+ if (response.versionId) {
30459
+ lines.push(
30460
+ "",
30461
+ response.published ? `Applied and published as Tag Manager version ${response.versionId}. These changes are live on the site.` : `Applied into Tag Manager version ${response.versionId}. It is NOT published \u2014 publish it in Tag Manager to go live.`
30462
+ );
30463
+ }
30464
+ if (response.compilerError) {
30465
+ lines.push("", `Tag Manager reported a problem building the version: ${response.compilerError}`);
30466
+ }
30467
+ return lines.join("\n");
30468
+ }
30469
+ async function draftList(json) {
30470
+ const chatId = requireChatId();
30471
+ try {
30472
+ const data = await apiPost("/api/tag-manager/draft", { chatId });
30473
+ if (json) {
30474
+ writeJsonEnvelope({ ok: true, data });
30475
+ return;
30476
+ }
30477
+ process.stdout.write(`${renderDraft(data)}
30478
+ `);
30479
+ } catch (err) {
30480
+ handleError3(err);
30481
+ }
30482
+ }
30483
+
30484
+ // src/commands/tag-manager/draft.ts
30485
+ registerSchema({
30486
+ command: "tagManager.draft",
30487
+ 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.",
30488
+ args: {
30489
+ json: { type: "boolean", description: "Print the raw JSON envelope instead of the readable list", required: false }
30490
+ }
30491
+ });
30492
+ var draftCommand3 = defineCommand158({
30493
+ meta: {
30494
+ name: "draft",
30495
+ description: "List, show, amend, remove, or clear staged Tag Manager changes for this chat"
30496
+ },
30497
+ subCommands: {
30498
+ list: defineCommand158({
30499
+ meta: {
30500
+ name: "list",
30501
+ description: "Review everything staged on this chat (--json for the raw envelope)"
30502
+ },
30503
+ args: { json: { type: "boolean", description: "Print the raw JSON envelope", required: false } },
30504
+ run: async ({ args }) => {
30505
+ await draftList(args.json === true);
30506
+ }
30507
+ }),
30508
+ show: defineCommand158({
30509
+ meta: {
30510
+ name: "show",
30511
+ description: "Print the full staged payload for one change \u2014 the receipt to verify it looks right before publish (never truncated)."
30512
+ },
30513
+ args: { ref: { type: "positional", description: "Staged ref (gtm_temp_*) or target", required: false } },
30514
+ run: async ({ args }) => {
30515
+ await draftAction2("/api/tag-manager/draft/show", {
30516
+ ref: requireTarget4(args, "change")
30517
+ });
30518
+ }
30519
+ }),
30520
+ amend: defineCommand158({
30521
+ meta: {
30522
+ name: "amend",
30523
+ 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."
30524
+ },
30525
+ args: {
30526
+ ref: { type: "positional", description: "Staged ref (gtm_temp_*) or target", required: false },
30527
+ patch: { type: "string", description: "Inline JSON patch object", required: false },
30528
+ file: { type: "string", description: "JSON file with the patch object", required: false }
30529
+ },
30530
+ run: async ({ args }) => {
30531
+ await draftAction2("/api/tag-manager/draft/amend", {
30532
+ ref: requireTarget4(args, "change"),
30533
+ patch: loadJsonArg(args, "patch")
30534
+ });
30535
+ }
30536
+ }),
30537
+ remove: defineCommand158({
30538
+ meta: { name: "remove", description: "Remove one staged change (cascades to anything depending on it)" },
30539
+ args: { ref: { type: "positional", description: "Staged ref (gtm_temp_*) or target", required: false } },
30540
+ run: async ({ args }) => {
30541
+ await draftAction2("/api/tag-manager/draft/remove", {
30542
+ ref: requireTarget4(args, "change")
30543
+ });
30544
+ }
30545
+ }),
30546
+ clear: defineCommand158({
30547
+ meta: { name: "clear", description: "Discard all Tag Manager changes staged on this chat" },
30548
+ run: async () => {
30549
+ await draftAction2("/api/tag-manager/draft/clear", {});
30550
+ }
30551
+ })
30552
+ }
30553
+ });
30554
+
30555
+ // src/commands/tag-manager/read.ts
30556
+ import { defineCommand as defineCommand159 } from "citty";
30557
+ registerSchema({
30558
+ command: "tagManager.containers",
30559
+ 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.",
30560
+ args: {}
30561
+ });
30562
+ registerSchema({
30563
+ command: "tagManager.read",
30564
+ 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.",
30565
+ args: {
30566
+ container: { type: "string", description: "Numeric container id (defaults to the connected one)", required: false },
30567
+ workspace: { type: "string", description: "Workspace id (defaults to the default workspace)", required: false },
30568
+ entities: {
30569
+ type: "string",
30570
+ description: "Comma-separated subset: tag,trigger,variable,folder,builtInVariable",
30571
+ required: false
30572
+ },
30573
+ full: { type: "boolean", description: "Include the raw Tag Manager object for each entity", required: false }
30574
+ }
30575
+ });
30576
+ var containersCommand = defineCommand159({
30577
+ meta: {
30578
+ name: "containers",
30579
+ description: `List Tag Manager containers reachable by this company's connection.
30580
+
30581
+ Start here:
30582
+ baker tag-manager containers`
30583
+ },
30584
+ run: async () => {
30585
+ try {
30586
+ const data = await apiGet("/api/tag-manager/containers");
30587
+ writeJsonEnvelope({ ok: true, data });
30588
+ } catch (err) {
30589
+ handleError3(err);
30590
+ }
30591
+ }
30592
+ });
30593
+ var readCommand = defineCommand159({
30594
+ meta: {
30595
+ name: "read",
30596
+ description: `Read the current contents of the Tag Manager container \u2014 always do this before staging changes.
30597
+
30598
+ Examples:
30599
+ baker tag-manager read
30600
+ baker tag-manager read --entities tag,trigger
30601
+ baker tag-manager read --full`
30602
+ },
30603
+ args: {
30604
+ container: { type: "string", description: "Numeric container id", required: false },
30605
+ workspace: { type: "string", description: "Workspace id", required: false },
30606
+ entities: { type: "string", description: "Comma-separated entity subset", required: false },
30607
+ full: { type: "boolean", description: "Include raw Tag Manager objects", required: false }
30608
+ },
30609
+ run: async ({ args }) => {
30610
+ const entities = typeof args.entities === "string" && args.entities.length > 0 ? args.entities.split(",").map((entry) => entry.trim()) : void 0;
30611
+ try {
30612
+ const data = await apiPost("/api/tag-manager/read", {
30613
+ containerId: args.container,
30614
+ workspaceId: args.workspace,
30615
+ entities,
30616
+ full: args.full === true
30617
+ });
30618
+ const hints = [];
30619
+ if (data.installedContainerMismatch) {
30620
+ hints.push(data.installedContainerMismatch);
30621
+ }
30622
+ hints.push(
30623
+ "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."
30624
+ );
30625
+ writeJsonEnvelope({ ok: true, data, hints });
30626
+ } catch (err) {
30627
+ handleError3(err);
30628
+ }
30629
+ }
30630
+ });
30631
+
30632
+ // src/commands/tag-manager/write-commands.ts
30633
+ import { defineCommand as defineCommand160 } from "citty";
30634
+ var ENTITIES = [
30635
+ {
30636
+ entity: "tag",
30637
+ noun: "tag",
30638
+ 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.',
30639
+ example: '{"name":"GA4 - Generate Lead","type":"gaawe","firingTriggerId":["gtm_temp_trigger_x_0"]}'
30640
+ },
30641
+ {
30642
+ entity: "trigger",
30643
+ noun: "trigger",
30644
+ 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.',
30645
+ example: '{"name":"Form - Submit","type":"formSubmission"}'
30646
+ },
30647
+ {
30648
+ entity: "variable",
30649
+ noun: "variable",
30650
+ 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}}.',
30651
+ example: '{"name":"DL - lead_id","type":"v","parameter":[{"type":"template","key":"name","value":"lead_id"}]}'
30652
+ },
30653
+ {
30654
+ entity: "folder",
30655
+ noun: "folder",
30656
+ createHint: "Payload needs name. Use folders to keep a large container legible.",
30657
+ example: '{"name":"Google Ads"}'
30658
+ }
30659
+ ];
30660
+ for (const { entity, noun, createHint } of ENTITIES) {
30661
+ registerSchema({
30662
+ command: `tagManager.${entity}.create`,
30663
+ description: `Stage the creation of a Tag Manager ${noun}. ${createHint} Staged only \u2014 applies when the chat completes.`,
30664
+ args: {
30665
+ json: { type: "string", description: `Inline JSON ${noun} definition`, required: false },
30666
+ file: { type: "string", description: `Path to a JSON file with the ${noun} definition`, required: false },
30667
+ container: { type: "string", description: "Numeric container id", required: false }
30668
+ }
30669
+ });
30670
+ registerSchema({
30671
+ command: `tagManager.${entity}.update`,
30672
+ 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.`,
30673
+ args: {
30674
+ json: { type: "string", description: "Inline JSON with the changed fields", required: false },
30675
+ file: { type: "string", description: "Path to a JSON file with the changed fields", required: false }
30676
+ }
30677
+ });
30678
+ registerSchema({
30679
+ command: `tagManager.${entity}.delete`,
30680
+ 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.`,
30681
+ args: {}
30682
+ });
30683
+ }
30684
+ function entityCommand(entity, noun, example) {
30685
+ return defineCommand160({
30686
+ meta: { name: entity, description: `Stage ${noun} changes on this chat's Tag Manager draft` },
30687
+ subCommands: {
30688
+ create: defineCommand160({
30689
+ meta: {
30690
+ name: "create",
30691
+ description: `Stage a new ${noun}
30692
+
30693
+ Examples:
30694
+ baker tag-manager ${entity} create --json '${example}'
30695
+ baker tag-manager ${entity} create --file ./${entity}.json`
30696
+ },
30697
+ args: {
30698
+ json: { type: "string", description: "Inline JSON definition", required: false },
30699
+ file: { type: "string", description: "Path to a JSON file", required: false },
30700
+ container: { type: "string", description: "Numeric container id", required: false }
30701
+ },
30702
+ run: async ({ args }) => {
30703
+ await stageOp3({
30704
+ kind: `tagManager.${entity}.create`,
30705
+ payload: loadJsonArg(args),
30706
+ ...typeof args.container === "string" ? { containerId: args.container } : {}
30707
+ });
30708
+ }
30709
+ }),
30710
+ update: defineCommand160({
30711
+ meta: {
30712
+ name: "update",
30713
+ description: `Stage an update to an existing ${noun} (pass its id or path)`
30714
+ },
30715
+ args: {
30716
+ id: { type: "positional", description: `${noun} id or path`, required: false },
30717
+ json: { type: "string", description: "Inline JSON with changed fields", required: false },
30718
+ file: { type: "string", description: "Path to a JSON file", required: false },
30719
+ container: { type: "string", description: "Numeric container id", required: false }
30720
+ },
30721
+ run: async ({ args }) => {
30722
+ await stageOp3({
30723
+ kind: `tagManager.${entity}.update`,
30724
+ target: requireTarget4(args, noun),
30725
+ payload: loadJsonArg(args),
30726
+ ...typeof args.container === "string" ? { containerId: args.container } : {}
30727
+ });
30728
+ }
30729
+ }),
30730
+ delete: defineCommand160({
30731
+ meta: { name: "delete", description: `Stage the deletion of a ${noun} (pass its id or path)` },
30732
+ args: {
30733
+ id: { type: "positional", description: `${noun} id or path`, required: false },
30734
+ container: { type: "string", description: "Numeric container id", required: false }
30735
+ },
30736
+ run: async ({ args }) => {
30737
+ await stageOp3({
30738
+ kind: `tagManager.${entity}.delete`,
30739
+ target: requireTarget4(args, noun),
30740
+ ...typeof args.container === "string" ? { containerId: args.container } : {}
30741
+ });
30742
+ }
30743
+ })
30744
+ }
30745
+ });
30746
+ }
30747
+ var exampleFor = (entity) => ENTITIES.find((e) => e.entity === entity)?.example ?? "{}";
30748
+ var tagCommand = entityCommand("tag", "tag", exampleFor("tag"));
30749
+ var triggerCommand2 = entityCommand("trigger", "trigger", exampleFor("trigger"));
30750
+ var variableCommand = entityCommand("variable", "variable", exampleFor("variable"));
30751
+ var folderCommand = entityCommand("folder", "folder", exampleFor("folder"));
30752
+ registerSchema({
30753
+ command: "tagManager.builtin",
30754
+ 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}}.",
30755
+ args: {
30756
+ types: { type: "string", description: "Comma-separated built-in variable types", required: true }
30757
+ }
30758
+ });
30759
+ function builtinTypes(args) {
30760
+ const raw = args.types;
30761
+ if (typeof raw !== "string" || raw.length === 0) {
30762
+ process.stdout.write(
30763
+ `${JSON.stringify({ ok: false, error: { code: "VALIDATION_ERROR", message: "pass --types with comma-separated built-in variable types" } }, null, 2)}
30764
+ `
30765
+ );
30766
+ process.exit(1);
30767
+ }
30768
+ return raw.split(",").map((entry) => entry.trim());
30769
+ }
30770
+ var builtinCommand = defineCommand160({
30771
+ meta: {
30772
+ name: "builtin",
30773
+ description: `Enable or disable built-in variables
30774
+
30775
+ Examples:
30776
+ baker tag-manager builtin enable --types clickUrl,clickText
30777
+ baker tag-manager builtin disable --types formId`
30778
+ },
30779
+ subCommands: {
30780
+ enable: defineCommand160({
30781
+ meta: { name: "enable", description: "Stage enabling built-in variables" },
30782
+ args: {
30783
+ types: { type: "string", description: "Comma-separated types", required: false },
30784
+ container: { type: "string", description: "Numeric container id", required: false }
30785
+ },
30786
+ run: async ({ args }) => {
30787
+ await stageOp3({
30788
+ kind: "tagManager.builtInVariable.enable",
30789
+ payload: { type: builtinTypes(args) },
30790
+ ...typeof args.container === "string" ? { containerId: args.container } : {}
30791
+ });
30792
+ }
30793
+ }),
30794
+ disable: defineCommand160({
30795
+ meta: { name: "disable", description: "Stage disabling built-in variables" },
30796
+ args: {
30797
+ types: { type: "string", description: "Comma-separated types", required: false },
30798
+ container: { type: "string", description: "Numeric container id", required: false }
30799
+ },
30800
+ run: async ({ args }) => {
30801
+ await stageOp3({
30802
+ kind: "tagManager.builtInVariable.disable",
30803
+ payload: { type: builtinTypes(args) },
30804
+ ...typeof args.container === "string" ? { containerId: args.container } : {}
30805
+ });
30806
+ }
30807
+ })
30808
+ }
30809
+ });
30810
+
30811
+ // src/commands/tag-manager/index.ts
30812
+ var tagManagerCommand = defineCommand161({
30813
+ meta: {
30814
+ name: "tag-manager",
30815
+ description: `Read and change what lives inside the client's Google Tag Manager container \u2014 tags, triggers, variables, folders and built-in variables.
30816
+
30817
+ Start here:
30818
+ baker tag-manager read \u2014 see what the container holds today
30819
+
30820
+ Then stage changes (nothing is sent to Tag Manager until publish):
30821
+ baker tag-manager trigger create --json '{"name":"Quote Submit","type":"customEvent"}'
30822
+ baker tag-manager tag create --json '{"name":"GA4 Quote","type":"gaawe","firingTriggerId":["gtm_temp_trigger_..."]}'
30823
+ baker tag-manager draft list \u2014 review everything staged
30824
+
30825
+ On publish, staged changes are written into a new Tag Manager version that is NOT published.
30826
+ Tell the user to publish that version in Tag Manager to make it live.
30827
+
30828
+ This owns the CONTENTS of the container. Installing the container snippet on the site is a
30829
+ different job \u2014 that is \`baker tags\` with the googleTagManager tag.
30830
+
30831
+ Full guide: __tooling__/docs/tools/baker/tag-manager.md`
30832
+ },
30833
+ subCommands: {
30834
+ containers: containersCommand,
30835
+ read: readCommand,
30836
+ tag: tagCommand,
30837
+ trigger: triggerCommand2,
30838
+ variable: variableCommand,
30839
+ folder: folderCommand,
30840
+ builtin: builtinCommand,
30841
+ draft: draftCommand3
30842
+ }
30843
+ });
30844
+
30845
+ // src/commands/tags/index.ts
30846
+ import { defineCommand as defineCommand162 } from "citty";
30847
+
30240
30848
  // src/commands/tags/shared.ts
30241
30849
  function failApi3(err) {
30242
30850
  if (err instanceof ApiError) {
@@ -30301,7 +30909,7 @@ async function listTags(json) {
30301
30909
  failApi3(err);
30302
30910
  }
30303
30911
  }
30304
- var listCommand10 = defineCommand158({
30912
+ var listCommand10 = defineCommand162({
30305
30913
  meta: {
30306
30914
  name: "list",
30307
30915
  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"
@@ -30320,7 +30928,7 @@ async function listDraft3() {
30320
30928
  failApi3(err);
30321
30929
  }
30322
30930
  }
30323
- var draftCommand3 = defineCommand158({
30931
+ var draftCommand4 = defineCommand162({
30324
30932
  meta: {
30325
30933
  name: "draft",
30326
30934
  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)."
@@ -30329,7 +30937,7 @@ var draftCommand3 = defineCommand158({
30329
30937
  await listDraft3();
30330
30938
  }
30331
30939
  });
30332
- var tagsCommand3 = defineCommand158({
30940
+ var tagsCommand3 = defineCommand162({
30333
30941
  meta: {
30334
30942
  name: "tags",
30335
30943
  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.
@@ -30347,7 +30955,7 @@ Full guide: __tooling__/docs/tools/baker/tags.md`
30347
30955
  },
30348
30956
  subCommands: {
30349
30957
  list: listCommand10,
30350
- draft: draftCommand3
30958
+ draft: draftCommand4
30351
30959
  },
30352
30960
  run: async () => {
30353
30961
  await listTags(false);
@@ -30355,10 +30963,10 @@ Full guide: __tooling__/docs/tools/baker/tags.md`
30355
30963
  });
30356
30964
 
30357
30965
  // src/commands/testimonials/index.ts
30358
- import { defineCommand as defineCommand162 } from "citty";
30966
+ import { defineCommand as defineCommand166 } from "citty";
30359
30967
 
30360
30968
  // src/commands/testimonials/get.ts
30361
- import { defineCommand as defineCommand159 } from "citty";
30969
+ import { defineCommand as defineCommand163 } from "citty";
30362
30970
  registerSchema({
30363
30971
  command: "testimonials.get",
30364
30972
  description: "Get a single testimonial by ID",
@@ -30366,7 +30974,7 @@ registerSchema({
30366
30974
  id: { type: "string", description: "Testimonial ID", required: true }
30367
30975
  }
30368
30976
  });
30369
- var getCommand4 = defineCommand159({
30977
+ var getCommand4 = defineCommand163({
30370
30978
  meta: { name: "get", description: "Get a single testimonial by ID. Example: baker testimonials get j571abc123" },
30371
30979
  args: {
30372
30980
  id: { type: "positional", description: "Testimonial ID", required: false },
@@ -30403,7 +31011,7 @@ var getCommand4 = defineCommand159({
30403
31011
  });
30404
31012
 
30405
31013
  // src/commands/testimonials/list.ts
30406
- import { defineCommand as defineCommand160 } from "citty";
31014
+ import { defineCommand as defineCommand164 } from "citty";
30407
31015
  registerSchema({
30408
31016
  command: "testimonials.list",
30409
31017
  description: "List testimonials with optional filters.",
@@ -30433,7 +31041,7 @@ registerSchema({
30433
31041
  limit: { type: "number", description: "Max results (default 50)", required: false, default: 50 }
30434
31042
  }
30435
31043
  });
30436
- var listCommand11 = defineCommand160({
31044
+ var listCommand11 = defineCommand164({
30437
31045
  meta: {
30438
31046
  name: "list",
30439
31047
  description: "List testimonials with optional filters. Example: baker testimonials list --source google --sentiment positive"
@@ -30482,7 +31090,7 @@ var listCommand11 = defineCommand160({
30482
31090
  });
30483
31091
 
30484
31092
  // src/commands/testimonials/search.ts
30485
- import { defineCommand as defineCommand161 } from "citty";
31093
+ import { defineCommand as defineCommand165 } from "citty";
30486
31094
  function languageBiasHint(results, requestedLanguage) {
30487
31095
  if (requestedLanguage) {
30488
31096
  return null;
@@ -30560,7 +31168,7 @@ function buildSearchRequest(query, args) {
30560
31168
  }
30561
31169
  return body;
30562
31170
  }
30563
- var searchCommand2 = defineCommand161({
31171
+ var searchCommand2 = defineCommand165({
30564
31172
  meta: {
30565
31173
  name: "search",
30566
31174
  description: "Semantic search testimonials by text query. Uses hybrid BM25 + vector + reranking. Example: baker testimonials search 'great service' --rating-min 4"
@@ -30616,7 +31224,7 @@ var searchCommand2 = defineCommand161({
30616
31224
  var tagsCommand4 = makeTagsCommand("testimonials", "testimonial", "/api/testimonials/tags");
30617
31225
 
30618
31226
  // src/commands/testimonials/index.ts
30619
- var testimonialsCommand = defineCommand162({
31227
+ var testimonialsCommand = defineCommand166({
30620
31228
  meta: {
30621
31229
  name: "testimonials",
30622
31230
  description: `Find and browse testimonials in Baker. Subcommands: search, get, list, tags.
@@ -30638,10 +31246,10 @@ Full guide: __tooling__/docs/tools/baker/testimonials.md`
30638
31246
  });
30639
31247
 
30640
31248
  // src/commands/videos/index.ts
30641
- import { defineCommand as defineCommand167 } from "citty";
31249
+ import { defineCommand as defineCommand171 } from "citty";
30642
31250
 
30643
31251
  // src/commands/videos/delete.ts
30644
- import { defineCommand as defineCommand163 } from "citty";
31252
+ import { defineCommand as defineCommand167 } from "citty";
30645
31253
  registerSchema({
30646
31254
  command: "videos.delete",
30647
31255
  description: "Delete a video by ID",
@@ -30655,7 +31263,7 @@ registerSchema({
30655
31263
  }
30656
31264
  }
30657
31265
  });
30658
- var deleteCommand3 = defineCommand163({
31266
+ var deleteCommand3 = defineCommand167({
30659
31267
  meta: {
30660
31268
  name: "delete",
30661
31269
  description: "Delete a video by ID. Use --dry-run to preview. Example: baker videos delete j571abc123 --dry-run"
@@ -30696,7 +31304,7 @@ var deleteCommand3 = defineCommand163({
30696
31304
  });
30697
31305
 
30698
31306
  // src/commands/videos/get.ts
30699
- import { defineCommand as defineCommand164 } from "citty";
31307
+ import { defineCommand as defineCommand168 } from "citty";
30700
31308
  registerSchema({
30701
31309
  command: "videos.get",
30702
31310
  description: "Get a single video by ID",
@@ -30704,7 +31312,7 @@ registerSchema({
30704
31312
  id: { type: "string", description: "Video ID", required: true }
30705
31313
  }
30706
31314
  });
30707
- var getCommand5 = defineCommand164({
31315
+ var getCommand5 = defineCommand168({
30708
31316
  meta: { name: "get", description: "Get a single video by ID. Example: baker videos get j571abc123" },
30709
31317
  args: {
30710
31318
  id: { type: "positional", description: "Video ID", required: false },
@@ -30741,7 +31349,7 @@ var getCommand5 = defineCommand164({
30741
31349
  });
30742
31350
 
30743
31351
  // src/commands/videos/search.ts
30744
- import { defineCommand as defineCommand165 } from "citty";
31352
+ import { defineCommand as defineCommand169 } from "citty";
30745
31353
  registerSchema({
30746
31354
  command: "videos.search",
30747
31355
  description: "Search videos by text query. Only returns ready videos.",
@@ -30751,7 +31359,7 @@ registerSchema({
30751
31359
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
30752
31360
  }
30753
31361
  });
30754
- var searchCommand3 = defineCommand165({
31362
+ var searchCommand3 = defineCommand169({
30755
31363
  meta: {
30756
31364
  name: "search",
30757
31365
  description: "Semantic search videos by text query. Uses hybrid BM25 + vector + reranking. Example: baker videos search 'product demo' --tags tutorial"
@@ -30803,7 +31411,7 @@ var tagsCommand5 = makeTagsCommand("videos", "video", "/api/videos/tags");
30803
31411
  // src/commands/videos/upload.ts
30804
31412
  import { readFile as readFile23, stat as stat7 } from "fs/promises";
30805
31413
  import { extname as extname3 } from "path";
30806
- import { defineCommand as defineCommand166 } from "citty";
31414
+ import { defineCommand as defineCommand170 } from "citty";
30807
31415
  var MIME_MAP = {
30808
31416
  ".mp4": "video/mp4",
30809
31417
  ".mov": "video/quicktime",
@@ -30837,7 +31445,7 @@ function detectContentType(filePath) {
30837
31445
  }
30838
31446
  return mime;
30839
31447
  }
30840
- var uploadCommand2 = defineCommand166({
31448
+ var uploadCommand2 = defineCommand170({
30841
31449
  meta: {
30842
31450
  name: "upload",
30843
31451
  description: "Upload a video file to Baker via Mux direct upload. Auto-detects content type. Example: baker videos upload ./demo.mp4"
@@ -30891,7 +31499,7 @@ var uploadCommand2 = defineCommand166({
30891
31499
  });
30892
31500
 
30893
31501
  // src/commands/videos/index.ts
30894
- var videosCommand = defineCommand167({
31502
+ var videosCommand = defineCommand171({
30895
31503
  meta: {
30896
31504
  name: "videos",
30897
31505
  description: `Find and manage videos in Baker. Subcommands: search, get, upload, delete, tags.
@@ -30915,10 +31523,10 @@ Full guide: __tooling__/docs/tools/baker/videos.md`
30915
31523
  });
30916
31524
 
30917
31525
  // src/commands/winning-ads/index.ts
30918
- import { defineCommand as defineCommand180 } from "citty";
31526
+ import { defineCommand as defineCommand184 } from "citty";
30919
31527
 
30920
31528
  // src/commands/winning-ads/advertisers.ts
30921
- import { defineCommand as defineCommand168 } from "citty";
31529
+ import { defineCommand as defineCommand172 } from "citty";
30922
31530
 
30923
31531
  // src/commands/winning-ads/shared.ts
30924
31532
  function splitList(value) {
@@ -30971,7 +31579,7 @@ function advertiserNormalizer(record, full) {
30971
31579
  last_synced_at: record.last_synced_at ?? null
30972
31580
  };
30973
31581
  }
30974
- var advertisersCommand2 = defineCommand168({
31582
+ var advertisersCommand2 = defineCommand172({
30975
31583
  meta: {
30976
31584
  name: "advertisers",
30977
31585
  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'
@@ -31029,7 +31637,7 @@ var advertisersCommand2 = defineCommand168({
31029
31637
  });
31030
31638
 
31031
31639
  // src/commands/winning-ads/brief.ts
31032
- import { defineCommand as defineCommand169 } from "citty";
31640
+ import { defineCommand as defineCommand173 } from "citty";
31033
31641
  registerSchema({
31034
31642
  command: "winning-ads.brief",
31035
31643
  description: "Generate a creative brief grounded in strategically-similar winning ads. Optionally describe the target creative with --dna (JSON) and steer with --notes.",
@@ -31075,7 +31683,7 @@ function parseDna(raw) {
31075
31683
  }
31076
31684
  return parsed;
31077
31685
  }
31078
- var briefCommand = defineCommand169({
31686
+ var briefCommand = defineCommand173({
31079
31687
  meta: {
31080
31688
  name: "brief",
31081
31689
  description: `Generate a creative brief from winning references. Example: baker winning-ads brief --dna '{"angle":"cost savings"}' --notes "B2B, LinkedIn video" --k 8`
@@ -31111,7 +31719,7 @@ var briefCommand = defineCommand169({
31111
31719
  });
31112
31720
 
31113
31721
  // src/commands/winning-ads/content.ts
31114
- import { defineCommand as defineCommand170 } from "citty";
31722
+ import { defineCommand as defineCommand174 } from "citty";
31115
31723
  registerSchema({
31116
31724
  command: "winning-ads.content",
31117
31725
  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.",
@@ -31124,7 +31732,7 @@ registerSchema({
31124
31732
  }
31125
31733
  }
31126
31734
  });
31127
- var contentCommand = defineCommand170({
31735
+ var contentCommand = defineCommand174({
31128
31736
  meta: {
31129
31737
  name: "content",
31130
31738
  description: "Read the transcript + on-screen text + copy of one winning ad. Example: baker winning-ads content adg_123 --platform meta --full --output md"
@@ -31173,7 +31781,7 @@ var contentCommand = defineCommand170({
31173
31781
  });
31174
31782
 
31175
31783
  // src/commands/winning-ads/feed.ts
31176
- import { defineCommand as defineCommand171 } from "citty";
31784
+ import { defineCommand as defineCommand175 } from "citty";
31177
31785
  function buildFeedParams(input) {
31178
31786
  const params = {};
31179
31787
  const advertiser = splitList(input.advertiser);
@@ -31225,7 +31833,7 @@ registerSchema({
31225
31833
  format: { type: "string", description: "Comma-separated formats to include (e.g. static,video)", required: false }
31226
31834
  }
31227
31835
  });
31228
- var feedCommand = defineCommand171({
31836
+ var feedCommand = defineCommand175({
31229
31837
  meta: {
31230
31838
  name: "feed",
31231
31839
  description: "Winners across every brand you follow (browse, then trim per advertiser). Example: baker winning-ads feed --per-advertiser 5 --output md"
@@ -31310,7 +31918,7 @@ var feedCommand = defineCommand171({
31310
31918
  });
31311
31919
 
31312
31920
  // src/commands/winning-ads/follow.ts
31313
- import { defineCommand as defineCommand172 } from "citty";
31921
+ import { defineCommand as defineCommand176 } from "citty";
31314
31922
  var PLATFORMS = ["meta", "linkedin"];
31315
31923
  registerSchema({
31316
31924
  command: "winning-ads.follow",
@@ -31325,7 +31933,7 @@ registerSchema({
31325
31933
  label: { type: "string", description: "Optional display label (defaults to the resolved name)", required: false }
31326
31934
  }
31327
31935
  });
31328
- var followCommand = defineCommand172({
31936
+ var followCommand = defineCommand176({
31329
31937
  meta: {
31330
31938
  name: "follow",
31331
31939
  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'
@@ -31372,7 +31980,7 @@ var followCommand = defineCommand172({
31372
31980
  });
31373
31981
 
31374
31982
  // src/commands/winning-ads/follow-competitors.ts
31375
- import { defineCommand as defineCommand173 } from "citty";
31983
+ import { defineCommand as defineCommand177 } from "citty";
31376
31984
  var PLATFORMS2 = ["meta", "linkedin"];
31377
31985
  var BATCH_TIMEOUT_MS = 3e5;
31378
31986
  function buildFollowBatchBody(input) {
@@ -31405,7 +32013,7 @@ registerSchema({
31405
32013
  }
31406
32014
  }
31407
32015
  });
31408
- var followCompetitorsCommand = defineCommand173({
32016
+ var followCompetitorsCommand = defineCommand177({
31409
32017
  meta: {
31410
32018
  name: "follow-competitors",
31411
32019
  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"'
@@ -31480,7 +32088,7 @@ var followCompetitorsCommand = defineCommand173({
31480
32088
  });
31481
32089
 
31482
32090
  // src/commands/winning-ads/following.ts
31483
- import { defineCommand as defineCommand174 } from "citty";
32091
+ import { defineCommand as defineCommand178 } from "citty";
31484
32092
  registerSchema({
31485
32093
  command: "winning-ads.following",
31486
32094
  description: "List the brands you follow in your ad-dna library, with each one's status (ready vs still adding) and cached ad counts.",
@@ -31513,7 +32121,7 @@ function followingNormalizer(record, full) {
31513
32121
  platforms: Array.isArray(record.platforms) ? record.platforms : []
31514
32122
  };
31515
32123
  }
31516
- var followingCommand = defineCommand174({
32124
+ var followingCommand = defineCommand178({
31517
32125
  meta: {
31518
32126
  name: "following",
31519
32127
  description: "List brands you follow, with status (ready / adding\u2026) and cached counts. Example: baker winning-ads following --output md"
@@ -31548,7 +32156,7 @@ var followingCommand = defineCommand174({
31548
32156
  });
31549
32157
 
31550
32158
  // src/commands/winning-ads/patterns.ts
31551
- import { defineCommand as defineCommand175 } from "citty";
32159
+ import { defineCommand as defineCommand179 } from "citty";
31552
32160
  registerSchema({
31553
32161
  command: "winning-ads.patterns",
31554
32162
  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.",
@@ -31587,7 +32195,7 @@ function discriminatorRow(record) {
31587
32195
  top_values_duds: Array.isArray(record.top_values_b) ? record.top_values_b.join(", ") : ""
31588
32196
  };
31589
32197
  }
31590
- var patternsCommand = defineCommand175({
32198
+ var patternsCommand = defineCommand179({
31591
32199
  meta: {
31592
32200
  name: "patterns",
31593
32201
  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"
@@ -31643,7 +32251,7 @@ var patternsCommand = defineCommand175({
31643
32251
  });
31644
32252
 
31645
32253
  // src/commands/winning-ads/search.ts
31646
- import { defineCommand as defineCommand176 } from "citty";
32254
+ import { defineCommand as defineCommand180 } from "citty";
31647
32255
  registerSchema({
31648
32256
  command: "winning-ads.search",
31649
32257
  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.",
@@ -31751,7 +32359,7 @@ function buildSearchBody(args) {
31751
32359
  }
31752
32360
  return body;
31753
32361
  }
31754
- var searchCommand4 = defineCommand176({
32362
+ var searchCommand4 = defineCommand180({
31755
32363
  meta: {
31756
32364
  name: "search",
31757
32365
  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"
@@ -31866,7 +32474,7 @@ var searchCommand4 = defineCommand176({
31866
32474
  });
31867
32475
 
31868
32476
  // src/commands/winning-ads/seeds.ts
31869
- import { defineCommand as defineCommand177 } from "citty";
32477
+ import { defineCommand as defineCommand181 } from "citty";
31870
32478
  function leanRow(r) {
31871
32479
  return {
31872
32480
  key: r.key,
@@ -31894,7 +32502,7 @@ function makeSeedCommand(opts) {
31894
32502
  limit: { type: "number", description: "Max keys 1-100 (default 20)", required: false, default: 20 }
31895
32503
  }
31896
32504
  });
31897
- return defineCommand177({
32505
+ return defineCommand181({
31898
32506
  meta: { name: opts.name, description: opts.description },
31899
32507
  args: {
31900
32508
  platform: { type: "string", description: "Single platform to segment on", required: false },
@@ -31943,7 +32551,7 @@ var formatsCommand = makeSeedCommand({
31943
32551
  });
31944
32552
 
31945
32553
  // src/commands/winning-ads/unfollow.ts
31946
- import { defineCommand as defineCommand178 } from "citty";
32554
+ import { defineCommand as defineCommand182 } from "citty";
31947
32555
  registerSchema({
31948
32556
  command: "winning-ads.unfollow",
31949
32557
  description: "Stop following a brand \u2014 removes it from your ad-dna library by advertiser id.",
@@ -31951,7 +32559,7 @@ registerSchema({
31951
32559
  advertiser: { type: "string", description: "Advertiser id to unfollow", required: true }
31952
32560
  }
31953
32561
  });
31954
- var unfollowCommand = defineCommand178({
32562
+ var unfollowCommand = defineCommand182({
31955
32563
  meta: {
31956
32564
  name: "unfollow",
31957
32565
  description: "Stop following a brand by advertiser id. Example: baker winning-ads unfollow adv_123"
@@ -31972,7 +32580,7 @@ var unfollowCommand = defineCommand178({
31972
32580
  });
31973
32581
 
31974
32582
  // src/commands/winning-ads/winners.ts
31975
- import { defineCommand as defineCommand179 } from "citty";
32583
+ import { defineCommand as defineCommand183 } from "citty";
31976
32584
  registerSchema({
31977
32585
  command: "winning-ads.winners",
31978
32586
  description: "Top winning ads for one advertiser id (from `advertisers` or `following`). Returns lean winner cards; add --full for DNA + longevity.",
@@ -31982,7 +32590,7 @@ registerSchema({
31982
32590
  platform: { type: "string", description: "Filter to a single platform: meta|linkedin", required: false }
31983
32591
  }
31984
32592
  });
31985
- var winnersCommand = defineCommand179({
32593
+ var winnersCommand = defineCommand183({
31986
32594
  meta: {
31987
32595
  name: "winners",
31988
32596
  description: "Top winning ads for a specific advertiser id. Example: baker winning-ads winners adv_123 --top 15 --output md"
@@ -32032,7 +32640,7 @@ var winnersCommand = defineCommand179({
32032
32640
  });
32033
32641
 
32034
32642
  // src/commands/winning-ads/index.ts
32035
- var winningAdsCommand = defineCommand180({
32643
+ var winningAdsCommand = defineCommand184({
32036
32644
  meta: {
32037
32645
  name: "winning-ads",
32038
32646
  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.
@@ -32088,7 +32696,7 @@ Full guide: __tooling__/docs/tools/baker/winning-ads.md`
32088
32696
  });
32089
32697
 
32090
32698
  // src/version.ts
32091
- import { readFileSync as readFileSync12 } from "fs";
32699
+ import { readFileSync as readFileSync13 } from "fs";
32092
32700
  function packageJsonUrl() {
32093
32701
  return new URL("../package.json", import.meta.url);
32094
32702
  }
@@ -32100,15 +32708,15 @@ function parsePackageVersion(raw) {
32100
32708
  throw new Error("Invalid CLI package.json: missing version");
32101
32709
  }
32102
32710
  function getCliVersion() {
32103
- return parsePackageVersion(readFileSync12(packageJsonUrl(), "utf8"));
32711
+ return parsePackageVersion(readFileSync13(packageJsonUrl(), "utf8"));
32104
32712
  }
32105
32713
 
32106
32714
  // src/cli.ts
32107
- var main = defineCommand181({
32715
+ var main = defineCommand185({
32108
32716
  meta: {
32109
32717
  name: "baker",
32110
32718
  version: getCliVersion(),
32111
- 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.
32719
+ 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.
32112
32720
 
32113
32721
  Auth: Set BAKER_API_KEY (starts with bk_) and BAKER_API_URL environment variables.
32114
32722
  Chat: Set BAKER_CHAT_ID for action and scheduled-action commands that stage changes against a chat.
@@ -32132,6 +32740,7 @@ Introspection: Run 'baker schema <command>' to inspect argument schemas.`
32132
32740
  testimonials: testimonialsCommand,
32133
32741
  canvas: canvasCommand,
32134
32742
  tags: tagsCommand3,
32743
+ "tag-manager": tagManagerCommand,
32135
32744
  chats: chatsCommand,
32136
32745
  history: historyCommand,
32137
32746
  hubspot: hubspotCommand,