@koda-sl/baker-cli 0.178.0 → 0.180.0-dev.e916855f4

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
@@ -528,6 +528,9 @@ baker ads google keywords add --customer-id 1234567890 --ad-group-ref g_temp_<ad
528
528
  --text "brand name, brand shop:PHRASE, brand store" --match-type EXACT
529
529
  baker ads google keyword-lists add --customer-id 1234567890 --list-ref <list-id> --file negatives.txt
530
530
  # negatives.txt: one keyword[:MATCH_TYPE] per line; blank lines and "# comments" are skipped
531
+ # Send one keyword to its own page, then put it back on the ad's — never remove + re-add for this
532
+ baker ads google keywords update <criterion-id> --customer-id 1234567890 --final-url https://example.com/solar
533
+ baker ads google keywords update <criterion-id> --customer-id 1234567890 --clear-final-url
531
534
  # Responsive search ad
532
535
  baker ads google ads create --customer-id 1234567890 --ad-group-ref g_temp_<adgroup> \
533
536
  --headlines "Fast Widgets,Buy Online,Free Shipping" --descriptions "Best widgets around.,Ships tomorrow." \
@@ -4916,7 +4919,24 @@ baker creatives publish ./canvas/<run_id>/<final>.png --title "Spring Offer 4x5"
4916
4919
 
4917
4920
  Without `--slug` the command behaves as before (one creative record per published image). With `--slug` it upserts the repo-convention row — the same one the dashboard's Creatives tab and the `src/creatives/{slug}/` folder describe — so publish, repo sync, and run history all land on a single record regardless of order.
4918
4921
 
4919
- ## Landing Quality (`baker landing`)
4922
+ ## Landing Folders (`baker landing folder`)
4923
+
4924
+ Files landing pages into folders in the company's Landings view — how it stays readable once there are more pages than fit on a screen.
4925
+
4926
+ ```bash
4927
+ baker landing folder list # folders in use, with page counts
4928
+ baker landing folder set maii-info maii-dossier --name Masters # file two pages
4929
+ baker landing folder set old-promo --none # pull a page back out
4930
+ ```
4931
+
4932
+ - **A folder is nothing but the name its pages share.** There is no folder record: filing a page under an unused name creates the folder, and the last page leaving it takes the folder with it, so an empty folder cannot exist.
4933
+ - **A folder is never part of the page's URL.** The slug (the directory under `src/pages/`) is the URL and is untouched — filing a page that is live in ads moves nothing.
4934
+ - **`set` writes both ends.** It applies immediately to pages already published, and records `folder:` in each page's `_definition.md`. That second half matters: a landing has no record in Baker until its first publish registers it, so the definition is the only channel that can file a page that has never been published. A slug with no published page is reported in `data.pendingPublish` with a hint, not as an error.
4935
+ - **The definition is read once, at first publish.** After that the user owns the filing — they move pages in the dashboard and a republish never drags them back. Editing `folder:` on a published page does nothing; use `set`.
4936
+ - **`list` before you name a folder.** A near-duplicate ("Masters" vs "Master IA") splits one group in two and undoes the point of folders. `set` emits a hint when the name it just created collides with an existing one under case, accents, or separators.
4937
+ - Names are whitespace-collapsed, at most 48 characters, and may not contain `/`.
4938
+
4939
+ ## Landing Quality (`baker landing critique`)
4920
4940
 
4921
4941
  Deterministic design-quality critic for landing pages — an offline, no-LLM detector for the well-known "AI slop" tells (gradient text, overused fonts like Inter, side-tab colored borders, cream/parchment grounds, purple-on-heading palettes, buzzword copy, broken images, and more). It scores a landing's `.astro` source and returns findings tiered **block / warn / advisory**.
4922
4942
 
@@ -5012,6 +5032,9 @@ This CLI is designed for AI agent consumption. Key patterns:
5012
5032
  - **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.
5013
5033
  - **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.
5014
5034
  - **0.178.0**: `baker ads google keywords add --final-url` is now described and nudged as the exception it is. A keyword-level final URL overrides the ad's landing page for every click on that keyword, and the flag applies to every keyword in the call — so one URL pasted across a batch restates the ad's own destination on each row while making the change review read like a per-keyword routing decision. The flag's description says to leave it unset unless the destination is keyword-specific, and staging with it returns a hint: on a batch, that the shared page belongs on the ad (`ads create --final-url`) and the keywords should be re-staged without it; on a single keyword, to keep it only where that keyword's destination differs. The review surface changed to match — a staged keyword's final URL is now shown in full on its row in the change card (it used to be visible only on a keyword *update*), so a URL that adds nothing is visible to the user rather than buried.
5035
+ - **0.179.0**: `baker ads google keywords update --final-url` / `--clear-final-url` — changing where one keyword sends its clicks no longer costs the keyword its history. The write contract has always accepted `finalUrls` on a keyword update (`baker capabilities google-ads` reported it as a field of `google.keyword.update`), but no CLI flag reached it, so an agent that followed capabilities got "update needs at least one field" and the only route left was remove + re-add — a new criterion to Google, learning from zero, for a change that was only ever about the destination. `--clear-final-url` stages the empty set Google reads as "inherit the ad's landing page", which is the undo for the misuse `--final-url` already warns about. Same nudge as `add` on the way in.
5036
+
5037
+ A keyword's final URL now also shows on its row when the keyword is being **removed**. 0.178.0 read it from the stage-time snapshot, but the snapshot never asked Google for it, so the removal half of that change silently did nothing — the card said which keyword was going away but not which landing page it had been sending its clicks to. The batch hint says outright that `--final-url` has no per-keyword form rather than implying destinations could differ within one call, and the tool doc now lists each `keywords` subcommand's own flags instead of one shared set (`--ad-group-ref`, `--text` and `--match-type` are `add`-only; `remove` takes a target and nothing else).
5015
5038
 
5016
5039
  ## Publishing
5017
5040
 
@@ -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