@aicopycoders/exodus 2026.6.2900-beta.6 → 2026.6.2900-beta.8

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.
@@ -55,9 +55,9 @@ These are the **Exodus** pipelines — the automatic system that drives the Gene
55
55
  | **Write** | `exodus-write` | **Start here for copywriting.** Say what you want ("exodus, write me some ads"); it routes new-brand → foundation → brief → genesis → Doc and sequences the whole flow. |
56
56
  | **Genesis** | `exodus-genesis` | Brief → ad copy in two voices (Mario + Infeed). Default 1 pass = 2 variants; scale with `--passes` (pass 2 adds the Top-Ads-biased primer). Also writes from a pasted reel (`--reel`). |
57
57
  | **Idea** | `exodus-idea` | The Idea Bank — capture reels/brain-dumps/swipes into formed ideas, curate them, then write the keepers through Genesis. Use when collecting and curating ideas rather than writing one ad on the spot. |
58
- | **Image** | `exodus-image` | **Start here for static images.** Say what you want; it reads the request and routes to the right engine — `exodus-creative` (renders from copy) or `exodus-template` (33 ad-type formats). |
58
+ | **Image** | `exodus-image` | **Start here for static images.** Say what you want; it reads the request and routes to the right engine — `exodus-creative` (renders from copy) or `exodus-template` (50 ad-type formats). |
59
59
  | **Creative** | `exodus-creative` | A specific creative-suite engine (native / copy-derived renders from copy, or ref-match to a reference image) when you've already picked one. Otherwise start at **Image**. |
60
- | **Template** | `exodus-template` | Finished ad copy → many static ads across 33 structured formats (testimonial, hero, UGC…). Otherwise start at **Image**. |
60
+ | **Template** | `exodus-template` | Finished ad copy → many static ads across 50 structured formats (testimonial, hero, UGC…). Otherwise start at **Image**. |
61
61
  | **Meme** | `exodus-meme` | Meme ads — recommends formats for a brief, then one batched run renders AI-image and classic Imgflip-template memes server-side. |
62
62
  | **Browse** | `exodus-browse` | View history and retrieve past outputs |
