@koda-sl/baker-cli 0.286.0-dev.93be96120 → 0.287.0-dev.4a5d811dc
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 +34 -45
- package/dist/cli.js +5503 -5732
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1260,10 +1260,21 @@ baker analytics conversions # what counts as a conversion, and wha
|
|
|
1260
1260
|
baker analytics conversions --candidates # everything these pages do, and which of it is counted
|
|
1261
1261
|
baker analytics conversions --event page:request_demo --name "Demo requested"
|
|
1262
1262
|
baker analytics conversions --remove page:request_demo
|
|
1263
|
+
baker analytics sends # where each conversion is sent, and what happened to each upload
|
|
1264
|
+
baker analytics sends --set --conversion "Deal won" --platform meta --tag <tagId> --event Purchase # staged; spends budget once published
|
|
1265
|
+
baker analytics sends --remove s1 # stop one, when the chat is published
|
|
1263
1266
|
baker analytics delivery --page 2 # the next page of a long list
|
|
1264
1267
|
baker analytics submissions --flow contact # every attempt at a Form, delivered or not
|
|
1268
|
+
baker analytics sources # the addresses the client's own tools send events to
|
|
1269
|
+
baker analytics sources --create --name HubSpot # a new address, learning; hand the URL to the user
|
|
1270
|
+
baker analytics sources --map --source <id> --field email=contact.email --field value=deal.amount
|
|
1271
|
+
baker analytics sources --create-hubspot --property lifecyclestage --value customer # a source Baker reads from HubSpot every five minutes
|
|
1265
1272
|
```
|
|
1266
1273
|
|
|
1274
|
+
**Sources: a URL for a tool that cannot be told what to post.** `sending-events` is the contract a developer writes to; `sources` is for a CRM webhook, an automation step or a billing notification that posts whatever it posts. A new source is `learning`: its first send is kept as a masked sample and counted as nothing until `--map` confirms which path holds which field (`--field` repeats; a body already in Baker's own format needs none). From then on every arrival is an ordinary event carrying `baker_source: <name>`, and `--pause`, `--resume` and `--remove` are by `--source <id>`.
|
|
1275
|
+
|
|
1276
|
+
**`--create-hubspot --property <name> --value <value> [--name <name>]`: a source Baker reads, for a HubSpot plan that cannot send a webhook.** Every five minutes the connected account is checked and every contact whose property becomes that value is one event named after the source (default: the value's label), carrying `baker_source`, the contact's email, phone, id, name, address and HubSpot's `gclid` / `fbclid`. Names come from `baker hubspot contacts properties`. Counts from creation on, never earlier contacts; a contact already at the value counts only when it leaves and re-enters. `counting` at once, nothing to map, no URL; the source carries `hubspot { property, propertyLabel, value, valueLabel }`, `countingSince`, `lastPolledAt` and `lastError`. Deals are not watched — sync the outcome onto the contact and read it there.
|
|
1277
|
+
|
|
1267
1278
|
Shared flags: `--days <n>` (default 30) or `--start-date` / `--end-date` (`YYYY-MM-DD`, end inclusive); `--full`; `--compare` to also return the preceding window of the same length; `--timezone <IANA>` and `--granularity hour|day|week|month` to shape a series. Every call reads live — there is no cache layer, so there is nothing to bypass.
|
|
1268
1279
|
|
|
1269
1280
|
**`--output md` when the numbers are going to be read.** Every report is emitted as compact JSON by default, and `--output md` renders each block as a Markdown table instead — one header rather than the keys repeated on every row, with the identical `warnings` and `hints`. Measured across all sixteen presets against a real deployment, the two changes together take the family from 110 KB to 44 KB — **about 60% smaller overall**, 38% at worst (`ads`, which is dense numeric data with nothing to prune) and 77% at best (`landings`). Three further economies apply to both formats, because the reader is usually an LLM with a context window rather than a browser:
|
|
@@ -1276,6 +1287,8 @@ Shared flags: `--days <n>` (default 30) or `--start-date` / `--end-date` (`YYYY-
|
|
|
1276
1287
|
|
|
1277
1288
|
**A conversion is a company-level named event, not a property of a Form.** `baker analytics conversions` is the only place "what counts" is decided, and an empty `definitions` list means every conversion number in every other report is zero — not because nobody converted, but because nothing is named as an outcome and Baker never guesses which event is the point of a page. `--candidates` lists every event these pages actually produced — each Form step and trigger, every `data-baker-*` event, every outbound destination — with volume and whether it is already counted; that list is where an event key comes from, so never invent one. `--event <key> --name "Booked a call"` starts counting it **immediately and retroactively**: the whole stored history is rescored, so naming the right event today also fixes last month. Nothing here is staged and publishing is not involved. Two events given the same `--name` become one row and one number, which is how a call booked on three different Forms reads as one outcome. `--count-mode every_time` is for the outcomes people genuinely repeat (a guide downloaded twice is two downloads); the default counts once per visit. One outcome per call — repeating `--event` is refused rather than silently keeping the last one.
|
|
1278
1289
|
|
|
1290
|
+
**A send belongs to the conversion, and `baker analytics sends` is where it is set.** "Deal won" → Meta Purchase means every event counted as "Deal won" — from any Form, page or the client's own systems — is uploaded to Meta with who the person is, the click that brought them and the amount forwarded automatically; there is nothing to map, and a send is never set on a funnel step (the step reads its conversions' sends back as `sends[]`). With no flags it lists the sends in effect with `sends[].outcome` ("12 sent · 1 failed", `lastError` in the platform's words), `platforms[]` with the tags on Tracking that can send, and `conversions[]`, the names that can be sent. `--set --conversion <name> --platform <p> --tag <tagId> --event <e>` **stages** a send: it lands when the chat is published, and from then on it **spends ad budget** on every event under the name — say so plainly; never call a published send a preview. `--tag` is copied from `platforms[].tags[].tagId`, which lists only the tags that hold their server credential — one typed from memory is refused, and a platform with none says what to add on Tracking. A name nothing is counted as is refused too. `--remove <id>` stops one, staged the same way. `warnings[]` names a Form that already sends the same thing itself, so the platform would count it twice.
|
|
1291
|
+
|
|
1279
1292
|
**Campaign parameters are per platform, and so are the numbers.** `baker analytics ads` returns `adPlatformTraffic` (visits, conversions and *tagged* visits for each of the nine platforms), `adParams` for the platform in `--platform` only, and `adParamsUnattributed` for names arriving on visits with no click id and no recognisable source. Read `adPlatformTraffic` first: a platform with visits and zero tagged visits has untagged ad URLs, and no mapping can fix that from Baker's side. `baker analytics map` needs `--platform` for any change, accepts a parameter name **nobody has sent yet** — which is how a tracking template is configured before its campaign runs — and takes an answer back with `--remove`. `--set <name>=ignore` says a parameter is not campaign information at all, so Baker stops storing it and stops listing it.
|
|
1280
1293
|
|
|
1281
1294
|
**Mapping is staged on the chat, not applied.** Answers appear under **Campaign parameters** in the dashboard and take effect when the chat is published; a discard takes them all back, and a report run in the same turn still reads the old vocabulary. Reading the mapping back inside the chat *does* include what it has staged, so a second turn never re-maps the same parameter.
|
|
@@ -1309,47 +1322,38 @@ Data is retained for 400 days; a window reaching further back comes back with a
|
|
|
1309
1322
|
|
|
1310
1323
|
---
|
|
1311
1324
|
|
|
1312
|
-
###
|
|
1325
|
+
### The company's funnel (`baker funnel`)
|
|
1313
1326
|
|
|
1314
|
-
|
|
1327
|
+
The steps a company's business actually has, wired as a graph, joined to the moments Baker already records — and counted in PEOPLE on every read. It is what makes "how far did the people from this campaign get" answerable: a conversion count can say how many converted, never how many reached Meeting booked and where the rest stopped.
|
|
1315
1328
|
|
|
1316
1329
|
```bash
|
|
1317
|
-
baker
|
|
1318
|
-
baker
|
|
1319
|
-
baker
|
|
1320
|
-
baker
|
|
1321
|
-
baker
|
|
1322
|
-
baker
|
|
1323
|
-
baker experiment finish --id a3f91c2b # send all the traffic to whichever won
|
|
1324
|
-
baker experiment finish --id a3f91c2b --abandon # stop it, keep the page as it is, record no result
|
|
1325
|
-
baker experiment fold # make the page's own content the version that won
|
|
1330
|
+
baker funnel # the funnel this company has, with 30 days of people per step
|
|
1331
|
+
baker funnel --days 90 # a longer window
|
|
1332
|
+
baker funnel --moments # everything a trigger can name — read before writing any trigger
|
|
1333
|
+
baker funnel --templates # the step library and the presets
|
|
1334
|
+
baker funnel --preset b2b_sales # draw the closest graph
|
|
1335
|
+
baker funnel --steps '[{"key":"lead","displayName":"Enquiry","stepClass":"lead","after":[],"triggers":[{"id":"t1","kind":"conversion","eventKey":"submit:contact"}]}]'
|
|
1326
1336
|
```
|
|
1327
1337
|
|
|
1328
|
-
**
|
|
1338
|
+
**Every step comes back counted.** `people` reached it, `atLeast` got at least that far (the step or anything after it), `before` got as far as the step before, `share` is `atLeast / before`, and `money` is what its events said they were worth, by currency. `window` says which days, `worstStep` names where the most people are lost, and the hints say it in a sentence. Counted in people across visits and devices — a landing viewed on a phone, a Form completed on a laptop and a deal a CRM posted under its own id are one person when a contact digest joins them — and unordered, so a deal the CRM posted before the site's submit landed still counts. Never quote these as visits.
|
|
1329
1339
|
|
|
1330
|
-
**
|
|
1340
|
+
**The vocabulary is the company's; the stage is closed.** A company invents its own steps and names them whatever it calls them — `displayName` is unconstrained. `key` is the stable identifier (lowercase letters, digits, underscores) and never changes when the name does. Every step also carries one of six stages — `visited`, `lead`, `engaged`, `customer`, `repeat`, `lost` — plain words every business has, used to compare one company's funnel with another's.
|
|
1331
1341
|
|
|
1332
|
-
|
|
1342
|
+
**It is a graph, not a ladder.** `after` is a list of predecessors, so two ways into one step is ordinary rather than an anomaly — people arrive by seeing the landing *or* by asking for a demo. An empty `after` means people come in there. A cycle or a predecessor that does not exist is refused with the step named.
|
|
1333
1343
|
|
|
1334
|
-
|
|
1344
|
+
**`--steps` replaces the whole funnel.** Read first and restate everything you want to keep. `--preset` is the shortcut for a company that has none.
|
|
1335
1345
|
|
|
1336
|
-
|
|
1346
|
+
**A step says what triggers it, and every trigger is PICKED from `--moments`.** `triggers` is a list and any one of them firing is enough. Two kinds and no third: `{"kind":"conversion","eventKey":"submit:contact"}` for an outcome the company counts (`moments.conversions`), and `{"kind":"landing","landingId":"<id>"}` for a landing being viewed (`moments.landings`). A moment nobody has named yet — a Form step, a `data-baker-*` event, an event a client's system posts (`moments.events`, `moments.sources`) — is named as a conversion first with `baker analytics conversions --event <key> --name <name>` and then used; the funnel has no vocabulary of its own for an unnamed moment, which is what keeps it and every conversion report agreeing. `moments.events[].origin` tells a page's event (`site`) from one the client's own systems sent (`systems`); `moments.sources` lists the client's systems by the `page:<name>` key each raises, pickable before the first send arrives. A key typed from memory saves, publishes and counts nobody forever — the hint "has a trigger and nobody reached it while N people entered" is that mistake caught after the fact. A step written with `offlineConversions` or an `analytics` trigger is refused before any round trip, with where each now lives.
|
|
1337
1347
|
|
|
1338
|
-
|
|
1339
|
-
|---|---|
|
|
1340
|
-
| `leads` (default) | Whatever this company already marks as a conversion |
|
|
1341
|
-
| `leads:<form>` | The same, one Form only |
|
|
1342
|
-
| `event:<name>` | An event the page declares for itself (`data-baker-click="request_demo"`) |
|
|
1343
|
-
| `event:<name>/<key>=<value>` | The same, narrowed by one of its own properties |
|
|
1344
|
-
| `click` / `click:<host>` | Leaving the site, optionally for one destination |
|
|
1348
|
+
**A landing being viewed can be a step** — where most funnels start — and never a conversion: `baker analytics conversions` refuses a `view:` key.
|
|
1345
1349
|
|
|
1346
|
-
|
|
1350
|
+
**A step nothing enters advances nobody, silently.** No `triggers` — or one whose narrowing was never filled in — means the report shows zero, nothing errors, and it looks exactly like a stage people never reach. Every response names these in `hints`. **There is no default door either:** a Form is counted only where a trigger names it, and `hints` names every Form no step counts.
|
|
1347
1351
|
|
|
1348
|
-
**
|
|
1352
|
+
**Where a step sends is read here and set on the conversion.** Every step comes back with `sends[]` — one entry per send of every conversion among its triggers (`id`, `conversionName`, `platform`, `event`, and `outcome`: "12 sent · 1 failed", or "Nothing sent yet"). It is read-only: a send belongs to the conversion by name, so it is set with `baker analytics sends --set --conversion <name> --platform <p> --tag <tagId> --event <e>`, never on a step, and a step written with `offlineConversions` is refused with that command named. A send that failed is named in the hints (`SEND FAILED on step <key>: …`) with the platform's own error.
|
|
1349
1353
|
|
|
1350
|
-
|
|
1354
|
+
**Staged, and published in one piece.** The funnel becomes the company's when the chat is published; discarding takes it back. The client sees what you staged on the funnel canvas inside the chat and can edit it there — the same canvas, the same publish — so read the funnel back before restating it. The version it replaces is kept, so removing a step does not remove what was measured under it.
|
|
1351
1355
|
|
|
1352
|
-
|
|
1356
|
+
---
|
|
1353
1357
|
|
|
1354
1358
|
### Google Analytics 4 (`baker ga4`)
|
|
1355
1359
|
|
|
@@ -3438,7 +3442,7 @@ baker history list --category ads --full # ad writes with raw metadata
|
|
|
3438
3442
|
|
|
3439
3443
|
---
|
|
3440
3444
|
|
|
3441
|
-
### `baker hubspot forms list | view | submissions` · `baker hubspot workflows list | view` · `baker hubspot pipelines list` · `baker hubspot contacts summary | lookup` · `baker hubspot meetings list | view`
|
|
3445
|
+
### `baker hubspot forms list | view | submissions` · `baker hubspot workflows list | view` · `baker hubspot pipelines list` · `baker hubspot contacts summary | lookup | properties` · `baker hubspot meetings list | view`
|
|
3442
3446
|
|
|
3443
3447
|
Read-only view of the company's **connected HubSpot account** — the forms and meeting links (calendars) that live there, the leads those forms received, and the workflows, deal pipelines, and contact records a lead runs through afterwards. Fetched from HubSpot live on every call; nothing is cached and nothing is ever written back.
|
|
3444
3448
|
|
|
@@ -3459,6 +3463,7 @@ baker hubspot workflows view <workflowId> --full # + the values each branch com
|
|
|
3459
3463
|
baker hubspot pipelines list # where a lead lands, and the stages after it
|
|
3460
3464
|
baker hubspot contacts summary --days 30 # are recent leads being worked? (counts only)
|
|
3461
3465
|
baker hubspot contacts lookup lead@example.com # was this one lead picked up?
|
|
3466
|
+
baker hubspot contacts properties # what a HubSpot-read source can watch, with each value
|
|
3462
3467
|
baker hubspot meetings list # every calendar + whether booking redirects
|
|
3463
3468
|
baker hubspot meetings view <slug> # booking-form fields for one calendar
|
|
3464
3469
|
baker hubspot meetings view <slug> --as-node # the form.external blob for a flow node
|
|
@@ -3472,6 +3477,7 @@ baker hubspot meetings view <slug> --as-node # the form.external blob for a f
|
|
|
3472
3477
|
**`pipelines list`:** no flags; stages come back in the order they run in HubSpot.
|
|
3473
3478
|
**`contacts summary` flags:** `--days <1-365>` (default 30) over contact creation date. Returns counts only — total contacts, how many have an owner, and the lifecycle-stage split — never a name or an email. `truncated: true` means the page budget ran out, so the counts are floors.
|
|
3474
3479
|
**`contacts lookup`:** positional `<email>`; returns that contact's lifecycle stage, owner, the account (company) it was filed under, and associated deals with their stage. `dealsReadable: false` / `companyReadable: false` mean the connection does not cover deals or accounts, so an empty `deals` or a null `company` is unknown rather than absent. `found: false` means no contact with that email — the lead never reached the CRM, which is a finding rather than an error.
|
|
3480
|
+
**`contacts properties`:** no flags; returns every contact property with a closed list of values — `{ name, label, groupName, options: [{ value, label }] }` — the ones a HubSpot-read source can watch. Names are HubSpot's internal ones; hand them to `baker analytics sources --create-hubspot --property <name> --value <value>` as printed. No personal data.
|
|
3475
3481
|
**`meetings list` flags:** `--search <text>`, `--redirecting-only`.
|
|
3476
3482
|
**`meetings view`:** positional `<slug>` — the only reliable source for a calendar's booking fields (HubSpot omits `customParams` from the list endpoint, so `fieldCount: null` there means unknown, not zero); `--as-node` as above.
|
|
3477
3483
|
|
|
@@ -5828,23 +5834,6 @@ baker landing inspiration scrape <url> --out .baker/<dir> # capture any page n
|
|
|
5828
5834
|
- **Inspiration, never a clipboard.** A reference's structure and mechanism are the lesson; write the client's own words.
|
|
5829
5835
|
- **A capture has to land in `.baker/`, and `scrape` refuses anywhere else in a workspace.** `--out` is free-form and the directory is the agent's choice, but a capture is tens of MB of lossless PNG whose individual files clear every size gate — written to a tracked directory it becomes a permanent part of the client's repo, force-cloned into every later session. `.baker/` is gitignored precisely so a capture costs nothing. The refusal is scoped to the workspace: outside a git work tree — a local run against `/tmp` — any path is allowed, because there is no history to bloat.
|
|
5830
5836
|
|
|
5831
|
-
### `baker landing variant`
|
|
5832
|
-
|
|
5833
|
-
Creates a new **version of a page** for an A/B test. It **imports the page's sections** and owns its own copy of only what you fork, so the two versions differ by exactly the hypothesis.
|
|
5834
|
-
|
|
5835
|
-
```bash
|
|
5836
|
-
baker landing variant oferta --fork Hero.astro
|
|
5837
|
-
baker landing variant oferta --fork Hero.astro,Proof.astro --because "60% of visitors never scroll past the hero" --change "put the price in the headline"
|
|
5838
|
-
```
|
|
5839
|
-
|
|
5840
|
-
- **You do not choose the slug.** It is `<page>--<n>`, numbered per page and never reused — so it cannot collide with an archived version, and it cannot merge with an older variant's analytics history. `--` is also what every tool decides by: which analytics id the page stamps, whether the sitemap lists it, and whether the edge answers the URL. It replaced a `<page>-b` convention that nine live pages across the fleet would have been misread by.
|
|
5841
|
-
|
|
5842
|
-
- **Sharing, not copying, is what keeps the test honest.** A duplicated page drifts from its control on every later edit — a price, a typo, a new testimonial — and the test then reports that drift plus the hypothesis as one result. A shared section is one file, so an edit reaches both arms at once. Baker follows the sharing too: both pages redeploy together, and both are re-critiqued when a shared section changes.
|
|
5843
|
-
- **Relative paths are re-aimed, not string-patched.** Every specifier in the copied files is resolved and re-relativised, so `index.astro` and a forked `_components/Hero.astro` — which sit at different depths — both keep pointing at the right thing, including the control's `_images/` and shared wrappers like `src/components/Video.astro`.
|
|
5844
|
-
- **`_definition.md` is the page's**, with a fresh quoted `internalId`, a `(variant)` suffix on `internalTitle`, and a note recording what differs, the observation behind it and the change. `metaTitle`/`metaDescription` stay identical on purpose: the variant is served under the control's URL.
|
|
5845
|
-
- **Errors carry the retry.** `NOT_FOUND` returns `availableSlugs` (wrong page) or `availableComponents` (wrong `--fork`); `INVALID_SLUG` fires when the slug is not a page, or is already a variant of one. Omitting `--fork` succeeds with a hint saying the page currently renders exactly the control, so a test between them cannot conclude.
|
|
5846
|
-
- **Run `baker experiment plan` first.** Most pages never get the traffic to settle most questions.
|
|
5847
|
-
|
|
5848
5837
|
### `baker landing critique`
|
|
5849
5838
|
|
|
5850
5839
|
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**.
|