@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,6 +4,7 @@ import { createRequire } from 'node:module';
4
4
  import { fileURLToPath, pathToFileURL } from 'node:url';
5
5
  import type { ToolDefinition, ToolContext } from '@laitszkin/tool-registry';
6
6
  import yaml from 'js-yaml';
7
+ import { UserInputError, SystemError, createPlatformAdapter } from '@laitszkin/tool-utils';
7
8
 
8
9
  // ── Apply & Template helpers (mirrors cli.js internals for the new verbs) ─────
9
10
 
@@ -77,7 +78,7 @@ function removeSubmodule(feature: any, slug: string, merged?: any): boolean {
77
78
 
78
79
  function parseEndpoint(value: string): { feature: string; submodule?: string } {
79
80
  const parts = value.split('/').filter(Boolean);
80
- if (parts.length === 0) throw new Error(`Invalid endpoint: "${value}"`);
81
+ if (parts.length === 0) throw new UserInputError(`Invalid endpoint: "${value}"`);
81
82
  return parts.length > 1
82
83
  ? { feature: parts[0], submodule: parts[1] }
83
84
  : { feature: parts[0] };
@@ -145,17 +146,13 @@ function yamlStr(value: string): string {
145
146
 
146
147
  async function handleApply(applyArgs: string[], context: ToolContext): Promise<number> {
147
148
  const stdout = context.stdout || process.stdout;
148
- const stderr = context.stderr || process.stderr;
149
149
  const sourceRoot =
150
150
  context.sourceRoot ||
151
151
  path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..', '..', '..');
152
152
 
153
153
  const yamlArg = applyArgs[0];
154
154
  if (!yamlArg || yamlArg.startsWith('--')) {
155
- stderr.write(
156
- 'Usage: apltk architecture apply <yaml-file> [--spec <dir>] [--project <root>] [--no-render]\n',
157
- );
158
- return 1;
155
+ throw new UserInputError('Missing architecture specification YAML file path. Usage: apltk architecture apply <yaml-file>');
159
156
  }
160
157
 
161
158
  // Simple flag parser for trailing flags (--spec, --project, --no-render)
@@ -176,13 +173,11 @@ async function handleApply(applyArgs: string[], context: ToolContext): Promise<n
176
173
  batch = yaml.load(raw);
177
174
  } catch (e: any) {
178
175
  const location = e.mark ? ` at line ${e.mark.line + 1}` : '';
179
- stderr.write(`Error parsing apply YAML (${yamlArg})${location}: ${e.message}\n`);
180
- return 1;
176
+ throw new UserInputError(`Error parsing apply YAML (${yamlArg})${location}: ${e.message}`);
181
177
  }
182
178
 
183
179
  if (!batch || typeof batch !== 'object') {
184
- stderr.write('Invalid apply YAML: expected an object with "features" / "edges" keys.\n');
185
- return 1;
180
+ throw new UserInputError('Invalid apply YAML: expected an object with "features" / "edges" keys.');
186
181
  }
187
182
 
188
183
  // Import atlas modules (shared with the existing JS CLI)
@@ -215,8 +210,7 @@ async function handleApply(applyArgs: string[], context: ToolContext): Promise<n
215
210
  try {
216
211
  projectRoot = cli.resolveProjectRoot(flags);
217
212
  } catch (e: any) {
218
- stderr.write(`${e.message}\n`);
219
- return 1;
213
+ throw new UserInputError(e.message);
220
214
  }
221
215
 
222
216
  const isSpec = Boolean(flags.spec);
@@ -244,7 +238,7 @@ async function handleApply(applyArgs: string[], context: ToolContext): Promise<n
244
238
  // 1) Features (add / modify / remove)
245
239
  for (const feat of batch.features || []) {
246
240
  const slug: string = feat.slug;
247
- if (!slug) throw new Error('"features" entry missing required "slug" field');
241
+ if (!slug) throw new UserInputError('"features" entry missing required "slug" field');
248
242
 
249
243
  switch (feat.action) {
250
244
  case 'add': {
@@ -259,7 +253,7 @@ async function handleApply(applyArgs: string[], context: ToolContext): Promise<n
259
253
  }
260
254
  case 'modify': {
261
255
  const existing = findFeature(merged, slug);
262
- if (!existing) throw new Error(`feature "${slug}" not found for action "modify"`);
256
+ if (!existing) throw new UserInputError(`feature "${slug}" not found for action "modify"`);
263
257
  if (feat.title !== undefined) existing.title = String(feat.title);
264
258
  if (feat.story !== undefined) existing.story = String(feat.story);
265
259
  if (feat.dependsOn !== undefined)
@@ -271,7 +265,7 @@ async function handleApply(applyArgs: string[], context: ToolContext): Promise<n
271
265
  removeFeature(merged, slug);
272
266
  break;
273
267
  default:
274
- throw new Error(`feature "${slug}": unknown action "${feat.action}"`);
268
+ throw new UserInputError(`feature "${slug}": unknown action "${feat.action}"`);
275
269
  }
276
270
  }
277
271
 
@@ -279,7 +273,7 @@ async function handleApply(applyArgs: string[], context: ToolContext): Promise<n
279
273
  for (const feat of batch.features || []) {
280
274
  if (feat.action === 'remove') continue;
281
275
  const parent = findFeature(merged, feat.slug);
282
- if (!parent) throw new Error(`feature "${feat.slug}" not found for submodule operations`);
276
+ if (!parent) throw new UserInputError(`feature "${feat.slug}" not found for submodule operations`);
283
277
  for (const sub of feat.submodules || []) {
284
278
  switch (sub.action) {
285
279
  case 'add': {
@@ -294,7 +288,7 @@ async function handleApply(applyArgs: string[], context: ToolContext): Promise<n
294
288
  removeSubmodule(parent, sub.slug, merged);
295
289
  break;
296
290
  default:
297
- throw new Error(
291
+ throw new UserInputError(
298
292
  `submodule "${feat.slug}/${sub.slug}": unknown action "${sub.action}"`,
299
293
  );
300
294
  }
@@ -310,7 +304,7 @@ async function handleApply(applyArgs: string[], context: ToolContext): Promise<n
310
304
  if (sub.action === 'remove') continue;
311
305
  const subMod = findSubmodule(parent, sub.slug);
312
306
  if (!subMod)
313
- throw new Error(`submodule "${feat.slug}/${sub.slug}" not found for function operations`);
307
+ throw new UserInputError(`submodule "${feat.slug}/${sub.slug}" not found for function operations`);
314
308
  for (const fn of sub.functions || []) {
315
309
  switch (fn.action) {
316
310
  case 'add': {
@@ -332,7 +326,7 @@ async function handleApply(applyArgs: string[], context: ToolContext): Promise<n
332
326
  );
333
327
  break;
334
328
  default:
335
- throw new Error(
329
+ throw new UserInputError(
336
330
  `function "${feat.slug}/${sub.slug}/${fn.name}": unknown action "${fn.action}"`,
337
331
  );
338
332
  }
@@ -348,7 +342,7 @@ async function handleApply(applyArgs: string[], context: ToolContext): Promise<n
348
342
  from = parseEndpoint(edge.from);
349
343
  to = parseEndpoint(edge.to);
350
344
  } catch (er: any) {
351
- throw new Error(`edge: ${er.message}`);
345
+ throw new UserInputError(`edge: ${er.message}`, undefined, { cause: er });
352
346
  }
353
347
 
354
348
  switch (edge.action) {
@@ -356,28 +350,28 @@ async function handleApply(applyArgs: string[], context: ToolContext): Promise<n
356
350
  // Referential integrity validation
357
351
  const fromFeature = findFeature(merged, from.feature);
358
352
  if (!fromFeature) {
359
- throw new Error(
353
+ throw new UserInputError(
360
354
  `edge "${edge.from} → ${edge.to}": source feature "${from.feature}" not found`,
361
355
  );
362
356
  }
363
357
  if (from.submodule) {
364
358
  const fromSub = findSubmodule(fromFeature, from.submodule);
365
359
  if (!fromSub) {
366
- throw new Error(
360
+ throw new UserInputError(
367
361
  `edge "${edge.from} → ${edge.to}": source submodule "${from.submodule}" not found in feature "${from.feature}"`,
368
362
  );
369
363
  }
370
364
  }
371
365
  const toFeature = findFeature(merged, to.feature);
372
366
  if (!toFeature) {
373
- throw new Error(
367
+ throw new UserInputError(
374
368
  `edge "${edge.from} → ${edge.to}": target feature "${to.feature}" not found`,
375
369
  );
376
370
  }
377
371
  if (to.submodule) {
378
372
  const toSub = findSubmodule(toFeature, to.submodule);
379
373
  if (!toSub) {
380
- throw new Error(
374
+ throw new UserInputError(
381
375
  `edge "${edge.from} → ${edge.to}": target submodule "${to.submodule}" not found in feature "${to.feature}"`,
382
376
  );
383
377
  }
@@ -426,12 +420,14 @@ async function handleApply(applyArgs: string[], context: ToolContext): Promise<n
426
420
  break;
427
421
  }
428
422
  default:
429
- throw new Error(`edge "${edge.from} → ${edge.to}": unknown action "${edge.action}"`);
423
+ throw new UserInputError(`edge "${edge.from} → ${edge.to}": unknown action "${edge.action}"`);
430
424
  }
431
425
  }
432
426
  } catch (e: any) {
433
- stderr.write(`Batch aborted: ${e.message}\n`);
434
- return 1;
427
+ if (e instanceof UserInputError || e instanceof SystemError) {
428
+ throw e;
429
+ }
430
+ throw new UserInputError(e.message);
435
431
  }
436
432
 
437
433
  // ── All mutations succeeded — persist ──
@@ -481,54 +477,50 @@ async function handleApply(applyArgs: string[], context: ToolContext): Promise<n
481
477
  // ── template ─────────────────────────────────────────────────────────────────
482
478
 
483
479
  async function handleTemplate(templateArgs: string[], context: ToolContext): Promise<number> {
484
- const stdout = context.stdout || process.stdout;
485
- const stderr = context.stderr || process.stderr;
486
-
487
- // Parse --spec <dir> --output <dir>
488
- let specDir: string | undefined;
489
- let outputDir: string | undefined;
490
- for (let i = 0; i < templateArgs.length; i++) {
491
- const a = templateArgs[i];
492
- if (a === '--spec' && i + 1 < templateArgs.length) specDir = templateArgs[++i];
493
- else if (a === '--output' && i + 1 < templateArgs.length) outputDir = templateArgs[++i];
494
- }
480
+ const stdout = context.stdout || process.stdout;
481
+ const adapter = createPlatformAdapter();
482
+
483
+ // Parse --spec <dir> --output <dir>
484
+ let specDir: string | undefined;
485
+ let outputDir: string | undefined;
486
+ for (let i = 0; i < templateArgs.length; i++) {
487
+ const a = templateArgs[i];
488
+ if (a === '--spec' && i + 1 < templateArgs.length) specDir = templateArgs[++i];
489
+ else if (a === '--output' && i + 1 < templateArgs.length) outputDir = templateArgs[++i];
490
+ }
495
491
 
496
- if (!specDir || !outputDir) {
497
- stderr.write('Usage: apltk architecture template --spec <spec-dir> --output <output-dir>\n');
498
- return 1;
499
- }
492
+ if (!specDir || !outputDir) {
493
+ throw new UserInputError('Missing --spec and/or --output arguments. Usage: apltk architecture template --spec <spec-dir> --output <output-dir>');
494
+ }
500
495
 
501
- const specPath = path.resolve(specDir, 'SPEC.md');
502
- const outputDirPath = path.resolve(outputDir);
503
- const outputPath = path.join(outputDirPath, 'proposal.yaml');
496
+ const specPath = path.resolve(specDir, 'SPEC.md');
497
+ const outputDirPath = path.resolve(outputDir);
498
+ const outputPath = path.join(outputDirPath, 'proposal.yaml');
504
499
 
505
- // Extract spec metadata (title, goal) from SPEC.md
506
- let featureSlug = 'feature';
507
- let featureTitle = 'Feature';
508
- let goal = '';
500
+ // Extract spec metadata (title, goal) from SPEC.md
501
+ let featureSlug = 'feature';
502
+ let featureTitle = 'Feature';
503
+ let goal = '';
509
504
 
510
- if (fs.existsSync(specPath)) {
511
- const meta = parseSpecMetadata(specPath);
512
- if (meta.title) {
513
- featureTitle = meta.title;
514
- featureSlug = toSlug(featureTitle);
515
- }
516
- if (meta.goal) {
517
- goal = meta.goal;
518
- }
519
- } else {
505
+ if (fs.existsSync(specPath)) {
506
+ const meta = parseSpecMetadata(specPath);
507
+ if (meta.title) {
508
+ featureTitle = meta.title;
509
+ featureSlug = toSlug(featureTitle);
510
+ }
511
+ if (meta.goal) {
512
+ goal = meta.goal;
513
+ }
514
+ } else {
520
515
  const resolvedSpecDir = path.resolve(specDir);
521
516
  if (!fs.existsSync(resolvedSpecDir)) {
522
- stderr.write(`Spec directory not found: ${resolvedSpecDir}\n`);
523
- } else {
524
- const mdFiles = fs.readdirSync(resolvedSpecDir).filter((f: string) => f.endsWith('.md'));
525
- if (mdFiles.length > 0) {
526
- stderr.write(`Spec directory found but no SPEC.md. Found: ${mdFiles.join(', ')}\n`);
527
- } else {
528
- stderr.write(`Spec directory found but no SPEC.md. No .md files found.\n`);
529
- }
517
+ throw new UserInputError(`Spec directory not found: ${resolvedSpecDir}`);
518
+ }
519
+ const mdFiles = fs.readdirSync(resolvedSpecDir).filter((f: string) => f.endsWith('.md'));
520
+ if (mdFiles.length > 0) {
521
+ throw new UserInputError(`Spec directory found but no SPEC.md. Found: ${mdFiles.join(', ')}`);
530
522
  }
531
- return 1;
523
+ throw new UserInputError('Spec directory found but no SPEC.md. No .md files found.');
532
524
  }
533
525
 
534
526
  // Build proposal.yaml content
@@ -554,11 +546,10 @@ async function handleTemplate(templateArgs: string[], context: ToolContext): Pro
554
546
 
555
547
  try {
556
548
  fs.mkdirSync(outputDirPath, { recursive: true });
557
- fs.writeFileSync(outputPath, lines.join('\n'), 'utf8');
549
+ fs.writeFileSync(outputPath, lines.join(adapter.EOL), 'utf8');
558
550
  stdout.write(`${outputPath}\n`);
559
551
  } catch (e: any) {
560
- stderr.write(`Error writing proposal.yaml: ${e.message}\n`);
561
- return 1;
552
+ throw new SystemError(`Error writing proposal.yaml: ${e.message}`);
562
553
  }
563
554
 
564
555
  // Try to enrich with CodeGraph API listing
@@ -581,7 +572,7 @@ async function handleTemplate(templateArgs: string[], context: ToolContext): Pro
581
572
  }
582
573
  apiLines.push('#');
583
574
  const existing = fs.readFileSync(outputPath, 'utf8');
584
- fs.writeFileSync(outputPath, apiLines.join('\n') + '\n' + existing);
575
+ fs.writeFileSync(outputPath, apiLines.join(adapter.EOL) + adapter.EOL + existing);
585
576
  cg.close();
586
577
  }
587
578
  } catch {
@@ -593,14 +584,27 @@ async function handleTemplate(templateArgs: string[], context: ToolContext): Pro
593
584
 
594
585
  // ── Handler entrypoint ───────────────────────────────────────────────────────
595
586
 
587
+ /**
588
+ * architectureHandler — Known carryover from the createToolRunner migration.
589
+ *
590
+ * Reason for not using createToolRunner:
591
+ * - Mixed TS/JS dispatch: "apply" and "template" subcommands use TypeScript
592
+ * with AppError throws. Other subcommands delegate to the JS atlas CLI
593
+ * (cli.js) which has its own error handling.
594
+ * - Subcommand-level flag parsing: Each subcommand has unique flags; a single
595
+ * ToolSchema can't express this. See DESIGN.md §2.3 for the full picture.
596
+ *
597
+ * Error handling: All TS paths throw UserInputError/SystemError. JS paths are
598
+ * handled by cli.dispatch()'s internal catch.
599
+ */
596
600
  export async function architectureHandler(
597
601
  args: string[],
598
602
  context: ToolContext,
599
603
  ): Promise<number> {
600
604
  // Intercept apply / template before passing through to the JS CLI
601
605
  const first = args[0] || '';
602
- if (first === 'apply') return handleApply(args.slice(1), context);
603
- if (first === 'template') return handleTemplate(args.slice(1), context);
606
+ if (first === 'apply') return await handleApply(args.slice(1), context);
607
+ if (first === 'template') return await handleTemplate(args.slice(1), context);
604
608
 
605
609
  // Delegate to the existing atlas CLI (still in JS)
606
610
  const sourceRoot =
@@ -615,20 +619,13 @@ export async function architectureHandler(
615
619
  'cli.js',
616
620
  );
617
621
 
618
- try {
619
- // Use file URL for ESM import compatibility on Windows — import() requires forward slashes.
620
- const cliModule = await import(pathToFileURL(cliPath).href);
621
- const cli = cliModule.default;
622
- return cli.dispatch(args, {
623
- stdout: context.stdout || process.stdout,
624
- stderr: context.stderr || process.stderr,
625
- });
626
- } catch (error: any) {
627
- (context.stderr || process.stderr).write(
628
- `Error loading atlas CLI: ${error.message}\n`,
629
- );
630
- return 1;
631
- }
622
+ // Use file URL for ESM import compatibility on Windows — import() requires forward slashes.
623
+ const cliModule = await import(pathToFileURL(cliPath).href);
624
+ const cli = cliModule.default;
625
+ return cli.dispatch(args, {
626
+ stdout: context.stdout || process.stdout,
627
+ stderr: context.stderr || process.stderr,
628
+ });
632
629
  }
633
630
 
634
631
  export const tool: ToolDefinition = {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@laitszkin/tool-architecture",
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,4 @@
1
+ import { SystemError, UserInputError } from '../../../tool-utils/dist/index.js';
1
2
  import { findProjectRoot } from './lib/cg-instance.js';
2
3
  import { handleInit } from './lib/cmd-init.js';
3
4
  import { handleSync } from './lib/cmd-sync.js';
@@ -35,13 +36,11 @@ export async function codegraphHandler(args, context) {
35
36
  projectRoot = findProjectRoot(context.cwd || process.cwd());
36
37
  }
37
38
  catch (error) {
38
- if (error.code === 'MODULE_NOT_FOUND' || (error.message && error.message.includes('Cannot find module'))) {
39
- stderr.write('`@colbymchenry/codegraph` is not installed. Run `npm install @colbymchenry/codegraph` in your project directory.\n');
39
+ const message = error instanceof Error ? error.message : 'Unknown error finding project root';
40
+ if (error?.code === 'MODULE_NOT_FOUND' || message.includes('Cannot find module')) {
41
+ throw new UserInputError('`@colbymchenry/codegraph` is not installed. Run `npm install @colbymchenry/codegraph` in your project directory.');
40
42
  }
41
- else {
42
- stderr.write(`Error finding project root: ${error.message}\n`);
43
- }
44
- return 1;
43
+ throw new SystemError(`Error finding project root: ${message}`);
45
44
  }
46
45
  // Parse --spec <dir> for verify
47
46
  const specIndex = rest.indexOf('--spec');
@@ -85,16 +84,14 @@ export async function codegraphHandler(args, context) {
85
84
  case 'search': {
86
85
  const query = rest.join(' ');
87
86
  if (!query) {
88
- stderr.write('Usage: apltk codegraph search <query> [--limit N] [--json]\n');
89
- return 1;
87
+ throw new UserInputError('Usage: apltk codegraph search <query> [--limit N] [--json]');
90
88
  }
91
89
  return await handleSearch(projectRoot, query, { limit, json: isJson });
92
90
  }
93
91
  case 'explore': {
94
92
  const query = rest.join(' ');
95
93
  if (!query) {
96
- stderr.write('Usage: apltk codegraph explore <query> [--json]\n');
97
- return 1;
94
+ throw new UserInputError('Usage: apltk codegraph explore <query> [--json]');
98
95
  }
99
96
  return await handleExplore(projectRoot, query, { json: isJson, feature: featureName });
100
97
  }
@@ -111,25 +108,18 @@ export async function codegraphHandler(args, context) {
111
108
  }
112
109
  case 'verify': {
113
110
  if (!specDir) {
114
- stderr.write('Usage: apltk codegraph verify --spec <spec-dir> [--json]\n');
115
- return 1;
111
+ throw new UserInputError('Usage: apltk codegraph verify --spec <spec-dir> [--json]');
116
112
  }
117
113
  return await handleVerify(projectRoot, specDir, { json: isJson });
118
114
  }
119
115
  default:
120
- stderr.write(`Unknown subcommand: ${subcommand}\n\n`);
121
- printHelp(stderr);
122
- return 1;
116
+ throw new SystemError(`Unknown codegraph subcommand: ${subcommand}`);
123
117
  }
124
118
  }
125
119
  catch (error) {
126
- if (error.code === 'MODULE_NOT_FOUND' || (error.message && error.message.includes('Cannot find module'))) {
127
- stderr.write('`@colbymchenry/codegraph` is not installed. Run `npm install @colbymchenry/codegraph` in your project directory.\n');
128
- }
129
- else {
130
- stderr.write(`Error running codegraph ${subcommand}: ${error.message}\n`);
131
- }
132
- return 1;
120
+ if (error instanceof SystemError || error instanceof UserInputError)
121
+ throw error;
122
+ throw new SystemError(error instanceof Error ? error.message : 'Unknown error in codegraph', { cause: error instanceof Error ? error : undefined });
133
123
  }
134
124
  }
135
125
  function printHelp(stream) {