@laitszkin/apollo-toolkit 4.1.4 → 5.0.1

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.
Files changed (205) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/bin/apollo-toolkit.ts +4 -0
  3. package/dist/bin/apollo-toolkit.js +4 -0
  4. package/package.json +3 -2
  5. package/packages/cli/dist/help-text-builder.d.ts +23 -0
  6. package/packages/cli/dist/help-text-builder.js +166 -0
  7. package/packages/cli/dist/index.d.ts +6 -17
  8. package/packages/cli/dist/index.js +52 -246
  9. package/packages/cli/dist/installer.d.ts +1 -0
  10. package/packages/cli/dist/installer.js +20 -7
  11. package/packages/cli/dist/parsers/install-parser.d.ts +15 -0
  12. package/packages/cli/dist/parsers/install-parser.js +87 -0
  13. package/packages/cli/dist/parsers/parser-utils.d.ts +9 -0
  14. package/packages/cli/dist/parsers/parser-utils.js +16 -0
  15. package/packages/cli/dist/parsers/tool-parser.d.ts +16 -0
  16. package/packages/cli/dist/parsers/tool-parser.js +58 -0
  17. package/packages/cli/dist/parsers/types.d.ts +50 -0
  18. package/packages/cli/dist/parsers/types.js +1 -0
  19. package/packages/cli/dist/parsers/uninstall-parser.d.ts +15 -0
  20. package/packages/cli/dist/parsers/uninstall-parser.js +67 -0
  21. package/packages/cli/dist/tool-registration.d.ts +2 -0
  22. package/packages/cli/dist/tool-registration.js +2 -0
  23. package/packages/cli/dist/tsconfig.tsbuildinfo +1 -1
  24. package/packages/cli/dist/types.d.ts +3 -1
  25. package/packages/cli/dist/updater.js +11 -5
  26. package/packages/cli/help-text-builder.ts +180 -0
  27. package/packages/cli/index.ts +59 -251
  28. package/packages/cli/installer.ts +19 -7
  29. package/packages/cli/package.json +14 -4
  30. package/packages/cli/parsers/install-parser.ts +94 -0
  31. package/packages/cli/parsers/parser-utils.ts +17 -0
  32. package/packages/cli/parsers/tool-parser.ts +65 -0
  33. package/packages/cli/parsers/types.ts +56 -0
  34. package/packages/cli/parsers/uninstall-parser.ts +75 -0
  35. package/packages/cli/tool-registration.ts +3 -0
  36. package/packages/cli/types.ts +6 -1
  37. package/packages/cli/updater.ts +11 -5
  38. package/packages/tool-registry/dist/registry.js +3 -4
  39. package/packages/tool-registry/dist/tsconfig.tsbuildinfo +1 -1
  40. package/packages/tool-registry/dist/types.d.ts +2 -9
  41. package/packages/tool-registry/package.json +11 -4
  42. package/packages/tool-registry/registry.ts +3 -4
  43. package/packages/tool-registry/tsconfig.json +6 -2
  44. package/packages/tool-registry/types.ts +3 -9
  45. package/packages/tool-utils/app-error.ts +97 -0
  46. package/packages/tool-utils/dist/app-error.d.ts +49 -0
  47. package/packages/tool-utils/dist/app-error.js +80 -0
  48. package/packages/tool-utils/dist/index.d.ts +5 -0
  49. package/packages/tool-utils/dist/index.js +3 -0
  50. package/packages/tool-utils/dist/platform-adapter.d.ts +48 -0
  51. package/packages/tool-utils/dist/platform-adapter.js +73 -0
  52. package/packages/tool-utils/dist/schema.d.ts +68 -0
  53. package/packages/tool-utils/dist/schema.js +67 -0
  54. package/packages/tool-utils/dist/tsconfig.tsbuildinfo +1 -1
  55. package/packages/tool-utils/index.ts +12 -0
  56. package/packages/tool-utils/package.json +11 -4
  57. package/packages/tool-utils/platform-adapter.ts +112 -0
  58. package/packages/tool-utils/schema.ts +122 -0
  59. package/packages/tools/architecture/dist/index.d.ts +13 -0
  60. package/packages/tools/architecture/dist/index.js +55 -57
  61. package/packages/tools/architecture/dist/index.test.js +17 -4
  62. package/packages/tools/architecture/dist/tsconfig.tsbuildinfo +1 -1
  63. package/packages/tools/architecture/index.test.ts +27 -14
  64. package/packages/tools/architecture/index.ts +85 -88
  65. package/packages/tools/architecture/package.json +11 -4
  66. package/packages/tools/codegraph/dist/index.js +12 -22
  67. package/packages/tools/codegraph/dist/tsconfig.tsbuildinfo +1 -1
  68. package/packages/tools/codegraph/index.ts +13 -22
  69. package/packages/tools/codegraph/package.json +11 -4
  70. package/packages/tools/create-review-report/dist/index.d.ts +1 -2
  71. package/packages/tools/create-review-report/dist/index.js +46 -77
  72. package/packages/tools/create-review-report/dist/tsconfig.tsbuildinfo +1 -1
  73. package/packages/tools/create-review-report/index.ts +52 -81
  74. package/packages/tools/create-review-report/package.json +11 -4
  75. package/packages/tools/create-specs/dist/index.d.ts +1 -2
  76. package/packages/tools/create-specs/dist/index.js +70 -123
  77. package/packages/tools/create-specs/dist/tsconfig.tsbuildinfo +1 -1
  78. package/packages/tools/create-specs/index.ts +82 -128
  79. package/packages/tools/create-specs/package.json +11 -4
  80. package/packages/tools/docs-to-voice/dist/index.d.ts +1 -2
  81. package/packages/tools/docs-to-voice/dist/index.js +116 -219
  82. package/packages/tools/docs-to-voice/dist/tsconfig.tsbuildinfo +1 -1
  83. package/packages/tools/docs-to-voice/index.ts +265 -385
  84. package/packages/tools/docs-to-voice/package.json +11 -4
  85. package/packages/tools/enforce-video-aspect-ratio/dist/index.d.ts +1 -2
  86. package/packages/tools/enforce-video-aspect-ratio/dist/index.js +77 -154
  87. package/packages/tools/enforce-video-aspect-ratio/dist/tsconfig.tsbuildinfo +1 -1
  88. package/packages/tools/enforce-video-aspect-ratio/index.ts +87 -172
  89. package/packages/tools/enforce-video-aspect-ratio/package.json +11 -4
  90. package/packages/tools/eval/dist/index.js +7 -0
  91. package/packages/tools/eval/dist/tsconfig.tsbuildinfo +1 -1
  92. package/packages/tools/eval/index.ts +8 -0
  93. package/packages/tools/eval/package.json +11 -4
  94. package/packages/tools/extract-conversations/dist/index.d.ts +1 -2
  95. package/packages/tools/extract-conversations/dist/index.js +31 -29
  96. package/packages/tools/extract-conversations/dist/tsconfig.tsbuildinfo +1 -1
  97. package/packages/tools/extract-conversations/index.ts +37 -30
  98. package/packages/tools/extract-conversations/package.json +11 -4
  99. package/packages/tools/extract-pdf-text/dist/index.d.ts +1 -2
  100. package/packages/tools/extract-pdf-text/dist/index.js +44 -65
  101. package/packages/tools/extract-pdf-text/dist/tsconfig.tsbuildinfo +1 -1
  102. package/packages/tools/extract-pdf-text/index.ts +55 -74
  103. package/packages/tools/extract-pdf-text/package.json +11 -4
  104. package/packages/tools/filter-logs/dist/index.js +60 -84
  105. package/packages/tools/filter-logs/dist/tsconfig.tsbuildinfo +1 -1
  106. package/packages/tools/filter-logs/index.ts +67 -97
  107. package/packages/tools/filter-logs/package.json +11 -4
  108. package/packages/tools/find-github-issues/dist/index.d.ts +10 -0
  109. package/packages/tools/find-github-issues/dist/index.js +34 -5
  110. package/packages/tools/find-github-issues/dist/tsconfig.tsbuildinfo +1 -1
  111. package/packages/tools/find-github-issues/index.ts +37 -5
  112. package/packages/tools/find-github-issues/package.json +11 -4
  113. package/packages/tools/generate-storyboard-images/dist/index.d.ts +1 -2
  114. package/packages/tools/generate-storyboard-images/dist/index.js +98 -173
  115. package/packages/tools/generate-storyboard-images/dist/tsconfig.tsbuildinfo +1 -1
  116. package/packages/tools/generate-storyboard-images/index.ts +100 -188
  117. package/packages/tools/generate-storyboard-images/package.json +11 -4
  118. package/packages/tools/open-github-issue/dist/index.d.ts +13 -0
  119. package/packages/tools/open-github-issue/dist/index.js +67 -68
  120. package/packages/tools/open-github-issue/dist/tsconfig.tsbuildinfo +1 -1
  121. package/packages/tools/open-github-issue/index.ts +71 -72
  122. package/packages/tools/open-github-issue/package.json +11 -4
  123. package/packages/tools/read-github-issue/dist/index.d.ts +16 -1
  124. package/packages/tools/read-github-issue/dist/index.js +32 -40
  125. package/packages/tools/read-github-issue/dist/tsconfig.tsbuildinfo +1 -1
  126. package/packages/tools/read-github-issue/index.ts +32 -45
  127. package/packages/tools/read-github-issue/package.json +11 -4
  128. package/packages/tools/render-error-book/dist/index.d.ts +1 -2
  129. package/packages/tools/render-error-book/dist/index.js +74 -95
  130. package/packages/tools/render-error-book/dist/tsconfig.tsbuildinfo +1 -1
  131. package/packages/tools/render-error-book/index.ts +88 -103
  132. package/packages/tools/render-error-book/package.json +11 -4
  133. package/packages/tools/render-katex/dist/index.d.ts +1 -2
  134. package/packages/tools/render-katex/dist/index.js +70 -157
  135. package/packages/tools/render-katex/dist/tsconfig.tsbuildinfo +1 -1
  136. package/packages/tools/render-katex/index.ts +138 -222
  137. package/packages/tools/render-katex/package.json +11 -4
  138. package/packages/tools/review-threads/dist/index.d.ts +12 -0
  139. package/packages/tools/review-threads/dist/index.js +83 -86
  140. package/packages/tools/review-threads/dist/tsconfig.tsbuildinfo +1 -1
  141. package/packages/tools/review-threads/index.ts +90 -84
  142. package/packages/tools/review-threads/package.json +11 -4
  143. package/packages/tools/search-logs/dist/index.js +100 -136
  144. package/packages/tools/search-logs/dist/tsconfig.tsbuildinfo +1 -1
  145. package/packages/tools/search-logs/index.ts +113 -145
  146. package/packages/tools/search-logs/package.json +11 -4
  147. package/packages/tools/sync-memory-index/dist/index.js +34 -28
  148. package/packages/tools/sync-memory-index/dist/tsconfig.tsbuildinfo +1 -1
  149. package/packages/tools/sync-memory-index/index.ts +37 -28
  150. package/packages/tools/sync-memory-index/package.json +11 -4
  151. package/packages/tools/validate-openai-agent-config/dist/index.js +13 -7
  152. package/packages/tools/validate-openai-agent-config/dist/tsconfig.tsbuildinfo +1 -1
  153. package/packages/tools/validate-openai-agent-config/index.ts +13 -7
  154. package/packages/tools/validate-openai-agent-config/package.json +11 -4
  155. package/packages/tools/validate-skill-frontmatter/dist/index.js +12 -6
  156. package/packages/tools/validate-skill-frontmatter/dist/tsconfig.tsbuildinfo +1 -1
  157. package/packages/tools/validate-skill-frontmatter/index.ts +12 -6
  158. package/packages/tools/validate-skill-frontmatter/package.json +11 -4
  159. package/packages/tui/dist/index.d.ts +2 -1
  160. package/packages/tui/dist/index.js +1 -0
  161. package/packages/tui/dist/stdio-adapter.d.ts +36 -0
  162. package/packages/tui/dist/stdio-adapter.js +69 -0
  163. package/packages/tui/dist/terminal.js +3 -1
  164. package/packages/tui/dist/tsconfig.tsbuildinfo +1 -1
  165. package/packages/tui/dist/types.d.ts +17 -0
  166. package/packages/tui/index.ts +2 -1
  167. package/packages/tui/package.json +14 -6
  168. package/packages/tui/stdio-adapter.ts +85 -0
  169. package/packages/tui/terminal.ts +3 -1
  170. package/packages/tui/tsconfig.json +5 -2
  171. package/packages/tui/types.ts +19 -0
  172. package/resources/project-architecture/assets/architecture.css +2 -1
  173. package/resources/project-architecture/atlas/atlas.history.log +1 -0
  174. package/resources/project-architecture/atlas/atlas.history.undo.json +13 -2
  175. package/resources/project-architecture/atlas/atlas.history.undo.stack.json +610 -0
  176. package/resources/project-architecture/atlas/atlas.index.yaml +81 -5
  177. package/resources/project-architecture/atlas/features/cli-dispatch.yaml +43 -0
  178. package/resources/project-architecture/atlas/features/terminal-ui.yaml +29 -0
  179. package/resources/project-architecture/atlas/features/tool-registry.yaml +22 -0
  180. package/resources/project-architecture/atlas/features/tool-utils.yaml +22 -0
  181. package/resources/project-architecture/features/cli-dispatch/arg-parser.html +40 -0
  182. package/resources/project-architecture/features/cli-dispatch/help-builder.html +40 -0
  183. package/resources/project-architecture/features/cli-dispatch/index.html +64 -0
  184. package/resources/project-architecture/features/cli-dispatch/installer-core.html +40 -0
  185. package/resources/project-architecture/features/cli-dispatch/tool-discovery.html +40 -0
  186. package/resources/project-architecture/features/cli-dispatch/update-checker.html +40 -0
  187. package/resources/project-architecture/features/terminal-ui/banner-display.html +40 -0
  188. package/resources/project-architecture/features/terminal-ui/index.html +50 -0
  189. package/resources/project-architecture/features/terminal-ui/interactive-prompts.html +40 -0
  190. package/resources/project-architecture/features/terminal-ui/terminal-detection.html +40 -0
  191. package/resources/project-architecture/features/tool-registry/formatter.html +40 -0
  192. package/resources/project-architecture/features/tool-registry/index.html +43 -0
  193. package/resources/project-architecture/features/tool-registry/registry-core.html +40 -0
  194. package/resources/project-architecture/features/tool-utils/index.html +43 -0
  195. package/resources/project-architecture/features/tool-utils/log-utils.html +40 -0
  196. package/resources/project-architecture/features/tool-utils/skill-discovery.html +40 -0
  197. package/resources/project-architecture/index.html +365 -121
  198. package/scripts/rewrite-imports.mjs +2 -2
  199. package/scripts/test.sh +144 -8
  200. package/skills/design/SKILL.md +57 -64
  201. package/skills/design/assets/templates/DESIGN.md +12 -0
  202. package/skills/design/references/code-smells.md +94 -0
  203. package/skills/design/references/module-boundary-adjustment.md +126 -0
  204. package/skills/design/references/module-internal-restructuring.md +132 -0
  205. package/skills/design/references/module-internal-simplification.md +164 -0
