add-skill 1.0.20 → 1.0.22
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 +44 -28
- package/dist/index.js +221 -108
- package/package.json +11 -9
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Install agent skills onto your coding agents from any git repository.
|
|
4
4
|
|
|
5
5
|
<!-- agent-list:start -->
|
|
6
|
-
Supports **Opencode**, **Claude Code**, **Codex**, **Cursor**, and [
|
|
6
|
+
Supports **Opencode**, **Claude Code**, **Codex**, **Cursor**, and [12 more](#available-agents).
|
|
7
7
|
<!-- agent-list:end -->
|
|
8
8
|
|
|
9
9
|
## Quick Start
|
|
@@ -82,22 +82,34 @@ Skills can be installed to any of these supported agents. Use `-g, --global` to
|
|
|
82
82
|
<!-- available-agents:start -->
|
|
83
83
|
| Agent | `--agent` | Project Path | Global Path |
|
|
84
84
|
|-------|-----------|--------------|-------------|
|
|
85
|
-
|
|
|
85
|
+
| Amp | `amp` | `.agents/skills/` | `~/.config/agents/skills/` |
|
|
86
|
+
| Antigravity | `antigravity` | `.agent/skills/` | `~/.gemini/antigravity/skills/` |
|
|
86
87
|
| Claude Code | `claude-code` | `.claude/skills/` | `~/.claude/skills/` |
|
|
88
|
+
| Clawdbot | `clawdbot` | `skills/` | `~/.clawdbot/skills/` |
|
|
87
89
|
| Codex | `codex` | `.codex/skills/` | `~/.codex/skills/` |
|
|
88
90
|
| Cursor | `cursor` | `.cursor/skills/` | `~/.cursor/skills/` |
|
|
89
|
-
|
|
|
90
|
-
| Kilo Code | `kilo` | `.kilocode/skills/` | `~/.kilocode/skills/` |
|
|
91
|
-
| Roo Code | `roo` | `.roo/skills/` | `~/.roo/skills/` |
|
|
92
|
-
| Goose | `goose` | `.goose/skills/` | `~/.config/goose/skills/` |
|
|
91
|
+
| Droid | `droid` | `.factory/skills/` | `~/.factory/skills/` |
|
|
93
92
|
| Gemini CLI | `gemini-cli` | `.gemini/skills/` | `~/.gemini/skills/` |
|
|
94
|
-
| Antigravity | `antigravity` | `.agent/skills/` | `~/.gemini/antigravity/skills/` |
|
|
95
93
|
| GitHub Copilot | `github-copilot` | `.github/skills/` | `~/.copilot/skills/` |
|
|
96
|
-
|
|
|
97
|
-
|
|
|
94
|
+
| Goose | `goose` | `.goose/skills/` | `~/.config/goose/skills/` |
|
|
95
|
+
| Kilo Code | `kilo` | `.kilocode/skills/` | `~/.kilocode/skills/` |
|
|
96
|
+
| Kiro CLI | `kiro-cli` | `.kiro/skills/` | `~/.kiro/skills/` |
|
|
97
|
+
| OpenCode | `opencode` | `.opencode/skills/` | `~/.config/opencode/skills/` |
|
|
98
|
+
| Roo Code | `roo` | `.roo/skills/` | `~/.roo/skills/` |
|
|
99
|
+
| Trae | `trae` | `.trae/skills/` | `~/.trae/skills/` |
|
|
98
100
|
| Windsurf | `windsurf` | `.windsurf/skills/` | `~/.codeium/windsurf/skills/` |
|
|
99
101
|
<!-- available-agents:end -->
|
|
100
102
|
|
|
103
|
+
> [!NOTE]
|
|
104
|
+
> **Kiro CLI users:** After installing skills, you need to manually add them to your custom agent's `resources` in `.kiro/agents/<agent>.json`:
|
|
105
|
+
> ```json
|
|
106
|
+
> {
|
|
107
|
+
> "resources": [
|
|
108
|
+
> "skill://.kiro/skills/**/SKILL.md"
|
|
109
|
+
> ]
|
|
110
|
+
> }
|
|
111
|
+
> ```
|
|
112
|
+
|
|
101
113
|
## Agent Detection
|
|
102
114
|
|
|
103
115
|
The CLI automatically detects which coding agents you have installed by checking for their configuration directories. If none are detected, you'll be prompted to select which agents to install to.
|
|
@@ -141,19 +153,21 @@ The CLI searches for skills in these locations within a repository:
|
|
|
141
153
|
- `skills/.curated/`
|
|
142
154
|
- `skills/.experimental/`
|
|
143
155
|
- `skills/.system/`
|
|
144
|
-
- `.
|
|
156
|
+
- `.agents/skills/`
|
|
157
|
+
- `.agent/skills/`
|
|
145
158
|
- `.claude/skills/`
|
|
159
|
+
- `./skills/`
|
|
146
160
|
- `.codex/skills/`
|
|
147
161
|
- `.cursor/skills/`
|
|
148
|
-
- `.
|
|
149
|
-
- `.kilocode/skills/`
|
|
150
|
-
- `.roo/skills/`
|
|
151
|
-
- `.goose/skills/`
|
|
162
|
+
- `.factory/skills/`
|
|
152
163
|
- `.gemini/skills/`
|
|
153
|
-
- `.agent/skills/`
|
|
154
164
|
- `.github/skills/`
|
|
155
|
-
-
|
|
156
|
-
- `.
|
|
165
|
+
- `.goose/skills/`
|
|
166
|
+
- `.kilocode/skills/`
|
|
167
|
+
- `.kiro/skills/`
|
|
168
|
+
- `.opencode/skills/`
|
|
169
|
+
- `.roo/skills/`
|
|
170
|
+
- `.trae/skills/`
|
|
157
171
|
- `.windsurf/skills/`
|
|
158
172
|
<!-- skill-discovery:end -->
|
|
159
173
|
|
|
@@ -163,12 +177,12 @@ If no skills are found in standard locations, a recursive search is performed.
|
|
|
163
177
|
|
|
164
178
|
Skills are generally compatible across agents since they follow a shared [Agent Skills specification](https://agentskills.io). However, some features may be agent-specific:
|
|
165
179
|
|
|
166
|
-
| Feature | OpenCode | Claude Code | Codex | Cursor | Antigravity | Roo Code | Github Copilot | Amp | Clawdbot |
|
|
167
|
-
|
|
168
|
-
| Basic skills | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
|
|
169
|
-
| `allowed-tools` | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
|
|
170
|
-
| `context: fork` | No | Yes | No | No | No | No | No | No | No |
|
|
171
|
-
| Hooks | No | Yes | No | No | No | No | No | No | No |
|
|
180
|
+
| Feature | OpenCode | Claude Code | Codex | Kiro CLI | Cursor | Antigravity | Roo Code | Github Copilot | Amp | Clawdbot |
|
|
181
|
+
|---------|----------|-------------|-------|----------|--------|-------------|----------|----------------|-----|----------|
|
|
182
|
+
| Basic skills | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
|
|
183
|
+
| `allowed-tools` | Yes | Yes | Yes | No | Yes | Yes | Yes | Yes | Yes | Yes |
|
|
184
|
+
| `context: fork` | No | Yes | No | No | No | No | No | No | No | No |
|
|
185
|
+
| Hooks | No | Yes | No | No | No | No | No | No | No | No |
|
|
172
186
|
|
|
173
187
|
## Troubleshooting
|
|
174
188
|
|
|
@@ -202,18 +216,20 @@ Telemetry is also automatically disabled in CI environments.
|
|
|
202
216
|
|
|
203
217
|
## Related Links
|
|
204
218
|
|
|
205
|
-
- [Vercel Agent Skills Repository](https://github.com/vercel-labs/agent-skills)
|
|
206
219
|
- [Agent Skills Specification](https://agentskills.io)
|
|
207
|
-
- [
|
|
220
|
+
- [Amp Skills Documentation](https://ampcode.com/manual#agent-skills)
|
|
221
|
+
- [Antigravity Skills Documentation](https://antigravity.google/docs/skills)
|
|
208
222
|
- [Claude Code Skills Documentation](https://code.claude.com/docs/en/skills)
|
|
223
|
+
- [Clawdbot Skills Documentation](https://docs.clawd.bot/tools/skills)
|
|
209
224
|
- [Codex Skills Documentation](https://developers.openai.com/codex/skills)
|
|
210
225
|
- [Cursor Skills Documentation](https://cursor.com/docs/context/skills)
|
|
211
226
|
- [Gemini CLI Skills Documentation](https://geminicli.com/docs/cli/skills/)
|
|
212
|
-
- [Amp Skills Documentation](https://ampcode.com/manual#agent-skills)
|
|
213
|
-
- [Antigravity Skills Documentation](https://antigravity.google/docs/skills)
|
|
214
227
|
- [GitHub Copilot Agent Skills](https://docs.github.com/en/copilot/concepts/agents/about-agent-skills)
|
|
228
|
+
- [Kiro CLI Skills Documentation](https://kiro.dev/docs/cli/custom-agents/configuration-reference/#skill-resources)
|
|
229
|
+
- [OpenCode Skills Documentation](https://opencode.ai/docs/skills)
|
|
215
230
|
- [Roo Code Skills Documentation](https://docs.roocode.com/features/skills)
|
|
216
|
-
- [
|
|
231
|
+
- [Trae Skills Documentation](https://docs.trae.ai/ide/skills)
|
|
232
|
+
- [Vercel Agent Skills Repository](https://github.com/vercel-labs/agent-skills)
|
|
217
233
|
|
|
218
234
|
## License
|
|
219
235
|
|
package/dist/index.js
CHANGED
|
@@ -197,16 +197,18 @@ async function discoverSkills(basePath, subpath) {
|
|
|
197
197
|
join2(searchPath, "skills/.curated"),
|
|
198
198
|
join2(searchPath, "skills/.experimental"),
|
|
199
199
|
join2(searchPath, "skills/.system"),
|
|
200
|
-
join2(searchPath, ".
|
|
200
|
+
join2(searchPath, ".agent/skills"),
|
|
201
|
+
join2(searchPath, ".agents/skills"),
|
|
201
202
|
join2(searchPath, ".claude/skills"),
|
|
202
|
-
join2(searchPath, ".
|
|
203
|
+
join2(searchPath, ".codex/skills"),
|
|
203
204
|
join2(searchPath, ".cursor/skills"),
|
|
204
|
-
join2(searchPath, ".
|
|
205
|
+
join2(searchPath, ".github/skills"),
|
|
206
|
+
join2(searchPath, ".goose/skills"),
|
|
205
207
|
join2(searchPath, ".kilocode/skills"),
|
|
208
|
+
join2(searchPath, ".kiro/skills"),
|
|
209
|
+
join2(searchPath, ".opencode/skills"),
|
|
206
210
|
join2(searchPath, ".roo/skills"),
|
|
207
|
-
join2(searchPath, ".
|
|
208
|
-
join2(searchPath, ".agent/skills"),
|
|
209
|
-
join2(searchPath, ".github/skills")
|
|
211
|
+
join2(searchPath, ".trae/skills")
|
|
210
212
|
];
|
|
211
213
|
for (const dir of prioritySearchDirs) {
|
|
212
214
|
try {
|
|
@@ -243,8 +245,9 @@ function getSkillDisplayName(skill) {
|
|
|
243
245
|
}
|
|
244
246
|
|
|
245
247
|
// src/installer.ts
|
|
246
|
-
import { mkdir, cp, access, readdir as readdir2 } from "fs/promises";
|
|
247
|
-
import { join as join4, basename as basename2, normalize as normalize2, resolve as resolve3, sep as sep2 } from "path";
|
|
248
|
+
import { mkdir, cp, access, readdir as readdir2, symlink, lstat, rm as rm2, readlink } from "fs/promises";
|
|
249
|
+
import { join as join4, basename as basename2, normalize as normalize2, resolve as resolve3, sep as sep2, relative } from "path";
|
|
250
|
+
import { homedir as homedir2, platform } from "os";
|
|
248
251
|
|
|
249
252
|
// src/agents.ts
|
|
250
253
|
import { homedir } from "os";
|
|
@@ -252,13 +255,22 @@ import { join as join3 } from "path";
|
|
|
252
255
|
import { existsSync } from "fs";
|
|
253
256
|
var home = homedir();
|
|
254
257
|
var agents = {
|
|
255
|
-
|
|
256
|
-
name: "
|
|
257
|
-
displayName: "
|
|
258
|
-
skillsDir: ".
|
|
259
|
-
globalSkillsDir: join3(home, ".config/
|
|
258
|
+
amp: {
|
|
259
|
+
name: "amp",
|
|
260
|
+
displayName: "Amp",
|
|
261
|
+
skillsDir: ".agents/skills",
|
|
262
|
+
globalSkillsDir: join3(home, ".config/agents/skills"),
|
|
260
263
|
detectInstalled: async () => {
|
|
261
|
-
return existsSync(join3(home, ".config/
|
|
264
|
+
return existsSync(join3(home, ".config/amp"));
|
|
265
|
+
}
|
|
266
|
+
},
|
|
267
|
+
antigravity: {
|
|
268
|
+
name: "antigravity",
|
|
269
|
+
displayName: "Antigravity",
|
|
270
|
+
skillsDir: ".agent/skills",
|
|
271
|
+
globalSkillsDir: join3(home, ".gemini/antigravity/skills"),
|
|
272
|
+
detectInstalled: async () => {
|
|
273
|
+
return existsSync(join3(process.cwd(), ".agent")) || existsSync(join3(home, ".gemini/antigravity"));
|
|
262
274
|
}
|
|
263
275
|
},
|
|
264
276
|
"claude-code": {
|
|
@@ -270,6 +282,15 @@ var agents = {
|
|
|
270
282
|
return existsSync(join3(home, ".claude"));
|
|
271
283
|
}
|
|
272
284
|
},
|
|
285
|
+
clawdbot: {
|
|
286
|
+
name: "clawdbot",
|
|
287
|
+
displayName: "Clawdbot",
|
|
288
|
+
skillsDir: "skills",
|
|
289
|
+
globalSkillsDir: join3(home, ".clawdbot/skills"),
|
|
290
|
+
detectInstalled: async () => {
|
|
291
|
+
return existsSync(join3(home, ".clawdbot"));
|
|
292
|
+
}
|
|
293
|
+
},
|
|
273
294
|
codex: {
|
|
274
295
|
name: "codex",
|
|
275
296
|
displayName: "Codex",
|
|
@@ -288,31 +309,31 @@ var agents = {
|
|
|
288
309
|
return existsSync(join3(home, ".cursor"));
|
|
289
310
|
}
|
|
290
311
|
},
|
|
291
|
-
|
|
292
|
-
name: "
|
|
293
|
-
displayName: "
|
|
294
|
-
skillsDir: ".
|
|
295
|
-
globalSkillsDir: join3(home, ".
|
|
312
|
+
droid: {
|
|
313
|
+
name: "droid",
|
|
314
|
+
displayName: "Droid",
|
|
315
|
+
skillsDir: ".factory/skills",
|
|
316
|
+
globalSkillsDir: join3(home, ".factory/skills"),
|
|
296
317
|
detectInstalled: async () => {
|
|
297
|
-
return existsSync(join3(home, ".
|
|
318
|
+
return existsSync(join3(home, ".factory/skills"));
|
|
298
319
|
}
|
|
299
320
|
},
|
|
300
|
-
|
|
301
|
-
name: "
|
|
302
|
-
displayName: "
|
|
303
|
-
skillsDir: ".
|
|
304
|
-
globalSkillsDir: join3(home, ".
|
|
321
|
+
"gemini-cli": {
|
|
322
|
+
name: "gemini-cli",
|
|
323
|
+
displayName: "Gemini CLI",
|
|
324
|
+
skillsDir: ".gemini/skills",
|
|
325
|
+
globalSkillsDir: join3(home, ".gemini/skills"),
|
|
305
326
|
detectInstalled: async () => {
|
|
306
|
-
return existsSync(join3(home, ".
|
|
327
|
+
return existsSync(join3(home, ".gemini"));
|
|
307
328
|
}
|
|
308
329
|
},
|
|
309
|
-
|
|
310
|
-
name: "
|
|
311
|
-
displayName: "
|
|
312
|
-
skillsDir: ".
|
|
313
|
-
globalSkillsDir: join3(home, ".
|
|
330
|
+
"github-copilot": {
|
|
331
|
+
name: "github-copilot",
|
|
332
|
+
displayName: "GitHub Copilot",
|
|
333
|
+
skillsDir: ".github/skills",
|
|
334
|
+
globalSkillsDir: join3(home, ".copilot/skills"),
|
|
314
335
|
detectInstalled: async () => {
|
|
315
|
-
return existsSync(join3(home, ".
|
|
336
|
+
return existsSync(join3(process.cwd(), ".github")) || existsSync(join3(home, ".copilot"));
|
|
316
337
|
}
|
|
317
338
|
},
|
|
318
339
|
goose: {
|
|
@@ -324,49 +345,49 @@ var agents = {
|
|
|
324
345
|
return existsSync(join3(home, ".config/goose"));
|
|
325
346
|
}
|
|
326
347
|
},
|
|
327
|
-
|
|
328
|
-
name: "
|
|
329
|
-
displayName: "
|
|
330
|
-
skillsDir: ".
|
|
331
|
-
globalSkillsDir: join3(home, ".
|
|
348
|
+
kilo: {
|
|
349
|
+
name: "kilo",
|
|
350
|
+
displayName: "Kilo Code",
|
|
351
|
+
skillsDir: ".kilocode/skills",
|
|
352
|
+
globalSkillsDir: join3(home, ".kilocode/skills"),
|
|
332
353
|
detectInstalled: async () => {
|
|
333
|
-
return existsSync(join3(home, ".
|
|
354
|
+
return existsSync(join3(home, ".kilocode"));
|
|
334
355
|
}
|
|
335
356
|
},
|
|
336
|
-
|
|
337
|
-
name: "
|
|
338
|
-
displayName: "
|
|
339
|
-
skillsDir: ".
|
|
340
|
-
globalSkillsDir: join3(home, ".
|
|
357
|
+
"kiro-cli": {
|
|
358
|
+
name: "kiro-cli",
|
|
359
|
+
displayName: "Kiro CLI",
|
|
360
|
+
skillsDir: ".kiro/skills",
|
|
361
|
+
globalSkillsDir: join3(home, ".kiro/skills"),
|
|
341
362
|
detectInstalled: async () => {
|
|
342
|
-
return existsSync(join3(
|
|
363
|
+
return existsSync(join3(home, ".kiro"));
|
|
343
364
|
}
|
|
344
365
|
},
|
|
345
|
-
|
|
346
|
-
name: "
|
|
347
|
-
displayName: "
|
|
348
|
-
skillsDir: ".
|
|
349
|
-
globalSkillsDir: join3(home, ".
|
|
366
|
+
opencode: {
|
|
367
|
+
name: "opencode",
|
|
368
|
+
displayName: "OpenCode",
|
|
369
|
+
skillsDir: ".opencode/skills",
|
|
370
|
+
globalSkillsDir: join3(home, ".config/opencode/skills"),
|
|
350
371
|
detectInstalled: async () => {
|
|
351
|
-
return existsSync(join3(
|
|
372
|
+
return existsSync(join3(home, ".config/opencode")) || existsSync(join3(home, ".claude/skills"));
|
|
352
373
|
}
|
|
353
374
|
},
|
|
354
|
-
|
|
355
|
-
name: "
|
|
356
|
-
displayName: "
|
|
357
|
-
skillsDir: "skills",
|
|
358
|
-
globalSkillsDir: join3(home, ".
|
|
375
|
+
roo: {
|
|
376
|
+
name: "roo",
|
|
377
|
+
displayName: "Roo Code",
|
|
378
|
+
skillsDir: ".roo/skills",
|
|
379
|
+
globalSkillsDir: join3(home, ".roo/skills"),
|
|
359
380
|
detectInstalled: async () => {
|
|
360
|
-
return existsSync(join3(home, ".
|
|
381
|
+
return existsSync(join3(home, ".roo"));
|
|
361
382
|
}
|
|
362
383
|
},
|
|
363
|
-
|
|
364
|
-
name: "
|
|
365
|
-
displayName: "
|
|
366
|
-
skillsDir: ".
|
|
367
|
-
globalSkillsDir: join3(home, ".
|
|
384
|
+
trae: {
|
|
385
|
+
name: "trae",
|
|
386
|
+
displayName: "Trae",
|
|
387
|
+
skillsDir: ".trae/skills",
|
|
388
|
+
globalSkillsDir: join3(home, ".trae/skills"),
|
|
368
389
|
detectInstalled: async () => {
|
|
369
|
-
return existsSync(join3(home, ".
|
|
390
|
+
return existsSync(join3(home, ".trae"));
|
|
370
391
|
}
|
|
371
392
|
},
|
|
372
393
|
windsurf: {
|
|
@@ -390,6 +411,8 @@ async function detectInstalledAgents() {
|
|
|
390
411
|
}
|
|
391
412
|
|
|
392
413
|
// src/installer.ts
|
|
414
|
+
var AGENTS_DIR = ".agents";
|
|
415
|
+
var SKILLS_SUBDIR = "skills";
|
|
393
416
|
function sanitizeName(name) {
|
|
394
417
|
let sanitized = name.replace(/[\/\\:\0]/g, "");
|
|
395
418
|
sanitized = sanitized.replace(/^[.\s]+|[.\s]+$/g, "");
|
|
@@ -407,27 +430,82 @@ function isPathSafe(basePath, targetPath) {
|
|
|
407
430
|
const normalizedTarget = normalize2(resolve3(targetPath));
|
|
408
431
|
return normalizedTarget.startsWith(normalizedBase + sep2) || normalizedTarget === normalizedBase;
|
|
409
432
|
}
|
|
433
|
+
function getCanonicalSkillsDir(global, cwd) {
|
|
434
|
+
const baseDir = global ? homedir2() : cwd || process.cwd();
|
|
435
|
+
return join4(baseDir, AGENTS_DIR, SKILLS_SUBDIR);
|
|
436
|
+
}
|
|
437
|
+
async function createSymlink(target, linkPath) {
|
|
438
|
+
try {
|
|
439
|
+
try {
|
|
440
|
+
const stats = await lstat(linkPath);
|
|
441
|
+
if (stats.isSymbolicLink()) {
|
|
442
|
+
const existingTarget = await readlink(linkPath);
|
|
443
|
+
if (resolve3(existingTarget) === resolve3(target)) {
|
|
444
|
+
return true;
|
|
445
|
+
}
|
|
446
|
+
await rm2(linkPath);
|
|
447
|
+
} else {
|
|
448
|
+
await rm2(linkPath, { recursive: true });
|
|
449
|
+
}
|
|
450
|
+
} catch {
|
|
451
|
+
}
|
|
452
|
+
const linkDir = join4(linkPath, "..");
|
|
453
|
+
await mkdir(linkDir, { recursive: true });
|
|
454
|
+
const relativePath = relative(linkDir, target);
|
|
455
|
+
const symlinkType = platform() === "win32" ? "junction" : void 0;
|
|
456
|
+
await symlink(relativePath, linkPath, symlinkType);
|
|
457
|
+
return true;
|
|
458
|
+
} catch {
|
|
459
|
+
return false;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
410
462
|
async function installSkillForAgent(skill, agentType, options = {}) {
|
|
411
463
|
const agent = agents[agentType];
|
|
464
|
+
const isGlobal = options.global ?? false;
|
|
465
|
+
const cwd = options.cwd || process.cwd();
|
|
412
466
|
const rawSkillName = skill.name || basename2(skill.path);
|
|
413
467
|
const skillName = sanitizeName(rawSkillName);
|
|
414
|
-
const
|
|
415
|
-
const
|
|
416
|
-
|
|
468
|
+
const canonicalBase = getCanonicalSkillsDir(isGlobal, cwd);
|
|
469
|
+
const canonicalDir = join4(canonicalBase, skillName);
|
|
470
|
+
const agentBase = isGlobal ? agent.globalSkillsDir : join4(cwd, agent.skillsDir);
|
|
471
|
+
const agentDir = join4(agentBase, skillName);
|
|
472
|
+
if (!isPathSafe(canonicalBase, canonicalDir)) {
|
|
417
473
|
return {
|
|
418
474
|
success: false,
|
|
419
|
-
path:
|
|
475
|
+
path: agentDir,
|
|
476
|
+
error: "Invalid skill name: potential path traversal detected"
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
if (!isPathSafe(agentBase, agentDir)) {
|
|
480
|
+
return {
|
|
481
|
+
success: false,
|
|
482
|
+
path: agentDir,
|
|
420
483
|
error: "Invalid skill name: potential path traversal detected"
|
|
421
484
|
};
|
|
422
485
|
}
|
|
423
486
|
try {
|
|
424
|
-
await mkdir(
|
|
425
|
-
await copyDirectory(skill.path,
|
|
426
|
-
|
|
487
|
+
await mkdir(canonicalDir, { recursive: true });
|
|
488
|
+
await copyDirectory(skill.path, canonicalDir);
|
|
489
|
+
const symlinkCreated = await createSymlink(canonicalDir, agentDir);
|
|
490
|
+
if (!symlinkCreated) {
|
|
491
|
+
await mkdir(agentDir, { recursive: true });
|
|
492
|
+
await copyDirectory(skill.path, agentDir);
|
|
493
|
+
return {
|
|
494
|
+
success: true,
|
|
495
|
+
path: agentDir,
|
|
496
|
+
canonicalPath: canonicalDir,
|
|
497
|
+
symlinkFailed: true
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
return {
|
|
501
|
+
success: true,
|
|
502
|
+
path: agentDir,
|
|
503
|
+
canonicalPath: canonicalDir
|
|
504
|
+
};
|
|
427
505
|
} catch (error) {
|
|
428
506
|
return {
|
|
429
507
|
success: false,
|
|
430
|
-
path:
|
|
508
|
+
path: agentDir,
|
|
431
509
|
error: error instanceof Error ? error.message : "Unknown error"
|
|
432
510
|
};
|
|
433
511
|
}
|
|
@@ -472,17 +550,19 @@ async function isSkillInstalled(skillName, agentType, options = {}) {
|
|
|
472
550
|
return false;
|
|
473
551
|
}
|
|
474
552
|
}
|
|
475
|
-
function
|
|
476
|
-
const agent = agents[agentType];
|
|
553
|
+
function getCanonicalPath(skillName, options = {}) {
|
|
477
554
|
const sanitized = sanitizeName(skillName);
|
|
478
|
-
const
|
|
479
|
-
const
|
|
480
|
-
if (!isPathSafe(
|
|
555
|
+
const canonicalBase = getCanonicalSkillsDir(options.global ?? false, options.cwd);
|
|
556
|
+
const canonicalPath = join4(canonicalBase, sanitized);
|
|
557
|
+
if (!isPathSafe(canonicalBase, canonicalPath)) {
|
|
481
558
|
throw new Error("Invalid skill name: potential path traversal detected");
|
|
482
559
|
}
|
|
483
|
-
return
|
|
560
|
+
return canonicalPath;
|
|
484
561
|
}
|
|
485
562
|
|
|
563
|
+
// src/index.ts
|
|
564
|
+
import { homedir as homedir3 } from "os";
|
|
565
|
+
|
|
486
566
|
// src/telemetry.ts
|
|
487
567
|
var TELEMETRY_URL = "https://add-skill.vercel.sh/t";
|
|
488
568
|
var cliVersion = null;
|
|
@@ -519,7 +599,7 @@ function track(data) {
|
|
|
519
599
|
// package.json
|
|
520
600
|
var package_default = {
|
|
521
601
|
name: "add-skill",
|
|
522
|
-
version: "1.0.
|
|
602
|
+
version: "1.0.22",
|
|
523
603
|
description: "Install agent skills onto coding agents (OpenCode, Claude Code, Codex, Cursor)",
|
|
524
604
|
type: "module",
|
|
525
605
|
bin: {
|
|
@@ -539,19 +619,21 @@ var package_default = {
|
|
|
539
619
|
"agent-skills",
|
|
540
620
|
"skills",
|
|
541
621
|
"ai-agents",
|
|
542
|
-
"
|
|
622
|
+
"amp",
|
|
623
|
+
"antigravity",
|
|
543
624
|
"claude-code",
|
|
625
|
+
"clawdbot",
|
|
544
626
|
"codex",
|
|
545
627
|
"cursor",
|
|
546
|
-
"
|
|
547
|
-
"kilo",
|
|
548
|
-
"roo",
|
|
549
|
-
"goose",
|
|
628
|
+
"droid",
|
|
550
629
|
"gemini-cli",
|
|
551
|
-
"antigravity",
|
|
552
630
|
"github-copilot",
|
|
553
|
-
"
|
|
554
|
-
"
|
|
631
|
+
"goose",
|
|
632
|
+
"kilo",
|
|
633
|
+
"kiro-cli",
|
|
634
|
+
"opencode",
|
|
635
|
+
"roo",
|
|
636
|
+
"trae",
|
|
555
637
|
"windsurf"
|
|
556
638
|
],
|
|
557
639
|
repository: {
|
|
@@ -583,9 +665,27 @@ var package_default = {
|
|
|
583
665
|
};
|
|
584
666
|
|
|
585
667
|
// src/index.ts
|
|
668
|
+
function shortenPath(fullPath, cwd) {
|
|
669
|
+
const home2 = homedir3();
|
|
670
|
+
if (fullPath.startsWith(home2)) {
|
|
671
|
+
return fullPath.replace(home2, "~");
|
|
672
|
+
}
|
|
673
|
+
if (fullPath.startsWith(cwd)) {
|
|
674
|
+
return "." + fullPath.slice(cwd.length);
|
|
675
|
+
}
|
|
676
|
+
return fullPath;
|
|
677
|
+
}
|
|
678
|
+
function formatList(items, maxShow = 5) {
|
|
679
|
+
if (items.length <= maxShow) {
|
|
680
|
+
return items.join(", ");
|
|
681
|
+
}
|
|
682
|
+
const shown = items.slice(0, maxShow);
|
|
683
|
+
const remaining = items.length - maxShow;
|
|
684
|
+
return `${shown.join(", ")} +${remaining} more`;
|
|
685
|
+
}
|
|
586
686
|
var version = package_default.version;
|
|
587
687
|
setVersion(version);
|
|
588
|
-
program.name("add-skill").description("Install skills onto coding agents (OpenCode, Claude Code, Codex, Cursor, Antigravity, Github Copilot, Roo Code)").version(version).argument("<source>", "Git repo URL, GitHub shorthand (owner/repo), local path (./path), or direct path to skill").option("-g, --global", "Install skill globally (user-level) instead of project-level").option("-a, --agent <agents...>", "Specify agents to install to (opencode, claude-code, codex, cursor, antigravity,
|
|
688
|
+
program.name("add-skill").description("Install skills onto coding agents (OpenCode, Claude Code, Codex, Kiro CLI, Cursor, Antigravity, Github Copilot, Roo Code)").version(version).argument("<source>", "Git repo URL, GitHub shorthand (owner/repo), local path (./path), or direct path to skill").option("-g, --global", "Install skill globally (user-level) instead of project-level").option("-a, --agent <agents...>", "Specify agents to install to (opencode, claude-code, codex, kiro-cli, cursor, antigravity, github-copilot, roo)").option("-s, --skill <skills...>", "Specify skill names to install (skip selection prompt)").option("-l, --list", "List available skills in the repository without installing").option("-y, --yes", "Skip confirmation prompts").option("--all", "Install all skills to all agents without any prompts (implies -y -g)").configureOutput({
|
|
589
689
|
outputError: (str, write) => {
|
|
590
690
|
if (str.includes("missing required argument")) {
|
|
591
691
|
console.log();
|
|
@@ -786,8 +886,8 @@ async function main(source, options) {
|
|
|
786
886
|
}
|
|
787
887
|
installGlobally = scope;
|
|
788
888
|
}
|
|
889
|
+
const cwd = process.cwd();
|
|
789
890
|
const summaryLines = [];
|
|
790
|
-
const maxAgentLen = Math.max(...targetAgents.map((a) => agents[a].displayName.length));
|
|
791
891
|
const overwriteStatus = /* @__PURE__ */ new Map();
|
|
792
892
|
for (const skill of selectedSkills) {
|
|
793
893
|
const agentStatus = /* @__PURE__ */ new Map();
|
|
@@ -796,18 +896,20 @@ async function main(source, options) {
|
|
|
796
896
|
}
|
|
797
897
|
overwriteStatus.set(skill.name, agentStatus);
|
|
798
898
|
}
|
|
899
|
+
const agentNames = targetAgents.map((a) => agents[a].displayName);
|
|
900
|
+
const hasOverwrites = Array.from(overwriteStatus.values()).some(
|
|
901
|
+
(agentMap) => Array.from(agentMap.values()).some((v) => v)
|
|
902
|
+
);
|
|
799
903
|
for (const skill of selectedSkills) {
|
|
800
904
|
if (summaryLines.length > 0) summaryLines.push("");
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
summaryLines.push(
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
const agentName = agents[agent].displayName.padEnd(maxAgentLen + 2);
|
|
810
|
-
summaryLines.push(` ${agentName}${chalk.dim(basePath)}${status}`);
|
|
905
|
+
const canonicalPath = getCanonicalPath(skill.name, { global: installGlobally });
|
|
906
|
+
const shortCanonical = shortenPath(canonicalPath, cwd);
|
|
907
|
+
summaryLines.push(`${chalk.cyan(shortCanonical)}`);
|
|
908
|
+
const skillOverwrites = overwriteStatus.get(skill.name);
|
|
909
|
+
const overwriteAgents = targetAgents.filter((a) => skillOverwrites?.get(a)).map((a) => agents[a].displayName);
|
|
910
|
+
summaryLines.push(` ${chalk.dim("\u2192")} ${formatList(agentNames)}`);
|
|
911
|
+
if (overwriteAgents.length > 0) {
|
|
912
|
+
summaryLines.push(` ${chalk.yellow("overwrites:")} ${formatList(overwriteAgents)}`);
|
|
811
913
|
}
|
|
812
914
|
}
|
|
813
915
|
console.log();
|
|
@@ -862,25 +964,36 @@ async function main(source, options) {
|
|
|
862
964
|
if (successful.length > 0) {
|
|
863
965
|
const bySkill = /* @__PURE__ */ new Map();
|
|
864
966
|
for (const r of successful) {
|
|
865
|
-
const
|
|
866
|
-
|
|
867
|
-
bySkill.set(r.skill,
|
|
967
|
+
const skillResults = bySkill.get(r.skill) || [];
|
|
968
|
+
skillResults.push(r);
|
|
969
|
+
bySkill.set(r.skill, skillResults);
|
|
868
970
|
}
|
|
869
971
|
const skillCount = bySkill.size;
|
|
870
972
|
const agentCount = new Set(successful.map((r) => r.agent)).size;
|
|
973
|
+
const symlinkFailures = successful.filter((r) => r.symlinkFailed);
|
|
974
|
+
const copiedAgents = symlinkFailures.map((r) => r.agent);
|
|
871
975
|
const resultLines = [];
|
|
872
|
-
for (const [
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
976
|
+
for (const [, skillResults] of bySkill) {
|
|
977
|
+
const firstResult = skillResults[0];
|
|
978
|
+
if (firstResult.canonicalPath) {
|
|
979
|
+
const shortPath = shortenPath(firstResult.canonicalPath, cwd);
|
|
980
|
+
resultLines.push(`${chalk.green("\u2713")} ${shortPath}`);
|
|
981
|
+
}
|
|
982
|
+
const symlinked = skillResults.filter((r) => !r.symlinkFailed).map((r) => r.agent);
|
|
983
|
+
const copied = skillResults.filter((r) => r.symlinkFailed).map((r) => r.agent);
|
|
984
|
+
if (symlinked.length > 0) {
|
|
985
|
+
resultLines.push(` ${chalk.dim("\u2192")} ${formatList(symlinked)}`);
|
|
986
|
+
}
|
|
987
|
+
if (copied.length > 0) {
|
|
988
|
+
resultLines.push(` ${chalk.yellow("copied \u2192")} ${formatList(copied)}`);
|
|
876
989
|
}
|
|
877
|
-
resultLines.push("");
|
|
878
|
-
}
|
|
879
|
-
if (resultLines.length > 0 && resultLines[resultLines.length - 1] === "") {
|
|
880
|
-
resultLines.pop();
|
|
881
990
|
}
|
|
882
991
|
const title = chalk.green(`Installed ${skillCount} skill${skillCount !== 1 ? "s" : ""} to ${agentCount} agent${agentCount !== 1 ? "s" : ""}`);
|
|
883
992
|
p.note(resultLines.join("\n"), title);
|
|
993
|
+
if (symlinkFailures.length > 0) {
|
|
994
|
+
p.log.warn(chalk.yellow(`Symlinks failed for: ${formatList(copiedAgents)}`));
|
|
995
|
+
p.log.message(chalk.dim(" Files were copied instead. On Windows, enable Developer Mode for symlink support."));
|
|
996
|
+
}
|
|
884
997
|
}
|
|
885
998
|
if (failed.length > 0) {
|
|
886
999
|
console.log();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "add-skill",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.22",
|
|
4
4
|
"description": "Install agent skills onto coding agents (OpenCode, Claude Code, Codex, Cursor)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -20,19 +20,21 @@
|
|
|
20
20
|
"agent-skills",
|
|
21
21
|
"skills",
|
|
22
22
|
"ai-agents",
|
|
23
|
-
"
|
|
23
|
+
"amp",
|
|
24
|
+
"antigravity",
|
|
24
25
|
"claude-code",
|
|
26
|
+
"clawdbot",
|
|
25
27
|
"codex",
|
|
26
28
|
"cursor",
|
|
27
|
-
"
|
|
28
|
-
"kilo",
|
|
29
|
-
"roo",
|
|
30
|
-
"goose",
|
|
29
|
+
"droid",
|
|
31
30
|
"gemini-cli",
|
|
32
|
-
"antigravity",
|
|
33
31
|
"github-copilot",
|
|
34
|
-
"
|
|
35
|
-
"
|
|
32
|
+
"goose",
|
|
33
|
+
"kilo",
|
|
34
|
+
"kiro-cli",
|
|
35
|
+
"opencode",
|
|
36
|
+
"roo",
|
|
37
|
+
"trae",
|
|
36
38
|
"windsurf"
|
|
37
39
|
],
|
|
38
40
|
"repository": {
|