skills 1.4.8 → 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.
- package/README.md +77 -56
- package/dist/cli.mjs +264 -121
- 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
|
|
101
|
-
| `npx skills
|
|
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
|
|
132
|
+
### `skills update`
|
|
132
133
|
|
|
133
134
|
```bash
|
|
134
|
-
#
|
|
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
|
-
|
|
211
|
-
|
|
212
|
-
|
|
|
213
|
-
|
|
|
214
|
-
|
|
|
215
|
-
|
|
|
216
|
-
|
|
|
217
|
-
|
|
|
218
|
-
|
|
|
219
|
-
|
|
|
220
|
-
|
|
|
221
|
-
|
|
|
222
|
-
|
|
|
223
|
-
|
|
|
224
|
-
|
|
|
225
|
-
|
|
|
226
|
-
|
|
|
227
|
-
|
|
|
228
|
-
|
|
|
229
|
-
|
|
|
230
|
-
|
|
|
231
|
-
|
|
|
232
|
-
|
|
|
233
|
-
|
|
|
234
|
-
|
|
|
235
|
-
|
|
|
236
|
-
|
|
|
237
|
-
|
|
|
238
|
-
|
|
|
239
|
-
|
|
|
240
|
-
|
|
|
241
|
-
|
|
|
242
|
-
|
|
|
243
|
-
|
|
|
244
|
-
|
|
|
245
|
-
|
|
|
246
|
-
|
|
|
247
|
-
| Trae
|
|
248
|
-
|
|
|
249
|
-
|
|
|
250
|
-
|
|
|
251
|
-
|
|
|
252
|
-
|
|
|
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.
|
|
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);
|
|
@@ -2645,6 +2645,16 @@ async function runAdd(args, options = {}) {
|
|
|
2645
2645
|
spinner.start("Parsing source...");
|
|
2646
2646
|
const parsed = parseSource(source);
|
|
2647
2647
|
spinner.stop(`Source: ${parsed.type === "local" ? parsed.localPath : parsed.url}${parsed.ref ? ` @ ${import_picocolors.default.yellow(parsed.ref)}` : ""}${parsed.subpath ? ` (${parsed.subpath})` : ""}${parsed.skillFilter ? ` ${import_picocolors.default.dim("@")}${import_picocolors.default.cyan(parsed.skillFilter)}` : ""}`);
|
|
2648
|
+
if (getOwnerRepo(parsed)?.split("/")[0]?.toLowerCase() === "openclaw" && !options.dangerouslyAcceptOpenclawRisks) {
|
|
2649
|
+
console.log();
|
|
2650
|
+
M.warn(import_picocolors.default.yellow(import_picocolors.default.bold("⚠ OpenClaw skills are unverified community submissions.")));
|
|
2651
|
+
M.message(import_picocolors.default.yellow("This source contains user-submitted skills that have not been reviewed for safety or quality."));
|
|
2652
|
+
M.message(import_picocolors.default.yellow("Skills run with full agent permissions and could be malicious."));
|
|
2653
|
+
console.log();
|
|
2654
|
+
M.message(`If you understand the risks, re-run with:\n\n ${import_picocolors.default.cyan(`npx skills add ${source} --dangerously-accept-openclaw-risks`)}\n`);
|
|
2655
|
+
Se(import_picocolors.default.red("Installation blocked"));
|
|
2656
|
+
process.exit(1);
|
|
2657
|
+
}
|
|
2648
2658
|
if (parsed.type === "well-known") {
|
|
2649
2659
|
await handleWellKnownSkills(source, parsed.url, options, spinner);
|
|
2650
2660
|
return;
|
|
@@ -3222,6 +3232,7 @@ function parseAddOptions(args) {
|
|
|
3222
3232
|
i--;
|
|
3223
3233
|
} else if (arg === "--full-depth") options.fullDepth = true;
|
|
3224
3234
|
else if (arg === "--copy") options.copy = true;
|
|
3235
|
+
else if (arg === "--dangerously-accept-openclaw-risks") options.dangerouslyAcceptOpenclawRisks = true;
|
|
3225
3236
|
else if (arg && !arg.startsWith("-")) source.push(arg);
|
|
3226
3237
|
}
|
|
3227
3238
|
return {
|
|
@@ -4100,6 +4111,9 @@ function buildUpdateInstallSource(entry) {
|
|
|
4100
4111
|
if (entry.ref) installSource = `${installSource}#${entry.ref}`;
|
|
4101
4112
|
return installSource;
|
|
4102
4113
|
}
|
|
4114
|
+
function buildLocalUpdateSource(entry) {
|
|
4115
|
+
return formatSourceInput(entry.source, entry.ref);
|
|
4116
|
+
}
|
|
4103
4117
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
4104
4118
|
function getVersion() {
|
|
4105
4119
|
try {
|
|
@@ -4147,8 +4161,7 @@ function showBanner() {
|
|
|
4147
4161
|
console.log(` ${DIM}$${RESET} ${TEXT}npx skills list${RESET} ${DIM}List installed skills${RESET}`);
|
|
4148
4162
|
console.log(` ${DIM}$${RESET} ${TEXT}npx skills find ${DIM}[query]${RESET} ${DIM}Search for skills${RESET}`);
|
|
4149
4163
|
console.log();
|
|
4150
|
-
console.log(` ${DIM}$${RESET} ${TEXT}npx skills
|
|
4151
|
-
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}`);
|
|
4152
4165
|
console.log();
|
|
4153
4166
|
console.log(` ${DIM}$${RESET} ${TEXT}npx skills experimental_install${RESET} ${DIM}Restore from skills-lock.json${RESET}`);
|
|
4154
4167
|
console.log(` ${DIM}$${RESET} ${TEXT}npx skills init ${DIM}[name]${RESET} ${DIM}Create a new skill${RESET}`);
|
|
@@ -4172,8 +4185,12 @@ ${BOLD}Manage Skills:${RESET}
|
|
|
4172
4185
|
find [query] Search for skills interactively
|
|
4173
4186
|
|
|
4174
4187
|
${BOLD}Updates:${RESET}
|
|
4175
|
-
|
|
4176
|
-
|
|
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)
|
|
4177
4194
|
|
|
4178
4195
|
${BOLD}Project:${RESET}
|
|
4179
4196
|
experimental_install Restore skills from skills-lock.json
|
|
@@ -4224,8 +4241,9 @@ ${BOLD}Examples:${RESET}
|
|
|
4224
4241
|
${DIM}$${RESET} skills ls --json ${DIM}# JSON output${RESET}
|
|
4225
4242
|
${DIM}$${RESET} skills find ${DIM}# interactive search${RESET}
|
|
4226
4243
|
${DIM}$${RESET} skills find typescript ${DIM}# search by keyword${RESET}
|
|
4227
|
-
${DIM}$${RESET} skills check
|
|
4228
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}
|
|
4229
4247
|
${DIM}$${RESET} skills experimental_install ${DIM}# restore from skills-lock.json${RESET}
|
|
4230
4248
|
${DIM}$${RESET} skills init my-skill
|
|
4231
4249
|
${DIM}$${RESET} skills experimental_sync ${DIM}# sync from node_modules${RESET}
|
|
@@ -4340,127 +4358,144 @@ function readSkillLock() {
|
|
|
4340
4358
|
};
|
|
4341
4359
|
}
|
|
4342
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
|
+
}
|
|
4343
4424
|
function getSkipReason(entry) {
|
|
4344
4425
|
if (entry.sourceType === "local") return "Local path";
|
|
4345
|
-
if (entry.sourceType === "git") return "Git URL
|
|
4346
|
-
if (
|
|
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";
|
|
4347
4429
|
if (!entry.skillPath) return "No skill path recorded";
|
|
4348
4430
|
return "No version tracking";
|
|
4349
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
|
+
}
|
|
4350
4440
|
function printSkippedSkills(skipped) {
|
|
4351
4441
|
if (skipped.length === 0) return;
|
|
4352
4442
|
console.log();
|
|
4353
4443
|
console.log(`${DIM}${skipped.length} skill(s) cannot be checked automatically:${RESET}`);
|
|
4444
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
4354
4445
|
for (const skill of skipped) {
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
}
|
|
4369
|
-
const token = getGitHubToken();
|
|
4370
|
-
const skillsBySource = /* @__PURE__ */ new Map();
|
|
4371
|
-
const skipped = [];
|
|
4372
|
-
for (const skillName of skillNames) {
|
|
4373
|
-
const entry = lock.skills[skillName];
|
|
4374
|
-
if (!entry) continue;
|
|
4375
|
-
if (!entry.skillFolderHash || !entry.skillPath) {
|
|
4376
|
-
skipped.push({
|
|
4377
|
-
name: skillName,
|
|
4378
|
-
reason: getSkipReason(entry),
|
|
4379
|
-
sourceUrl: entry.sourceUrl,
|
|
4380
|
-
ref: entry.ref
|
|
4381
|
-
});
|
|
4382
|
-
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}`);
|
|
4383
4459
|
}
|
|
4384
|
-
|
|
4385
|
-
existing.push({
|
|
4386
|
-
name: skillName,
|
|
4387
|
-
entry
|
|
4388
|
-
});
|
|
4389
|
-
skillsBySource.set(entry.source, existing);
|
|
4460
|
+
console.log(` ${DIM}To update: ${TEXT}npx skills add ${source} -g -y${RESET}`);
|
|
4390
4461
|
}
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
const errors = [];
|
|
4400
|
-
for (const [source, skills] of skillsBySource) for (const { name, entry } of skills) try {
|
|
4401
|
-
const latestHash = await fetchSkillFolderHash(source, entry.skillPath, token, entry.ref);
|
|
4402
|
-
if (!latestHash) {
|
|
4403
|
-
errors.push({
|
|
4404
|
-
name,
|
|
4405
|
-
source,
|
|
4406
|
-
error: "Could not fetch from GitHub"
|
|
4407
|
-
});
|
|
4408
|
-
continue;
|
|
4409
|
-
}
|
|
4410
|
-
if (latestHash !== entry.skillFolderHash) updates.push({
|
|
4411
|
-
name,
|
|
4412
|
-
source
|
|
4413
|
-
});
|
|
4414
|
-
} catch (err) {
|
|
4415
|
-
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({
|
|
4416
4470
|
name,
|
|
4417
|
-
source,
|
|
4418
|
-
|
|
4471
|
+
source: entry.source,
|
|
4472
|
+
entry
|
|
4419
4473
|
});
|
|
4420
4474
|
}
|
|
4421
|
-
|
|
4422
|
-
if (updates.length === 0) console.log(`${TEXT}✓ All skills are up to date${RESET}`);
|
|
4423
|
-
else {
|
|
4424
|
-
console.log(`${TEXT}${updates.length} update(s) available:${RESET}`);
|
|
4425
|
-
console.log();
|
|
4426
|
-
for (const update of updates) {
|
|
4427
|
-
console.log(` ${TEXT}↑${RESET} ${update.name}`);
|
|
4428
|
-
console.log(` ${DIM}source: ${update.source}${RESET}`);
|
|
4429
|
-
}
|
|
4430
|
-
console.log();
|
|
4431
|
-
console.log(`${DIM}Run${RESET} ${TEXT}npx skills update${RESET} ${DIM}to update all skills${RESET}`);
|
|
4432
|
-
}
|
|
4433
|
-
if (errors.length > 0) {
|
|
4434
|
-
console.log();
|
|
4435
|
-
console.log(`${DIM}Could not check ${errors.length} skill(s) (may need reinstall)${RESET}`);
|
|
4436
|
-
console.log();
|
|
4437
|
-
for (const error of errors) {
|
|
4438
|
-
console.log(` ${DIM}✗${RESET} ${error.name}`);
|
|
4439
|
-
console.log(` ${DIM}source: ${error.source}${RESET}`);
|
|
4440
|
-
}
|
|
4441
|
-
}
|
|
4442
|
-
printSkippedSkills(skipped);
|
|
4443
|
-
track({
|
|
4444
|
-
event: "check",
|
|
4445
|
-
skillCount: String(totalSkills),
|
|
4446
|
-
updatesAvailable: String(updates.length)
|
|
4447
|
-
});
|
|
4448
|
-
console.log();
|
|
4475
|
+
return skills;
|
|
4449
4476
|
}
|
|
4450
|
-
async function
|
|
4451
|
-
console.log(`${TEXT}Checking for skill updates...${RESET}`);
|
|
4452
|
-
console.log();
|
|
4477
|
+
async function updateGlobalSkills(skillFilter) {
|
|
4453
4478
|
const lock = readSkillLock();
|
|
4454
4479
|
const skillNames = Object.keys(lock.skills);
|
|
4480
|
+
let successCount = 0;
|
|
4481
|
+
let failCount = 0;
|
|
4455
4482
|
if (skillNames.length === 0) {
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
|
|
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
|
+
};
|
|
4459
4492
|
}
|
|
4460
4493
|
const token = getGitHubToken();
|
|
4461
4494
|
const updates = [];
|
|
4462
4495
|
const skipped = [];
|
|
4496
|
+
const checkable = [];
|
|
4463
4497
|
for (const skillName of skillNames) {
|
|
4498
|
+
if (!matchesSkillFilter(skillName, skillFilter)) continue;
|
|
4464
4499
|
const entry = lock.skills[skillName];
|
|
4465
4500
|
if (!entry) continue;
|
|
4466
4501
|
if (!entry.skillFolderHash || !entry.skillPath) {
|
|
@@ -4468,10 +4503,19 @@ async function runUpdate() {
|
|
|
4468
4503
|
name: skillName,
|
|
4469
4504
|
reason: getSkipReason(entry),
|
|
4470
4505
|
sourceUrl: entry.sourceUrl,
|
|
4506
|
+
sourceType: entry.sourceType,
|
|
4471
4507
|
ref: entry.ref
|
|
4472
4508
|
});
|
|
4473
4509
|
continue;
|
|
4474
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`);
|
|
4475
4519
|
try {
|
|
4476
4520
|
const latestHash = await fetchSkillFolderHash(entry.source, entry.skillPath, token, entry.ref);
|
|
4477
4521
|
if (latestHash && latestHash !== entry.skillFolderHash) updates.push({
|
|
@@ -4481,20 +4525,34 @@ async function runUpdate() {
|
|
|
4481
4525
|
});
|
|
4482
4526
|
} catch {}
|
|
4483
4527
|
}
|
|
4484
|
-
if (
|
|
4485
|
-
|
|
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) {
|
|
4486
4539
|
printSkippedSkills(skipped);
|
|
4487
|
-
return
|
|
4540
|
+
return {
|
|
4541
|
+
successCount,
|
|
4542
|
+
failCount,
|
|
4543
|
+
checkedCount
|
|
4544
|
+
};
|
|
4488
4545
|
}
|
|
4489
4546
|
if (updates.length === 0) {
|
|
4490
|
-
console.log(`${TEXT}✓ All skills are up to date${RESET}`);
|
|
4491
|
-
|
|
4492
|
-
|
|
4547
|
+
console.log(`${TEXT}✓ All global skills are up to date${RESET}`);
|
|
4548
|
+
return {
|
|
4549
|
+
successCount,
|
|
4550
|
+
failCount,
|
|
4551
|
+
checkedCount
|
|
4552
|
+
};
|
|
4493
4553
|
}
|
|
4494
|
-
console.log(`${TEXT}Found ${updates.length} update(s)${RESET}`);
|
|
4554
|
+
console.log(`${TEXT}Found ${updates.length} global update(s)${RESET}`);
|
|
4495
4555
|
console.log();
|
|
4496
|
-
let successCount = 0;
|
|
4497
|
-
let failCount = 0;
|
|
4498
4556
|
for (const update of updates) {
|
|
4499
4557
|
console.log(`${TEXT}Updating ${update.name}...${RESET}`);
|
|
4500
4558
|
const installUrl = buildUpdateInstallSource(update.entry);
|
|
@@ -4526,14 +4584,101 @@ async function runUpdate() {
|
|
|
4526
4584
|
console.log(` ${DIM}✗ Failed to update ${update.name}${RESET}`);
|
|
4527
4585
|
}
|
|
4528
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}`);
|
|
4529
4610
|
console.log();
|
|
4530
|
-
|
|
4531
|
-
|
|
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}`);
|
|
4652
|
+
console.log();
|
|
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) {}
|
|
4532
4676
|
track({
|
|
4533
4677
|
event: "update",
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4678
|
+
scope,
|
|
4679
|
+
skillCount: String(totalSuccess + totalFail),
|
|
4680
|
+
successCount: String(totalSuccess),
|
|
4681
|
+
failCount: String(totalFail)
|
|
4537
4682
|
});
|
|
4538
4683
|
console.log();
|
|
4539
4684
|
}
|
|
@@ -4593,11 +4738,9 @@ async function main() {
|
|
|
4593
4738
|
await runList(restArgs);
|
|
4594
4739
|
break;
|
|
4595
4740
|
case "check":
|
|
4596
|
-
runCheck(restArgs);
|
|
4597
|
-
break;
|
|
4598
4741
|
case "update":
|
|
4599
4742
|
case "upgrade":
|
|
4600
|
-
runUpdate();
|
|
4743
|
+
await runUpdate(restArgs);
|
|
4601
4744
|
break;
|
|
4602
4745
|
case "--help":
|
|
4603
4746
|
case "-h":
|