@laitszkin/apollo-toolkit 4.1.3 → 5.0.0

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 +7 -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 +6 -3
  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 +3 -3
  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 +3 -3
  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 +3 -3
  66. package/packages/tools/codegraph/dist/index.js +21 -17
  67. package/packages/tools/codegraph/dist/tsconfig.tsbuildinfo +1 -1
  68. package/packages/tools/codegraph/index.ts +21 -17
  69. package/packages/tools/codegraph/package.json +3 -3
  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 +3 -3
  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 +3 -3
  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 +3 -3
  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 +3 -3
  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 +3 -3
  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 +3 -3
  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 +3 -3
  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 +3 -3
  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 +3 -3
  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 +3 -3
  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 +3 -3
  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 +3 -3
  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 +3 -3
  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 +3 -3
  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 +3 -3
  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 +3 -3
  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 +3 -3
  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 +3 -3
  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 +3 -3
  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 +6 -5
  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
@@ -1,257 +1,50 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
- import { color, supportsColor, buildBanner, buildWelcomeScreen, buildSupportedTargetLines, animateWelcomeScreen, promptYesNo, promptForModes, isInteractive } from '../../tui/dist/index.js';
5
- import { formatToolList, buildToolDiscoveryHelp, runTool } from '../../tool-registry/dist/index.js';
6
- import { formatExamples } from '../../tool-registry/dist/index.js';
4
+ import { color, supportsColor, buildBanner, buildWelcomeScreen, animateWelcomeScreen, promptYesNo, promptForModes, isInteractive, createStdioWriter } from '../../tui/dist/index.js';
5
+ import { runTool } from '../../tool-registry/dist/index.js';
7
6
  import { TARGET_DEFINITIONS, VALID_MODES, installLinks, listAllKnownSkillNames, listCodexSkillNames, normalizeModes, resolveToolkitHome, syncToolkitHome, uninstallSkills, getTargetRoots, getUninstallTargetRoots, expandUserPath, readManifest, writeManifest, resolveHomeDirectory, listSkillNames, } from './installer.js';
8
7
  import { checkForPackageUpdate, compareVersions, execCommand } from './updater.js';
9
8
  import { registerAllTools, isKnownToolName } from './tool-registration.js';
10
9
  // Re-export installer functions for external consumers (tests, bin)
11
10
  export { TARGET_DEFINITIONS, VALID_MODES, installLinks, listAllKnownSkillNames, listCodexSkillNames, normalizeModes, resolveToolkitHome, syncToolkitHome, uninstallSkills, getTargetRoots, getUninstallTargetRoots, expandUserPath, readManifest, writeManifest, resolveHomeDirectory, listSkillNames, checkForPackageUpdate, compareVersions, execCommand, };
