@aicopycoders/exodus 2026.6.2900-beta.5 → 2026.6.2900-beta.7

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,13 @@
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
9
  exodus template resume --id <runId> Finalize an orphaned run (#56)
10
- exodus template ad-types Print the 33 AD_TYPES
10
+ exodus template ad-types Print all 50 AD_TYPES
11
11
  exodus template reptile-triggers Print the 13 reptile triggers
12
12
 
13
13
  run options:
@@ -33,7 +33,7 @@ Returns:
33
33
 
34
34
  # Template — Ad-Type Format Variations (Fernando's pipeline)
35
35
 
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.
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 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
37
 
38
38
  This is the **format-coverage** tool. It is not engine-based rendering (that's `exodus-creative`: native / copy-derived / ref-match).
39
39
 
@@ -101,6 +101,6 @@ If a previous run's renders stalled or were left incomplete, finalize it:
101
101
  ## Admin
102
102
 
103
103
  - 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`.
104
+ - 50 AD_TYPES and 13 reptile triggers are inspectable via `exodus template ad-types` / `exodus template reptile-triggers`.
105
105
  - Resume hits `POST /api/creative-suite-template/runs/<id>/resume`.
106
106
  - Don't echo render prompts or bot system prompts in your reply — that's IP.
@@ -1,4 +1,4 @@
1
- import { PROVIDER_ENV_MAP, fetchRemoteKeys, mapKeysToEnvVars, resolveEnvFilePath, upsertEnvVars, } from "../lib/keys-sync.js";
1
+ import { PROVIDER_ENV_MAP, SERVER_RESOLVED_PROVIDERS, fetchRemoteKeys, mapKeysToEnvVars, resolveEnvFilePath, upsertEnvVars, } from "../lib/keys-sync.js";
2
2
  export const helpText = `
3
3
  exodus keys — sync your provider API keys from the dashboard to local .env
4
4
 
@@ -24,6 +24,7 @@ const PROVIDER_LABEL = {
24
24
  openrouter: "OpenRouter",
25
25
  elevenlabs: "ElevenLabs",
26
26
  kie: "Kie.ai",
27
+ imgflip: "Imgflip",
27
28
  };
28
29
  async function status() {
29
30
  let remote;
@@ -46,6 +47,12 @@ async function status() {
46
47
  const local = localSet ? "local ✓" : "local —";
47
48
  console.log(` ${label} ${dash} ${local} (${envName})`);
48
49
  }
50
+ for (const provider of SERVER_RESOLVED_PROVIDERS) {
51
+ const inDashboard = provider in remote.keys;
52
+ const label = (PROVIDER_LABEL[provider] ?? provider).padEnd(11);
53
+ const dash = inDashboard ? "dashboard ✓" : "dashboard —";
54
+ console.log(` ${label} ${dash} server-side (Settings → Keys; no .env)`);
55
+ }
49
56
  if (remote.failed.length) {
50
57
  console.log(`\n⚠ Could not decrypt: ${remote.failed.join(", ")} — re-save in the dashboard.`);
51
58
  }
@@ -69,7 +76,7 @@ async function pull() {
69
76
  }
70
77
  return;
71
78
  }
72
- const { vars, skipped } = mapKeysToEnvVars(remote.keys);
79
+ const { vars, serverResolved, skipped } = mapKeysToEnvVars(remote.keys);
73
80
  const envPath = resolveEnvFilePath();
74
81
  const results = upsertEnvVars(envPath, vars);
75
82
  const added = results.filter((r) => r.action === "added").map((r) => r.name);
@@ -82,6 +89,9 @@ async function pull() {
82
89
  console.log(` updated: ${updated.join(", ")}`);
83
90
  if (unchanged.length)
84
91
  console.log(` unchanged: ${unchanged.join(", ")}`);
92
+ if (serverResolved.length) {
93
+ console.log(` dashboard: ${serverResolved.join(", ")} — used server-side from Settings → Keys; no .env entry needed.`);
94
+ }
85
95
  if (skipped.length) {
86
96
  console.log(` skipped: ${skipped.join(", ")} (no known env var mapping)`);
87
97
  }
@@ -2,7 +2,7 @@ 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]
@@ -1,4 +1,5 @@
1
1
  export declare const PROVIDER_ENV_MAP: Record<string, string>;
2
+ export declare const SERVER_RESOLVED_PROVIDERS: Set<string>;
2
3
  export interface RemoteKeys {
3
4
  keys: Record<string, string>;
4
5
  failed: string[];
@@ -12,5 +13,6 @@ export interface UpsertResult {
12
13
  export declare function upsertEnvVars(filePath: string, vars: Record<string, string>): UpsertResult[];
13
14
  export declare function mapKeysToEnvVars(keys: Record<string, string>): {
14
15
  vars: Record<string, string>;
16
+ serverResolved: string[];
15
17
  skipped: string[];
16
18
  };
@@ -8,6 +8,7 @@ export const PROVIDER_ENV_MAP = {
8
8
  elevenlabs: "ELEVENLABS_API_KEY",
9
9
  kie: "KIE_API_KEY",
10
10
  };
11
+ export const SERVER_RESOLVED_PROVIDERS = new Set(["imgflip"]);
11
12
  export async function fetchRemoteKeys() {
12
13
  const res = await apiGetDashboard("/api/settings/keys");
13
14
  if (!res.ok) {
@@ -70,15 +71,18 @@ export function upsertEnvVars(filePath, vars) {
70
71
  }
71
72
  export function mapKeysToEnvVars(keys) {
72
73
  const vars = {};
74
+ const serverResolved = [];
73
75
  const skipped = [];
74
76
  for (const [provider, value] of Object.entries(keys)) {
75
77
  const envName = PROVIDER_ENV_MAP[provider];
76
78
  if (envName)
77
79
  vars[envName] = value;
80
+ else if (SERVER_RESOLVED_PROVIDERS.has(provider))
81
+ serverResolved.push(provider);
78
82
  else
79
83
  skipped.push(provider);
80
84
  }
81
- return { vars, skipped };
85
+ return { vars, serverResolved, skipped };
82
86
  }
83
87
  function formatEnvValue(value) {
84
88
  if (/[\s#'"]/.test(value)) {
@@ -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.5",
3
+ "version": "2026.6.2900-beta.7",
4
4
  "description": "CLI + Claude Code skills for the Copycoders Exodus ad pipeline",
5
5
  "repository": {
6
6
  "type": "git",