skills 1.5.24 → 1.5.26
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 +6 -6
- package/dist/cli.mjs +284 -100
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
The CLI for the open agent skills ecosystem.
|
|
4
4
|
|
|
5
5
|
<!-- agent-list:start -->
|
|
6
|
-
Supports **OpenCode**, **Claude Code**, **Codex**, **Cursor**, and [
|
|
6
|
+
Supports **OpenCode**, **Claude Code**, **Codex**, **Cursor**, and [75 more](#supported-agents).
|
|
7
7
|
<!-- agent-list:end -->
|
|
8
8
|
|
|
9
9
|
[](https://skills.sh/vercel-labs/skills)
|
|
@@ -279,7 +279,7 @@ Skills can be installed to any of these agents:
|
|
|
279
279
|
| IBM Bob | `bob` | `.bob/skills/` | `~/.bob/skills/` |
|
|
280
280
|
| Claude Code | `claude-code` | `.claude/skills/` | `~/.claude/skills/` |
|
|
281
281
|
| OpenClaw | `openclaw` | `skills/` | `~/.openclaw/skills/` |
|
|
282
|
-
| Cline, Dexto, Kimi Code CLI, Loaf, Warp, Zed | `cline`, `dexto`, `kimi-code-cli`, `loaf`, `warp`, `zed` | `.agents/skills/` | `~/.agents/skills/` |
|
|
282
|
+
| Cline, Dexto, Kimi Code CLI, Loaf, Sarvam Code, Warp, Zed | `cline`, `dexto`, `kimi-code-cli`, `loaf`, `sarvam-code`, `warp`, `zed` | `.agents/skills/` | `~/.agents/skills/` |
|
|
283
283
|
| CodeArts Agent | `codearts-agent` | `.codeartsdoer/skills/` | `~/.codeartsdoer/skills/` |
|
|
284
284
|
| CodeBuddy | `codebuddy` | `.codebuddy/skills/` | `~/.codebuddy/skills/` |
|
|
285
285
|
| Codemaker | `codemaker` | `.codemaker/skills/` | `~/.codemaker/skills/` |
|
|
@@ -292,10 +292,11 @@ Skills can be installed to any of these agents:
|
|
|
292
292
|
| Cursor | `cursor` | `.agents/skills/` | `~/.cursor/skills/` |
|
|
293
293
|
| Deep Agents | `deepagents` | `.agents/skills/` | `~/.deepagents/agent/skills/` |
|
|
294
294
|
| Devin for Terminal | `devin` | `.devin/skills/` | `~/.config/devin/skills/` |
|
|
295
|
-
| Droid | `droid` | `.
|
|
295
|
+
| Droid | `droid` | `.agents/skills/` | `~/.factory/skills/` |
|
|
296
296
|
| Eve | `eve` | `agent/skills/` | N/A (project-only) |
|
|
297
297
|
| Firebender | `firebender` | `.agents/skills/` | `~/.firebender/skills/` |
|
|
298
298
|
| ForgeCode | `forgecode` | `.forge/skills/` | `~/.forge/skills/` |
|
|
299
|
+
| fx | `fx` | `.fx/skills/` | `~/.fx/skills/` |
|
|
299
300
|
| Gemini CLI | `gemini-cli` | `.agents/skills/` | `~/.gemini/skills/` |
|
|
300
301
|
| GitHub Copilot | `github-copilot` | `.agents/skills/` | `~/.copilot/skills/` |
|
|
301
302
|
| Goose | `goose` | `.goose/skills/` | `~/.config/goose/skills/` |
|
|
@@ -305,7 +306,7 @@ Skills can be installed to any of these agents:
|
|
|
305
306
|
| Jazz | `jazz` | `.jazz/skills/` | `~/.jazz/skills/` |
|
|
306
307
|
| Junie | `junie` | `.junie/skills/` | `~/.junie/skills/` |
|
|
307
308
|
| iFlow CLI | `iflow-cli` | `.iflow/skills/` | `~/.iflow/skills/` |
|
|
308
|
-
| Kilo Code | `kilo` | `.
|
|
309
|
+
| Kilo Code | `kilo` | `.agents/skills/` | `~/.kilo/skills/` |
|
|
309
310
|
| Kimchi | `kimchi` | `.kimchi/skills/` | `~/.config/kimchi/harness/skills/` |
|
|
310
311
|
| Kiro CLI | `kiro-cli` | `.kiro/skills/` | `~/.kiro/skills/` |
|
|
311
312
|
| Kode | `kode` | `.kode/skills/` | `~/.kode/skills/` |
|
|
@@ -430,9 +431,9 @@ discover `SKILL.md` files outside these container directories (e.g. under
|
|
|
430
431
|
- `.cortex/skills/`
|
|
431
432
|
- `.crush/skills/`
|
|
432
433
|
- `.devin/skills/`
|
|
433
|
-
- `.factory/skills/`
|
|
434
434
|
- `agent/skills/`
|
|
435
435
|
- `.forge/skills/`
|
|
436
|
+
- `.fx/skills/`
|
|
436
437
|
- `.goose/skills/`
|
|
437
438
|
- `.grok/skills/`
|
|
438
439
|
- `.hermes/skills/`
|
|
@@ -440,7 +441,6 @@ discover `SKILL.md` files outside these container directories (e.g. under
|
|
|
440
441
|
- `.jazz/skills/`
|
|
441
442
|
- `.junie/skills/`
|
|
442
443
|
- `.iflow/skills/`
|
|
443
|
-
- `.kilocode/skills/`
|
|
444
444
|
- `.kimchi/skills/`
|
|
445
445
|
- `.kiro/skills/`
|
|
446
446
|
- `.kode/skills/`
|
package/dist/cli.mjs
CHANGED
|
@@ -18,7 +18,7 @@ import { Writable } from "stream";
|
|
|
18
18
|
import { promisify } from "util";
|
|
19
19
|
import { execFile, spawn, spawnSync } from "child_process";
|
|
20
20
|
import { access, chmod, cp, lstat, mkdir, mkdtemp, readFile, readdir, readlink, realpath, rm, stat, symlink, writeFile } from "fs/promises";
|
|
21
|
-
import { parse } from "yaml";
|
|
21
|
+
import { parse, stringify } from "yaml";
|
|
22
22
|
import { createHash } from "crypto";
|
|
23
23
|
import { mkdir as mkdir$1, mkdtemp as mkdtemp$1, readFile as readFile$1, rm as rm$1, stat as stat$1, writeFile as writeFile$1 } from "node:fs/promises";
|
|
24
24
|
import { createHash as createHash$1 } from "node:crypto";
|
|
@@ -598,21 +598,30 @@ async function searchMultiselect(options) {
|
|
|
598
598
|
lastRenderHeight = countVisualRowsForLines(lines, process.stdout.columns);
|
|
599
599
|
};
|
|
600
600
|
const cleanup = () => {
|
|
601
|
+
rl.removeListener("close", closeHandler);
|
|
601
602
|
process.stdin.removeListener("keypress", keypressHandler);
|
|
602
603
|
if (process.stdin.isTTY) process.stdin.setRawMode(false);
|
|
603
604
|
rl.close();
|
|
604
605
|
};
|
|
606
|
+
let settled = false;
|
|
605
607
|
const submit = () => {
|
|
608
|
+
if (settled) return;
|
|
606
609
|
if (required && selected.size === 0 && lockedValues.length === 0) return;
|
|
610
|
+
settled = true;
|
|
607
611
|
render("submit");
|
|
608
612
|
cleanup();
|
|
609
613
|
resolve([...lockedValues, ...Array.from(selected)]);
|
|
610
614
|
};
|
|
611
615
|
const cancel = () => {
|
|
616
|
+
if (settled) return;
|
|
617
|
+
settled = true;
|
|
612
618
|
render("cancel");
|
|
613
619
|
cleanup();
|
|
614
620
|
resolve(cancelSymbol);
|
|
615
621
|
};
|
|
622
|
+
const closeHandler = () => {
|
|
623
|
+
cancel();
|
|
624
|
+
};
|
|
616
625
|
const keypressHandler = (_str, key) => {
|
|
617
626
|
if (!key) return;
|
|
618
627
|
const entries = buildSearchEntries(getFiltered(), selectGroups, collapsedGroups);
|
|
@@ -673,6 +682,11 @@ async function searchMultiselect(options) {
|
|
|
673
682
|
}
|
|
674
683
|
};
|
|
675
684
|
process.stdin.on("keypress", keypressHandler);
|
|
685
|
+
rl.on("close", closeHandler);
|
|
686
|
+
if (process.stdin.readableEnded || process.stdin.destroyed) {
|
|
687
|
+
cancel();
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
676
690
|
render();
|
|
677
691
|
});
|
|
678
692
|
}
|
|
@@ -1139,11 +1153,13 @@ const AGENT_PROJECT_SKILL_DIRS = [
|
|
|
1139
1153
|
".codex/skills",
|
|
1140
1154
|
".commandcode/skills",
|
|
1141
1155
|
".continue/skills",
|
|
1156
|
+
".factory/skills",
|
|
1142
1157
|
".github/skills",
|
|
1143
1158
|
".goose/skills",
|
|
1144
1159
|
".grok/skills",
|
|
1145
1160
|
".iflow/skills",
|
|
1146
1161
|
".junie/skills",
|
|
1162
|
+
".kilo/skills",
|
|
1147
1163
|
".kilocode/skills",
|
|
1148
1164
|
".kimchi/skills",
|
|
1149
1165
|
".kiro/skills",
|
|
@@ -1339,6 +1355,7 @@ const vibeHome = process.env.VIBE_HOME?.trim() || join(home, ".vibe");
|
|
|
1339
1355
|
const hermesHome = process.env.HERMES_HOME?.trim() || join(home, ".hermes");
|
|
1340
1356
|
const autohandHome = process.env.AUTOHAND_HOME?.trim() || join(home, ".autohand");
|
|
1341
1357
|
const grokHome = process.env.GROK_HOME?.trim() || join(home, ".grok");
|
|
1358
|
+
const sarvamHome = process.env.SARVAM_HOME?.trim() || join(home, ".sarvam");
|
|
1342
1359
|
const zedAppDataHome = process.env.APPDATA?.trim();
|
|
1343
1360
|
const zedFlatpakConfigHome = process.env.FLATPAK_XDG_CONFIG_HOME?.trim();
|
|
1344
1361
|
function packageJsonHasDependency(packageJsonPath, dependencyName) {
|
|
@@ -1391,6 +1408,7 @@ const agents = {
|
|
|
1391
1408
|
displayName: "Antigravity",
|
|
1392
1409
|
skillsDir: ".agents/skills",
|
|
1393
1410
|
globalSkillsDir: join(home, ".gemini/antigravity/skills"),
|
|
1411
|
+
showInUniversalPrompt: false,
|
|
1394
1412
|
detectInstalled: async () => {
|
|
1395
1413
|
return existsSync(join(home, ".gemini/antigravity"));
|
|
1396
1414
|
}
|
|
@@ -1400,6 +1418,7 @@ const agents = {
|
|
|
1400
1418
|
displayName: "Antigravity CLI",
|
|
1401
1419
|
skillsDir: ".agents/skills",
|
|
1402
1420
|
globalSkillsDir: join(home, ".gemini/antigravity-cli/skills"),
|
|
1421
|
+
showInUniversalPrompt: false,
|
|
1403
1422
|
detectInstalled: async () => {
|
|
1404
1423
|
return existsSync(join(home, ".gemini/antigravity-cli"));
|
|
1405
1424
|
}
|
|
@@ -1445,6 +1464,7 @@ const agents = {
|
|
|
1445
1464
|
displayName: "Claude Code",
|
|
1446
1465
|
skillsDir: ".claude/skills",
|
|
1447
1466
|
globalSkillsDir: join(claudeHome, "skills"),
|
|
1467
|
+
createProjectSkillsDirByDefault: true,
|
|
1448
1468
|
detectInstalled: async () => {
|
|
1449
1469
|
return existsSync(claudeHome);
|
|
1450
1470
|
}
|
|
@@ -1562,6 +1582,7 @@ const agents = {
|
|
|
1562
1582
|
displayName: "Deep Agents",
|
|
1563
1583
|
skillsDir: ".agents/skills",
|
|
1564
1584
|
globalSkillsDir: join(home, ".deepagents/agent/skills"),
|
|
1585
|
+
showInUniversalPrompt: false,
|
|
1565
1586
|
detectInstalled: async () => {
|
|
1566
1587
|
return existsSync(join(home, ".deepagents"));
|
|
1567
1588
|
}
|
|
@@ -1588,7 +1609,7 @@ const agents = {
|
|
|
1588
1609
|
droid: {
|
|
1589
1610
|
name: "droid",
|
|
1590
1611
|
displayName: "Droid",
|
|
1591
|
-
skillsDir: ".
|
|
1612
|
+
skillsDir: ".agents/skills",
|
|
1592
1613
|
globalSkillsDir: join(home, ".factory/skills"),
|
|
1593
1614
|
detectInstalled: async () => {
|
|
1594
1615
|
return existsSync(join(home, ".factory"));
|
|
@@ -1623,6 +1644,15 @@ const agents = {
|
|
|
1623
1644
|
return existsSync(join(home, ".forge"));
|
|
1624
1645
|
}
|
|
1625
1646
|
},
|
|
1647
|
+
fx: {
|
|
1648
|
+
name: "fx",
|
|
1649
|
+
displayName: "fx",
|
|
1650
|
+
skillsDir: ".fx/skills",
|
|
1651
|
+
globalSkillsDir: join(home, ".fx/skills"),
|
|
1652
|
+
detectInstalled: async () => {
|
|
1653
|
+
return existsSync(join(home, ".fx"));
|
|
1654
|
+
}
|
|
1655
|
+
},
|
|
1626
1656
|
"gemini-cli": {
|
|
1627
1657
|
name: "gemini-cli",
|
|
1628
1658
|
displayName: "Gemini CLI",
|
|
@@ -1707,10 +1737,10 @@ const agents = {
|
|
|
1707
1737
|
kilo: {
|
|
1708
1738
|
name: "kilo",
|
|
1709
1739
|
displayName: "Kilo Code",
|
|
1710
|
-
skillsDir: ".
|
|
1711
|
-
globalSkillsDir: join(home, ".
|
|
1740
|
+
skillsDir: ".agents/skills",
|
|
1741
|
+
globalSkillsDir: join(home, ".kilo/skills"),
|
|
1712
1742
|
detectInstalled: async () => {
|
|
1713
|
-
return existsSync(join(home, ".kilocode"));
|
|
1743
|
+
return existsSync(join(home, ".kilo")) || existsSync(join(home, ".kilocode"));
|
|
1714
1744
|
}
|
|
1715
1745
|
},
|
|
1716
1746
|
kimchi: {
|
|
@@ -1922,6 +1952,16 @@ const agents = {
|
|
|
1922
1952
|
return existsSync(join(home, ".roo"));
|
|
1923
1953
|
}
|
|
1924
1954
|
},
|
|
1955
|
+
"sarvam-code": {
|
|
1956
|
+
name: "sarvam-code",
|
|
1957
|
+
displayName: "Sarvam Code",
|
|
1958
|
+
skillsDir: ".agents/skills",
|
|
1959
|
+
globalSkillsDir: join(home, ".agents/skills"),
|
|
1960
|
+
showInUniversalPrompt: false,
|
|
1961
|
+
detectInstalled: async () => {
|
|
1962
|
+
return existsSync(sarvamHome);
|
|
1963
|
+
}
|
|
1964
|
+
},
|
|
1925
1965
|
"tabnine-cli": {
|
|
1926
1966
|
name: "tabnine-cli",
|
|
1927
1967
|
displayName: "Tabnine CLI",
|
|
@@ -2106,6 +2146,11 @@ function isPathSafe$2(basePath, targetPath) {
|
|
|
2106
2146
|
function pathsOverlap(pathA, pathB) {
|
|
2107
2147
|
return isPathSafe$2(pathA, pathB) || isPathSafe$2(pathB, pathA);
|
|
2108
2148
|
}
|
|
2149
|
+
function shouldSkipProjectAgentSymlink(agentType, isGlobal, cwd, createMissingAgentRoot) {
|
|
2150
|
+
if (isGlobal || isUniversalAgent(agentType) || createMissingAgentRoot || agents[agentType].createProjectSkillsDirByDefault) return false;
|
|
2151
|
+
const agentRoot = agents[agentType].skillsDir.split("/")[0];
|
|
2152
|
+
return !existsSync(join(cwd, agentRoot));
|
|
2153
|
+
}
|
|
2109
2154
|
async function isDirEntryOrSymlinkToDir(entry, entryPath) {
|
|
2110
2155
|
if (entry.isDirectory()) return true;
|
|
2111
2156
|
if (!entry.isSymbolicLink()) return false;
|
|
@@ -2241,15 +2286,14 @@ async function installSkillForAgent(skill, agentType, options = {}) {
|
|
|
2241
2286
|
canonicalPath: canonicalDir,
|
|
2242
2287
|
mode: "symlink"
|
|
2243
2288
|
};
|
|
2244
|
-
if (
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
}
|
|
2289
|
+
if (shouldSkipProjectAgentSymlink(agentType, isGlobal, cwd, options.createMissingAgentRoot ?? false)) return {
|
|
2290
|
+
success: true,
|
|
2291
|
+
path: canonicalDir,
|
|
2292
|
+
canonicalPath: canonicalDir,
|
|
2293
|
+
mode: "symlink",
|
|
2294
|
+
skipped: true,
|
|
2295
|
+
skipReason: "missing-agent-project-directory"
|
|
2296
|
+
};
|
|
2253
2297
|
if (!await createSymlink(canonicalDir, agentDir)) {
|
|
2254
2298
|
await cleanAndCreateDirectory(agentDir);
|
|
2255
2299
|
await copyDirectory(skill.path, agentDir, agentType);
|
|
@@ -2290,15 +2334,14 @@ const isExcluded$1 = (name, isDirectory = false) => {
|
|
|
2290
2334
|
function stripIgnoredEveFrontmatter(raw) {
|
|
2291
2335
|
const { data, content } = parseFrontmatter(raw);
|
|
2292
2336
|
const eveData = {};
|
|
2337
|
+
if (typeof data.name === "string") eveData.name = data.name;
|
|
2293
2338
|
if (typeof data.description === "string") eveData.description = data.description;
|
|
2294
2339
|
if (typeof data.license === "string") eveData.license = data.license;
|
|
2295
|
-
if (data.
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
if (keys.length === 0) return content.replace(/^\r?\n/u, "");
|
|
2301
|
-
return `---\n${keys.map((key) => `${key}: ${JSON.stringify(eveData[key])}`).join("\n")}\n---\n${content.replace(/^\r?\n/u, "")}`;
|
|
2340
|
+
if (data.compatibility !== void 0) eveData.compatibility = data.compatibility;
|
|
2341
|
+
if (typeof data.version === "string" || typeof data.version === "number") eveData.version = data.version;
|
|
2342
|
+
if (data.metadata && typeof data.metadata === "object" && !Array.isArray(data.metadata)) eveData.metadata = data.metadata;
|
|
2343
|
+
if (Object.keys(eveData).length === 0) return content.replace(/^\r?\n/u, "");
|
|
2344
|
+
return `---\n${stringify(eveData).trimEnd()}\n---\n${content.replace(/^\r?\n/u, "")}`;
|
|
2302
2345
|
}
|
|
2303
2346
|
async function copyDirectory(src, dest, agentType) {
|
|
2304
2347
|
await mkdir(dest, { recursive: true });
|
|
@@ -2532,15 +2575,14 @@ async function installBlobSkillForAgent(skill, agentType, options = {}) {
|
|
|
2532
2575
|
canonicalPath: canonicalDir,
|
|
2533
2576
|
mode: "symlink"
|
|
2534
2577
|
};
|
|
2535
|
-
if (
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
}
|
|
2578
|
+
if (shouldSkipProjectAgentSymlink(agentType, isGlobal, cwd, options.createMissingAgentRoot ?? false)) return {
|
|
2579
|
+
success: true,
|
|
2580
|
+
path: canonicalDir,
|
|
2581
|
+
canonicalPath: canonicalDir,
|
|
2582
|
+
mode: "symlink",
|
|
2583
|
+
skipped: true,
|
|
2584
|
+
skipReason: "missing-agent-project-directory"
|
|
2585
|
+
};
|
|
2544
2586
|
if (!await createSymlink(canonicalDir, agentDir)) {
|
|
2545
2587
|
await cleanAndCreateDirectory(agentDir);
|
|
2546
2588
|
await writeSkillFiles(agentDir);
|
|
@@ -3871,11 +3913,13 @@ const PRIORITY_PREFIXES = [
|
|
|
3871
3913
|
".codex/skills/",
|
|
3872
3914
|
".commandcode/skills/",
|
|
3873
3915
|
".continue/skills/",
|
|
3916
|
+
".factory/skills/",
|
|
3874
3917
|
".github/skills/",
|
|
3875
3918
|
".goose/skills/",
|
|
3876
3919
|
".grok/skills/",
|
|
3877
3920
|
".iflow/skills/",
|
|
3878
3921
|
".junie/skills/",
|
|
3922
|
+
".kilo/skills/",
|
|
3879
3923
|
".kilocode/skills/",
|
|
3880
3924
|
".kimchi/skills/",
|
|
3881
3925
|
".kiro/skills/",
|
|
@@ -3977,6 +4021,35 @@ function computeSnapshotHash(files) {
|
|
|
3977
4021
|
}
|
|
3978
4022
|
return hash.digest("hex");
|
|
3979
4023
|
}
|
|
4024
|
+
function getSkillFolderPath(skillMdPath) {
|
|
4025
|
+
const pathLower = skillMdPath.toLowerCase();
|
|
4026
|
+
if (pathLower.endsWith("/skill.md")) return skillMdPath.slice(0, -9);
|
|
4027
|
+
if (pathLower === "skill.md") return "";
|
|
4028
|
+
return skillMdPath.slice(0, -9);
|
|
4029
|
+
}
|
|
4030
|
+
const SNAPSHOT_EXCLUDED_FILES = /* @__PURE__ */ new Set(["metadata.json"]);
|
|
4031
|
+
const SNAPSHOT_EXCLUDED_DIRS = /* @__PURE__ */ new Set([
|
|
4032
|
+
".git",
|
|
4033
|
+
"__pycache__",
|
|
4034
|
+
"__pypackages__"
|
|
4035
|
+
]);
|
|
4036
|
+
function isInstallableSnapshotPath(path) {
|
|
4037
|
+
const parts = path.split("/");
|
|
4038
|
+
const fileName = parts.at(-1);
|
|
4039
|
+
if (!fileName || SNAPSHOT_EXCLUDED_FILES.has(fileName)) return false;
|
|
4040
|
+
return parts.slice(0, -1).every((part) => !SNAPSHOT_EXCLUDED_DIRS.has(part));
|
|
4041
|
+
}
|
|
4042
|
+
function hasCompleteNestedSnapshot(tree, skillMdPath, files) {
|
|
4043
|
+
const folderPath = getSkillFolderPath(skillMdPath);
|
|
4044
|
+
if (!folderPath) return true;
|
|
4045
|
+
const folderPrefix = `${folderPath}/`;
|
|
4046
|
+
const snapshotPaths = new Set(files.map((file) => file.path));
|
|
4047
|
+
return tree.tree.every((entry) => {
|
|
4048
|
+
if (entry.type !== "blob" || !entry.path.startsWith(folderPrefix)) return true;
|
|
4049
|
+
const relativePath = entry.path.slice(folderPrefix.length);
|
|
4050
|
+
return !isInstallableSnapshotPath(relativePath) || snapshotPaths.has(relativePath);
|
|
4051
|
+
});
|
|
4052
|
+
}
|
|
3980
4053
|
async function tryBlobInstall(ownerRepo, options = {}) {
|
|
3981
4054
|
if (options.ref !== void 0) return null;
|
|
3982
4055
|
const tree = await fetchRepoTree(ownerRepo, options.ref, options.getToken);
|
|
@@ -4033,10 +4106,10 @@ async function tryBlobInstall(ownerRepo, options = {}) {
|
|
|
4033
4106
|
};
|
|
4034
4107
|
}));
|
|
4035
4108
|
if (!downloads.every((d) => d.download !== null)) return null;
|
|
4109
|
+
if (!downloads.every(({ skill, download }) => hasCompleteNestedSnapshot(tree, skill.mdPath, download.files))) return null;
|
|
4036
4110
|
return {
|
|
4037
4111
|
skills: downloads.map(({ skill, download }) => {
|
|
4038
|
-
const
|
|
4039
|
-
const files = (mdPathLower.endsWith("/skill.md") ? skill.mdPath.slice(0, -9) : mdPathLower === "skill.md" ? "" : skill.mdPath.slice(0, -9)) ? download.files : download.files.filter((file) => file.path.toLowerCase() === "skill.md");
|
|
4112
|
+
const files = getSkillFolderPath(skill.mdPath) ? download.files : download.files.filter((file) => file.path.toLowerCase() === "skill.md");
|
|
4040
4113
|
return {
|
|
4041
4114
|
name: skill.name,
|
|
4042
4115
|
description: skill.description,
|
|
@@ -4051,7 +4124,7 @@ async function tryBlobInstall(ownerRepo, options = {}) {
|
|
|
4051
4124
|
tree
|
|
4052
4125
|
};
|
|
4053
4126
|
}
|
|
4054
|
-
var version$1 = "1.5.
|
|
4127
|
+
var version$1 = "1.5.26";
|
|
4055
4128
|
const isCancelled$1 = (value) => typeof value === "symbol";
|
|
4056
4129
|
const EVE_AGENT_LABEL = "eve agent";
|
|
4057
4130
|
async function isSourcePrivate(source) {
|
|
@@ -4199,11 +4272,21 @@ function buildResultLines(results, targetAgents) {
|
|
|
4199
4272
|
const { universal, symlinked: symlinkAgents } = splitAgentsByType(targetAgents);
|
|
4200
4273
|
const successfulSymlinks = results.filter((r) => !r.symlinkFailed && !r.skipped && !universal.includes(r.agent)).map((r) => r.agent);
|
|
4201
4274
|
const failedSymlinks = results.filter((r) => r.symlinkFailed && !r.skipped).map((r) => r.agent);
|
|
4275
|
+
const skippedSymlinks = results.filter((r) => r.skipped && r.skipReason === "missing-agent-project-directory" && symlinkAgents.includes(r.agent)).map((r) => r.agent);
|
|
4202
4276
|
if (universal.length > 0) lines.push(` ${import_picocolors.default.green("universal:")} ${formatList$1(universal)}`);
|
|
4203
4277
|
if (successfulSymlinks.length > 0) lines.push(` ${import_picocolors.default.dim("symlinked:")} ${formatList$1(successfulSymlinks)}`);
|
|
4204
4278
|
if (failedSymlinks.length > 0) lines.push(` ${import_picocolors.default.yellow("copied:")} ${formatList$1(failedSymlinks)}`);
|
|
4279
|
+
if (skippedSymlinks.length > 0) lines.push(` ${import_picocolors.default.yellow("skipped:")} ${formatList$1(skippedSymlinks)} ${import_picocolors.default.dim("(project directory not found)")}`);
|
|
4205
4280
|
return lines;
|
|
4206
4281
|
}
|
|
4282
|
+
function exitInstallationCancelled() {
|
|
4283
|
+
cancel("Installation cancelled");
|
|
4284
|
+
if (!process.stdin.isTTY) {
|
|
4285
|
+
console.error("Interactive prompt required but stdin is not a TTY. Nothing was installed. Use --agent <name> (or --agent '*') and -y to run non-interactively.");
|
|
4286
|
+
process.exit(1);
|
|
4287
|
+
}
|
|
4288
|
+
process.exit(0);
|
|
4289
|
+
}
|
|
4207
4290
|
async function promptForAgents(message, choices) {
|
|
4208
4291
|
let lastSelected;
|
|
4209
4292
|
try {
|
|
@@ -4263,6 +4346,17 @@ async function selectAgentsInteractive(options) {
|
|
|
4263
4346
|
return selected;
|
|
4264
4347
|
}
|
|
4265
4348
|
setVersion(version$1);
|
|
4349
|
+
function buildJsonSecurity(auditData, skillName, source) {
|
|
4350
|
+
const data = auditData?.[skillName];
|
|
4351
|
+
if (!data || Object.keys(data).length === 0) return null;
|
|
4352
|
+
const socketAlerts = data.socket?.alerts ?? 0;
|
|
4353
|
+
return {
|
|
4354
|
+
...data.ath && { gen: data.ath.risk },
|
|
4355
|
+
...data.socket && { socket: `${socketAlerts} alert${socketAlerts !== 1 ? "s" : ""}` },
|
|
4356
|
+
...data.snyk && { snyk: data.snyk.risk },
|
|
4357
|
+
...source && { details: `https://skills.sh/${source}` }
|
|
4358
|
+
};
|
|
4359
|
+
}
|
|
4266
4360
|
function isSkillsShPackUrl(url) {
|
|
4267
4361
|
try {
|
|
4268
4362
|
const parsed = new URL(url);
|
|
@@ -4337,10 +4431,7 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
4337
4431
|
maxVisible: 20,
|
|
4338
4432
|
selectAll: true
|
|
4339
4433
|
});
|
|
4340
|
-
if (isCancelled$1(selected))
|
|
4341
|
-
cancel("Installation cancelled");
|
|
4342
|
-
process.exit(0);
|
|
4343
|
-
}
|
|
4434
|
+
if (isCancelled$1(selected)) exitInstallationCancelled();
|
|
4344
4435
|
selectedSkills = selected;
|
|
4345
4436
|
}
|
|
4346
4437
|
let targetAgents;
|
|
@@ -4370,10 +4461,7 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
4370
4461
|
value: key,
|
|
4371
4462
|
label: config.displayName
|
|
4372
4463
|
})));
|
|
4373
|
-
if (
|
|
4374
|
-
cancel("Installation cancelled");
|
|
4375
|
-
process.exit(0);
|
|
4376
|
-
}
|
|
4464
|
+
if (isCancelled$1(selected)) exitInstallationCancelled();
|
|
4377
4465
|
targetAgents = selected;
|
|
4378
4466
|
}
|
|
4379
4467
|
else if (installedAgents.length === 1 || options.yes) {
|
|
@@ -4384,10 +4472,7 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
4384
4472
|
} else log.info(`Installing to: ${installedAgents.map((a) => import_picocolors.default.cyan(agents[a].displayName)).join(", ")}`);
|
|
4385
4473
|
} else {
|
|
4386
4474
|
const selected = await selectAgentsInteractive({ global: options.global });
|
|
4387
|
-
if (
|
|
4388
|
-
cancel("Installation cancelled");
|
|
4389
|
-
process.exit(0);
|
|
4390
|
-
}
|
|
4475
|
+
if (isCancelled$1(selected)) exitInstallationCancelled();
|
|
4391
4476
|
targetAgents = selected;
|
|
4392
4477
|
}
|
|
4393
4478
|
}
|
|
@@ -4406,10 +4491,7 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
4406
4491
|
hint: "Install in home directory (available across all projects)"
|
|
4407
4492
|
}]
|
|
4408
4493
|
});
|
|
4409
|
-
if (isCancel(scope))
|
|
4410
|
-
cancel("Installation cancelled");
|
|
4411
|
-
process.exit(0);
|
|
4412
|
-
}
|
|
4494
|
+
if (isCancel(scope)) exitInstallationCancelled();
|
|
4413
4495
|
installGlobally = scope;
|
|
4414
4496
|
}
|
|
4415
4497
|
let installMode = options.copy ? "copy" : "symlink";
|
|
@@ -4427,10 +4509,7 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
4427
4509
|
hint: "Independent copies for each agent"
|
|
4428
4510
|
}]
|
|
4429
4511
|
});
|
|
4430
|
-
if (isCancel(modeChoice))
|
|
4431
|
-
cancel("Installation cancelled");
|
|
4432
|
-
process.exit(0);
|
|
4433
|
-
}
|
|
4512
|
+
if (isCancel(modeChoice)) exitInstallationCancelled();
|
|
4434
4513
|
installMode = modeChoice;
|
|
4435
4514
|
} else if (uniqueDirs.size <= 1) installMode = "copy";
|
|
4436
4515
|
const cwd = process.cwd();
|
|
@@ -4460,10 +4539,7 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
4460
4539
|
note(summaryLines.join("\n"), "Installation Summary");
|
|
4461
4540
|
if (!options.yes) {
|
|
4462
4541
|
const confirmed = await confirm({ message: "Proceed with installation?" });
|
|
4463
|
-
if (isCancel(confirmed) || !confirmed)
|
|
4464
|
-
cancel("Installation cancelled");
|
|
4465
|
-
process.exit(0);
|
|
4466
|
-
}
|
|
4542
|
+
if (isCancel(confirmed) || !confirmed) exitInstallationCancelled();
|
|
4467
4543
|
}
|
|
4468
4544
|
const sourceIdentifier = wellKnownProvider.getSourceIdentifier(url);
|
|
4469
4545
|
const wellKnownPrivacyPromise = isSourcePrivate(sourceIdentifier).catch(() => null);
|
|
@@ -4484,6 +4560,7 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
4484
4560
|
console.log();
|
|
4485
4561
|
const successful = results.filter((r) => r.success);
|
|
4486
4562
|
const failed = results.filter((r) => !r.success);
|
|
4563
|
+
const successfulSkillNames = new Set(successful.map((r) => r.skill));
|
|
4487
4564
|
const skillFiles = {};
|
|
4488
4565
|
for (const skill of selectedSkills) skillFiles[skill.installName] = skill.sourceUrl;
|
|
4489
4566
|
if (await wellKnownPrivacyPromise !== true) track({
|
|
@@ -4498,7 +4575,6 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
4498
4575
|
sourceType: "well-known"
|
|
4499
4576
|
});
|
|
4500
4577
|
if (successful.length > 0 && installGlobally) {
|
|
4501
|
-
const successfulSkillNames = new Set(successful.map((r) => r.skill));
|
|
4502
4578
|
for (const skill of selectedSkills) if (successfulSkillNames.has(skill.installName)) try {
|
|
4503
4579
|
await addSkillToLock(skill.installName, {
|
|
4504
4580
|
source: sourceIdentifier,
|
|
@@ -4511,7 +4587,6 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
4511
4587
|
} catch {}
|
|
4512
4588
|
}
|
|
4513
4589
|
if (successful.length > 0 && !installGlobally) {
|
|
4514
|
-
const successfulSkillNames = new Set(successful.map((r) => r.skill));
|
|
4515
4590
|
for (const skill of selectedSkills) if (successfulSkillNames.has(skill.installName)) try {
|
|
4516
4591
|
const matchingResult = successful.find((r) => r.skill === skill.installName);
|
|
4517
4592
|
const installDir = matchingResult?.canonicalPath || matchingResult?.path;
|
|
@@ -4578,6 +4653,40 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
4578
4653
|
async function runAdd(args, options = {}) {
|
|
4579
4654
|
const source = args[0];
|
|
4580
4655
|
let installTipShown = false;
|
|
4656
|
+
const jsonMode = options.json === true;
|
|
4657
|
+
const jsonResults = [];
|
|
4658
|
+
const originalStdoutWrite = process.stdout.write;
|
|
4659
|
+
let stdoutSuppressed = false;
|
|
4660
|
+
if (jsonMode) {
|
|
4661
|
+
process.stdout.write = process.stderr.write.bind(process.stderr);
|
|
4662
|
+
stdoutSuppressed = true;
|
|
4663
|
+
}
|
|
4664
|
+
const restoreStdout = () => {
|
|
4665
|
+
if (stdoutSuppressed) {
|
|
4666
|
+
process.stdout.write = originalStdoutWrite;
|
|
4667
|
+
stdoutSuppressed = false;
|
|
4668
|
+
}
|
|
4669
|
+
};
|
|
4670
|
+
let jsonEmitted = false;
|
|
4671
|
+
const emitJson = () => {
|
|
4672
|
+
if (!jsonMode || jsonEmitted) return;
|
|
4673
|
+
jsonEmitted = true;
|
|
4674
|
+
restoreStdout();
|
|
4675
|
+
console.log(JSON.stringify(jsonResults, null, 2));
|
|
4676
|
+
};
|
|
4677
|
+
const emitJsonAndExit = (code, errorMessage) => {
|
|
4678
|
+
if (jsonMode) {
|
|
4679
|
+
if (errorMessage !== void 0) console.error(errorMessage);
|
|
4680
|
+
if (code !== 0 && jsonResults.length === 0) jsonResults.push({
|
|
4681
|
+
status: "failed",
|
|
4682
|
+
error: errorMessage ?? "Installation failed"
|
|
4683
|
+
});
|
|
4684
|
+
emitJson();
|
|
4685
|
+
}
|
|
4686
|
+
process.exit(code);
|
|
4687
|
+
};
|
|
4688
|
+
const emitJsonOnExit = () => emitJson();
|
|
4689
|
+
if (jsonMode) process.once("exit", emitJsonOnExit);
|
|
4581
4690
|
const showInstallTip = () => {
|
|
4582
4691
|
if (installTipShown) return;
|
|
4583
4692
|
log.message(import_picocolors.default.dim("Tip: use the --yes (-y) and --global (-g) flags to install without prompts."));
|
|
@@ -4593,8 +4702,9 @@ async function runAdd(args, options = {}) {
|
|
|
4593
4702
|
console.log(import_picocolors.default.dim(" Example:"));
|
|
4594
4703
|
console.log(` ${import_picocolors.default.cyan("npx skills add")} ${import_picocolors.default.yellow("vercel-labs/agent-skills")}`);
|
|
4595
4704
|
console.log();
|
|
4596
|
-
|
|
4705
|
+
emitJsonAndExit(1, "Missing required argument: source");
|
|
4597
4706
|
}
|
|
4707
|
+
const explicitlySelectedAgents = new Set(options.agent?.includes("*") ? [] : options.agent ?? []);
|
|
4598
4708
|
if (options.all) {
|
|
4599
4709
|
options.skill = ["*"];
|
|
4600
4710
|
options.agent = ["*"];
|
|
@@ -4608,13 +4718,19 @@ async function runAdd(args, options = {}) {
|
|
|
4608
4718
|
if (mappedAgent) options.agent = ensureUniversalAgents([mappedAgent]);
|
|
4609
4719
|
}
|
|
4610
4720
|
}
|
|
4721
|
+
if (jsonMode && !options.yes) emitJsonAndExit(1, "The --json flag requires --yes (or --all) to run non-interactively.");
|
|
4722
|
+
if (jsonMode && options.list) emitJsonAndExit(1, "The --json flag cannot be combined with --list.");
|
|
4611
4723
|
console.log();
|
|
4612
4724
|
if (!agentResult.isAgent) intro(import_picocolors.default.bgCyan(import_picocolors.default.black(" skills ")));
|
|
4613
4725
|
if (agentResult.isAgent) log.info(import_picocolors.default.bgCyan(import_picocolors.default.black(import_picocolors.default.bold(` ${agentResult.agent.name} `))) + " Agent detected — installing non-interactively");
|
|
4614
4726
|
else if (!process.stdin.isTTY) showInstallTip();
|
|
4615
4727
|
let tempDir = null;
|
|
4616
4728
|
try {
|
|
4617
|
-
const spinner$3 =
|
|
4729
|
+
const spinner$3 = jsonMode ? {
|
|
4730
|
+
start: () => {},
|
|
4731
|
+
stop: () => {},
|
|
4732
|
+
message: () => {}
|
|
4733
|
+
} : spinner();
|
|
4618
4734
|
spinner$3.start("Parsing source…");
|
|
4619
4735
|
const parsed = parseSource(source);
|
|
4620
4736
|
let directDownload = parsed.type === "download";
|
|
@@ -4628,6 +4744,7 @@ async function runAdd(args, options = {}) {
|
|
|
4628
4744
|
return isRepoPrivate(ownerRepo.owner, ownerRepo.repo).catch(() => null);
|
|
4629
4745
|
})();
|
|
4630
4746
|
if (parsed.type === "well-known") {
|
|
4747
|
+
if (jsonMode) emitJsonAndExit(1, "--json is not yet supported for well-known skill sources.");
|
|
4631
4748
|
if (await handleWellKnownSkills(source, parsed.url, options, spinner$3)) return;
|
|
4632
4749
|
directDownload = true;
|
|
4633
4750
|
}
|
|
@@ -4643,7 +4760,7 @@ async function runAdd(args, options = {}) {
|
|
|
4643
4760
|
if (!existsSync(parsed.localPath)) {
|
|
4644
4761
|
spinner$3.stop(import_picocolors.default.red("Path not found"));
|
|
4645
4762
|
outro(import_picocolors.default.red(`Local path does not exist: ${parsed.localPath}`));
|
|
4646
|
-
|
|
4763
|
+
emitJsonAndExit(1, `Local path does not exist: ${parsed.localPath}`);
|
|
4647
4764
|
}
|
|
4648
4765
|
spinner$3.stop("Local path validated");
|
|
4649
4766
|
spinner$3.start("Discovering skills…");
|
|
@@ -4662,15 +4779,18 @@ async function runAdd(args, options = {}) {
|
|
|
4662
4779
|
fullDepth: options.fullDepth
|
|
4663
4780
|
});
|
|
4664
4781
|
} else if (parsed.type === "github" && !options.fullDepth) {
|
|
4782
|
+
let attemptedBlobInstall = false;
|
|
4665
4783
|
const BLOB_ALLOWED_OWNERS = [
|
|
4666
4784
|
"vercel",
|
|
4667
4785
|
"vercel-labs",
|
|
4668
|
-
"heygen-com"
|
|
4786
|
+
"heygen-com",
|
|
4787
|
+
"remotion-dev"
|
|
4669
4788
|
];
|
|
4670
4789
|
const ownerRepo = getOwnerRepo(parsed);
|
|
4671
4790
|
const owner = ownerRepo?.split("/")[0]?.toLowerCase();
|
|
4672
4791
|
const isSelfHostedRepo = !!ownerRepo && Object.hasOwn(BLOB_ALLOWED_REPOS, ownerRepo.toLowerCase());
|
|
4673
4792
|
if (ownerRepo && owner && (isSelfHostedRepo || BLOB_ALLOWED_OWNERS.includes(owner))) {
|
|
4793
|
+
attemptedBlobInstall = true;
|
|
4674
4794
|
spinner$3.start("Fetching skills…");
|
|
4675
4795
|
blobResult = await tryBlobInstall(ownerRepo, {
|
|
4676
4796
|
subpath: parsed.subpath,
|
|
@@ -4679,13 +4799,13 @@ async function runAdd(args, options = {}) {
|
|
|
4679
4799
|
getToken: getGitHubToken,
|
|
4680
4800
|
includeInternal
|
|
4681
4801
|
});
|
|
4682
|
-
if (!blobResult) spinner$3.stop(import_picocolors.default.dim("Falling back to clone…"));
|
|
4683
4802
|
}
|
|
4684
4803
|
if (blobResult) {
|
|
4685
4804
|
skills = blobResult.skills;
|
|
4686
4805
|
spinner$3.stop(`Found ${import_picocolors.default.green(skills.length)} skill${skills.length > 1 ? "s" : ""}`);
|
|
4687
4806
|
} else {
|
|
4688
|
-
spinner$3.
|
|
4807
|
+
if (attemptedBlobInstall) spinner$3.message("Cloning repository…");
|
|
4808
|
+
else spinner$3.start("Cloning repository…");
|
|
4689
4809
|
tempDir = await cloneRepo(parsed.url, parsed.ref);
|
|
4690
4810
|
spinner$3.stop("Repository cloned");
|
|
4691
4811
|
spinner$3.start("Discovering skills…");
|
|
@@ -4708,7 +4828,7 @@ async function runAdd(args, options = {}) {
|
|
|
4708
4828
|
spinner$3.stop(import_picocolors.default.red("No skills found"));
|
|
4709
4829
|
outro(import_picocolors.default.red("No valid skills found. Skills require a SKILL.md with name and description."));
|
|
4710
4830
|
await cleanup(tempDir);
|
|
4711
|
-
|
|
4831
|
+
emitJsonAndExit(1, "No valid skills found. Skills require a SKILL.md with name and description.");
|
|
4712
4832
|
}
|
|
4713
4833
|
if (!blobResult) spinner$3.stop(`Found ${import_picocolors.default.green(skills.length)} skill${skills.length > 1 ? "s" : ""}`);
|
|
4714
4834
|
if (options.list) {
|
|
@@ -4741,7 +4861,7 @@ async function runAdd(args, options = {}) {
|
|
|
4741
4861
|
console.log();
|
|
4742
4862
|
outro("Use --skill <name> to install specific skills");
|
|
4743
4863
|
await cleanup(tempDir);
|
|
4744
|
-
|
|
4864
|
+
emitJsonAndExit(0);
|
|
4745
4865
|
}
|
|
4746
4866
|
let selectedSkills;
|
|
4747
4867
|
if (options.skill?.includes("*")) {
|
|
@@ -4749,12 +4869,19 @@ async function runAdd(args, options = {}) {
|
|
|
4749
4869
|
log.info(`Installing all ${skills.length} skills`);
|
|
4750
4870
|
} else if (options.skill && options.skill.length > 0) {
|
|
4751
4871
|
selectedSkills = filterSkills(skills, options.skill);
|
|
4872
|
+
if (jsonMode) {
|
|
4873
|
+
for (const requested of options.skill) if (filterSkills(skills, [requested]).length === 0) jsonResults.push({
|
|
4874
|
+
name: requested,
|
|
4875
|
+
status: "skipped",
|
|
4876
|
+
reason: "No matching skill found in source"
|
|
4877
|
+
});
|
|
4878
|
+
}
|
|
4752
4879
|
if (selectedSkills.length === 0) {
|
|
4753
4880
|
log.error(`No matching skills found for: ${options.skill.join(", ")}`);
|
|
4754
4881
|
log.info("Available skills:");
|
|
4755
4882
|
for (const s of skills) log.message(` - ${getSkillDisplayName(s)}`);
|
|
4756
4883
|
await cleanup(tempDir);
|
|
4757
|
-
|
|
4884
|
+
emitJsonAndExit(1, `No matching skills found for: ${options.skill.join(", ")}`);
|
|
4758
4885
|
}
|
|
4759
4886
|
log.info(`Selected ${selectedSkills.length} skill${selectedSkills.length !== 1 ? "s" : ""}: ${selectedSkills.map((s) => import_picocolors.default.cyan(getSkillDisplayName(s))).join(", ")}`);
|
|
4760
4887
|
} else if (skills.length === 1) {
|
|
@@ -4792,9 +4919,8 @@ async function runAdd(args, options = {}) {
|
|
|
4792
4919
|
selectAll: true
|
|
4793
4920
|
});
|
|
4794
4921
|
if (isCancelled$1(selected)) {
|
|
4795
|
-
cancel("Installation cancelled");
|
|
4796
4922
|
await cleanup(tempDir);
|
|
4797
|
-
|
|
4923
|
+
exitInstallationCancelled();
|
|
4798
4924
|
}
|
|
4799
4925
|
selectedSkills = selected;
|
|
4800
4926
|
}
|
|
@@ -4811,7 +4937,7 @@ async function runAdd(args, options = {}) {
|
|
|
4811
4937
|
log.error(`Invalid agents: ${invalidAgents.join(", ")}`);
|
|
4812
4938
|
log.info(`Valid agents: ${validAgents.join(", ")}`);
|
|
4813
4939
|
await cleanup(tempDir);
|
|
4814
|
-
|
|
4940
|
+
emitJsonAndExit(1, `Invalid agents: ${invalidAgents.join(", ")}`);
|
|
4815
4941
|
}
|
|
4816
4942
|
targetAgents = options.agent;
|
|
4817
4943
|
} else {
|
|
@@ -4825,21 +4951,21 @@ async function runAdd(args, options = {}) {
|
|
|
4825
4951
|
initialValue: true
|
|
4826
4952
|
});
|
|
4827
4953
|
if (isCancel(useEve)) {
|
|
4828
|
-
cancel("Installation cancelled");
|
|
4829
4954
|
await cleanup(tempDir);
|
|
4830
|
-
|
|
4955
|
+
exitInstallationCancelled();
|
|
4831
4956
|
}
|
|
4832
4957
|
if (useEve) {
|
|
4833
4958
|
targetAgents = ["eve"];
|
|
4959
|
+
if (!options.yes) explicitlySelectedAgents.add("eve");
|
|
4834
4960
|
log.info(`Installing to: ${import_picocolors.default.cyan(EVE_AGENT_LABEL)}`);
|
|
4835
4961
|
} else {
|
|
4836
4962
|
const selected = await selectAgentsInteractive({ global: options.global });
|
|
4837
|
-
if (
|
|
4838
|
-
cancel("Installation cancelled");
|
|
4963
|
+
if (isCancelled$1(selected)) {
|
|
4839
4964
|
await cleanup(tempDir);
|
|
4840
|
-
|
|
4965
|
+
exitInstallationCancelled();
|
|
4841
4966
|
}
|
|
4842
4967
|
targetAgents = selected;
|
|
4968
|
+
for (const agent of targetAgents) explicitlySelectedAgents.add(agent);
|
|
4843
4969
|
}
|
|
4844
4970
|
} else if (installedAgents.length === 0) if (options.yes) {
|
|
4845
4971
|
targetAgents = validAgents;
|
|
@@ -4850,12 +4976,12 @@ async function runAdd(args, options = {}) {
|
|
|
4850
4976
|
value: key,
|
|
4851
4977
|
label: config.displayName
|
|
4852
4978
|
})));
|
|
4853
|
-
if (
|
|
4854
|
-
cancel("Installation cancelled");
|
|
4979
|
+
if (isCancelled$1(selected)) {
|
|
4855
4980
|
await cleanup(tempDir);
|
|
4856
|
-
|
|
4981
|
+
exitInstallationCancelled();
|
|
4857
4982
|
}
|
|
4858
4983
|
targetAgents = selected;
|
|
4984
|
+
for (const agent of targetAgents) explicitlySelectedAgents.add(agent);
|
|
4859
4985
|
}
|
|
4860
4986
|
else if (installedAgents.length === 1 || options.yes) {
|
|
4861
4987
|
targetAgents = ensureUniversalAgents(installedAgents);
|
|
@@ -4865,15 +4991,18 @@ async function runAdd(args, options = {}) {
|
|
|
4865
4991
|
} else log.info(`Installing to: ${installedAgents.map((a) => import_picocolors.default.cyan(agents[a].displayName)).join(", ")}`);
|
|
4866
4992
|
} else {
|
|
4867
4993
|
const selected = await selectAgentsInteractive({ global: options.global });
|
|
4868
|
-
if (
|
|
4869
|
-
cancel("Installation cancelled");
|
|
4994
|
+
if (isCancelled$1(selected)) {
|
|
4870
4995
|
await cleanup(tempDir);
|
|
4871
|
-
|
|
4996
|
+
exitInstallationCancelled();
|
|
4872
4997
|
}
|
|
4873
4998
|
targetAgents = selected;
|
|
4999
|
+
for (const agent of targetAgents) explicitlySelectedAgents.add(agent);
|
|
4874
5000
|
}
|
|
4875
5001
|
}
|
|
4876
|
-
if (options.subagent && options.subagent.length > 0
|
|
5002
|
+
if (options.subagent && options.subagent.length > 0) {
|
|
5003
|
+
explicitlySelectedAgents.add("eve");
|
|
5004
|
+
if (!targetAgents.includes("eve")) targetAgents = [...targetAgents, "eve"];
|
|
5005
|
+
}
|
|
4877
5006
|
let eveSubagentTargets = [void 0];
|
|
4878
5007
|
if (targetAgents.includes("eve")) {
|
|
4879
5008
|
const availableSubagents = getEveSubagents(process.cwd());
|
|
@@ -4894,9 +5023,8 @@ async function runAdd(args, options = {}) {
|
|
|
4894
5023
|
required: true
|
|
4895
5024
|
});
|
|
4896
5025
|
if (isCancel(selectedSubagents)) {
|
|
4897
|
-
cancel("Installation cancelled");
|
|
4898
5026
|
await cleanup(tempDir);
|
|
4899
|
-
|
|
5027
|
+
exitInstallationCancelled();
|
|
4900
5028
|
}
|
|
4901
5029
|
eveSubagentTargets = selectedSubagents.map((s) => s === "" ? void 0 : s);
|
|
4902
5030
|
}
|
|
@@ -4918,9 +5046,8 @@ async function runAdd(args, options = {}) {
|
|
|
4918
5046
|
}]
|
|
4919
5047
|
});
|
|
4920
5048
|
if (isCancel(scope)) {
|
|
4921
|
-
cancel("Installation cancelled");
|
|
4922
5049
|
await cleanup(tempDir);
|
|
4923
|
-
|
|
5050
|
+
exitInstallationCancelled();
|
|
4924
5051
|
}
|
|
4925
5052
|
installGlobally = scope;
|
|
4926
5053
|
}
|
|
@@ -4941,9 +5068,8 @@ async function runAdd(args, options = {}) {
|
|
|
4941
5068
|
}]
|
|
4942
5069
|
});
|
|
4943
5070
|
if (isCancel(modeChoice)) {
|
|
4944
|
-
cancel("Installation cancelled");
|
|
4945
5071
|
await cleanup(tempDir);
|
|
4946
|
-
|
|
5072
|
+
exitInstallationCancelled();
|
|
4947
5073
|
}
|
|
4948
5074
|
installMode = modeChoice;
|
|
4949
5075
|
} else if (uniqueDirs.size <= 1 || allEve) installMode = "copy";
|
|
@@ -5000,8 +5126,10 @@ async function runAdd(args, options = {}) {
|
|
|
5000
5126
|
}
|
|
5001
5127
|
console.log();
|
|
5002
5128
|
note(summaryLines.join("\n"), "Installation Summary");
|
|
5129
|
+
let auditDataForJson = null;
|
|
5003
5130
|
try {
|
|
5004
5131
|
const auditData = await auditPromise;
|
|
5132
|
+
auditDataForJson = auditData;
|
|
5005
5133
|
if (auditData && ownerRepoForAudit) {
|
|
5006
5134
|
const securityLines = buildSecurityLines(auditData, selectedSkills.map((s) => ({
|
|
5007
5135
|
slug: getSkillDisplayName(s),
|
|
@@ -5013,9 +5141,8 @@ async function runAdd(args, options = {}) {
|
|
|
5013
5141
|
if (!options.yes) {
|
|
5014
5142
|
const confirmed = await confirm({ message: "Proceed with installation?" });
|
|
5015
5143
|
if (isCancel(confirmed) || !confirmed) {
|
|
5016
|
-
cancel("Installation cancelled");
|
|
5017
5144
|
await cleanup(tempDir);
|
|
5018
|
-
|
|
5145
|
+
exitInstallationCancelled();
|
|
5019
5146
|
}
|
|
5020
5147
|
}
|
|
5021
5148
|
spinner$3.start("Installing skills…");
|
|
@@ -5031,12 +5158,14 @@ async function runAdd(args, options = {}) {
|
|
|
5031
5158
|
}, agent, {
|
|
5032
5159
|
global: installGlobally,
|
|
5033
5160
|
mode: installMode,
|
|
5034
|
-
eveSubagent: subagent
|
|
5161
|
+
eveSubagent: subagent,
|
|
5162
|
+
createMissingAgentRoot: explicitlySelectedAgents.has(agent)
|
|
5035
5163
|
});
|
|
5036
5164
|
} else result = await installSkillForAgent(skill, agent, {
|
|
5037
5165
|
global: installGlobally,
|
|
5038
5166
|
mode: installMode,
|
|
5039
|
-
eveSubagent: subagent
|
|
5167
|
+
eveSubagent: subagent,
|
|
5168
|
+
createMissingAgentRoot: explicitlySelectedAgents.has(agent)
|
|
5040
5169
|
});
|
|
5041
5170
|
results.push({
|
|
5042
5171
|
skill: getSkillDisplayName(skill),
|
|
@@ -5049,6 +5178,7 @@ async function runAdd(args, options = {}) {
|
|
|
5049
5178
|
console.log();
|
|
5050
5179
|
const successful = results.filter((r) => r.success);
|
|
5051
5180
|
const failed = results.filter((r) => !r.success);
|
|
5181
|
+
const successfulSkillNames = new Set(successful.map((r) => r.skill));
|
|
5052
5182
|
const skillFiles = {};
|
|
5053
5183
|
for (const skill of selectedSkills) if (blobResult && "repoPath" in skill) skillFiles[skill.name] = skill.repoPath;
|
|
5054
5184
|
else if (tempDir && skill.path === tempDir) skillFiles[skill.name] = "SKILL.md";
|
|
@@ -5076,8 +5206,16 @@ async function runAdd(args, options = {}) {
|
|
|
5076
5206
|
skillFiles: JSON.stringify(skillFiles),
|
|
5077
5207
|
metadata: options.metadata
|
|
5078
5208
|
});
|
|
5209
|
+
const installedSkillHashes = /* @__PURE__ */ new Map();
|
|
5210
|
+
if (successful.length > 0 && (jsonMode || !installGlobally)) for (const skill of selectedSkills) {
|
|
5211
|
+
const skillDisplayName = getSkillDisplayName(skill);
|
|
5212
|
+
if (!successfulSkillNames.has(skillDisplayName)) continue;
|
|
5213
|
+
try {
|
|
5214
|
+
const computedHash = blobResult && "snapshotHash" in skill ? skill.snapshotHash : await computeSkillFolderHash(skill.path);
|
|
5215
|
+
installedSkillHashes.set(skillDisplayName, computedHash);
|
|
5216
|
+
} catch {}
|
|
5217
|
+
}
|
|
5079
5218
|
if (successful.length > 0 && installGlobally && normalizedSource) {
|
|
5080
|
-
const successfulSkillNames = new Set(successful.map((r) => r.skill));
|
|
5081
5219
|
let cachedTree;
|
|
5082
5220
|
if (parsed.type === "github" && !blobResult) cachedTree = await fetchRepoTree(normalizedSource, parsed.ref, getGitHubToken);
|
|
5083
5221
|
for (const skill of selectedSkills) {
|
|
@@ -5108,13 +5246,13 @@ async function runAdd(args, options = {}) {
|
|
|
5108
5246
|
}
|
|
5109
5247
|
}
|
|
5110
5248
|
if (successful.length > 0 && !installGlobally && !directDownload) {
|
|
5111
|
-
const successfulSkillNames = new Set(successful.map((r) => r.skill));
|
|
5112
5249
|
const eveSubagents = targetAgents.includes("eve") ? eveSubagentTargets.map((s) => s ?? "") : void 0;
|
|
5113
5250
|
const recordSubagents = eveSubagents && (eveSubagents.length > 1 || eveSubagents.some((s) => s !== ""));
|
|
5114
5251
|
for (const skill of selectedSkills) {
|
|
5115
5252
|
const skillDisplayName = getSkillDisplayName(skill);
|
|
5116
5253
|
if (successfulSkillNames.has(skillDisplayName)) try {
|
|
5117
|
-
const computedHash =
|
|
5254
|
+
const computedHash = installedSkillHashes.get(skillDisplayName);
|
|
5255
|
+
if (computedHash === void 0) continue;
|
|
5118
5256
|
const skillPathValue = skillFiles[skill.name];
|
|
5119
5257
|
await addSkillToLocalLock(skill.name, {
|
|
5120
5258
|
source: lockSource || parsed.url,
|
|
@@ -5128,6 +5266,37 @@ async function runAdd(args, options = {}) {
|
|
|
5128
5266
|
} catch {}
|
|
5129
5267
|
}
|
|
5130
5268
|
}
|
|
5269
|
+
if (jsonMode) {
|
|
5270
|
+
const jsonSource = normalizedSource ?? (parsed.type === "local" ? parsed.localPath : parsed.url);
|
|
5271
|
+
for (const skill of selectedSkills) {
|
|
5272
|
+
const name = getSkillDisplayName(skill);
|
|
5273
|
+
const skillResults = results.filter((r) => r.skill === name);
|
|
5274
|
+
const failures = skillResults.filter((r) => !r.success);
|
|
5275
|
+
if (failures.length > 0) {
|
|
5276
|
+
jsonResults.push({
|
|
5277
|
+
name,
|
|
5278
|
+
status: "failed",
|
|
5279
|
+
error: failures[0].error ?? "Installation failed"
|
|
5280
|
+
});
|
|
5281
|
+
continue;
|
|
5282
|
+
}
|
|
5283
|
+
jsonResults.push({
|
|
5284
|
+
name,
|
|
5285
|
+
status: "installed",
|
|
5286
|
+
source: jsonSource,
|
|
5287
|
+
ref: parsed.ref ?? null,
|
|
5288
|
+
hash: installedSkillHashes.get(name) ?? null,
|
|
5289
|
+
path: skillResults[0]?.canonicalPath ?? skillResults[0]?.path,
|
|
5290
|
+
scope: installGlobally ? "global" : "project",
|
|
5291
|
+
agents: skillResults.filter((r) => !r.skipped).map((r) => r.agent),
|
|
5292
|
+
mode: skillResults[0]?.mode ?? installMode,
|
|
5293
|
+
security: buildJsonSecurity(auditDataForJson, name, ownerRepoForAudit)
|
|
5294
|
+
});
|
|
5295
|
+
}
|
|
5296
|
+
emitJson();
|
|
5297
|
+
if (failed.length > 0 || jsonResults.some((result) => result.status === "skipped")) process.exitCode = 1;
|
|
5298
|
+
return;
|
|
5299
|
+
}
|
|
5131
5300
|
if (successful.length > 0) {
|
|
5132
5301
|
const bySkill = /* @__PURE__ */ new Map();
|
|
5133
5302
|
const groupedResults = {};
|
|
@@ -5205,8 +5374,10 @@ async function runAdd(args, options = {}) {
|
|
|
5205
5374
|
} else log.error(error instanceof Error ? error.message : "Unknown error occurred");
|
|
5206
5375
|
showInstallTip();
|
|
5207
5376
|
outro(import_picocolors.default.red("Installation failed"));
|
|
5208
|
-
|
|
5377
|
+
emitJsonAndExit(1, error instanceof GitCloneError ? `Failed to clone repository\n${error.message}` : error instanceof Error ? error.message : "Unknown error occurred");
|
|
5209
5378
|
} finally {
|
|
5379
|
+
if (jsonMode) process.removeListener("exit", emitJsonOnExit);
|
|
5380
|
+
restoreStdout();
|
|
5210
5381
|
await cleanup(tempDir);
|
|
5211
5382
|
}
|
|
5212
5383
|
}
|
|
@@ -5294,6 +5465,7 @@ function parseAddOptions(args) {
|
|
|
5294
5465
|
errors.push("--metadata must be valid JSON");
|
|
5295
5466
|
}
|
|
5296
5467
|
} else if (arg === "--full-depth") options.fullDepth = true;
|
|
5468
|
+
else if (arg === "--json") options.json = true;
|
|
5297
5469
|
else if (arg === "--copy") options.copy = true;
|
|
5298
5470
|
else if (arg === "--subagent") {
|
|
5299
5471
|
options.subagent = options.subagent || [];
|
|
@@ -6192,7 +6364,11 @@ async function removeCommand(skillNames, options) {
|
|
|
6192
6364
|
const scanDir = async (dir) => {
|
|
6193
6365
|
try {
|
|
6194
6366
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
6195
|
-
for (const entry of entries)
|
|
6367
|
+
for (const entry of entries) {
|
|
6368
|
+
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
6369
|
+
if (!await hasSkillMd(join(dir, entry.name))) continue;
|
|
6370
|
+
skillNamesSet.add(entry.name);
|
|
6371
|
+
}
|
|
6196
6372
|
} catch (err) {
|
|
6197
6373
|
if (err instanceof Error && err.code !== "ENOENT") log.warn(`Could not scan directory ${dir}: ${err.message}`);
|
|
6198
6374
|
}
|
|
@@ -7124,7 +7300,8 @@ async function runUpdate(args = []) {
|
|
|
7124
7300
|
const BLOB_ALLOWED_OWNERS = [
|
|
7125
7301
|
"vercel",
|
|
7126
7302
|
"vercel-labs",
|
|
7127
|
-
"heygen-com"
|
|
7303
|
+
"heygen-com",
|
|
7304
|
+
"remotion-dev"
|
|
7128
7305
|
];
|
|
7129
7306
|
const EXCLUDE_FILES = /* @__PURE__ */ new Set(["metadata.json"]);
|
|
7130
7307
|
const EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
@@ -7140,6 +7317,10 @@ const USE_AGENT_CONFIGS = {
|
|
|
7140
7317
|
codex: {
|
|
7141
7318
|
command: "codex",
|
|
7142
7319
|
args: []
|
|
7320
|
+
},
|
|
7321
|
+
"sarvam-code": {
|
|
7322
|
+
command: "sarvam-code",
|
|
7323
|
+
args: []
|
|
7143
7324
|
}
|
|
7144
7325
|
};
|
|
7145
7326
|
const SUPPORTED_USE_AGENTS = Object.keys(USE_AGENT_CONFIGS);
|
|
@@ -7602,6 +7783,7 @@ ${BOLD}Add Options:${RESET}
|
|
|
7602
7783
|
--subagent <names> Install to Eve subagents (use 'root' for the root agent)
|
|
7603
7784
|
--all Shorthand for --skill '*' --agent '*' -y
|
|
7604
7785
|
--full-depth Search all subdirectories even when a root SKILL.md exists
|
|
7786
|
+
--json Output results as JSON (machine-readable, no ANSI codes)
|
|
7605
7787
|
|
|
7606
7788
|
${BOLD}Use Options:${RESET}
|
|
7607
7789
|
-s, --skill <skill> Specify the skill to use
|
|
@@ -7635,6 +7817,7 @@ ${BOLD}Examples:${RESET}
|
|
|
7635
7817
|
${DIM}$${RESET} skills add vercel-labs/agent-skills -g
|
|
7636
7818
|
${DIM}$${RESET} skills add vercel-labs/agent-skills --agent claude-code cursor
|
|
7637
7819
|
${DIM}$${RESET} skills add vercel-labs/agent-skills --skill pr-review commit
|
|
7820
|
+
${DIM}$${RESET} skills add vercel-labs/agent-skills --json -y ${DIM}# JSON output${RESET}
|
|
7638
7821
|
${DIM}$${RESET} skills remove ${DIM}# interactive remove${RESET}
|
|
7639
7822
|
${DIM}$${RESET} skills remove web-design ${DIM}# remove by name${RESET}
|
|
7640
7823
|
${DIM}$${RESET} skills rm --global frontend-design
|
|
@@ -7769,10 +7952,11 @@ async function main() {
|
|
|
7769
7952
|
case "install":
|
|
7770
7953
|
case "a":
|
|
7771
7954
|
case "add": {
|
|
7772
|
-
if (!inAgent) showLogo();
|
|
7773
7955
|
const { source: addSource, options: addOpts, errors } = parseAddOptions(restArgs);
|
|
7956
|
+
if (!inAgent && !addOpts.json) showLogo();
|
|
7774
7957
|
if (errors.length > 0) {
|
|
7775
7958
|
for (const error of errors) console.error(`Error: ${error}`);
|
|
7959
|
+
if (addOpts.json) console.log("[]");
|
|
7776
7960
|
process.exitCode = 1;
|
|
7777
7961
|
break;
|
|
7778
7962
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skills",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.26",
|
|
4
4
|
"description": "The open agent skills ecosystem",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -64,6 +64,7 @@
|
|
|
64
64
|
"eve",
|
|
65
65
|
"firebender",
|
|
66
66
|
"forgecode",
|
|
67
|
+
"fx",
|
|
67
68
|
"gemini-cli",
|
|
68
69
|
"github-copilot",
|
|
69
70
|
"goose",
|
|
@@ -97,6 +98,7 @@
|
|
|
97
98
|
"reasonix",
|
|
98
99
|
"rovodev",
|
|
99
100
|
"roo",
|
|
101
|
+
"sarvam-code",
|
|
100
102
|
"tabnine-cli",
|
|
101
103
|
"terramind",
|
|
102
104
|
"tinycloud",
|