skillpm 0.0.1 → 0.0.4
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 +8 -72
- package/dist/cli.js +1 -1
- package/dist/commands/install.d.ts +1 -1
- package/dist/commands/install.js +57 -5
- package/dist/commands/list.js +2 -1
- package/dist/commands/uninstall.js +16 -1
- package/dist/manifest/schema.d.ts +7 -1
- package/dist/scanner/index.js +39 -3
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
# skillpm —
|
|
1
|
+
# skillpm — Package manager for Agent Skills. Built on npm.
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/skillpm)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
[](https://skillpm.dev)
|
|
6
6
|
|
|
7
|
-
The [Agent Skills spec](https://agentskills.io) defines what a skill is — but not how to publish, install, version, or share them. There's no registry, no dependency management, no way for one skill to build on another.
|
|
7
|
+
The [Agent Skills spec](https://agentskills.io) defines what a skill is — but not how to publish, install, version, or share them. There's no registry, no dependency management, no way for one skill to build on another. Without dependencies, skills become monoliths — authors duplicate instructions because there's no way to reuse another skill.
|
|
8
8
|
|
|
9
|
-
**skillpm** fills that gap
|
|
9
|
+
**skillpm** fills that gap. It's a lightweight orchestration layer — ~630 lines of code, 3 runtime dependencies — that maps Agent Skills onto npm's ecosystem. Same `package.json`, same `node_modules/`, same registry. Skills become npm packages you can publish, install, version, and depend on — just like any other package. Small skills that compose, not monoliths that overlap.
|
|
10
10
|
|
|
11
11
|
## Quick start
|
|
12
12
|
|
|
@@ -33,20 +33,22 @@ When you run `skillpm install <skill>`:
|
|
|
33
33
|
|
|
34
34
|
1. **npm install** — npm handles resolution, download, lockfile, `node_modules/`
|
|
35
35
|
2. **Scan** — skillpm scans `node_modules/` for packages containing `skills/*/SKILL.md`
|
|
36
|
-
3. **Link** — for each skill found, skillpm calls [`skills`](https://www.npmjs.com/package/skills) to wire it into
|
|
36
|
+
3. **Link** — for each skill found, skillpm calls [`skills`](https://www.npmjs.com/package/skills) to wire it into agent directories (Claude, Cursor, VS Code, Codex, and many more)
|
|
37
37
|
4. **MCP config** — skillpm collects `skillpm.mcpServers` from all skills (transitively) and configures each via [`add-mcp`](https://github.com/neondatabase/add-mcp)
|
|
38
38
|
|
|
39
39
|
That's it. Agents see the full skill tree with MCP servers configured.
|
|
40
40
|
|
|
41
41
|
## What's missing from the spec — and what skillpm adds
|
|
42
42
|
|
|
43
|
+
skillpm doesn't reinvent anything. It orchestrates three battle-tested tools: npm, [`skills`](https://www.npmjs.com/package/skills), and [`add-mcp`](https://github.com/neondatabase/add-mcp).
|
|
44
|
+
|
|
43
45
|
| The spec doesn't define... | skillpm adds... |
|
|
44
46
|
|---|---|
|
|
45
47
|
| A registry | Publish to npmjs.org with `skillpm publish` |
|
|
46
48
|
| An install command | `skillpm install` resolves the full dependency tree |
|
|
47
49
|
| Dependency management | Standard `package.json` `dependencies` — npm handles semver, lockfiles, audit |
|
|
48
50
|
| Versioning | npm semver, `package-lock.json`, reproducible installs |
|
|
49
|
-
| Agent wiring |
|
|
51
|
+
| Agent wiring | Links skills into agent directories via [`skills`](https://www.npmjs.com/package/skills) |
|
|
50
52
|
| MCP server config | Collects and configures MCP servers transitively via [`add-mcp`](https://github.com/neondatabase/add-mcp) |
|
|
51
53
|
|
|
52
54
|
## Commands
|
|
@@ -71,73 +73,7 @@ mkdir my-skill && cd my-skill
|
|
|
71
73
|
skillpm init
|
|
72
74
|
```
|
|
73
75
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
A skill is just an npm package with a `SKILL.md` inside a `skills/<name>/` subdirectory:
|
|
77
|
-
|
|
78
|
-
```
|
|
79
|
-
my-skill/
|
|
80
|
-
├── package.json # keywords: ["agent-skill"], deps, skillpm.mcpServers
|
|
81
|
-
├── README.md
|
|
82
|
-
└── skills/
|
|
83
|
-
└── my-skill/
|
|
84
|
-
├── SKILL.md # Skill definition
|
|
85
|
-
├── scripts/ # Optional scripts
|
|
86
|
-
├── references/ # Optional docs
|
|
87
|
-
└── assets/ # Optional templates/data
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
### Skill dependencies
|
|
91
|
-
|
|
92
|
-
Skill dependencies go in standard `package.json` `dependencies` — npm handles everything:
|
|
93
|
-
|
|
94
|
-
```json
|
|
95
|
-
{
|
|
96
|
-
"name": "my-skill",
|
|
97
|
-
"version": "1.0.0",
|
|
98
|
-
"keywords": ["agent-skill"],
|
|
99
|
-
"dependencies": {
|
|
100
|
-
"some-other-skill": "^1.0.0"
|
|
101
|
-
},
|
|
102
|
-
"skillpm": {
|
|
103
|
-
"mcpServers": ["@anthropic/mcp-server-filesystem"]
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
| Field | Purpose | Resolved by |
|
|
109
|
-
|---|---|---|
|
|
110
|
-
| `dependencies` | Skill packages on npm | npm (semver, lockfile, `node_modules/`) |
|
|
111
|
-
| `skillpm.mcpServers[]` | MCP servers to configure | `add-mcp` |
|
|
112
|
-
|
|
113
|
-
### SKILL.md format
|
|
114
|
-
|
|
115
|
-
```yaml
|
|
116
|
-
---
|
|
117
|
-
name: my-skill
|
|
118
|
-
description: What this skill does and when to use it.
|
|
119
|
-
license: MIT
|
|
120
|
-
allowed-tools: Bash Read
|
|
121
|
-
---
|
|
122
|
-
|
|
123
|
-
# My Skill
|
|
124
|
-
|
|
125
|
-
## When to use this skill
|
|
126
|
-
...
|
|
127
|
-
|
|
128
|
-
## Instructions
|
|
129
|
-
...
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
Version comes from `package.json` — don't duplicate it in SKILL.md.
|
|
133
|
-
|
|
134
|
-
### Publishing
|
|
135
|
-
|
|
136
|
-
```bash
|
|
137
|
-
skillpm publish
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
This validates the `"agent-skill"` keyword is present, then delegates to `npm publish`. Your skill will be discoverable on npmjs.org via [`keywords:agent-skill`](https://www.npmjs.com/search?q=keywords:agent-skill).
|
|
76
|
+
See the full [Creating Skills](https://skillpm.dev/creating-skills/) guide for package structure, SKILL.md format, dependencies, and publishing.
|
|
141
77
|
|
|
142
78
|
## What are Agent Skills?
|
|
143
79
|
|
package/dist/cli.js
CHANGED
|
@@ -7,7 +7,7 @@ import { publish } from './commands/publish.js';
|
|
|
7
7
|
import { sync } from './commands/sync.js';
|
|
8
8
|
import { mcp } from './commands/mcp.js';
|
|
9
9
|
import { log } from './utils/index.js';
|
|
10
|
-
const VERSION = '0.0.
|
|
10
|
+
const VERSION = '0.0.4';
|
|
11
11
|
const HELP = `
|
|
12
12
|
skillpm — Agent Skill package manager
|
|
13
13
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export declare function install(args: string[], cwd: string): Promise<void>;
|
|
2
|
-
export declare function wireSkills(
|
|
2
|
+
export declare function wireSkills(scanRoot: string, wireTarget?: string): Promise<void>;
|
package/dist/commands/install.js
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
import { npm, npx, log } from '../utils/index.js';
|
|
2
2
|
import { scanNodeModules, collectMcpServers } from '../scanner/index.js';
|
|
3
|
+
import { execFile } from 'node:child_process';
|
|
4
|
+
import { promisify } from 'node:util';
|
|
5
|
+
import { dirname } from 'node:path';
|
|
6
|
+
import { homedir } from 'node:os';
|
|
7
|
+
const execFileAsync = promisify(execFile);
|
|
8
|
+
function isGlobalFlag(args) {
|
|
9
|
+
return args.includes('-g') || args.includes('--global');
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Resolve the node_modules root to scan. For global installs, uses `npm root -g`.
|
|
13
|
+
*/
|
|
14
|
+
async function resolveNodeModulesRoot(args, cwd) {
|
|
15
|
+
if (!isGlobalFlag(args))
|
|
16
|
+
return cwd;
|
|
17
|
+
const { stdout } = await execFileAsync('npm', ['root', '-g']);
|
|
18
|
+
return dirname(stdout.trim());
|
|
19
|
+
}
|
|
3
20
|
export async function install(args, cwd) {
|
|
4
21
|
// Step 1: npm install
|
|
5
22
|
const npmArgs = ['install', ...args];
|
|
@@ -17,11 +34,15 @@ export async function install(args, cwd) {
|
|
|
17
34
|
process.exit(1);
|
|
18
35
|
}
|
|
19
36
|
// Step 2: Scan for skills and wire them
|
|
20
|
-
await
|
|
37
|
+
const scanRoot = await resolveNodeModulesRoot(args, cwd);
|
|
38
|
+
// For global installs, wire skills into user's home directory (not the npm prefix)
|
|
39
|
+
const wireTarget = isGlobalFlag(args) ? homedir() : cwd;
|
|
40
|
+
await wireSkills(scanRoot, wireTarget);
|
|
21
41
|
}
|
|
22
|
-
export async function wireSkills(
|
|
42
|
+
export async function wireSkills(scanRoot, wireTarget) {
|
|
43
|
+
const wireCwd = wireTarget ?? scanRoot;
|
|
23
44
|
// Scan node_modules/ for SKILL.md packages
|
|
24
|
-
const skills = await scanNodeModules(
|
|
45
|
+
const skills = await scanNodeModules(scanRoot);
|
|
25
46
|
if (skills.length === 0) {
|
|
26
47
|
log.info('No skill packages found in node_modules/');
|
|
27
48
|
return;
|
|
@@ -31,13 +52,16 @@ export async function wireSkills(cwd) {
|
|
|
31
52
|
for (const skill of skills) {
|
|
32
53
|
log.info(`Linking ${log.skill(skill.name, skill.version)} into agent directories`);
|
|
33
54
|
try {
|
|
34
|
-
await npx(['skills', 'add', skill.skillDir, '--all', '-y'], { cwd });
|
|
55
|
+
await npx(['skills', 'add', skill.skillDir, '--all', '-y'], { cwd: wireCwd });
|
|
35
56
|
log.success(`Linked ${skill.name}`);
|
|
36
57
|
}
|
|
37
58
|
catch (err) {
|
|
38
59
|
const msg = err instanceof Error ? err.message : String(err);
|
|
39
60
|
log.warn(`Failed to link ${skill.name}: ${msg}`);
|
|
40
61
|
}
|
|
62
|
+
if (skill.legacy) {
|
|
63
|
+
log.warn(`${skill.name}: SKILL.md is at package root. Move to skills/<name>/SKILL.md for full compatibility. See https://skillpm.dev/creating-skills/`);
|
|
64
|
+
}
|
|
41
65
|
}
|
|
42
66
|
// Collect and configure MCP servers
|
|
43
67
|
const mcpServers = collectMcpServers(skills);
|
|
@@ -46,7 +70,7 @@ export async function wireSkills(cwd) {
|
|
|
46
70
|
for (const server of mcpServers) {
|
|
47
71
|
log.info(`Configuring MCP server: ${server}`);
|
|
48
72
|
try {
|
|
49
|
-
await npx(['add-mcp', server, '-y'], { cwd });
|
|
73
|
+
await npx(['add-mcp', server, '-y'], { cwd: wireCwd });
|
|
50
74
|
log.success(`Configured ${server}`);
|
|
51
75
|
}
|
|
52
76
|
catch (err) {
|
|
@@ -55,5 +79,33 @@ export async function wireSkills(cwd) {
|
|
|
55
79
|
}
|
|
56
80
|
}
|
|
57
81
|
}
|
|
82
|
+
// Wire agent definitions via add-agent
|
|
83
|
+
for (const skill of skills) {
|
|
84
|
+
for (const agentFile of skill.agents) {
|
|
85
|
+
log.info(`Wiring agent from ${log.skill(skill.name, skill.version)}`);
|
|
86
|
+
try {
|
|
87
|
+
await npx(['add-agent', agentFile, '--package', skill.name], { cwd: wireCwd });
|
|
88
|
+
log.success(`Wired agent ${agentFile}`);
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
92
|
+
log.warn(`Failed to wire agent: ${msg}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// Wire prompts/instructions via add-prompt
|
|
97
|
+
for (const skill of skills) {
|
|
98
|
+
for (const promptFile of skill.prompts) {
|
|
99
|
+
log.info(`Wiring prompt from ${log.skill(skill.name, skill.version)}`);
|
|
100
|
+
try {
|
|
101
|
+
await npx(['add-prompt', promptFile, '--package', skill.name], { cwd: wireCwd });
|
|
102
|
+
log.success(`Wired prompt ${promptFile}`);
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
106
|
+
log.warn(`Failed to wire prompt: ${msg}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
58
110
|
}
|
|
59
111
|
//# sourceMappingURL=install.js.map
|
package/dist/commands/list.js
CHANGED
|
@@ -11,7 +11,8 @@ export async function list(cwd) {
|
|
|
11
11
|
for (const skill of skills) {
|
|
12
12
|
const frontmatter = await readSkillMd(skill.skillDir);
|
|
13
13
|
const description = frontmatter?.description ?? '';
|
|
14
|
-
|
|
14
|
+
const legacyTag = skill.legacy ? ' (legacy)' : '';
|
|
15
|
+
console.log(` ${log.skill(skill.name, skill.version)}${legacyTag}`);
|
|
15
16
|
if (description) {
|
|
16
17
|
console.log(` ${description}`);
|
|
17
18
|
}
|
|
@@ -1,10 +1,25 @@
|
|
|
1
|
-
import { npm, log } from '../utils/index.js';
|
|
1
|
+
import { npm, npx, log } from '../utils/index.js';
|
|
2
2
|
import { wireSkills } from './install.js';
|
|
3
3
|
export async function uninstall(args, cwd) {
|
|
4
4
|
if (args.length === 0) {
|
|
5
5
|
log.error('Usage: skillpm uninstall <skill> [skill...]');
|
|
6
6
|
process.exit(1);
|
|
7
7
|
}
|
|
8
|
+
// Clean up agents/prompts for removed packages before npm uninstall
|
|
9
|
+
for (const pkg of args) {
|
|
10
|
+
try {
|
|
11
|
+
await npx(['add-agent', '--remove-package', pkg], { cwd });
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
// Ignore — package may not have had agents
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
await npx(['add-prompt', '--remove-package', pkg], { cwd });
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
// Ignore — package may not have had prompts
|
|
21
|
+
}
|
|
22
|
+
}
|
|
8
23
|
log.info(`Running npm uninstall ${args.join(' ')}`);
|
|
9
24
|
try {
|
|
10
25
|
const result = await npm(['uninstall', ...args], { cwd });
|
|
@@ -14,7 +14,13 @@ export interface SkillInfo {
|
|
|
14
14
|
name: string;
|
|
15
15
|
version: string;
|
|
16
16
|
path: string;
|
|
17
|
-
/** Path to the
|
|
17
|
+
/** Path to the directory containing SKILL.md */
|
|
18
18
|
skillDir: string;
|
|
19
19
|
mcpServers: string[];
|
|
20
|
+
/** True if SKILL.md is at package root instead of skills/<name>/ */
|
|
21
|
+
legacy?: boolean;
|
|
22
|
+
/** Paths to agent definition .md files in agents/ */
|
|
23
|
+
agents: string[];
|
|
24
|
+
/** Paths to prompt/instruction .md files in prompts/ */
|
|
25
|
+
prompts: string[];
|
|
20
26
|
}
|
package/dist/scanner/index.js
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
import { readdir, access } from 'node:fs/promises';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { readPackageJson, parseSkillpmField } from '../manifest/index.js';
|
|
4
|
+
/**
|
|
5
|
+
* Scan a directory for .md files (used for agents/ and prompts/ dirs).
|
|
6
|
+
*/
|
|
7
|
+
async function scanMdFiles(dir) {
|
|
8
|
+
let entries;
|
|
9
|
+
try {
|
|
10
|
+
entries = await readdir(dir);
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
return entries.filter((e) => e.endsWith('.md')).map((e) => join(dir, e));
|
|
16
|
+
}
|
|
4
17
|
/**
|
|
5
18
|
* Scan node_modules/ for packages that contain a skills/<name>/SKILL.md file.
|
|
6
19
|
* Returns metadata for each discovered skill package.
|
|
@@ -48,14 +61,14 @@ async function tryReadSkill(pkgDir) {
|
|
|
48
61
|
const pkg = await readPackageJson(pkgDir);
|
|
49
62
|
if (!pkg)
|
|
50
63
|
return null;
|
|
51
|
-
//
|
|
64
|
+
// Preferred: look for skills/*/SKILL.md
|
|
52
65
|
const skillsDir = join(pkgDir, 'skills');
|
|
53
66
|
let skillSubdirs;
|
|
54
67
|
try {
|
|
55
68
|
skillSubdirs = await readdir(skillsDir);
|
|
56
69
|
}
|
|
57
70
|
catch {
|
|
58
|
-
|
|
71
|
+
skillSubdirs = [];
|
|
59
72
|
}
|
|
60
73
|
for (const sub of skillSubdirs) {
|
|
61
74
|
const skillDir = join(skillsDir, sub);
|
|
@@ -66,15 +79,38 @@ async function tryReadSkill(pkgDir) {
|
|
|
66
79
|
continue;
|
|
67
80
|
}
|
|
68
81
|
const skillpm = parseSkillpmField(pkg);
|
|
82
|
+
const agents = await scanMdFiles(join(pkgDir, 'agents'));
|
|
83
|
+
const prompts = await scanMdFiles(join(pkgDir, 'prompts'));
|
|
69
84
|
return {
|
|
70
85
|
name: pkg.name,
|
|
71
86
|
version: pkg.version,
|
|
72
87
|
path: pkgDir,
|
|
73
88
|
skillDir,
|
|
74
89
|
mcpServers: skillpm?.mcpServers ?? [],
|
|
90
|
+
agents,
|
|
91
|
+
prompts,
|
|
75
92
|
};
|
|
76
93
|
}
|
|
77
|
-
|
|
94
|
+
// Fallback: root SKILL.md (legacy format)
|
|
95
|
+
try {
|
|
96
|
+
await access(join(pkgDir, 'SKILL.md'));
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
const skillpm = parseSkillpmField(pkg);
|
|
102
|
+
const agents = await scanMdFiles(join(pkgDir, 'agents'));
|
|
103
|
+
const prompts = await scanMdFiles(join(pkgDir, 'prompts'));
|
|
104
|
+
return {
|
|
105
|
+
name: pkg.name,
|
|
106
|
+
version: pkg.version,
|
|
107
|
+
path: pkgDir,
|
|
108
|
+
skillDir: pkgDir,
|
|
109
|
+
mcpServers: skillpm?.mcpServers ?? [],
|
|
110
|
+
legacy: true,
|
|
111
|
+
agents,
|
|
112
|
+
prompts,
|
|
113
|
+
};
|
|
78
114
|
}
|
|
79
115
|
/**
|
|
80
116
|
* Collect all MCP server requirements from all discovered skills (transitive).
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skillpm",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.0.4",
|
|
4
|
+
"description": "Package manager for Agent Skills. Built on npm.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"skillpm": "
|
|
7
|
+
"skillpm": "dist/cli.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"build": "tsc",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@eslint/js": "^10.0.1",
|
|
55
|
-
"@types/node": "^25.3.
|
|
55
|
+
"@types/node": "^25.3.1",
|
|
56
56
|
"eslint": "^10.0.2",
|
|
57
57
|
"prettier": "^3.8.1",
|
|
58
58
|
"typescript": "^5.9.3",
|