@koda-sl/baker-cli 0.142.0 → 0.144.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 CHANGED
@@ -2132,7 +2132,11 @@ baker videos search "tutorial" --tags explainer,ugc --limit 5
2132
2132
 
2133
2133
  ### `baker videos get <id>`
2134
2134
 
2135
- Get a single video by ID.
2135
+ Get a single video by ID. `--full` includes the auto-generated **transcript** (Baker transcribes every upload with speech-to-text), a `transcript_segment_count`, and the titled/timestamped **scene breakdown** — so you can read what a clip says and shows without watching it. Default JSON also returns the raw `transcript` + timed `transcriptSegments`; `--full` is what surfaces them in `--output md`/`files` too.
2136
+
2137
+ ```bash
2138
+ baker videos get j571abc123 --full --output md
2139
+ ```
2136
2140
 
2137
2141
  ### `baker videos upload <file>`
2138
2142
 
@@ -2326,6 +2330,16 @@ Top winning ads for one advertiser id → `GET /api/ad-library/advertiser-winner
2326
2330
  baker winning-ads winners adv_123 --top 15 --output md
2327
2331
  ```
2328
2332
 
2333
+ ### `baker winning-ads content <ad-id>`
2334
+
2335
+ Read what's **inside** one ad → `GET /api/ad-library/ad-content`. `search`/`winners`/`feed` return a lean shortlist; this opens a single `ad_id` so you can understand a reference before reproducing it. For a **video** it returns the spoken `transcript`, the `on_screen_text` overlays, and the ad `copy` (`primary_text`/`headline`/`cta`); a **static** ad returns only the copy. Add `--full` for speech (voiceover style, speaker count, direct-address), pacing (scene count, shot length, key timeline moments), any recognised soundtrack, and each on-screen overlay's role + position (e.g. `GLOBAL PAYROLL @0.5s [hook, top]`) so you can place text faithfully. `--platform meta|linkedin` selects which platform the id is on (default `meta`).
2336
+
2337
+ ```bash
2338
+ baker winning-ads content adg_123 # transcript + on-screen text + copy
2339
+ baker winning-ads content adg_123 --platform meta --full # + speech, pacing, soundtrack
2340
+ baker winning-ads content adg_123 --full --output md
2341
+ ```
2342
+
2329
2343
  ### `baker winning-ads unfollow <advertiser>`
2330
2344
 
2331
2345
  Stop following a brand by advertiser id → `POST /api/ad-library/unfollow`. Returns `{ removed }`.
package/dist/cli.js CHANGED
@@ -73,7 +73,7 @@ import {
73
73
  } from "./chunk-RK67WL4O.js";
74
74
 
75
75
  // src/cli.ts
76
- import { defineCommand as defineCommand177, runMain } from "citty";
76
+ import { defineCommand as defineCommand178, runMain } from "citty";
77
77
 
78
78
  // src/commands/actions/index.ts
79
79
  import { defineCommand as defineCommand18 } from "citty";
@@ -145,6 +145,22 @@ function compactVideo(video) {
145
145
  status: String(video.status ?? "")
146
146
  };
147
147
  }
148
+ function sceneLines(value) {
149
+ if (!Array.isArray(value)) {
150
+ return [];
151
+ }
152
+ return value.flatMap((s) => {
153
+ const scene = asRecord(s);
154
+ const title = typeof scene.title === "string" ? scene.title : "";
155
+ if (!title) {
156
+ return [];
157
+ }
158
+ const start = round2(scene.startSecond);
159
+ const end = round2(scene.endSecond);
160
+ const range = start !== null && end !== null ? ` (${start}-${end}s)` : "";
161
+ return [`${title}${range}`];
162
+ });
163
+ }
148
164
  function fullVideo(video) {
149
165
  const compact = compactVideo(video);
150
166
  return {
@@ -152,6 +168,12 @@ function fullVideo(video) {
152
168
  duration: video.duration ?? null,
153
169
  muxPlaybackId: String(video.muxPlaybackId ?? ""),
154
170
  source: String(video.source ?? ""),
171
+ // The spoken transcript + AI scene breakdown of the client's own clip — the
172
+ // "understand what's in this video" payload. Kept out of the compact/search
173
+ // projection (too large for a list) and surfaced here on --full.
174
+ transcript: video.transcript ?? null,
175
+ transcript_segment_count: Array.isArray(video.transcriptSegments) ? video.transcriptSegments.length : 0,
176
+ scenes: sceneLines(video.scenes),
155
177
  createdAt: video.createdAt ?? null
156
178
  };
157
179
  }
@@ -243,6 +265,76 @@ function winningAdNormalizer(record, full) {
243
265
  }
244
266
  return compactWinningAd(record);
245
267
  }
