@koda-sl/baker-cli 0.121.0-dev.87e882ae1 → 0.121.0-dev.a2cdb40bc

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/README.md CHANGED
@@ -2504,22 +2504,26 @@ Rules:
2504
2504
 
2505
2505
  ### Marketing Tags (`baker tags`)
2506
2506
 
2507
- Read the client's marketing/analytics tags (Meta pixel, GA4, Google Ads, GTM, Clarity, Hotjar, PostHog, …) production tags overlaid with the changes staged in this chat.
2508
-
2509
- **The CLI is read-only.** Every tag change (create, edit, delete) goes through the `request_tag_input` tool (`baker_ui` MCP server): the agent proposes one or more changes — each becomes a tab in one blocking approval form — pre-filling the non-secret fields it knows; the user reviews, edits, fills secret fields, and approves or skips each tab. Approved changes stage on the chat and apply when the chat is published; discarding the chat drops them.
2507
+ Manage the client's marketing/analytics tags (Meta pixel, GA4, Google Ads, GTM, Clarity, Hotjar, PostHog, …) as per-chat **staged** changes. Nothing touches production tags until the chat is published; discarding the chat drops the draft.
2510
2508
 
2511
2509
  `BAKER_CHAT_ID` must be set.
2512
2510
 
2513
2511
  ```bash
2514
- baker tags list # effective view: production + staged, with secret status
2515
- baker tags draft # review the staged changes awaiting publish
2512
+ baker tags list # effective view: production + staged, with secret status
2513
+ baker tags add clarity --set projectId=abcde12345 # client-only tag, no secrets needed
2514
+ baker tags add meta --set pixelId=123 --request-secret accessToken
2515
+ baker tags update <ref> --set pixelId=999 --clear testEventCode
2516
+ baker tags remove <ref> # stage deletion (tag_temp_* ref drops the staged create)
2517
+ baker tags draft # review staged ops
2518
+ baker tags draft remove <ref> # drop one staged op
2519
+ baker tags draft clear # drop everything staged
2516
2520
  ```
2517
2521
 
2518
2522
  Notes:
2519
2523
 
2520
- - **Secrets never travel through this CLI or the chat.** Secret fields (`accessToken`, `apiSecret`, `authorizationToken`, `apiKey`, `conversionToken`, `oauthProviderId`) are entered only in the dashboard's secure tag form and flow straight into the staged draft; tool responses only ever name which secret fields are set/pending.
2521
- - Staged creates get a server-generated `tag_temp_*` ref (returned in the tool result and printed by `list`). Use it (or a real tag id) as flow side-effect `tagIds` — the published tag keeps resolving under the temp ref.
2522
- - Proposing a delete on a `tag_temp_*` ref drops the staged create instead.
2524
+ - **Secrets never travel through this CLI.** Secret fields (`accessToken`, `apiSecret`, `authorizationToken`, `apiKey`, `conversionToken`, `oauthProviderId`) are rejected in `--set`. Stage with `--request-secret <field>`; the user provides values via the secure tag form in the chat (`request_tag_input`), which writes them straight into the staged draft. Responses only ever name which secret fields are set/pending.
2525
+ - Staged creates get a server-generated `tag_temp_*` ref. Use it (or a real tag id) as flow side-effect `tagIds` — the published tag keeps resolving under the temp ref.
2526
+ - `update`/`remove` with a `tag_temp_*` ref amend/drop the staged create in place.
2523
2527
  - Single-instance types (`code`, `posthog`, `datafast`) reject a second instance against the chat's effective view.
2524
2528
  - Conflicts at publish (tag deleted in the dashboard, config invalid) skip the op with a recorded reason — they never block the publish.
2525
2529
 
package/dist/cli.js CHANGED
@@ -1939,6 +1939,46 @@ var TAG_TYPES = [
1939
1939
  "twitterAds"
1940
1940
  ];
1941
1941
  var tagTypeSchema = z5.enum(TAG_TYPES);
