ai-agent-skills 1.6.2 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -4
- package/cli.js +238 -14
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
<p align="center">
|
|
13
13
|
<img src="https://img.shields.io/badge/skills-40-blue?style=flat-square" alt="Skills" />
|
|
14
|
-
<img src="https://img.shields.io/badge/agents-
|
|
14
|
+
<img src="https://img.shields.io/badge/agents-11+-green?style=flat-square" alt="Compatible Agents" />
|
|
15
15
|
<img src="https://img.shields.io/badge/license-MIT-brightgreen?style=flat-square" alt="License" />
|
|
16
16
|
<img src="https://img.shields.io/npm/v/ai-agent-skills?style=flat-square&color=red" alt="npm" />
|
|
17
17
|
<img src="https://img.shields.io/npm/dt/ai-agent-skills?style=flat-square&color=orange" alt="Downloads" />
|
|
@@ -46,17 +46,17 @@ npx ai-agent-skills install anthropics/skills/pdf # specific skill
|
|
|
46
46
|
npx ai-agent-skills install ./my-custom-skill
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
**One command. Every agent.** By default, skills install to Claude Code, Cursor, Codex, Amp, VS Code, Copilot, Goose, Letta, and OpenCode simultaneously.
|
|
49
|
+
**One command. Every agent.** By default, skills install to Claude Code, Cursor, Codex, Amp, VS Code, Copilot, Gemini CLI, Goose, Letta, and OpenCode simultaneously.
|
|
50
50
|
|
|
51
51
|
## Why This Exists
|
|
52
52
|
|
|
53
53
|
Every major AI coding agent now supports skills. But they're scattered everywhere.
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
We also created our own repo to cureate the best in one place. Quality over quantity https://www.skillcreator.ai/explore All skills follow the [Agent Skills spec](https://agentskills.io).
|
|
56
56
|
|
|
57
57
|
## Compatible Agents
|
|
58
58
|
|
|
59
|
-
Works with **Claude Code**, **Cursor**, **Codex**, **Amp**, **VS Code**, **GitHub Copilot**, **Goose**, **Letta**, and **OpenCode**.
|
|
59
|
+
Works with **Claude Code**, **Cursor**, **Codex**, **Amp**, **VS Code**, **GitHub Copilot**, **Gemini CLI**, **Goose**, **Letta**, and **OpenCode**.
|
|
60
60
|
|
|
61
61
|
## Available Skills
|
|
62
62
|
|
|
@@ -162,6 +162,7 @@ By default, `install` targets **all agents**. Use `--agent <name>` to install to
|
|
|
162
162
|
| Codex | `--agent codex` | `~/.codex/skills/` |
|
|
163
163
|
| Amp | `--agent amp` | `~/.amp/skills/` |
|
|
164
164
|
| VS Code / Copilot | `--agent vscode` | `.github/skills/` |
|
|
165
|
+
| Gemini CLI | `--agent gemini` | `~/.gemini/skills/` |
|
|
165
166
|
| Goose | `--agent goose` | `~/.config/goose/skills/` |
|
|
166
167
|
| OpenCode | `--agent opencode` | `~/.opencode/skill/` |
|
|
167
168
|
| Letta | `--agent letta` | `~/.letta/skills/` |
|
package/cli.js
CHANGED
|
@@ -24,9 +24,10 @@ const AGENT_PATHS = {
|
|
|
24
24
|
copilot: path.join(process.cwd(), '.github', 'skills'),
|
|
25
25
|
project: path.join(process.cwd(), '.skills'),
|
|
26
26
|
goose: path.join(os.homedir(), '.config', 'goose', 'skills'),
|
|
27
|
-
opencode: path.join(os.homedir(), '.opencode', 'skill'),
|
|
27
|
+
opencode: path.join(os.homedir(), '.config', 'opencode', 'skill'),
|
|
28
28
|
codex: path.join(os.homedir(), '.codex', 'skills'),
|
|
29
29
|
letta: path.join(os.homedir(), '.letta', 'skills'),
|
|
30
|
+
gemini: path.join(os.homedir(), '.gemini', 'skills'),
|
|
30
31
|
};
|
|
31
32
|
|
|
32
33
|
const colors = {
|
|
@@ -70,6 +71,41 @@ function saveConfig(config) {
|
|
|
70
71
|
}
|
|
71
72
|
}
|
|
72
73
|
|
|
74
|
+
// ============ SKILL METADATA SUPPORT ============
|
|
75
|
+
|
|
76
|
+
const SKILL_META_FILE = '.skill-meta.json';
|
|
77
|
+
|
|
78
|
+
function writeSkillMeta(skillPath, meta) {
|
|
79
|
+
try {
|
|
80
|
+
const metaPath = path.join(skillPath, SKILL_META_FILE);
|
|
81
|
+
const now = new Date().toISOString();
|
|
82
|
+
const metadata = {
|
|
83
|
+
...meta,
|
|
84
|
+
// Preserve original installedAt if it exists, otherwise set it
|
|
85
|
+
installedAt: meta.installedAt || now,
|
|
86
|
+
// Always update the updatedAt timestamp
|
|
87
|
+
updatedAt: now
|
|
88
|
+
};
|
|
89
|
+
fs.writeFileSync(metaPath, JSON.stringify(metadata, null, 2));
|
|
90
|
+
return true;
|
|
91
|
+
} catch (e) {
|
|
92
|
+
// Non-fatal - skill still works without metadata
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function readSkillMeta(skillPath) {
|
|
98
|
+
try {
|
|
99
|
+
const metaPath = path.join(skillPath, SKILL_META_FILE);
|
|
100
|
+
if (fs.existsSync(metaPath)) {
|
|
101
|
+
return JSON.parse(fs.readFileSync(metaPath, 'utf8'));
|
|
102
|
+
}
|
|
103
|
+
} catch (e) {
|
|
104
|
+
// Ignore - treat as legacy skill
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
|
|
73
109
|
// ============ SECURITY VALIDATION ============
|
|
74
110
|
|
|
75
111
|
function validateSkillName(name) {
|
|
@@ -367,6 +403,12 @@ function installSkill(skillName, agent = 'claude', dryRun = false) {
|
|
|
367
403
|
|
|
368
404
|
copyDir(sourcePath, destPath);
|
|
369
405
|
|
|
406
|
+
// Write metadata for update tracking
|
|
407
|
+
writeSkillMeta(destPath, {
|
|
408
|
+
source: 'registry',
|
|
409
|
+
name: skillName
|
|
410
|
+
});
|
|
411
|
+
|
|
370
412
|
success(`\nInstalled: ${skillName}`);
|
|
371
413
|
info(`Agent: ${agent}`);
|
|
372
414
|
info(`Location: ${destPath}`);
|
|
@@ -393,7 +435,8 @@ function showAgentInstructions(agent, skillName, destPath) {
|
|
|
393
435
|
project: `The skill is installed in .skills/ in your current directory.\nThis makes it portable across all compatible agents.`,
|
|
394
436
|
letta: `The skill is now available in Letta.`,
|
|
395
437
|
goose: `The skill is now available in Goose.`,
|
|
396
|
-
opencode: `The skill is now available in OpenCode
|
|
438
|
+
opencode: `The skill is now available in OpenCode.`,
|
|
439
|
+
gemini: `The skill is now available in Gemini CLI.\nMake sure Agent Skills is enabled in your Gemini CLI settings.`
|
|
397
440
|
};
|
|
398
441
|
|
|
399
442
|
log(`${colors.dim}${instructions[agent] || `The skill is ready to use with ${agent}.`}${colors.reset}`);
|
|
@@ -474,32 +517,133 @@ function listInstalledSkills(agent = 'claude') {
|
|
|
474
517
|
log(`${colors.dim}Uninstall: npx ai-agent-skills uninstall <name> --agent ${agent}${colors.reset}`);
|
|
475
518
|
}
|
|
476
519
|
|
|
477
|
-
|
|
520
|
+
// Update from bundled registry
|
|
521
|
+
function updateFromRegistry(skillName, agent, destPath, dryRun) {
|
|
522
|
+
const sourcePath = path.join(SKILLS_DIR, skillName);
|
|
523
|
+
|
|
524
|
+
if (!fs.existsSync(sourcePath)) {
|
|
525
|
+
error(`Skill "${skillName}" not found in repository.`);
|
|
526
|
+
return false;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
if (dryRun) {
|
|
530
|
+
log(`\n${colors.bold}Dry Run${colors.reset} (no changes made)\n`);
|
|
531
|
+
info(`Would update: ${skillName} (from registry)`);
|
|
532
|
+
info(`Agent: ${agent}`);
|
|
533
|
+
info(`Path: ${destPath}`);
|
|
534
|
+
return true;
|
|
535
|
+
}
|
|
536
|
+
|
|
478
537
|
try {
|
|
479
|
-
|
|
538
|
+
fs.rmSync(destPath, { recursive: true });
|
|
539
|
+
copyDir(sourcePath, destPath);
|
|
540
|
+
|
|
541
|
+
// Write metadata
|
|
542
|
+
writeSkillMeta(destPath, {
|
|
543
|
+
source: 'registry',
|
|
544
|
+
name: skillName
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
success(`\nUpdated: ${skillName}`);
|
|
548
|
+
info(`Agent: ${agent}`);
|
|
549
|
+
info(`Location: ${destPath}`);
|
|
550
|
+
return true;
|
|
480
551
|
} catch (e) {
|
|
481
|
-
error(e.message);
|
|
552
|
+
error(`Failed to update skill: ${e.message}`);
|
|
482
553
|
return false;
|
|
483
554
|
}
|
|
555
|
+
}
|
|
484
556
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
const
|
|
557
|
+
// Update from GitHub repository
|
|
558
|
+
function updateFromGitHub(meta, skillName, agent, destPath, dryRun) {
|
|
559
|
+
const { execFileSync } = require('child_process');
|
|
560
|
+
const repo = meta.repo;
|
|
561
|
+
|
|
562
|
+
// Validate repo format
|
|
563
|
+
if (!repo || typeof repo !== 'string' || !repo.includes('/')) {
|
|
564
|
+
error(`Invalid repository in metadata: ${repo}`);
|
|
565
|
+
error(`Try reinstalling the skill from GitHub.`);
|
|
566
|
+
return false;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
if (dryRun) {
|
|
570
|
+
log(`\n${colors.bold}Dry Run${colors.reset} (no changes made)\n`);
|
|
571
|
+
info(`Would update: ${skillName} (from github:${repo})`);
|
|
572
|
+
info(`Agent: ${agent}`);
|
|
573
|
+
info(`Path: ${destPath}`);
|
|
574
|
+
return true;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
const tempDir = path.join(os.tmpdir(), `ai-skills-update-${Date.now()}`);
|
|
578
|
+
|
|
579
|
+
try {
|
|
580
|
+
info(`Updating ${skillName} from ${repo}...`);
|
|
581
|
+
const repoUrl = `https://github.com/${repo}.git`;
|
|
582
|
+
execFileSync('git', ['clone', '--depth', '1', repoUrl, tempDir], { stdio: 'pipe' });
|
|
583
|
+
|
|
584
|
+
// Determine source path in cloned repo
|
|
585
|
+
let sourcePath;
|
|
586
|
+
if (meta.isRootSkill) {
|
|
587
|
+
sourcePath = tempDir;
|
|
588
|
+
} else if (meta.skillPath) {
|
|
589
|
+
// Check if skills/ subdirectory exists
|
|
590
|
+
const skillsSubdir = path.join(tempDir, 'skills', meta.skillPath);
|
|
591
|
+
const directPath = path.join(tempDir, meta.skillPath);
|
|
592
|
+
sourcePath = fs.existsSync(skillsSubdir) ? skillsSubdir : directPath;
|
|
593
|
+
} else {
|
|
594
|
+
sourcePath = tempDir;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
if (!fs.existsSync(sourcePath) || !fs.existsSync(path.join(sourcePath, 'SKILL.md'))) {
|
|
598
|
+
error(`Skill not found in repository ${repo}`);
|
|
599
|
+
fs.rmSync(tempDir, { recursive: true });
|
|
600
|
+
return false;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
fs.rmSync(destPath, { recursive: true });
|
|
604
|
+
copyDir(sourcePath, destPath);
|
|
605
|
+
|
|
606
|
+
// Preserve metadata
|
|
607
|
+
writeSkillMeta(destPath, meta);
|
|
608
|
+
|
|
609
|
+
fs.rmSync(tempDir, { recursive: true });
|
|
610
|
+
|
|
611
|
+
success(`\nUpdated: ${skillName}`);
|
|
612
|
+
info(`Source: github:${repo}`);
|
|
613
|
+
info(`Agent: ${agent}`);
|
|
614
|
+
info(`Location: ${destPath}`);
|
|
615
|
+
return true;
|
|
616
|
+
} catch (e) {
|
|
617
|
+
error(`Failed to update from GitHub: ${e.message}`);
|
|
618
|
+
try { fs.rmSync(tempDir, { recursive: true }); } catch {}
|
|
619
|
+
return false;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// Update from local path
|
|
624
|
+
function updateFromLocalPath(meta, skillName, agent, destPath, dryRun) {
|
|
625
|
+
const sourcePath = meta.path;
|
|
626
|
+
|
|
627
|
+
if (!sourcePath || typeof sourcePath !== 'string') {
|
|
628
|
+
error(`Invalid path in metadata.`);
|
|
629
|
+
error(`Try reinstalling the skill from the local path.`);
|
|
630
|
+
return false;
|
|
631
|
+
}
|
|
488
632
|
|
|
489
633
|
if (!fs.existsSync(sourcePath)) {
|
|
490
|
-
error(`
|
|
634
|
+
error(`Source path no longer exists: ${sourcePath}`);
|
|
491
635
|
return false;
|
|
492
636
|
}
|
|
493
637
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
638
|
+
// Verify it's still a valid skill directory
|
|
639
|
+
if (!fs.existsSync(path.join(sourcePath, 'SKILL.md'))) {
|
|
640
|
+
error(`Source is no longer a valid skill (missing SKILL.md): ${sourcePath}`);
|
|
497
641
|
return false;
|
|
498
642
|
}
|
|
499
643
|
|
|
500
644
|
if (dryRun) {
|
|
501
645
|
log(`\n${colors.bold}Dry Run${colors.reset} (no changes made)\n`);
|
|
502
|
-
info(`Would update: ${skillName}`);
|
|
646
|
+
info(`Would update: ${skillName} (from local:${sourcePath})`);
|
|
503
647
|
info(`Agent: ${agent}`);
|
|
504
648
|
info(`Path: ${destPath}`);
|
|
505
649
|
return true;
|
|
@@ -509,16 +653,57 @@ function updateSkill(skillName, agent = 'claude', dryRun = false) {
|
|
|
509
653
|
fs.rmSync(destPath, { recursive: true });
|
|
510
654
|
copyDir(sourcePath, destPath);
|
|
511
655
|
|
|
656
|
+
// Preserve metadata
|
|
657
|
+
writeSkillMeta(destPath, meta);
|
|
658
|
+
|
|
512
659
|
success(`\nUpdated: ${skillName}`);
|
|
660
|
+
info(`Source: local:${sourcePath}`);
|
|
513
661
|
info(`Agent: ${agent}`);
|
|
514
662
|
info(`Location: ${destPath}`);
|
|
515
663
|
return true;
|
|
516
664
|
} catch (e) {
|
|
517
|
-
error(`Failed to update
|
|
665
|
+
error(`Failed to update from local path: ${e.message}`);
|
|
518
666
|
return false;
|
|
519
667
|
}
|
|
520
668
|
}
|
|
521
669
|
|
|
670
|
+
function updateSkill(skillName, agent = 'claude', dryRun = false) {
|
|
671
|
+
try {
|
|
672
|
+
validateSkillName(skillName);
|
|
673
|
+
} catch (e) {
|
|
674
|
+
error(e.message);
|
|
675
|
+
return false;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
const destDir = AGENT_PATHS[agent] || AGENT_PATHS.claude;
|
|
679
|
+
const destPath = path.join(destDir, skillName);
|
|
680
|
+
|
|
681
|
+
if (!fs.existsSync(destPath)) {
|
|
682
|
+
error(`Skill "${skillName}" is not installed for ${agent}.`);
|
|
683
|
+
log(`\nUse 'install' to add it first.`);
|
|
684
|
+
return false;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
// Read metadata to determine source
|
|
688
|
+
const meta = readSkillMeta(destPath);
|
|
689
|
+
|
|
690
|
+
if (!meta) {
|
|
691
|
+
// Legacy skill without metadata - try registry
|
|
692
|
+
return updateFromRegistry(skillName, agent, destPath, dryRun);
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
// Route to correct update method based on source
|
|
696
|
+
switch (meta.source) {
|
|
697
|
+
case 'github':
|
|
698
|
+
return updateFromGitHub(meta, skillName, agent, destPath, dryRun);
|
|
699
|
+
case 'local':
|
|
700
|
+
return updateFromLocalPath(meta, skillName, agent, destPath, dryRun);
|
|
701
|
+
case 'registry':
|
|
702
|
+
default:
|
|
703
|
+
return updateFromRegistry(skillName, agent, destPath, dryRun);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
522
707
|
function updateAllSkills(agent = 'claude', dryRun = false) {
|
|
523
708
|
const installed = getInstalledSkills(agent);
|
|
524
709
|
|
|
@@ -910,6 +1095,14 @@ async function installFromGitHub(source, agent = 'claude', dryRun = false) {
|
|
|
910
1095
|
}
|
|
911
1096
|
|
|
912
1097
|
copyDir(skillPath, destPath);
|
|
1098
|
+
|
|
1099
|
+
// Write metadata for update tracking
|
|
1100
|
+
writeSkillMeta(destPath, {
|
|
1101
|
+
source: 'github',
|
|
1102
|
+
repo: `${owner}/${repo}`,
|
|
1103
|
+
skillPath: skillName
|
|
1104
|
+
});
|
|
1105
|
+
|
|
913
1106
|
success(`\nInstalled: ${skillName} from ${owner}/${repo}`);
|
|
914
1107
|
info(`Location: ${destPath}`);
|
|
915
1108
|
} else if (isRootSkill) {
|
|
@@ -933,6 +1126,14 @@ async function installFromGitHub(source, agent = 'claude', dryRun = false) {
|
|
|
933
1126
|
}
|
|
934
1127
|
|
|
935
1128
|
copyDir(tempDir, destPath);
|
|
1129
|
+
|
|
1130
|
+
// Write metadata for update tracking
|
|
1131
|
+
writeSkillMeta(destPath, {
|
|
1132
|
+
source: 'github',
|
|
1133
|
+
repo: `${owner}/${repo}`,
|
|
1134
|
+
isRootSkill: true
|
|
1135
|
+
});
|
|
1136
|
+
|
|
936
1137
|
success(`\nInstalled: ${skillName} from ${owner}/${repo}`);
|
|
937
1138
|
info(`Location: ${destPath}`);
|
|
938
1139
|
} else {
|
|
@@ -952,6 +1153,14 @@ async function installFromGitHub(source, agent = 'claude', dryRun = false) {
|
|
|
952
1153
|
}
|
|
953
1154
|
|
|
954
1155
|
copyDir(skillPath, destPath);
|
|
1156
|
+
|
|
1157
|
+
// Write metadata for update tracking
|
|
1158
|
+
writeSkillMeta(destPath, {
|
|
1159
|
+
source: 'github',
|
|
1160
|
+
repo: `${owner}/${repo}`,
|
|
1161
|
+
skillPath: entry.name
|
|
1162
|
+
});
|
|
1163
|
+
|
|
955
1164
|
log(` ${colors.green}✓${colors.reset} ${entry.name}`);
|
|
956
1165
|
installed++;
|
|
957
1166
|
}
|
|
@@ -1010,6 +1219,13 @@ function installFromLocalPath(source, agent = 'claude', dryRun = false) {
|
|
|
1010
1219
|
}
|
|
1011
1220
|
|
|
1012
1221
|
copyDir(sourcePath, destPath);
|
|
1222
|
+
|
|
1223
|
+
// Write metadata for update tracking
|
|
1224
|
+
writeSkillMeta(destPath, {
|
|
1225
|
+
source: 'local',
|
|
1226
|
+
path: sourcePath
|
|
1227
|
+
});
|
|
1228
|
+
|
|
1013
1229
|
success(`\nInstalled: ${skillName} from local path`);
|
|
1014
1230
|
info(`Location: ${destPath}`);
|
|
1015
1231
|
} else {
|
|
@@ -1029,6 +1245,13 @@ function installFromLocalPath(source, agent = 'claude', dryRun = false) {
|
|
|
1029
1245
|
}
|
|
1030
1246
|
|
|
1031
1247
|
copyDir(skillPath, destPath);
|
|
1248
|
+
|
|
1249
|
+
// Write metadata for update tracking
|
|
1250
|
+
writeSkillMeta(destPath, {
|
|
1251
|
+
source: 'local',
|
|
1252
|
+
path: skillPath
|
|
1253
|
+
});
|
|
1254
|
+
|
|
1032
1255
|
log(` ${colors.green}✓${colors.reset} ${entry.name}`);
|
|
1033
1256
|
installed++;
|
|
1034
1257
|
}
|
|
@@ -1090,6 +1313,7 @@ ${colors.bold}Agents:${colors.reset} (install targets ALL by default)
|
|
|
1090
1313
|
${colors.cyan}amp${colors.reset} ~/.amp/skills/
|
|
1091
1314
|
${colors.cyan}vscode${colors.reset} .github/skills/ (project)
|
|
1092
1315
|
${colors.cyan}copilot${colors.reset} .github/skills/ (alias for vscode)
|
|
1316
|
+
${colors.cyan}gemini${colors.reset} ~/.gemini/skills/
|
|
1093
1317
|
${colors.cyan}goose${colors.reset} ~/.config/goose/skills/
|
|
1094
1318
|
${colors.cyan}opencode${colors.reset} ~/.opencode/skill/
|
|
1095
1319
|
${colors.cyan}letta${colors.reset} ~/.letta/skills/
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-agent-skills",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Install curated AI agent skills with one command. Works with Claude Code, Cursor,
|
|
3
|
+
"version": "1.8.0",
|
|
4
|
+
"description": "Install curated AI agent skills with one command. Works with Claude Code, Cursor, Codex, Gemini CLI, VS Code, Copilot, and 11+ agents.",
|
|
5
5
|
"main": "cli.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"ai-agent-skills": "./cli.js",
|
|
@@ -24,6 +24,9 @@
|
|
|
24
24
|
"skills",
|
|
25
25
|
"claude",
|
|
26
26
|
"cursor",
|
|
27
|
+
"codex",
|
|
28
|
+
"gemini",
|
|
29
|
+
"copilot",
|
|
27
30
|
"amp",
|
|
28
31
|
"vscode",
|
|
29
32
|
"mcp"
|