268
+ function overlayLines(value, detailed = false) {
269
+ if (!Array.isArray(value)) {
270
+ return [];
271
+ }
272
+ return value.flatMap((o) => {
273
+ const item = asRecord(o);
274
+ const text = typeof item.text === "string" ? item.text : "";
275
+ if (!text) {
276
+ return [];
277
+ }
278
+ const at = round2(item.appears_at_s);
279
+ let line = at !== null ? `${text} @${at}s` : text;
280
+ if (detailed) {
281
+ const tags = [item.role, item.position].filter((t) => typeof t === "string" && t.length > 0);
282
+ if (tags.length) {
283
+ line += ` [${tags.join(", ")}]`;
284
+ }
285
+ }
286
+ return [line];
287
+ });
288
+ }
289
+ function compactAdContent(record) {
290
+ return {
291
+ ad_id: record.ad_id ?? null,
292
+ advertiser: record.advertiser ?? null,
293
+ format: record.format ?? null,
294
+ media_kind: record.media_kind ?? null,
295
+ media_url: record.media_url ?? null,
296
+ summary: record.summary ?? null,
297
+ transcript: record.transcript ?? null,
298
+ on_screen_text: overlayLines(record.on_screen_text),
299
+ has_captions: typeof record.has_captions === "boolean" ? record.has_captions : null,
300
+ primary_text: record.primary_text ?? null,
301
+ headline: record.headline ?? null,
302
+ cta: record.cta ?? null
303
+ };
304
+ }
305
+ function fullAdContent(record) {
306
+ const speech = asRecord(record.speech);
307
+ const pacing = asRecord(record.pacing);
308
+ const music = asRecord(record.music);
309
+ return {
310
+ ...compactAdContent(record),
311
+ platform: record.platform ?? null,
312
+ // Re-derive with role/position tags — the lean compact version dropped them.
313
+ on_screen_text: overlayLines(record.on_screen_text, true),
314
+ winner_score: round2(record.winner_score),
315
+ winner_category: record.winner_category ?? null,
316
+ angle: record.angle ?? null,
317
+ hook_archetype: record.hook_archetype ?? null,
318
+ caption_style: record.caption_style ?? null,
319
+ has_speech: typeof record.has_speech === "boolean" ? record.has_speech : null,
320
+ speech_pct_of_runtime: round2(speech.speech_pct_of_runtime),
321
+ speaker_count: speech.speaker_count ?? null,
322
+ voiceover_vs_sync: speech.voiceover_vs_sync ?? null,
323
+ is_direct_address: typeof speech.is_direct_address === "boolean" ? speech.is_direct_address : null,
324
+ speech_emotion_dominant: speech.speech_emotion_dominant ?? null,
325
+ scene_count: pacing.scene_count ?? null,
326
+ avg_shot_length_s: round2(pacing.avg_shot_length_s),
327
+ pacing_shape: pacing.pacing_shape ?? null,
328
+ hook_ends_at_s: round2(pacing.hook_ends_at_s),
329
+ cta_first_shown_at_s: round2(pacing.cta_first_shown_at_s),
330
+ product_first_appears_at_s: round2(pacing.product_first_appears_at_s),
331
+ music_track_title: music.track_title ?? null,
332
+ music_track_artist: music.track_artist ?? null
333
+ };
334
+ }
335
+ function adContentNormalizer(record, full) {
336
+ return full ? fullAdContent(record) : compactAdContent(record);
337
+ }
246
338
  function applyFieldMask(data, fields) {
247
339
  const result = {};
248
340
  for (const field of fields) {
@@ -1135,6 +1227,65 @@ var seedCatalogResponseSchema = z2.object({
1135
1227
  segment: seedCatalogSegmentSchema.nullable(),
1136
1228
  patterns: z2.array(patternSeedSchema)
1137
1229
  });
1230
+ var adContentRequestSchema = z2.object({
1231
+ ad_id: z2.string().min(1),
1232
+ platform: adLibraryPlatformSchema.optional()
1233
+ });
1234
+ var adContentOverlaySchema = z2.object({
1235
+ text: z2.string(),
1236
+ appears_at_s: z2.number().nullable(),
1237
+ position: z2.string().nullable(),
1238
+ role: z2.string().nullable()
1239
+ });
1240
+ var adContentSpeechSchema = z2.object({
1241
+ speech_pct_of_runtime: z2.number().nullable(),
1242
+ speaker_count: z2.number().nullable(),
1243
+ voiceover_vs_sync: z2.string().nullable(),
1244
+ is_direct_address: z2.boolean().nullable(),
1245
+ speech_emotion_dominant: z2.string().nullable()
1246
+ });
1247
+ var adContentPacingSchema = z2.object({
1248
+ scene_count: z2.number().nullable(),
1249
+ avg_shot_length_s: z2.number().nullable(),
1250
+ pacing_shape: z2.string().nullable(),
1251
+ hook_ends_at_s: z2.number().nullable(),
1252
+ cta_first_shown_at_s: z2.number().nullable(),
1253
+ product_first_appears_at_s: z2.number().nullable()
1254
+ });
1255
+ var adContentMusicSchema = z2.object({
1256
+ track_title: z2.string().nullable(),
1257
+ track_artist: z2.string().nullable()
1258
+ });
1259
+ var adContentSchema = z2.object({
1260
+ ad_id: z2.string().nullable(),
1261
+ advertiser: z2.string().nullable(),
1262
+ advertiser_id: z2.string().nullable(),
1263
+ platform: z2.string().nullable(),
1264
+ format: z2.string().nullable(),
1265
+ media_kind: z2.string().nullable(),
1266
+ media_url: z2.string().nullable(),
1267
+ winner_score: z2.number().nullable(),
1268
+ winner_category: z2.string().nullable(),
1269
+ summary: z2.string().nullable(),
1270
+ angle: z2.string().nullable(),
1271
+ hook_archetype: z2.string().nullable(),
1272
+ // Spoken words (video only; null when the ad has no speech track).
1273
+ transcript: z2.string().nullable(),
1274
+ has_speech: z2.boolean().nullable(),
1275
+ speech: adContentSpeechSchema,
1276
+ // On-screen text / captions.
1277
+ has_captions: z2.boolean().nullable(),
1278
+ caption_style: z2.string().nullable(),
1279
+ on_screen_text: z2.array(adContentOverlaySchema),
1280
+ // Copy the viewer reads outside the media.
1281
+ primary_text: z2.string().nullable(),
1282
+ headline: z2.string().nullable(),
1283
+ cta: z2.string().nullable(),
1284
+ // Structure + soundtrack.
1285
+ pacing: adContentPacingSchema,
1286
+ music: adContentMusicSchema
1287
+ });
1288
+ var adContentResponseSchema = z2.object({ ad: adContentSchema });
1138
1289
 
1139
1290
  // ../api/src/ads-linkedin/limits.ts
1140
1291
  var LINKEDIN_LIMITS = {
@@ -4923,7 +5074,7 @@ var demandGenAdSchema = z12.object({
4923
5074
  squareImageAssets: z12.array(refSchema).optional(),
4924
5075
  logoImageAssets: z12.array(refSchema).optional()
4925
5076
  });
4926
- var adContentSchema = z12.discriminatedUnion("format", [
5077
+ var adContentSchema2 = z12.discriminatedUnion("format", [
4927
5078
  responsiveSearchAdSchema,
4928
5079
  responsiveDisplayAdSchema,
4929
5080
  callAdSchema,
@@ -4934,7 +5085,7 @@ var adContentSchema = z12.discriminatedUnion("format", [
4934
5085
  var adCreateSchema = z12.object({
4935
5086
  adGroup: refSchema,
4936
5087
  status: stageableStatusSchema2.default("PAUSED"),
4937
- content: adContentSchema
5088
+ content: adContentSchema2
4938
5089
  });
4939
5090
  var adUpdateSchema = z12.object({
4940
5091
  status: z12.enum(["ENABLED", "PAUSED", "REMOVED"]).optional(),
@@ -29434,7 +29585,7 @@ Full guide: __tooling__/docs/tools/baker/videos.md`
29434
29585
  });
29435
29586
 
29436
29587
  // src/commands/winning-ads/index.ts
29437
- import { defineCommand as defineCommand176 } from "citty";
29588
+ import { defineCommand as defineCommand177 } from "citty";
29438
29589
 
29439
29590
  // src/commands/winning-ads/advertisers.ts
29440
29591
  import { defineCommand as defineCommand165 } from "citty";
@@ -29629,8 +29780,70 @@ var briefCommand = defineCommand166({
29629
29780
  }
29630
29781
  });
29631
29782
 
29632
- // src/commands/winning-ads/feed.ts
29783
+ // src/commands/winning-ads/content.ts
29633
29784
  import { defineCommand as defineCommand167 } from "citty";
29785
+ registerSchema({
29786
+ command: "winning-ads.content",
29787
+ description: "Read what's INSIDE one winning ad: the spoken transcript, the on-screen text, and the ad copy. Use this after `search`/`winners`/`feed` return a shortlist \u2014 pass an ad_id to understand a reference before reproducing it. Add --full for speech, pacing, and soundtrack detail. Video ads carry the transcript/on-screen text; static ads carry only the copy.",
29788
+ args: {
29789
+ "ad-id": { type: "string", description: "The ad id to open (from search/winners/feed results)", required: true },
29790
+ platform: {
29791
+ type: "string",
29792
+ description: "Which platform the ad id is on: meta|linkedin (default meta)",
29793
+ required: false
29794
+ }
29795
+ }
29796
+ });
29797
+ var contentCommand = defineCommand167({
29798
+ meta: {
29799
+ name: "content",
29800
+ description: "Read the transcript + on-screen text + copy of one winning ad. Example: baker winning-ads content adg_123 --platform meta --full --output md"
29801
+ },
29802
+ args: {
29803
+ "ad-id": { type: "positional", description: "Ad id (from search/winners/feed)", required: true },
29804
+ platform: {
29805
+ type: "string",
29806
+ description: "Platform the ad id is on: meta|linkedin (default meta)",
29807
+ required: false
29808
+ },
29809
+ output: { type: "string", description: "Output format: json|files|md", required: false, default: "json" },
29810
+ fields: { type: "string", description: "Comma-separated field names to include", required: false },
29811
+ full: {
29812
+ type: "boolean",
29813
+ description: "Include speech, pacing, and soundtrack detail",
29814
+ required: false,
29815
+ default: false
29816
+ }
29817
+ },
29818
+ run: async ({ args }) => {
29819
+ try {
29820
+ const params = { id: String(args["ad-id"]) };
29821
+ if (args.platform) {
29822
+ params.platform = String(args.platform);
29823
+ }
29824
+ const data = await apiGet("/api/ad-library/ad-content", params);
29825
+ const output = args.output || "json";
29826
+ const full = args.full;
29827
+ const ad = data?.ad ?? {};
29828
+ if (output === "json") {
29829
+ writeJson({ ok: true, data: { ad: adContentNormalizer(ad, full) } });
29830
+ return;
29831
+ }
29832
+ writeOutput(
29833
+ { ok: true, data: ad },
29834
+ output,
29835
+ args.fields ? args.fields.split(",") : void 0,
29836
+ full,
29837
+ adContentNormalizer
29838
+ );
29839
+ } catch (err) {
29840
+ reportError(err);
29841
+ }
29842
+ }
29843
+ });
29844
+
29845
+ // src/commands/winning-ads/feed.ts
29846
+ import { defineCommand as defineCommand168 } from "citty";
29634
29847
  function buildFeedParams(input) {
29635
29848
  const params = {};
29636
29849
  const advertiser = splitList(input.advertiser);
@@ -29682,7 +29895,7 @@ registerSchema({
29682
29895
  format: { type: "string", description: "Comma-separated formats to include (e.g. static,video)", required: false }
29683
29896
  }
29684
29897
  });
29685
- var feedCommand = defineCommand167({
29898
+ var feedCommand = defineCommand168({
29686
29899
  meta: {
29687
29900
  name: "feed",
29688
29901
  description: "Winners across every brand you follow (browse, then trim per advertiser). Example: baker winning-ads feed --per-advertiser 5 --output md"
@@ -29767,7 +29980,7 @@ var feedCommand = defineCommand167({
29767
29980
  });
29768
29981
 
29769
29982
  // src/commands/winning-ads/follow.ts
29770
- import { defineCommand as defineCommand168 } from "citty";
29983
+ import { defineCommand as defineCommand169 } from "citty";
29771
29984
  var PLATFORMS = ["meta", "linkedin"];
29772
29985
  registerSchema({
29773
29986
  command: "winning-ads.follow",
@@ -29782,7 +29995,7 @@ registerSchema({
29782
29995
  label: { type: "string", description: "Optional display label (defaults to the resolved name)", required: false }
29783
29996
  }
29784
29997
  });
29785
- var followCommand = defineCommand168({
29998
+ var followCommand = defineCommand169({
29786
29999
  meta: {
29787
30000
  name: "follow",
29788
30001
  description: 'Follow a brand to track ALL its ads \u2014 every platform and country. --platform is how we read your input, not a limit. A domain tracks both Meta + LinkedIn. Example: baker winning-ads follow "deel.com" --platform meta'
@@ -29829,7 +30042,7 @@ var followCommand = defineCommand168({
29829
30042
  });
29830
30043
 
29831
30044
  // src/commands/winning-ads/follow-competitors.ts
29832
- import { defineCommand as defineCommand169 } from "citty";
30045
+ import { defineCommand as defineCommand170 } from "citty";
29833
30046
  var PLATFORMS2 = ["meta", "linkedin"];
29834
30047
  var BATCH_TIMEOUT_MS = 3e5;
29835
30048
  function buildFollowBatchBody(input) {
@@ -29862,7 +30075,7 @@ registerSchema({
29862
30075
  }
29863
30076
  }
29864
30077
  });
29865
- var followCompetitorsCommand = defineCommand169({
30078
+ var followCompetitorsCommand = defineCommand170({
29866
30079
  meta: {
29867
30080
  name: "follow-competitors",
29868
30081
  description: 'Follow many brands at once by domain \u2014 add every competitor in one call. Example: baker winning-ads follow-competitors "deel.com,notion.so,hubspot.com"'
@@ -29937,7 +30150,7 @@ var followCompetitorsCommand = defineCommand169({
29937
30150
  });
29938
30151
 
29939
30152
  // src/commands/winning-ads/following.ts
29940
- import { defineCommand as defineCommand170 } from "citty";
30153
+ import { defineCommand as defineCommand171 } from "citty";
29941
30154
  registerSchema({
29942
30155
  command: "winning-ads.following",
29943
30156
  description: "List the brands you follow in your ad-dna library, with each one's status (ready vs still adding) and cached ad counts.",
@@ -29970,7 +30183,7 @@ function followingNormalizer(record, full) {
29970
30183
  platforms: Array.isArray(record.platforms) ? record.platforms : []
29971
30184
  };
29972
30185
  }
29973
- var followingCommand = defineCommand170({
30186
+ var followingCommand = defineCommand171({
29974
30187
  meta: {
29975
30188
  name: "following",
29976
30189
  description: "List brands you follow, with status (ready / adding\u2026) and cached counts. Example: baker winning-ads following --output md"
@@ -30005,7 +30218,7 @@ var followingCommand = defineCommand170({
30005
30218
  });
30006
30219
 
30007
30220
  // src/commands/winning-ads/patterns.ts
30008
- import { defineCommand as defineCommand171 } from "citty";
30221
+ import { defineCommand as defineCommand172 } from "citty";
30009
30222
  registerSchema({
30010
30223
  command: "winning-ads.patterns",
30011
30224
  description: "Mine what separates two cohorts of ads: pass a comma-list of winning ad ids (--winners) and a comma-list of weaker ad ids (--duds). Returns the discriminating DNA fields.",
@@ -30044,7 +30257,7 @@ function discriminatorRow(record) {
30044
30257
  top_values_duds: Array.isArray(record.top_values_b) ? record.top_values_b.join(", ") : ""
30045
30258
  };
30046
30259
  }
30047
- var patternsCommand = defineCommand171({
30260
+ var patternsCommand = defineCommand172({
30048
30261
  meta: {
30049
30262
  name: "patterns",
30050
30263
  description: "Discover what separates winning ads from weak ones. Example: baker winning-ads patterns --winners a_1,a_2,a_3 --duds a_9,a_8 --output md"
@@ -30100,7 +30313,7 @@ var patternsCommand = defineCommand171({
30100
30313
  });
30101
30314
 
30102
30315
  // src/commands/winning-ads/search.ts
30103
- import { defineCommand as defineCommand172 } from "citty";
30316
+ import { defineCommand as defineCommand173 } from "citty";
30104
30317
  registerSchema({
30105
30318
  command: "winning-ads.search",
30106
30319
  description: "Search the ad-dna corpus of scored winning ads. Returns a lean shortlist (advertiser, summary, scores, media_url) to pick a reference to reproduce.",
@@ -30208,7 +30421,7 @@ function buildSearchBody(args) {
30208
30421
  }
30209
30422
  return body;
30210
30423
  }
30211
- var searchCommand4 = defineCommand172({
30424
+ var searchCommand4 = defineCommand173({
30212
30425
  meta: {
30213
30426
  name: "search",
30214
30427
  description: "Search winning reference ads. Example: baker winning-ads search 'B2B SaaS before/after AI automation' --platform meta --format static --winner-category winner --exclude-advertiser adv_123 --output md"
@@ -30323,7 +30536,7 @@ var searchCommand4 = defineCommand172({
30323
30536
  });
30324
30537
 
30325
30538
  // src/commands/winning-ads/seeds.ts
30326
- import { defineCommand as defineCommand173 } from "citty";
30539
+ import { defineCommand as defineCommand174 } from "citty";
30327
30540
  function leanRow(r) {
30328
30541
  return {
30329
30542
  key: r.key,
@@ -30351,7 +30564,7 @@ function makeSeedCommand(opts) {
30351
30564
  limit: { type: "number", description: "Max keys 1-100 (default 20)", required: false, default: 20 }
30352
30565
  }
30353
30566
  });
30354
- return defineCommand173({
30567
+ return defineCommand174({
30355
30568
  meta: { name: opts.name, description: opts.description },
30356
30569
  args: {
30357
30570
  platform: { type: "string", description: "Single platform to segment on", required: false },
@@ -30400,7 +30613,7 @@ var formatsCommand = makeSeedCommand({
30400
30613
  });
30401
30614
 
30402
30615
  // src/commands/winning-ads/unfollow.ts
30403
- import { defineCommand as defineCommand174 } from "citty";
30616
+ import { defineCommand as defineCommand175 } from "citty";
30404
30617
  registerSchema({
30405
30618
  command: "winning-ads.unfollow",
30406
30619
  description: "Stop following a brand \u2014 removes it from your ad-dna library by advertiser id.",
@@ -30408,7 +30621,7 @@ registerSchema({
30408
30621
  advertiser: { type: "string", description: "Advertiser id to unfollow", required: true }
30409
30622
  }
30410
30623
  });
30411
- var unfollowCommand = defineCommand174({
30624
+ var unfollowCommand = defineCommand175({
30412
30625
  meta: {
30413
30626
  name: "unfollow",
30414
30627
  description: "Stop following a brand by advertiser id. Example: baker winning-ads unfollow adv_123"
@@ -30429,7 +30642,7 @@ var unfollowCommand = defineCommand174({
30429
30642
  });
30430
30643
 
30431
30644
  // src/commands/winning-ads/winners.ts
30432
- import { defineCommand as defineCommand175 } from "citty";
30645
+ import { defineCommand as defineCommand176 } from "citty";
30433
30646
  registerSchema({
30434
30647
  command: "winning-ads.winners",
30435
30648
  description: "Top winning ads for one advertiser id (from `advertisers` or `following`). Returns lean winner cards; add --full for DNA + longevity.",
@@ -30439,7 +30652,7 @@ registerSchema({
30439
30652
  platform: { type: "string", description: "Filter to a single platform: meta|linkedin", required: false }
30440
30653
  }
30441
30654
  });
30442
- var winnersCommand = defineCommand175({
30655
+ var winnersCommand = defineCommand176({
30443
30656
  meta: {
30444
30657
  name: "winners",
30445
30658
  description: "Top winning ads for a specific advertiser id. Example: baker winning-ads winners adv_123 --top 15 --output md"
@@ -30489,7 +30702,7 @@ var winnersCommand = defineCommand175({
30489
30702
  });
30490
30703
 
30491
30704
  // src/commands/winning-ads/index.ts
30492
- var winningAdsCommand = defineCommand176({
30705
+ var winningAdsCommand = defineCommand177({
30493
30706
  meta: {
30494
30707
  name: "winning-ads",
30495
30708
  description: `Search the ad-dna corpus of scored "winning" ads for reference creatives to reproduce, and manage the brands your library tracks. Proxied through the Baker backend (BAKER_API_KEY) \u2014 no separate token needed.
@@ -30504,6 +30717,7 @@ Subcommands:
30504
30717
  baker winning-ads following \u2014 list brands you follow (status + counts)
30505
30718
  baker winning-ads feed \u2014 winners across EVERY brand you follow; trim with --advertiser
30506
30719
  baker winning-ads winners <advertiser> \u2014 top winners for one advertiser id
30720
+ baker winning-ads content <ad-id> \u2014 read ONE ad's transcript + on-screen text + copy (understand a reference before reproducing it)
30507
30721
  baker winning-ads unfollow <advertiser> \u2014 stop following a brand
30508
30722
  baker winning-ads brief \u2014 creative brief grounded in similar winners
30509
30723
  baker winning-ads patterns --winners \u2026 --duds \u2026 \u2014 what separates a winning cohort from a weak one
@@ -30519,6 +30733,7 @@ Examples:
30519
30733
  baker winning-ads feed --per-advertiser 5 --output md
30520
30734
  baker winning-ads feed --advertiser adv_123,adv_456 --platform meta --output md
30521
30735
  baker winning-ads winners adv_123 --top 15 --output md
30736
+ baker winning-ads content adg_123 --full --output md
30522
30737
  baker winning-ads hooks --platform meta --awareness problem_aware --industry saas --output md
30523
30738
  baker winning-ads search "fintech onboarding" --hook-archetype callout --winner-category winner --output md
30524
30739
  baker winning-ads patterns --winners a_1,a_2 --duds a_9,a_8 --output md
@@ -30532,6 +30747,7 @@ Full guide: __tooling__/docs/tools/baker/winning-ads.md`
30532
30747
  following: followingCommand,
30533
30748
  feed: feedCommand,
30534
30749
  winners: winnersCommand,
30750
+ content: contentCommand,
30535
30751
  unfollow: unfollowCommand,
30536
30752
  brief: briefCommand,
30537
30753
  patterns: patternsCommand,
@@ -30558,7 +30774,7 @@ function getCliVersion() {
30558
30774
  }
30559
30775
 
30560
30776
  // src/cli.ts
30561
- var main = defineCommand177({
30777
+ var main = defineCommand178({
30562
30778
  meta: {
30563
30779
  name: "baker",
30564
30780
  version: getCliVersion(),