@laitszkin/apollo-toolkit 3.1.0 → 3.1.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/CHANGELOG.md CHANGED
@@ -7,6 +7,12 @@ All notable changes to this repository are documented in this file.
7
7
  ### Changed
8
8
  - None yet.
9
9
 
10
+ ## [v3.1.1] - 2026-04-22
11
+
12
+ ### Changed
13
+ - Fix Apollo Toolkit installers so `codex`-only skills stay scoped to Codex targets, while shared skills continue to install across the selected destinations.
14
+ - Align the CLI welcome/help text, non-interactive guidance, and README examples with the supported `agents` target and current installer behavior.
15
+
10
16
  ## [v3.1.0] - 2026-04-22
11
17
 
12
18
  ### Added
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Apollo Toolkit Skills
2
2
 
3
- A curated skill catalog for Codex, OpenClaw, Trae, and Claude Code with a managed installer that keeps the toolkit in `~/.apollo-toolkit` and copies each skill into the targets you choose.
3
+ A curated skill catalog for Codex, OpenClaw, Trae, Agents, and Claude Code with a managed installer that keeps the toolkit in `~/.apollo-toolkit` and copies each skill into the targets you choose.
4
4
 
5
5
  ## Included skills
6
6
 
@@ -63,7 +63,7 @@ npx @laitszkin/apollo-toolkit
63
63
  The interactive installer:
64
64
  - shows a branded `Apollo Toolkit` terminal welcome screen with a short staged reveal
65
65
  - installs a managed copy into `~/.apollo-toolkit`
66
- - lets you multi-select `codex`, `openclaw`, `trae`, `claude-code`, or `all`
66
+ - lets you multi-select `codex`, `openclaw`, `trae`, `agents`, `claude-code`, or `all`
67
67
  - copies `~/.apollo-toolkit/<skill>` into each selected target
68
68
  - removes stale previously installed skill directories that existed in the previous installed version but no longer exist in the current package skill list
69
69
  - replaces legacy symlink-based installs created by older Apollo Toolkit installers with real copied directories
@@ -91,6 +91,7 @@ apltk open-github-issue --help
91
91
 
92
92
  ```bash
93
93
  npx @laitszkin/apollo-toolkit codex
94
+ npx @laitszkin/apollo-toolkit agents
94
95
  npx @laitszkin/apollo-toolkit claude-code
95
96
  npx @laitszkin/apollo-toolkit codex openclaw
96
97
  npx @laitszkin/apollo-toolkit all
@@ -103,6 +104,7 @@ APOLLO_TOOLKIT_HOME=~/custom-toolkit npx @laitszkin/apollo-toolkit codex
103
104
  CODEX_SKILLS_DIR=~/custom-codex-skills npx @laitszkin/apollo-toolkit codex
104
105
  OPENCLAW_HOME=~/.openclaw npx @laitszkin/apollo-toolkit openclaw
105
106
  TRAE_SKILLS_DIR=~/.trae/skills npx @laitszkin/apollo-toolkit trae
107
+ AGENTS_SKILLS_DIR=~/.agents/skills npx @laitszkin/apollo-toolkit agents
106
108
  CLAUDE_CODE_SKILLS_DIR=~/.claude/skills npx @laitszkin/apollo-toolkit claude-code
107
109
  ```
108
110
 
@@ -120,12 +122,14 @@ Installers still live in `scripts/` for local repository usage and curl / iwr in
120
122
  ./scripts/install_skills.sh codex
121
123
  ./scripts/install_skills.sh openclaw
122
124
  ./scripts/install_skills.sh trae
125
+ ./scripts/install_skills.sh agents
123
126
  ./scripts/install_skills.sh all
124
127
  ```
125
128
 
126
129
  ```powershell
127
130
  ./scripts/install_skills.ps1
128
131
  ./scripts/install_skills.ps1 codex
132
+ ./scripts/install_skills.ps1 agents
129
133
  ./scripts/install_skills.ps1 all
