@koda-sl/baker-cli 0.176.0 → 0.177.0-dev.0cbdaa224

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
@@ -1101,7 +1101,11 @@ Each finding: `{id, area, check, status, severity, evidence, fix: {explanation,
1101
1101
 
1102
1102
  ### Google Analytics 4 (`baker ga4`)
1103
1103
 
1104
- GA4 commands for multi-channel audits. Playbook-aligned presets, property health audits, and free-form Data API queries.
1104
+ GA4 commands for multi-channel audits **and** for configuring what the property measures. Playbook-aligned report presets, property health audits, free-form Data API queries, and staged Admin API writes.
1105
+
1106
+ Reads (`properties`, `config`, `audit`, `query`) happen immediately. Writes (`key-event`, `custom-dimension`, `custom-metric`, `custom-event`, `data-retention`) are **staged** onto the chat, reviewed in the dashboard's Analytics tab, and applied when the chat completes — at which point they take effect on the property immediately. There is no simulated mode and no undo.
1107
+
1108
+ A Google Analytics connection made before writes shipped is read-only and every write command refuses with a message saying to reconnect. `baker ga4 config` reports `canWrite`.
1105
1109
 
1106
1110
  ---
1107
1111
 
@@ -1115,6 +1119,18 @@ baker ga4 properties
1115
1119
 
1116
1120
  ---
1117
1121
 
1122
+ ### `baker ga4 config`
1123
+
1124
+ What the property measures today — key events, custom dimensions and metrics, custom events, data streams, retention — plus whether this connection may change it. Run before staging any write: every id an update or removal targets comes from here.
1125
+
1126
+ ```bash
1127
+ baker ga4 config
1128
+ baker ga4 config --property-id 123456789
1129
+ baker ga4 config --full
1130
+ ```
1131
+
1132
+ ---
1133
+
1118
1134
  ### `baker ga4 audit`
1119
1135
 
1120
1136
  Run all GA4 admin health checks at once. Checks data retention, Google Ads linkage, phantom conversions, audience defaults, data streams, and attribution settings.
@@ -1183,6 +1199,58 @@ baker ga4 query --dimensions "date,sessionSourceMedium" --metrics "sessions,conv
1183
1199
 
1184
1200
  ---
1185
1201
 
1202
+ ### `baker ga4 key-event` / `custom-dimension` / `custom-metric` / `custom-event` / `data-retention`
1203
+
1204
+ Staged Admin API writes. Each stages one change onto the chat; nothing reaches Google Analytics until the chat completes.
1205
+
1206
+ ```bash
1207
+ # What GA4 counts as a conversion (and Google Ads imports for bidding)
1208
+ baker ga4 key-event create --json '{"eventName":"generate_lead","countingMethod":"ONCE_PER_EVENT"}'
1209
+ baker ga4 key-event update 4185 --json '{"countingMethod":"ONCE_PER_SESSION"}'
1210
+ baker ga4 key-event delete 4185
1211
+
1212
+ # Make an event parameter reportable
1213
+ baker ga4 custom-dimension create --json '{"parameterName":"plan_tier","displayName":"Plan tier","scope":"EVENT"}'
1214
+ baker ga4 custom-dimension update 12 --json '{"displayName":"Plan"}'
1215
+ baker ga4 custom-dimension archive 12
1216
+ baker ga4 custom-metric create --json '{"parameterName":"deal_value","displayName":"Deal value","scope":"EVENT","measurementUnit":"CURRENCY","restrictedMetricType":["REVENUE_DATA"]}'
1217
+ baker ga4 custom-metric update 8 --json '{"description":"Closed-won value"}'
1218
+ baker ga4 custom-metric archive 8
1219
+
1220
+ # A new event built from one the site already sends (GA4's "Create event")
1221
+ baker ga4 custom-event create --json '{"destinationEvent":"quote_request","eventConditions":[{"field":"event_name","comparisonType":"EQUALS","value":"form_submit"},{"field":"page_location","comparisonType":"CONTAINS","value":"/quote"}],"sourceCopyParameters":true}'
1222
+ baker ga4 custom-event update 7 --json '{"sourceCopyParameters":false}'
1223
+ baker ga4 custom-event delete 7
1224
+
1225
+ # How long data is kept (months: 2, 14, 26, 38, 50 — over 14 needs Analytics 360)
1226
+ baker ga4 data-retention set --event-data 14 --user-data 14
1227
+
1228
+ # Several definitions in one call — one read of the property instead of one per definition
1229
+ baker ga4 custom-dimension create --json '[{"parameterName":"plan_tier","displayName":"Plan tier","scope":"EVENT"},{"parameterName":"lead_source","displayName":"Lead source","scope":"EVENT"}]'
1230
+ ```
1231
+
1232
+ Every `create` accepts a JSON array as well as a single object, and stages the whole list in one request (all-or-nothing). Prefer it for bulk work: each stage reads the property's full configuration first, so separate calls multiply that read.
1233
+
1234
+ Custom dimensions and metrics are **archived**, not deleted (the slot frees, past data stays); key events and custom events are **deleted**. Neither is reversible from Baker. Because `parameterName` and `scope` are immutable, changing one is an archive plus a create staged on the same chat — the removal applies first and frees the name. `--property-id` is required when more than one property is connected, and one chat stages changes for one property; `--data-stream` is required for a custom event when the property has more than one website stream.
1235
+
1236
+ ---
1237
+
1238
+ ### `baker ga4 draft`
1239
+
1240
+ Review and undo the Analytics changes staged on this chat.
1241
+
1242
+ ```bash
1243
+ baker ga4 draft list # everything staged (--json for the envelope)
1244
+ baker ga4 draft show ga4_temp_keyEvent_x_0 # one change in full, with today's values
1245
+ baker ga4 draft amend keyEvent:4185 --patch '{"countingMethod":"ONCE_PER_EVENT"}'
1246
+ baker ga4 draft remove keyEvent:4185
1247
+ baker ga4 draft clear
1248
+ ```
1249
+
1250
+ `list` and `show` take `--chat <id>` to read an earlier chat's staged changes.
1251
+
1252
+ ---
1253
+
1186
1254
  ### Google Search Console (`baker gsc`)
1187
1255
 
1188
1256
  GSC commands for PPC-SEO arbitrage, brand halo analysis, and negative keyword discovery.
@@ -4941,6 +5009,7 @@ This CLI is designed for AI agent consumption. Key patterns:
4941
5009
 
4942
5010
  Shipped with it: **staging a second removal of the same shape silently did nothing.** Amend-matching reduced any composite ref to the segment after its last `~` — built for the two-segment `adGroupId~adId` refs that ad and keyword targets heal to, where that segment really is the entity's own id. It is not, in three other places: an asset-group asset (`{assetGroup}~{asset}~{FIELD_TYPE}`) and a customer-level asset link (`{asset}~{FIELD_TYPE}`) both end in a field type every asset of that ratio shares, and a campaign shared set (`{campaign}~{sharedSet}`) ends in the list's id, the same in every campaign it is attached to. So the second detach amended the first instead of staging, returned the first's ref and reported success: a PMax creative swap could only ever remove one image per ratio, and detaching one negative list from three campaigns removed it from one. Nothing in the output said the rest had been dropped. Refs now compare whole, with one one-sided exception — a **bare** id still collapses onto the two-segment composite whose child it names, which is the only case the reduction ever existed for. The duplicate copy of the same helper in the staging path is gone rather than fixed twice. Also: linking an asset Google created automatically is refused at stage time on an asset group, not just on a campaign/ad-group/customer link — a Performance Max group is where auto-created creative is most likely to be picked up. And the live-context resolver now reads the asset group and asset an attach/detach names, so the review of a swap shows names and the images themselves rather than the ids inside a resource name.
4943
5011
 
5012
+ - **0.177.0**: `baker ga4` can change what a property measures, not only report on it. Five staged write families — `key-event` (create|update|delete), `custom-dimension` and `custom-metric` (create|update|archive), `custom-event` (create|update|delete), `data-retention set` — plus `baker ga4 config` to read the property's current configuration and `baker ga4 draft` (list|show|amend|remove|clear) to review the staged set. `custom-event` is GA4's "Create event" rule: build a new event out of one the site already sends (a `form_submit` on `/quote` becomes `quote_request`), then mark it a key event so Google Ads imports it — the loop that previously ended with "do this by hand in Analytics". Staging follows Tag Manager: nothing is sent during the chat, the user reviews the whole set in the dashboard's **Analytics** tab, and it applies when they complete the chat. Unlike Tag Manager there is no version left over — an applied change is in effect at once, and there is no undo, so removals ask for confirmation first. Everything Google would reject is refused at stage time against a fresh read of the property rather than discovered at publish: a duplicate `eventName` or `parameterName` (including against another change staged on the same chat), an immutable field in an update, a currency metric with no restricted type, a retention window past 14 months on a standard property, a target the property does not have. Marking an event Google fires by itself (`page_view`, `scroll`, …) as a key event is a **warning**, not a refusal — it is legal, it just inflates conversions and poisons Smart Bidding. The one thing that is *not* a duplicate is the replacement of something the same chat has already staged the removal of: ops apply in stage order, so `custom-dimension archive 12` followed by a create on the same `parameterName` works, which is the only way to change a field GA4 marks immutable. Every `create` also takes a JSON array and stages the list in one request, because each stage costs a full read of the property. One chat stages changes for one property. **The OAuth scope changed:** Baker now requests `analytics.edit`, so every Google Analytics connection made before this is read-only until it is reconnected; the write commands refuse up front with that as the fix, and `baker ga4 config` reports `canWrite`. Reporting is unaffected. Not covered, and reported as such by `baker capabilities`: property create/rename/merge, data streams, the Google Ads link, and audiences.
4944
5013
  - **0.169.0**: `baker capabilities` — one call, before any platform work, that answers what Baker **cannot** do. Everything else (`baker schema`, the family docs, `--help`) describes what it can, so the only way to learn a capability was absent was to try it, usually after telling the user it would happen. Per surface (`google-ads`, `meta-ads`, `linkedin-ads`, `x-ads`, `tag-manager`, `ga4`, `gsc`) it reports: what is connected and which accounts/properties/containers were picked; whether writes are **live** or **simulated** for this company, and that Tag Manager has neither mode — completing the chat changes the real container; and a `limits` list separating *not built yet* from *the platform forbids it* from *we hold no permission for it*, because each leads to something different to tell the user. Naming one surface (`baker capabilities google-ads`) also returns `ops`: every change with its required and optional fields and the exact values each accepts — so "is there a field to demote a conversion action, and how is it spelled?" is one call instead of four guesses. Almost everything is derived rather than written down: the accounts and permissions come from the connection, the write mode from the company's gate, the writable entities and field contracts from the same schemas the backend parses with, and read-only vs read-write from the OAuth scopes the app actually requests (which is why "GA4 is read-only" is a fact, not a sentence). The handful of hand-written entries each carry a machine-checkable claim about the code behind them, and a coverage test fails when one stops holding. A rejected staged write on Google, Meta or LinkedIn now points at this command instead of leaving the agent to guess another field name.
4945
5014
 
4946
5015
  ## Publishing
@@ -7,9 +7,9 @@ import {
7
7
  __toESM
8
8
  } from "./chunk-YL3HDEIJ.js";
9
9
 
10
- // ../../node_modules/.pnpm/safe-stable-stringify@2.5.0/node_modules/safe-stable-stringify/index.js
10
+ // ../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/810146e81bae4e3a061fe487864f2fde80c4b03b886877dc0f1fffbc6480b67e/node_modules/safe-stable-stringify/index.js
11
11
  var require_safe_stable_stringify = __commonJS({
12
- "../../node_modules/.pnpm/safe-stable-stringify@2.5.0/node_modules/safe-stable-stringify/index.js"(exports, module) {
12
+ "../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/810146e81bae4e3a061fe487864f2fde80c4b03b886877dc0f1fffbc6480b67e/node_modules/safe-stable-stringify/index.js"(exports, module) {
13
13
  "use strict";
14
14
  var { hasOwnProperty } = Object.prototype;
15
15
  var stringify = configure2();
@@ -1075,7 +1075,7 @@ function resolveAdaptFormats(params) {
1075
1075
  return params.formats ?? [];
1076
1076
  }
1077
1077
 
1078
- // ../../node_modules/.pnpm/safe-stable-stringify@2.5.0/node_modules/safe-stable-stringify/esm/wrapper.js
1078
+ // ../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/810146e81bae4e3a061fe487864f2fde80c4b03b886877dc0f1fffbc6480b67e/node_modules/safe-stable-stringify/esm/wrapper.js
1079
1079
  var import__ = __toESM(require_safe_stable_stringify(), 1);
1080
1080
  var configure = import__.default.configure;
1081
1081
  var wrapper_default = import__.default;
@@ -8028,4 +8028,4 @@ export {
8028
8028
  defaultRegistry,
8029
8029
  createEngineFromEnv
8030
8030
  };
8031
- //# sourceMappingURL=chunk-P2T3IZRE.js.map
8031
+ //# sourceMappingURL=chunk-QPVJGKV7.js.map