@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
@@ -4,110 +4,7 @@ import https from 'node:https';
4
4
  import http from 'node:http';
5
5
  import { fileURLToPath } from 'node:url';
6
6
  import type { ToolDefinition, ToolContext } from '@laitszkin/tool-registry';
7
-
8
- interface StoryboardArgs {
9
- contentName: string | null;
10
- projectDir: string;
11
- envFile: string | null;
12
- apiUrl: string | null;
13
- apiKey: string | null;
14
- promptsFile: string | null;
15
- prompts: string[];
16
- imageModel: string | null;
17
- aspectRatio: string | null;
18
- imageSize: string | null;
19
- quality: string | null;
20
- style: string | null;
21
- help: boolean;
22
- }
23
-
24
- function parseArgs(args: string[]): StoryboardArgs {
25
- const parsed: StoryboardArgs = {
26
- contentName: null,
27
- projectDir: '.',
28
- envFile: null,
29
- apiUrl: null,
30
- apiKey: null,
31
- promptsFile: null,
32
- prompts: [],
33
- imageModel: null,
34
- aspectRatio: null,
35
- imageSize: null,
36
- quality: null,
37
- style: null,
38
- help: false,
39
- };
40
-
41
- for (let i = 0; i < args.length; i++) {
42
- const arg = args[i];
43
- if (arg === '--help' || arg === '-h') {
44
- parsed.help = true;
45
- continue;
46
- }
47
- if (arg.startsWith('--')) {
48
- const eqIndex = arg.indexOf('=');
49
- let key: string;
50
- let value: string;
51
-
52
- if (eqIndex !== -1) {
53
- key = arg.slice(2, eqIndex);
54
- value = arg.slice(eqIndex + 1);
55
- } else {
56
- key = arg.slice(2);
57
- if (key === 'prompt') {
58
- // --prompt can be multiple
59
- value = args[++i] || '';
60
- parsed.prompts.push(value);
61
- continue;
62
- }
63
- value = args[++i] || '';
64
- }
65
-
66
- switch (key) {
67
- case 'input':
68
- case 'content-name':
69
- parsed.contentName = value;
70
- break;
71
- case 'project-dir':
72
- parsed.projectDir = value;
73
- break;
74
- case 'env-file':
75
- parsed.envFile = value;
76
- break;
77
- case 'api-url':
78
- parsed.apiUrl = value;
79
- break;
80
- case 'api-key':
81
- parsed.apiKey = value;
82
- break;
83
- case 'prompts-file':
84
- parsed.promptsFile = value;
85
- break;
86
- case 'image-model':
87
- parsed.imageModel = value;
88
- break;
89
- case 'aspect-ratio':
90
- parsed.aspectRatio = value;
91
- break;
92
- case 'image-size':
93
- case 'size':
94
- parsed.imageSize = value;
95
- break;
96
- case 'quality':
97
- parsed.quality = value;
98
- break;
99
- case 'style':
100
- parsed.style = value;
101
- break;
102
- }
103
- } else if (!parsed.contentName && !arg.startsWith('-')) {
104
- // positional: content name
105
- parsed.contentName = arg;
106
- }
107
- }
108
-
109
- return parsed;
110
- }
7
+ import { UserInputError, createToolRunner } from '@laitszkin/tool-utils';
111
8
 
112
9
  function sanitizeComponent(name: string, fallback: string): string {
113
10
  return name
@@ -194,19 +91,19 @@ function parsePromptEntries(raw: unknown[]): Array<{ title: string; prompt: stri
194
91
  const item = raw[i];
195
92
  if (typeof item === 'string') {
196
93
  const prompt = item.trim();
197
- if (!prompt) throw new Error(`Empty prompt at index ${i}`);
94
+ if (!prompt) throw new UserInputError(`Empty prompt at index ${i}`);
198
95
  items.push({ title: `scene-${i + 1}`, prompt });
199
96
  } else if (item && typeof item === 'object') {
200
97
  const obj = item as Record<string, unknown>;
201
98
  const prompt = String(obj.prompt || '').trim();
202
99
  const title = String(obj.title || `scene-${i + 1}`).trim() || `scene-${i + 1}`;
203
- if (!prompt) throw new Error(`Empty prompt in object at index ${i}`);
100
+ if (!prompt) throw new UserInputError(`Empty prompt in object at index ${i}`);
204
101
  items.push({ title, prompt });
205
102
  } else {
206
- throw new Error(`Invalid item type at index ${i}: expected string or object`);
103
+ throw new UserInputError(`Invalid item type at index ${i}: expected string or object`);
207
104
  }
208
105
  }
209
- if (items.length === 0) throw new Error('No prompts found.');
106
+ if (items.length === 0) throw new UserInputError('No prompts found.');
210
107
  return items;
211
108
  }
