@koda-sl/baker-cli 0.135.1 → 0.136.0-dev.3ffe27f0b

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/dist/cli.js CHANGED
@@ -34,7 +34,7 @@ import {
34
34
  toModelSafeImage,
35
35
  ulid,
36
36
  validateCanvasDeep
37
- } from "./chunk-CIF62V7L.js";
37
+ } from "./chunk-GKL4CUCC.js";
38
38
  import {
39
39
  csvOrJson,
40
40
  daysAgoIso,
@@ -20335,7 +20335,7 @@ var scaffoldStaticAdCommand = defineCommand91({
20335
20335
  font_slot: report.includes_font ? "Drop a brand font at the [TODO] brandfont path (the describe pass recorded the ad's typefaces under `fonts` in prompt.json \u2014 match those). The font is wired into the render as a TYPE SPECIMEN reference so generated text takes the brand letterforms. Delete the brandfont + type_ref nodes to skip it." : "skipped (--skip-font)",
20336
20336
  actor_sheets: report.actor_sheets.length > 0 ? `Each living hero (${report.actor_sheets.join(", ")}) is fused into a generated multi-view reference sheet (image_reference_sheet) that the render grounds on \u2014 so drop ONE clean photo at that hero's ingest and the sheet builds the consistent turnaround. Pass --skip-actor-sheets to ground on the lone photo instead.` : "none (no person/animal heroes detected, or --skip-actor-sheets)",
20337
20337
  placements: report.placements === null ? `Single format (${report.aspect_ratio}). Pass --placements meta_feed_stories|meta_all|linkedin|\u2026 to auto-fan-out to a platform's set.` : `Auto-fans the approved hero out to ${report.placements} \u2192 ${report.output_formats.join(", ")}. The hero ratio (${report.aspect_ratio}) passes through free; each other ratio is a billed render, AI-recomposed (not cropped). Re-read the copy and confirm the brand-color treatment on EVERY ratio before publishing \u2014 adaptation re-generates and can silently mangle text or flatten a color device. Pass --placements none for a single base ad.`,
20338
- note: "Populate as you go: for each [TODO] ingest slot, source its real asset and wire it into the slot right away \u2014 one at a time, not all sourced first then reconciled at the end. When every slot is filled, `baker canvas validate` then `baker canvas run`. Running generates a billed image \u2014 it is not free."
20338
+ note: "Populate as you go: for each [TODO] ingest slot, source its real asset and wire it into the slot right away \u2014 one at a time, not all sourced first then reconciled at the end. For any LOGO slot, use the company's committed brand logo from `src/brand/logos/` (per BRAND.md's Logos map) \u2014 do NOT re-fetch it with `baker images logo <domain>` when a brand logo exists, because the Brandfetch CDN often serves an OUTDATED mark and that is how an old logo ships on a live ad. When every slot is filled, `baker canvas validate` then `baker canvas run`. Running generates a billed image \u2014 it is not free."
20339
20339
  }
20340
20340
  },
20341
20341
  null,
@@ -27037,6 +27037,25 @@ var listCommand10 = defineCommand154({
27037
27037
 
27038
27038
  // src/commands/testimonials/search.ts
27039
27039
  import { defineCommand as defineCommand155 } from "citty";
27040
+ function languageBiasHint(results, requestedLanguage) {
27041
+ if (requestedLanguage) {
27042
+ return null;
27043
+ }
27044
+ const withLang = results.filter((r) => typeof r.language === "string" && r.language.length > 0);
27045
+ if (withLang.length < 3) {
27046
+ return null;
27047
+ }
27048
+ const counts = /* @__PURE__ */ new Map();
27049
+ for (const r of withLang) {
27050
+ const lang = r.language;
27051
+ counts.set(lang, (counts.get(lang) ?? 0) + 1);
27052
+ }
27053
+ const [topLang, topCount] = [...counts.entries()].sort((a, b) => b[1] - a[1])[0] ?? ["", 0];
27054
+ if (topLang && topCount / withLang.length >= 0.8) {
27055
+ return `Results skew ${topLang} (${topCount}/${withLang.length}). If you're sourcing quotes for a specific market, pass --language <code> (e.g. --language en) so the ad's language matches its audience.`;
27056
+ }
27057
+ return null;
27058
+ }
27040
27059
  registerSchema({
27041
27060
  command: "testimonials.search",
27042
27061
  description: "Search testimonials by text query. Uses hybrid BM25 + vector + reranking.",
@@ -27067,6 +27086,34 @@ registerSchema({
27067
27086
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
27068
27087
  }
27069
27088
  });
27089
+ function buildSearchRequest(query, args) {
27090
+ const body = { query };
27091
+ if (args.limit !== void 0) {
27092
+ body.limit = Number(args.limit);
27093
+ }
27094
+ if (args.source) {
27095
+ body.source = args.source;
27096
+ }
27097
+ if (args["rating-min"] !== void 0) {
27098
+ body.rating_min = Number(args["rating-min"]);
27099
+ }
27100
+ if (args["rating-max"] !== void 0) {
27101
+ body.rating_max = Number(args["rating-max"]);
27102
+ }
27103
+ if (args.status) {
27104
+ body.status = args.status;
27105
+ }
27106
+ if (args.sentiment) {
27107
+ body.sentiment = args.sentiment;
27108
+ }
27109
+ if (args.language) {
27110
+ body.language = args.language;
27111
+ }
27112
+ if (args.tags) {
27113
+ body.tags = args.tags.split(",").filter(Boolean);
27114
+ }
27115
+ return body;
27116
+ }
27070
27117
  var searchCommand2 = defineCommand155({
27071
27118
  meta: {
27072
27119
  name: "search",
@@ -27093,39 +27140,21 @@ var searchCommand2 = defineCommand155({
27093
27140
  writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message: "Search query is required" } });
27094
27141
  process.exit(1);
27095
27142
  }
27096
- const body = { query };
27097
- if (args.limit !== void 0) {
27098
- body.limit = Number(args.limit);
27099
- }
27100
- if (args.source) {
27101
- body.source = args.source;
27102
- }
27103
- if (args["rating-min"] !== void 0) {
27104
- body.rating_min = Number(args["rating-min"]);
27105
- }
27106
- if (args["rating-max"] !== void 0) {
27107
- body.rating_max = Number(args["rating-max"]);
27108
- }
27109
- if (args.status) {
27110
- body.status = args.status;
27111
- }
27112
- if (args.sentiment) {
27113
- body.sentiment = args.sentiment;
27114
- }
27115
- if (args.language) {
27116
- body.language = args.language;
27117
- }
27118
- if (args.tags) {
27119
- body.tags = args.tags.split(",").filter(Boolean);
27120
- }
27143
+ const body = buildSearchRequest(query, args);
27121
27144
  const data = await apiPost("/api/testimonials/search", body);
27145
+ const hint = languageBiasHint(data, body.language);
27146
+ const outputFormat = args.output || "json";
27122
27147
  writeOutput(
27123
- { ok: true, data },
27124
- args.output || "json",
27148
+ { ok: true, data, ...hint ? { hints: [hint] } : {} },
27149
+ outputFormat,
27125
27150
  args.fields ? args.fields.split(",") : void 0,
27126
27151
  args.full,
27127
27152
  testimonialNormalizer
27128
27153
  );
27154
+ if (hint && outputFormat !== "json") {
27155
+ process.stderr.write(`${hint}
27156
+ `);
27157
+ }
27129
27158
  } catch (err) {
27130
27159
  if (err instanceof ApiError) {
27131
27160
  writeJson({ ok: false, error: { code: err.code, message: err.message } });