@aicopycoders/exodus 2026.10.1600-beta.1 → 2026.10.1700

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.
@@ -152,7 +152,7 @@ run options:
152
152
  --mode auto|manual|hybrid Generation mode (default: auto)
153
153
  --render-mode images|prompts prompts = skip render, return LLM prompts only (default: images)
154
154
  --aspect 1:1|9:16 Image aspect ratio (default: 1:1)
155
- --model gpt-image-2.5|gpt-image-2.5-sunburst|gpt-image-2|nano-banana-pro
155
+ --model gpt-image-2.5|gpt-image-2.5-sunburst|gpt-image-2|nano-banana-pro|nano-banana-2-1
156
156
  Kie.ai model (default: gpt-image-2.5)
157
157
  --realism off|realistic Realism enforcement (default: off)
158
158
  --quantities "slug:N,slug:N" manual-mode per-type counts (e.g. "testimonial:3,hero:2")
@@ -17,7 +17,7 @@ run options:
17
17
  --mode auto|manual|hybrid Generation mode (default: auto)
18
18
  --render-mode images|prompts prompts = skip render, return LLM prompts only (default: images)
19
19
  --aspect 1:1|9:16 Image aspect ratio (default: 1:1)
20
- --model gpt-image-2.5|gpt-image-2.5-sunburst|gpt-image-2|nano-banana-pro
20
+ --model gpt-image-2.5|gpt-image-2.5-sunburst|gpt-image-2|nano-banana-pro|nano-banana-2-1
21
21
  Kie.ai model (default: gpt-image-2.5)
22
22
  --realism off|realistic Realism enforcement (default: off)
23
23
  --quantities "slug:N,slug:N" manual-mode per-type counts (e.g. "testimonial:3,hero:2")
@@ -377,11 +377,19 @@ What the CLI prints, and what you tell the user:
377
377
  can't tell who says a line. The error names the line, and `--json` lists them
378
378
  under `lines`. Ask the user who says it, put the name in front of that line
379
379
  (`SAM: …`), and run the same command again.
380
- - **Today's refusal (exit 1, nothing started):** the description was too thin
381
- to write from, the user has no working AI key for the tidy, or the AI call
382
- failed. The answer is the format's own message (for example "Line 1 has no
383
- speaker"). Ask the user for more detail about the ad, or for their AI key in
384
- Settings, or lay the file out in the format below yourself. A description
380
+ - **Too thin (`SCRIPT_TOO_THIN`, exit 1, nothing started):** the file only
381
+ described the ad, and there wasn't enough in it to write a script from. The
382
+ error says what is missing, in the dashboard's words ("There isn't enough
383
+ here to write a script from. Tell us what the product is."), and `--json`
384
+ lists the gaps under `missing` (`product`, `speaker`, `what-happens`). Ask
385
+ the user for what it names, add it to the file, and run the same command
386
+ again.
387
+ - **Today's refusal (exit 1, nothing started):** the user has no working AI
388
+ key for the tidy, the AI call failed, or the server could not write a usable
389
+ script from a description. The answer is the format's own message (for
390
+ example "Line 1 has no speaker"). Ask the user for more detail about the ad,
391
+ or for their AI key in Settings, or lay the file out in the format below
392
+ yourself. A description
385
393
  that reports what someone says ("a guy says it stopped his crash") is