212
109
 
@@ -220,7 +117,7 @@ function parsePromptsFile(filePath: string): Array<{ title: string; prompt: stri
220
117
  if (raw && typeof raw === 'object') {
221
118
  const scenes = raw.scenes;
222
119
  if (!Array.isArray(scenes) || scenes.length === 0) {
223
- throw new Error('Object mode requires a top-level "scenes" array.');
120
+ throw new UserInputError('Object mode requires a top-level "scenes" array.');
224
121
  }
225
122
 
226
123
  const characters: Record<string, Record<string, string>> = {};
@@ -244,12 +141,12 @@ function parsePromptsFile(filePath: string): Array<{ title: string; prompt: stri
244
141
  for (let si = 0; si < scenes.length; si++) {
245
142
  const scene = scenes[si] as Record<string, unknown>;
246
143
  if (!scene || typeof scene !== 'object') {
247
- throw new Error(`Invalid scene at index ${si}: expected object.`);
144
+ throw new UserInputError(`Invalid scene at index ${si}: expected object.`);
248
145
  }
249
146
 
250
147
  const title = String(scene.title || `scene-${si + 1}`).trim() || `scene-${si + 1}`;
251
148
  const description = String(scene.description || '').trim();
252
- if (!description) throw new Error(`Scene ${si}: 'description' is required.`);
149
+ if (!description) throw new UserInputError(`Scene ${si}: 'description' is required.`);
253
150
 
254
151
  let promptPayload: Record<string, unknown> = {
255
152
  scene_title: title,
@@ -281,50 +178,67 @@ function parsePromptsFile(filePath: string): Array<{ title: string; prompt: stri
281
178
  return items;
282
179
  }
283
180
 
284
- throw new Error('Top-level JSON must be an array or an object.');
181
+ throw new UserInputError('Top-level JSON must be an array or an object.');
285
182
  }
286
183
 
287
- export async function generateStoryboardImagesHandler(args: string[], context: ToolContext): Promise<number> {
288
- const stdout = context.stdout || process.stdout;
289
- const stderr = context.stderr || process.stderr;
290
-
291
- try {
292
- const opts = parseArgs(args);
293
-
294
- if (opts.help || (!opts.contentName)) {
295
- stdout.write(`Usage: apltk generate-storyboard-images --input <name> [options]
296
-
297
- Generate storyboard images from prompts via OpenAI-compatible API.
298
-
299
- Options:
300
- --input, --content-name <name> Output subfolder name under pictures/
301
- --project-dir <path> Project root (default: .)
302
- --env-file <path> Path to .env file
303
- --api-url <url> API base URL for /images/generations
304
- --api-key <key> API key
305
- --prompts-file <path> JSON file with prompt entries
306
- --prompt <text> Image prompt (repeatable)
307
- --image-model <model> Image model (default: gpt-image-1)
308
- --aspect-ratio <ratio> Aspect ratio, e.g. 16:9
309
- --image-size <size> Image size, e.g. 1024x768
310
- --quality <q> Image quality
311
- --style <style> Image style
312
-
313
- Either --prompts-file or at least one --prompt is required.
314
- `);
315
- return opts.contentName ? 1 : 0;
184
+ const schema = {
185
+ options: {
186
+ 'input': { type: 'string' as const },
187
+ 'content-name': { type: 'string' as const },
188
+ 'project-dir': { type: 'string' as const, default: '.' },
189
+ 'env-file': { type: 'string' as const },
190
+ 'api-url': { type: 'string' as const },
191
+ 'api-key': { type: 'string' as const },
192
+ 'prompts-file': { type: 'string' as const },
193
+ 'prompt': { type: 'string' as const, multiple: true },
194
+ 'image-model': { type: 'string' as const },
195
+ 'aspect-ratio': { type: 'string' as const },
196
+ 'image-size': { type: 'string' as const },
197
+ 'size': { type: 'string' as const },
198
+ 'quality': { type: 'string' as const },
199
+ 'style': { type: 'string' as const },
200
+ },
201
+ allowPositionals: true,
202
+ usage: 'apltk generate-storyboard-images --input <name> [options]',
203
+ description: 'Generate storyboard images from prompts via OpenAI-compatible API.',
204
+ handler: async (
205
+ values: Record<string, unknown>,
206
+ positionals: string[],
207
+ context: ToolContext,
208
+ ): Promise<number> => {
209
+ const stdout = context.stdout || process.stdout;
210
+ const stderr = context.stderr || process.stderr;
211
+
212
+ const contentName = (values['input'] as string | undefined) ||
213
+ (values['content-name'] as string | undefined) ||
214
+ positionals[0] ||
215
+ null;
216
+
217
+ const projectDir = (values['project-dir'] as string) || '.';
218
+ const envFile = (values['env-file'] as string | undefined) ?? null;
219
+ const apiUrl = (values['api-url'] as string | undefined) ?? null;
220
+ const apiKey = (values['api-key'] as string | undefined) ?? null;
221
+ const promptsFile = (values['prompts-file'] as string | undefined) ?? null;
222
+ const prompts = (values['prompt'] as string[] | undefined) || [];
223
+ const imageModel = (values['image-model'] as string | undefined) ?? null;
224
+ const aspectRatio = (values['aspect-ratio'] as string | undefined) ?? null;
225
+ const imageSize = (values['image-size'] as string | undefined) || (values['size'] as string | undefined) || null;
226
+ const quality = (values['quality'] as string | undefined) ?? null;
227
+ const style = (values['style'] as string | undefined) ?? null;
228
+
229
+ if (!contentName) {
230
+ throw new UserInputError('--input or --content-name is required.');
316
231
  }
317
232
 
318
- const projectDir = path.resolve(opts.projectDir);
319
- const contentName = opts.contentName;
233
+ const resolvedProjectDir = path.resolve(projectDir);
320
234
 
321
235
  // Resolve API config
322
236
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
323
237
  const sourceRoot = context.sourceRoot || path.resolve(__dirname, '..', '..');
324
238
 
325
239
  // Try loading env file
326
- const envFilePath = opts.envFile
327
- ? path.resolve(opts.envFile)
240
+ const envFilePath = envFile
241
+ ? path.resolve(envFile)
328
242
  : path.join(sourceRoot, 'openai-text-to-image-storyboard', '.env');
329
243
  if (fs.existsSync(envFilePath)) {
330
244
  const envContent = fs.readFileSync(envFilePath, 'utf-8');
@@ -346,43 +260,40 @@ Either --prompts-file or at least one --prompt is required.
346
260
  }
347
261
  }
348
262
 
349
- const apiUrl = opts.apiUrl || process.env.OPENAI_API_URL || '';
350
- const apiKey = opts.apiKey || process.env.OPENAI_API_KEY || '';
351
- const imageModel = opts.imageModel || process.env.OPENAI_IMAGE_MODEL || 'gpt-image-1';
352
- const aspectRatio = opts.aspectRatio || process.env.OPENAI_IMAGE_RATIO || process.env.OPENAI_IMAGE_ASPECT_RATIO || null;
353
- const imageSize = opts.imageSize || process.env.OPENAI_IMAGE_SIZE || null;
354
- const quality = opts.quality || process.env.OPENAI_IMAGE_QUALITY || null;
355
- const style = opts.style || process.env.OPENAI_IMAGE_STYLE || null;
356
-
357
- if (!apiUrl) {
358
- stderr.write('Error: Missing API URL. Set --api-url or OPENAI_API_URL.\n');
359
- return 1;
263
+ const resolvedApiUrl = apiUrl || process.env.OPENAI_API_URL || '';
264
+ const resolvedApiKey = apiKey || process.env.OPENAI_API_KEY || '';
265
+ const resolvedImageModel = imageModel || process.env.OPENAI_IMAGE_MODEL || 'gpt-image-1';
266
+ const resolvedAspectRatio = aspectRatio || process.env.OPENAI_IMAGE_RATIO || process.env.OPENAI_IMAGE_ASPECT_RATIO || null;
267
+ const resolvedImageSize = imageSize || process.env.OPENAI_IMAGE_SIZE || null;
268
+ const resolvedQuality = quality || process.env.OPENAI_IMAGE_QUALITY || null;
269
+ const resolvedStyle = style || process.env.OPENAI_IMAGE_STYLE || null;
270
+
271
+ if (!resolvedApiUrl) {
272
+ throw new UserInputError('Missing API URL. Set --api-url or OPENAI_API_URL.');
360
273
  }
361
- if (!apiKey) {
362
- stderr.write('Error: Missing API key. Set --api-key or OPENAI_API_KEY.\n');
363
- return 1;
274
+ if (!resolvedApiKey) {
275
+ throw new UserInputError('Missing API key. Set --api-key or OPENAI_API_KEY.');
364
276
  }
365
277
 
366
278
  // Build prompt items
367
279
  let promptItems: Array<{ title: string; prompt: string }>;
368
- if (opts.promptsFile) {
369
- promptItems = parsePromptsFile(path.resolve(opts.promptsFile));
370
- } else if (opts.prompts.length > 0) {
371
- promptItems = opts.prompts.map((p, i) => ({ title: `scene-${i + 1}`, prompt: p.trim() }));
280
+ if (promptsFile) {
281
+ promptItems = parsePromptsFile(path.resolve(promptsFile));
282
+ } else if (prompts.length > 0) {
283
+ promptItems = prompts.map((p, i) => ({ title: `scene-${i + 1}`, prompt: p.trim() }));
372
284
  } else {
373
- stderr.write('Error: Either --prompts-file or at least one --prompt is required.\n');
374
- return 1;
285
+ throw new UserInputError('Either --prompts-file or at least one --prompt is required.');
375
286
  }
376
287
 
377
288
  if (promptItems.length === 0) {
378
- stderr.write('Error: No prompts provided.\n');
379
- return 1;
289
+ throw new UserInputError('No prompts provided.');
380
290
  }
381
291
 
382
- const outputDir = path.join(projectDir, 'pictures', sanitizeComponent(contentName, 'untitled-content'));
292
+ const outputDir = path.join(resolvedProjectDir, 'pictures', sanitizeComponent(contentName, 'untitled-content'));
383
293
  fs.mkdirSync(outputDir, { recursive: true });
384
294
 
385
295
  const records: Array<Record<string, unknown>> = [];
296
+ let failures = 0;
386
297
 
387
298
  for (let i = 0; i < promptItems.length; i++) {
388
299
  const item = promptItems[i];
@@ -390,19 +301,20 @@ Either --prompts-file or at least one --prompt is required.
390
301
  const imagePath = uniquePath(path.join(outputDir, `${String(i + 1).padStart(2, '0')}_${titleSlug}.png`));
391
302
 
392
303
  const payload: Record<string, unknown> = {
393
- model: imageModel,
304
+ model: resolvedImageModel,
394
305
  prompt: item.prompt,
395
306
  };
396
- if (aspectRatio) payload.aspect_ratio = aspectRatio;
397
- if (imageSize) payload.size = imageSize;
398
- if (quality) payload.quality = quality;
399
- if (style) payload.style = style;
307
+ if (resolvedAspectRatio) payload.aspect_ratio = resolvedAspectRatio;
308
+ if (resolvedImageSize) payload.size = resolvedImageSize;
309
+ if (resolvedQuality) payload.quality = resolvedQuality;
310
+ if (resolvedStyle) payload.style = resolvedStyle;
400
311
 
401
- const response = await postJson(apiUrl, '/images/generations', apiKey, payload);
312
+ const response = await postJson(resolvedApiUrl, '/images/generations', resolvedApiKey, payload);
402
313
 
403
314
  const data = response.data;
404
315
  if (!Array.isArray(data) || data.length === 0) {
405
- stderr.write(`Error: No image data returned for prompt ${i + 1}.\n`);
316
+ stderr.write(`No image data returned for prompt ${i + 1}.\n`);
317
+ failures++;
406
318
  continue;
407
319
  }
408
320
 
@@ -414,7 +326,8 @@ Either --prompts-file or at least one --prompt is required.
414
326
  } else if (typeof first.url === 'string') {
415
327
  imageBytes = await fetchBinary(first.url);
416
328
  } else {
417
- stderr.write(`Error: Image payload missing b64_json/url for prompt ${i + 1}.\n`);
329
+ stderr.write(`Image payload missing b64_json/url for prompt ${i + 1}.\n`);
330
+ failures++;
418
331
  continue;
419
332
  }
420
333
 
@@ -436,29 +349,28 @@ Either --prompts-file or at least one --prompt is required.
436
349
  // Write summary
437
350
  const summary: Record<string, unknown> = {
438
351
  content_name: contentName,
439
- project_dir: projectDir,
352
+ project_dir: resolvedProjectDir,
440
353
  output_dir: outputDir,
441
- image_model: imageModel,
354
+ image_model: resolvedImageModel,
442
355
  images: records,
443
356
  };
444
- if (aspectRatio) summary.aspect_ratio = aspectRatio;
445
- if (imageSize) summary.image_size = imageSize;
357
+ if (resolvedAspectRatio) summary.aspect_ratio = resolvedAspectRatio;
358
+ if (resolvedImageSize) summary.image_size = resolvedImageSize;
446
359
 
447
360
  const summaryPath = path.join(outputDir, 'storyboard.json');
448
361
  fs.writeFileSync(summaryPath, JSON.stringify(summary, null, 2), 'utf-8');
449
362
  stdout.write(`[OK] Wrote plan to ${summaryPath}\n`);
450
363
 
451
- return 0;
452
- } catch (err: unknown) {
453
- const msg = err instanceof Error ? err.message : 'Unknown error';
454
- stderr.write(`Error: ${msg}\n`);
455
- return 1;
456
- }
457
- }
364
+ if (failures > 0) {
365
+ stderr.write(`Warning: ${failures} out of ${promptItems.length} prompts failed to generate images.\n`);
366
+ }
367
+ return failures > 0 ? 1 : 0;
368
+ },
369
+ };
458
370
 
459
371
  export const tool: ToolDefinition = {
460
372
  name: 'generate-storyboard-images',
461
373
  category: 'media',
462
374
  description: 'Generate storyboard images from prompts via OpenAI-compatible API.',
463
- handler: generateStoryboardImagesHandler,
375
+ handler: createToolRunner(schema),
464
376
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@laitszkin/tool-generate-storyboard-images",
3
- "version": "4.0.8",
4
- "description": "Apollo Toolkit CLI tool",
3
+ "version": "5.0.0",
4
+ "description": "Apollo Toolkit \u2014 CLI tool",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "main": "./dist/index.js",
@@ -18,5 +18,12 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "@laitszkin/tool-registry": "*"
21
- }
22
- }
21
+ },
22
+ "publishConfig": {
23
+ "access": "public",
24
+ "registry": "https://registry.npmjs.org"
25
+ },
26
+ "files": [
27
+ "dist"
28
+ ]
29
+ }
@@ -1,3 +1,16 @@
1
1
  import type { ToolDefinition, ToolContext } from '@laitszkin/tool-registry';
2
+ /**
3
+ * openGitHubIssueHandler — Known carryover from createToolRunner migration.
4
+ *
5
+ * Reason for not using createToolRunner:
6
+ * - Positional subcommand architecture (create/draft) with 15+ tool-specific
7
+ * flags doesn't map cleanly to createToolRunner's options schema.
8
+ * - Error handling follows the AppError convention (UserInputError/SystemError
9
+ * throws) which is handled by the CLI boundary's formatAppError.
10
+ * - Argument parsing and help text are handled manually — 83-line parseArgs().
11
+ *
12
+ * See DESIGN.md §2.3 for the full architecture discussion.
13
+ * --help is now supported.
14
+ */
2
15
  export declare function openGitHubIssueHandler(argv: string[], context: ToolContext): Promise<number>;
3
16
  export declare const tool: ToolDefinition;