skills 1.4.9 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +77 -56
  2. package/dist/cli.mjs +253 -121
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -3,7 +3,9 @@
3
3
  The CLI for the open agent skills ecosystem.
4
4
 
5
5
  <!-- agent-list:start -->
6
+
6
7
  Supports **OpenCode**, **Claude Code**, **Codex**, **Cursor**, and [41 more](#available-agents).
8
+
7
9
  <!-- agent-list:end -->
8
10
 
9
11
  ## Install a Skill
@@ -92,14 +94,13 @@ When installing interactively, you can choose:
92
94
 
93
95
  ## Other Commands
94
96
 
95
- | Command | Description |
96
- | ---------------------------- | ---------------------------------------------- |
97
- | `npx skills list` | List installed skills (alias: `ls`) |
98
- | `npx skills find [query]` | Search for skills interactively or by keyword |
99
- | `npx skills remove [skills]` | Remove installed skills from agents |
100
- | `npx skills check` | Check for available skill updates |
101
- | `npx skills update` | Update all installed skills to latest versions |
102
- | `npx skills init [name]` | Create a new SKILL.md template |
97
+ | Command | Description |
98
+ | ---------------------------- | --------------------------------------------- |
99
+ | `npx skills list` | List installed skills (alias: `ls`) |
100
+ | `npx skills find [query]` | Search for skills interactively or by keyword |
101
+ | `npx skills remove [skills]` | Remove installed skills from agents |
102
+ | `npx skills update [skills]` | Update installed skills to latest versions |
103
+ | `npx skills init [name]` | Create a new SKILL.md template |
103
104
 
104
105
  ### `skills list`
105
106
 
@@ -128,16 +129,33 @@ npx skills find
128
129
  npx skills find typescript
129
130
  ```
130
131
 
131
- ### `skills check` / `skills update`
132
+ ### `skills update`
132
133
 
133
134
  ```bash
134
- # Check if any installed skills have updates
135
- npx skills check
136
-
137
- # Update all skills to latest versions
135
+ # Update all skills (interactive scope prompt)
138
136
  npx skills update
137
+
138
+ # Update a single skill by name
139
+ npx skills update my-skill
140
+
141
+ # Update multiple specific skills
142
+ npx skills update frontend-design web-design-guidelines
143
+
144
+ # Update only global or project skills
145
+ npx skills update -g
146
+ npx skills update -p
147
+
148
+ # Non-interactive (auto-detects scope: project if in a project, else global)
149
+ npx skills update -y
139
150
  ```
140
151
 
152
+ | Option | Description |
153
+ | --------------- | ------------------------------------------------------------------------- |
154
+ | `-g, --global` | Only update global skills |
155
+ | `-p, --project` | Only update project skills |
156
+ | `-y, --yes` | Skip scope prompt (auto-detect: project if in a project dir, else global) |
157
+ | `[skills...]` | Update specific skills by name instead of all |
158
+
141
159
  ### `skills init`
142
160
 
143
161
  ```bash
@@ -207,49 +225,51 @@ Discover skills at **[skills.sh](https://skills.sh)**
207
225
  Skills can be installed to any of these agents:
208
226
 
209
227
  <!-- supported-agents:start -->
210
- | Agent | `--agent` | Project Path | Global Path |
211
- |-------|-----------|--------------|-------------|
212
- | Amp, Kimi Code CLI, Replit, Universal | `amp`, `kimi-cli`, `replit`, `universal` | `.agents/skills/` | `~/.config/agents/skills/` |
213
- | Antigravity | `antigravity` | `.agents/skills/` | `~/.gemini/antigravity/skills/` |
214
- | Augment | `augment` | `.augment/skills/` | `~/.augment/skills/` |
215
- | IBM Bob | `bob` | `.bob/skills/` | `~/.bob/skills/` |
216
- | Claude Code | `claude-code` | `.claude/skills/` | `~/.claude/skills/` |
217
- | OpenClaw | `openclaw` | `skills/` | `~/.openclaw/skills/` |
218
- | Cline, Warp | `cline`, `warp` | `.agents/skills/` | `~/.agents/skills/` |
219
- | CodeBuddy | `codebuddy` | `.codebuddy/skills/` | `~/.codebuddy/skills/` |
220
- | Codex | `codex` | `.agents/skills/` | `~/.codex/skills/` |
221
- | Command Code | `command-code` | `.commandcode/skills/` | `~/.commandcode/skills/` |
222
- | Continue | `continue` | `.continue/skills/` | `~/.continue/skills/` |
223
- | Cortex Code | `cortex` | `.cortex/skills/` | `~/.snowflake/cortex/skills/` |
224
- | Crush | `crush` | `.crush/skills/` | `~/.config/crush/skills/` |
225
- | Cursor | `cursor` | `.agents/skills/` | `~/.cursor/skills/` |
226
- | Deep Agents | `deepagents` | `.agents/skills/` | `~/.deepagents/agent/skills/` |
227
- | Droid | `droid` | `.factory/skills/` | `~/.factory/skills/` |
228
- | Firebender | `firebender` | `.agents/skills/` | `~/.firebender/skills/` |
229
- | Gemini CLI | `gemini-cli` | `.agents/skills/` | `~/.gemini/skills/` |
230
- | GitHub Copilot | `github-copilot` | `.agents/skills/` | `~/.copilot/skills/` |
231
- | Goose | `goose` | `.goose/skills/` | `~/.config/goose/skills/` |
232
- | Junie | `junie` | `.junie/skills/` | `~/.junie/skills/` |
233
- | iFlow CLI | `iflow-cli` | `.iflow/skills/` | `~/.iflow/skills/` |
234
- | Kilo Code | `kilo` | `.kilocode/skills/` | `~/.kilocode/skills/` |
235
- | Kiro CLI | `kiro-cli` | `.kiro/skills/` | `~/.kiro/skills/` |
236
- | Kode | `kode` | `.kode/skills/` | `~/.kode/skills/` |
237
- | MCPJam | `mcpjam` | `.mcpjam/skills/` | `~/.mcpjam/skills/` |
238
- | Mistral Vibe | `mistral-vibe` | `.vibe/skills/` | `~/.vibe/skills/` |
239
- | Mux | `mux` | `.mux/skills/` | `~/.mux/skills/` |
240
- | OpenCode | `opencode` | `.agents/skills/` | `~/.config/opencode/skills/` |
241
- | OpenHands | `openhands` | `.openhands/skills/` | `~/.openhands/skills/` |
242
- | Pi | `pi` | `.pi/skills/` | `~/.pi/agent/skills/` |
243
- | Qoder | `qoder` | `.qoder/skills/` | `~/.qoder/skills/` |
244
- | Qwen Code | `qwen-code` | `.qwen/skills/` | `~/.qwen/skills/` |
245
- | Roo Code | `roo` | `.roo/skills/` | `~/.roo/skills/` |
246
- | Trae | `trae` | `.trae/skills/` | `~/.trae/skills/` |
247
- | Trae CN | `trae-cn` | `.trae/skills/` | `~/.trae-cn/skills/` |
248
- | Windsurf | `windsurf` | `.windsurf/skills/` | `~/.codeium/windsurf/skills/` |
249
- | Zencoder | `zencoder` | `.zencoder/skills/` | `~/.zencoder/skills/` |
250
- | Neovate | `neovate` | `.neovate/skills/` | `~/.neovate/skills/` |
251
- | Pochi | `pochi` | `.pochi/skills/` | `~/.pochi/skills/` |
252
- | AdaL | `adal` | `.adal/skills/` | `~/.adal/skills/` |
228
+
229
+ | Agent | `--agent` | Project Path | Global Path |
230
+ | ------------------------------------- | ---------------------------------------- | ---------------------- | ------------------------------- |
231
+ | Amp, Kimi Code CLI, Replit, Universal | `amp`, `kimi-cli`, `replit`, `universal` | `.agents/skills/` | `~/.config/agents/skills/` |
232
+ | Antigravity | `antigravity` | `.agents/skills/` | `~/.gemini/antigravity/skills/` |
233
+ | Augment | `augment` | `.augment/skills/` | `~/.augment/skills/` |
234
+ | IBM Bob | `bob` | `.bob/skills/` | `~/.bob/skills/` |
235
+ | Claude Code | `claude-code` | `.claude/skills/` | `~/.claude/skills/` |
236
+ | OpenClaw | `openclaw` | `skills/` | `~/.openclaw/skills/` |
237
+ | Cline, Warp | `cline`, `warp` | `.agents/skills/` | `~/.agents/skills/` |
238
+ | CodeBuddy | `codebuddy` | `.codebuddy/skills/` | `~/.codebuddy/skills/` |
239
+ | Codex | `codex` | `.agents/skills/` | `~/.codex/skills/` |
240
+ | Command Code | `command-code` | `.commandcode/skills/` | `~/.commandcode/skills/` |
241
+ | Continue | `continue` | `.continue/skills/` | `~/.continue/skills/` |
242
+ | Cortex Code | `cortex` | `.cortex/skills/` | `~/.snowflake/cortex/skills/` |
243
+ | Crush | `crush` | `.crush/skills/` | `~/.config/crush/skills/` |
244
+ | Cursor | `cursor` | `.agents/skills/` | `~/.cursor/skills/` |
245
+ | Deep Agents | `deepagents` | `.agents/skills/` | `~/.deepagents/agent/skills/` |
246
+ | Droid | `droid` | `.factory/skills/` | `~/.factory/skills/` |
247
+ | Firebender | `firebender` | `.agents/skills/` | `~/.firebender/skills/` |
248
+ | Gemini CLI | `gemini-cli` | `.agents/skills/` | `~/.gemini/skills/` |
249
+ | GitHub Copilot | `github-copilot` | `.agents/skills/` | `~/.copilot/skills/` |
250
+ | Goose | `goose` | `.goose/skills/` | `~/.config/goose/skills/` |
251
+ | Junie | `junie` | `.junie/skills/` | `~/.junie/skills/` |
252
+ | iFlow CLI | `iflow-cli` | `.iflow/skills/` | `~/.iflow/skills/` |
253
+ | Kilo Code | `kilo` | `.kilocode/skills/` | `~/.kilocode/skills/` |
254
+ | Kiro CLI | `kiro-cli` | `.kiro/skills/` | `~/.kiro/skills/` |
255
+ | Kode | `kode` | `.kode/skills/` | `~/.kode/skills/` |
256
+ | MCPJam | `mcpjam` | `.mcpjam/skills/` | `~/.mcpjam/skills/` |
257
+ | Mistral Vibe | `mistral-vibe` | `.vibe/skills/` | `~/.vibe/skills/` |
258
+ | Mux | `mux` | `.mux/skills/` | `~/.mux/skills/` |
259
+ | OpenCode | `opencode` | `.agents/skills/` | `~/.config/opencode/skills/` |
260
+ | OpenHands | `openhands` | `.openhands/skills/` | `~/.openhands/skills/` |
261
+ | Pi | `pi` | `.pi/skills/` | `~/.pi/agent/skills/` |
262
+ | Qoder | `qoder` | `.qoder/skills/` | `~/.qoder/skills/` |
263
+ | Qwen Code | `qwen-code` | `.qwen/skills/` | `~/.qwen/skills/` |
264
+ | Roo Code | `roo` | `.roo/skills/` | `~/.roo/skills/` |
265
+ | Trae | `trae` | `.trae/skills/` | `~/.trae/skills/` |
266
+ | Trae CN | `trae-cn` | `.trae/skills/` | `~/.trae-cn/skills/` |
267
+ | Windsurf | `windsurf` | `.windsurf/skills/` | `~/.codeium/windsurf/skills/` |
268
+ | Zencoder | `zencoder` | `.zencoder/skills/` | `~/.zencoder/skills/` |
269
+ | Neovate | `neovate` | `.neovate/skills/` | `~/.neovate/skills/` |
270
+ | Pochi | `pochi` | `.pochi/skills/` | `~/.pochi/skills/` |
271
+ | AdaL | `adal` | `.adal/skills/` | `~/.adal/skills/` |
272
+
253
273
  <!-- supported-agents:end -->
254
274
 
255
275
  > [!NOTE]
@@ -314,6 +334,7 @@ metadata:
314
334
  The CLI searches for skills in these locations within a repository:
315
335
 
316
336
  <!-- skill-discovery:start -->
337
+
317
338
  - Root directory (if it contains `SKILL.md`)
318
339
  - `skills/`
319
340
  - `skills/.curated/`
package/dist/cli.mjs CHANGED
@@ -7,7 +7,7 @@ import "./_chunks/libs/@kwsites/promise-deferred.mjs";
7
7
  import { t as esm_default } from "./_chunks/libs/simple-git.mjs";
8
8
  import { t as xdgConfig } from "./_chunks/libs/xdg-basedir.mjs";
9
9
  import { execSync, spawnSync } from "child_process";
10
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
10
+ import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "fs";
11
11
  import { basename, dirname, isAbsolute, join, normalize, relative, resolve, sep } from "path";
12
12
  import { homedir, platform, tmpdir } from "os";
13
13
  import { fileURLToPath } from "url";
@@ -2164,7 +2164,7 @@ async function tryBlobInstall(ownerRepo, options = {}) {
2164
2164
  tree
2165
2165
  };
2166
2166
  }
2167
- var version$1 = "1.4.9";
2167
+ var version$1 = "1.5.0";
2168
2168
  const isCancelled$1 = (value) => typeof value === "symbol";
2169
2169
  async function isSourcePrivate(source) {
2170
2170
  const ownerRepo = parseOwnerRepo(source);
@@ -4111,6 +4111,9 @@ function buildUpdateInstallSource(entry) {
4111
4111
  if (entry.ref) installSource = `${installSource}#${entry.ref}`;
4112
4112
  return installSource;
4113
4113
  }
4114
+ function buildLocalUpdateSource(entry) {
4115
+ return formatSourceInput(entry.source, entry.ref);
4116
+ }
4114
4117
  const __dirname = dirname(fileURLToPath(import.meta.url));
4115
4118
  function getVersion() {
4116
4119
  try {
@@ -4158,8 +4161,7 @@ function showBanner() {
4158
4161
  console.log(` ${DIM}$${RESET} ${TEXT}npx skills list${RESET} ${DIM}List installed skills${RESET}`);
4159
4162
  console.log(` ${DIM}$${RESET} ${TEXT}npx skills find ${DIM}[query]${RESET} ${DIM}Search for skills${RESET}`);
4160
4163
  console.log();
4161
- console.log(` ${DIM}$${RESET} ${TEXT}npx skills check${RESET} ${DIM}Check for updates${RESET}`);
4162
- console.log(` ${DIM}$${RESET} ${TEXT}npx skills update${RESET} ${DIM}Update all skills${RESET}`);
4164
+ console.log(` ${DIM}$${RESET} ${TEXT}npx skills update${RESET} ${DIM}Update installed skills${RESET}`);
4163
4165
  console.log();
4164
4166
  console.log(` ${DIM}$${RESET} ${TEXT}npx skills experimental_install${RESET} ${DIM}Restore from skills-lock.json${RESET}`);
4165
4167
  console.log(` ${DIM}$${RESET} ${TEXT}npx skills init ${DIM}[name]${RESET} ${DIM}Create a new skill${RESET}`);
@@ -4183,8 +4185,12 @@ ${BOLD}Manage Skills:${RESET}
4183
4185
  find [query] Search for skills interactively
4184
4186
 
4185
4187
  ${BOLD}Updates:${RESET}
4186
- check Check for available skill updates
4187
- update Update all skills to latest versions
4188
+ update [skills...] Update skills to latest versions (alias: upgrade)
4189
+
4190
+ ${BOLD}Update Options:${RESET}
4191
+ -g, --global Update global skills only
4192
+ -p, --project Update project skills only
4193
+ -y, --yes Skip scope prompt (auto-detect: project if in a project, else global)
4188
4194
 
4189
4195
  ${BOLD}Project:${RESET}
4190
4196
  experimental_install Restore skills from skills-lock.json
@@ -4235,8 +4241,9 @@ ${BOLD}Examples:${RESET}
4235
4241
  ${DIM}$${RESET} skills ls --json ${DIM}# JSON output${RESET}
4236
4242
  ${DIM}$${RESET} skills find ${DIM}# interactive search${RESET}
4237
4243
  ${DIM}$${RESET} skills find typescript ${DIM}# search by keyword${RESET}
4238
- ${DIM}$${RESET} skills check
4239
4244
  ${DIM}$${RESET} skills update
4245
+ ${DIM}$${RESET} skills update my-skill ${DIM}# update a single skill${RESET}
4246
+ ${DIM}$${RESET} skills update -g ${DIM}# update global skills only${RESET}
4240
4247
  ${DIM}$${RESET} skills experimental_install ${DIM}# restore from skills-lock.json${RESET}
4241
4248
  ${DIM}$${RESET} skills init my-skill
4242
4249
  ${DIM}$${RESET} skills experimental_sync ${DIM}# sync from node_modules${RESET}
@@ -4351,127 +4358,144 @@ function readSkillLock() {
4351
4358
  };
4352
4359
  }
4353
4360
  }
4361
+ function parseUpdateOptions(args) {
4362
+ const options = {};
4363
+ const positional = [];
4364
+ for (const arg of args) if (arg === "-g" || arg === "--global") options.global = true;
4365
+ else if (arg === "-p" || arg === "--project") options.project = true;
4366
+ else if (arg === "-y" || arg === "--yes") options.yes = true;
4367
+ else if (!arg.startsWith("-")) positional.push(arg);
4368
+ if (positional.length > 0) options.skills = positional;
4369
+ return options;
4370
+ }
4371
+ function hasProjectSkills(cwd) {
4372
+ const dir = cwd || process.cwd();
4373
+ if (existsSync(join(dir, "skills-lock.json"))) return true;
4374
+ const skillsDir = join(dir, ".agents", "skills");
4375
+ try {
4376
+ const entries = readdirSync(skillsDir, { withFileTypes: true });
4377
+ for (const entry of entries) if (entry.isDirectory()) {
4378
+ if (existsSync(join(skillsDir, entry.name, "SKILL.md"))) return true;
4379
+ }
4380
+ } catch {}
4381
+ return false;
4382
+ }
4383
+ async function resolveUpdateScope(options) {
4384
+ if (options.skills && options.skills.length > 0) {
4385
+ if (options.global) return "global";
4386
+ if (options.project) return "project";
4387
+ return "both";
4388
+ }
4389
+ if (options.global && options.project) return "both";
4390
+ if (options.global) return "global";
4391
+ if (options.project) return "project";
4392
+ if (options.yes || !process.stdin.isTTY) return hasProjectSkills() ? "project" : "global";
4393
+ const scope = await ve({
4394
+ message: "Update scope",
4395
+ options: [
4396
+ {
4397
+ value: "project",
4398
+ label: "Project",
4399
+ hint: "Update skills in current directory"
4400
+ },
4401
+ {
4402
+ value: "global",
4403
+ label: "Global",
4404
+ hint: "Update skills in home directory"
4405
+ },
4406
+ {
4407
+ value: "both",
4408
+ label: "Both",
4409
+ hint: "Update all skills"
4410
+ }
4411
+ ]
4412
+ });
4413
+ if (pD(scope)) {
4414
+ xe("Cancelled");
4415
+ process.exit(0);
4416
+ }
4417
+ return scope;
4418
+ }
4419
+ function matchesSkillFilter(name, filter) {
4420
+ if (!filter || filter.length === 0) return true;
4421
+ const lower = name.toLowerCase();
4422
+ return filter.some((f) => f.toLowerCase() === lower);
4423
+ }
4354
4424
  function getSkipReason(entry) {
4355
4425
  if (entry.sourceType === "local") return "Local path";
4356
- if (entry.sourceType === "git") return "Git URL (hash tracking not supported)";
4357
- if (!entry.skillFolderHash) return "No version hash available";
4426
+ if (entry.sourceType === "git") return "Git URL";
4427
+ if (entry.sourceType === "well-known") return "Well-known skill";
4428
+ if (!entry.skillFolderHash) return "Private or deleted repo";
4358
4429
  if (!entry.skillPath) return "No skill path recorded";
4359
4430
  return "No version tracking";
4360
4431
  }
4432
+ function getInstallSource(skill) {
4433
+ let url = skill.sourceUrl;
4434
+ if (skill.sourceType === "well-known") {
4435
+ const idx = url.indexOf("/.well-known/");
4436
+ if (idx !== -1) url = url.slice(0, idx);
4437
+ }
4438
+ return formatSourceInput(url, skill.ref);
4439
+ }
4361
4440
  function printSkippedSkills(skipped) {
4362
4441
  if (skipped.length === 0) return;
4363
4442
  console.log();
4364
4443
  console.log(`${DIM}${skipped.length} skill(s) cannot be checked automatically:${RESET}`);
4444
+ const grouped = /* @__PURE__ */ new Map();
4365
4445
  for (const skill of skipped) {
4366
- console.log(` ${TEXT}•${RESET} ${skill.name} ${DIM}(${skill.reason})${RESET}`);
4367
- console.log(` ${DIM}To update: ${TEXT}npx skills add ${formatSourceInput(skill.sourceUrl, skill.ref)} -g -y${RESET}`);
4368
- }
4369
- }
4370
- async function runCheck(args = []) {
4371
- console.log(`${TEXT}Checking for skill updates...${RESET}`);
4372
- console.log();
4373
- const lock = readSkillLock();
4374
- const skillNames = Object.keys(lock.skills);
4375
- if (skillNames.length === 0) {
4376
- console.log(`${DIM}No skills tracked in lock file.${RESET}`);
4377
- console.log(`${DIM}Install skills with${RESET} ${TEXT}npx skills add <package>${RESET}`);
4378
- return;
4379
- }
4380
- const token = getGitHubToken();
4381
- const skillsBySource = /* @__PURE__ */ new Map();
4382
- const skipped = [];
4383
- for (const skillName of skillNames) {
4384
- const entry = lock.skills[skillName];
4385
- if (!entry) continue;
4386
- if (!entry.skillFolderHash || !entry.skillPath) {
4387
- skipped.push({
4388
- name: skillName,
4389
- reason: getSkipReason(entry),
4390
- sourceUrl: entry.sourceUrl,
4391
- ref: entry.ref
4392
- });
4393
- continue;
4446
+ const source = getInstallSource(skill);
4447
+ const existing = grouped.get(source) || [];
4448
+ existing.push(skill);
4449
+ grouped.set(source, existing);
4450
+ }
4451
+ for (const [source, skills] of grouped) {
4452
+ if (skills.length === 1) {
4453
+ const skill = skills[0];
4454
+ console.log(` ${TEXT}•${RESET} ${skill.name} ${DIM}(${skill.reason})${RESET}`);
4455
+ } else {
4456
+ const reason = skills[0].reason;
4457
+ const names = skills.map((s) => s.name).join(", ");
4458
+ console.log(` ${TEXT}•${RESET} ${names} ${DIM}(${reason})${RESET}`);
4394
4459
  }
4395
- const existing = skillsBySource.get(entry.source) || [];
4396
- existing.push({
4397
- name: skillName,
4398
- entry
4399
- });
4400
- skillsBySource.set(entry.source, existing);
4401
- }
4402
- const totalSkills = skillNames.length - skipped.length;
4403
- if (totalSkills === 0) {
4404
- console.log(`${DIM}No GitHub skills to check.${RESET}`);
4405
- printSkippedSkills(skipped);
4406
- return;
4460
+ console.log(` ${DIM}To update: ${TEXT}npx skills add ${source} -g -y${RESET}`);
4407
4461
  }
4408
- console.log(`${DIM}Checking ${totalSkills} skill(s) for updates...${RESET}`);
4409
- const updates = [];
4410
- const errors = [];
4411
- for (const [source, skills] of skillsBySource) for (const { name, entry } of skills) try {
4412
- const latestHash = await fetchSkillFolderHash(source, entry.skillPath, token, entry.ref);
4413
- if (!latestHash) {
4414
- errors.push({
4415
- name,
4416
- source,
4417
- error: "Could not fetch from GitHub"
4418
- });
4419
- continue;
4420
- }
4421
- if (latestHash !== entry.skillFolderHash) updates.push({
4422
- name,
4423
- source
4424
- });
4425
- } catch (err) {
4426
- errors.push({
4462
+ }
4463
+ async function getProjectSkillsForUpdate(skillFilter) {
4464
+ const localLock = await readLocalLock();
4465
+ const skills = [];
4466
+ for (const [name, entry] of Object.entries(localLock.skills)) {
4467
+ if (!matchesSkillFilter(name, skillFilter)) continue;
4468
+ if (entry.sourceType === "node_modules" || entry.sourceType === "local") continue;
4469
+ skills.push({
4427
4470
  name,
4428
- source,
4429
- error: err instanceof Error ? err.message : "Unknown error"
4471
+ source: entry.source,
4472
+ entry
4430
4473
  });
4431
4474
  }
4432
- console.log();
4433
- if (updates.length === 0) console.log(`${TEXT}✓ All skills are up to date${RESET}`);
4434
- else {
4435
- console.log(`${TEXT}${updates.length} update(s) available:${RESET}`);
4436
- console.log();
4437
- for (const update of updates) {
4438
- console.log(` ${TEXT}↑${RESET} ${update.name}`);
4439
- console.log(` ${DIM}source: ${update.source}${RESET}`);
4440
- }
4441
- console.log();
4442
- console.log(`${DIM}Run${RESET} ${TEXT}npx skills update${RESET} ${DIM}to update all skills${RESET}`);
4443
- }
4444
- if (errors.length > 0) {
4445
- console.log();
4446
- console.log(`${DIM}Could not check ${errors.length} skill(s) (may need reinstall)${RESET}`);
4447
- console.log();
4448
- for (const error of errors) {
4449
- console.log(` ${DIM}✗${RESET} ${error.name}`);
4450
- console.log(` ${DIM}source: ${error.source}${RESET}`);
4451
- }
4452
- }
4453
- printSkippedSkills(skipped);
4454
- track({
4455
- event: "check",
4456
- skillCount: String(totalSkills),
4457
- updatesAvailable: String(updates.length)
4458
- });
4459
- console.log();
4475
+ return skills;
4460
4476
  }
4461
- async function runUpdate() {
4462
- console.log(`${TEXT}Checking for skill updates...${RESET}`);
4463
- console.log();
4477
+ async function updateGlobalSkills(skillFilter) {
4464
4478
  const lock = readSkillLock();
4465
4479
  const skillNames = Object.keys(lock.skills);
4480
+ let successCount = 0;
4481
+ let failCount = 0;
4466
4482
  if (skillNames.length === 0) {
4467
- console.log(`${DIM}No skills tracked in lock file.${RESET}`);
4468
- console.log(`${DIM}Install skills with${RESET} ${TEXT}npx skills add <package>${RESET}`);
4469
- return;
4483
+ if (!skillFilter) {
4484
+ console.log(`${DIM}No global skills tracked in lock file.${RESET}`);
4485
+ console.log(`${DIM}Install skills with${RESET} ${TEXT}npx skills add <package> -g${RESET}`);
4486
+ }
4487
+ return {
4488
+ successCount,
4489
+ failCount,
4490
+ checkedCount: 0
4491
+ };
4470
4492
  }
4471
4493
  const token = getGitHubToken();
4472
4494
  const updates = [];
4473
4495
  const skipped = [];
4496
+ const checkable = [];
4474
4497
  for (const skillName of skillNames) {
4498
+ if (!matchesSkillFilter(skillName, skillFilter)) continue;
4475
4499
  const entry = lock.skills[skillName];
4476
4500
  if (!entry) continue;
4477
4501
  if (!entry.skillFolderHash || !entry.skillPath) {
@@ -4479,10 +4503,19 @@ async function runUpdate() {
4479
4503
  name: skillName,
4480
4504
  reason: getSkipReason(entry),
4481
4505
  sourceUrl: entry.sourceUrl,
4506
+ sourceType: entry.sourceType,
4482
4507
  ref: entry.ref
4483
4508
  });
4484
4509
  continue;
4485
4510
  }
4511
+ checkable.push({
4512
+ name: skillName,
4513
+ entry
4514
+ });
4515
+ }
4516
+ for (let i = 0; i < checkable.length; i++) {
4517
+ const { name: skillName, entry } = checkable[i];
4518
+ process.stdout.write(`\r${DIM}Checking global skill ${i + 1}/${checkable.length}: ${skillName}${RESET}\x1b[K`);
4486
4519
  try {
4487
4520
  const latestHash = await fetchSkillFolderHash(entry.source, entry.skillPath, token, entry.ref);
4488
4521
  if (latestHash && latestHash !== entry.skillFolderHash) updates.push({
@@ -4492,20 +4525,34 @@ async function runUpdate() {
4492
4525
  });
4493
4526
  } catch {}
4494
4527
  }
4495
- if (skillNames.length - skipped.length === 0) {
4496
- console.log(`${DIM}No skills to check.${RESET}`);
4528
+ if (checkable.length > 0) process.stdout.write("\r\x1B[K");
4529
+ const checkedCount = checkable.length + skipped.length;
4530
+ if (checkable.length === 0 && skipped.length === 0) {
4531
+ if (!skillFilter) console.log(`${DIM}No global skills to check.${RESET}`);
4532
+ return {
4533
+ successCount,
4534
+ failCount,
4535
+ checkedCount: 0
4536
+ };
4537
+ }
4538
+ if (checkable.length === 0 && skipped.length > 0) {
4497
4539
  printSkippedSkills(skipped);
4498
- return;
4540
+ return {
4541
+ successCount,
4542
+ failCount,
4543
+ checkedCount
4544
+ };
4499
4545
  }
4500
4546
  if (updates.length === 0) {
4501
- console.log(`${TEXT}✓ All skills are up to date${RESET}`);
4502
- console.log();
4503
- return;
4547
+ console.log(`${TEXT}✓ All global skills are up to date${RESET}`);
4548
+ return {
4549
+ successCount,
4550
+ failCount,
4551
+ checkedCount
4552
+ };
4504
4553
  }
4505
- console.log(`${TEXT}Found ${updates.length} update(s)${RESET}`);
4554
+ console.log(`${TEXT}Found ${updates.length} global update(s)${RESET}`);
4506
4555
  console.log();
4507
- let successCount = 0;
4508
- let failCount = 0;
4509
4556
  for (const update of updates) {
4510
4557
  console.log(`${TEXT}Updating ${update.name}...${RESET}`);
4511
4558
  const installUrl = buildUpdateInstallSource(update.entry);
@@ -4537,14 +4584,101 @@ async function runUpdate() {
4537
4584
  console.log(` ${DIM}✗ Failed to update ${update.name}${RESET}`);
4538
4585
  }
4539
4586
  }
4587
+ printSkippedSkills(skipped);
4588
+ return {
4589
+ successCount,
4590
+ failCount,
4591
+ checkedCount
4592
+ };
4593
+ }
4594
+ async function updateProjectSkills(skillFilter) {
4595
+ const projectSkills = await getProjectSkillsForUpdate(skillFilter);
4596
+ let successCount = 0;
4597
+ let failCount = 0;
4598
+ if (projectSkills.length === 0) {
4599
+ if (!skillFilter) {
4600
+ console.log(`${DIM}No project skills to update.${RESET}`);
4601
+ console.log(`${DIM}Install project skills with${RESET} ${TEXT}npx skills add <package>${RESET}`);
4602
+ }
4603
+ return {
4604
+ successCount,
4605
+ failCount,
4606
+ foundCount: 0
4607
+ };
4608
+ }
4609
+ console.log(`${TEXT}Refreshing ${projectSkills.length} project skill(s)...${RESET}`);
4610
+ console.log();
4611
+ for (const skill of projectSkills) {
4612
+ console.log(`${TEXT}Updating ${skill.name}...${RESET}`);
4613
+ const installUrl = buildLocalUpdateSource(skill.entry);
4614
+ const cliEntry = join(__dirname, "..", "bin", "cli.mjs");
4615
+ if (!existsSync(cliEntry)) {
4616
+ failCount++;
4617
+ console.log(` ${DIM}✗ Failed to update ${skill.name}: CLI entrypoint not found at ${cliEntry}${RESET}`);
4618
+ continue;
4619
+ }
4620
+ if (spawnSync(process.execPath, [
4621
+ cliEntry,
4622
+ "add",
4623
+ installUrl,
4624
+ "-y"
4625
+ ], {
4626
+ stdio: [
4627
+ "inherit",
4628
+ "pipe",
4629
+ "pipe"
4630
+ ],
4631
+ encoding: "utf-8",
4632
+ shell: process.platform === "win32"
4633
+ }).status === 0) {
4634
+ successCount++;
4635
+ console.log(` ${TEXT}✓${RESET} Updated ${skill.name}`);
4636
+ } else {
4637
+ failCount++;
4638
+ console.log(` ${DIM}✗ Failed to update ${skill.name}${RESET}`);
4639
+ }
4640
+ }
4641
+ return {
4642
+ successCount,
4643
+ failCount,
4644
+ foundCount: projectSkills.length
4645
+ };
4646
+ }
4647
+ async function runUpdate(args = []) {
4648
+ const options = parseUpdateOptions(args);
4649
+ const scope = await resolveUpdateScope(options);
4650
+ if (options.skills) console.log(`${TEXT}Updating ${options.skills.join(", ")}...${RESET}`);
4651
+ else console.log(`${TEXT}Checking for skill updates...${RESET}`);
4540
4652
  console.log();
4541
- if (successCount > 0) console.log(`${TEXT}✓ Updated ${successCount} skill(s)${RESET}`);
4542
- if (failCount > 0) console.log(`${DIM}Failed to update ${failCount} skill(s)${RESET}`);
4653
+ let totalSuccess = 0;
4654
+ let totalFail = 0;
4655
+ let totalFound = 0;
4656
+ if (scope === "global" || scope === "both") {
4657
+ if (scope === "both" && !options.skills) console.log(`${BOLD}Global Skills${RESET}`);
4658
+ const { successCount, failCount, checkedCount } = await updateGlobalSkills(options.skills);
4659
+ totalSuccess += successCount;
4660
+ totalFail += failCount;
4661
+ totalFound += checkedCount;
4662
+ if (scope === "both" && !options.skills) console.log();
4663
+ }
4664
+ if (scope === "project" || scope === "both") {
4665
+ if (scope === "both" && !options.skills) console.log(`${BOLD}Project Skills${RESET}`);
4666
+ const { successCount, failCount, foundCount } = await updateProjectSkills(options.skills);
4667
+ totalSuccess += successCount;
4668
+ totalFail += failCount;
4669
+ totalFound += foundCount;
4670
+ }
4671
+ if (options.skills && totalFound === 0) console.log(`${DIM}No installed skills found matching: ${options.skills.join(", ")}${RESET}`);
4672
+ console.log();
4673
+ if (totalSuccess > 0) console.log(`${TEXT}✓ Updated ${totalSuccess} skill(s)${RESET}`);
4674
+ if (totalFail > 0) console.log(`${DIM}Failed to update ${totalFail} skill(s)${RESET}`);
4675
+ if (totalSuccess === 0 && totalFail === 0) {}
4543
4676
  track({
4544
4677
  event: "update",
4545
- skillCount: String(updates.length),
4546
- successCount: String(successCount),
4547
- failCount: String(failCount)
4678
+ scope,
4679
+ skillCount: String(totalSuccess + totalFail),
4680
+ successCount: String(totalSuccess),
4681
+ failCount: String(totalFail)
4548
4682
  });
4549
4683
  console.log();
4550
4684
  }
@@ -4604,11 +4738,9 @@ async function main() {
4604
4738
  await runList(restArgs);
4605
4739
  break;
4606
4740
  case "check":
4607
- runCheck(restArgs);
4608
- break;
4609
4741
  case "update":
4610
4742
  case "upgrade":
4611
- runUpdate();
4743
+ await runUpdate(restArgs);
4612
4744
  break;
4613
4745
  case "--help":
4614
4746
  case "-h":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skills",
3
- "version": "1.4.9",
3
+ "version": "1.5.0",
4
4
  "description": "The open agent skills ecosystem",
5
5
  "type": "module",
6
6
  "bin": {