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 CHANGED
@@ -1,12 +1,12 @@
1
- # skillpm — npm for Agent Skills
1
+ # skillpm — Package manager for Agent Skills. Built on npm.
2
2
 
3
3
  [![npm](https://img.shields.io/npm/v/skillpm)](https://www.npmjs.com/package/skillpm)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
  [![Docs](https://img.shields.io/badge/docs-skillpm.dev-blue)](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 by mapping 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.
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 37+ agent directories (Claude, Cursor, VS Code, Codex, etc.)
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 | Auto-links skills into 37+ agent directories via [`skills`](https://www.npmjs.com/package/skills) |
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
- This creates a `package.json` (with the `"agent-skill"` keyword) and `skills/<name>/SKILL.md`. Edit the SKILL.md to define your skill.
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.1';
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(cwd: string): Promise<void>;
2
+ export declare function wireSkills(scanRoot: string, wireTarget?: string): Promise<void>;
@@ -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 wireSkills(cwd);
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(cwd) {
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(cwd);
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
@@ -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
- console.log(` ${log.skill(skill.name, skill.version)}`);
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 skills/<name>/ subdirectory containing SKILL.md */
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
  }
@@ -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
- // Look for skills/*/SKILL.md
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
- return null;
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
- return null;
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.1",
4
- "description": "npm for Agent Skills",
3
+ "version": "0.0.4",
4
+ "description": "Package manager for Agent Skills. Built on npm.",
5
5
  "type": "module",
6
6
  "bin": {
7
- "skillpm": "./dist/cli.js"
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.0",
55
+ "@types/node": "^25.3.1",
56
56
  "eslint": "^10.0.2",
57
57
  "prettier": "^3.8.1",
58
58
  "typescript": "^5.9.3",