1942
+ var TAG_SECRET_FIELDS = {
1943
+ meta: ["accessToken"],
1944
+ amplitude: [],
1945
+ googleAds: ["oauthProviderId", "customerAccountId", "customerId", "loginCustomerId"],
1946
+ tiktok: ["accessToken"],
1947
+ vwo: [],
1948
+ hotjar: [],
1949
+ clarity: [],
1950
+ pinterest: ["conversionToken"],
1951
+ code: [],
1952
+ googleAnalytics: ["apiSecret"],
1953
+ googleTagManager: ["authorizationToken"],
1954
+ hubspot: [],
1955
+ linkedinInsightTag: ["oauthProviderId"],
1956
+ onetrust: [],
1957
+ posthog: [],
1958
+ datafast: ["apiKey"],
1959
+ recaptcha: [],
1960
+ twitterAds: ["oauthProviderId"]
1961
+ };
1962
+ var TAG_REQUESTABLE_SECRET_FIELDS = {
1963
+ meta: ["accessToken"],
1964
+ amplitude: [],
1965
+ googleAds: ["oauthProviderId", "customerAccountId"],
1966
+ tiktok: ["accessToken"],
1967
+ vwo: [],
1968
+ hotjar: [],
1969
+ clarity: [],
1970
+ pinterest: ["conversionToken"],
1971
+ code: [],
1972
+ googleAnalytics: ["apiSecret"],
1973
+ googleTagManager: ["authorizationToken"],
1974
+ hubspot: [],
1975
+ linkedinInsightTag: ["oauthProviderId"],
1976
+ onetrust: [],
1977
+ posthog: [],
1978
+ datafast: ["apiKey"],
1979
+ recaptcha: [],
1980
+ twitterAds: ["oauthProviderId"]
1981
+ };
1942
1982
  var tagDraftOpKindSchema = z5.enum(["create", "update", "delete"]);