12
- function buildModeUsagePattern() {
13
- return `${VALID_MODES.join('|')}|all`;
14
- }
15
- function buildInteractiveModeHint() {
16
- const quotedModes = [...VALID_MODES, 'all'].map((mode) => `\`${mode}\``);
17
- return `${quotedModes.slice(0, -1).join(', ')}, or ${quotedModes.at(-1)}`;
18
- }
19
- function buildHelpText({ version, colorEnabled }) {
20
- const examples = [
21
- { command: 'apltk --help', result: 'Shows the top-level Apollo Toolkit guide, including install modes and bundled task-tool discovery.' },
22
- { command: 'apltk tools --help', result: 'Lists bundled tools by task so you can decide which CLI helper to inspect next.' },
23
- { command: 'apltk architecture --help', result: 'Shows the architecture atlas command tree, task guidance, and action-specific follow-up help paths.' },
24
- { command: 'apltk tools architecture --help', result: 'Shows what the architecture atlas tool is for, then prints its native command tree and examples.' },
25
- { command: 'apltk filter-logs app.log --start 2026-03-24T10:00:00Z', result: 'Prints only the log lines whose timestamps fall within the requested time window.' },
26
- ];
27
- return [
28
- buildBanner({ version, colorEnabled }),
29
- '',
30
- 'Usage:',
31
- ` apltk [install] [${buildModeUsagePattern()}]...`,
32
- ` apollo-toolkit [install] [${buildModeUsagePattern()}]...`,
33
- ` apltk uninstall [${buildModeUsagePattern()}]... [--yes]`,
34
- ' apltk tools',
35
- ' apltk <tool> [...args]',
36
- ' apltk tools <tool> [...args]',
37
- ' apltk --help',
38
- ' apollo-toolkit --help',
39
- '',
40
- 'Common goals:',
41
- ' - Install or refresh skills in one or more agent targets: `apltk install --help`',
42
- ' - Remove manifest-tracked installs from selected targets: `apltk uninstall --help`',
43
- ' - Discover which bundled helper tool matches a task: `apltk tools --help`',
44
- ' - Inspect one tool deeply before running it: `apltk tools <tool> --help`',
45
- '',
46
- 'Bundled tools:',
47
- formatToolList(),
48
- '',
49
- buildToolDiscoveryHelp(),
50
- '',
51
- 'Options:',
52
- ' --home <path> Override Apollo Toolkit home directory',
53
- ' --symlink Install skills as symlinks instead of copied directories',
54
- ' --copy Install skills as copied directories instead of symlinks',
55
- ' --yes, -y Skip uninstall confirmation',
56
- ' --help Show this help text',
57
- '',
58
- 'Examples:',
59
- formatExamples(examples),
60
- ].join('\n');
61
- }
62
- function buildToolsHelp({ version, colorEnabled }) {
63
- const examples = [
64
- { command: 'apltk tools', result: 'Lists all bundled tools plus the task-oriented discovery guide.' },
65
- { command: 'apltk tools open-github-issue --help', result: 'Shows when to use the GitHub issue publisher, then prints its exact script flags and examples.' },
66
- { command: 'apltk tools architecture --help', result: 'Shows when to use the architecture atlas CLI, then prints its native command tree.' },
67
- ];
68
- return [
69
- buildBanner({ version, colorEnabled }),
70
- '',
71
- 'Usage:',
72
- ' apltk tools',
73
- ' apltk <tool> [...args]',
74
- ' apltk tools <tool> [...args]',
75
- '',
76
- buildToolDiscoveryHelp(),
77
- '',
78
- 'Bundled tools:',
79
- formatToolList(),
80
- '',
81
- 'Tip:',
82
- ' Pass `--help` after a tool name to view task guidance, native script flags, and concrete examples.',
83
- '',
84
- 'Examples:',
85
- formatExamples(examples),
86
- ].join('\n');
87
- }
88
- function buildInstallHelpText({ version, colorEnabled }) {
89
- const examples = [
90
- { command: 'apltk', result: 'Launches the interactive installer, opens the target selector, and then walks through link-mode confirmation.' },
91
- { command: 'apltk codex openclaw --symlink', result: 'Performs a non-interactive install into `codex` and `openclaw` targets using symlinks.' },
92
- { command: 'npx @laitszkin/apollo-toolkit all --copy', result: 'Installs a copied snapshot into every supported target instead of symlinking.' },
93
- ];
94
- return [
95
- buildBanner({ version, colorEnabled }),
96
- '',
97
- 'Usage:',
98
- ` apltk [install] [${buildModeUsagePattern()}]...`,
99
- ` apollo-toolkit [install] [${buildModeUsagePattern()}]...`,
100
- '',
101
- 'Use this when:',
102
- ' - You want to install or refresh Apollo Toolkit skills in one or more agent targets.',
103
- ' - You need to choose between symlink mode (auto-updating) and copy mode (stable snapshot).',
104
- '',
105
- 'Supported targets:',
106
- buildSupportedTargetLines({ targets: [...TARGET_DEFINITIONS], colorEnabled }),
107
- '',
108
- 'Behavior notes:',
109
- ' - Running `apltk` with no targets opens the interactive installer and target selector.',
110
- ' - `--symlink` keeps installed skills connected to the managed toolkit checkout in `~/.apollo-toolkit`.',
111
- ' - `--copy` installs a snapshot that only changes when you run the installer again.',
112
- ' - The installer can optionally include codex-exclusive skills in non-codex targets after prompting.',
113
- '',
114
- 'Options:',
115
- ' --home <path> Override Apollo Toolkit home directory',
116
- ' --symlink Install skills as symlinks (recommended)',
117
- ' --copy Install skills as copied directories',
118
- ' --help Show this install help',
119
- '',
120
- 'Examples:',
121
- formatExamples(examples),
122
- ].join('\n');
123
- }
124
- function buildUninstallHelpText({ version, colorEnabled }) {
125
- const examples = [
126
- { command: 'apltk uninstall', result: 'Opens the interactive uninstall selector when running in a TTY and then asks for confirmation before removal.' },
127
- { command: 'apltk uninstall codex agents --yes', result: 'Removes Apollo Toolkit-managed installs from `codex` and `agents` without another confirmation prompt.' },
128
- { command: 'apltk uninstall codex --home /tmp/custom-home', result: 'Uses the custom managed toolkit home while removing manifest-tracked installs from the selected target.' },
129
- ];
130
- return [
131
- buildBanner({ version, colorEnabled }),
132
- '',
133
- 'Usage:',
134
- ` apltk uninstall [${buildModeUsagePattern()}]... [--yes]`,
135
- '',
136
- 'Use this when:',
137
- ' - You want to remove Apollo Toolkit-managed skills from one or more agent targets.',
138
- ' - You need to clean up manifest-tracked historical installs as well as the current installed skills.',
139
- '',
140
- 'Supported targets:',
141
- buildSupportedTargetLines({ targets: [...TARGET_DEFINITIONS], colorEnabled }),
142
- '',
143
- 'Behavior notes:',
144
- ' - With no explicit targets, uninstall opens the interactive selector in a TTY and otherwise falls back to all targets.',
145
- ' - Uninstall removes manifest-tracked current and historical Apollo Toolkit skill directories.',
146
- ' - `--yes` skips the confirmation prompt after the target list is resolved.',
147
- '',
148
- 'Options:',
149
- ' --home <path> Override Apollo Toolkit home directory',
150
- ' --yes, -y Skip uninstall confirmation',
151
- ' --help Show this uninstall help',
152
- '',
153
- 'Examples:',
154
- formatExamples(examples),
155
- ].join('\n');
156
- }
11
+ import { formatAppError } from '../../tool-utils/dist/index.js';
12
+ import { InstallArgsParser } from './parsers/install-parser.js';
13
+ import { UninstallArgsParser } from './parsers/uninstall-parser.js';
14
+ import { ToolArgsParser } from './parsers/tool-parser.js';
15
+ import { HelpTextBuilder } from './help-text-builder.js';
157
16
  function readPackageJson(sourceRoot) {
158
17
  return JSON.parse(fs.readFileSync(path.join(sourceRoot, 'package.json'), 'utf8'));
159
18
  }