@@ -3,91 +3,7 @@ import path from 'node:path';
3
3
  import https from 'node:https';
4
4
  import http from 'node:http';
5
5
  import { fileURLToPath } from 'node:url';
6
- function parseArgs(args) {
7
- const parsed = {
8
- contentName: null,
9
- projectDir: '.',
10
- envFile: null,
11
- apiUrl: null,
12
- apiKey: null,
13
- promptsFile: null,
14
- prompts: [],
15
- imageModel: null,
16
- aspectRatio: null,
17
- imageSize: null,
18
- quality: null,
19
- style: null,
20
- help: false,
21
- };
22
- for (let i = 0; i < args.length; i++) {
23
- const arg = args[i];
24
- if (arg === '--help' || arg === '-h') {
25
- parsed.help = true;
26
- continue;
27
- }
28
- if (arg.startsWith('--')) {
29
- const eqIndex = arg.indexOf('=');
30
- let key;
31
- let value;
32
- if (eqIndex !== -1) {
33
- key = arg.slice(2, eqIndex);
34
- value = arg.slice(eqIndex + 1);
35
- }
36
- else {
37
- key = arg.slice(2);
38
- if (key === 'prompt') {
39
- // --prompt can be multiple
40
- value = args[++i] || '';
41
- parsed.prompts.push(value);
42
- continue;
43
- }
44
- value = args[++i] || '';
45
- }
46
- switch (key) {
47
- case 'input':
48
- case 'content-name':
49
- parsed.contentName = value;
50
- break;
51
- case 'project-dir':
52
- parsed.projectDir = value;
53
- break;
54
- case 'env-file':
55
- parsed.envFile = value;
56
- break;
57
- case 'api-url':
58
- parsed.apiUrl = value;
59
- break;
60
- case 'api-key':
61
- parsed.apiKey = value;
62
- break;
63
- case 'prompts-file':
64
- parsed.promptsFile = value;
65
- break;
66
- case 'image-model':
67
- parsed.imageModel = value;
68
- break;
69
- case 'aspect-ratio':
70
- parsed.aspectRatio = value;
71
- break;
72
- case 'image-size':
73
- case 'size':
74
- parsed.imageSize = value;
75
- break;
76
- case 'quality':
77
- parsed.quality = value;
78
- break;
79
- case 'style':
80
- parsed.style = value;
81
- break;
82
- }
83
- }
84
- else if (!parsed.contentName && !arg.startsWith('-')) {
85
- // positional: content name
86
- parsed.contentName = arg;
87
- }
88
- }
89
- return parsed;
90
- }
6
+ import { UserInputError, createToolRunner } from '../../../tool-utils/dist/index.js';
91
7
  function sanitizeComponent(name, fallback) {
92
8
  return name
93
9
  .replace(/[\\/:*?"<>|]+/g, '_')
@@ -163,7 +79,7 @@ function parsePromptEntries(raw) {
163
79
  if (typeof item === 'string') {
164
80
  const prompt = item.trim();
165
81
  if (!prompt)
166
- throw new Error(`Empty prompt at index ${i}`);
82
+ throw new UserInputError(`Empty prompt at index ${i}`);
167
83
  items.push({ title: `scene-${i + 1}`, prompt });
168
84
  }
169
85
  else if (item && typeof item === 'object') {
@@ -171,15 +87,15 @@ function parsePromptEntries(raw) {
171
87
  const prompt = String(obj.prompt || '').trim();
172
88
  const title = String(obj.title || `scene-${i + 1}`).trim() || `scene-${i + 1}`;
173
89
  if (!prompt)
174
- throw new Error(`Empty prompt in object at index ${i}`);
90
+ throw new UserInputError(`Empty prompt in object at index ${i}`);
175
91
  items.push({ title, prompt });
176
92
  }
177
93
  else {
178
- throw new Error(`Invalid item type at index ${i}: expected string or object`);
94
+ throw new UserInputError(`Invalid item type at index ${i}: expected string or object`);
179
95
  }
180
96
  }
181
97
  if (items.length === 0)
182
- throw new Error('No prompts found.');
98
+ throw new UserInputError('No prompts found.');
183
99
  return items;
184
100
  }
185
101
  function parsePromptsFile(filePath) {
@@ -190,7 +106,7 @@ function parsePromptsFile(filePath) {
190
106
  if (raw && typeof raw === 'object') {
191
107
  const scenes = raw.scenes;
192
108
  if (!Array.isArray(scenes) || scenes.length === 0) {
193
- throw new Error('Object mode requires a top-level "scenes" array.');
109
+ throw new UserInputError('Object mode requires a top-level "scenes" array.');
194
110
  }
195
111
  const characters = {};
196
112
  if (Array.isArray(raw.characters)) {
@@ -213,12 +129,12 @@ function parsePromptsFile(filePath) {
213
129
  for (let si = 0; si < scenes.length; si++) {
214
130
  const scene = scenes[si];
215
131
  if (!scene || typeof scene !== 'object') {
216
- throw new Error(`Invalid scene at index ${si}: expected object.`);
132
+ throw new UserInputError(`Invalid scene at index ${si}: expected object.`);
217
133
  }
218
134
  const title = String(scene.title || `scene-${si + 1}`).trim() || `scene-${si + 1}`;
219
135
  const description = String(scene.description || '').trim();
220
136
  if (!description)
221
- throw new Error(`Scene ${si}: 'description' is required.`);
137
+ throw new UserInputError(`Scene ${si}: 'description' is required.`);
222
138
  let promptPayload = {
223
139
  scene_title: title,
224
140
  description,
@@ -248,44 +164,56 @@ function parsePromptsFile(filePath) {
248
164
  }
249
165
  return items;
250
166
  }
251
- throw new Error('Top-level JSON must be an array or an object.');
167
+ throw new UserInputError('Top-level JSON must be an array or an object.');
252
168
  }
253
- export async function generateStoryboardImagesHandler(args, context) {
254
- const stdout = context.stdout || process.stdout;
255
- const stderr = context.stderr || process.stderr;
256
- try {
257
- const opts = parseArgs(args);
258
- if (opts.help || (!opts.contentName)) {
259
- stdout.write(`Usage: apltk generate-storyboard-images --input <name> [options]
260
-
261
- Generate storyboard images from prompts via OpenAI-compatible API.
262
-
263
- Options:
264
- --input, --content-name <name> Output subfolder name under pictures/
265
- --project-dir <path> Project root (default: .)
266
- --env-file <path> Path to .env file
267
- --api-url <url> API base URL for /images/generations
268
- --api-key <key> API key
269
- --prompts-file <path> JSON file with prompt entries
270
- --prompt <text> Image prompt (repeatable)
271
- --image-model <model> Image model (default: gpt-image-1)
272
- --aspect-ratio <ratio> Aspect ratio, e.g. 16:9
273
- --image-size <size> Image size, e.g. 1024x768
274
- --quality <q> Image quality
275
- --style <style> Image style
276
-
277
- Either --prompts-file or at least one --prompt is required.
278
- `);
279
- return opts.contentName ? 1 : 0;
169
+ const schema = {
170
+ options: {
171
+ 'input': { type: 'string' },
172
+ 'content-name': { type: 'string' },
173
+ 'project-dir': { type: 'string', default: '.' },
174
+ 'env-file': { type: 'string' },
175
+ 'api-url': { type: 'string' },
176
+ 'api-key': { type: 'string' },
177
+ 'prompts-file': { type: 'string' },
178
+ 'prompt': { type: 'string', multiple: true },
179
+ 'image-model': { type: 'string' },
180
+ 'aspect-ratio': { type: 'string' },
181
+ 'image-size': { type: 'string' },
182
+ 'size': { type: 'string' },
183
+ 'quality': { type: 'string' },
184
+ 'style': { type: 'string' },
185
+ },
186
+ allowPositionals: true,
187
+ usage: 'apltk generate-storyboard-images --input <name> [options]',
188
+ description: 'Generate storyboard images from prompts via OpenAI-compatible API.',
189
+ handler: async (values, positionals, context) => {
190
+ const stdout = context.stdout || process.stdout;
191
+ const stderr = context.stderr || process.stderr;
192
+ const contentName = values['input'] ||
193
+ values['content-name'] ||
194
+ positionals[0] ||
195
+ null;
196
+ const projectDir = values['project-dir'] || '.';
197
+ const envFile = values['env-file'] ?? null;
198
+ const apiUrl = values['api-url'] ?? null;
199
+ const apiKey = values['api-key'] ?? null;
200
+ const promptsFile = values['prompts-file'] ?? null;
201
+ const prompts = values['prompt'] || [];
202
+ const imageModel = values['image-model'] ?? null;
203
+ const aspectRatio = values['aspect-ratio'] ?? null;
204
+ const imageSize = values['image-size'] || values['size'] || null;
205
+ const quality = values['quality'] ?? null;
206
+ const style = values['style'] ?? null;
207
+ if (!contentName) {
208
+ throw new UserInputError('--input or --content-name is required.');
280
209
  }
281
- const projectDir = path.resolve(opts.projectDir);
282
- const contentName = opts.contentName;
210
+ const resolvedProjectDir = path.resolve(projectDir);
283
211
  // Resolve API config
284
212
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
285
213
  const sourceRoot = context.sourceRoot || path.resolve(__dirname, '..', '..');
286
214
  // Try loading env file
287
- const envFilePath = opts.envFile
288
- ? path.resolve(opts.envFile)
215
+ const envFilePath = envFile
216
+ ? path.resolve(envFile)
289
217
  : path.join(sourceRoot, 'openai-text-to-image-storyboard', '.env');
290
218
  if (fs.existsSync(envFilePath)) {
291
219
  const envContent = fs.readFileSync(envFilePath, 'utf-8');
@@ -309,60 +237,58 @@ Either --prompts-file or at least one --prompt is required.
309
237
  }
310
238
  }
311
239
  }
312
- const apiUrl = opts.apiUrl || process.env.OPENAI_API_URL || '';
313
- const apiKey = opts.apiKey || process.env.OPENAI_API_KEY || '';
314
- const imageModel = opts.imageModel || process.env.OPENAI_IMAGE_MODEL || 'gpt-image-1';
315
- const aspectRatio = opts.aspectRatio || process.env.OPENAI_IMAGE_RATIO || process.env.OPENAI_IMAGE_ASPECT_RATIO || null;
316
- const imageSize = opts.imageSize || process.env.OPENAI_IMAGE_SIZE || null;
317
- const quality = opts.quality || process.env.OPENAI_IMAGE_QUALITY || null;
318
- const style = opts.style || process.env.OPENAI_IMAGE_STYLE || null;
319
- if (!apiUrl) {
320
- stderr.write('Error: Missing API URL. Set --api-url or OPENAI_API_URL.\n');
321
- return 1;
240
+ const resolvedApiUrl = apiUrl || process.env.OPENAI_API_URL || '';
241
+ const resolvedApiKey = apiKey || process.env.OPENAI_API_KEY || '';
242
+ const resolvedImageModel = imageModel || process.env.OPENAI_IMAGE_MODEL || 'gpt-image-1';
243
+ const resolvedAspectRatio = aspectRatio || process.env.OPENAI_IMAGE_RATIO || process.env.OPENAI_IMAGE_ASPECT_RATIO || null;
244
+ const resolvedImageSize = imageSize || process.env.OPENAI_IMAGE_SIZE || null;
245
+ const resolvedQuality = quality || process.env.OPENAI_IMAGE_QUALITY || null;
246
+ const resolvedStyle = style || process.env.OPENAI_IMAGE_STYLE || null;
247
+ if (!resolvedApiUrl) {
248
+ throw new UserInputError('Missing API URL. Set --api-url or OPENAI_API_URL.');
322
249
  }
323
- if (!apiKey) {
324
- stderr.write('Error: Missing API key. Set --api-key or OPENAI_API_KEY.\n');
325
- return 1;
250
+ if (!resolvedApiKey) {
251
+ throw new UserInputError('Missing API key. Set --api-key or OPENAI_API_KEY.');
326
252
  }
327
253
  // Build prompt items
328
254
  let promptItems;
329
- if (opts.promptsFile) {
330
- promptItems = parsePromptsFile(path.resolve(opts.promptsFile));
255
+ if (promptsFile) {
256
+ promptItems = parsePromptsFile(path.resolve(promptsFile));
331
257
  }
332
- else if (opts.prompts.length > 0) {
333
- promptItems = opts.prompts.map((p, i) => ({ title: `scene-${i + 1}`, prompt: p.trim() }));
258
+ else if (prompts.length > 0) {
259
+ promptItems = prompts.map((p, i) => ({ title: `scene-${i + 1}`, prompt: p.trim() }));
334
260
  }
335
261
  else {
336
- stderr.write('Error: Either --prompts-file or at least one --prompt is required.\n');
337
- return 1;
262
+ throw new UserInputError('Either --prompts-file or at least one --prompt is required.');
338
263
  }
339
264
  if (promptItems.length === 0) {
340
- stderr.write('Error: No prompts provided.\n');
341
- return 1;
265
+ throw new UserInputError('No prompts provided.');
342
266
  }
343
- const outputDir = path.join(projectDir, 'pictures', sanitizeComponent(contentName, 'untitled-content'));
267
+ const outputDir = path.join(resolvedProjectDir, 'pictures', sanitizeComponent(contentName, 'untitled-content'));
344
268
  fs.mkdirSync(outputDir, { recursive: true });
345
269
  const records = [];
270
+ let failures = 0;
346
271
  for (let i = 0; i < promptItems.length; i++) {
347
272
  const item = promptItems[i];
348
273
  const titleSlug = sanitizeComponent(item.title, `scene-${i + 1}`);
349
274
  const imagePath = uniquePath(path.join(outputDir, `${String(i + 1).padStart(2, '0')}_${titleSlug}.png`));
350
275
  const payload = {
351
- model: imageModel,
276
+ model: resolvedImageModel,
352
277
  prompt: item.prompt,
353
278
  };
354
- if (aspectRatio)
355
- payload.aspect_ratio = aspectRatio;
356
- if (imageSize)
357
- payload.size = imageSize;
358
- if (quality)
359
- payload.quality = quality;
360
- if (style)
361
- payload.style = style;
362
- const response = await postJson(apiUrl, '/images/generations', apiKey, payload);
279
+ if (resolvedAspectRatio)
280
+ payload.aspect_ratio = resolvedAspectRatio;
281
+ if (resolvedImageSize)
282
+ payload.size = resolvedImageSize;
283
+ if (resolvedQuality)
284
+ payload.quality = resolvedQuality;
285
+ if (resolvedStyle)
286
+ payload.style = resolvedStyle;
287
+ const response = await postJson(resolvedApiUrl, '/images/generations', resolvedApiKey, payload);
363
288
  const data = response.data;
364
289
  if (!Array.isArray(data) || data.length === 0) {
365
- stderr.write(`Error: No image data returned for prompt ${i + 1}.\n`);
290
+ stderr.write(`No image data returned for prompt ${i + 1}.\n`);
291
+ failures++;
366
292
  continue;
367
293
  }
368
294
  const first = data[0];
@@ -374,7 +300,8 @@ Either --prompts-file or at least one --prompt is required.
374
300
  imageBytes = await fetchBinary(first.url);
375
301
  }
376
302
  else {
377
- stderr.write(`Error: Image payload missing b64_json/url for prompt ${i + 1}.\n`);
303
+ stderr.write(`Image payload missing b64_json/url for prompt ${i + 1}.\n`);
304
+ failures++;
378
305
  continue;
379
306
  }
380
307
  fs.writeFileSync(imagePath, imageBytes);
@@ -393,29 +320,27 @@ Either --prompts-file or at least one --prompt is required.
393
320
  // Write summary
394
321
  const summary = {
395
322
  content_name: contentName,
396
- project_dir: projectDir,
323
+ project_dir: resolvedProjectDir,
397
324
  output_dir: outputDir,
398
- image_model: imageModel,
325
+ image_model: resolvedImageModel,
399
326
  images: records,
400
327
  };
401
- if (aspectRatio)
402
- summary.aspect_ratio = aspectRatio;
403
- if (imageSize)
404
- summary.image_size = imageSize;
328
+ if (resolvedAspectRatio)
329
+ summary.aspect_ratio = resolvedAspectRatio;
330
+ if (resolvedImageSize)
331
+ summary.image_size = resolvedImageSize;
405
332
  const summaryPath = path.join(outputDir, 'storyboard.json');
406
333
  fs.writeFileSync(summaryPath, JSON.stringify(summary, null, 2), 'utf-8');
407
334
  stdout.write(`[OK] Wrote plan to ${summaryPath}\n`);
408
- return 0;
409
- }
410
- catch (err) {
411
- const msg = err instanceof Error ? err.message : 'Unknown error';
412
- stderr.write(`Error: ${msg}\n`);
413
- return 1;
414
- }
415
- }
335
+ if (failures > 0) {
336
+ stderr.write(`Warning: ${failures} out of ${promptItems.length} prompts failed to generate images.\n`);
337
+ }
338
+ return failures > 0 ? 1 : 0;
339
+ },
340
+ };
416
341
  export const tool = {
417
342
  name: 'generate-storyboard-images',
418
343
  category: 'media',
419
344
  description: 'Generate storyboard images from prompts via OpenAI-compatible API.',
420
- handler: generateStoryboardImagesHandler,
345
+ handler: createToolRunner(schema),
421
346
  };