1943
1983
  var tagDraftOpViewSchema = z5.object({
1944
1984
  /** `tag_temp_*` for staged creates; the real tag id for update/delete ops. */
@@ -1974,17 +2014,49 @@ var tagsEffectiveEntrySchema = z5.object({
1974
2014
  });
1975
2015
  var tagsListRequestSchema = z5.object({ chatId: z5.string() });
1976
2016
  var tagsListResponseSchema = z5.object({ tags: z5.array(tagsEffectiveEntrySchema) });
2017
+ var tagsDraftStageRequestSchema = z5.discriminatedUnion("kind", [
2018
+ z5.object({
2019
+ kind: z5.literal("create"),
2020
+ chatId: z5.string(),
2021
+ type: tagTypeSchema,
2022
+ config: z5.record(z5.string(), z5.string()),
2023
+ requestSecrets: z5.array(z5.string()).optional(),
2024
+ summary: z5.string().optional()
2025
+ }),
2026
+ z5.object({
2027
+ kind: z5.literal("update"),
2028
+ chatId: z5.string(),
2029
+ ref: z5.string(),
2030
+ config: z5.record(z5.string(), z5.string()).optional(),
2031
+ clearFields: z5.array(z5.string()).optional(),
2032
+ requestSecrets: z5.array(z5.string()).optional(),
2033
+ summary: z5.string().optional()
2034
+ }),
2035
+ z5.object({
2036
+ kind: z5.literal("delete"),
2037
+ chatId: z5.string(),
2038
+ ref: z5.string()
2039
+ })
2040
+ ]);
2041
+ var tagsDraftStageResponseSchema = z5.object({
2042
+ /** Null when the request dissolved a staged create (delete/clearing an unpublished temp ref). */
2043
+ op: tagDraftOpViewSchema.nullable(),
2044
+ /** True when a `delete <tag_temp_*>` dropped the staged create instead of staging a delete. */
2045
+ removedStagedCreate: z5.boolean().optional()
2046
+ });
1977
2047
  var tagsDraftListRequestSchema = z5.object({ chatId: z5.string() });
1978
2048
  var tagsDraftListResponseSchema = z5.object({
1979
2049
  status: z5.enum(["active", "publishing", "applied", "discarded", "none"]),
1980
2050
  ops: z5.array(tagDraftOpViewSchema)
1981
2051
  });
2052
+ var tagsDraftRemoveRequestSchema = z5.object({ chatId: z5.string(), ref: z5.string() });
2053
+ var tagsDraftRemoveResponseSchema = z5.object({ removed: z5.boolean() });
2054
+ var tagsDraftClearRequestSchema = z5.object({ chatId: z5.string() });
2055
+ var tagsDraftClearResponseSchema = z5.object({ cleared: z5.number() });
1982
2056
  var tagInputRequestSchema = z5.object({
1983
- // Every tag change is a tab in the approval form: create/edit show the full
1984
- // body; delete shows a confirm. No tag change bypasses this approval.
1985
- mode: z5.enum(["create", "edit", "delete"]),
2057
+ mode: z5.enum(["create", "edit"]),
1986
2058
  tagType: tagTypeSchema,
1987
- /** Edit/delete mode — the real tag id or `tag_temp_*` ref being changed. */
2059
+ /** Edit mode — the real tag id or `tag_temp_*` ref being edited. */
1988
2060
  ref: z5.string().optional(),
1989
2061
  /** Non-secret values the agent proposes to prefill. Secret keys are stripped at every boundary. */
1990
2062
  prefilledConfig: z5.record(z5.string(), z5.string()).optional(),
@@ -1993,9 +2065,6 @@ var tagInputRequestSchema = z5.object({
1993
2065
  /** Short message shown above the form explaining why the input is needed. */
1994
2066
  message: z5.string().optional()
1995
2067
  });
1996
- var tagChangeToolInputSchema = z5.object({
1997
- changes: z5.array(tagInputRequestSchema).min(1).max(8)
1998
- });
1999
2068
  var tagInputResultSchema = z5.discriminatedUnion("status", [
2000
2069
  z5.object({
2001
2070
  status: z5.literal("submitted"),
@@ -2011,9 +2080,6 @@ var tagInputResultSchema = z5.discriminatedUnion("status", [
2011
2080
  reason: z5.string().optional()
2012
2081
  })
2013
2082
  ]);
2014
- var tagChangeToolResultSchema = z5.object({
2015
- results: z5.array(tagInputResultSchema)
2016
- });
2017
2083
 
2018
2084
  // ../api/src/testimonials.ts
2019
2085
  import { z as z6 } from "zod";
@@ -23916,6 +23982,10 @@ var schemaCommand = defineCommand147({
23916
23982
  import { defineCommand as defineCommand148 } from "citty";
23917
23983
 
23918
23984
  // src/commands/tags/shared.ts
23985
+ function failValidation3(message) {
23986
+ writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message } });
23987
+ process.exit(1);
23988
+ }
23919
23989
  function failApi3(err) {
23920
23990
  if (err instanceof ApiError) {
23921
23991
  writeJson({ ok: false, error: { code: err.code, message: err.message } });
@@ -23928,6 +23998,53 @@ function failApi3(err) {
23928
23998
  writeJson({ ok: false, error: { code: "INTERNAL_ERROR", message: "Unexpected error" } });
23929
23999
  process.exit(1);
23930
24000
  }
24001
+ function parseTagType(value) {
24002
+ const parsed = tagTypeSchema.safeParse(value);
24003
+ if (!parsed.success) {
24004
+ failValidation3(`Unknown tag type "${value}". Valid types: ${TAG_TYPES.join(", ")}.`);
24005
+ }
24006
+ return parsed.data;
24007
+ }
24008
+ function toArray(value) {
24009
+ if (value === void 0 || typeof value === "boolean") {
24010
+ return [];
24011
+ }
24012
+ return Array.isArray(value) ? value : [value];
24013
+ }
24014
+ function parseSetArgs(value) {
24015
+ const config = {};
24016
+ for (const entry of toArray(value)) {
24017
+ const eq = entry.indexOf("=");
24018
+ if (eq <= 0) {
24019
+ failValidation3(`--set expects key=value, got "${entry}".`);
24020
+ }
24021
+ config[entry.slice(0, eq)] = entry.slice(eq + 1);
24022
+ }
24023
+ return config;
24024
+ }
24025
+ function parseListArg(value) {
24026
+ return toArray(value).flatMap(
24027
+ (entry) => entry.split(",").map((item) => item.trim()).filter((item) => item !== "")
24028
+ );
24029
+ }
24030
+ function rejectSecretSets(type, config) {
24031
+ const secretFields = TAG_SECRET_FIELDS[type];
24032
+ const offending = Object.keys(config).filter((key) => secretFields.includes(key));
24033
+ if (offending.length > 0) {
24034
+ failValidation3(
24035
+ `${offending.join(", ")} ${offending.length === 1 ? "is a secret field" : "are secret fields"} \u2014 never pass secret values on the command line. Re-run with --request-secret ${offending[0]} and the user will provide it via the secure tag form in the chat.`
24036
+ );
24037
+ }
24038
+ }
24039
+ function validateRequestSecrets(type, fields) {
24040
+ const requestable = TAG_REQUESTABLE_SECRET_FIELDS[type];
24041
+ const invalid = fields.filter((field) => !requestable.includes(field));
24042
+ if (invalid.length > 0) {
24043
+ failValidation3(
24044
+ `Cannot request ${invalid.join(", ")} for a ${type} tag. Requestable secret fields: ${requestable.length > 0 ? requestable.join(", ") : "(none \u2014 this type has no secret fields)"}.`
24045
+ );
24046
+ }
24047
+ }
23931
24048
  function renderEffectiveEntry(entry) {
23932
24049
  const parts = [entry.ref, entry.type];
23933
24050
  if (entry.identifier !== void 0) {
@@ -23949,11 +24066,47 @@ function renderEffectiveEntry(entry) {
23949
24066
  // src/commands/tags/index.ts
23950
24067
  registerSchema({
23951
24068
  command: "tags.list",
23952
- description: "Effective marketing tags for this chat: production tags overlaid with the changes staged in this chat. The printed refs (tag ids or tag_temp_*) are exactly what flow side-effect tagIds should reference. Read-only \u2014 every tag change (create, edit, delete) goes through the request_tag_input approval tool.",
24069
+ description: "Effective marketing tags for this chat: production tags overlaid with the ops staged in this chat's draft. The printed refs (tag ids or tag_temp_*) are exactly what flow side-effect tagIds should reference.",
23953
24070
  args: {
23954
24071
  json: { type: "boolean", description: "Print the raw JSON envelope instead of the readable list", required: false }
23955
24072
  }
23956
24073
  });
24074
+ registerSchema({
24075
+ command: "tags.add",
24076
+ description: "Stage creation of a marketing tag (applies when the chat publishes). Set every non-secret field with --set; request secret fields (API keys, tokens, OAuth connections) with --request-secret \u2014 the user provides them via the secure tag form in the chat, never through the CLI.",
24077
+ args: {
24078
+ type: {
24079
+ type: "positional",
24080
+ description: "Tag type (see `baker tags list` output / meta, googleAnalytics, clarity, \u2026)",
24081
+ required: true
24082
+ },
24083
+ set: { type: "string", description: "Repeatable key=value for non-secret config fields", required: false },
24084
+ "request-secret": {
24085
+ type: "string",
24086
+ description: "Repeatable secret field name to request from the user",
24087
+ required: false
24088
+ }
24089
+ }
24090
+ });
24091
+ registerSchema({
24092
+ command: "tags.update",
24093
+ description: "Stage an update to a tag by ref (real tag id, or tag_temp_* to amend a create staged in this chat). --set patches fields, --clear removes them, --request-secret asks the user for secret values.",
24094
+ args: {
24095
+ ref: { type: "positional", description: "Tag ref from `baker tags list`", required: true },
24096
+ set: { type: "string", description: "Repeatable key=value for non-secret config fields", required: false },
24097
+ clear: { type: "string", description: "Repeatable field name to clear", required: false },
24098
+ "request-secret": {
24099
+ type: "string",
24100
+ description: "Repeatable secret field name to request from the user",
24101
+ required: false
24102
+ }
24103
+ }
24104
+ });
24105
+ registerSchema({
24106
+ command: "tags.remove",
24107
+ description: "Stage deletion of a tag by ref (applies on publish). Removing a tag_temp_* ref drops the staged create instead.",
24108
+ args: { ref: { type: "positional", description: "Tag ref from `baker tags list`", required: true } }
24109
+ });
23957
24110
  async function listTags(json) {
23958
24111
  try {
23959
24112
  const chatId = requireChatId();
@@ -23963,7 +24116,7 @@ async function listTags(json) {
23963
24116
  return;
23964
24117
  }
23965
24118
  if (response.tags.length === 0) {
23966
- process.stdout.write("No tags configured. Propose one with the request_tag_input tool (baker_ui).\n");
24119
+ process.stdout.write("No tags configured. Stage one with `baker tags add <type> --set key=value`.\n");
23967
24120
  return;
23968
24121
  }
23969
24122
  process.stdout.write(`${response.tags.map(renderEffectiveEntry).join("\n")}
@@ -23982,6 +24135,84 @@ var listCommand7 = defineCommand148({
23982
24135
  await listTags(args.json === true);
23983
24136
  }
23984
24137
  });
24138
+ async function stage(body) {
24139
+ try {
24140
+ const response = await apiPost("/api/tags/draft/stage", body);
24141
+ writeJson({ ok: true, data: response });
24142
+ } catch (err) {
24143
+ failApi3(err);
24144
+ }
24145
+ }
24146
+ var addCommand2 = defineCommand148({
24147
+ meta: {
24148
+ name: "add",
24149
+ description: "Stage a new tag (applies on publish). Secret fields are never passed here \u2014 use --request-secret and the user fills them in the chat's secure tag form. Example: baker tags add meta --set pixelId=123456 --request-secret accessToken"
24150
+ },
24151
+ args: {
24152
+ type: {
24153
+ type: "positional",
24154
+ description: "Tag type (meta, googleAnalytics, googleAds, clarity, \u2026)",
24155
+ required: true
24156
+ },
24157
+ set: { type: "string", description: "key=value for a non-secret config field (repeatable)" },
24158
+ "request-secret": { type: "string", description: "Secret field name the user should provide (repeatable)" }
24159
+ },
24160
+ run: async ({ args }) => {
24161
+ const type = parseTagType(args.type);
24162
+ const config = parseSetArgs(args.set);
24163
+ const requestSecrets = parseListArg(args["request-secret"]);
24164
+ rejectSecretSets(type, config);
24165
+ validateRequestSecrets(type, requestSecrets);
24166
+ const chatId = requireChatId();
24167
+ await stage({
24168
+ kind: "create",
24169
+ chatId,
24170
+ type,
24171
+ config,
24172
+ ...requestSecrets.length > 0 ? { requestSecrets } : {}
24173
+ });
24174
+ }
24175
+ });
24176
+ var updateCommand3 = defineCommand148({
24177
+ meta: {
24178
+ name: "update",
24179
+ description: "Stage an update to a tag. A tag_temp_* ref amends the create staged in this chat. Example: baker tags update <ref> --set pixelId=999 --clear testEventCode"
24180
+ },
24181
+ args: {
24182
+ ref: { type: "positional", description: "Tag ref from `baker tags list`", required: true },
24183
+ set: { type: "string", description: "key=value patch for a non-secret config field (repeatable)" },
24184
+ clear: { type: "string", description: "Field name to clear (repeatable)" },
24185
+ "request-secret": { type: "string", description: "Secret field name the user should provide (repeatable)" }
24186
+ },
24187
+ run: async ({ args }) => {
24188
+ const config = parseSetArgs(args.set);
24189
+ const clearFields = parseListArg(args.clear);
24190
+ const requestSecrets = parseListArg(args["request-secret"]);
24191
+ if (Object.keys(config).length === 0 && clearFields.length === 0 && requestSecrets.length === 0) {
24192
+ failValidation3("Nothing to update \u2014 pass --set, --clear, or --request-secret.");
24193
+ }
24194
+ const chatId = requireChatId();
24195
+ await stage({
24196
+ kind: "update",
24197
+ chatId,
24198
+ ref: args.ref,
24199
+ ...Object.keys(config).length > 0 ? { config } : {},
24200
+ ...clearFields.length > 0 ? { clearFields } : {},
24201
+ ...requestSecrets.length > 0 ? { requestSecrets } : {}
24202
+ });
24203
+ }
24204
+ });
24205
+ var removeCommand5 = defineCommand148({
24206
+ meta: {
24207
+ name: "remove",
24208
+ description: "Stage deletion of a tag (applies on publish). Removing a tag_temp_* ref drops the staged create instead. Example: baker tags remove <ref>"
24209
+ },
24210
+ args: { ref: { type: "positional", description: "Tag ref from `baker tags list`", required: true } },
24211
+ run: async ({ args }) => {
24212
+ const chatId = requireChatId();
24213
+ await stage({ kind: "delete", chatId, ref: args.ref });
24214
+ }
24215
+ });
23985
24216
  async function listDraft3() {
23986
24217
  try {
23987
24218
  const chatId = requireChatId();
@@ -23991,10 +24222,55 @@ async function listDraft3() {
23991
24222
  failApi3(err);
23992
24223
  }
23993
24224
  }
24225
+ var draftListCommand = defineCommand148({
24226
+ meta: { name: "list", description: "Review the tag ops staged in this chat. Example: baker tags draft" },
24227
+ run: async () => {
24228
+ await listDraft3();
24229
+ }
24230
+ });
24231
+ var draftRemoveCommand = defineCommand148({
24232
+ meta: {
24233
+ name: "remove",
24234
+ description: "Drop one staged tag op by ref. Example: baker tags draft remove tag_temp_abc123"
24235
+ },
24236
+ args: { ref: { type: "positional", description: "Staged op ref", required: true } },
24237
+ run: async ({ args }) => {
24238
+ try {
24239
+ const chatId = requireChatId();
24240
+ const response = await apiPost("/api/tags/draft/remove", {
24241
+ chatId,
24242
+ ref: args.ref
24243
+ });
24244
+ writeJson({ ok: true, data: response });
24245
+ } catch (err) {
24246
+ failApi3(err);
24247
+ }
24248
+ }
24249
+ });
24250
+ var draftClearCommand = defineCommand148({
24251
+ meta: {
24252
+ name: "clear",
24253
+ description: "Drop ALL tag ops staged in this chat \u2014 nothing will apply on publish. Example: baker tags draft clear"
24254
+ },
24255
+ run: async () => {
24256
+ try {
24257
+ const chatId = requireChatId();
24258
+ const response = await apiPost("/api/tags/draft/clear", { chatId });
24259
+ writeJson({ ok: true, data: response });
24260
+ } catch (err) {
24261
+ failApi3(err);
24262
+ }
24263
+ }
24264
+ });
23994
24265
  var draftCommand3 = defineCommand148({
23995
24266
  meta: {
23996
24267
  name: "draft",
23997
- 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)."
24268
+ description: "Review and edit the tag ops staged in this chat BEFORE publish. Subcommands: list (default), remove, clear. Ops never touch production tags until the chat is published."
24269
+ },
24270
+ subCommands: {
24271
+ list: draftListCommand,
24272
+ remove: draftRemoveCommand,
24273
+ clear: draftClearCommand
23998
24274
  },
23999
24275
  run: async () => {
24000
24276
  await listDraft3();
@@ -24003,18 +24279,25 @@ var draftCommand3 = defineCommand148({
24003
24279
  var tagsCommand3 = defineCommand148({
24004
24280
  meta: {
24005
24281
  name: "tags",
24006
- 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.
24282
+ description: `Manage the client's marketing/analytics tags (Meta pixel, GA4, Google Ads, GTM, Clarity, Hotjar, \u2026) as per-chat STAGED changes \u2014 nothing touches production until the chat is published.
24007
24283
 
24008
- This command is READ-ONLY. Every tag change \u2014 create, edit, or delete \u2014 goes through the request_tag_input tool (baker_ui MCP server): propose one or more changes (each becomes a tab in one approval form), pre-fill the non-secret fields you know, and the user reviews, fills secrets, and approves or skips each. Secret values (accessToken, apiSecret, authorizationToken, apiKey, conversionToken, OAuth connections) never pass through the CLI or the chat.
24284
+ Secrets: secret fields (accessToken, apiSecret, authorizationToken, apiKey, conversionToken, oauthProviderId) are NEVER typed into chat or passed to this CLI. Stage with --request-secret <field>, then invoke the request_tag_input tool (baker_ui) so the user fills them in the dashboard's secure tag form.
24009
24285
 
24010
24286
  Refs: \`baker tags list\` prints each tag's ref \u2014 a real tag id, or tag_temp_* for creates staged in this chat. Use these refs as flow side-effect tagIds; they keep resolving after publish.
24011
24287
 
24012
24288
  Examples:
24013
- baker tags list # production + staged, with secret status
24014
- baker tags draft # review the staged changes awaiting publish`
24289
+ baker tags list # production + staged, with secret status
24290
+ baker tags add clarity --set projectId=abcde12345 # client-only tag, no secrets needed
24291
+ baker tags add meta --set pixelId=123 --request-secret accessToken
24292
+ baker tags update <ref> --set pixelId=999 --clear testEventCode
24293
+ baker tags remove <ref> # stage deletion (temp ref = drop the create)
24294
+ baker tags draft # review staged ops before finishing`
24015
24295
  },
24016
24296
  subCommands: {
24017
24297
  list: listCommand7,
24298
+ add: addCommand2,
24299
+ update: updateCommand3,
24300
+ remove: removeCommand5,
24018
24301
  draft: draftCommand3
24019
24302
  },
24020
24303
  run: async () => {