@laitszkin/apollo-toolkit 3.2.2 → 3.3.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 +12 -4
- package/CHANGELOG.md +26 -2
- package/README.md +39 -8
- 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/docs-to-voice/scripts/__pycache__/docs_to_voice.cpython-312.pyc +0 -0
- package/generate-spec/scripts/__pycache__/create-specscpython-312.pyc +0 -0
- package/katex/scripts/__pycache__/render_katex.cpython-312.pyc +0 -0
- package/lib/cli.js +260 -24
- package/lib/installer.js +181 -8
- package/open-github-issue/scripts/__pycache__/open_github_issue.cpython-312.pyc +0 -0
- package/package.json +1 -1
- 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/resolve-review-comments/scripts/__pycache__/review_threads.cpython-312.pyc +0 -0
- package/scripts/install_skills.ps1 +488 -108
- package/scripts/install_skills.sh +429 -15
- package/text-to-short-video/scripts/__pycache__/enforce_video_aspect_ratio.cpython-312.pyc +0 -0
package/AGENTS.md
CHANGED
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
- This repository is a skill catalog: each top-level skill lives in its own directory and is installable when that directory contains `SKILL.md`.
|
|
6
6
|
- Typical skill layout is lightweight and consistent: `SKILL.md`, `README.md`, `LICENSE`, plus optional `agents/`, `references/`, and `scripts/`.
|
|
7
|
-
- The npm package exposes an `apollo-toolkit` CLI that stages a managed copy under `~/.apollo-toolkit` and copies each skill folder into selected target directories.
|
|
8
|
-
-
|
|
7
|
+
- The npm package exposes an `apollo-toolkit` CLI that stages a managed copy under `~/.apollo-toolkit` and copies or symlinks each skill folder into selected target directories.
|
|
8
|
+
- The installer writes a `.apollo-toolkit-manifest.json` per target directory to track installed skills, historical skill names, and install mode for future uninstall and deduplication.
|
|
9
|
+
- `scripts/install_skills.sh` and `scripts/install_skills.ps1` remain available for local/curl installs and mirror the managed-home install behavior with symlink/copy choice and uninstall support.
|
|
9
10
|
|
|
10
11
|
## Core Business Flow
|
|
11
12
|
|
|
@@ -20,7 +21,9 @@ This repository enables users to install and run a curated set of reusable agent
|
|
|
20
21
|
- Users can research a topic deeply and produce evidence-based deliverables.
|
|
21
22
|
- Users can research the latest completed market week and produce a PDF watchlist of tradeable instruments for the coming week.
|
|
22
23
|
- Users can turn a marked weekly finance PDF into a concise evidence-based financial event report.
|
|
23
|
-
- Users can install Apollo Toolkit through npm or npx and interactively choose one or more target skill directories to populate with copied skills.
|
|
24
|
+
- Users can install Apollo Toolkit through npm or npx and interactively choose one or more target skill directories to populate with copied or symlinked skills, with the option to include codex-exclusive skills in non-codex targets.
|
|
25
|
+
- Users can uninstall Apollo Toolkit-installed skills through an interactive target selector or specific non-interactive targets via `apltk uninstall`.
|
|
26
|
+
- Users can choose between symlink mode (auto-update via git pull) and copy mode (stable snapshot) with `--symlink` / `--copy` flags.
|
|
24
27
|
- Users can run bundled helper tools through `apltk tools` and direct `apltk <tool>` commands for selected packaged skill scripts.
|
|
25
28
|
- Users can design and implement new features through a spec-first workflow.
|
|
26
29
|
- Users can generate shared feature planning artifacts for approval-gated workflows, including parallel multi-spec batches coordinated through one batch-level `coordination.md`.
|
|
@@ -72,7 +75,12 @@ This repository enables users to install and run a curated set of reusable agent
|
|
|
72
75
|
- `python3 scripts/validate_skill_frontmatter.py` - 驗證所有頂層技能 `SKILL.md` 的 frontmatter。
|
|
73
76
|
- `python3 scripts/validate_openai_agent_config.py` - 驗證所有技能 `agents/openai.yaml` 設定。
|
|
74
77
|
- `./scripts/install_skills.sh codex` - 用本地安裝腳本把技能安裝到 Codex 目錄。
|
|
75
|
-
- `./scripts/install_skills.sh
|
|
78
|
+
- `./scripts/install_skills.sh codex --symlink` - 以 symlink 模式安裝(推薦)。
|
|
79
|
+
- `./scripts/install_skills.sh all --copy` - 以複製模式安裝到所有支援目標。
|
|
80
|
+
- `./scripts/install_skills.sh uninstall` - 從所有目標移除已安裝的技能。
|
|
81
|
+
- `./scripts/install_skills.sh uninstall codex` - 只從 codex 目標移除。
|
|
82
|
+
- `node bin/apollo-toolkit.js uninstall` - 透過 CLI 互動選擇要移除的 agent target 技能。
|
|
83
|
+
- `node bin/apollo-toolkit.js uninstall codex --yes` - 以非互動方式移除指定 target 的已安裝技能。
|
|
76
84
|
|
|
77
85
|
## Core Project Purpose
|
|
78
86
|
|
package/CHANGELOG.md
CHANGED
|
@@ -4,8 +4,32 @@ All notable changes to this repository are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
-
###
|
|
8
|
-
- None yet
|
|
7
|
+
### Added
|
|
8
|
+
- (None yet)
|
|
9
|
+
|
|
10
|
+
## [v3.3.1] - 2026-04-26
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Add an interactive `apltk uninstall` target selector so users can choose which agent skill directories to remove.
|
|
14
|
+
- Add `apltk uninstall --yes` for non-interactive uninstall confirmation.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- Fix default `apltk uninstall` cleanup so a missing OpenClaw workspace no longer prevents uninstalling Codex, Trae, Agents, or Claude Code targets.
|
|
18
|
+
- Remove manifest-tracked historical skills during CLI uninstall so renamed or removed skills do not remain behind.
|
|
19
|
+
- Ignore unsafe manifest skill names during install and uninstall cleanup so removals remain scoped to direct child skill directories.
|
|
20
|
+
|
|
21
|
+
## [v3.3.0] - 2026-04-26
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
- Add `apltk uninstall` command to remove all installed skills from all targets (or specific targets) via manifest-based cleanup.
|
|
25
|
+
- Add symlink install mode (`--symlink`) so skills auto-update when `git pull` runs in `~/.apollo-toolkit`, removing the need to re-run the installer after patch updates.
|
|
26
|
+
- Add `--copy` flag to explicitly select copy mode when symlink is not desired.
|
|
27
|
+
- Add interactive prompt during install that explains symlink pros/cons and lets the user choose between symlink and copy mode.
|
|
28
|
+
- Add interactive prompt to optionally install codex-exclusive skills into non-codex targets during global install.
|
|
29
|
+
- Add `.apollo-toolkit-manifest.json` per target directory to track installed skills, historical skill names, and install mode for future uninstall and deduplication.
|
|
30
|
+
- Add `listAllKnownSkillNames()` to combine current and historically-appeared skill names with automatic deduplication.
|
|
31
|
+
- Add `uninstall` subcommand to `scripts/install_skills.sh` and `scripts/install_skills.ps1`.
|
|
32
|
+
- Add `--symlink` / `--copy` flags to both shell and PowerShell install scripts.
|
|
9
33
|
|
|
10
34
|
## [v3.2.2] - 2026-04-25
|
|
11
35
|
|
package/README.md
CHANGED
|
@@ -65,9 +65,30 @@ The interactive installer:
|
|
|
65
65
|
- shows a branded `Apollo Toolkit` terminal welcome screen with a short staged reveal
|
|
66
66
|
- installs a managed copy into `~/.apollo-toolkit`
|
|
67
67
|
- lets you multi-select `codex`, `openclaw`, `trae`, `agents`, `claude-code`, or `all`
|
|
68
|
-
-
|
|
68
|
+
- asks whether to install skills as **symlinks** (recommended) or **file copies**
|
|
69
|
+
- lets you choose whether to include codex-exclusive skills in non-codex targets
|
|
70
|
+
- copies or symlinks `~/.apollo-toolkit/<skill>` into each selected target
|
|
69
71
|
- removes stale previously installed skill directories that existed in the previous installed version but no longer exist in the current package skill list
|
|
70
72
|
- replaces legacy symlink-based installs created by older Apollo Toolkit installers with real copied directories
|
|
73
|
+
- writes a manifest (`.apollo-toolkit-manifest.json`) per target for future uninstall and skill tracking
|
|
74
|
+
|
|
75
|
+
### Symlink vs Copy
|
|
76
|
+
|
|
77
|
+
| Mode | Pro | Con |
|
|
78
|
+
| --- | --- | --- |
|
|
79
|
+
| **Symlink** (recommended) | Auto-updates when you `git pull` in `~/.apollo-toolkit`; no need to re-run installer after patch updates | Changes pushed to the repo automatically reflect in your skills — you may receive updates you did not intend to accept |
|
|
80
|
+
| **Copy** | Stable snapshot; won't change until you re-run the installer | Must manually re-run `apltk` after each toolkit update to get the latest skills |
|
|
81
|
+
|
|
82
|
+
### Uninstall
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
apltk uninstall # Choose which agent targets to uninstall
|
|
86
|
+
apltk uninstall codex # Remove only from codex
|
|
87
|
+
apltk uninstall codex agents --yes # Non-interactive cleanup for selected targets
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
The uninstall flow removes the manifest-tracked current and historical skill
|
|
91
|
+
directories for the selected targets, then removes each target manifest.
|
|
71
92
|
|
|
72
93
|
### Global install
|
|
73
94
|
|
|
@@ -98,6 +119,13 @@ npx @laitszkin/apollo-toolkit codex openclaw
|
|
|
98
119
|
npx @laitszkin/apollo-toolkit all
|
|
99
120
|
```
|
|
100
121
|
|
|
122
|
+
Add `--symlink` (recommended) or `--copy` to skip the interactive prompt:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
npx @laitszkin/apollo-toolkit codex --symlink
|
|
126
|
+
npx @laitszkin/apollo-toolkit all --copy
|
|
127
|
+
```
|
|
128
|
+
|
|
101
129
|
### Optional overrides
|
|
102
130
|
|
|
103
131
|
```bash
|
|
@@ -121,24 +149,27 @@ Installers still live in `scripts/` for local repository usage and curl / iwr in
|
|
|
121
149
|
```bash
|
|
122
150
|
./scripts/install_skills.sh
|
|
123
151
|
./scripts/install_skills.sh codex
|
|
124
|
-
./scripts/install_skills.sh
|
|
125
|
-
./scripts/install_skills.sh
|
|
126
|
-
./scripts/install_skills.sh
|
|
127
|
-
./scripts/install_skills.sh
|
|
152
|
+
./scripts/install_skills.sh codex --symlink
|
|
153
|
+
./scripts/install_skills.sh all --copy
|
|
154
|
+
./scripts/install_skills.sh uninstall
|
|
155
|
+
./scripts/install_skills.sh uninstall codex trae
|
|
128
156
|
```
|
|
129
157
|
|
|
130
158
|
```powershell
|
|
131
159
|
./scripts/install_skills.ps1
|
|
132
160
|
./scripts/install_skills.ps1 codex
|
|
133
|
-
./scripts/install_skills.ps1 agents
|
|
134
|
-
./scripts/install_skills.ps1 all
|
|
161
|
+
./scripts/install_skills.ps1 agents --symlink
|
|
162
|
+
./scripts/install_skills.ps1 all --copy
|
|
163
|
+
./scripts/install_skills.ps1 uninstall
|
|
164
|
+
./scripts/install_skills.ps1 uninstall codex trae
|
|
135
165
|
```
|
|
136
166
|
|
|
137
167
|
### Curl / iwr one-liners
|
|
138
168
|
|
|
139
169
|
```bash
|
|
140
170
|
curl -fsSL https://raw.githubusercontent.com/LaiTszKin/apollo-toolkit/main/scripts/install_skills.sh | bash
|
|
141
|
-
curl -fsSL https://raw.githubusercontent.com/LaiTszKin/apollo-toolkit/main/scripts/install_skills.sh | bash -s -- codex
|
|
171
|
+
curl -fsSL https://raw.githubusercontent.com/LaiTszKin/apollo-toolkit/main/scripts/install_skills.sh | bash -s -- codex --symlink
|
|
172
|
+
curl -fsSL https://raw.githubusercontent.com/LaiTszKin/apollo-toolkit/main/scripts/install_skills.sh | bash -s -- uninstall
|
|
142
173
|
```
|
|
143
174
|
|
|
144
175
|
```powershell
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/lib/cli.js
CHANGED
|
@@ -6,16 +6,20 @@ const {
|
|
|
6
6
|
TARGET_DEFINITIONS,
|
|
7
7
|
VALID_MODES,
|
|
8
8
|
installLinks,
|
|
9
|
+
listAllKnownSkillNames,
|
|
10
|
+
listCodexSkillNames,
|
|
9
11
|
normalizeModes,
|
|
10
12
|
resolveToolkitHome,
|
|
11
13
|
syncToolkitHome,
|
|
14
|
+
uninstallSkills,
|
|
12
15
|
getTargetRoots,
|
|
16
|
+
getUninstallTargetRoots,
|
|
13
17
|
} = require('./installer');
|
|
14
18
|
const { formatToolList, getToolCommand, runTool } = require('./tool-runner');
|
|
15
19
|
const { checkForPackageUpdate } = require('./updater');
|
|
16
20
|
|
|
17
21
|
const TARGET_OPTIONS = [
|
|
18
|
-
{ id: 'all', label: 'All', description: '
|
|
22
|
+
{ id: 'all', label: 'All', description: 'Select every supported target below' },
|
|
19
23
|
...TARGET_DEFINITIONS,
|
|
20
24
|
];
|
|
21
25
|
|
|
@@ -41,7 +45,7 @@ function color(text, code, enabled) {
|
|
|
41
45
|
return text;
|
|
42
46
|
}
|
|
43
47
|
|
|
44
|
-
return
|
|
48
|
+
return `[${code}m${text}[0m`;
|
|
45
49
|
}
|
|
46
50
|
|
|
47
51
|
function sleep(ms) {
|
|
@@ -134,6 +138,7 @@ function buildHelpText({ version, colorEnabled }) {
|
|
|
134
138
|
'Usage:',
|
|
135
139
|
` apltk [install] [${buildModeUsagePattern()}]...`,
|
|
136
140
|
` apollo-toolkit [install] [${buildModeUsagePattern()}]...`,
|
|
141
|
+
` apltk uninstall [${buildModeUsagePattern()}]... [--yes]`,
|
|
137
142
|
' apltk tools',
|
|
138
143
|
' apltk <tool> [...args]',
|
|
139
144
|
' apltk tools <tool> [...args]',
|
|
@@ -143,6 +148,8 @@ function buildHelpText({ version, colorEnabled }) {
|
|
|
143
148
|
'Examples:',
|
|
144
149
|
' apltk',
|
|
145
150
|
' apltk codex openclaw',
|
|
151
|
+
' apltk uninstall',
|
|
152
|
+
' apltk uninstall codex agents --yes',
|
|
146
153
|
' npx @laitszkin/apollo-toolkit',
|
|
147
154
|
' npx @laitszkin/apollo-toolkit codex openclaw',
|
|
148
155
|
' npm i -g @laitszkin/apollo-toolkit',
|
|
@@ -159,6 +166,7 @@ function buildHelpText({ version, colorEnabled }) {
|
|
|
159
166
|
'',
|
|
160
167
|
'Options:',
|
|
161
168
|
' --home <path> Override Apollo Toolkit home directory',
|
|
169
|
+
' --yes, -y Skip uninstall confirmation',
|
|
162
170
|
' --help Show this help text',
|
|
163
171
|
].join('\n');
|
|
164
172
|
}
|
|
@@ -194,8 +202,32 @@ function parseArguments(argv) {
|
|
|
194
202
|
toolkitHome: null,
|
|
195
203
|
toolName: null,
|
|
196
204
|
toolArgs: [],
|
|
205
|
+
linkMode: null, // 'copy' | 'symlink' | null (prompt)
|
|
206
|
+
assumeYes: false,
|
|
197
207
|
};
|
|
198
208
|
|
|
209
|
+
if (args[0] === 'uninstall') {
|
|
210
|
+
result.command = 'uninstall';
|
|
211
|
+
args.shift();
|
|
212
|
+
while (args.length > 0) {
|
|
213
|
+
const arg = args.shift();
|
|
214
|
+
if (arg === '--help' || arg === '-h') {
|
|
215
|
+
result.showHelp = true;
|
|
216
|
+
} else if (arg === '--yes' || arg === '-y') {
|
|
217
|
+
result.assumeYes = true;
|
|
218
|
+
} else if (arg === '--home') {
|
|
219
|
+
const toolkitHome = args.shift();
|
|
220
|
+
if (!toolkitHome) {
|
|
221
|
+
throw new Error('Missing value for --home');
|
|
222
|
+
}
|
|
223
|
+
result.toolkitHome = path.resolve(toolkitHome);
|
|
224
|
+
} else {
|
|
225
|
+
result.modes.push(arg);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return result;
|
|
229
|
+
}
|
|
230
|
+
|
|
199
231
|
if (args[0] === 'tools' || args[0] === 'tool') {
|
|
200
232
|
args.shift();
|
|
201
233
|
if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
|
|
@@ -234,6 +266,16 @@ function parseArguments(argv) {
|
|
|
234
266
|
continue;
|
|
235
267
|
}
|
|
236
268
|
|
|
269
|
+
if (arg === '--symlink') {
|
|
270
|
+
result.linkMode = 'symlink';
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (arg === '--copy') {
|
|
275
|
+
result.linkMode = 'copy';
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
|
|
237
279
|
if (arg === 'install') {
|
|
238
280
|
continue;
|
|
239
281
|
}
|
|
@@ -246,17 +288,25 @@ function parseArguments(argv) {
|
|
|
246
288
|
|
|
247
289
|
function clearScreen(output) {
|
|
248
290
|
if (output.isTTY) {
|
|
249
|
-
output.write('
|
|
291
|
+
output.write('[2J[H');
|
|
250
292
|
}
|
|
251
293
|
}
|
|
252
294
|
|
|
253
|
-
function renderSelectionScreen({
|
|
295
|
+
function renderSelectionScreen({
|
|
296
|
+
output,
|
|
297
|
+
version,
|
|
298
|
+
cursor,
|
|
299
|
+
selected,
|
|
300
|
+
message,
|
|
301
|
+
env,
|
|
302
|
+
intro = 'Choose where Apollo Toolkit should copy managed skills.',
|
|
303
|
+
}) {
|
|
254
304
|
const colorEnabled = supportsColor(output, env);
|
|
255
305
|
const allSelected = VALID_MODES.every((mode) => selected.has(mode));
|
|
256
306
|
|
|
257
307
|
clearScreen(output);
|
|
258
308
|
output.write(`${buildBanner({ version, colorEnabled })}\n\n`);
|
|
259
|
-
output.write(
|
|
309
|
+
output.write(`${intro}\n`);
|
|
260
310
|
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`);
|
|
261
311
|
output.write(`Press ${color('a', '1;33', colorEnabled)} to toggle all, ${color('q', '1;33', colorEnabled)} to cancel.\n\n`);
|
|
262
312
|
|
|
@@ -277,13 +327,11 @@ function renderSelectionScreen({ output, version, cursor, selected, message, env
|
|
|
277
327
|
}
|
|
278
328
|
}
|
|
279
329
|
|
|
280
|
-
async function
|
|
330
|
+
async function promptForSelectableModes({ stdin, stdout, version, env, intro, ttyError, cancelMessage }) {
|
|
281
331
|
if (!stdin.isTTY || !stdout.isTTY) {
|
|
282
|
-
throw new Error(
|
|
332
|
+
throw new Error(ttyError);
|
|
283
333
|
}
|
|
284
334
|
|
|
285
|
-
await animateWelcomeScreen({ output: stdout, version, env });
|
|
286
|
-
|
|
287
335
|
return new Promise((resolve, reject) => {
|
|
288
336
|
let cursor = 0;
|
|
289
337
|
let message = '';
|
|
@@ -315,50 +363,50 @@ async function promptForModes({ stdin, stdout, version, env }) {
|
|
|
315
363
|
|
|
316
364
|
const onData = (chunk) => {
|
|
317
365
|
const value = chunk.toString('utf8');
|
|
318
|
-
if (value === '
|
|
366
|
+
if (value === '') {
|
|
319
367
|
cleanup();
|
|
320
|
-
reject(new Error(
|
|
368
|
+
reject(new Error(cancelMessage));
|
|
321
369
|
return;
|
|
322
370
|
}
|
|
323
371
|
|
|
324
|
-
if (value === '
|
|
372
|
+
if (value === '[A' || value === 'k') {
|
|
325
373
|
cursor = (cursor - 1 + TARGET_OPTIONS.length) % TARGET_OPTIONS.length;
|
|
326
374
|
message = '';
|
|
327
|
-
renderSelectionScreen({ output: stdout, version, cursor, selected, message, env });
|
|
375
|
+
renderSelectionScreen({ output: stdout, version, cursor, selected, message, env, intro });
|
|
328
376
|
return;
|
|
329
377
|
}
|
|
330
378
|
|
|
331
|
-
if (value === '
|
|
379
|
+
if (value === '[B' || value === 'j') {
|
|
332
380
|
cursor = (cursor + 1) % TARGET_OPTIONS.length;
|
|
333
381
|
message = '';
|
|
334
|
-
renderSelectionScreen({ output: stdout, version, cursor, selected, message, env });
|
|
382
|
+
renderSelectionScreen({ output: stdout, version, cursor, selected, message, env, intro });
|
|
335
383
|
return;
|
|
336
384
|
}
|
|
337
385
|
|
|
338
386
|
if (value === ' ') {
|
|
339
387
|
toggleMode(TARGET_OPTIONS[cursor].id);
|
|
340
388
|
message = '';
|
|
341
|
-
renderSelectionScreen({ output: stdout, version, cursor, selected, message, env });
|
|
389
|
+
renderSelectionScreen({ output: stdout, version, cursor, selected, message, env, intro });
|
|
342
390
|
return;
|
|
343
391
|
}
|
|
344
392
|
|
|
345
393
|
if (value.toLowerCase() === 'a') {
|
|
346
394
|
toggleMode('all');
|
|
347
395
|
message = '';
|
|
348
|
-
renderSelectionScreen({ output: stdout, version, cursor, selected, message, env });
|
|
396
|
+
renderSelectionScreen({ output: stdout, version, cursor, selected, message, env, intro });
|
|
349
397
|
return;
|
|
350
398
|
}
|
|
351
399
|
|
|
352
|
-
if (value.toLowerCase() === 'q' || value === '
|
|
400
|
+
if (value.toLowerCase() === 'q' || value === '') {
|
|
353
401
|
cleanup();
|
|
354
|
-
reject(new Error(
|
|
402
|
+
reject(new Error(cancelMessage));
|
|
355
403
|
return;
|
|
356
404
|
}
|
|
357
405
|
|
|
358
406
|
if (value === '\r') {
|
|
359
407
|
if (selected.size === 0) {
|
|
360
408
|
message = 'Select at least one target before continuing.';
|
|
361
|
-
renderSelectionScreen({ output: stdout, version, cursor, selected, message, env });
|
|
409
|
+
renderSelectionScreen({ output: stdout, version, cursor, selected, message, env, intro });
|
|
362
410
|
return;
|
|
363
411
|
}
|
|
364
412
|
|
|
@@ -370,15 +418,106 @@ async function promptForModes({ stdin, stdout, version, env }) {
|
|
|
370
418
|
stdin.setRawMode(true);
|
|
371
419
|
stdin.resume();
|
|
372
420
|
stdin.on('data', onData);
|
|
373
|
-
renderSelectionScreen({ output: stdout, version, cursor, selected, message, env });
|
|
421
|
+
renderSelectionScreen({ output: stdout, version, cursor, selected, message, env, intro });
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
async function promptForModes({ stdin, stdout, version, env }) {
|
|
426
|
+
await animateWelcomeScreen({ output: stdout, version, env });
|
|
427
|
+
return promptForSelectableModes({
|
|
428
|
+
stdin,
|
|
429
|
+
stdout,
|
|
430
|
+
version,
|
|
431
|
+
env,
|
|
432
|
+
intro: 'Choose where Apollo Toolkit should copy managed skills.',
|
|
433
|
+
ttyError: `Interactive install requires a TTY. Re-run with targets like ${buildInteractiveModeHint()}.`,
|
|
434
|
+
cancelMessage: 'Installation cancelled.',
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
async function promptForUninstallModes({ stdin, stdout, version, env }) {
|
|
439
|
+
return promptForSelectableModes({
|
|
440
|
+
stdin,
|
|
441
|
+
stdout,
|
|
442
|
+
version,
|
|
443
|
+
env,
|
|
444
|
+
intro: 'Choose which agent skill targets Apollo Toolkit should uninstall.',
|
|
445
|
+
ttyError: `Interactive uninstall requires a TTY. Re-run with targets like ${buildInteractiveModeHint()}.`,
|
|
446
|
+
cancelMessage: 'Uninstall cancelled.',
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
async function promptYesNo({ stdin, stdout, env, question, defaultYes = true }) {
|
|
451
|
+
if (!stdin.isTTY || !stdout.isTTY) {
|
|
452
|
+
return defaultYes;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const rl = createInterface({ input: stdin, output: stdout });
|
|
456
|
+
try {
|
|
457
|
+
const hint = defaultYes ? '[Y/n]' : '[y/N]';
|
|
458
|
+
const answer = await rl.question(`${question} ${hint} `);
|
|
459
|
+
const trimmed = answer.trim().toLowerCase();
|
|
460
|
+
if (trimmed === '') {
|
|
461
|
+
return defaultYes;
|
|
462
|
+
}
|
|
463
|
+
return trimmed === 'y' || trimmed === 'yes';
|
|
464
|
+
} finally {
|
|
465
|
+
rl.close();
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
function buildSymlinkInfo({ colorEnabled }) {
|
|
470
|
+
return [
|
|
471
|
+
'',
|
|
472
|
+
color('Symlink mode:', '1', colorEnabled),
|
|
473
|
+
` ${color('Pro:', '1;32', colorEnabled)} Skills auto-update when you ${color('git pull', '1;33', colorEnabled)} in ~/.apollo-toolkit`,
|
|
474
|
+
` ${color('Pro:', '1;32', colorEnabled)} No need to re-run installer after patch updates`,
|
|
475
|
+
` ${color('Con:', '1;31', colorEnabled)} Changes pushed to the repo automatically reflect in your skills -`,
|
|
476
|
+
` you may receive updates you did not intend to accept`,
|
|
477
|
+
'',
|
|
478
|
+
].join('\n');
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
async function promptSymlinkChoice({ stdin, stdout, env, colorEnabled }) {
|
|
482
|
+
stdout.write(buildSymlinkInfo({ colorEnabled }));
|
|
483
|
+
return promptYesNo({
|
|
484
|
+
stdin,
|
|
485
|
+
stdout,
|
|
486
|
+
env,
|
|
487
|
+
question: 'Install skills as symlinks (recommended)?',
|
|
488
|
+
defaultYes: true,
|
|
374
489
|
});
|
|
375
490
|
}
|
|
376
491
|
|
|
377
|
-
|
|
492
|
+
// Ask user whether to include codex-exclusive skills in non-codex targets.
|
|
493
|
+
async function promptIncludeExclusiveSkills({ stdin, stdout, env, colorEnabled, codexSkillNames, nonCodexModes }) {
|
|
494
|
+
if (codexSkillNames.length === 0 || nonCodexModes.length === 0) {
|
|
495
|
+
return false;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
stdout.write([
|
|
499
|
+
'',
|
|
500
|
+
color('Exclusive skills detected:', '1;33', colorEnabled),
|
|
501
|
+
` The following skills are exclusive to codex: ${codexSkillNames.join(', ')}`,
|
|
502
|
+
` Your selected non-codex targets: ${nonCodexModes.join(', ')}`,
|
|
503
|
+
'',
|
|
504
|
+
].join('\n'));
|
|
505
|
+
|
|
506
|
+
return promptYesNo({
|
|
507
|
+
stdin,
|
|
508
|
+
stdout,
|
|
509
|
+
env,
|
|
510
|
+
question: `Install codex-exclusive skills to ${nonCodexModes.join(', ')} as well?`,
|
|
511
|
+
defaultYes: false,
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
async function confirmInstall({ stdin, stdout, version, toolkitHome, modes, linkMode, env }) {
|
|
378
516
|
const colorEnabled = supportsColor(stdout, env);
|
|
379
517
|
stdout.write(`${buildBanner({ version, colorEnabled })}\n\n`);
|
|
380
518
|
stdout.write(`Apollo Toolkit home: ${toolkitHome}\n`);
|
|
381
|
-
stdout.write(`Targets: ${modes.join(', ')}\n
|
|
519
|
+
stdout.write(`Targets: ${modes.join(', ')}\n`);
|
|
520
|
+
stdout.write(`Install mode: ${linkMode === 'symlink' ? 'symlink (auto-update via git pull)' : 'copy (manual reinstall for updates)'}\n\n`);
|
|
382
521
|
|
|
383
522
|
const targets = await getTargetRoots(modes, env).catch((error) => {
|
|
384
523
|
throw error;
|
|
@@ -408,6 +547,7 @@ function printSummary({ stdout, version, toolkitHome, modes, installResult, env
|
|
|
408
547
|
stdout.write('\n');
|
|
409
548
|
stdout.write(`Apollo Toolkit home: ${toolkitHome}\n`);
|
|
410
549
|
stdout.write(`Installed skills: ${installResult.skillNames.length}\n`);
|
|
550
|
+
stdout.write(`Install mode: ${installResult.linkMode === 'symlink' ? 'symlink' : 'copy'}\n`);
|
|
411
551
|
stdout.write(`Targets: ${modes.join(', ')}\n\n`);
|
|
412
552
|
|
|
413
553
|
for (const target of installResult.targets) {
|
|
@@ -415,6 +555,22 @@ function printSummary({ stdout, version, toolkitHome, modes, installResult, env
|
|
|
415
555
|
}
|
|
416
556
|
}
|
|
417
557
|
|
|
558
|
+
function printUninstallSummary({ stdout, uninstallResult, env }) {
|
|
559
|
+
const colorEnabled = supportsColor(stdout, env);
|
|
560
|
+
|
|
561
|
+
if (uninstallResult.length === 0) {
|
|
562
|
+
stdout.write(color('No Apollo Toolkit installations found.\n', '1;33', colorEnabled));
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
stdout.write(color('Uninstall complete.', '1;32', colorEnabled));
|
|
567
|
+
stdout.write('\n\n');
|
|
568
|
+
for (const result of uninstallResult) {
|
|
569
|
+
stdout.write(`${color(result.target, '1', colorEnabled)} (${result.root})\n`);
|
|
570
|
+
stdout.write(` Removed: ${result.removedSkills.length > 0 ? result.removedSkills.join(', ') : '(manifest only)'}\n`);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
418
574
|
async function run(argv, context = {}) {
|
|
419
575
|
const sourceRoot = context.sourceRoot || path.resolve(__dirname, '..');
|
|
420
576
|
const stdout = context.stdout || process.stdout;
|
|
@@ -425,14 +581,17 @@ async function run(argv, context = {}) {
|
|
|
425
581
|
|
|
426
582
|
try {
|
|
427
583
|
const parsed = parseArguments(argv);
|
|
584
|
+
|
|
428
585
|
if (parsed.showHelp) {
|
|
429
586
|
stdout.write(`${buildHelpText({ version: packageJson.version, colorEnabled: supportsColor(stdout, env) })}\n`);
|
|
430
587
|
return 0;
|
|
431
588
|
}
|
|
589
|
+
|
|
432
590
|
if (parsed.showToolsHelp) {
|
|
433
591
|
stdout.write(`${buildToolsHelp({ version: packageJson.version, colorEnabled: supportsColor(stdout, env) })}\n`);
|
|
434
592
|
return 0;
|
|
435
593
|
}
|
|
594
|
+
|
|
436
595
|
if (parsed.command === 'tool') {
|
|
437
596
|
return (context.runTool || runTool)(parsed.toolName, parsed.toolArgs, {
|
|
438
597
|
sourceRoot,
|
|
@@ -443,6 +602,51 @@ async function run(argv, context = {}) {
|
|
|
443
602
|
});
|
|
444
603
|
}
|
|
445
604
|
|
|
605
|
+
// --- Uninstall flow ---
|
|
606
|
+
if (parsed.command === 'uninstall') {
|
|
607
|
+
const toolkitHome = parsed.toolkitHome || resolveToolkitHome(env);
|
|
608
|
+
const modes = parsed.modes.length > 0
|
|
609
|
+
? normalizeModes(parsed.modes)
|
|
610
|
+
: (stdin.isTTY && stdout.isTTY
|
|
611
|
+
? normalizeModes(await promptForUninstallModes({ stdin, stdout, version: packageJson.version, env }))
|
|
612
|
+
: null);
|
|
613
|
+
const modesForLookup = modes || VALID_MODES;
|
|
614
|
+
const targets = await getUninstallTargetRoots(modesForLookup, env);
|
|
615
|
+
|
|
616
|
+
// Show what will be removed
|
|
617
|
+
const allKnown = await listAllKnownSkillNames({ toolkitHome, modes: modesForLookup, env });
|
|
618
|
+
stdout.write(color(`Apollo Toolkit home: ${toolkitHome}\n`, '2', supportsColor(stdout, env)));
|
|
619
|
+
if (targets.length > 0) {
|
|
620
|
+
stdout.write('Targets:\n');
|
|
621
|
+
for (const target of targets) {
|
|
622
|
+
stdout.write(`- ${target.label}: ${target.root}\n`);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
const confirmed = parsed.assumeYes || await promptYesNo({
|
|
627
|
+
stdin,
|
|
628
|
+
stdout,
|
|
629
|
+
env,
|
|
630
|
+
question: `This will remove Apollo Toolkit-installed skills${modes ? ` from: ${modes.join(', ')}` : ' from all targets'}. Continue?`,
|
|
631
|
+
defaultYes: false,
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
if (!confirmed) {
|
|
635
|
+
stdout.write('Uninstall cancelled.\n');
|
|
636
|
+
return 1;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
const uninstallResult = await uninstallSkills({ env, modes });
|
|
640
|
+
printUninstallSummary({ stdout, uninstallResult, env });
|
|
641
|
+
|
|
642
|
+
if (allKnown.length > 0) {
|
|
643
|
+
stdout.write(`\nPreviously known skills (may still exist elsewhere): ${allKnown.join(', ')}\n`);
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
return 0;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// --- Install flow ---
|
|
446
650
|
const updateResult = await checkForPackageUpdate({
|
|
447
651
|
packageName: packageJson.name,
|
|
448
652
|
currentVersion: packageJson.version,
|
|
@@ -463,12 +667,39 @@ async function run(argv, context = {}) {
|
|
|
463
667
|
? normalizeModes(parsed.modes)
|
|
464
668
|
: normalizeModes(await promptForModes({ stdin, stdout, version: packageJson.version, env }));
|
|
465
669
|
|
|
670
|
+
const colorEnabled = supportsColor(stdout, env);
|
|
671
|
+
|
|
672
|
+
// Determine link mode
|
|
673
|
+
let linkMode = parsed.linkMode;
|
|
674
|
+
if (!linkMode) {
|
|
675
|
+
linkMode = (await promptSymlinkChoice({ stdin, stdout, env, colorEnabled })) ? 'symlink' : 'copy';
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// Determine whether to include exclusive (codex) skills in non-codex targets
|
|
679
|
+
const nonCodexModes = modes.filter((m) => m !== 'codex');
|
|
680
|
+
const codexSkillNames = await listCodexSkillNames(toolkitHome).catch(() => []);
|
|
681
|
+
const includeExclusiveSkills = await promptIncludeExclusiveSkills({
|
|
682
|
+
stdin,
|
|
683
|
+
stdout,
|
|
684
|
+
env,
|
|
685
|
+
colorEnabled,
|
|
686
|
+
codexSkillNames,
|
|
687
|
+
nonCodexModes,
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
// syncToolkitHome needs to include the codex container when exclusive skills
|
|
691
|
+
// are requested, so the source files are available for symlink/copy.
|
|
692
|
+
const effectiveModes = includeExclusiveSkills
|
|
693
|
+
? [...new Set([...modes, 'codex'])]
|
|
694
|
+
: modes;
|
|
695
|
+
|
|
466
696
|
const confirmed = await confirmInstall({
|
|
467
697
|
stdin,
|
|
468
698
|
stdout,
|
|
469
699
|
version: packageJson.version,
|
|
470
700
|
toolkitHome,
|
|
471
701
|
modes,
|
|
702
|
+
linkMode,
|
|
472
703
|
env,
|
|
473
704
|
});
|
|
474
705
|
|
|
@@ -481,13 +712,15 @@ async function run(argv, context = {}) {
|
|
|
481
712
|
sourceRoot,
|
|
482
713
|
toolkitHome,
|
|
483
714
|
version: packageJson.version,
|
|
484
|
-
modes,
|
|
715
|
+
modes: effectiveModes,
|
|
485
716
|
});
|
|
486
717
|
|
|
487
718
|
const installResult = await installLinks({
|
|
488
719
|
toolkitHome,
|
|
489
720
|
modes,
|
|
490
721
|
previousSkillNames: syncResult.previousSkillNames,
|
|
722
|
+
linkMode,
|
|
723
|
+
includeExclusiveSkills,
|
|
491
724
|
env: {
|
|
492
725
|
...env,
|
|
493
726
|
APOLLO_TOOLKIT_HOME: toolkitHome,
|
|
@@ -509,6 +742,9 @@ module.exports = {
|
|
|
509
742
|
buildToolsHelp,
|
|
510
743
|
parseArguments,
|
|
511
744
|
promptForModes,
|
|
745
|
+
promptForUninstallModes,
|
|
746
|
+
promptSymlinkChoice,
|
|
747
|
+
promptIncludeExclusiveSkills,
|
|
512
748
|
readPackageJson,
|
|
513
749
|
run,
|
|
514
750
|
};
|