63
63
  | **Drive** | `exodus-drive` | Read/write Google Docs, Sheets, and Drive files via `npx @aicopycoders/exodus drive` (uses the dashboard's Google OAuth — no local CLI) |
@@ -12,7 +12,7 @@ In Claude Code you don't click buttons. You say **"exodus"** plus what you want
12
12
  | exodus-genesis | Brief → Ad Copy (two voices; 1 pass = 2 variants, scalable via --passes) |
13
13
  | exodus-image | The front door for static images — routes to exodus-creative or exodus-template. Start here for "make me images / statics". |
14
14
  | exodus-creative | Creative-Suite Engines (native / copy-derived / ref-match) — when you've already picked an engine |
15
- | exodus-template | Ad-Type Format Variations across 33 structured formats |
15
+ | exodus-template | Ad-Type Format Variations across 50 structured formats |
16
16
  | exodus-meme | Meme Ad Generator — recommend formats, one batched server-side run |
17
17
  | exodus-browse | View History and Surface the Right Run |
18
18
  | exodus-drive | Google Drive, Docs, Sheets via the Dashboard's OAuth |
@@ -71,7 +71,7 @@ Examples:
71
71
  you want — "exodus, make me some statics from this ad", "exodus, render AD-001" —
72
72
  and Claude reads the request and runs the right engine: **exodus-creative**
73
73
  (renders straight from copy, or matches a reference image) or **exodus-template**
74
- (one piece of copy spread across 33 ad-type formats). You don't pick the engine;
74
+ (one piece of copy spread across 50 ad-type formats). You don't pick the engine;
75
75
  the request decides it. Use the engines below directly only when you already know
76
76
  exactly which one you want.
77
77
 
@@ -113,13 +113,13 @@ Auth: Bearer (kickoff via Convex HTTP; status via dashboard route).
113
113
 
114
114
  ## exodus-template
115
115
 
116
- **What it does:** Turn finished ad copy into many static ad images spread across structured ad-type formats (testimonial, hero, UGC, logo, infographic, and 28 more). Runs Fernando's 5-stage Template pipeline — give it one brief or a numbered list of ads and it generates format variations, then renders them.
116
+ **What it does:** Turn finished ad copy into many static ad images spread across structured ad-type formats (testimonial, hero, UGC, logo, infographic, and 45 more). Runs Fernando's 5-stage Template pipeline — give it one brief or a numbered list of ads and it generates format variations, then renders them.
117
117
 
118
118
  ```operator-guide
119
119
  Subcommands:
120
120
  exodus template run --input "<text>" [options] Kick off a run
121
121
  exodus template resume --id <runId> Finalize an orphaned run (#56)
122
- exodus template ad-types Print the 33 AD_TYPES
122
+ exodus template ad-types Print all 50 AD_TYPES
123
123
  exodus template reptile-triggers Print the 13 reptile triggers
124
124
 
125
125
  run options:
@@ -1,13 +1,14 @@
1
1
  ---
2
2
  name: exodus-template
3
- description: Spread finished ad copy across structured ad-type formats — testimonial, hero, UGC, logo, infographic, and 28 more — via Fernando's 5-stage Exodus Template pipeline (one brief or a numbered list of ads → format variations → renders). Only invoke when the user has explicitly invoked Exodus AND the ask is specifically about FORMAT COVERAGE: they said "exodus" in the request ("exodus template ads", "exodus, spread this copy across the 33 ad types", "exodus, give me testimonial + hero + UGC versions"), named this skill or /exodus-template, ran an `npx @aicopycoders/exodus template` command, or the `exodus` hub skill routed here. Never claim generic format requests ("template ads", "spread this copy across formats") — in shared folders those may belong to the user's other tools; if the user did not say exodus, this skill is not for them. For an unsure "exodus, just make me images / statics" ask, start at the `exodus-image` front door — it reads the request and routes here. For native / copy-derived / reference-match renders use `exodus-creative`; for meme formats use `exodus-meme`.
3
+ description: Spread finished ad copy across structured ad-type formats — testimonial, hero, UGC, logo, infographic, and 45 more — via Fernando's 5-stage Exodus Template pipeline (one brief or a numbered list of ads → format variations → renders). Only invoke when the user has explicitly invoked Exodus AND the ask is specifically about FORMAT COVERAGE: they said "exodus" in the request ("exodus template ads", "exodus, spread this copy across the 50 ad types", "exodus, give me testimonial + hero + UGC versions"), named this skill or /exodus-template, ran an `npx @aicopycoders/exodus template` command, or the `exodus` hub skill routed here. Never claim generic format requests ("template ads", "spread this copy across formats") — in shared folders those may belong to the user's other tools; if the user did not say exodus, this skill is not for them. For an unsure "exodus, just make me images / statics" ask, start at the `exodus-image` front door — it reads the request and routes here. For native / copy-derived / reference-match renders use `exodus-creative`; for meme formats use `exodus-meme`.
4
4
  ---
5
5
 
6
6
  ```operator-guide
7
7
  Subcommands:
8
8
  exodus template run --input "<text>" [options] Kick off a run
9
+ exodus template status --id <runId> Status + rendered image URLs
9
10
  exodus template resume --id <runId> Finalize an orphaned run (#56)
10
- exodus template ad-types Print the 33 AD_TYPES
11
+ exodus template ad-types Print all 50 AD_TYPES
11
12
  exodus template reptile-triggers Print the 13 reptile triggers
12
13
 
13
14
  run options:
@@ -27,13 +28,14 @@ run options:
27
28
  (use `--quantities`).
28
29
 
29
30
  Returns:
30
- runId + dashboard URL. POST-only — there is NO CLI status endpoint yet.
31
- Live progress renders in the dashboard at /creative-suite/template/sessions/<runId>.
31
+ runId + dashboard URL. Kickoff is fire-and-forget; poll with
32
+ `exodus template status --id <runId>` for status + the rendered image URLs.
33
+ Live progress also renders in the dashboard at /creative-suite/template/sessions/<runId>.
32
34
  ```
33
35
 
34
36
  # Template — Ad-Type Format Variations (Fernando's pipeline)
35
37
 
36
- Reach for this when the user has **finished ad copy** and wants it turned into a spread of **static image ads across structured formats** — testimonial, hero, UGC, infographic, logo, and the rest of the 33 AD_TYPES. The pipeline reads the copy, decides (or is told) which formats to produce, writes a render prompt per format, and renders the images.
38
+ Reach for this when the user has **finished ad copy** and wants it turned into a spread of **static image ads across structured formats** — testimonial, hero, UGC, infographic, logo, and the rest of the 50 AD_TYPES. The pipeline reads the copy, decides (or is told) which formats to produce, writes a render prompt per format, and renders the images.
37
39
 
38
40
  This is the **format-coverage** tool. It is not engine-based rendering (that's `exodus-creative`: native / copy-derived / ref-match).
39
41
 
@@ -76,11 +78,11 @@ With controls:
76
78
 
77
79
  **Cheap smoke test:** use `--render-mode prompts` to validate the format selection + prompts without burning render compute, then re-run with `--render-mode images` once the mix looks right.
78
80
 
79
- The command returns a `runId` + dashboard URL and exits — there is **no CLI status polling** for template (the kickoff route is POST-only). Renders land in the dashboard.
81
+ The command returns a `runId` + dashboard URL and exits — kickoff is fire-and-forget. Poll with `exodus template status --id <runId>` to read the status and pull the rendered image URLs once they complete.
80
82
 
81
83
  ### 3. Report
82
84
 
83
- Per the **Default Post-Run Reporting** rule in `exodus-strategist`: surface the dashboard URL + a 2-line take, then stop. Don't claim the images are done — the CLI only confirms the run *started*; the renders complete asynchronously in the dashboard. Tell the user where to watch: `/creative-suite/template/sessions/<runId>`.
85
+ Per the **Default Post-Run Reporting** rule in `exodus-strategist`: surface the dashboard URL + a 2-line take, then stop. Don't claim the images are done off the kickoff — that only confirms the run *started*; the renders complete asynchronously. Use `exodus template status --id <runId>` to confirm completion and retrieve the render URLs, or point the user to `/creative-suite/template/sessions/<runId>`.
84
86
 
85
87
  ### 4. Recover an orphaned run
86
88
 
@@ -95,12 +97,13 @@ If a previous run's renders stalled or were left incomplete, finalize it:
95
97
  - **`--mode manual` without `--quantities`** (or `hybrid` without `--pairings`) errors immediately — supply the required flag.
96
98
  - **Unknown ad-type slug** in `--quantities`/`--pairings` errors with the bad slug named. Run `exodus template ad-types` and use the exact slug.
97
99
  - **whoami failure** ("Check EXODUS_API_KEY…") means auth/base-URL isn't resolving — run `npx @aicopycoders/exodus doctor`.
98
- - **No CLI status** — if the user asks "is it done?", point them to the dashboard sessions URL; there is no `exodus status` for template yet. Don't fabricate a status.
100
+ - **Checking status** — if the user asks "is it done?", run `exodus template status --id <runId>`: it returns the run status and the rendered image URLs. Don't fabricate a status; read it.
99
101
  - **`--ref-image` is NOT supported in V1** — the Bearer kickoff route doesn't accept reference image ids. If the user wants reference matching, route to the `exodus-creative` skill's ref-match instead.
100
102
 
101
103
  ## Admin
102
104
 
103
105
  - Backend: Convex HTTP `POST /api/creative-suite-template/run` (Bearer-auth); Trigger.dev task `creative-suite-template` (Fernando's 5-stage pipeline).
104
- - 33 AD_TYPES and 13 reptile triggers are inspectable via `exodus template ad-types` / `exodus template reptile-triggers`.
106
+ - 50 AD_TYPES and 13 reptile triggers are inspectable via `exodus template ad-types` / `exodus template reptile-triggers`.
107
+ - Status hits `GET /api/v2/template?runId=<id>` (status + rendered image URLs).
105
108
  - Resume hits `POST /api/creative-suite-template/runs/<id>/resume`.
106
109
  - Don't echo render prompts or bot system prompts in your reply — that's IP.
@@ -1,2 +1,11 @@
1
1
  export declare const helpText: string;
2
+ interface CreativeImage {
3
+ url: string;
4
+ source?: string;
5
+ cNumber?: string;
6
+ title?: string;
7
+ model?: string;
8
+ }
9
+ export declare function formatImageLines(images: CreativeImage[] | undefined): string[];
2
10
  export declare function run(flags: Record<string, string | boolean>): Promise<void>;
11
+ export {};
@@ -186,13 +186,28 @@ async function runStatus(flags) {
186
186
  if (d.completedImages !== undefined || d.totalImages !== undefined) {
187
187
  console.log(`progress: ${d.completedImages ?? 0} / ${d.totalImages ?? "?"} completed${d.failedImages ? `, ${d.failedImages} failed` : ""}`);
188
188
  }
189
- else if (d.imageCount !== undefined) {
189
+ else if (d.imageCount !== undefined && (!d.images || d.images.length === 0)) {
190
190
  console.log(`images: ${d.imageCount}`);
191
191
  }
192
192
  if (d.errorMessage)
193
193
  console.log(`error: ${d.errorMessage}`);
194
+ printImageUrls(d.images);
194
195
  console.log(`dashboard: ${getDashboardUrl()}/creative-suite/runs/${runId}`);
195
196
  }
197
+ export function formatImageLines(images) {
198
+ if (!images || images.length === 0)
199
+ return [];
200
+ const lines = [`images: ${images.length} (URLs below)`];
201
+ images.forEach((im, i) => {
202
+ const label = [im.cNumber, im.source].filter(Boolean).join(" · ");
203
+ lines.push(` ${String(i + 1).padStart(2)}. ${im.url}${label ? ` (${label})` : ""}`);
204
+ });
205
+ return lines;
206
+ }
207
+ function printImageUrls(images) {
208
+ for (const line of formatImageLines(images))
209
+ console.log(line);
210
+ }
196
211
  export async function run(flags) {
197
212
  const sub = process.argv[3] ?? "";
198
213
  if (sub === "native") {
@@ -1,2 +1,10 @@
1
1
  export declare const helpText: string;
2
+ interface TemplateRender {
3
+ url: string;
4
+ adType?: string;
5
+ status?: string;
6
+ model?: string;
7
+ }
8
+ export declare function formatRenderLines(renders: TemplateRender[] | undefined): string[];
2
9
  export declare function run(flags: Record<string, string | boolean>): Promise<void>;
10
+ export {};
@@ -2,10 +2,11 @@ import { apiGet, apiPost, apiPostDashboard, getDashboardUrl } from "../lib/clien
2
2
  import { formatError } from "../lib/format.js";
3
3
  import { AD_TYPES, AD_TYPE_NAMES, REPTILE_TRIGGERS, } from "../lib/template-constants.js";
4
4
  export const helpText = `
5
- exodus template — Fernando's Template pipeline (33 AD_TYPES, resume)
5
+ exodus template — Fernando's Template pipeline (${AD_TYPES.length} AD_TYPES, resume)
6
6
 
7
7
  Usage:
8
8
  exodus template run --input "<ad brief or numbered ads>" [options]
9
+ exodus template status --id <runId>
9
10
  exodus template resume --id <runId>
10
11
  exodus template ad-types
11
12
  exodus template reptile-triggers
@@ -20,19 +21,24 @@ Run options:
20
21
  --realism off|realistic Realism enforcement (default: off)
21
22
  --quantities <slug:N,slug:N> Manual-mode per-type counts (e.g. "testimonial:3,hero:2")
22
23
  --requested-count N Auto-mode total render target (optional)
23
- --no-wait (default — no CLI-side polling endpoint yet)
24
+ --no-wait (default — kickoff is fire-and-forget; poll with status)
25
+
26
+ Status options:
27
+ --id <runId> Template run to read back (status + render URLs)
24
28
 
25
29
  Resume options:
26
30
  --id <runId> Template run id to resume (#56 orphan finalization)
27
31
 
28
- Status polling:
29
- The Convex HTTP route is POST-only. View live progress at the dashboard URL
30
- printed at kickoff (or the dashboard's /creative-suite/template/sessions list).
32
+ Status:
33
+ exodus template status --id <runId> prints the run status and the rendered
34
+ image URLs (GET /api/v2/template). Live progress is also on the dashboard URL
35
+ printed at kickoff.
31
36
 
32
37
  Examples:
33
38
  exodus template run --input "silver-threaded grounding sheets for inflammation"
34
39
  exodus template run --input "..." --aspect 9:16 --model nano-banana-pro --realism realistic
35
40
  exodus template run --input "..." --mode manual --quantities "testimonial:3,hero:2,ugc:4"
41
+ exodus template status --id <runId>
36
42
  exodus template resume --id <runId>
37
43
  exodus template ad-types
38
44
  `.trim();
@@ -167,7 +173,7 @@ async function runTemplate(flags) {
167
173
  console.log(` triggerRunId: ${triggerRunId}`);
168
174
  console.log(` dashboard: ${dashboardUrlForRun(runId)}`);
169
175
  console.log("");
170
- console.log("Status updates render live in the dashboard (no CLI status endpoint yet).");
176
+ console.log(`Poll status + pull render URLs: exodus template status --id ${runId}`);
171
177
  }
172
178
  async function runResume(flags) {
173
179
  const runId = typeof flags["id"] === "string" ? flags["id"] : undefined;
@@ -193,6 +199,42 @@ async function runResume(flags) {
193
199
  console.log(` triggerRunId: ${triggerRunId}`);
194
200
  console.log(` dashboard: ${dashboardUrlForRun(runId)}`);
195
201
  }
202
+ export function formatRenderLines(renders) {
203
+ if (!renders || renders.length === 0)
204
+ return [];
205
+ const lines = [`renders: ${renders.length} (URLs below)`];
206
+ renders.forEach((r, i) => {
207
+ lines.push(` ${String(i + 1).padStart(2)}. ${r.url}${r.adType ? ` (${r.adType})` : ""}`);
208
+ });
209
+ return lines;
210
+ }
211
+ async function runStatus(flags) {
212
+ const runId = typeof flags["id"] === "string" ? flags["id"] : undefined;
213
+ if (!runId) {
214
+ console.error("Error: template status requires --id <runId>.");
215
+ process.exit(1);
216
+ return;
217
+ }
218
+ const res = await apiGet(`/api/v2/template?runId=${encodeURIComponent(runId)}`);
219
+ if (!res.ok) {
220
+ console.error(formatError(res));
221
+ process.exit(1);
222
+ return;
223
+ }
224
+ const d = res.data;
225
+ console.log(`runId: ${d._id ?? runId}`);
226
+ if (d.name)
227
+ console.log(`name: ${d.name}`);
228
+ console.log(`status: ${d.status ?? "—"}`);
229
+ if (d.completedImageCount !== undefined || d.requestedImageCount !== undefined) {
230
+ console.log(`progress: ${d.completedImageCount ?? 0} / ${d.requestedImageCount ?? "?"} completed${d.failedImageCount ? `, ${d.failedImageCount} failed` : ""}`);
231
+ }
232
+ if (d.errorMessage)
233
+ console.log(`error: ${d.errorMessage}`);
234
+ for (const line of formatRenderLines(d.renders))
235
+ console.log(line);
236
+ console.log(`dashboard: ${dashboardUrlForRun(runId)}`);
237
+ }
196
238
  export async function run(flags) {
197
239
  const sub = process.argv[3] ?? "";
198
240
  if (sub === "ad-types") {
@@ -207,12 +249,16 @@ export async function run(flags) {
207
249
  await runResume(flags);
208
250
  return;
209
251
  }
252
+ if (sub === "status") {
253
+ await runStatus(flags);
254
+ return;
255
+ }
210
256
  if (sub === "run") {
211
257
  await runTemplate(flags);
212
258
  return;
213
259
  }
214
260
  if (!sub || sub.startsWith("--")) {
215
- console.error("Error: template requires a subcommand (run | resume | ad-types | reptile-triggers).");
261
+ console.error("Error: template requires a subcommand (run | resume | status | ad-types | reptile-triggers).");
216
262
  console.error("");
217
263
  console.error(helpText);
218
264
  process.exit(1);
@@ -1,4 +1,4 @@
1
- export declare const AD_TYPES: readonly ["before-after", "native-news", "testimonial", "bold", "meme", "screenshot", "hero", "holding-sign", "handwritten", "comment", "breaking-news", "lofi", "comparison", "infographic", "headline", "collage", "product-breakdown", "scientific", "animation", "founder-note", "carousel", "statistics", "post-it-notes", "happy-avatar", "problem-solution", "writing-on-body", "multi-testimonial", "receipt", "step-by-step", "sale-promotional-offer", "cost-of-inaction", "ugc", "quiz-interactive"];
1
+ export declare const AD_TYPES: readonly ["before-after", "native-news", "testimonial", "bold", "meme", "screenshot", "hero", "holding-sign", "handwritten", "comment", "breaking-news", "lofi", "comparison", "infographic", "headline", "collage", "product-breakdown", "scientific", "animation", "founder-note", "carousel", "statistics", "post-it-notes", "happy-avatar", "problem-solution", "writing-on-body", "multi-testimonial", "receipt", "step-by-step", "sale-promotional-offer", "cost-of-inaction", "ugc", "quiz-interactive", "action-figure", "at-home-routine", "billboard", "black-and-white", "blurred", "choose", "cutout", "drawing", "facetime", "marker", "notes", "photo", "quadrant", "search", "thumbnail", "xray", "zoom-bubble"];
2
2
  export type AdType = (typeof AD_TYPES)[number];
3
3
  export declare const AD_TYPE_NAMES: Record<AdType, string>;
4
4
  export declare const REPTILE_TRIGGERS: readonly ["ultra-real", "bizarre", "voyeur", "suffering", "gory", "sexual", "primal-fear", "odd-contrast", "inside-joke", "time-warp", "victory-lap", "selfie", "uncanny-objects"];
@@ -32,6 +32,23 @@ export const AD_TYPES = [
32
32
  "cost-of-inaction",
33
33
  "ugc",
34
34
  "quiz-interactive",
35
+ "action-figure",
36
+ "at-home-routine",
37
+ "billboard",
38
+ "black-and-white",
39
+ "blurred",
40
+ "choose",
41
+ "cutout",
42
+ "drawing",
43
+ "facetime",
44
+ "marker",
45
+ "notes",
46
+ "photo",
47
+ "quadrant",
48
+ "search",
49
+ "thumbnail",
50
+ "xray",
51
+ "zoom-bubble",
35
52
  ];
36
53
  export const AD_TYPE_NAMES = {
37
54
  "before-after": "Before / After",
@@ -67,6 +84,23 @@ export const AD_TYPE_NAMES = {
67
84
  "cost-of-inaction": "Cost of Inaction",
68
85
  ugc: "UGC",
69
86
  "quiz-interactive": "Quiz / Interactive Self-Selection",
87
+ "action-figure": "Action Figure",
88
+ "at-home-routine": "At Home Routine",
89
+ billboard: "Billboard",
90
+ "black-and-white": "Black and White",
91
+ blurred: "Blurred",
92
+ choose: "Choose",
93
+ cutout: "Cutout",
94
+ drawing: "Drawing",
95
+ facetime: "FaceTime",
96
+ marker: "Marker",
97
+ notes: "Notes",
98
+ photo: "Photo",
99
+ quadrant: "Quadrant",
100
+ search: "Search",
101
+ thumbnail: "Thumbnail",
102
+ xray: "X-ray",
103
+ "zoom-bubble": "Zoom Bubble",
70
104
  };
71
105
  export const REPTILE_TRIGGERS = [
72
106
  "ultra-real",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aicopycoders/exodus",
3
- "version": "2026.6.2900-beta.6",
3
+ "version": "2026.6.2900-beta.8",
4
4
  "description": "CLI + Claude Code skills for the Copycoders Exodus ad pipeline",
5
5
  "repository": {
6
6
  "type": "git",