@laitszkin/apollo-toolkit 3.13.2 → 3.14.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.
- package/AGENTS.md +7 -7
- package/CHANGELOG.md +36 -0
- package/CLAUDE.md +8 -8
- package/analyse-app-logs/SKILL.md +3 -3
- package/bin/apollo-toolkit.ts +7 -0
- package/codex/codex-memory-manager/SKILL.md +2 -2
- package/codex/learn-skill-from-conversations/SKILL.md +3 -3
- package/dist/bin/apollo-toolkit.d.ts +2 -0
- package/dist/bin/apollo-toolkit.js +7 -0
- package/dist/lib/cli.d.ts +41 -0
- package/dist/lib/cli.js +655 -0
- package/dist/lib/installer.d.ts +59 -0
- package/dist/lib/installer.js +404 -0
- package/dist/lib/tool-runner.d.ts +19 -0
- package/dist/lib/tool-runner.js +536 -0
- package/dist/lib/tools/architecture.d.ts +2 -0
- package/dist/lib/tools/architecture.js +23 -0
- package/dist/lib/tools/create-specs.d.ts +2 -0
- package/dist/lib/tools/create-specs.js +175 -0
- package/dist/lib/tools/docs-to-voice.d.ts +2 -0
- package/dist/lib/tools/docs-to-voice.js +705 -0
- package/dist/lib/tools/enforce-video-aspect-ratio.d.ts +2 -0
- package/dist/lib/tools/enforce-video-aspect-ratio.js +312 -0
- package/dist/lib/tools/extract-conversations.d.ts +2 -0
- package/dist/lib/tools/extract-conversations.js +105 -0
- package/dist/lib/tools/extract-pdf-text.d.ts +2 -0
- package/dist/lib/tools/extract-pdf-text.js +92 -0
- package/dist/lib/tools/filter-logs.d.ts +2 -0
- package/dist/lib/tools/filter-logs.js +94 -0
- package/dist/lib/tools/find-github-issues.d.ts +2 -0
- package/dist/lib/tools/find-github-issues.js +176 -0
- package/dist/lib/tools/generate-storyboard-images.d.ts +2 -0
- package/dist/lib/tools/generate-storyboard-images.js +419 -0
- package/dist/lib/tools/log-cli-utils.d.ts +35 -0
- package/dist/lib/tools/log-cli-utils.js +233 -0
- package/dist/lib/tools/open-github-issue.d.ts +2 -0
- package/dist/lib/tools/open-github-issue.js +750 -0
- package/dist/lib/tools/read-github-issue.d.ts +2 -0
- package/dist/lib/tools/read-github-issue.js +134 -0
- package/dist/lib/tools/render-error-book.d.ts +2 -0
- package/dist/lib/tools/render-error-book.js +265 -0
- package/dist/lib/tools/render-katex.d.ts +2 -0
- package/dist/lib/tools/render-katex.js +294 -0
- package/dist/lib/tools/review-threads.d.ts +2 -0
- package/dist/lib/tools/review-threads.js +491 -0
- package/dist/lib/tools/search-logs.d.ts +2 -0
- package/dist/lib/tools/search-logs.js +164 -0
- package/dist/lib/tools/sync-memory-index.d.ts +2 -0
- package/dist/lib/tools/sync-memory-index.js +113 -0
- package/dist/lib/tools/validate-openai-agent-config.d.ts +2 -0
- package/dist/lib/tools/validate-openai-agent-config.js +190 -0
- package/dist/lib/tools/validate-skill-frontmatter.d.ts +2 -0
- package/dist/lib/tools/validate-skill-frontmatter.js +118 -0
- package/dist/lib/types.d.ts +82 -0
- package/dist/lib/types.js +2 -0
- package/dist/lib/updater.d.ts +34 -0
- package/dist/lib/updater.js +112 -0
- package/dist/lib/utils/format.d.ts +2 -0
- package/dist/lib/utils/format.js +6 -0
- package/dist/lib/utils/terminal.d.ts +12 -0
- package/dist/lib/utils/terminal.js +26 -0
- package/docs-to-voice/SKILL.md +0 -1
- package/generate-spec/SKILL.md +1 -1
- package/katex/SKILL.md +1 -2
- package/lib/cli.ts +780 -0
- package/lib/installer.ts +466 -0
- package/lib/tool-runner.ts +561 -0
- package/lib/tools/architecture.ts +20 -0
- package/lib/tools/create-specs.ts +204 -0
- package/lib/tools/docs-to-voice.ts +799 -0
- package/lib/tools/enforce-video-aspect-ratio.ts +368 -0
- package/lib/tools/extract-conversations.ts +114 -0
- package/lib/tools/extract-pdf-text.ts +99 -0
- package/lib/tools/filter-logs.ts +118 -0
- package/lib/tools/find-github-issues.ts +211 -0
- package/lib/tools/generate-storyboard-images.ts +455 -0
- package/lib/tools/log-cli-utils.ts +262 -0
- package/lib/tools/open-github-issue.ts +930 -0
- package/lib/tools/read-github-issue.ts +179 -0
- package/lib/tools/render-error-book.ts +300 -0
- package/lib/tools/render-katex.ts +325 -0
- package/lib/tools/review-threads.ts +590 -0
- package/lib/tools/search-logs.ts +200 -0
- package/lib/tools/sync-memory-index.ts +114 -0
- package/lib/tools/validate-openai-agent-config.ts +213 -0
- package/lib/tools/validate-skill-frontmatter.ts +124 -0
- package/lib/types.ts +90 -0
- package/lib/updater.ts +165 -0
- package/lib/utils/format.ts +7 -0
- package/lib/utils/terminal.ts +22 -0
- package/open-github-issue/SKILL.md +2 -2
- package/optimise-skill/SKILL.md +1 -1
- package/package.json +13 -4
- package/resources/project-architecture/assets/architecture.css +764 -0
- package/resources/project-architecture/assets/viewer.client.js +144 -0
- package/resources/project-architecture/index.html +42 -0
- package/review-spec-related-changes/SKILL.md +1 -1
- package/solve-issues-found-during-review/SKILL.md +2 -1
- package/tsconfig.json +28 -0
- package/analyse-app-logs/scripts/__pycache__/filter_logs_by_time.cpython-312.pyc +0 -0
- package/analyse-app-logs/scripts/__pycache__/log_cli_utils.cpython-312.pyc +0 -0
- package/analyse-app-logs/scripts/__pycache__/search_logs.cpython-312.pyc +0 -0
- package/analyse-app-logs/scripts/filter_logs_by_time.py +0 -64
- package/analyse-app-logs/scripts/log_cli_utils.py +0 -112
- package/analyse-app-logs/scripts/search_logs.py +0 -137
- package/analyse-app-logs/tests/test_filter_logs_by_time.py +0 -95
- package/analyse-app-logs/tests/test_search_logs.py +0 -100
- package/codex/codex-memory-manager/scripts/extract_recent_conversations.py +0 -369
- package/codex/codex-memory-manager/scripts/sync_memory_index.py +0 -130
- package/codex/codex-memory-manager/tests/test_extract_recent_conversations.py +0 -177
- package/codex/codex-memory-manager/tests/test_memory_template.py +0 -37
- package/codex/codex-memory-manager/tests/test_sync_memory_index.py +0 -84
- package/codex/learn-skill-from-conversations/scripts/extract_recent_conversations.py +0 -369
- package/codex/learn-skill-from-conversations/tests/test_extract_recent_conversations.py +0 -177
- package/docs-to-voice/scripts/__pycache__/docs_to_voice.cpython-312.pyc +0 -0
- package/docs-to-voice/scripts/docs_to_voice.py +0 -1385
- package/docs-to-voice/scripts/docs_to_voice.sh +0 -11
- package/docs-to-voice/tests/test_docs_to_voice_api_max_chars.py +0 -210
- package/docs-to-voice/tests/test_docs_to_voice_sentence_timeline.py +0 -115
- package/docs-to-voice/tests/test_docs_to_voice_settings.py +0 -43
- package/docs-to-voice/tests/test_docs_to_voice_shell_wrapper.py +0 -51
- package/docs-to-voice/tests/test_docs_to_voice_speech_rate.py +0 -57
- package/generate-spec/scripts/__pycache__/create-specscpython-312.pyc +0 -0
- package/generate-spec/scripts/create-specs +0 -215
- package/generate-spec/tests/test_create_specs.py +0 -200
- package/init-project-html/scripts/architecture-bootstrap-render.js +0 -16
- package/init-project-html/scripts/architecture.js +0 -296
- package/katex/scripts/__pycache__/render_katex.cpython-312.pyc +0 -0
- package/katex/scripts/render_katex.py +0 -247
- package/katex/scripts/render_katex.sh +0 -11
- package/katex/tests/test_render_katex.py +0 -174
- package/learning-error-book/scripts/render_error_book_json_to_pdf.py +0 -590
- package/learning-error-book/tests/test_render_error_book_json_to_pdf.py +0 -134
- package/open-github-issue/scripts/__pycache__/open_github_issue.cpython-312.pyc +0 -0
- package/open-github-issue/scripts/open_github_issue.py +0 -705
- package/open-github-issue/tests/test_open_github_issue.py +0 -381
- package/openai-text-to-image-storyboard/scripts/generate_storyboard_images.py +0 -763
- package/openai-text-to-image-storyboard/tests/test_generate_storyboard_images.py +0 -177
- package/read-github-issue/scripts/__pycache__/find_issues.cpython-312.pyc +0 -0
- package/read-github-issue/scripts/__pycache__/read_issue.cpython-312.pyc +0 -0
- package/read-github-issue/scripts/find_issues.py +0 -148
- package/read-github-issue/scripts/read_issue.py +0 -108
- package/read-github-issue/tests/test_find_issues.py +0 -127
- package/read-github-issue/tests/test_read_issue.py +0 -109
- package/resolve-review-comments/scripts/__pycache__/review_threads.cpython-312.pyc +0 -0
- package/resolve-review-comments/scripts/review_threads.py +0 -425
- package/resolve-review-comments/tests/test_review_threads.py +0 -74
- package/scripts/validate_openai_agent_config.py +0 -209
- package/scripts/validate_skill_frontmatter.py +0 -131
- package/text-to-short-video/scripts/__pycache__/enforce_video_aspect_ratio.cpython-312.pyc +0 -0
- package/text-to-short-video/scripts/enforce_video_aspect_ratio.py +0 -350
- package/text-to-short-video/tests/test_enforce_video_aspect_ratio.py +0 -194
- package/weekly-financial-event-report/scripts/extract_pdf_text_pdfkit.swift +0 -99
- package/weekly-financial-event-report/tests/test_extract_pdf_text_pdfkit.py +0 -64
package/lib/cli.ts
ADDED
|
@@ -0,0 +1,780 @@
|
|
|
1
|
+
import { createInterface } from 'node:readline/promises';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { color, clearScreen, sleep, supportsAnimation, supportsColor } from './utils/terminal';
|
|
5
|
+
import { formatToolList, buildToolDiscoveryHelp, runTool, getToolCommand } from './tool-runner';
|
|
6
|
+
import { formatExamples } from './utils/format';
|
|
7
|
+
import {
|
|
8
|
+
TARGET_DEFINITIONS,
|
|
9
|
+
VALID_MODES,
|
|
10
|
+
installLinks,
|
|
11
|
+
listAllKnownSkillNames,
|
|
12
|
+
listCodexSkillNames,
|
|
13
|
+
normalizeModes,
|
|
14
|
+
resolveToolkitHome,
|
|
15
|
+
syncToolkitHome,
|
|
16
|
+
uninstallSkills,
|
|
17
|
+
getTargetRoots,
|
|
18
|
+
getUninstallTargetRoots,
|
|
19
|
+
} from './installer';
|
|
20
|
+
import { checkForPackageUpdate } from './updater';
|
|
21
|
+
import type { CliContext, InstallMode, ParsedArguments } from './types';
|
|
22
|
+
|
|
23
|
+
const TARGET_OPTIONS = [
|
|
24
|
+
{ id: 'all', label: 'All', description: 'Select every supported target below' },
|
|
25
|
+
...TARGET_DEFINITIONS,
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
const WORDMARK_LINES = [
|
|
29
|
+
' _ _ _ _____ _ _ _ _ ',
|
|
30
|
+
' / \\\\ _ __ ___ | | | ___ |_ _|__ ___ | | | _(_) |_ ',
|
|
31
|
+
" / _ \\\\ | '_ \\\\ / _ \\\\| | |/ _ \\\\ | |/ _ \\\\ / _ \\\\| | |/ / | __|",
|
|
32
|
+
' / ___ \\\\| |_) | (_) | | | (_) | | | (_) | (_) | | <| | |_ ',
|
|
33
|
+
'/_/ \\\\_\\\\ .__/ \\\\___/|_|_|\\\\___/ |_|\\\\___/ \\\\___/|_|_|\\\\_\\\\_|\\\\__|',
|
|
34
|
+
' |_| ',
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
function buildWordmark({ colorEnabled }: { colorEnabled: boolean }): string {
|
|
38
|
+
return WORDMARK_LINES.map((line) => color(line, '1;36', colorEnabled)).join('\n');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function buildBanner({ version, colorEnabled }: { version: string; colorEnabled: boolean }): string {
|
|
42
|
+
return [
|
|
43
|
+
buildWordmark({ colorEnabled }),
|
|
44
|
+
color('Apollo Toolkit', '1', colorEnabled),
|
|
45
|
+
color('Install curated skills for Codex, OpenClaw, Trae, Agents, and Claude Code', '2', colorEnabled),
|
|
46
|
+
color(`Version ${version}`, '1;33', colorEnabled),
|
|
47
|
+
].join('\n');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function buildModeUsagePattern(): string {
|
|
51
|
+
return `${VALID_MODES.join('|')}|all`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function buildInteractiveModeHint(): string {
|
|
55
|
+
const quotedModes = [...VALID_MODES, 'all'].map((mode) => `\`${mode}\``);
|
|
56
|
+
return `${quotedModes.slice(0, -1).join(', ')}, or ${quotedModes.at(-1)}`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function buildSupportedTargetLines({ colorEnabled }: { colorEnabled: boolean }): string {
|
|
60
|
+
const labelWidth = TARGET_DEFINITIONS.reduce((max, target) => Math.max(max, target.label.length), 0);
|
|
61
|
+
return TARGET_DEFINITIONS.map((target) => (
|
|
62
|
+
` ${color(target.label.padEnd(labelWidth, ' '), '1', colorEnabled)} ${target.description}`
|
|
63
|
+
)).join('\n');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function buildWelcomeScreen({ version, colorEnabled, stage = 4 }: { version: string; colorEnabled: boolean; stage?: number }): string {
|
|
67
|
+
const lines = [buildBanner({ version, colorEnabled })];
|
|
68
|
+
|
|
69
|
+
if (stage >= 1) {
|
|
70
|
+
lines.push(
|
|
71
|
+
'',
|
|
72
|
+
'This setup will configure:',
|
|
73
|
+
` ${color('*', '1;33', colorEnabled)} A managed Apollo Toolkit home in ${color('~/.apollo-toolkit', '1', colorEnabled)}`,
|
|
74
|
+
` ${color('*', '1;33', colorEnabled)} Copied skill folders for your selected targets`,
|
|
75
|
+
` ${color('*', '1;33', colorEnabled)} A clean install flow with target-aware replacement`,
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (stage >= 2) {
|
|
80
|
+
lines.push(
|
|
81
|
+
'',
|
|
82
|
+
'Quick start after setup:',
|
|
83
|
+
` ${color('npx @laitszkin/apollo-toolkit codex', '1;33', colorEnabled)}`,
|
|
84
|
+
` ${color('apollo-toolkit all', '1;33', colorEnabled)}`,
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (stage >= 3) {
|
|
89
|
+
lines.push(
|
|
90
|
+
'',
|
|
91
|
+
color('Supported targets:', '2', colorEnabled),
|
|
92
|
+
buildSupportedTargetLines({ colorEnabled }),
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (stage >= 4) {
|
|
97
|
+
lines.push('', color('Launching target selector...', '1;36', colorEnabled));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return lines.join('\n');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function animateWelcomeScreen({ output, version, env }: { output: NodeJS.WriteStream; version: string; env: NodeJS.ProcessEnv }): Promise<void> {
|
|
104
|
+
if (!supportsAnimation(output, env)) return;
|
|
105
|
+
|
|
106
|
+
const colorEnabled = supportsColor(output, env);
|
|
107
|
+
for (const stage of [0, 1, 2, 3, 4]) {
|
|
108
|
+
clearScreen(output);
|
|
109
|
+
output.write(`${buildWelcomeScreen({ version, colorEnabled, stage })}\n`);
|
|
110
|
+
await sleep(stage === 0 ? 120 : 160);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function buildHelpText({ version, colorEnabled }: { version: string; colorEnabled: boolean }): string {
|
|
115
|
+
const examples = [
|
|
116
|
+
{ command: 'apltk --help', result: 'Shows the top-level Apollo Toolkit guide, including install modes and bundled task-tool discovery.' },
|
|
117
|
+
{ command: 'apltk tools --help', result: 'Lists bundled tools by task so you can decide which CLI helper to inspect next.' },
|
|
118
|
+
{ command: 'apltk architecture --help', result: 'Shows the architecture atlas command tree, task guidance, and action-specific follow-up help paths.' },
|
|
119
|
+
{ command: 'apltk tools architecture --help', result: 'Shows what the architecture atlas tool is for, then prints its native command tree and examples.' },
|
|
120
|
+
{ 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.' },
|
|
121
|
+
];
|
|
122
|
+
return [
|
|
123
|
+
buildBanner({ version, colorEnabled }),
|
|
124
|
+
'',
|
|
125
|
+
'Usage:',
|
|
126
|
+
` apltk [install] [${buildModeUsagePattern()}]...`,
|
|
127
|
+
` apollo-toolkit [install] [${buildModeUsagePattern()}]...`,
|
|
128
|
+
` apltk uninstall [${buildModeUsagePattern()}]... [--yes]`,
|
|
129
|
+
' apltk tools',
|
|
130
|
+
' apltk <tool> [...args]',
|
|
131
|
+
' apltk tools <tool> [...args]',
|
|
132
|
+
' apltk --help',
|
|
133
|
+
' apollo-toolkit --help',
|
|
134
|
+
'',
|
|
135
|
+
'Common goals:',
|
|
136
|
+
' - Install or refresh skills in one or more agent targets: `apltk install --help`',
|
|
137
|
+
' - Remove manifest-tracked installs from selected targets: `apltk uninstall --help`',
|
|
138
|
+
' - Discover which bundled helper tool matches a task: `apltk tools --help`',
|
|
139
|
+
' - Inspect one tool deeply before running it: `apltk tools <tool> --help`',
|
|
140
|
+
'',
|
|
141
|
+
'Bundled tools:',
|
|
142
|
+
formatToolList(),
|
|
143
|
+
'',
|
|
144
|
+
buildToolDiscoveryHelp(),
|
|
145
|
+
'',
|
|
146
|
+
'Options:',
|
|
147
|
+
' --home <path> Override Apollo Toolkit home directory',
|
|
148
|
+
' --symlink Install skills as symlinks instead of copied directories',
|
|
149
|
+
' --copy Install skills as copied directories instead of symlinks',
|
|
150
|
+
' --yes, -y Skip uninstall confirmation',
|
|
151
|
+
' --help Show this help text',
|
|
152
|
+
'',
|
|
153
|
+
'Examples:',
|
|
154
|
+
formatExamples(examples),
|
|
155
|
+
].join('\n');
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function buildToolsHelp({ version, colorEnabled }: { version: string; colorEnabled: boolean }): string {
|
|
159
|
+
const examples = [
|
|
160
|
+
{ command: 'apltk tools', result: 'Lists all bundled tools plus the task-oriented discovery guide.' },
|
|
161
|
+
{ command: 'apltk tools open-github-issue --help', result: 'Shows when to use the GitHub issue publisher, then prints its exact script flags and examples.' },
|
|
162
|
+
{ command: 'apltk tools architecture --help', result: 'Shows when to use the architecture atlas CLI, then prints its native command tree.' },
|
|
163
|
+
];
|
|
164
|
+
return [
|
|
165
|
+
buildBanner({ version, colorEnabled }),
|
|
166
|
+
'',
|
|
167
|
+
'Usage:',
|
|
168
|
+
' apltk tools',
|
|
169
|
+
' apltk <tool> [...args]',
|
|
170
|
+
' apltk tools <tool> [...args]',
|
|
171
|
+
'',
|
|
172
|
+
buildToolDiscoveryHelp(),
|
|
173
|
+
'',
|
|
174
|
+
'Bundled tools:',
|
|
175
|
+
formatToolList(),
|
|
176
|
+
'',
|
|
177
|
+
'Tip:',
|
|
178
|
+
' Pass `--help` after a tool name to view task guidance, native script flags, and concrete examples.',
|
|
179
|
+
'',
|
|
180
|
+
'Examples:',
|
|
181
|
+
formatExamples(examples),
|
|
182
|
+
].join('\n');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function buildInstallHelpText({ version, colorEnabled }: { version: string; colorEnabled: boolean }): string {
|
|
186
|
+
const examples = [
|
|
187
|
+
{ command: 'apltk', result: 'Launches the interactive installer, opens the target selector, and then walks through link-mode confirmation.' },
|
|
188
|
+
{ command: 'apltk codex openclaw --symlink', result: 'Performs a non-interactive install into `codex` and `openclaw` targets using symlinks.' },
|
|
189
|
+
{ command: 'npx @laitszkin/apollo-toolkit all --copy', result: 'Installs a copied snapshot into every supported target instead of symlinking.' },
|
|
190
|
+
];
|
|
191
|
+
return [
|
|
192
|
+
buildBanner({ version, colorEnabled }),
|
|
193
|
+
'',
|
|
194
|
+
'Usage:',
|
|
195
|
+
` apltk [install] [${buildModeUsagePattern()}]...`,
|
|
196
|
+
` apollo-toolkit [install] [${buildModeUsagePattern()}]...`,
|
|
197
|
+
'',
|
|
198
|
+
'Use this when:',
|
|
199
|
+
' - You want to install or refresh Apollo Toolkit skills in one or more agent targets.',
|
|
200
|
+
' - You need to choose between symlink mode (auto-updating) and copy mode (stable snapshot).',
|
|
201
|
+
'',
|
|
202
|
+
'Supported targets:',
|
|
203
|
+
buildSupportedTargetLines({ colorEnabled }),
|
|
204
|
+
'',
|
|
205
|
+
'Behavior notes:',
|
|
206
|
+
' - Running `apltk` with no targets opens the interactive installer and target selector.',
|
|
207
|
+
' - `--symlink` keeps installed skills connected to the managed toolkit checkout in `~/.apollo-toolkit`.',
|
|
208
|
+
' - `--copy` installs a snapshot that only changes when you run the installer again.',
|
|
209
|
+
' - The installer can optionally include codex-exclusive skills in non-codex targets after prompting.',
|
|
210
|
+
'',
|
|
211
|
+
'Options:',
|
|
212
|
+
' --home <path> Override Apollo Toolkit home directory',
|
|
213
|
+
' --symlink Install skills as symlinks (recommended)',
|
|
214
|
+
' --copy Install skills as copied directories',
|
|
215
|
+
' --help Show this install help',
|
|
216
|
+
'',
|
|
217
|
+
'Examples:',
|
|
218
|
+
formatExamples(examples),
|
|
219
|
+
].join('\n');
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function buildUninstallHelpText({ version, colorEnabled }: { version: string; colorEnabled: boolean }): string {
|
|
223
|
+
const examples = [
|
|
224
|
+
{ command: 'apltk uninstall', result: 'Opens the interactive uninstall selector when running in a TTY and then asks for confirmation before removal.' },
|
|
225
|
+
{ command: 'apltk uninstall codex agents --yes', result: 'Removes Apollo Toolkit-managed installs from `codex` and `agents` without another confirmation prompt.' },
|
|
226
|
+
{ command: 'apltk uninstall codex --home /tmp/custom-home', result: 'Uses the custom managed toolkit home while removing manifest-tracked installs from the selected target.' },
|
|
227
|
+
];
|
|
228
|
+
return [
|
|
229
|
+
buildBanner({ version, colorEnabled }),
|
|
230
|
+
'',
|
|
231
|
+
'Usage:',
|
|
232
|
+
` apltk uninstall [${buildModeUsagePattern()}]... [--yes]`,
|
|
233
|
+
'',
|
|
234
|
+
'Use this when:',
|
|
235
|
+
' - You want to remove Apollo Toolkit-managed skills from one or more agent targets.',
|
|
236
|
+
' - You need to clean up manifest-tracked historical installs as well as the current installed skills.',
|
|
237
|
+
'',
|
|
238
|
+
'Supported targets:',
|
|
239
|
+
buildSupportedTargetLines({ colorEnabled }),
|
|
240
|
+
'',
|
|
241
|
+
'Behavior notes:',
|
|
242
|
+
' - With no explicit targets, uninstall opens the interactive selector in a TTY and otherwise falls back to all targets.',
|
|
243
|
+
' - Uninstall removes manifest-tracked current and historical Apollo Toolkit skill directories.',
|
|
244
|
+
' - `--yes` skips the confirmation prompt after the target list is resolved.',
|
|
245
|
+
'',
|
|
246
|
+
'Options:',
|
|
247
|
+
' --home <path> Override Apollo Toolkit home directory',
|
|
248
|
+
' --yes, -y Skip uninstall confirmation',
|
|
249
|
+
' --help Show this uninstall help',
|
|
250
|
+
'',
|
|
251
|
+
'Examples:',
|
|
252
|
+
formatExamples(examples),
|
|
253
|
+
].join('\n');
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function readPackageJson(sourceRoot: string): { version: string; name: string } {
|
|
257
|
+
return JSON.parse(fs.readFileSync(path.join(sourceRoot, 'package.json'), 'utf8'));
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function parseArguments(argv: string[]): ParsedArguments {
|
|
261
|
+
const args = [...argv];
|
|
262
|
+
const result: ParsedArguments = {
|
|
263
|
+
command: 'install',
|
|
264
|
+
modes: [],
|
|
265
|
+
showHelp: false,
|
|
266
|
+
showToolsHelp: false,
|
|
267
|
+
toolkitHome: null,
|
|
268
|
+
toolName: null,
|
|
269
|
+
toolArgs: [],
|
|
270
|
+
linkMode: null,
|
|
271
|
+
assumeYes: false,
|
|
272
|
+
explicitInstallCommand: false,
|
|
273
|
+
helpTopic: 'overview',
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
if (args[0] === 'uninstall') {
|
|
277
|
+
result.command = 'uninstall';
|
|
278
|
+
args.shift();
|
|
279
|
+
while (args.length > 0) {
|
|
280
|
+
const arg = args.shift()!;
|
|
281
|
+
if (arg === '--help' || arg === '-h') {
|
|
282
|
+
result.showHelp = true;
|
|
283
|
+
} else if (arg === '--yes' || arg === '-y') {
|
|
284
|
+
result.assumeYes = true;
|
|
285
|
+
} else if (arg === '--home') {
|
|
286
|
+
const toolkitHome = args.shift();
|
|
287
|
+
if (!toolkitHome) throw new Error('Missing value for --home');
|
|
288
|
+
result.toolkitHome = path.resolve(toolkitHome);
|
|
289
|
+
} else {
|
|
290
|
+
result.modes.push(arg as InstallMode);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
if (result.showHelp) result.helpTopic = 'uninstall';
|
|
294
|
+
return result;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (args[0] === 'tools' || args[0] === 'tool') {
|
|
298
|
+
args.shift();
|
|
299
|
+
const nextArg: string | undefined = args[0];
|
|
300
|
+
if (args.length === 0 || nextArg === '--help' || nextArg === '-h') {
|
|
301
|
+
result.command = 'tools-help';
|
|
302
|
+
result.showToolsHelp = true;
|
|
303
|
+
return result;
|
|
304
|
+
}
|
|
305
|
+
result.command = 'tool';
|
|
306
|
+
result.toolName = args.shift() || null;
|
|
307
|
+
result.toolArgs = args;
|
|
308
|
+
return result;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const firstArg = args[0];
|
|
312
|
+
if (firstArg && getToolCommand(firstArg)) {
|
|
313
|
+
result.command = 'tool';
|
|
314
|
+
result.toolName = args.shift() || null;
|
|
315
|
+
result.toolArgs = args;
|
|
316
|
+
return result;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
while (args.length > 0) {
|
|
320
|
+
const arg = args.shift()!;
|
|
321
|
+
if (arg === '--help' || arg === '-h') {
|
|
322
|
+
result.showHelp = true;
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
if (arg === '--home') {
|
|
326
|
+
const toolkitHome = args.shift();
|
|
327
|
+
if (!toolkitHome) throw new Error('Missing value for --home');
|
|
328
|
+
result.toolkitHome = path.resolve(toolkitHome);
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
if (arg === '--symlink') {
|
|
332
|
+
result.linkMode = 'symlink';
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
if (arg === '--copy') {
|
|
336
|
+
result.linkMode = 'copy';
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
if (arg === 'install') {
|
|
340
|
+
result.explicitInstallCommand = true;
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
result.modes.push(arg as InstallMode);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
if (result.showHelp) {
|
|
347
|
+
const installContextRequested = result.explicitInstallCommand
|
|
348
|
+
|| result.modes.length > 0
|
|
349
|
+
|| result.linkMode !== null
|
|
350
|
+
|| result.toolkitHome !== null;
|
|
351
|
+
result.helpTopic = installContextRequested ? 'install' : 'overview';
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return result;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function renderSelectionScreen({ output, version, cursor, selected, message, env, intro = 'Choose where Apollo Toolkit should copy managed skills.' }: {
|
|
358
|
+
output: NodeJS.WriteStream;
|
|
359
|
+
version: string;
|
|
360
|
+
cursor: number;
|
|
361
|
+
selected: Set<string>;
|
|
362
|
+
message: string;
|
|
363
|
+
env: NodeJS.ProcessEnv;
|
|
364
|
+
intro?: string;
|
|
365
|
+
}): void {
|
|
366
|
+
const colorEnabled = supportsColor(output, env);
|
|
367
|
+
const allSelected = VALID_MODES.every((mode) => selected.has(mode));
|
|
368
|
+
|
|
369
|
+
clearScreen(output);
|
|
370
|
+
output.write(`${buildBanner({ version, colorEnabled })}\n\n`);
|
|
371
|
+
output.write(`${intro}\n`);
|
|
372
|
+
output.write(`${color('Use Up/Down', '1;33', colorEnabled)} (or ${color('j/k', '1;33', colorEnabled)}) to move, ${color('Space', '1;33', colorEnabled)} to toggle, ${color('Enter', '1;33', colorEnabled)} to continue.\n`);
|
|
373
|
+
output.write(`Press ${color('a', '1;33', colorEnabled)} to toggle all, ${color('q', '1;33', colorEnabled)} to cancel.\n\n`);
|
|
374
|
+
|
|
375
|
+
TARGET_OPTIONS.forEach((option, index) => {
|
|
376
|
+
const isFocused = index === cursor;
|
|
377
|
+
const isChecked = option.id === 'all' ? allSelected : selected.has(option.id);
|
|
378
|
+
const prefix = isFocused ? color('>', '1;33', colorEnabled) : ' ';
|
|
379
|
+
const checkbox = isChecked ? color('[x]', '1;32', colorEnabled) : '[ ]';
|
|
380
|
+
const label = isFocused ? color(option.label, '1', colorEnabled) : option.label;
|
|
381
|
+
output.write(`${prefix} ${checkbox} ${label} ${color(option.description, '2', colorEnabled)}\n`);
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
const selectedModes = allSelected ? [...VALID_MODES] : [...selected].sort();
|
|
385
|
+
output.write('\n');
|
|
386
|
+
output.write(`Selected: ${selectedModes.length > 0 ? selectedModes.join(', ') : 'none'}\n`);
|
|
387
|
+
if (message) output.write(`${color(message, '1;31', colorEnabled)}\n`);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
async function promptForSelectableModes({ stdin, stdout, version, env, intro, ttyError, cancelMessage }: {
|
|
391
|
+
stdin: NodeJS.ReadStream;
|
|
392
|
+
stdout: NodeJS.WriteStream;
|
|
393
|
+
version: string;
|
|
394
|
+
env: NodeJS.ProcessEnv;
|
|
395
|
+
intro: string;
|
|
396
|
+
ttyError: string;
|
|
397
|
+
cancelMessage: string;
|
|
398
|
+
}): Promise<string[]> {
|
|
399
|
+
if (!stdin.isTTY || !stdout.isTTY) throw new Error(ttyError);
|
|
400
|
+
|
|
401
|
+
return new Promise((resolve, reject) => {
|
|
402
|
+
let cursor = 0;
|
|
403
|
+
let message = '';
|
|
404
|
+
const selected = new Set<string>();
|
|
405
|
+
|
|
406
|
+
const cleanup = () => {
|
|
407
|
+
stdin.setRawMode(false);
|
|
408
|
+
stdin.pause();
|
|
409
|
+
stdin.removeListener('data', onData);
|
|
410
|
+
stdout.write('\n');
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
const toggleMode = (mode: string) => {
|
|
414
|
+
if (mode === 'all') {
|
|
415
|
+
const shouldSelectAll = !VALID_MODES.every((candidate) => selected.has(candidate));
|
|
416
|
+
selected.clear();
|
|
417
|
+
if (shouldSelectAll) VALID_MODES.forEach((candidate) => selected.add(candidate));
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
if (selected.has(mode)) selected.delete(mode);
|
|
421
|
+
else selected.add(mode);
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
const onData = (chunk: Buffer) => {
|
|
425
|
+
const value = chunk.toString('utf8');
|
|
426
|
+
if (value === '\x03') { cleanup(); reject(new Error(cancelMessage)); return; }
|
|
427
|
+
if (value === '\x1b[A' || value === 'k') {
|
|
428
|
+
cursor = (cursor - 1 + TARGET_OPTIONS.length) % TARGET_OPTIONS.length;
|
|
429
|
+
message = '';
|
|
430
|
+
renderSelectionScreen({ output: stdout, version, cursor, selected, message, env, intro });
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
if (value === '\x1b[B' || value === 'j') {
|
|
434
|
+
cursor = (cursor + 1) % TARGET_OPTIONS.length;
|
|
435
|
+
message = '';
|
|
436
|
+
renderSelectionScreen({ output: stdout, version, cursor, selected, message, env, intro });
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
if (value === ' ') {
|
|
440
|
+
toggleMode(TARGET_OPTIONS[cursor].id);
|
|
441
|
+
message = '';
|
|
442
|
+
renderSelectionScreen({ output: stdout, version, cursor, selected, message, env, intro });
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
if (value.toLowerCase() === 'a') {
|
|
446
|
+
toggleMode('all');
|
|
447
|
+
message = '';
|
|
448
|
+
renderSelectionScreen({ output: stdout, version, cursor, selected, message, env, intro });
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
if (value.toLowerCase() === 'q' || value === '\x1b') {
|
|
452
|
+
cleanup();
|
|
453
|
+
reject(new Error(cancelMessage));
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
if (value === '\r') {
|
|
457
|
+
if (selected.size === 0) {
|
|
458
|
+
message = 'Select at least one target before continuing.';
|
|
459
|
+
renderSelectionScreen({ output: stdout, version, cursor, selected, message, env, intro });
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
cleanup();
|
|
463
|
+
resolve([...selected]);
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
stdin.setRawMode(true);
|
|
468
|
+
stdin.resume();
|
|
469
|
+
stdin.on('data', onData);
|
|
470
|
+
renderSelectionScreen({ output: stdout, version, cursor, selected, message, env, intro });
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
async function promptForModes({ stdin, stdout, version, env }: { stdin: NodeJS.ReadStream; stdout: NodeJS.WriteStream; version: string; env: NodeJS.ProcessEnv }): Promise<string[]> {
|
|
475
|
+
await animateWelcomeScreen({ output: stdout, version, env });
|
|
476
|
+
return promptForSelectableModes({
|
|
477
|
+
stdin,
|
|
478
|
+
stdout,
|
|
479
|
+
version,
|
|
480
|
+
env,
|
|
481
|
+
intro: 'Choose where Apollo Toolkit should copy managed skills.',
|
|
482
|
+
ttyError: `Interactive install requires a TTY. Re-run with targets like ${buildInteractiveModeHint()}.`,
|
|
483
|
+
cancelMessage: 'Installation cancelled.',
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
async function promptForUninstallModes({ stdin, stdout, version, env }: { stdin: NodeJS.ReadStream; stdout: NodeJS.WriteStream; version: string; env: NodeJS.ProcessEnv }): Promise<string[]> {
|
|
488
|
+
return promptForSelectableModes({
|
|
489
|
+
stdin,
|
|
490
|
+
stdout,
|
|
491
|
+
version,
|
|
492
|
+
env,
|
|
493
|
+
intro: 'Choose which agent skill targets Apollo Toolkit should uninstall.',
|
|
494
|
+
ttyError: `Interactive uninstall requires a TTY. Re-run with targets like ${buildInteractiveModeHint()}.`,
|
|
495
|
+
cancelMessage: 'Uninstall cancelled.',
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
async function promptYesNo({ stdin, stdout, env, question, defaultYes = true }: {
|
|
500
|
+
stdin: NodeJS.ReadStream;
|
|
501
|
+
stdout: NodeJS.WriteStream;
|
|
502
|
+
env: NodeJS.ProcessEnv;
|
|
503
|
+
question: string;
|
|
504
|
+
defaultYes?: boolean;
|
|
505
|
+
}): Promise<boolean> {
|
|
506
|
+
if (!stdin.isTTY || !stdout.isTTY) return defaultYes;
|
|
507
|
+
|
|
508
|
+
const rl = createInterface({ input: stdin, output: stdout });
|
|
509
|
+
try {
|
|
510
|
+
const hint = defaultYes ? '[Y/n]' : '[y/N]';
|
|
511
|
+
const answer = await rl.question(`${question} ${hint} `);
|
|
512
|
+
const trimmed = answer.trim().toLowerCase();
|
|
513
|
+
if (trimmed === '') return defaultYes;
|
|
514
|
+
return trimmed === 'y' || trimmed === 'yes';
|
|
515
|
+
} finally {
|
|
516
|
+
rl.close();
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
function buildSymlinkInfo({ colorEnabled }: { colorEnabled: boolean }): string {
|
|
521
|
+
return [
|
|
522
|
+
'',
|
|
523
|
+
color('Symlink mode:', '1', colorEnabled),
|
|
524
|
+
` ${color('Pro:', '1;32', colorEnabled)} Skills auto-update when you ${color('git pull', '1;33', colorEnabled)} in ~/.apollo-toolkit`,
|
|
525
|
+
` ${color('Pro:', '1;32', colorEnabled)} No need to re-run installer after patch updates`,
|
|
526
|
+
` ${color('Con:', '1;31', colorEnabled)} Changes pushed to the repo automatically reflect in your skills -`,
|
|
527
|
+
` you may receive updates you did not intend to accept`,
|
|
528
|
+
'',
|
|
529
|
+
].join('\n');
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
async function promptSymlinkChoice({ stdin, stdout, env, colorEnabled }: {
|
|
533
|
+
stdin: NodeJS.ReadStream;
|
|
534
|
+
stdout: NodeJS.WriteStream;
|
|
535
|
+
env: NodeJS.ProcessEnv;
|
|
536
|
+
colorEnabled: boolean;
|
|
537
|
+
}): Promise<boolean> {
|
|
538
|
+
stdout.write(buildSymlinkInfo({ colorEnabled }));
|
|
539
|
+
return promptYesNo({ stdin, stdout, env, question: 'Install skills as symlinks (recommended)?', defaultYes: true });
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
async function promptIncludeExclusiveSkills({ stdin, stdout, env, colorEnabled, codexSkillNames, nonCodexModes }: {
|
|
543
|
+
stdin: NodeJS.ReadStream;
|
|
544
|
+
stdout: NodeJS.WriteStream;
|
|
545
|
+
env: NodeJS.ProcessEnv;
|
|
546
|
+
colorEnabled: boolean;
|
|
547
|
+
codexSkillNames: string[];
|
|
548
|
+
nonCodexModes: string[];
|
|
549
|
+
}): Promise<boolean> {
|
|
550
|
+
if (codexSkillNames.length === 0 || nonCodexModes.length === 0) return false;
|
|
551
|
+
|
|
552
|
+
stdout.write([
|
|
553
|
+
'',
|
|
554
|
+
color('Exclusive skills detected:', '1;33', colorEnabled),
|
|
555
|
+
` The following skills are exclusive to codex: ${codexSkillNames.join(', ')}`,
|
|
556
|
+
` Your selected non-codex targets: ${nonCodexModes.join(', ')}`,
|
|
557
|
+
'',
|
|
558
|
+
].join('\n'));
|
|
559
|
+
|
|
560
|
+
return promptYesNo({
|
|
561
|
+
stdin, stdout, env,
|
|
562
|
+
question: `Install codex-exclusive skills to ${nonCodexModes.join(', ')} as well?`,
|
|
563
|
+
defaultYes: false,
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
async function confirmInstall({ stdin, stdout, version, toolkitHome, modes, linkMode, env }: {
|
|
568
|
+
stdin: NodeJS.ReadStream;
|
|
569
|
+
stdout: NodeJS.WriteStream;
|
|
570
|
+
version: string;
|
|
571
|
+
toolkitHome: string;
|
|
572
|
+
modes: string[];
|
|
573
|
+
linkMode: string;
|
|
574
|
+
env: NodeJS.ProcessEnv;
|
|
575
|
+
}): Promise<boolean> {
|
|
576
|
+
const colorEnabled = supportsColor(stdout, env);
|
|
577
|
+
stdout.write(`${buildBanner({ version, colorEnabled })}\n\n`);
|
|
578
|
+
stdout.write(`Apollo Toolkit home: ${toolkitHome}\n`);
|
|
579
|
+
stdout.write(`Targets: ${modes.join(', ')}\n`);
|
|
580
|
+
stdout.write(`Install mode: ${linkMode === 'symlink' ? 'symlink (auto-update via git pull)' : 'copy (manual reinstall for updates)'}\n\n`);
|
|
581
|
+
|
|
582
|
+
const targets = await getTargetRoots(modes, env);
|
|
583
|
+
for (const target of targets) {
|
|
584
|
+
stdout.write(`- ${target.label}: ${target.root}\n`);
|
|
585
|
+
}
|
|
586
|
+
stdout.write('\n');
|
|
587
|
+
|
|
588
|
+
if (!stdin.isTTY || !stdout.isTTY) return true;
|
|
589
|
+
|
|
590
|
+
const rl = createInterface({ input: stdin, output: stdout });
|
|
591
|
+
try {
|
|
592
|
+
const answer = await rl.question('Install Apollo Toolkit to these targets? [Y/n] ');
|
|
593
|
+
return answer.trim() === '' || answer.trim().toLowerCase() === 'y';
|
|
594
|
+
} finally {
|
|
595
|
+
rl.close();
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
function printSummary({ stdout, version, toolkitHome, modes, installResult, env }: {
|
|
600
|
+
stdout: NodeJS.WriteStream;
|
|
601
|
+
version: string;
|
|
602
|
+
toolkitHome: string;
|
|
603
|
+
modes: string[];
|
|
604
|
+
installResult: any;
|
|
605
|
+
env: NodeJS.ProcessEnv;
|
|
606
|
+
}): void {
|
|
607
|
+
const colorEnabled = supportsColor(stdout, env);
|
|
608
|
+
stdout.write(`\n${buildBanner({ version, colorEnabled })}\n\n`);
|
|
609
|
+
stdout.write(color('Installation complete.', '1;32', colorEnabled));
|
|
610
|
+
stdout.write('\n');
|
|
611
|
+
stdout.write(`Apollo Toolkit home: ${toolkitHome}\n`);
|
|
612
|
+
stdout.write(`Installed skills: ${installResult.skillNames.length}\n`);
|
|
613
|
+
stdout.write(`Install mode: ${installResult.linkMode === 'symlink' ? 'symlink' : 'copy'}\n`);
|
|
614
|
+
stdout.write(`Targets: ${modes.join(', ')}\n\n`);
|
|
615
|
+
|
|
616
|
+
for (const target of installResult.targets) {
|
|
617
|
+
stdout.write(`- ${target.label}: ${target.root}\n`);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
function printUninstallSummary({ stdout, uninstallResult, env }: {
|
|
622
|
+
stdout: NodeJS.WriteStream;
|
|
623
|
+
uninstallResult: { target: string; root: string; removedSkills: string[] }[];
|
|
624
|
+
env: NodeJS.ProcessEnv;
|
|
625
|
+
}): void {
|
|
626
|
+
const colorEnabled = supportsColor(stdout, env);
|
|
627
|
+
|
|
628
|
+
if (uninstallResult.length === 0) {
|
|
629
|
+
stdout.write(color('No Apollo Toolkit installations found.\n', '1;33', colorEnabled));
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
stdout.write(color('Uninstall complete.', '1;32', colorEnabled));
|
|
634
|
+
stdout.write('\n\n');
|
|
635
|
+
for (const result of uninstallResult) {
|
|
636
|
+
stdout.write(`${color(result.target, '1', colorEnabled)} (${result.root})\n`);
|
|
637
|
+
stdout.write(` Removed: ${result.removedSkills.length > 0 ? result.removedSkills.join(', ') : '(manifest only)'}\n`);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
export { parseArguments, buildHelpText, buildInstallHelpText, buildUninstallHelpText, buildToolsHelp, buildBanner, buildWelcomeScreen, promptForModes, promptForUninstallModes };
|
|
642
|
+
|
|
643
|
+
export async function run(argv: string[], context: CliContext = {}): Promise<number> {
|
|
644
|
+
const sourceRoot = context.sourceRoot || path.resolve(__dirname, '../..');
|
|
645
|
+
const stdout = context.stdout || process.stdout;
|
|
646
|
+
const stderr = context.stderr || process.stderr;
|
|
647
|
+
const stdin = context.stdin || process.stdin;
|
|
648
|
+
const env = context.env || process.env;
|
|
649
|
+
let packageJson = readPackageJson(sourceRoot);
|
|
650
|
+
|
|
651
|
+
try {
|
|
652
|
+
const parsed = parseArguments(argv);
|
|
653
|
+
|
|
654
|
+
if (parsed.showHelp) {
|
|
655
|
+
const colorEnabled = supportsColor(stdout, env);
|
|
656
|
+
const helpText = parsed.helpTopic === 'install'
|
|
657
|
+
? buildInstallHelpText({ version: packageJson.version, colorEnabled })
|
|
658
|
+
: parsed.helpTopic === 'uninstall'
|
|
659
|
+
? buildUninstallHelpText({ version: packageJson.version, colorEnabled })
|
|
660
|
+
: buildHelpText({ version: packageJson.version, colorEnabled });
|
|
661
|
+
stdout.write(`${helpText}\n`);
|
|
662
|
+
return 0;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
if (parsed.showToolsHelp) {
|
|
666
|
+
stdout.write(`${buildToolsHelp({ version: packageJson.version, colorEnabled: supportsColor(stdout, env) })}\n`);
|
|
667
|
+
return 0;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
if (parsed.command === 'tool') {
|
|
671
|
+
return (context.runTool || runTool)(parsed.toolName!, parsed.toolArgs, {
|
|
672
|
+
sourceRoot, stdout, stderr, env, spawnCommand: context.spawnCommand,
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
// Uninstall flow
|
|
677
|
+
if (parsed.command === 'uninstall') {
|
|
678
|
+
const toolkitHome = parsed.toolkitHome || resolveToolkitHome(env);
|
|
679
|
+
const modes = parsed.modes.length > 0
|
|
680
|
+
? normalizeModes(parsed.modes)
|
|
681
|
+
: (stdin.isTTY && stdout.isTTY
|
|
682
|
+
? normalizeModes(await promptForUninstallModes({ stdin, stdout, version: packageJson.version, env }))
|
|
683
|
+
: null);
|
|
684
|
+
const modesForLookup = modes || [...VALID_MODES];
|
|
685
|
+
const targets = await getUninstallTargetRoots(modesForLookup, env);
|
|
686
|
+
|
|
687
|
+
const allKnown = await listAllKnownSkillNames({ toolkitHome, modes: modesForLookup, env });
|
|
688
|
+
stdout.write(color(`Apollo Toolkit home: ${toolkitHome}\n`, '2', supportsColor(stdout, env)));
|
|
689
|
+
if (targets.length > 0) {
|
|
690
|
+
stdout.write('Targets:\n');
|
|
691
|
+
for (const target of targets) {
|
|
692
|
+
stdout.write(`- ${target.label}: ${target.root}\n`);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
const confirmed = parsed.assumeYes || await promptYesNo({
|
|
697
|
+
stdin, stdout, env,
|
|
698
|
+
question: `This will remove Apollo Toolkit-installed skills${modes ? ` from: ${modes.join(', ')}` : ' from all targets'}. Continue?`,
|
|
699
|
+
defaultYes: false,
|
|
700
|
+
});
|
|
701
|
+
|
|
702
|
+
if (!confirmed) {
|
|
703
|
+
stdout.write('Uninstall cancelled.\n');
|
|
704
|
+
return 1;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
const uninstallResult = await uninstallSkills({ env, modes: modes ? [...normalizeModes(modes)] as InstallMode[] : undefined });
|
|
708
|
+
printUninstallSummary({ stdout, uninstallResult, env });
|
|
709
|
+
|
|
710
|
+
if (allKnown.length > 0) {
|
|
711
|
+
stdout.write(`\nPreviously known skills (may still exist elsewhere): ${allKnown.join(', ')}\n`);
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
return 0;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
// Install flow
|
|
718
|
+
const updateResult = await checkForPackageUpdate({
|
|
719
|
+
packageName: packageJson.name,
|
|
720
|
+
currentVersion: packageJson.version,
|
|
721
|
+
env,
|
|
722
|
+
stdin,
|
|
723
|
+
stdout,
|
|
724
|
+
stderr,
|
|
725
|
+
exec: context.execCommand as any,
|
|
726
|
+
confirmUpdate: context.confirmUpdate as any,
|
|
727
|
+
});
|
|
728
|
+
|
|
729
|
+
if (updateResult.updated) {
|
|
730
|
+
packageJson = readPackageJson(sourceRoot);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
const toolkitHome = parsed.toolkitHome || resolveToolkitHome(env);
|
|
734
|
+
const modes: InstallMode[] = parsed.modes.length > 0
|
|
735
|
+
? normalizeModes(parsed.modes)
|
|
736
|
+
: normalizeModes(await promptForModes({ stdin, stdout, version: packageJson.version, env }));
|
|
737
|
+
|
|
738
|
+
const colorEnabled = supportsColor(stdout, env);
|
|
739
|
+
|
|
740
|
+
let linkMode: 'copy' | 'symlink' | null = parsed.linkMode;
|
|
741
|
+
if (!linkMode) {
|
|
742
|
+
linkMode = (await promptSymlinkChoice({ stdin, stdout, env, colorEnabled })) ? 'symlink' : 'copy';
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
const nonCodexModes = modes.filter((m) => m !== 'codex');
|
|
746
|
+
const codexSkillNames = await listCodexSkillNames(toolkitHome).catch(() => []);
|
|
747
|
+
const includeExclusiveSkills = await promptIncludeExclusiveSkills({
|
|
748
|
+
stdin, stdout, env, colorEnabled, codexSkillNames, nonCodexModes,
|
|
749
|
+
});
|
|
750
|
+
|
|
751
|
+
const effectiveModes: InstallMode[] = includeExclusiveSkills
|
|
752
|
+
? [...new Set<InstallMode>([...modes, 'codex'])]
|
|
753
|
+
: modes;
|
|
754
|
+
|
|
755
|
+
const confirmed = await confirmInstall({
|
|
756
|
+
stdin, stdout, version: packageJson.version, toolkitHome, modes, linkMode, env,
|
|
757
|
+
});
|
|
758
|
+
|
|
759
|
+
if (!confirmed) {
|
|
760
|
+
stdout.write('Installation cancelled.\n');
|
|
761
|
+
return 1;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
const syncResult = await syncToolkitHome({ sourceRoot, toolkitHome, version: packageJson.version, modes: effectiveModes });
|
|
765
|
+
const installResult = await installLinks({
|
|
766
|
+
toolkitHome,
|
|
767
|
+
modes,
|
|
768
|
+
previousSkillNames: syncResult.previousSkillNames,
|
|
769
|
+
linkMode,
|
|
770
|
+
includeExclusiveSkills,
|
|
771
|
+
env: { ...env, APOLLO_TOOLKIT_HOME: toolkitHome },
|
|
772
|
+
});
|
|
773
|
+
|
|
774
|
+
printSummary({ stdout, version: packageJson.version, toolkitHome, modes, installResult, env });
|
|
775
|
+
return 0;
|
|
776
|
+
} catch (error) {
|
|
777
|
+
stderr.write(`Error: ${(error as Error).message}\n`);
|
|
778
|
+
return 1;
|
|
779
|
+
}
|
|
780
|
+
}
|