@koda-sl/baker-cli 0.408.0 → 0.410.0-dev.53b577900

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.
Files changed (26) hide show
  1. package/README.md +31 -23
  2. package/dist/{chunk-N72QYDJY.js → chunk-ISCXDKWB.js} +25 -8
  3. package/dist/{chunk-N72QYDJY.js.map → chunk-ISCXDKWB.js.map} +1 -1
  4. package/dist/{chunk-5X5LUMVV.js → chunk-ISJOVAJP.js} +2 -2
  5. package/dist/{chunk-WQE4SRLY.js → chunk-S5BY45H7.js} +3 -3
  6. package/dist/{chunk-KMCVSEGU.js → chunk-TI5KBK4V.js} +6 -6
  7. package/dist/chunk-TI5KBK4V.js.map +1 -0
  8. package/dist/{chunk-X37GPJ54.js → chunk-VKWHVMMD.js} +3 -3
  9. package/dist/{chunk-3ZN2DUBU.js → chunk-ZIOW64BL.js} +4 -4
  10. package/dist/cli.js +1290 -904
  11. package/dist/cli.js.map +1 -1
  12. package/dist/{client-BPKOHQWA.js → client-AY6TPFQ4.js} +4 -4
  13. package/dist/engine/index.js +3 -3
  14. package/dist/{env-B7V77K6K.js → env-CWEGTJF3.js} +2 -2
  15. package/dist/{output-5M2QPMKZ.js → output-EPF3HHJ4.js} +5 -5
  16. package/dist/{shared-OEQKSDWO.js → shared-53G34I3U.js} +6 -6
  17. package/package.json +1 -1
  18. package/dist/chunk-KMCVSEGU.js.map +0 -1
  19. /package/dist/{chunk-5X5LUMVV.js.map → chunk-ISJOVAJP.js.map} +0 -0
  20. /package/dist/{chunk-WQE4SRLY.js.map → chunk-S5BY45H7.js.map} +0 -0
  21. /package/dist/{chunk-X37GPJ54.js.map → chunk-VKWHVMMD.js.map} +0 -0
  22. /package/dist/{chunk-3ZN2DUBU.js.map → chunk-ZIOW64BL.js.map} +0 -0
  23. /package/dist/{client-BPKOHQWA.js.map → client-AY6TPFQ4.js.map} +0 -0
  24. /package/dist/{env-B7V77K6K.js.map → env-CWEGTJF3.js.map} +0 -0
  25. /package/dist/{output-5M2QPMKZ.js.map → output-EPF3HHJ4.js.map} +0 -0
  26. /package/dist/{shared-OEQKSDWO.js.map → shared-53G34I3U.js.map} +0 -0
package/README.md CHANGED
@@ -3080,6 +3080,23 @@ baker testimonials search "competitor support" --source trustpilot --sentiment n
3080
3080
  baker testimonials search "[product/offer] for [persona] that delivers [USP/outcome]" --rating-min 4 --language en --output md --full --limit 12
