skills 1.2.0 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/dist/cli.mjs +108 -89
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -3,9 +3,7 @@
|
|
|
3
3
|
The CLI for the open agent skills ecosystem.
|
|
4
4
|
|
|
5
5
|
<!-- agent-list:start -->
|
|
6
|
-
|
|
7
|
-
Supports **OpenCode**, **Claude Code**, **Codex**, **Cursor**, and [29 more](#available-agents).
|
|
8
|
-
|
|
6
|
+
Supports **OpenCode**, **Claude Code**, **Codex**, **Cursor**, and [31 more](#available-agents).
|
|
9
7
|
<!-- agent-list:end -->
|
|
10
8
|
|
|
11
9
|
## Install a Skill
|
|
@@ -205,7 +203,7 @@ Discover skills at **[skills.sh](https://skills.sh)**
|
|
|
205
203
|
|
|
206
204
|
Skills can be installed to any of these agents:
|
|
207
205
|
|
|
208
|
-
<!--
|
|
206
|
+
<!-- supported-agents:start -->
|
|
209
207
|
|
|
210
208
|
| Agent | `--agent` | Project Path | Global Path |
|
|
211
209
|
| ------------------ | ----------------- | ---------------------- | -------------------------------------- |
|
|
@@ -237,12 +235,14 @@ Skills can be installed to any of these agents:
|
|
|
237
235
|
| Qwen Code | `qwen-code` | `.qwen/skills/` | `~/.qwen/skills/` |
|
|
238
236
|
| Roo Code | `roo` | `.roo/skills/` | `~/.roo/skills/` |
|
|
239
237
|
| Trae | `trae` | `.trae/skills/` | `~/.trae/skills/` |
|
|
238
|
+
| Trae CN | `trae-cn` | `.trae/skills/` | `~/.trae-cn/skills/` |
|
|
240
239
|
| Windsurf | `windsurf` | `.windsurf/skills/` | `~/.codeium/windsurf/skills/` |
|
|
241
240
|
| Zencoder | `zencoder` | `.zencoder/skills/` | `~/.zencoder/skills/` |
|
|
241
|
+
| OpenClaude IDE | `openclaude` | `.openclaude/skills/` | `~/.openclaude/skills/` |
|
|
242
242
|
| Neovate | `neovate` | `.neovate/skills/` | `~/.neovate/skills/` |
|
|
243
243
|
| Pochi | `pochi` | `.pochi/skills/` | `~/.pochi/skills/` |
|
|
244
244
|
|
|
245
|
-
<!--
|
|
245
|
+
<!-- supported-agents:end -->
|
|
246
246
|
|
|
247
247
|
> [!NOTE]
|
|
248
248
|
> **Kiro CLI users:** After installing skills, manually add them to your custom agent's `resources` in
|
|
@@ -306,7 +306,6 @@ metadata:
|
|
|
306
306
|
The CLI searches for skills in these locations within a repository:
|
|
307
307
|
|
|
308
308
|
<!-- skill-discovery:start -->
|
|
309
|
-
|
|
310
309
|
- Root directory (if it contains `SKILL.md`)
|
|
311
310
|
- `skills/`
|
|
312
311
|
- `skills/.curated/`
|
|
@@ -334,6 +333,7 @@ The CLI searches for skills in these locations within a repository:
|
|
|
334
333
|
- `.mcpjam/skills/`
|
|
335
334
|
- `.mux/skills/`
|
|
336
335
|
- `.opencode/skills/`
|
|
336
|
+
- `.openclaude/skills/`
|
|
337
337
|
- `.openhands/skills/`
|
|
338
338
|
- `.pi/skills/`
|
|
339
339
|
- `.qoder/skills/`
|
package/dist/cli.mjs
CHANGED
|
@@ -15,6 +15,7 @@ import { homedir, platform, tmpdir } from "os";
|
|
|
15
15
|
import "crypto";
|
|
16
16
|
import { fileURLToPath } from "url";
|
|
17
17
|
import { access, cp, lstat, mkdir, mkdtemp, readFile, readdir, readlink, rm, stat, symlink, writeFile } from "fs/promises";
|
|
18
|
+
import { xdgConfig } from "xdg-basedir";
|
|
18
19
|
import * as readline from "readline";
|
|
19
20
|
var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
|
|
20
21
|
function getOwnerRepo(parsed) {
|
|
@@ -23,6 +24,23 @@ function getOwnerRepo(parsed) {
|
|
|
23
24
|
if (match) return `${match[1]}/${match[2]}`;
|
|
24
25
|
return null;
|
|
25
26
|
}
|
|
27
|
+
function parseOwnerRepo(ownerRepo) {
|
|
28
|
+
const match = ownerRepo.match(/^([^/]+)\/([^/]+)$/);
|
|
29
|
+
if (match) return {
|
|
30
|
+
owner: match[1],
|
|
31
|
+
repo: match[2]
|
|
32
|
+
};
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
async function isRepoPrivate(owner, repo) {
|
|
36
|
+
try {
|
|
37
|
+
const res = await fetch(`https://api.github.com/repos/${owner}/${repo}`);
|
|
38
|
+
if (!res.ok) return null;
|
|
39
|
+
return (await res.json()).private === true;
|
|
40
|
+
} catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
26
44
|
function isLocalPath(input) {
|
|
27
45
|
return isAbsolute(input) || input.startsWith("./") || input.startsWith("../") || input === "." || input === ".." || /^[a-zA-Z]:[/\\]/.test(input);
|
|
28
46
|
}
|
|
@@ -75,22 +93,22 @@ function parseSource(input) {
|
|
|
75
93
|
url: `https://github.com/${owner}/${repo.replace(/\.git$/, "")}.git`
|
|
76
94
|
};
|
|
77
95
|
}
|
|
78
|
-
const gitlabTreeWithPathMatch = input.match(
|
|
96
|
+
const gitlabTreeWithPathMatch = input.match(/^(https?):\/\/([^/]+)\/(.+?)\/-\/tree\/([^/]+)\/(.+)/);
|
|
79
97
|
if (gitlabTreeWithPathMatch) {
|
|
80
|
-
const [,
|
|
81
|
-
return {
|
|
98
|
+
const [, protocol, hostname, repoPath, ref, subpath] = gitlabTreeWithPathMatch;
|
|
99
|
+
if (hostname !== "github.com" && repoPath) return {
|
|
82
100
|
type: "gitlab",
|
|
83
|
-
url:
|
|
101
|
+
url: `${protocol}://${hostname}/${repoPath.replace(/\.git$/, "")}.git`,
|
|
84
102
|
ref,
|
|
85
103
|
subpath
|
|
86
104
|
};
|
|
87
105
|
}
|
|
88
|
-
const gitlabTreeMatch = input.match(
|
|
106
|
+
const gitlabTreeMatch = input.match(/^(https?):\/\/([^/]+)\/(.+?)\/-\/tree\/([^/]+)$/);
|
|
89
107
|
if (gitlabTreeMatch) {
|
|
90
|
-
const [,
|
|
91
|
-
return {
|
|
108
|
+
const [, protocol, hostname, repoPath, ref] = gitlabTreeMatch;
|
|
109
|
+
if (hostname !== "github.com" && repoPath) return {
|
|
92
110
|
type: "gitlab",
|
|
93
|
-
url:
|
|
111
|
+
url: `${protocol}://${hostname}/${repoPath.replace(/\.git$/, "")}.git`,
|
|
94
112
|
ref
|
|
95
113
|
};
|
|
96
114
|
}
|
|
@@ -273,6 +291,7 @@ async function discoverSkills(basePath, subpath, options) {
|
|
|
273
291
|
join(searchPath, ".kiro/skills"),
|
|
274
292
|
join(searchPath, ".mux/skills"),
|
|
275
293
|
join(searchPath, ".neovate/skills"),
|
|
294
|
+
join(searchPath, ".openclaude/skills"),
|
|
276
295
|
join(searchPath, ".opencode/skills"),
|
|
277
296
|
join(searchPath, ".openhands/skills"),
|
|
278
297
|
join(searchPath, ".pi/skills"),
|
|
@@ -319,6 +338,7 @@ function filterSkills(skills, inputNames) {
|
|
|
319
338
|
});
|
|
320
339
|
}
|
|
321
340
|
const home = homedir();
|
|
341
|
+
const configHome = xdgConfig ?? join(home, ".config");
|
|
322
342
|
const codexHome = process.env.CODEX_HOME?.trim() || join(home, ".codex");
|
|
323
343
|
const claudeHome = process.env.CLAUDE_CONFIG_DIR?.trim() || join(home, ".claude");
|
|
324
344
|
const agents = {
|
|
@@ -326,9 +346,9 @@ const agents = {
|
|
|
326
346
|
name: "amp",
|
|
327
347
|
displayName: "Amp",
|
|
328
348
|
skillsDir: ".agents/skills",
|
|
329
|
-
globalSkillsDir: join(
|
|
349
|
+
globalSkillsDir: join(configHome, "agents/skills"),
|
|
330
350
|
detectInstalled: async () => {
|
|
331
|
-
return existsSync(join(
|
|
351
|
+
return existsSync(join(configHome, "amp"));
|
|
332
352
|
}
|
|
333
353
|
},
|
|
334
354
|
antigravity: {
|
|
@@ -452,9 +472,9 @@ const agents = {
|
|
|
452
472
|
name: "goose",
|
|
453
473
|
displayName: "Goose",
|
|
454
474
|
skillsDir: ".goose/skills",
|
|
455
|
-
globalSkillsDir: join(
|
|
475
|
+
globalSkillsDir: join(configHome, "goose/skills"),
|
|
456
476
|
detectInstalled: async () => {
|
|
457
|
-
return existsSync(join(
|
|
477
|
+
return existsSync(join(configHome, "goose"));
|
|
458
478
|
}
|
|
459
479
|
},
|
|
460
480
|
junie: {
|
|
@@ -524,9 +544,18 @@ const agents = {
|
|
|
524
544
|
name: "opencode",
|
|
525
545
|
displayName: "OpenCode",
|
|
526
546
|
skillsDir: ".opencode/skills",
|
|
527
|
-
globalSkillsDir: join(
|
|
547
|
+
globalSkillsDir: join(configHome, "opencode/skills"),
|
|
528
548
|
detectInstalled: async () => {
|
|
529
|
-
return existsSync(join(
|
|
549
|
+
return existsSync(join(configHome, "opencode")) || existsSync(join(claudeHome, "skills"));
|
|
550
|
+
}
|
|
551
|
+
},
|
|
552
|
+
openclaude: {
|
|
553
|
+
name: "openclaude",
|
|
554
|
+
displayName: "OpenClaude IDE",
|
|
555
|
+
skillsDir: ".openclaude/skills",
|
|
556
|
+
globalSkillsDir: join(home, ".openclaude/skills"),
|
|
557
|
+
detectInstalled: async () => {
|
|
558
|
+
return existsSync(join(home, ".openclaude")) || existsSync(join(process.cwd(), ".openclaude"));
|
|
530
559
|
}
|
|
531
560
|
},
|
|
532
561
|
openhands: {
|
|
@@ -583,6 +612,15 @@ const agents = {
|
|
|
583
612
|
return existsSync(join(home, ".trae"));
|
|
584
613
|
}
|
|
585
614
|
},
|
|
615
|
+
"trae-cn": {
|
|
616
|
+
name: "trae-cn",
|
|
617
|
+
displayName: "Trae CN",
|
|
618
|
+
skillsDir: ".trae/skills",
|
|
619
|
+
globalSkillsDir: join(home, ".trae-cn/skills"),
|
|
620
|
+
detectInstalled: async () => {
|
|
621
|
+
return existsSync(join(home, ".trae-cn"));
|
|
622
|
+
}
|
|
623
|
+
},
|
|
586
624
|
windsurf: {
|
|
587
625
|
name: "windsurf",
|
|
588
626
|
displayName: "Windsurf",
|
|
@@ -642,21 +680,13 @@ function getCanonicalSkillsDir(global, cwd) {
|
|
|
642
680
|
function resolveSymlinkTarget(linkPath, linkTarget) {
|
|
643
681
|
return resolve(dirname(linkPath), linkTarget);
|
|
644
682
|
}
|
|
645
|
-
async function
|
|
683
|
+
async function cleanAndCreateDirectory(path) {
|
|
646
684
|
try {
|
|
647
|
-
|
|
685
|
+
await rm(path, {
|
|
648
686
|
recursive: true,
|
|
649
687
|
force: true
|
|
650
688
|
});
|
|
651
|
-
} catch
|
|
652
|
-
if (err && typeof err === "object" && "code" in err) {
|
|
653
|
-
if (err.code !== "ENOENT") if (err.code === "ELOOP") await rm(path, {
|
|
654
|
-
recursive: true,
|
|
655
|
-
force: true
|
|
656
|
-
});
|
|
657
|
-
else throw err;
|
|
658
|
-
} else if (err) throw err;
|
|
659
|
-
}
|
|
689
|
+
} catch {}
|
|
660
690
|
await mkdir(path, { recursive: true });
|
|
661
691
|
}
|
|
662
692
|
async function createSymlink(target, linkPath) {
|
|
@@ -705,7 +735,7 @@ async function installSkillForAgent(skill, agentType, options = {}) {
|
|
|
705
735
|
};
|
|
706
736
|
try {
|
|
707
737
|
if (installMode === "copy") {
|
|
708
|
-
await
|
|
738
|
+
await cleanAndCreateDirectory(agentDir);
|
|
709
739
|
await copyDirectory(skill.path, agentDir);
|
|
710
740
|
return {
|
|
711
741
|
success: true,
|
|
@@ -713,16 +743,10 @@ async function installSkillForAgent(skill, agentType, options = {}) {
|
|
|
713
743
|
mode: "copy"
|
|
714
744
|
};
|
|
715
745
|
}
|
|
716
|
-
await
|
|
746
|
+
await cleanAndCreateDirectory(canonicalDir);
|
|
717
747
|
await copyDirectory(skill.path, canonicalDir);
|
|
718
748
|
if (!await createSymlink(canonicalDir, agentDir)) {
|
|
719
|
-
|
|
720
|
-
await rm(agentDir, {
|
|
721
|
-
recursive: true,
|
|
722
|
-
force: true
|
|
723
|
-
});
|
|
724
|
-
} catch {}
|
|
725
|
-
await mkdir(agentDir, { recursive: true });
|
|
749
|
+
await cleanAndCreateDirectory(agentDir);
|
|
726
750
|
await copyDirectory(skill.path, agentDir);
|
|
727
751
|
return {
|
|
728
752
|
success: true,
|
|
@@ -821,7 +845,7 @@ async function installRemoteSkillForAgent(skill, agentType, options = {}) {
|
|
|
821
845
|
};
|
|
822
846
|
try {
|
|
823
847
|
if (installMode === "copy") {
|
|
824
|
-
await
|
|
848
|
+
await cleanAndCreateDirectory(agentDir);
|
|
825
849
|
await writeFile(join(agentDir, "SKILL.md"), skill.content, "utf-8");
|
|
826
850
|
return {
|
|
827
851
|
success: true,
|
|
@@ -829,16 +853,10 @@ async function installRemoteSkillForAgent(skill, agentType, options = {}) {
|
|
|
829
853
|
mode: "copy"
|
|
830
854
|
};
|
|
831
855
|
}
|
|
832
|
-
await
|
|
856
|
+
await cleanAndCreateDirectory(canonicalDir);
|
|
833
857
|
await writeFile(join(canonicalDir, "SKILL.md"), skill.content, "utf-8");
|
|
834
858
|
if (!await createSymlink(canonicalDir, agentDir)) {
|
|
835
|
-
|
|
836
|
-
await rm(agentDir, {
|
|
837
|
-
recursive: true,
|
|
838
|
-
force: true
|
|
839
|
-
});
|
|
840
|
-
} catch {}
|
|
841
|
-
await mkdir(agentDir, { recursive: true });
|
|
859
|
+
await cleanAndCreateDirectory(agentDir);
|
|
842
860
|
await writeFile(join(agentDir, "SKILL.md"), skill.content, "utf-8");
|
|
843
861
|
return {
|
|
844
862
|
success: true,
|
|
@@ -886,7 +904,6 @@ async function installWellKnownSkillForAgent(skill, agentType, options = {}) {
|
|
|
886
904
|
error: "Invalid skill name: potential path traversal detected"
|
|
887
905
|
};
|
|
888
906
|
async function writeSkillFiles(targetDir) {
|
|
889
|
-
await mkdir(targetDir, { recursive: true });
|
|
890
907
|
for (const [filePath, content] of skill.files) {
|
|
891
908
|
const fullPath = join(targetDir, filePath);
|
|
892
909
|
if (!isPathSafe(targetDir, fullPath)) continue;
|
|
@@ -897,6 +914,7 @@ async function installWellKnownSkillForAgent(skill, agentType, options = {}) {
|
|
|
897
914
|
}
|
|
898
915
|
try {
|
|
899
916
|
if (installMode === "copy") {
|
|
917
|
+
await cleanAndCreateDirectory(agentDir);
|
|
900
918
|
await writeSkillFiles(agentDir);
|
|
901
919
|
return {
|
|
902
920
|
success: true,
|
|
@@ -904,14 +922,10 @@ async function installWellKnownSkillForAgent(skill, agentType, options = {}) {
|
|
|
904
922
|
mode: "copy"
|
|
905
923
|
};
|
|
906
924
|
}
|
|
925
|
+
await cleanAndCreateDirectory(canonicalDir);
|
|
907
926
|
await writeSkillFiles(canonicalDir);
|
|
908
927
|
if (!await createSymlink(canonicalDir, agentDir)) {
|
|
909
|
-
|
|
910
|
-
await rm(agentDir, {
|
|
911
|
-
recursive: true,
|
|
912
|
-
force: true
|
|
913
|
-
});
|
|
914
|
-
} catch {}
|
|
928
|
+
await cleanAndCreateDirectory(agentDir);
|
|
915
929
|
await writeSkillFiles(agentDir);
|
|
916
930
|
return {
|
|
917
931
|
success: true,
|
|
@@ -1073,10 +1087,7 @@ var MintlifyProvider = class {
|
|
|
1073
1087
|
}
|
|
1074
1088
|
async fetchSkill(url) {
|
|
1075
1089
|
try {
|
|
1076
|
-
const
|
|
1077
|
-
const timeout = setTimeout(() => controller.abort(), 3e4);
|
|
1078
|
-
const response = await fetch(url, { signal: controller.signal });
|
|
1079
|
-
clearTimeout(timeout);
|
|
1090
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(3e4) });
|
|
1080
1091
|
if (!response.ok) return null;
|
|
1081
1092
|
const content = await response.text();
|
|
1082
1093
|
const { data } = (0, import_gray_matter.default)(content);
|
|
@@ -1121,10 +1132,7 @@ var HuggingFaceProvider = class {
|
|
|
1121
1132
|
async fetchSkill(url) {
|
|
1122
1133
|
try {
|
|
1123
1134
|
const rawUrl = this.toRawUrl(url);
|
|
1124
|
-
const
|
|
1125
|
-
const timeout = setTimeout(() => controller.abort(), 3e4);
|
|
1126
|
-
const response = await fetch(rawUrl, { signal: controller.signal });
|
|
1127
|
-
clearTimeout(timeout);
|
|
1135
|
+
const response = await fetch(rawUrl, { signal: AbortSignal.timeout(3e4) });
|
|
1128
1136
|
if (!response.ok) return null;
|
|
1129
1137
|
const content = await response.text();
|
|
1130
1138
|
const { data } = (0, import_gray_matter.default)(content);
|
|
@@ -1229,7 +1237,7 @@ var WellKnownProvider = class {
|
|
|
1229
1237
|
}
|
|
1230
1238
|
for (const file of e.files) {
|
|
1231
1239
|
if (typeof file !== "string") return false;
|
|
1232
|
-
if (file.startsWith("/") || file.includes("..")) return false;
|
|
1240
|
+
if (file.startsWith("/") || file.startsWith("\\") || file.includes("..")) return false;
|
|
1233
1241
|
}
|
|
1234
1242
|
if (!e.files.some((f) => typeof f === "string" && f.toLowerCase() === "skill.md")) return false;
|
|
1235
1243
|
return true;
|
|
@@ -1338,10 +1346,7 @@ registerProvider(mintlifyProvider);
|
|
|
1338
1346
|
registerProvider(huggingFaceProvider);
|
|
1339
1347
|
async function fetchMintlifySkill(url) {
|
|
1340
1348
|
try {
|
|
1341
|
-
const
|
|
1342
|
-
const timeout = setTimeout(() => controller.abort(), 3e4);
|
|
1343
|
-
const response = await fetch(url, { signal: controller.signal });
|
|
1344
|
-
clearTimeout(timeout);
|
|
1349
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(3e4) });
|
|
1345
1350
|
if (!response.ok) return null;
|
|
1346
1351
|
const content = await response.text();
|
|
1347
1352
|
const { data } = (0, import_gray_matter.default)(content);
|
|
@@ -1383,7 +1388,7 @@ async function writeSkillLock$1(lock) {
|
|
|
1383
1388
|
await writeFile(lockPath, JSON.stringify(lock, null, 2), "utf-8");
|
|
1384
1389
|
}
|
|
1385
1390
|
async function fetchSkillFolderHash(ownerRepo, skillPath) {
|
|
1386
|
-
let folderPath = skillPath;
|
|
1391
|
+
let folderPath = skillPath.replace(/\\/g, "/");
|
|
1387
1392
|
if (folderPath.endsWith("/SKILL.md")) folderPath = folderPath.slice(0, -9);
|
|
1388
1393
|
else if (folderPath.endsWith("SKILL.md")) folderPath = folderPath.slice(0, -8);
|
|
1389
1394
|
if (folderPath.endsWith("/")) folderPath = folderPath.slice(0, -1);
|
|
@@ -1448,14 +1453,19 @@ async function saveSelectedAgents(agents) {
|
|
|
1448
1453
|
lock.lastSelectedAgents = agents;
|
|
1449
1454
|
await writeSkillLock$1(lock);
|
|
1450
1455
|
}
|
|
1451
|
-
var version$1 = "1.2.
|
|
1456
|
+
var version$1 = "1.2.2";
|
|
1457
|
+
async function isSourcePrivate(source) {
|
|
1458
|
+
const ownerRepo = parseOwnerRepo(source);
|
|
1459
|
+
if (!ownerRepo) return false;
|
|
1460
|
+
return isRepoPrivate(ownerRepo.owner, ownerRepo.repo);
|
|
1461
|
+
}
|
|
1452
1462
|
function initTelemetry(version) {
|
|
1453
1463
|
setVersion(version);
|
|
1454
1464
|
}
|
|
1455
1465
|
function shortenPath$1(fullPath, cwd) {
|
|
1456
1466
|
const home = homedir();
|
|
1457
|
-
if (fullPath.startsWith(home)) return fullPath.
|
|
1458
|
-
if (fullPath.startsWith(cwd)) return "." + fullPath.slice(cwd.length);
|
|
1467
|
+
if (fullPath === home || fullPath.startsWith(home + sep)) return "~" + fullPath.slice(home.length);
|
|
1468
|
+
if (fullPath === cwd || fullPath.startsWith(cwd + sep)) return "." + fullPath.slice(cwd.length);
|
|
1459
1469
|
return fullPath;
|
|
1460
1470
|
}
|
|
1461
1471
|
function formatList$1(items, maxShow = 5) {
|
|
@@ -1699,7 +1709,7 @@ async function handleRemoteSkill(source, url, options, spinner) {
|
|
|
1699
1709
|
console.log();
|
|
1700
1710
|
const successful = results.filter((r) => r.success);
|
|
1701
1711
|
const failed = results.filter((r) => !r.success);
|
|
1702
|
-
track({
|
|
1712
|
+
if (await isSourcePrivate(remoteSkill.sourceIdentifier) !== true) track({
|
|
1703
1713
|
event: "install",
|
|
1704
1714
|
source: remoteSkill.sourceIdentifier,
|
|
1705
1715
|
skills: remoteSkill.installName,
|
|
@@ -1756,7 +1766,7 @@ async function handleRemoteSkill(source, url, options, spinner) {
|
|
|
1756
1766
|
}
|
|
1757
1767
|
console.log();
|
|
1758
1768
|
Se(import_picocolors.default.green("Done!"));
|
|
1759
|
-
await promptForFindSkills();
|
|
1769
|
+
await promptForFindSkills(options);
|
|
1760
1770
|
}
|
|
1761
1771
|
async function handleWellKnownSkills(source, url, options, spinner) {
|
|
1762
1772
|
spinner.start("Discovering skills from well-known endpoint...");
|
|
@@ -1961,7 +1971,7 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
1961
1971
|
const sourceIdentifier = wellKnownProvider.getSourceIdentifier(url);
|
|
1962
1972
|
const skillFiles = {};
|
|
1963
1973
|
for (const skill of selectedSkills) skillFiles[skill.installName] = skill.sourceUrl;
|
|
1964
|
-
track({
|
|
1974
|
+
if (await isSourcePrivate(sourceIdentifier) !== true) track({
|
|
1965
1975
|
event: "install",
|
|
1966
1976
|
source: sourceIdentifier,
|
|
1967
1977
|
skills: selectedSkills.map((s) => s.installName).join(","),
|
|
@@ -2026,7 +2036,7 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
2026
2036
|
}
|
|
2027
2037
|
console.log();
|
|
2028
2038
|
Se(import_picocolors.default.green("Done!"));
|
|
2029
|
-
await promptForFindSkills();
|
|
2039
|
+
await promptForFindSkills(options);
|
|
2030
2040
|
}
|
|
2031
2041
|
async function handleDirectUrlSkillLegacy(source, url, options, spinner) {
|
|
2032
2042
|
spinner.start("Fetching skill.md...");
|
|
@@ -2206,7 +2216,7 @@ async function handleDirectUrlSkillLegacy(source, url, options, spinner) {
|
|
|
2206
2216
|
}
|
|
2207
2217
|
console.log();
|
|
2208
2218
|
Se(import_picocolors.default.green("Done!"));
|
|
2209
|
-
await promptForFindSkills();
|
|
2219
|
+
await promptForFindSkills(options);
|
|
2210
2220
|
}
|
|
2211
2221
|
async function runAdd(args, options = {}) {
|
|
2212
2222
|
const source = args[0];
|
|
@@ -2474,19 +2484,31 @@ async function runAdd(args, options = {}) {
|
|
|
2474
2484
|
for (const skill of selectedSkills) {
|
|
2475
2485
|
let relativePath;
|
|
2476
2486
|
if (tempDir && skill.path === tempDir) relativePath = "SKILL.md";
|
|
2477
|
-
else if (tempDir && skill.path.startsWith(tempDir +
|
|
2487
|
+
else if (tempDir && skill.path.startsWith(tempDir + sep)) relativePath = skill.path.slice(tempDir.length + 1).split(sep).join("/") + "/SKILL.md";
|
|
2478
2488
|
else continue;
|
|
2479
2489
|
skillFiles[skill.name] = relativePath;
|
|
2480
2490
|
}
|
|
2481
2491
|
const normalizedSource = getOwnerRepo(parsed);
|
|
2482
|
-
if (normalizedSource)
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2492
|
+
if (normalizedSource) {
|
|
2493
|
+
const ownerRepo = parseOwnerRepo(normalizedSource);
|
|
2494
|
+
if (ownerRepo) {
|
|
2495
|
+
if (await isRepoPrivate(ownerRepo.owner, ownerRepo.repo) === false) track({
|
|
2496
|
+
event: "install",
|
|
2497
|
+
source: normalizedSource,
|
|
2498
|
+
skills: selectedSkills.map((s) => s.name).join(","),
|
|
2499
|
+
agents: targetAgents.join(","),
|
|
2500
|
+
...installGlobally && { global: "1" },
|
|
2501
|
+
skillFiles: JSON.stringify(skillFiles)
|
|
2502
|
+
});
|
|
2503
|
+
} else track({
|
|
2504
|
+
event: "install",
|
|
2505
|
+
source: normalizedSource,
|
|
2506
|
+
skills: selectedSkills.map((s) => s.name).join(","),
|
|
2507
|
+
agents: targetAgents.join(","),
|
|
2508
|
+
...installGlobally && { global: "1" },
|
|
2509
|
+
skillFiles: JSON.stringify(skillFiles)
|
|
2510
|
+
});
|
|
2511
|
+
}
|
|
2490
2512
|
if (successful.length > 0 && installGlobally && normalizedSource) {
|
|
2491
2513
|
const successfulSkillNames = new Set(successful.map((r) => r.skill));
|
|
2492
2514
|
for (const skill of selectedSkills) {
|
|
@@ -2553,7 +2575,7 @@ async function runAdd(args, options = {}) {
|
|
|
2553
2575
|
}
|
|
2554
2576
|
console.log();
|
|
2555
2577
|
Se(import_picocolors.default.green("Done!"));
|
|
2556
|
-
await promptForFindSkills();
|
|
2578
|
+
await promptForFindSkills(options);
|
|
2557
2579
|
} catch (error) {
|
|
2558
2580
|
if (error instanceof GitCloneError) {
|
|
2559
2581
|
M.error(import_picocolors.default.red("Failed to clone repository"));
|
|
@@ -2571,8 +2593,9 @@ async function cleanup(tempDir) {
|
|
|
2571
2593
|
await cleanupTempDir(tempDir);
|
|
2572
2594
|
} catch {}
|
|
2573
2595
|
}
|
|
2574
|
-
async function promptForFindSkills() {
|
|
2596
|
+
async function promptForFindSkills(options) {
|
|
2575
2597
|
if (!process.stdin.isTTY) return;
|
|
2598
|
+
if (options?.yes) return;
|
|
2576
2599
|
try {
|
|
2577
2600
|
if (await isPromptDismissed("findSkillsPrompt")) return;
|
|
2578
2601
|
if (await isSkillInstalled("find-skills", "claude-code", { global: true })) {
|
|
@@ -2793,11 +2816,7 @@ function getOwnerRepoFromString(pkg) {
|
|
|
2793
2816
|
return null;
|
|
2794
2817
|
}
|
|
2795
2818
|
async function isRepoPublic(owner, repo) {
|
|
2796
|
-
|
|
2797
|
-
return (await fetch(`https://api.github.com/repos/${owner}/${repo}`)).ok;
|
|
2798
|
-
} catch {
|
|
2799
|
-
return false;
|
|
2800
|
-
}
|
|
2819
|
+
return await isRepoPrivate(owner, repo) === false;
|
|
2801
2820
|
}
|
|
2802
2821
|
async function runFind(args) {
|
|
2803
2822
|
const query = args.join(" ");
|
|
@@ -3154,7 +3173,7 @@ function showBanner() {
|
|
|
3154
3173
|
console.log(` ${DIM}$${RESET} ${TEXT}npx skills find ${DIM}[query]${RESET} ${DIM}Search for skills${RESET}`);
|
|
3155
3174
|
console.log(` ${DIM}$${RESET} ${TEXT}npx skills check${RESET} ${DIM}Check for updates${RESET}`);
|
|
3156
3175
|
console.log(` ${DIM}$${RESET} ${TEXT}npx skills update${RESET} ${DIM}Update all skills${RESET}`);
|
|
3157
|
-
console.log(` ${DIM}$${RESET} ${TEXT}npx skills remove${RESET}
|
|
3176
|
+
console.log(` ${DIM}$${RESET} ${TEXT}npx skills remove${RESET} ${DIM}Remove installed skills${RESET}`);
|
|
3158
3177
|
console.log(` ${DIM}$${RESET} ${TEXT}npx skills init ${DIM}[name]${RESET} ${DIM}Create a new skill${RESET}`);
|
|
3159
3178
|
console.log();
|
|
3160
3179
|
console.log(`${DIM}try:${RESET} npx skills add vercel-labs/agent-skills`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skills",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"description": "The open agent skills ecosystem",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -57,12 +57,14 @@
|
|
|
57
57
|
"mcpjam",
|
|
58
58
|
"mux",
|
|
59
59
|
"opencode",
|
|
60
|
+
"openclaude",
|
|
60
61
|
"openhands",
|
|
61
62
|
"pi",
|
|
62
63
|
"qoder",
|
|
63
64
|
"qwen-code",
|
|
64
65
|
"roo",
|
|
65
66
|
"trae",
|
|
67
|
+
"trae-cn",
|
|
66
68
|
"windsurf",
|
|
67
69
|
"zencoder",
|
|
68
70
|
"neovate",
|
|
@@ -78,6 +80,9 @@
|
|
|
78
80
|
},
|
|
79
81
|
"author": "",
|
|
80
82
|
"license": "MIT",
|
|
83
|
+
"dependencies": {
|
|
84
|
+
"xdg-basedir": "^5.1.0"
|
|
85
|
+
},
|
|
81
86
|
"devDependencies": {
|
|
82
87
|
"@clack/prompts": "^0.11.0",
|
|
83
88
|
"@types/bun": "latest",
|