@koda-sl/baker-cli 0.202.0 → 0.203.0
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 +10 -0
- package/dist/cli.js +85 -28
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2505,6 +2505,16 @@ List testimonials with optional filters.
|
|
|
2505
2505
|
baker testimonials list --source google --sentiment positive --limit 20
|
|
2506
2506
|
```
|
|
2507
2507
|
|
|
2508
|
+
When `list` or `search` returns nothing, the envelope carries `meta.empty` explaining why, checked against the company's connected review sources rather than inferred from the empty result:
|
|
2509
|
+
|
|
2510
|
+
| `meta.empty.reason` | Meaning | What to do |
|
|
2511
|
+
| --- | --- | --- |
|
|
2512
|
+
| `filtered_out` | Filters were applied, so the corpus itself was not examined | Drop every filter and re-run once |
|
|
2513
|
+
| `no_corpus_connected` | The company has no review source connected | Terminal — source proof another way; no rewording will help |
|
|
2514
|
+
| `corpus_empty` | A source is connected but holds no testimonials yet | Ingestion may still be running; do not report the client as having no reviews |
|
|
2515
|
+
|
|
2516
|
+
If the source check itself fails, `meta.empty` is omitted entirely and the hint says so — an unverified absence is never reported as one.
|
|
2517
|
+
|
|
2508
2518
|
### `baker testimonials tags`
|
|
2509
2519
|
|
|
2510
2520
|
List the available testimonial tag names — built-in defaults plus the company's custom tags. Use it before filtering with `--tags`. Defaults to a markdown list (`--output json` for the `{ ok, data }` envelope).
|
package/dist/cli.js
CHANGED
|
@@ -18463,7 +18463,7 @@ Performance (the workhorse):
|
|
|
18463
18463
|
baker ads meta insights --object act_123 --level ad --intent ranking \u2014 flag creatives to refresh
|
|
18464
18464
|
baker ads meta insights --object act_123 --level adset --breakdowns publisher_platform,age \u2014 placement \xD7 age
|
|
18465
18465
|
baker ads meta insights --object act_123 --level ad --since 2026-01-01 --until 2026-03-31 \u2014 auto-async
|
|
18466
|
-
baker ads meta insights --list-
|
|
18466
|
+
baker ads meta insights --list-intents
|
|
18467
18467
|
|
|
18468
18468
|
Audit & review:
|
|
18469
18469
|
baker ads meta activities --account-id act_123 --days 14
|
|
@@ -40550,7 +40550,7 @@ registerSchema({
|
|
|
40550
40550
|
},
|
|
40551
40551
|
"from-video": {
|
|
40552
40552
|
type: "string",
|
|
40553
|
-
description: "The same, but from the VIDEO LIBRARY: comma-separated video ids (see `baker videos
|
|
40553
|
+
description: "The same, but from the VIDEO LIBRARY: comma-separated video ids (see `baker videos search`) to build on. Use this for footage the client uploaded or synced rather than something the Studio rendered. Counts against the same 10 source clips as --from-clip. The first use of a given video spends a minute encoding a reusable copy and is refused with a 'try again shortly' message; every use after that is instant.",
|
|
40554
40554
|
required: false
|
|
40555
40555
|
},
|
|
40556
40556
|
model: {
|
|
@@ -42193,26 +42193,85 @@ var getCommand5 = defineCommand193({
|
|
|
42193
42193
|
import { defineCommand as defineCommand194 } from "citty";
|
|
42194
42194
|
|
|
42195
42195
|
// src/commands/testimonials/emptyCorpusHints.ts
|
|
42196
|
-
|
|
42197
|
-
|
|
42198
|
-
|
|
42199
|
-
|
|
42200
|
-
|
|
42201
|
-
search: "No hits. Search reranks the whole corpus, so an empty result is nearly always an empty library rather than a missed query \u2014 confirm once with `baker testimonials list --limit 1`. If that is empty too, ",
|
|
42202
|
-
list: "No rows on an unfiltered browse, which is proof rather than a hint: "
|
|
42203
|
-
};
|
|
42204
|
-
function emptyCorpusHints({ command, resultCount, activeFilters }) {
|
|
42196
|
+
function resolveEmptyReason({
|
|
42197
|
+
resultCount,
|
|
42198
|
+
activeFilters,
|
|
42199
|
+
sourceCount
|
|
42200
|
+
}) {
|
|
42205
42201
|
if (resultCount > 0) {
|
|
42206
|
-
return
|
|
42202
|
+
return null;
|
|
42207
42203
|
}
|
|
42208
|
-
const hints = [];
|
|
42209
42204
|
if (activeFilters.length > 0) {
|
|
42210
|
-
|
|
42205
|
+
return "filtered_out";
|
|
42206
|
+
}
|
|
42207
|
+
return sourceCount === 0 ? "no_corpus_connected" : "corpus_empty";
|
|
42208
|
+
}
|
|
42209
|
+
var NO_CORPUS_EXIT = 'Testimonials are ingested from the client\'s Google/Trustpilot reviews \u2014 an agent cannot write one \u2014 so no rewording will find quotes. Stop searching and source proof another way: a result or case study the client already published, a named-customer logo (`baker images logo`), or a founder claim attributed as a claim. Otherwise ship without a testimonial block and file the gap: `baker actions create --name "Collect customer quotes for <page>" --priority medium --tags <slug>`. Never invent, paraphrase-into-existence, or pad with unrelated praise.';
|
|
42210
|
+
function emptyReasonDetail(reason, sourceCount, activeFilters) {
|
|
42211
|
+
if (reason === "filtered_out") {
|
|
42212
|
+
return `No rows matched, and ${activeFilters.join(", ")} narrowed this read. The corpus was not checked \u2014 drop every filter to see whether the library itself is empty.`;
|
|
42213
|
+
}
|
|
42214
|
+
if (reason === "no_corpus_connected") {
|
|
42215
|
+
return "This company has no review source connected \u2014 checked against its source list, not inferred from the empty result.";
|
|
42216
|
+
}
|
|
42217
|
+
return `${sourceCount} review source${sourceCount === 1 ? " is" : "s are"} connected but hold no testimonials yet \u2014 the source is there, the rows are not. Ingestion may still be running.`;
|
|
42218
|
+
}
|
|
42219
|
+
async function measureEmptyCorpus({
|
|
42220
|
+
command,
|
|
42221
|
+
resultCount,
|
|
42222
|
+
activeFilters
|
|
42223
|
+
}) {
|
|
42224
|
+
if (resultCount > 0) {
|
|
42225
|
+
return { hints: [] };
|
|
42226
|
+
}
|
|
42227
|
+
if (activeFilters.length > 0) {
|
|
42228
|
+
const reason2 = "filtered_out";
|
|
42229
|
+
return {
|
|
42230
|
+
hints: emptyCorpusHints({ command, reason: reason2, sourceCount: 0, activeFilters }),
|
|
42231
|
+
empty: { reason: reason2, detail: emptyReasonDetail(reason2, 0, activeFilters) }
|
|
42232
|
+
};
|
|
42233
|
+
}
|
|
42234
|
+
let sourceCount;
|
|
42235
|
+
try {
|
|
42236
|
+
const sources = await apiGet("/api/testimonials/sources");
|
|
42237
|
+
sourceCount = Array.isArray(sources) ? sources.length : 0;
|
|
42238
|
+
} catch {
|
|
42239
|
+
return {
|
|
42240
|
+
hints: [
|
|
42241
|
+
"No rows, and the check for connected review sources failed \u2014 so this is NOT evidence that the client has no reviews. Do not record an absence you could not verify. Re-run once; if it fails again, say the library could not be read rather than that it is empty."
|
|
42242
|
+
]
|
|
42243
|
+
};
|
|
42244
|
+
}
|
|
42245
|
+
const reason = resolveEmptyReason({ resultCount, activeFilters, sourceCount });
|
|
42246
|
+
if (reason === null) {
|
|
42247
|
+
return { hints: [] };
|
|
42248
|
+
}
|
|
42249
|
+
return {
|
|
42250
|
+
hints: emptyCorpusHints({ command, reason, sourceCount, activeFilters }),
|
|
42251
|
+
empty: { reason, detail: emptyReasonDetail(reason, sourceCount, activeFilters) }
|
|
42252
|
+
};
|
|
42253
|
+
}
|
|
42254
|
+
function emptyCorpusHints({
|
|
42255
|
+
command,
|
|
42256
|
+
reason,
|
|
42257
|
+
sourceCount,
|
|
42258
|
+
activeFilters
|
|
42259
|
+
}) {
|
|
42260
|
+
if (reason === null) {
|
|
42261
|
+
return [];
|
|
42262
|
+
}
|
|
42263
|
+
if (reason === "filtered_out") {
|
|
42264
|
+
return [
|
|
42211
42265
|
`No rows \u2014 ${activeFilters.join(", ")} narrowed this. Drop every filter and re-run once before touching the query; a filter combination and an empty library look identical from here.`
|
|
42212
|
-
|
|
42266
|
+
];
|
|
42213
42267
|
}
|
|
42214
|
-
|
|
42215
|
-
|
|
42268
|
+
const lead = command === "search" ? "No hits" : "No rows";
|
|
42269
|
+
if (reason === "no_corpus_connected") {
|
|
42270
|
+
return [`${lead}, and this company's source list is empty: there is no review corpus to search. ${NO_CORPUS_EXIT}`];
|
|
42271
|
+
}
|
|
42272
|
+
return [
|
|
42273
|
+
`${lead}. ${sourceCount} review source${sourceCount === 1 ? " is" : "s are"} connected but no testimonials have landed yet \u2014 so this is a timing or ingestion gap, not a missing connection, and re-querying will not change it. Ingestion may still be running: say so rather than reporting the client has no reviews. ${NO_CORPUS_EXIT}`
|
|
42274
|
+
];
|
|
42216
42275
|
}
|
|
42217
42276
|
|
|
42218
42277
|
// src/commands/testimonials/list.ts
|
|
@@ -42287,14 +42346,14 @@ var listCommand18 = defineCommand194({
|
|
|
42287
42346
|
run: async ({ args }) => {
|
|
42288
42347
|
try {
|
|
42289
42348
|
const data = await apiGet("/api/testimonials", buildListParams(args));
|
|
42290
|
-
const hints =
|
|
42349
|
+
const { hints, empty } = await measureEmptyCorpus({
|
|
42291
42350
|
command: "list",
|
|
42292
42351
|
resultCount: data.length,
|
|
42293
42352
|
activeFilters: activeFilterFlags(args, FILTER_FLAGS)
|
|
42294
42353
|
});
|
|
42295
42354
|
const outputFormat = args.output || "json";
|
|
42296
42355
|
writeOutput(
|
|
42297
|
-
{ ok: true, data, ...hints.length > 0 ? { hints } : {} },
|
|
42356
|
+
{ ok: true, data, ...empty ? { meta: { empty } } : {}, ...hints.length > 0 ? { hints } : {} },
|
|
42298
42357
|
outputFormat,
|
|
42299
42358
|
args.fields ? args.fields.split(",") : void 0,
|
|
42300
42359
|
args.full,
|
|
@@ -42426,17 +42485,15 @@ var searchCommand3 = defineCommand195({
|
|
|
42426
42485
|
const body = buildSearchRequest(query, args);
|
|
42427
42486
|
const data = await apiPost("/api/testimonials/search", body);
|
|
42428
42487
|
const languageHint = languageBiasHint(data, body.language);
|
|
42429
|
-
const hints =
|
|
42430
|
-
|
|
42431
|
-
|
|
42432
|
-
|
|
42433
|
-
|
|
42434
|
-
|
|
42435
|
-
...languageHint ? [languageHint] : []
|
|
42436
|
-
];
|
|
42488
|
+
const { hints: emptyHints, empty } = await measureEmptyCorpus({
|
|
42489
|
+
command: "search",
|
|
42490
|
+
resultCount: data.length,
|
|
42491
|
+
activeFilters: activeFilterFlags(args, FILTER_FLAGS2)
|
|
42492
|
+
});
|
|
42493
|
+
const hints = [...emptyHints, ...languageHint ? [languageHint] : []];
|
|
42437
42494
|
const outputFormat = args.output || "json";
|
|
42438
42495
|
writeOutput(
|
|
42439
|
-
{ ok: true, data, ...hints.length > 0 ? { hints } : {} },
|
|
42496
|
+
{ ok: true, data, ...empty ? { meta: { empty } } : {}, ...hints.length > 0 ? { hints } : {} },
|
|
42440
42497
|
outputFormat,
|
|
42441
42498
|
args.fields ? args.fields.split(",") : void 0,
|
|
42442
42499
|
args.full,
|