3081
3081
  ```
3082
3082
 
3083
+ ### `baker testimonials sources`
3084
+
3085
+ The review pages testimonials are imported from — Google Maps, Trustpilot, Yelp, Tripadvisor, G2, Capterra, Amazon, App Store, Google Play. Adding or removing one applies immediately and shows on the chat as an already-applied change.
3086
+
3087
+ | Command | What it does |
3088
+ | --- | --- |
3089
+ | `baker testimonials sources add <link \| business name> [--platform <site>] [--every never\|daily\|weekly\|monthly]` | Recognise the page from its link (a business name finds a Google Maps listing) and import its reviews in the background. Idempotent. |
3090
+ | `baker testimonials sources list` | Every source: id, site, status, reviews imported, auto-sync. |
3091
+ | `baker testimonials sources sync <id>` | Import new reviews now. |
3092
+ | `baker testimonials sources schedule <id> --every <interval>` | Set the auto-sync cadence. |
3093
+ | `baker testimonials sources remove <id>` | Remove the source and every testimonial it imported. |
3094
+
3095
+ ```bash
3096
+ baker testimonials sources add https://www.trustpilot.com/review/acme.com
3097
+ baker testimonials sources add "Acme Bakery Madrid"
3098
+ ```
3099
+
3083
3100
  ### `baker testimonials get <id>`
3084
3101
 
3085
3102
  Get a single testimonial by ID.
@@ -3237,11 +3254,11 @@ Search the **ad-dna** corpus of scored "winning" competitor ads for reference cr
3237
3254
 
3238
3255
  ### `baker winning-ads search <query>`
3239
3256
 
3240
- Semantic search (dense recall + BM25 + rerank) → `POST /api/ad-library/winners/search`. The CLI projects each result to a **lean, decision-focused** shape so the agent's context stays small — default fields: `advertiser`, `advertiser_id`, `platform`, `format`, `relevance`, `winner_score`, `summary` (what the ad is about), `media_url`, and `visual` (what the viewer sees) + `container` (a static's layout pattern) + `mechanism` (why it works) when known; plus top-level `pool_size`, `match_confidence` (`high|medium|low`), and `below_floor_count` (matches dropped under the relevance floor). `--full` adds DNA detail (`angle`, `target_persona`, `hook_archetype`, `awareness_stage`, `industry`) + longevity (`days_active`, `reach`, `active`, `winner_category`, `media_kind`). `--output json` (default) returns the lean objects; `--output md` prints a table.
3257
+ Semantic search (dense recall + BM25 + rerank) → `POST /api/ad-library/winners/search`. The CLI projects each result to a **lean, decision-focused** shape so the agent's context stays small — default fields: `advertiser`, `advertiser_id`, `platform`, `format`, `relevance`, `winner_score`, `summary` (what the ad is about), `media_url`, and `visual` (what the viewer sees) + `container` (a static's layout pattern) + `mechanism` (why it works) when known; plus top-level `pool_size`, `match_confidence` (`high|medium|low`), and `below_floor_count` (matches dropped under the relevance floor). The proof signals ride along (`winner_category`, `days_active`, `active`); `--full` adds DNA detail (`angle`, `target_persona`, `hook_archetype`, `awareness_stage`, `industry`) + `reach` and `media_kind`. By default the search draws from the brands the company follows first (`--scope followed-first`) and tops up from the shared library; every result says `source: followed|library`. `--output json` (default) returns the lean objects; `--output md` prints a table.
3241
3258
 
3242
3259
  > `media_url` is the creative itself: for `static` it's the image, for `video` it's the video file. ad-dna stores **no separate poster** for videos, so a video result has only the video URL.
3243
3260
 
3244
- > Results are already de-junked and winner-first: the corpus auto-drops measured failures (duds, thrash) and ranks `final = relevance^0.25 × winner_score^0.75`. So `--winner-category winner` is usually unnecessary — it's a stricter filter that also removes `untested` (unproven-but-maybe-fresh) ads. Rely on the default + `--min-relevance`; only add `--winner-category` to force-exclude unproven creatives. (With a query, restricting to a single `--advertiser-id` turns the failure-exclusion and the per-advertiser cap off: you then see that brand's duds too. A search with no query is a browse, which always drops failures and `untested` ads. A company that follows one brand keeps both with `--scope followed`/`followed-first`.)
3261
+ > Results are already de-junked and winner-first: the corpus auto-drops measured failures (duds, thrash) and ranks `final = relevance^0.5 × winner_score^0.5`, so a direct match beats a stronger-but-tangential ad. So `--winner-category winner` is usually unnecessary — it's a stricter filter that also removes `untested` (unproven-but-maybe-fresh) ads. Rely on the default + `--min-relevance`; only add `--winner-category` to force-exclude unproven creatives. (With a query, restricting to a single `--advertiser-id` turns the failure-exclusion and the per-advertiser cap off: you then see that brand's duds too. A search with no query is a browse, which always drops failures and `untested` ads. A company that follows one brand keeps both with `--scope followed`/`followed-first`.)
3245
3262
 
3246
3263
  ```bash
3247
3264
  # Only LinkedIn video ads:
@@ -3260,6 +3277,8 @@ baker winning-ads search --format-pattern comparison_table --platform meta --out
3260
3277
  |---|---|
3261
3278
  | `query` (positional) | Free-text natural-language query |
3262
3279
  | `--ref-ad-id <id>` | Find ads similar to this ad (instead of free text) |