130
134
  ```
131
135
 
package/lib/cli.js CHANGED
@@ -3,6 +3,7 @@ const fs = require('node:fs');
3
3
  const path = require('node:path');
4
4
 
5
5
  const {
6
+ TARGET_DEFINITIONS,
6
7
  VALID_MODES,
7
8
  installLinks,
8
9
  normalizeModes,
@@ -15,11 +16,7 @@ const { checkForPackageUpdate } = require('./updater');
15
16
 
16
17
  const TARGET_OPTIONS = [
17
18
  { id: 'all', label: 'All', description: 'Install every supported target below' },
18
- { id: 'codex', label: 'Codex', description: '~/.codex/skills' },
19
- { id: 'openclaw', label: 'OpenClaw', description: '~/.openclaw/workspace*/skills' },
20
- { id: 'trae', label: 'Trae', description: '~/.trae/skills' },
21
- { id: 'agents', label: 'Agents', description: '~/.agents/skills' },
22
- { id: 'claude-code', label: 'Claude Code', description: '~/.claude/skills' },
19
+ ...TARGET_DEFINITIONS,
23
20
  ];
24
21
 
25
22
  const WORDMARK_LINES = [
@@ -64,6 +61,22 @@ function buildBanner({ version, colorEnabled }) {
64
61
  ].join('\n');
65
62
  }
66
63
 
64
+ function buildModeUsagePattern() {
65
+ return `${VALID_MODES.join('|')}|all`;
66
+ }
67
+
68
+ function buildInteractiveModeHint() {
69
+ const quotedModes = [...VALID_MODES, 'all'].map((mode) => `\`${mode}\``);
70
+ return `${quotedModes.slice(0, -1).join(', ')}, or ${quotedModes.at(-1)}`;
71
+ }
72
+
73
+ function buildSupportedTargetLines({ colorEnabled }) {
74
+ const labelWidth = TARGET_DEFINITIONS.reduce((max, target) => Math.max(max, target.label.length), 0);
75
+ return TARGET_DEFINITIONS.map((target) => (
76
+ ` ${color(target.label.padEnd(labelWidth, ' '), '1', colorEnabled)} ${target.description}`
77
+ )).join('\n');
78
+ }
79
+
67
80
  function buildWelcomeScreen({ version, colorEnabled, stage = 4 }) {
68
81
  const lines = [buildBanner({ version, colorEnabled })];
69
82
 
@@ -90,11 +103,7 @@ function buildWelcomeScreen({ version, colorEnabled, stage = 4 }) {
90
103
  lines.push(
91
104
  '',
92
105
  color('Supported targets:', '2', colorEnabled),
93
- ` ${color('Codex', '1', colorEnabled)} ~/.codex/skills`,
94
- ` ${color('OpenClaw', '1', colorEnabled)} ~/.openclaw/workspace*/skills`,
95
- ` ${color('Trae', '1', colorEnabled)} ~/.trae/skills`,
96
- ` ${color('Agents', '1', colorEnabled)} ~/.agents/skills`,
97
- ` ${color('Claude Code', '1', colorEnabled)} ~/.claude/skills`,
106
+ buildSupportedTargetLines({ colorEnabled }),
98
107
  );
99
108
  }
100
109
 
@@ -123,8 +132,8 @@ function buildHelpText({ version, colorEnabled }) {
123
132
  buildBanner({ version, colorEnabled }),
124
133
  '',
125
134
  'Usage:',
126
- ' apltk [install] [codex|openclaw|trae|agents|claude-code|all]...',
127
- ' apollo-toolkit [install] [codex|openclaw|trae|agents|claude-code|all]...',
135
+ ` apltk [install] [${buildModeUsagePattern()}]...`,
136
+ ` apollo-toolkit [install] [${buildModeUsagePattern()}]...`,
128
137
  ' apltk tools',
129
138
  ' apltk <tool> [...args]',
130
139
  ' apltk tools <tool> [...args]',
@@ -270,7 +279,7 @@ function renderSelectionScreen({ output, version, cursor, selected, message, env
270
279
 
271
280
  async function promptForModes({ stdin, stdout, version, env }) {
272
281
  if (!stdin.isTTY || !stdout.isTTY) {
273
- throw new Error('Interactive install requires a TTY. Re-run with targets like `codex`, `openclaw`, `trae`, `claude-code`, or `all`.');
282
+ throw new Error(`Interactive install requires a TTY. Re-run with targets like ${buildInteractiveModeHint()}.`);
274
283
  }
275
284
 
276
285
  await animateWelcomeScreen({ output: stdout, version, env });
package/lib/installer.js CHANGED
@@ -3,7 +3,14 @@ const fsp = require('node:fs/promises');
3
3
  const os = require('node:os');
4
4
  const path = require('node:path');
5
5
 
6
- const VALID_MODES = ['codex', 'openclaw', 'trae', 'agents', 'claude-code'];
6
+ const TARGET_DEFINITIONS = Object.freeze([
7
+ { id: 'codex', label: 'Codex', description: '~/.codex/skills' },
8
+ { id: 'openclaw', label: 'OpenClaw', description: '~/.openclaw/workspace*/skills' },
9
+ { id: 'trae', label: 'Trae', description: '~/.trae/skills' },
10
+ { id: 'agents', label: 'Agents', description: '~/.agents/skills' },
11
+ { id: 'claude-code', label: 'Claude Code', description: '~/.claude/skills' },
12
+ ]);
13
+ const VALID_MODES = TARGET_DEFINITIONS.map(({ id }) => id);
7
14
  const COPY_FILES = new Set(['AGENTS.md', 'CHANGELOG.md', 'LICENSE', 'README.md', 'package.json']);
8
15
  const COPY_DIRS = new Set(['scripts']);
9
16
 
@@ -63,7 +70,7 @@ function normalizeModes(inputModes) {
63
70
 
64
71
  async function listSkillNames(rootDir, modes = []) {
65
72
  const entries = await fsp.readdir(rootDir, { withFileTypes: true });
66
- const skillNames = [];
73
+ const skillNames = new Set();
67
74
 
68
75
  for (const entry of entries) {
69
76
  if (!entry.isDirectory()) {
@@ -71,7 +78,7 @@ async function listSkillNames(rootDir, modes = []) {
71
78
  }
72
79
 
73
80
  if (fs.existsSync(path.join(rootDir, entry.name, 'SKILL.md'))) {
74
- skillNames.push(entry.name);
81
+ skillNames.add(entry.name);
75
82
  }
76
83
  }
77
84
 
@@ -82,13 +89,42 @@ async function listSkillNames(rootDir, modes = []) {
82
89
  const codexEntries = await fsp.readdir(codexDir, { withFileTypes: true });
83
90
  for (const entry of codexEntries) {
84
91
  if (entry.isDirectory() && fs.existsSync(path.join(codexDir, entry.name, 'SKILL.md'))) {
85
- skillNames.push(entry.name);
92
+ skillNames.add(entry.name);
86
93
  }
87
94
  }
88
95
  }
89
96
  }
90
97
 
91
- return skillNames.sort();
98
+ return [...skillNames].sort();
99
+ }
100
+
101
+ async function listCodexSkillNames(rootDir) {
102
+ const codexDir = path.join(rootDir, 'codex');
103
+ if (!fs.existsSync(codexDir)) {
104
+ return [];
105
+ }
106
+
107
+ const entries = await fsp.readdir(codexDir, { withFileTypes: true });
108
+ return entries
109
+ .filter((entry) => entry.isDirectory() && fs.existsSync(path.join(codexDir, entry.name, 'SKILL.md')))
110
+ .map((entry) => entry.name)
111
+ .sort();
112
+ }
113
+
114
+ function getTargetSkillNames({ targetMode, sharedSkillNames, codexSkillNames }) {
115
+ if (targetMode !== 'codex') {
116
+ return sharedSkillNames;
117
+ }
118
+
119
+ return [...new Set([...sharedSkillNames, ...codexSkillNames])].sort();
120
+ }
121
+
122
+ function resolveInstallSourcePath({ toolkitHome, targetMode, skillName, codexSkillNames }) {
123
+ if (targetMode === 'codex' && codexSkillNames.includes(skillName)) {
124
+ return path.join(toolkitHome, 'codex', skillName);
125
+ }
126
+
127
+ return path.join(toolkitHome, skillName);
92
128
  }
93
129
 
94
130
  function shouldCopyEntry(sourceRoot, entry) {
@@ -265,24 +301,33 @@ async function replaceWithCopy(sourcePath, targetPath) {
265
301
 
266
302
  async function installLinks({ toolkitHome, modes, env = process.env, previousSkillNames = [] }) {
267
303
  const normalizedModes = normalizeModes(modes);
268
- const skillNames = await listSkillNames(toolkitHome, normalizedModes);
304
+ const sharedSkillNames = await listSkillNames(toolkitHome);
305
+ const codexSkillNames = normalizedModes.includes('codex') ? await listCodexSkillNames(toolkitHome) : [];
306
+ const skillNames = normalizedModes.includes('codex')
307
+ ? [...new Set([...sharedSkillNames, ...codexSkillNames])].sort()
308
+ : sharedSkillNames;
269
309
  const targets = await getTargetRoots(normalizedModes, env);
270
310
  const copiedPaths = [];
271
- const staleSkillNames = previousSkillNames.filter((skillName) => !skillNames.includes(skillName));
272
311
 
273
312
  for (const target of targets) {
313
+ const targetSkillNames = getTargetSkillNames({
314
+ targetMode: target.mode,
315
+ sharedSkillNames,
316
+ codexSkillNames,
317
+ });
318
+ const staleSkillNames = previousSkillNames.filter((skillName) => !targetSkillNames.includes(skillName));
319
+
274
320
  await ensureDirectory(target.root);
275
321
  for (const staleSkillName of staleSkillNames) {
276
322
  await fsp.rm(path.join(target.root, staleSkillName), { recursive: true, force: true });
277
323
  }
278
- for (const skillName of skillNames) {
279
- // For codex skills, use the ./codex/ subdirectory as source
280
- let sourcePath;
281
- if (normalizedModes.includes('codex') && fs.existsSync(path.join(toolkitHome, 'codex', skillName))) {
282
- sourcePath = path.join(toolkitHome, 'codex', skillName);
283
- } else {
284
- sourcePath = path.join(toolkitHome, skillName);
285
- }
324
+ for (const skillName of targetSkillNames) {
325
+ const sourcePath = resolveInstallSourcePath({
326
+ toolkitHome,
327
+ targetMode: target.mode,
328
+ skillName,
329
+ codexSkillNames,
330
+ });
286
331
  const targetPath = path.join(target.root, skillName);
287
332
  await replaceWithCopy(sourcePath, targetPath);
288
333
  copiedPaths.push({ target: target.label, path: targetPath, skillName });
@@ -298,6 +343,7 @@ async function installLinks({ toolkitHome, modes, env = process.env, previousSki
298
343
 
299
344
  module.exports = {
300
345
  expandUserPath,
346
+ TARGET_DEFINITIONS,
301
347
  VALID_MODES,
302
348
  getTargetRoots,
303
349
  installLinks,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@laitszkin/apollo-toolkit",
3
- "version": "3.1.0",
3
+ "version": "3.1.1",
4
4
  "description": "Apollo Toolkit npm installer for managed skill copying across Codex, OpenClaw, and Trae.",
5
5
  "license": "MIT",
6
6
  "author": "LaiTszKin",
@@ -110,15 +110,16 @@ else {
110
110
  $RepoRoot = $ToolkitHome
111
111
  }
112
112
 
113
- function Get-SkillPaths {
113
+ function Get-SkillPathGroups {
114
114
  param([string[]]$SelectedModes)
115
115
 
116
116
  $dirs = Get-ChildItem -Path $RepoRoot -Directory | Sort-Object Name
117
- $skills = @()
117
+ $sharedSkills = @()
118
+ $codexSkills = @()
118
119
 
119
120
  foreach ($dir in $dirs) {
120
121
  if (Test-Path -LiteralPath (Join-Path $dir.FullName "SKILL.md") -PathType Leaf) {
121
- $skills += $dir.FullName
122
+ $sharedSkills += $dir.FullName
122
123
  }
123
124
  }
124
125
 
@@ -129,17 +130,20 @@ function Get-SkillPaths {
129
130
  $codexDirs = Get-ChildItem -Path $codexDir -Directory | Sort-Object Name
130
131
  foreach ($dir in $codexDirs) {
131
132
  if (Test-Path -LiteralPath (Join-Path $dir.FullName "SKILL.md") -PathType Leaf) {
132
- $skills += $dir.FullName
133
+ $codexSkills += $dir.FullName
133
134
  }
134
135
  }
135
136
  }
136
137
  }
137
138
 
138
- if ($skills.Count -eq 0) {
139
+ if ($sharedSkills.Count -eq 0) {
139
140
  throw "No skill folders found in: $RepoRoot"
140
141
  }
141
142
 
142
- return $skills
143
+ [PSCustomObject]@{
144
+ Shared = $sharedSkills
145
+ Codex = $codexSkills
146
+ }
143
147
  }
144
148
 
145
149
  function Add-ModeOnce {
@@ -343,15 +347,15 @@ if ($Modes.Count -gt 0 -and ($Modes[0] -eq "-h" -or $Modes[0] -eq "--help")) {
343
347
  }
344
348
 
345
349
  $selectedModes = Resolve-Modes -Requested $Modes
346
- $skillPaths = Get-SkillPaths -SelectedModes $selectedModes
350
+ $skillPathGroups = Get-SkillPathGroups -SelectedModes $selectedModes
347
351
 
348
352
  foreach ($mode in $selectedModes) {
349
353
  switch ($mode) {
350
- "codex" { Install-Codex -SkillPaths $skillPaths }
351
- "openclaw" { Install-OpenClaw -SkillPaths $skillPaths }
352
- "trae" { Install-Trae -SkillPaths $skillPaths }
353
- "agents" { Install-Agents -SkillPaths $skillPaths }
354
- "claude-code" { Install-ClaudeCode -SkillPaths $skillPaths }
354
+ "codex" { Install-Codex -SkillPaths ($skillPathGroups.Shared + $skillPathGroups.Codex) }
355
+ "openclaw" { Install-OpenClaw -SkillPaths $skillPathGroups.Shared }
356
+ "trae" { Install-Trae -SkillPaths $skillPathGroups.Shared }
357
+ "agents" { Install-Agents -SkillPaths $skillPathGroups.Shared }
358
+ "claude-code" { Install-ClaudeCode -SkillPaths $skillPathGroups.Shared }
355
359
  default { throw "Unknown mode: $mode" }
356
360
  }
357
361
  }
@@ -78,14 +78,16 @@ else
78
78
  SCRIPT_DIR="$REPO_ROOT/scripts"
79
79
  fi
80
80
  SELECTED_MODES=()
81
- SKILL_PATHS=()
81
+ SHARED_SKILL_PATHS=()
82
+ CODEX_SKILL_PATHS=()
82
83
 
83
84
  collect_skills() {
84
85
  local dir
85
- SKILL_PATHS=()
86
+ SHARED_SKILL_PATHS=()
87
+ CODEX_SKILL_PATHS=()
86
88
  while IFS= read -r dir; do
87
89
  if [[ -f "$dir/SKILL.md" ]]; then
88
- SKILL_PATHS+=("$dir")
90
+ SHARED_SKILL_PATHS+=("$dir")
89
91
  fi
90
92
  done < <(find "$REPO_ROOT" -mindepth 1 -maxdepth 1 -type d | sort)
91
93
 
@@ -95,13 +97,13 @@ collect_skills() {
95
97
  if [[ -d "$codex_dir" ]]; then
96
98
  while IFS= read -r dir; do
97
99
  if [[ -f "$dir/SKILL.md" ]]; then
98
- SKILL_PATHS+=("$dir")
100
+ CODEX_SKILL_PATHS+=("$dir")
99
101
  fi
100
102
  done < <(find "$codex_dir" -mindepth 1 -maxdepth 1 -type d | sort)
101
103
  fi
102
104
  fi
103
105
 
104
- if [[ ${#SKILL_PATHS[@]} -eq 0 ]]; then
106
+ if [[ ${#SHARED_SKILL_PATHS[@]} -eq 0 ]]; then
105
107
  echo "No skill folders found in: $REPO_ROOT" >&2
106
108
  exit 1
107
109
  fi
@@ -124,17 +126,17 @@ replace_with_copy() {
124
126
  }
125
127
 
126
128
  install_codex() {
127
- local codex_skills_dir
129
+ local codex_skills_dir src
128
130
  codex_skills_dir="$(expand_user_path "${CODEX_SKILLS_DIR:-$HOME/.codex/skills}")"
129
131
 
130
132
  echo "Installing to codex: $codex_skills_dir"
131
- for src in "${SKILL_PATHS[@]}"; do
133
+ for src in "${SHARED_SKILL_PATHS[@]}" "${CODEX_SKILL_PATHS[@]}"; do
132
134
  replace_with_copy "$src" "$codex_skills_dir"
133
135
  done
134
136
  }
135
137
 
136
138
  install_openclaw() {
137
- local openclaw_home workspace skills_dir
139
+ local openclaw_home workspace skills_dir src
138
140
  local -a workspaces
139
141
 
140
142
  openclaw_home="$(expand_user_path "${OPENCLAW_HOME:-$HOME/.openclaw}")"
@@ -152,38 +154,38 @@ install_openclaw() {
152
154
  for workspace in "${workspaces[@]}"; do
153
155
  skills_dir="$workspace/skills"
154
156
  echo "Installing to openclaw workspace: $skills_dir"
155
- for src in "${SKILL_PATHS[@]}"; do
157
+ for src in "${SHARED_SKILL_PATHS[@]}"; do
156
158
  replace_with_copy "$src" "$skills_dir"
157
159
  done
158
160
  done
159
161
  }
160
162
 
161
163
  install_trae() {
162
- local trae_skills_dir
164
+ local trae_skills_dir src
163
165
  trae_skills_dir="$(expand_user_path "${TRAE_SKILLS_DIR:-$HOME/.trae/skills}")"
164
166
 
165
167
  echo "Installing to trae: $trae_skills_dir"
166
- for src in "${SKILL_PATHS[@]}"; do
168
+ for src in "${SHARED_SKILL_PATHS[@]}"; do
167
169
  replace_with_copy "$src" "$trae_skills_dir"
168
170
  done
169
171
  }
170
172
 
171
173
  install_agents() {
172
- local agents_skills_dir
174
+ local agents_skills_dir src
173
175
  agents_skills_dir="$(expand_user_path "${AGENTS_SKILLS_DIR:-$HOME/.agents/skills}")"
174
176
 
175
177
  echo "Installing to agents: $agents_skills_dir"
176
- for src in "${SKILL_PATHS[@]}"; do
178
+ for src in "${SHARED_SKILL_PATHS[@]}"; do
177
179
  replace_with_copy "$src" "$agents_skills_dir"
178
180
  done
179
181
  }
180
182
 
181
183
  install_claude_code() {
182
- local claude_code_skills_dir
184
+ local claude_code_skills_dir src
183
185
  claude_code_skills_dir="$(expand_user_path "${CLAUDE_CODE_SKILLS_DIR:-$HOME/.claude/skills}")"
184
186
 
185
187
  echo "Installing to claude-code: $claude_code_skills_dir"
186
- for src in "${SKILL_PATHS[@]}"; do
188
+ for src in "${SHARED_SKILL_PATHS[@]}"; do
187
189
  replace_with_copy "$src" "$claude_code_skills_dir"
188
190
  done
189
191
  }
@@ -233,7 +235,7 @@ read_choice_from_user() {
233
235
  elif [[ -r /dev/tty ]]; then
234
236
  read -r -p "$prompt" result < /dev/tty
235
237
  else
236
- echo "Interactive input unavailable. Pass mode arguments (e.g. codex/openclaw/trae/all)." >&2
238
+ echo "Interactive input unavailable. Pass mode arguments (e.g. codex/openclaw/trae/agents/claude-code/all)." >&2
237
239
  exit 1
238
240
  fi
239
241
 
@@ -264,7 +266,7 @@ choose_modes_interactive() {
264
266
  3) add_mode_once "trae" ;;
265
267
  4) add_mode_once "agents" ;;
266
268
  5) add_mode_once "claude-code" ;;
267
- 6) add_mode_once "codex"; add_mode_once "openclaw"; add_mode_once "trae"; add_mode_once "claude-code" ;;
269
+ 6) add_mode_once "codex"; add_mode_once "openclaw"; add_mode_once "trae"; add_mode_once "agents"; add_mode_once "claude-code" ;;
268
270
  *)
269
271
  echo "Invalid choice: $raw_choice" >&2
270
272
  exit 1