160
19
  function parseArguments(argv) {
161
- const args = [...argv];
162
- const result = {
163
- command: 'install',
164
- modes: [],
165
- showHelp: false,
166
- showToolsHelp: false,
167
- toolkitHome: null,
168
- toolName: null,
169
- toolArgs: [],
170
- linkMode: null,
171
- assumeYes: false,
172
- explicitInstallCommand: false,
173
- helpTopic: 'overview',
174
- };
175
- if (args[0] === 'uninstall') {
176
- result.command = 'uninstall';
177
- args.shift();
178
- while (args.length > 0) {
179
- const arg = args.shift();
180
- if (arg === '--help' || arg === '-h') {
181
- result.showHelp = true;
182
- }
183
- else if (arg === '--yes' || arg === '-y') {
184
- result.assumeYes = true;
185
- }
186
- else if (arg === '--home') {
187
- const toolkitHome = args.shift();
188
- if (!toolkitHome)
189
- throw new Error('Missing value for --home');
190
- result.toolkitHome = path.resolve(toolkitHome);
191
- }
192
- else {
193
- result.modes.push(arg);
194
- }
20
+ const firstArg = argv[0];
21
+ // Shared parser instances (eliminates double instantiation)
22
+ const installParser = new InstallArgsParser();
23
+ const toolParser = new ToolArgsParser();
24
+ // Dispatch table for all known command types
25
+ // ==== Collision zone (FIX-09) ====
26
+ // L55-190 and L349-360 are high-collision regions touched by dispatch,
27
+ // parser, and error-boundary changes. Modify with care.
28
+ // =================================
29
+ const commandParsers = new Map([
30
+ ['install', installParser],
31
+ ['uninstall', new UninstallArgsParser()],
32
+ ['tools', toolParser],
33
+ ['tool', toolParser],
34
+ ]);
35
+ // Command dispatch: iterate parsers, first match wins
36
+ for (const [name, parser] of commandParsers) {
37
+ if (firstArg === name) {
38
+ const result = parser.parse(argv);
39
+ return parser.toParsedArguments(result);
195
40
  }
196
- if (result.showHelp)
197
- result.helpTopic = 'uninstall';
198
- return result;
199
41
  }
200
- if (args[0] === 'tools' || args[0] === 'tool') {
201
- args.shift();
202
- const nextArg = args[0];
203
- if (args.length === 0 || nextArg === '--help' || nextArg === '-h') {
204
- result.command = 'tools-help';
205
- result.showToolsHelp = true;
206
- return result;
207
- }
208
- result.command = 'tool';
209
- result.toolName = args.shift() || null;
210
- result.toolArgs = args;
211
- return result;
212
- }
213
- const firstArg = args[0];
42
+ // Direct tool name (no "tools" prefix) route through the 'tool' dispatch table entry
214
43
  if (firstArg && isKnownToolName(firstArg)) {
215
- result.command = 'tool';
216
- result.toolName = args.shift() || null;
217
- result.toolArgs = args;
218
- return result;
219
- }
220
- while (args.length > 0) {
221
- const arg = args.shift();
222
- if (arg === '--help' || arg === '-h') {
223
- result.showHelp = true;
224
- continue;
225
- }
226
- if (arg === '--home') {
227
- const toolkitHome = args.shift();
228
- if (!toolkitHome)
229
- throw new Error('Missing value for --home');
230
- result.toolkitHome = path.resolve(toolkitHome);
231
- continue;
232
- }
233
- if (arg === '--symlink') {
234
- result.linkMode = 'symlink';
235
- continue;
236
- }
237
- if (arg === '--copy') {
238
- result.linkMode = 'copy';
239
- continue;
240
- }
241
- if (arg === 'install') {
242
- result.explicitInstallCommand = true;
243
- continue;
244
- }
245
- result.modes.push(arg);
246
- }
247
- if (result.showHelp) {
248
- const installContextRequested = result.explicitInstallCommand
249
- || result.modes.length > 0
250
- || result.linkMode !== null
251
- || result.toolkitHome !== null;
252
- result.helpTopic = installContextRequested ? 'install' : 'overview';
44
+ return toolParser.toParsedArguments(toolParser.parse(argv));
253
45
  }
254
- return result;
46
+ // Default: install (handles bare arguments like "codex", "--help", or empty argv)
47
+ return installParser.toParsedArguments(installParser.parse(argv));
255
48
  }
256
49
  function buildSymlinkInfo({ colorEnabled }) {
257
50
  return [
@@ -326,7 +119,12 @@ function printUninstallSummary({ stdout, uninstallResult, env }) {
326
119
  stdout.write(` Removed: ${result.removedSkills.length > 0 ? result.removedSkills.join(', ') : '(manifest only)'}\n`);
327
120
  }
328
121
  }
329
- export { parseArguments, buildHelpText, buildInstallHelpText, buildUninstallHelpText, buildToolsHelp, buildBanner, buildWelcomeScreen, registerAllTools };
122
+ export { InstallArgsParser } from './parsers/install-parser.js';
123
+ export { UninstallArgsParser } from './parsers/uninstall-parser.js';
124
+ export { ToolArgsParser } from './parsers/tool-parser.js';
125
+ export { HelpTextBuilder } from './help-text-builder.js';
126
+ export { normalizeParseError } from './parsers/parser-utils.js';
127
+ export { parseArguments, buildBanner, buildWelcomeScreen, registerAllTools };
330
128
  export async function run(argv, context = {}) {
331
129
  const __filename = fileURLToPath(import.meta.url);
332
130
  const __dir = path.dirname(__filename);
@@ -335,6 +133,7 @@ export async function run(argv, context = {}) {
335
133
  const stderr = context.stderr || process.stderr;
336
134
  const stdin = context.stdin || process.stdin;
337
135
  const env = context.env || process.env;
136
+ const stdioWriter = createStdioWriter({ stdout, stderr, env });
338
137
  let packageJson = readPackageJson(sourceRoot);
339
138
  try {
340
139
  const parsed = parseArguments(argv);
@@ -342,23 +141,30 @@ export async function run(argv, context = {}) {
342
141
  const colorEnabled = supportsColor(stdout, env);
343
142
  if (parsed.helpTopic === 'overview')
344
143
  await registerAllTools();
144
+ const builder = new HelpTextBuilder({ version: packageJson.version, colorEnabled });
345
145
  const helpText = parsed.helpTopic === 'install'
346
- ? buildInstallHelpText({ version: packageJson.version, colorEnabled })
146
+ ? builder.install()
347
147
  : parsed.helpTopic === 'uninstall'
348
- ? buildUninstallHelpText({ version: packageJson.version, colorEnabled })
349
- : buildHelpText({ version: packageJson.version, colorEnabled });
148
+ ? builder.uninstall()
149
+ : builder.overview();
350
150
  stdout.write(`${helpText}\n`);
351
151
  return 0;
352
152
  }
353
153
  if (parsed.showToolsHelp) {
354
154
  await registerAllTools();
355
- stdout.write(`${buildToolsHelp({ version: packageJson.version, colorEnabled: supportsColor(stdout, env) })}\n`);
155
+ stdout.write(`${new HelpTextBuilder({ version: packageJson.version, colorEnabled: supportsColor(stdout, env) }).toolsHelp()}\n`);
356
156
  return 0;
357
157
  }
158
+ // Tool dispatch error patterns (FIX-10):
159
+ // Pattern A (createToolRunner tools): handler throws -> caught internally ->
160
+ // formatAppError + return 1
161
+ // Pattern B (carryover tools): handler throws -> propagates through runTool ->
162
+ // CLI boundary catch -> formatAppError + return 1
163
+ // Both patterns converge on the same formatting at the boundary.
358
164
  if (parsed.command === 'tool') {
359
165
  await registerAllTools();
360
- return (context.runTool || runTool)(parsed.toolName, parsed.toolArgs, {
361
- sourceRoot, stdout, stderr, env, spawnCommand: context.spawnCommand,
166
+ return await (context.runTool || runTool)(parsed.toolName, parsed.toolArgs, {
167
+ sourceRoot, stdout, stderr, env, spawnCommand: context.spawnCommand, stdioWriter,
362
168
  });
363
169
  }
364
170
  // Uninstall flow
@@ -465,7 +271,7 @@ export async function run(argv, context = {}) {
465
271
  return 0;
466
272
  }
467
273
  catch (error) {
468
- stderr.write(`Error: ${error.message}\n`);
274
+ formatAppError(stderr, error);
469
275
  return 1;
470
276
  }
471
277
  }
@@ -14,6 +14,7 @@ export declare function normalizeModes(inputModes: string[]): InstallMode[];
14
14
  export declare function listSkillNames(rootDir: string, modes?: InstallMode[]): Promise<string[]>;
15
15
  export declare function listCodexSkillNames(rootDir: string): Promise<string[]>;
16
16
  export declare function readManifest(targetRoot: string): Promise<ManifestData | null>;
17
+ export declare function isSafeSkillName(skillName: string): boolean;
17
18
  export declare function writeManifest(targetRoot: string, { version, linkMode, skills, previousSkills }: {
18
19
  version: string;
19
20
  linkMode: string;
@@ -1,7 +1,8 @@
1
1
  import fs from 'node:fs';
2
2
  import fsp from 'node:fs/promises';
3
- import os from 'node:os';
4
3
  import path from 'node:path';
4
+ import { createPlatformAdapter } from '../../tool-utils/dist/index.js';
5
+ const platformAdapter = createPlatformAdapter();
5
6
  export const TARGET_DEFINITIONS = Object.freeze([
6
7
  { id: 'codex', label: 'Codex', description: '~/.codex/skills' },
7
8
  { id: 'openclaw', label: 'OpenClaw', description: '~/.openclaw/workspace*/skills' },
@@ -14,7 +15,7 @@ const COPY_FILES = new Set(['AGENTS.md', 'CHANGELOG.md', 'LICENSE', 'README.md',
14
15
  const SKILLS_DIRNAME = 'skills';
15
16
  export const MANIFEST_FILENAME = '.apollo-toolkit-manifest.json';
16
17
  export function resolveHomeDirectory(env = process.env) {
17
- return env.HOME || env.USERPROFILE || os.homedir();
18
+ return createPlatformAdapter().homeDir(env);
18
19
  }
19
20
  export function expandUserPath(inputPath, env = process.env) {
20
21
  if (!inputPath)
@@ -99,12 +100,12 @@ export async function readManifest(targetRoot) {
99
100
  return null;
100
101
  }
101
102
  }
102
- function isSafeSkillName(skillName) {
103
+ export function isSafeSkillName(skillName) {
103
104
  return typeof skillName === 'string'
104
105
  && skillName.length > 0
105
106
  && !skillName.includes('\0')
106
107
  && !skillName.includes('/')
107
- && !skillName.includes('\\')
108
+ && !(skillName.includes('\\') && createPlatformAdapter().isWindows())
108
109
  && !path.isAbsolute(skillName)
109
110
  && skillName !== '.'
110
111
  && skillName !== '..';
@@ -125,7 +126,7 @@ export async function writeManifest(targetRoot, { version, linkMode, skills, pre
125
126
  historicalSkills,
126
127
  };
127
128
  await fsp.mkdir(targetRoot, { recursive: true });
128
- await fsp.writeFile(path.join(targetRoot, MANIFEST_FILENAME), `${JSON.stringify(manifest, null, 2)}\n`, 'utf8');
129
+ await fsp.writeFile(path.join(targetRoot, MANIFEST_FILENAME), `${JSON.stringify(manifest, null, 2)}${platformAdapter.EOL}`, 'utf8');
129
130
  }
130
131
  export async function listAllKnownSkillNames({ toolkitHome, modes = [], env = process.env }) {
131
132
  const allNames = new Set();
@@ -186,7 +187,7 @@ async function stageToolkitContents({ sourceRoot, destinationRoot, version, mode
186
187
  copiedEntries.push(entry.name);
187
188
  }
188
189
  const metadata = { version, installedAt: new Date().toISOString(), source: 'npm-package' };
189
- await fsp.writeFile(path.join(destinationRoot, '.apollo-toolkit-install.json'), `${JSON.stringify(metadata, null, 2)}\n`, 'utf8');
190
+ await fsp.writeFile(path.join(destinationRoot, '.apollo-toolkit-install.json'), `${JSON.stringify(metadata, null, 2)}${platformAdapter.EOL}`, 'utf8');
190
191
  return copiedEntries.sort();
191
192
  }
192
193
  export async function syncToolkitHome({ sourceRoot, toolkitHome, version, modes = [] }) {
@@ -297,9 +298,21 @@ async function replaceWithCopy(sourcePath, targetPath) {
297
298
  await fsp.cp(sourcePath, targetPath, { recursive: true, force: true });
298
299
  }
299
300
  async function replaceWithSymlink(sourcePath, targetPath) {
301
+ const adapter = createPlatformAdapter();
300
302
  await fsp.rm(targetPath, { recursive: true, force: true });
301
303
  await ensureDirectory(path.dirname(targetPath));
302
- await fsp.symlink(sourcePath, targetPath, process.platform === 'win32' ? 'junction' : 'dir');
304
+ try {
305
+ await fsp.symlink(sourcePath, targetPath, adapter.symlinkType());
306
+ }
307
+ catch (err) {
308
+ if (err.code === 'EPERM') {
309
+ process.stderr.write(`Warning: Symlink not supported (EPERM). Falling back to copy mode.${adapter.EOL}`);
310
+ await replaceWithCopy(sourcePath, targetPath);
311
+ }
312
+ else {
313
+ throw err;
314
+ }
315
+ }
303
316
  }
304
317
  export async function installLinks({ toolkitHome, modes, env = process.env, previousSkillNames = [], linkMode = 'copy', includeExclusiveSkills = false }) {
305
318
  const normalizedModes = normalizeModes(modes);
@@ -0,0 +1,15 @@
1
+ import type { ParsedArguments } from '../types.js';
2
+ import type { CommandParser, InstallCommand } from './types.js';
3
+ /**
4
+ * Parser for the install (default) command mode.
5
+ *
6
+ * Recognises:
7
+ * - Positional args: install keyword, mode names (codex, openclaw, ...)
8
+ * - --help / -h
9
+ * - --home <path>
10
+ * - --symlink, --copy
11
+ */
12
+ export declare class InstallArgsParser implements CommandParser<InstallCommand> {
13
+ parse(argv: string[]): InstallCommand;
14
+ toParsedArguments(result: InstallCommand): ParsedArguments;
15
+ }
@@ -0,0 +1,87 @@
1
+ import { parseArgs } from 'node:util';
2
+ import path from 'node:path';
3
+ import { UserInputError } from '../../../tool-utils/dist/index.js';
4
+ import { normalizeParseError } from './parser-utils.js';
5
+ /**
6
+ * Parser for the install (default) command mode.
7
+ *
8
+ * Recognises:
9
+ * - Positional args: install keyword, mode names (codex, openclaw, ...)
10
+ * - --help / -h
11
+ * - --home <path>
12
+ * - --symlink, --copy
13
+ */
14
+ export class InstallArgsParser {
15
+ parse(argv) {
16
+ let showHelp = false;
17
+ let toolkitHome = null;
18
+ let linkMode = null;
19
+ let explicitInstallCommand = false;
20
+ const modes = [];
21
+ try {
22
+ const { values, positionals } = parseArgs({
23
+ args: argv,
24
+ allowPositionals: true,
25
+ options: {
26
+ help: { type: 'boolean', short: 'h' },
27
+ home: { type: 'string' },
28
+ symlink: { type: 'boolean' },
29
+ copy: { type: 'boolean' },
30
+ },
31
+ });
32
+ showHelp = values.help ?? false;
33
+ if (values.home) {
34
+ toolkitHome = path.resolve(values.home);
35
+ }
36
+ if (values.symlink) {
37
+ linkMode = 'symlink';
38
+ }
39
+ if (values.copy) {
40
+ linkMode = 'copy';
41
+ }
42
+ if (values.symlink && values.copy) {
43
+ throw new UserInputError('Cannot use both --symlink and --copy');
44
+ }
45
+ for (const pos of positionals) {
46
+ if (pos === 'install') {
47
+ explicitInstallCommand = true;
48
+ }
49
+ else {
50
+ modes.push(pos);
51
+ }
52
+ }
53
+ }
54
+ catch (err) {
55
+ normalizeParseError(err);
56
+ }
57
+ const helpTopic = showHelp
58
+ ? (explicitInstallCommand || modes.length > 0 || linkMode !== null || toolkitHome !== null)
59
+ ? 'install'
60
+ : 'overview'
61
+ : 'overview';
62
+ return {
63
+ command: 'install',
64
+ modes: modes,
65
+ showHelp,
66
+ toolkitHome,
67
+ linkMode,
68
+ explicitInstallCommand,
69
+ helpTopic,
70
+ };
71
+ }
72
+ toParsedArguments(result) {
73
+ return {
74
+ command: 'install',
75
+ modes: result.modes,
76
+ showHelp: result.showHelp,
77
+ showToolsHelp: false,
78
+ toolkitHome: result.toolkitHome,
79
+ toolName: null,
80
+ toolArgs: [],
81
+ linkMode: result.linkMode,
82
+ assumeYes: false,
83
+ explicitInstallCommand: result.explicitInstallCommand,
84
+ helpTopic: result.helpTopic,
85
+ };
86
+ }
87
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Normalises common parseArgs errors into user-facing UserInputErrors.
3
+ *
4
+ * Currently handles:
5
+ * - --home without a value
6
+ *
7
+ * Call this inside a catch block to normalise the error before propagating it.
8
+ */
9
+ export declare function normalizeParseError(err: unknown): never;
@@ -0,0 +1,16 @@
1
+ import { UserInputError } from '../../../tool-utils/dist/index.js';
2
+ /**
3
+ * Normalises common parseArgs errors into user-facing UserInputErrors.
4
+ *
5
+ * Currently handles:
6
+ * - --home without a value
7
+ *
8
+ * Call this inside a catch block to normalise the error before propagating it.
9
+ */
10
+ export function normalizeParseError(err) {
11
+ const message = err.message;
12
+ if (message.includes('--home') && (message.includes('argument missing') || message.includes('value') || message.includes('ambiguous'))) {
13
+ throw new UserInputError('Missing value for --home');
14
+ }
15
+ throw err;
16
+ }
@@ -0,0 +1,16 @@
1
+ import type { ParsedArguments } from '../types.js';
2
+ import type { CommandParser, ToolCommand, ToolsHelpCommand } from './types.js';
3
+ /**
4
+ * Parser for tool-invocation modes.
5
+ *
6
+ * Two entry paths:
7
+ * 1. `tools` / `tool` prefix – argv[0] is 'tools' or 'tool'
8
+ * 2. Direct tool name – argv[0] is a known tool name (detected by caller)
9
+ *
10
+ * Returns a ToolsHelpCommand when no tool is named or --help is passed,
11
+ * otherwise returns a ToolCommand with the tool name and remaining args.
12
+ */
13
+ export declare class ToolArgsParser implements CommandParser<ToolCommand | ToolsHelpCommand> {
14
+ parse(argv: string[]): ToolCommand | ToolsHelpCommand;
15
+ toParsedArguments(result: ToolCommand | ToolsHelpCommand): ParsedArguments;
16
+ }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Parser for tool-invocation modes.
3
+ *
4
+ * Two entry paths:
5
+ * 1. `tools` / `tool` prefix – argv[0] is 'tools' or 'tool'
6
+ * 2. Direct tool name – argv[0] is a known tool name (detected by caller)
7
+ *
8
+ * Returns a ToolsHelpCommand when no tool is named or --help is passed,
9
+ * otherwise returns a ToolCommand with the tool name and remaining args.
10
+ */
11
+ export class ToolArgsParser {
12
+ parse(argv) {
13
+ const args = [...argv];
14
+ // Strip leading 'tools'/'tool' prefix if present
15
+ const hasToolsPrefix = args[0] === 'tools' || args[0] === 'tool';
16
+ if (hasToolsPrefix) {
17
+ args.shift();
18
+ }
19
+ if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
20
+ return { command: 'tools-help', showToolsHelp: true };
21
+ }
22
+ return {
23
+ command: 'tool',
24
+ toolName: args.shift() ?? null,
25
+ toolArgs: args,
26
+ };
27
+ }
28
+ toParsedArguments(result) {
29
+ if (result.command === 'tools-help') {
30
+ return {
31
+ command: 'tools-help',
32
+ modes: [],
33
+ showHelp: false,
34
+ showToolsHelp: true,
35
+ toolkitHome: null,
36
+ toolName: null,
37
+ toolArgs: [],
38
+ linkMode: null,
39
+ assumeYes: false,
40
+ explicitInstallCommand: false,
41
+ helpTopic: 'tools-help',
42
+ };
43
+ }
44
+ return {
45
+ command: 'tool',
46
+ modes: [],
47
+ showHelp: false,
48
+ showToolsHelp: false,
49
+ toolkitHome: null,
50
+ toolName: result.toolName,
51
+ toolArgs: result.toolArgs,
52
+ linkMode: null,
53
+ assumeYes: false,
54
+ explicitInstallCommand: false,
55
+ helpTopic: 'overview',
56
+ };
57
+ }
58
+ }