@laitszkin/apollo-toolkit 3.3.0 → 3.3.2
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 +3 -2
- package/CHANGELOG.md +16 -0
- package/README.md +6 -2
- 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 +77 -22
- package/lib/installer.js +49 -15
- 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/text-to-short-video/scripts/__pycache__/enforce_video_aspect_ratio.cpython-312.pyc +0 -0
- package/version-release/SKILL.md +3 -1
package/AGENTS.md
CHANGED
|
@@ -22,7 +22,7 @@ This repository enables users to install and run a curated set of reusable agent
|
|
|
22
22
|
- Users can research the latest completed market week and produce a PDF watchlist of tradeable instruments for the coming week.
|
|
23
23
|
- Users can turn a marked weekly finance PDF into a concise evidence-based financial event report.
|
|
24
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
|
|
25
|
+
- Users can uninstall Apollo Toolkit-installed skills through an interactive target selector or specific non-interactive targets via `apltk uninstall`.
|
|
26
26
|
- Users can choose between symlink mode (auto-update via git pull) and copy mode (stable snapshot) with `--symlink` / `--copy` flags.
|
|
27
27
|
- Users can run bundled helper tools through `apltk tools` and direct `apltk <tool>` commands for selected packaged skill scripts.
|
|
28
28
|
- Users can design and implement new features through a spec-first workflow.
|
|
@@ -79,7 +79,8 @@ This repository enables users to install and run a curated set of reusable agent
|
|
|
79
79
|
- `./scripts/install_skills.sh all --copy` - 以複製模式安裝到所有支援目標。
|
|
80
80
|
- `./scripts/install_skills.sh uninstall` - 從所有目標移除已安裝的技能。
|
|
81
81
|
- `./scripts/install_skills.sh uninstall codex` - 只從 codex 目標移除。
|
|
82
|
-
- `node bin/apollo-toolkit.js uninstall` - 透過 CLI
|
|
82
|
+
- `node bin/apollo-toolkit.js uninstall` - 透過 CLI 互動選擇要移除的 agent target 技能。
|
|
83
|
+
- `node bin/apollo-toolkit.js uninstall codex --yes` - 以非互動方式移除指定 target 的已安裝技能。
|
|
83
84
|
|
|
84
85
|
## Core Project Purpose
|
|
85
86
|
|
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,22 @@ All notable changes to this repository are documented in this file.
|
|
|
7
7
|
### Added
|
|
8
8
|
- (None yet)
|
|
9
9
|
|
|
10
|
+
## [v3.3.2] - 2026-04-27
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- Tighten `version-release` so GitHub release prerelease state must come from explicit user intent or a verified repository convention, instead of being inferred from tag text such as `alpha-*`.
|
|
14
|
+
|
|
15
|
+
## [v3.3.1] - 2026-04-26
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- Add an interactive `apltk uninstall` target selector so users can choose which agent skill directories to remove.
|
|
19
|
+
- Add `apltk uninstall --yes` for non-interactive uninstall confirmation.
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
- Fix default `apltk uninstall` cleanup so a missing OpenClaw workspace no longer prevents uninstalling Codex, Trae, Agents, or Claude Code targets.
|
|
23
|
+
- Remove manifest-tracked historical skills during CLI uninstall so renamed or removed skills do not remain behind.
|
|
24
|
+
- Ignore unsafe manifest skill names during install and uninstall cleanup so removals remain scoped to direct child skill directories.
|
|
25
|
+
|
|
10
26
|
## [v3.3.0] - 2026-04-26
|
|
11
27
|
|
|
12
28
|
### Added
|
package/README.md
CHANGED
|
@@ -82,10 +82,14 @@ The interactive installer:
|
|
|
82
82
|
### Uninstall
|
|
83
83
|
|
|
84
84
|
```bash
|
|
85
|
-
apltk uninstall
|
|
86
|
-
apltk uninstall codex
|
|
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
|
|
87
88
|
```
|
|
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.
|
|
92
|
+
|
|
89
93
|
### Global install
|
|
90
94
|
|
|
91
95
|
```bash
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/lib/cli.js
CHANGED
|
@@ -13,12 +13,13 @@ const {
|
|
|
13
13
|
syncToolkitHome,
|
|
14
14
|
uninstallSkills,
|
|
15
15
|
getTargetRoots,
|
|
16
|
+
getUninstallTargetRoots,
|
|
16
17
|
} = require('./installer');
|
|
17
18
|
const { formatToolList, getToolCommand, runTool } = require('./tool-runner');
|
|
18
19
|
const { checkForPackageUpdate } = require('./updater');
|
|
19
20
|
|
|
20
21
|
const TARGET_OPTIONS = [
|
|
21
|
-
{ id: 'all', label: 'All', description: '
|
|
22
|
+
{ id: 'all', label: 'All', description: 'Select every supported target below' },
|
|
22
23
|
...TARGET_DEFINITIONS,
|
|
23
24
|
];
|
|
24
25
|
|
|
@@ -137,7 +138,7 @@ function buildHelpText({ version, colorEnabled }) {
|
|
|
137
138
|
'Usage:',
|
|
138
139
|
` apltk [install] [${buildModeUsagePattern()}]...`,
|
|
139
140
|
` apollo-toolkit [install] [${buildModeUsagePattern()}]...`,
|
|
140
|
-
|
|
141
|
+
` apltk uninstall [${buildModeUsagePattern()}]... [--yes]`,
|
|
141
142
|
' apltk tools',
|
|
142
143
|
' apltk <tool> [...args]',
|
|
143
144
|
' apltk tools <tool> [...args]',
|
|
@@ -148,6 +149,7 @@ function buildHelpText({ version, colorEnabled }) {
|
|
|
148
149
|
' apltk',
|
|
149
150
|
' apltk codex openclaw',
|
|
150
151
|
' apltk uninstall',
|
|
152
|
+
' apltk uninstall codex agents --yes',
|
|
151
153
|
' npx @laitszkin/apollo-toolkit',
|
|
152
154
|
' npx @laitszkin/apollo-toolkit codex openclaw',
|
|
153
155
|
' npm i -g @laitszkin/apollo-toolkit',
|
|
@@ -164,6 +166,7 @@ function buildHelpText({ version, colorEnabled }) {
|
|
|
164
166
|
'',
|
|
165
167
|
'Options:',
|
|
166
168
|
' --home <path> Override Apollo Toolkit home directory',
|
|
169
|
+
' --yes, -y Skip uninstall confirmation',
|
|
167
170
|
' --help Show this help text',
|
|
168
171
|
].join('\n');
|
|
169
172
|
}
|
|
@@ -200,16 +203,24 @@ function parseArguments(argv) {
|
|
|
200
203
|
toolName: null,
|
|
201
204
|
toolArgs: [],
|
|
202
205
|
linkMode: null, // 'copy' | 'symlink' | null (prompt)
|
|
206
|
+
assumeYes: false,
|
|
203
207
|
};
|
|
204
208
|
|
|
205
209
|
if (args[0] === 'uninstall') {
|
|
206
210
|
result.command = 'uninstall';
|
|
207
211
|
args.shift();
|
|
208
|
-
// remaining args could be specific modes
|
|
209
212
|
while (args.length > 0) {
|
|
210
213
|
const arg = args.shift();
|
|
211
214
|
if (arg === '--help' || arg === '-h') {
|
|
212
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);
|
|
213
224
|
} else {
|
|
214
225
|
result.modes.push(arg);
|
|
215
226
|
}
|
|
@@ -281,13 +292,21 @@ function clearScreen(output) {
|
|
|
281
292
|
}
|
|
282
293
|
}
|
|
283
294
|
|
|
284
|
-
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
|
+
}) {
|
|
285
304
|
const colorEnabled = supportsColor(output, env);
|
|
286
305
|
const allSelected = VALID_MODES.every((mode) => selected.has(mode));
|
|
287
306
|
|
|
288
307
|
clearScreen(output);
|
|
289
308
|
output.write(`${buildBanner({ version, colorEnabled })}\n\n`);
|
|
290
|
-
output.write(
|
|
309
|
+
output.write(`${intro}\n`);
|
|
291
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`);
|
|
292
311
|
output.write(`Press ${color('a', '1;33', colorEnabled)} to toggle all, ${color('q', '1;33', colorEnabled)} to cancel.\n\n`);
|
|
293
312
|
|
|
@@ -308,13 +327,11 @@ function renderSelectionScreen({ output, version, cursor, selected, message, env
|
|
|
308
327
|
}
|
|
309
328
|
}
|
|
310
329
|
|
|
311
|
-
async function
|
|
330
|
+
async function promptForSelectableModes({ stdin, stdout, version, env, intro, ttyError, cancelMessage }) {
|
|
312
331
|
if (!stdin.isTTY || !stdout.isTTY) {
|
|
313
|
-
throw new Error(
|
|
332
|
+
throw new Error(ttyError);
|
|
314
333
|
}
|
|
315
334
|
|
|
316
|
-
await animateWelcomeScreen({ output: stdout, version, env });
|
|
317
|
-
|
|
318
335
|
return new Promise((resolve, reject) => {
|
|
319
336
|
let cursor = 0;
|
|
320
337
|
let message = '';
|
|
@@ -348,48 +365,48 @@ async function promptForModes({ stdin, stdout, version, env }) {
|
|
|
348
365
|
const value = chunk.toString('utf8');
|
|
349
366
|
if (value === '') {
|
|
350
367
|
cleanup();
|
|
351
|
-
reject(new Error(
|
|
368
|
+
reject(new Error(cancelMessage));
|
|
352
369
|
return;
|
|
353
370
|
}
|
|
354
371
|
|
|
355
372
|
if (value === '[A' || value === 'k') {
|
|
356
373
|
cursor = (cursor - 1 + TARGET_OPTIONS.length) % TARGET_OPTIONS.length;
|
|
357
374
|
message = '';
|
|
358
|
-
renderSelectionScreen({ output: stdout, version, cursor, selected, message, env });
|
|
375
|
+
renderSelectionScreen({ output: stdout, version, cursor, selected, message, env, intro });
|
|
359
376
|
return;
|
|
360
377
|
}
|
|
361
378
|
|
|
362
379
|
if (value === '[B' || value === 'j') {
|
|
363
380
|
cursor = (cursor + 1) % TARGET_OPTIONS.length;
|
|
364
381
|
message = '';
|
|
365
|
-
renderSelectionScreen({ output: stdout, version, cursor, selected, message, env });
|
|
382
|
+
renderSelectionScreen({ output: stdout, version, cursor, selected, message, env, intro });
|
|
366
383
|
return;
|
|
367
384
|
}
|
|
368
385
|
|
|
369
386
|
if (value === ' ') {
|
|
370
387
|
toggleMode(TARGET_OPTIONS[cursor].id);
|
|
371
388
|
message = '';
|
|
372
|
-
renderSelectionScreen({ output: stdout, version, cursor, selected, message, env });
|
|
389
|
+
renderSelectionScreen({ output: stdout, version, cursor, selected, message, env, intro });
|
|
373
390
|
return;
|
|
374
391
|
}
|
|
375
392
|
|
|
376
393
|
if (value.toLowerCase() === 'a') {
|
|
377
394
|
toggleMode('all');
|
|
378
395
|
message = '';
|
|
379
|
-
renderSelectionScreen({ output: stdout, version, cursor, selected, message, env });
|
|
396
|
+
renderSelectionScreen({ output: stdout, version, cursor, selected, message, env, intro });
|
|
380
397
|
return;
|
|
381
398
|
}
|
|
382
399
|
|
|
383
400
|
if (value.toLowerCase() === 'q' || value === '') {
|
|
384
401
|
cleanup();
|
|
385
|
-
reject(new Error(
|
|
402
|
+
reject(new Error(cancelMessage));
|
|
386
403
|
return;
|
|
387
404
|
}
|
|
388
405
|
|
|
389
406
|
if (value === '\r') {
|
|
390
407
|
if (selected.size === 0) {
|
|
391
408
|
message = 'Select at least one target before continuing.';
|
|
392
|
-
renderSelectionScreen({ output: stdout, version, cursor, selected, message, env });
|
|
409
|
+
renderSelectionScreen({ output: stdout, version, cursor, selected, message, env, intro });
|
|
393
410
|
return;
|
|
394
411
|
}
|
|
395
412
|
|
|
@@ -401,7 +418,32 @@ async function promptForModes({ stdin, stdout, version, env }) {
|
|
|
401
418
|
stdin.setRawMode(true);
|
|
402
419
|
stdin.resume();
|
|
403
420
|
stdin.on('data', onData);
|
|
404
|
-
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.',
|
|
405
447
|
});
|
|
406
448
|
}
|
|
407
449
|
|
|
@@ -525,7 +567,7 @@ function printUninstallSummary({ stdout, uninstallResult, env }) {
|
|
|
525
567
|
stdout.write('\n\n');
|
|
526
568
|
for (const result of uninstallResult) {
|
|
527
569
|
stdout.write(`${color(result.target, '1', colorEnabled)} (${result.root})\n`);
|
|
528
|
-
stdout.write(` Removed: ${result.removedSkills.join(', ')}\n`);
|
|
570
|
+
stdout.write(` Removed: ${result.removedSkills.length > 0 ? result.removedSkills.join(', ') : '(manifest only)'}\n`);
|
|
529
571
|
}
|
|
530
572
|
}
|
|
531
573
|
|
|
@@ -563,17 +605,29 @@ async function run(argv, context = {}) {
|
|
|
563
605
|
// --- Uninstall flow ---
|
|
564
606
|
if (parsed.command === 'uninstall') {
|
|
565
607
|
const toolkitHome = parsed.toolkitHome || resolveToolkitHome(env);
|
|
566
|
-
const modes = parsed.modes.length > 0
|
|
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);
|
|
567
615
|
|
|
568
616
|
// Show what will be removed
|
|
569
|
-
const allKnown = await listAllKnownSkillNames({ toolkitHome, modes:
|
|
617
|
+
const allKnown = await listAllKnownSkillNames({ toolkitHome, modes: modesForLookup, env });
|
|
570
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
|
+
}
|
|
571
625
|
|
|
572
|
-
const confirmed = await promptYesNo({
|
|
626
|
+
const confirmed = parsed.assumeYes || await promptYesNo({
|
|
573
627
|
stdin,
|
|
574
628
|
stdout,
|
|
575
629
|
env,
|
|
576
|
-
question: `This will remove
|
|
630
|
+
question: `This will remove Apollo Toolkit-installed skills${modes ? ` from: ${modes.join(', ')}` : ' from all targets'}. Continue?`,
|
|
577
631
|
defaultYes: false,
|
|
578
632
|
});
|
|
579
633
|
|
|
@@ -688,6 +742,7 @@ module.exports = {
|
|
|
688
742
|
buildToolsHelp,
|
|
689
743
|
parseArguments,
|
|
690
744
|
promptForModes,
|
|
745
|
+
promptForUninstallModes,
|
|
691
746
|
promptSymlinkChoice,
|
|
692
747
|
promptIncludeExclusiveSkills,
|
|
693
748
|
readPackageJson,
|
package/lib/installer.js
CHANGED
|
@@ -122,6 +122,25 @@ async function readManifest(targetRoot) {
|
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
+
function isSafeSkillName(skillName) {
|
|
126
|
+
return typeof skillName === 'string'
|
|
127
|
+
&& skillName.length > 0
|
|
128
|
+
&& !skillName.includes('\0')
|
|
129
|
+
&& !skillName.includes('/')
|
|
130
|
+
&& !skillName.includes('\\')
|
|
131
|
+
&& !path.isAbsolute(skillName)
|
|
132
|
+
&& !path.win32.isAbsolute(skillName)
|
|
133
|
+
&& skillName !== '.'
|
|
134
|
+
&& skillName !== '..';
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function getManifestSkillNames(manifest) {
|
|
138
|
+
return [...new Set([
|
|
139
|
+
...(Array.isArray(manifest.historicalSkills) ? manifest.historicalSkills : []),
|
|
140
|
+
...(Array.isArray(manifest.skills) ? manifest.skills : []),
|
|
141
|
+
])].filter(isSafeSkillName).sort();
|
|
142
|
+
}
|
|
143
|
+
|
|
125
144
|
// Write manifest to a target directory.
|
|
126
145
|
async function writeManifest(targetRoot, { version, linkMode, skills, previousSkills = [] }) {
|
|
127
146
|
const historicalSkills = [...new Set([...previousSkills, ...skills])].sort();
|
|
@@ -145,17 +164,17 @@ async function listAllKnownSkillNames({ toolkitHome, modes = [], env = process.e
|
|
|
145
164
|
const allNames = new Set();
|
|
146
165
|
|
|
147
166
|
// Current skills from toolkit home
|
|
148
|
-
const currentSkills = await listSkillNames(toolkitHome, modes);
|
|
167
|
+
const currentSkills = await listSkillNames(toolkitHome, modes).catch(() => []);
|
|
149
168
|
for (const name of currentSkills) {
|
|
150
169
|
allNames.add(name);
|
|
151
170
|
}
|
|
152
171
|
|
|
153
172
|
// Historical skills from all target manifests
|
|
154
|
-
const targets = await
|
|
173
|
+
const targets = await getUninstallTargetRoots(modes, env);
|
|
155
174
|
for (const target of targets) {
|
|
156
175
|
const manifest = await readManifest(target.root);
|
|
157
176
|
if (manifest && manifest.historicalSkills) {
|
|
158
|
-
for (const name of manifest
|
|
177
|
+
for (const name of getManifestSkillNames(manifest)) {
|
|
159
178
|
allNames.add(name);
|
|
160
179
|
}
|
|
161
180
|
}
|
|
@@ -343,6 +362,21 @@ async function getTargetRoots(modes, env = process.env) {
|
|
|
343
362
|
return targets;
|
|
344
363
|
}
|
|
345
364
|
|
|
365
|
+
async function getUninstallTargetRoots(modes = VALID_MODES, env = process.env) {
|
|
366
|
+
const targets = [];
|
|
367
|
+
|
|
368
|
+
for (const mode of normalizeModes(modes)) {
|
|
369
|
+
try {
|
|
370
|
+
targets.push(...await getTargetRoots([mode], env));
|
|
371
|
+
} catch {
|
|
372
|
+
// Uninstall is best-effort across agents. A missing OpenClaw workspace
|
|
373
|
+
// must not prevent cleanup from Codex, Trae, Agents, or Claude Code.
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return targets;
|
|
378
|
+
}
|
|
379
|
+
|
|
346
380
|
async function ensureDirectory(dirPath) {
|
|
347
381
|
await fsp.mkdir(dirPath, { recursive: true });
|
|
348
382
|
}
|
|
@@ -387,8 +421,8 @@ async function installLinks({ toolkitHome, modes, env = process.env, previousSki
|
|
|
387
421
|
// Read existing manifest to carry forward historical skills
|
|
388
422
|
const existingManifest = await readManifest(target.root);
|
|
389
423
|
const allPreviousSkills = existingManifest
|
|
390
|
-
? [...new Set([...existingManifest
|
|
391
|
-
: previousSkillNames;
|
|
424
|
+
? [...new Set([...getManifestSkillNames(existingManifest), ...previousSkillNames.filter(isSafeSkillName)])]
|
|
425
|
+
: previousSkillNames.filter(isSafeSkillName);
|
|
392
426
|
|
|
393
427
|
const staleSkillNames = allPreviousSkills.filter(
|
|
394
428
|
(skillName) => !targetSkillNames.includes(skillName),
|
|
@@ -435,17 +469,18 @@ async function installLinks({ toolkitHome, modes, env = process.env, previousSki
|
|
|
435
469
|
// Uninstall all skills from all target directories that have manifests.
|
|
436
470
|
async function uninstallSkills({ env = process.env, modes = null } = {}) {
|
|
437
471
|
const normalizedModes = modes ? normalizeModes(modes) : VALID_MODES;
|
|
438
|
-
const targets = await
|
|
472
|
+
const targets = await getUninstallTargetRoots(normalizedModes, env);
|
|
439
473
|
const results = [];
|
|
440
474
|
|
|
441
475
|
for (const target of targets) {
|
|
442
476
|
const manifest = await readManifest(target.root);
|
|
443
|
-
if (!manifest
|
|
477
|
+
if (!manifest) {
|
|
444
478
|
continue;
|
|
445
479
|
}
|
|
446
480
|
|
|
481
|
+
const skillNames = getManifestSkillNames(manifest);
|
|
447
482
|
const removedSkills = [];
|
|
448
|
-
for (const skillName of
|
|
483
|
+
for (const skillName of skillNames) {
|
|
449
484
|
const skillPath = path.join(target.root, skillName);
|
|
450
485
|
try {
|
|
451
486
|
await fsp.rm(skillPath, { recursive: true, force: true });
|
|
@@ -462,13 +497,11 @@ async function uninstallSkills({ env = process.env, modes = null } = {}) {
|
|
|
462
497
|
// ok if already gone
|
|
463
498
|
}
|
|
464
499
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
});
|
|
471
|
-
}
|
|
500
|
+
results.push({
|
|
501
|
+
target: target.label,
|
|
502
|
+
root: target.root,
|
|
503
|
+
removedSkills,
|
|
504
|
+
});
|
|
472
505
|
}
|
|
473
506
|
|
|
474
507
|
return results;
|
|
@@ -480,6 +513,7 @@ module.exports = {
|
|
|
480
513
|
VALID_MODES,
|
|
481
514
|
MANIFEST_FILENAME,
|
|
482
515
|
getTargetRoots,
|
|
516
|
+
getUninstallTargetRoots,
|
|
483
517
|
installLinks,
|
|
484
518
|
listAllKnownSkillNames,
|
|
485
519
|
listCodexSkillNames,
|
|
Binary file
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/version-release/SKILL.md
CHANGED
|
@@ -15,7 +15,7 @@ description: "Guide the agent to prepare and publish a versioned release (versio
|
|
|
15
15
|
## Standards
|
|
16
16
|
|
|
17
17
|
- Evidence: Inspect the active change set, current version files, existing tag format, existing remote tags/releases, and root `CHANGELOG.md` `Unreleased` content before touching version files, tags, or release metadata.
|
|
18
|
-
- Execution: Use this workflow only for explicit release intent, run the required quality gates when applicable, and treat every conditional gate whose scenario is met as blocking before versioning or publication; hand the repository to `submission-readiness-check` before versioning work, invoke `archive-specs` directly whenever completed plan sets should be converted or project docs need alignment, and if the worktree is already clean inspect the current version, local/remote tag state, and existing GitHub release state before deciding whether the request is already satisfied; when the user explicitly wants the same prerelease version to point at newer fixes, retarget the existing prerelease tag and GitHub release instead of inventing an extra version bump; when editing an existing GitHub prerelease during that retarget flow, use a GitHub-accepted release target such as the intended branch name if the tool or API rejects a raw commit SHA for `target_commitish`; otherwise cut the release directly from `CHANGELOG.md` `Unreleased`, update versions and docs, commit, tag, push, and publish the GitHub release with actual release tooling rather than PR-surrogate directives; run git mutations sequentially and verify both the branch tip and release tag exist remotely before publishing the GitHub release, and never treat UI git directives such as `::git-stage`, `::git-commit`, or `::git-push` as evidence that the release commit or tag already exists.
|
|
18
|
+
- Execution: Use this workflow only for explicit release intent, run the required quality gates when applicable, and treat every conditional gate whose scenario is met as blocking before versioning or publication; hand the repository to `submission-readiness-check` before versioning work, invoke `archive-specs` directly whenever completed plan sets should be converted or project docs need alignment, and if the worktree is already clean inspect the current version, local/remote tag state, and existing GitHub release state before deciding whether the request is already satisfied; decide whether the GitHub release is a prerelease only from explicit user instruction or explicit repository convention already documented in the request context, never from tag naming alone; when the user explicitly wants the same prerelease version to point at newer fixes, retarget the existing prerelease tag and GitHub release instead of inventing an extra version bump; when editing an existing GitHub prerelease during that retarget flow, use a GitHub-accepted release target such as the intended branch name if the tool or API rejects a raw commit SHA for `target_commitish`; otherwise cut the release directly from `CHANGELOG.md` `Unreleased`, update versions and docs, commit, tag, push, and publish the GitHub release with actual release tooling rather than PR-surrogate directives; run git mutations sequentially and verify both the branch tip and release tag exist remotely before publishing the GitHub release, and never treat UI git directives such as `::git-stage`, `::git-commit`, or `::git-push` as evidence that the release commit or tag already exists.
|
|
19
19
|
- Quality: Never guess versions, align user-facing docs with actual code, do not bypass readiness blockers from `submission-readiness-check`, do not reconstruct release notes from `git diff` when curated changelog content already exists, and do not report release success until the commit, tag, and GitHub release all exist for the same version.
|
|
20
20
|
- Output: Produce a versioned release commit and tag, publish a matching GitHub release, and keep changelog plus relevant repository documentation synchronized.
|
|
21
21
|
|
|
@@ -69,6 +69,7 @@ Load only when needed:
|
|
|
69
69
|
- Read existing version files (for example `project.toml`, `package.json`, or repo-specific version files).
|
|
70
70
|
- Infer existing tag format (`vX.Y.Z` or `X.Y.Z`) from repository tags.
|
|
71
71
|
- Inspect existing local and remote tags plus any existing GitHub Release for the target version before creating new release metadata, so duplicate or conflicting releases are caught early.
|
|
72
|
+
- Determine release channel explicitly before publishing: if the user did not say `prerelease`, `draft`, `latest`, or an equivalent repository-specific release state, default to asking or to the repository's already-documented convention instead of inferring from the tag text.
|
|
72
73
|
- If the user explicitly asks to keep the same prerelease version or to `repoint`, `retarget`, or `move` an existing prerelease after follow-up fixes, treat that as a retarget flow: keep the version unchanged, confirm the existing prerelease tag/release name, and plan to move that tag/release to the new commit instead of bumping semver.
|
|
73
74
|
- If the requested version tag and matching published GitHub release already exist and point at the intended commit, report that the release is already complete instead of creating duplicate metadata.
|
|
74
75
|
- If the user provides the target version, use it directly.
|
|
@@ -101,6 +102,7 @@ Load only when needed:
|
|
|
101
102
|
- If any git step finishes ambiguously or the remote hashes do not match local state, rerun the missing step sequentially and re-check before publishing the GitHub release.
|
|
102
103
|
10. Publish the GitHub release
|
|
103
104
|
- Create a non-draft GitHub release that matches the pushed version tag.
|
|
105
|
+
- Set the GitHub release's prerelease flag only when the user explicitly asked for a prerelease or an already-verified repository convention for this exact release channel requires it.
|
|
104
106
|
- For prerelease retarget flows, update the existing GitHub prerelease so it points at the rewritten tag/commit and refresh its notes when the new fix changes the shipped behavior.
|
|
105
107
|
- If the release tool rejects a raw commit SHA while updating `target_commitish`, retry with the authoritative branch name or another GitHub-accepted target identifier, then verify the published release now resolves to the rewritten tag.
|
|
106
108
|
- Use the release notes from the new `CHANGELOG.md` entry unless the repository has a stronger established release-note source.
|