3280
+ | `--scope followed-first\|followed\|all` | Which brands to draw from (default `followed-first`: the company's followed brands, topped up from the shared library; the whole library when it follows nobody) |
3281
+ | `--sort winner\|longest_running\|newest` | Order of a no-query browse (default `winner`); ignored when a query is given |
3263
3282
  | `--limit <n>` | Max results 1–100 (**default 10** — shortlist size) |
3264
3283
  | `--max-per-advertiser <n>` | Cap results per advertiser 0–50 (default 3; `0` = no cap) |
3265
3284
  | `--min-relevance <0-1>` | Relevance floor; trims weak matches |
@@ -3287,7 +3306,7 @@ Read the **winner-weighted rollup** of the ad-dna corpus → `GET /api/ad-librar
3287
3306
  mechanism / static-format patterns that actually win, ranked by `winner_score` among the ads of the segment you
3288
3307
  pass (`--platform`, `--awareness`, `--industry`; an unset one means any), keyed on the closed seeds/enums the
3289
3308
  extractor emits (no clustering). Ground a creative in a **proven** pattern for the exact segment instead of
3290
- guessing. The rollup is recomputed daily. (Distinct from `winning-ads patterns`, which contrasts two ad cohorts.)
3309
+ guessing. The rollup is recomputed daily.
3291
3310
 
3292
3311
  ```bash
3293
3312
  # Top winning hook seeds for problem-aware SaaS on Meta:
@@ -3390,24 +3409,6 @@ Stop following a brand by advertiser id → `POST /api/ad-library/unfollow`. Ret
3390
3409
  baker winning-ads unfollow adv_123
3391
3410
  ```
3392
3411
 
3393
- ### `baker winning-ads brief`
3394
-
3395
- Generate a creative brief grounded in strategically-similar winners → `POST /api/ad-library/brief`. Optionally describe the target creative with `--dna` (a JSON object), steer with `--notes`, and cap references with `--k`. Returns `brief_markdown` + `reference_ad_ids`.
3396
-
3397
- ```bash
3398
- baker winning-ads brief --dna '{"angle":"cost savings","awareness_stage":"solution_aware"}' --notes "B2B, LinkedIn video" --k 8
3399
- ```
3400
-
3401
- ### `baker winning-ads patterns --winners <adIds> --duds <adIds>`
3402
-
3403
- Mine what separates two cohorts of ads → `POST /api/ad-library/patterns`. Pass a comma-list of winning ad ids (`--winners`, cohort A) and weaker/dud ad ids (`--duds`, cohort B), plus optional `--top-n`. Returns each discriminating DNA field with the cohort it `favors` (`winners`/`duds`), a `score`, and the top values on each side.
3404
-
3405
- ```bash
3406
- baker winning-ads patterns --winners a_1,a_2,a_3 --duds a_9,a_8 --top-n 10 --output md
3407
- ```
3408
-
3409
- ---
3410
-
3411
3412
  ### Scheduled agents (`baker scheduled-agents`)
3412
3413
 
3413
3414
  A Scheduled agent is a **trigger** and a **prompt**. When the trigger fires, a Session opens with that prompt and does the job — there is no Task in between. `create`, `update`, and `delete` stage draft ops on `BAKER_CHAT_ID` and apply when the chat is published. `unstage` takes back what this chat staged on one item before Publish (`--index <n>` for one change only, by its index in `list` → `draft.ops`). `list` and `get` include draft state when `BAKER_CHAT_ID` is set; `get` on a published item also returns `runsAs` — who each run acts as and which tools it loads. `run` fires a published item immediately and does not require a chat id. Publishing every run unreviewed is switched on by the user on the item, never through the CLI: `--auto-publish` is refused with an `ask_user` fix.
@@ -5737,7 +5738,7 @@ The two scaffold passes are billed (the full `video_deconstruct` is the heavy on
5737
5738
 
5738
5739
  **New-creative limit:** a chat makes at most 10 new creatives. The eleventh `scaffold-static-ad`, `scaffold-ad` or `scaffold-video` with a new `--slug` exits 2 with `CREATIVE_LIMIT` before any billed pass. Re-scaffolding a creative already made in the chat, or one whose `_definition.md` existed before, does not count.
5739
5740
 
5740
- `<image>` is a **local path or an `http(s)` URL** (e.g. a presigned S3 link from ad-dna). With `--slug` the source is localized to durable, content-addressed storage before anything bills, so the emitted canvas stays runnable in any sandbox; without a slug a URL is passed straight into the `original` ingest node (a presigned one expires). For the URL case, `--out` defaults to the current directory — pass `--out` to scaffold two URL sources into distinct files, or the second run overwrites the first.
5741
+ `<image>` is a **local path or an `http(s)` URL** (e.g. a presigned S3 link from ad-dna), and it is **optional when `--source-ad-id` is given**: the library's picture is used. With `--slug` the source is localized to durable, content-addressed storage before anything bills, so the emitted canvas stays runnable in any sandbox; without a slug a URL is passed straight into the `original` ingest node (a presigned one expires). For the URL case, `--out` defaults to the current directory — pass `--out` to scaffold two URL sources into distinct files, or the second run overwrites the first.
5741
5742
 
5742
5743
  Turn a source/inspiration image into a **runnable, self-validated static-ad canvas** — the static counterpart of `scaffold-video`. It runs **billed Gemini passes** up front:
5743
5744
 
@@ -5759,7 +5760,11 @@ The canvas is validated before it is written. stdout returns `{ ok, canvas_path,
5759
5760
 
5760
5761
  The render prompt is **model-aware**: for **gpt-image** the exclusions (no invented badges/watermark) are hoisted to the very *end* of the prompt where the model reads them as pure exclusions rather than composition. Run `baker canvas critique` on the scaffolded canvas for an advisory read before you spend.
5761
5762
 
5763
+ **A reference from the winners library goes in by id.** `--source-ad-id <ad_id> --source-platform meta|linkedin|tiktok [--source-advertiser-id <id>]` (the ids beside the ad in `baker winning-ads search`) fetches the library's structured read of that ad, the same one `baker winning-ads content <ad_id> --full` returns, and writes it into `prompt.json` as a `reference` block: `{ ad_id, platform, advertiser, advertiser_id, format_pattern, layout: { composition, focal_subject, aspect_ratio, background, palette_mood, colors, production, aesthetic, cta_in_image, markers }, message: { mechanism, awareness, sophistication, angle, hook_archetype, pain_points, desires, value_props, objections_handled, proof, risk_reversal }, offer: { offer_type, pricing_model, entry_price, no_credit_card, guarantee, guarantee_days, urgency, bonus }, text_blocks: [{ text, role, position }], copy: { primary_text, headline, cta } }` (filled fields only). The describe pass still reads the pixels but is told that read as grounding, so the two cannot disagree on the container or the mechanism; when it names no `winning_mechanisms`, the library's container + mechanism seeds the list. The source ids (`sourceAdId`, `sourcePlatform`, `sourceAdvertiserId`, and `sourceAdvertiser` from the library when `--advertiser` is absent) are written to `_definition.md` and synced to the creative at scaffold time. A library read that fails warns, records the ids anyway and scaffolds from the picture alone (`hints`: `REFERENCE NOT READ`); with no picture either, the command exits 2 (`source_unavailable`). `baker canvas sync-definition` re-reads the three ids from `_definition.md` on every sync.
5764
+
5762
5765
  ```bash
5766
+ baker canvas scaffold-static-ad --source-ad-id adg_123 --source-platform meta --slug spring-offer --platform meta
5767
+ # → the library's picture and read; writes src/creatives/spring-offer/{spring-offer.canvas.json, prompt.json, _definition.md}
5763
5768
  baker canvas scaffold-static-ad ./reference-ad.png --context "competitor ad for <brand>, <category>, <market>"
5764
5769
  # → writes ./static-ad.canvas.json + ./prompt.json
5765
5770
  # edit prompt.json into your ad + replace each [TODO] ingest path with a real file, then:
@@ -5769,7 +5774,10 @@ baker canvas run ./static-ad.canvas.json
5769
5774
 
5770
5775
  | Flag | Default | Effect |
5771
5776
  |---|---|---|
5772
- | `--context <text>` | — | Known provenance (advertiser, category, market) to ground the describe. |
5777
+ | `--source-ad-id <ad_id>` | — | The winners-library ad this creative adapts. Fetches its structured read into `prompt.json` `reference`, grounds the describe pass with it, and records the source on the creative. Makes `<image>` optional. |
5778
+ | `--source-platform <platform>` | `meta` | Platform the library ad is on: `meta`\|`linkedin`\|`tiktok`. Another value exits 2 (`invalid_source_platform`). |
5779
+ | `--source-advertiser-id <id>` | the library's | The reference's library advertiser id, recorded as `sourceAdvertiserId`. |
5780
+ | `--context <text>` | — | Known provenance (advertiser, category, market) to ground the describe. Appended to the library's read when `--source-ad-id` is given. |
5773
5781
  | `--out <path>` | `<image-dir>/static-ad.canvas.json` (cwd when `<image>` is a URL) | Where to write the canvas (`prompt.json` is written alongside). |
5774
5782
  | `--slug <slug>` | — | Creative slug (lowercase kebab): writes the canvas to `src/creatives/<slug>/<slug>.canvas.json` — the repo convention that attaches every run to the creative's dashboard generation history. `--out` wins over `--slug`. With a slug, the reference image is localized to durable storage and normalized to a model-safe format (SVG/AVIF/HEIC/… → PNG), so the canvas ingests a stable URL instead of an expiring one. |
5775
5783
  | `--describe-model <id>` | registry default (`~google/gemini-pro-latest`) | Override the `image_describe` model. |
@@ -951,7 +951,26 @@ function bakerUses(value) {
951
951
  const uses = asRecord(value);
952
952
  const companies = typeof uses.companies === "number" ? uses.companies : 0;
953
953
  if (companies === 0) return {};
954
- return { baker_uses: companies, ...uses.ours === true ? { already_adapted: true } : {} };
954
+ const count = (n) => typeof n === "number" && n > 0 ? n : void 0;
955
+ const approved = count(uses.approved);
956
+ const rejected = count(uses.rejected);
957
+ return {
958
+ baker_uses: companies,
959
+ ...uses.ours === true ? { already_adapted: true } : {},
960
+ ...approved !== void 0 ? { approved } : {},
961
+ ...rejected !== void 0 ? { rejected } : {}
962
+ };
963
+ }
964
+ function dnaMove(dna) {
965
+ const text = (value) => typeof value === "string" && value ? value : void 0;
966
+ const visual = text(dna.visual);
967
+ const container = text(dna.container);
968
+ const mechanism = text(dna.mechanism);
969
+ return {
970
+ ...visual ? { visual } : {},
971
+ ...container ? { container } : {},
972
+ ...mechanism ? { mechanism } : {}
973
+ };
955
974
  }
956
975
  function compactWinningAd(record) {
957
976
  const dna = asRecord(record.dna);
@@ -961,13 +980,14 @@ function compactWinningAd(record) {
961
980
  format: String(record.format ?? ""),
962
981
  relevance: round2(record.relevance),
963
982
  winner_score: round2(record.winner_score),
983
+ winner_category: typeof record.winner_category === "string" ? record.winner_category : null,
984
+ days_active: typeof record.days_active === "number" ? record.days_active : null,
985
+ active: typeof record.active === "boolean" ? record.active : null,
964
986
  summary: String(dna.creative_concept ?? ""),
965
987
  advertiser_id: String(record.advertiser_id ?? ""),
966
988
  ad_id: String(record.ad_id ?? ""),
967
989
  media_url: String(record.media_url ?? ""),
968
- ...typeof dna.visual === "string" && dna.visual ? { visual: dna.visual } : {},
969
- ...typeof dna.container === "string" && dna.container ? { container: dna.container } : {},
970
- ...typeof dna.mechanism === "string" && dna.mechanism ? { mechanism: dna.mechanism } : {},
990
+ ...dnaMove(dna),
971
991
  ...bakerUses(record.baker_uses),
972
992
  ...record.source === "followed" || record.source === "library" ? { source: record.source } : {}
973
993
  };
@@ -977,11 +997,8 @@ function fullWinningAd(record) {
977
997
  const dna = asRecord(record.dna);
978
998
  return {
979
999
  ...compact,
980
- winner_category: String(record.winner_category ?? ""),
981
1000
  media_kind: record.media_kind ?? null,
982
- days_active: record.days_active ?? null,
983
1001
  reach: record.reach ?? null,
984
- active: typeof record.active === "boolean" ? record.active : null,
985
1002
  angle: dna.angle ?? null,
986
1003
  awareness_stage: dna.awareness_stage ?? null,
987
1004
  target_persona: dna.target_persona ?? null,
@@ -1339,4 +1356,4 @@ export {
1339
1356
  captureVimeoCookie,
1340
1357
  childEnvWith
1341
1358
  };
1342
- //# sourceMappingURL=chunk-N72QYDJY.js.map
1359
+ //# sourceMappingURL=chunk-ISCXDKWB.js.map