386
394
  sometimes refused this way (#3747): describe the ad without the reported
387
395
  words, or give the lines as a draft, and run it again.
@@ -391,8 +399,9 @@ What the CLI prints, and what you tell the user:
391
399
  already in the format, so it is not tidied or paid for again.
392
400
 
393
401
  **`--stop-at-script`** prints the script the run would start on and starts
394
- nothing (exit 0). Use it when the user wants to read a written or laid-out
395
- script before anything is spent. To go ahead, save the printed script to a file
402
+ nothing (exit 0). A question or a description too thin to write from answers
403
+ as above instead (exit 1). Use it when the user wants to read a written or
404
+ laid-out script before anything is spent. To go ahead, save the printed script to a file
396
405
  and run the same command with `--script <that file>` and without the flag.
397
406
  `--json` answers `{ stoppedAtScript: true, script, tidied?, written? }`.
398
407
 
@@ -17,7 +17,7 @@ Run options:
17
17
  --mode auto|manual Generation mode (default: auto)
18
18
  --render-mode images|prompts Skip render and return prompts only (default: images)
19
19
  --aspect 1:1|9:16 Image aspect ratio (default: 1:1)
20
- --model gpt-image-2.5|gpt-image-2.5-sunburst|gpt-image-2|nano-banana-pro
20
+ --model gpt-image-2.5|gpt-image-2.5-sunburst|gpt-image-2|nano-banana-pro|nano-banana-2-1
21
21
  Kie.ai model (default: gpt-image-2.5)
22
22
  --realism off|realistic Realism enforcement (default: off)
23
23
  --quantities <slug:N,slug:N> Manual-mode per-type counts (e.g. "testimonial:3,hero:2")
@@ -109,6 +109,7 @@ async function runTemplate(flags) {
109
109
  "gpt-image-2.5-sunburst",
110
110
  "gpt-image-2",
111
111
  "nano-banana-pro",
112
+ "nano-banana-2-1",
112
113
  ];
113
114
  const model = typeof flags["model"] === "string" ? flags["model"] : "gpt-image-2.5";
114
115
  if (!TEMPLATE_MODELS.includes(model)) {
@@ -162,7 +162,8 @@ Options:
162
162
  and says what it did. A description gets a script
163
163
  written from it, printed, and the run starts on it. If
164
164
  the server can't tell who says a line, it asks and
165
- starts nothing
165
+ starts nothing. If a description is too thin to write
166
+ from, it says what is missing and starts nothing
166
167
  --stop-at-script Print the script the run would start on, and start
167
168
  nothing (start, with --conceit). To read a written or
168
169
  laid-out script first; send it back with --script
@@ -1633,16 +1634,21 @@ function scriptWrittenHead(written, started) {
1633
1634
  function writtenOf(value) {
1634
1635
  return typeof value === "string" && value.trim() !== "" ? value : null;
1635
1636
  }
1636
- function scriptQuestionResult(res, json) {
1637
+ function scriptNeedsCallerResult(res, json) {
1637
1638
  const data = res.data;
1638
- if (!isRecord(data) || !isRecord(data.error) || data.error.code !== "SCRIPT_QUESTION")
1639
+ if (!isRecord(data) || !isRecord(data.error))
1640
+ return null;
1641
+ const code = data.error.code;
1642
+ const field = code === "SCRIPT_QUESTION" ? "lines" : code === "SCRIPT_TOO_THIN" ? "missing" : null;
1643
+ if (field === null)
1639
1644
  return null;
1640
1645
  const message = formatApiError(res);
1641
- const lines = stringsOf(data.lines);
1642
1646
  if (json) {
1643
1647
  return {
1644
1648
  code: 1,
1645
- lines: [JSON.stringify({ ok: false, status: res.status, code: "SCRIPT_QUESTION", error: message, lines })],
1649
+ lines: [
1650
+ JSON.stringify({ ok: false, status: res.status, code, error: message, [field]: stringsOf(data[field]) }),
1651
+ ],
1646
1652
  };
1647
1653
  }
1648
1654
  return { code: 1, lines: [message] };
@@ -1778,7 +1784,7 @@ export async function startScriptFlow(opts, deps) {
1778
1784
  ...(opts.stopAtScript ? { stopAtScript: true } : {}),
1779
1785
  });
1780
1786
  if (!res.ok)
1781
- return scriptQuestionResult(res, opts.json) ?? scriptStartRefused(res, opts.json);
1787
+ return scriptNeedsCallerResult(res, opts.json) ?? scriptStartRefused(res, opts.json);
1782
1788
  if (isRecord(res.data) && res.data.stoppedAtScript === true) {
1783
1789
  return stoppedAtScriptResult(res.data, opts.json);
1784
1790
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aicopycoders/exodus",
3
- "version": "2026.10.1600-beta.1",
3
+ "version": "2026.10.1700",
4
4
  "description": "CLI + Claude Code skills for the Copycoders Exodus ad pipeline",
5
5
  "repository": {
6
6
  "type": "git",