skillpm 0.0.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 skillpm contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,157 @@
1
+ # skillpm — npm for Agent Skills
2
+
3
+ [![npm](https://img.shields.io/npm/v/skillpm)](https://www.npmjs.com/package/skillpm)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![Docs](https://img.shields.io/badge/docs-skillpm.dev-blue)](https://skillpm.dev)
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.
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.
10
+
11
+ ## Quick start
12
+
13
+ ```bash
14
+ # Install a skill (no global install needed)
15
+ npx skillpm install <skill-name>
16
+
17
+ # List installed skills
18
+ npx skillpm list
19
+
20
+ # Scaffold a new skill package
21
+ npx skillpm init
22
+ ```
23
+
24
+ Or install globally for convenience:
25
+
26
+ ```bash
27
+ npm install -g skillpm
28
+ ```
29
+
30
+ ## How it works
31
+
32
+ When you run `skillpm install <skill>`:
33
+
34
+ 1. **npm install** — npm handles resolution, download, lockfile, `node_modules/`
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.)
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
+
39
+ That's it. Agents see the full skill tree with MCP servers configured.
40
+
41
+ ## What's missing from the spec — and what skillpm adds
42
+
43
+ | The spec doesn't define... | skillpm adds... |
44
+ |---|---|
45
+ | A registry | Publish to npmjs.org with `skillpm publish` |
46
+ | An install command | `skillpm install` resolves the full dependency tree |
47
+ | Dependency management | Standard `package.json` `dependencies` — npm handles semver, lockfiles, audit |
48
+ | 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) |
50
+ | MCP server config | Collects and configures MCP servers transitively via [`add-mcp`](https://github.com/neondatabase/add-mcp) |
51
+
52
+ ## Commands
53
+
54
+ | Command | Description |
55
+ |---|---|
56
+ | `skillpm install [skill...]` | Install skill(s) + full dependency tree, wire into agent dirs |
57
+ | `skillpm uninstall <skill...>` | Remove skill(s) and clean up |
58
+ | `skillpm list` | List installed skill packages |
59
+ | `skillpm init` | Scaffold a new skill package |
60
+ | `skillpm publish` | Publish to npmjs.org (validates `"agent-skill"` keyword) |
61
+ | `skillpm sync` | Re-wire agent directories without reinstalling |
62
+ | `skillpm mcp add <source...>` | Configure MCP server(s) across agents |
63
+ | `skillpm mcp list` | List configured MCP servers |
64
+
65
+ Aliases: `i` for `install`, `rm`/`remove` for `uninstall`, `ls` for `list`.
66
+
67
+ ## Creating a skill
68
+
69
+ ```bash
70
+ mkdir my-skill && cd my-skill
71
+ skillpm init
72
+ ```
73
+
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).
141
+
142
+ ## What are Agent Skills?
143
+
144
+ Agent Skills are modular, reusable packages of instructions, scripts, and resources that AI agents can dynamically load to extend their capabilities. They follow an [open standard](https://agentskills.io) adopted by Claude, Codex, Cursor, Gemini CLI, Augment, and others.
145
+
146
+ ## Development
147
+
148
+ ```bash
149
+ npm install # install dependencies
150
+ npm run build # compile TypeScript
151
+ npm test # run tests
152
+ npm run lint # lint
153
+ ```
154
+
155
+ ## License
156
+
157
+ MIT
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env node
2
+ import { install } from './commands/install.js';
3
+ import { uninstall } from './commands/uninstall.js';
4
+ import { list } from './commands/list.js';
5
+ import { init } from './commands/init.js';
6
+ import { publish } from './commands/publish.js';
7
+ import { sync } from './commands/sync.js';
8
+ import { mcp } from './commands/mcp.js';
9
+ import { log } from './utils/index.js';
10
+ const VERSION = '0.0.1';
11
+ const HELP = `
12
+ skillpm — Agent Skill package manager
13
+
14
+ Usage:
15
+ skillpm install [skill...] Install skill(s) + wire into agent directories
16
+ skillpm uninstall <skill...> Remove skill(s) and clean up
17
+ skillpm list List installed skill packages
18
+ skillpm init Scaffold a new skill package
19
+ skillpm publish [args...] Publish to npmjs.org (wraps npm publish)
20
+ skillpm sync Re-wire agent directories without reinstalling
21
+ skillpm mcp add <source...> Configure MCP server(s) across agents
22
+ skillpm mcp list List configured MCP servers
23
+ skillpm --help Show this help
24
+ skillpm --version Show version
25
+ `;
26
+ async function main() {
27
+ const args = process.argv.slice(2);
28
+ const cwd = process.cwd();
29
+ if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
30
+ console.log(HELP.trim());
31
+ return;
32
+ }
33
+ if (args.includes('--version') || args.includes('-v')) {
34
+ console.log(VERSION);
35
+ return;
36
+ }
37
+ const command = args[0];
38
+ const rest = args.slice(1);
39
+ switch (command) {
40
+ case 'install':
41
+ case 'i':
42
+ await install(rest, cwd);
43
+ break;
44
+ case 'uninstall':
45
+ case 'remove':
46
+ case 'rm':
47
+ await uninstall(rest, cwd);
48
+ break;
49
+ case 'list':
50
+ case 'ls':
51
+ await list(cwd);
52
+ break;
53
+ case 'init':
54
+ await init(cwd);
55
+ break;
56
+ case 'publish':
57
+ await publish(rest, cwd);
58
+ break;
59
+ case 'sync':
60
+ await sync(cwd);
61
+ break;
62
+ case 'mcp':
63
+ if (rest.length === 0) {
64
+ log.error('Usage: skillpm mcp <add|list> [args...]');
65
+ process.exit(1);
66
+ }
67
+ await mcp(rest[0], rest.slice(1), cwd);
68
+ break;
69
+ default:
70
+ log.error(`Unknown command: ${command}`);
71
+ console.log(HELP.trim());
72
+ process.exit(1);
73
+ }
74
+ }
75
+ main().catch((err) => {
76
+ log.error(err.message);
77
+ process.exit(1);
78
+ });
79
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ export declare function init(cwd: string): Promise<void>;
@@ -0,0 +1,51 @@
1
+ import { readFile, writeFile, mkdir } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { npm, log } from '../utils/index.js';
4
+ export async function init(cwd) {
5
+ // Run npm init
6
+ log.info('Initializing package...');
7
+ try {
8
+ await npm(['init', '-y'], { cwd });
9
+ }
10
+ catch (err) {
11
+ const msg = err instanceof Error ? err.message : String(err);
12
+ log.error(`npm init failed: ${msg}`);
13
+ process.exit(1);
14
+ }
15
+ // Read the generated package.json to add "agent-skill" keyword
16
+ const pkgPath = join(cwd, 'package.json');
17
+ const { readPackageJson } = await import('../manifest/index.js');
18
+ const pkg = await readPackageJson(cwd);
19
+ const name = pkg?.name ?? 'my-skill';
20
+ // Add "agent-skill" keyword to package.json
21
+ const rawPkg = JSON.parse(await readFile(pkgPath, 'utf-8'));
22
+ const keywords = rawPkg.keywords ?? [];
23
+ if (!keywords.includes('agent-skill')) {
24
+ keywords.push('agent-skill');
25
+ }
26
+ rawPkg.keywords = keywords;
27
+ await writeFile(pkgPath, JSON.stringify(rawPkg, null, 2) + '\n', 'utf-8');
28
+ // Create skills/<name>/SKILL.md
29
+ const skillName = name.replace(/^@[^/]+\//, ''); // strip scope for dir name
30
+ const skillDir = join(cwd, 'skills', skillName);
31
+ await mkdir(skillDir, { recursive: true });
32
+ const skillMd = `---
33
+ name: ${skillName}
34
+ description: TODO — describe what this skill does and when to use it.
35
+ ---
36
+
37
+ # ${skillName}
38
+
39
+ ## When to use this skill
40
+
41
+ TODO
42
+
43
+ ## Instructions
44
+
45
+ TODO
46
+ `;
47
+ await writeFile(join(skillDir, 'SKILL.md'), skillMd, 'utf-8');
48
+ log.success(`Created skills/${skillName}/SKILL.md`);
49
+ log.success(`Skill package initialized. Edit skills/${skillName}/SKILL.md to define your skill.`);
50
+ }
51
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1,2 @@
1
+ export declare function install(args: string[], cwd: string): Promise<void>;
2
+ export declare function wireSkills(cwd: string): Promise<void>;
@@ -0,0 +1,59 @@
1
+ import { npm, npx, log } from '../utils/index.js';
2
+ import { scanNodeModules, collectMcpServers } from '../scanner/index.js';
3
+ export async function install(args, cwd) {
4
+ // Step 1: npm install
5
+ const npmArgs = ['install', ...args];
6
+ log.info(`Running npm ${npmArgs.join(' ')}`);
7
+ try {
8
+ const result = await npm(npmArgs, { cwd });
9
+ if (result.stderr)
10
+ process.stderr.write(result.stderr);
11
+ if (result.stdout)
12
+ process.stdout.write(result.stdout);
13
+ }
14
+ catch (err) {
15
+ const msg = err instanceof Error ? err.message : String(err);
16
+ log.error(`npm install failed: ${msg}`);
17
+ process.exit(1);
18
+ }
19
+ // Step 2: Scan for skills and wire them
20
+ await wireSkills(cwd);
21
+ }
22
+ export async function wireSkills(cwd) {
23
+ // Scan node_modules/ for SKILL.md packages
24
+ const skills = await scanNodeModules(cwd);
25
+ if (skills.length === 0) {
26
+ log.info('No skill packages found in node_modules/');
27
+ return;
28
+ }
29
+ log.info(`Found ${skills.length} skill package(s)`);
30
+ // Wire each skill into agent directories via skills CLI
31
+ for (const skill of skills) {
32
+ log.info(`Linking ${log.skill(skill.name, skill.version)} into agent directories`);
33
+ try {
34
+ await npx(['skills', 'add', skill.skillDir, '--all', '-y'], { cwd });
35
+ log.success(`Linked ${skill.name}`);
36
+ }
37
+ catch (err) {
38
+ const msg = err instanceof Error ? err.message : String(err);
39
+ log.warn(`Failed to link ${skill.name}: ${msg}`);
40
+ }
41
+ }
42
+ // Collect and configure MCP servers
43
+ const mcpServers = collectMcpServers(skills);
44
+ if (mcpServers.length > 0) {
45
+ log.info(`Configuring ${mcpServers.length} MCP server(s)`);
46
+ for (const server of mcpServers) {
47
+ log.info(`Configuring MCP server: ${server}`);
48
+ try {
49
+ await npx(['add-mcp', server, '-y'], { cwd });
50
+ log.success(`Configured ${server}`);
51
+ }
52
+ catch (err) {
53
+ const msg = err instanceof Error ? err.message : String(err);
54
+ log.warn(`Failed to configure MCP server ${server}: ${msg}`);
55
+ }
56
+ }
57
+ }
58
+ }
59
+ //# sourceMappingURL=install.js.map
@@ -0,0 +1 @@
1
+ export declare function list(cwd: string): Promise<void>;
@@ -0,0 +1,24 @@
1
+ import { scanNodeModules } from '../scanner/index.js';
2
+ import { readSkillMd } from '../manifest/index.js';
3
+ import { log } from '../utils/index.js';
4
+ export async function list(cwd) {
5
+ const skills = await scanNodeModules(cwd);
6
+ if (skills.length === 0) {
7
+ log.info('No skill packages installed');
8
+ return;
9
+ }
10
+ console.log(`\n${skills.length} skill package(s) installed:\n`);
11
+ for (const skill of skills) {
12
+ const frontmatter = await readSkillMd(skill.skillDir);
13
+ const description = frontmatter?.description ?? '';
14
+ console.log(` ${log.skill(skill.name, skill.version)}`);
15
+ if (description) {
16
+ console.log(` ${description}`);
17
+ }
18
+ if (skill.mcpServers.length > 0) {
19
+ console.log(` MCP servers: ${skill.mcpServers.join(', ')}`);
20
+ }
21
+ }
22
+ console.log();
23
+ }
24
+ //# sourceMappingURL=list.js.map
@@ -0,0 +1 @@
1
+ export declare function mcp(subcommand: string, args: string[], cwd: string): Promise<void>;
@@ -0,0 +1,33 @@
1
+ import { npx, log } from '../utils/index.js';
2
+ export async function mcp(subcommand, args, cwd) {
3
+ switch (subcommand) {
4
+ case 'add': {
5
+ if (args.length === 0) {
6
+ log.error('Usage: skillpm mcp add <source>');
7
+ process.exit(1);
8
+ }
9
+ for (const source of args) {
10
+ log.info(`Configuring MCP server: ${source}`);
11
+ try {
12
+ await npx(['add-mcp', source, '-y'], { cwd });
13
+ log.success(`Configured ${source}`);
14
+ }
15
+ catch (err) {
16
+ const msg = err instanceof Error ? err.message : String(err);
17
+ log.error(`Failed to configure MCP server ${source}: ${msg}`);
18
+ }
19
+ }
20
+ break;
21
+ }
22
+ case 'list': {
23
+ log.info('Listing MCP servers is not yet supported by add-mcp.');
24
+ log.info('Check your agent config files directly (.cursor/mcp.json, etc.)');
25
+ break;
26
+ }
27
+ default:
28
+ log.error(`Unknown mcp subcommand: ${subcommand}`);
29
+ log.error('Usage: skillpm mcp <add|list> [args...]');
30
+ process.exit(1);
31
+ }
32
+ }
33
+ //# sourceMappingURL=mcp.js.map
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Validate a skill package is ready to publish.
3
+ * Returns an array of error messages (empty = valid).
4
+ */
5
+ export declare function validatePublish(cwd: string): Promise<string[]>;
6
+ export declare function publish(args: string[], cwd: string): Promise<void>;
@@ -0,0 +1,79 @@
1
+ import { readdir, access } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { npm, npx, log } from '../utils/index.js';
4
+ import { readPackageJson } from '../manifest/index.js';
5
+ /**
6
+ * Validate a skill package is ready to publish.
7
+ * Returns an array of error messages (empty = valid).
8
+ */
9
+ export async function validatePublish(cwd) {
10
+ const errors = [];
11
+ // 1. Check package.json exists and has required fields
12
+ const pkg = await readPackageJson(cwd);
13
+ if (!pkg) {
14
+ return ['No package.json found. Run "skillpm init" or "npm init" first.'];
15
+ }
16
+ if (!pkg.keywords || !pkg.keywords.includes('agent-skill')) {
17
+ errors.push('package.json must include "agent-skill" in keywords. Add it manually or run "skillpm init".');
18
+ }
19
+ // 2. Find skills/<name>/SKILL.md
20
+ const skillsDir = join(cwd, 'skills');
21
+ let skillSubdirs;
22
+ try {
23
+ skillSubdirs = await readdir(skillsDir);
24
+ }
25
+ catch {
26
+ errors.push('No skills/ directory found. Create skills/<name>/SKILL.md with your skill definition.');
27
+ return errors;
28
+ }
29
+ let foundSkillDir = null;
30
+ for (const sub of skillSubdirs) {
31
+ const candidate = join(skillsDir, sub);
32
+ try {
33
+ await access(join(candidate, 'SKILL.md'));
34
+ foundSkillDir = candidate;
35
+ break;
36
+ }
37
+ catch {
38
+ continue;
39
+ }
40
+ }
41
+ if (!foundSkillDir) {
42
+ errors.push('No SKILL.md found in skills/ subdirectories. Create skills/<name>/SKILL.md.');
43
+ return errors;
44
+ }
45
+ // 3. Validate against the Agent Skills spec via skills-ref
46
+ try {
47
+ await npx(['skills-ref', 'validate', foundSkillDir]);
48
+ }
49
+ catch (err) {
50
+ const msg = err instanceof Error ? err.message : String(err);
51
+ errors.push(`SKILL.md spec validation failed: ${msg}`);
52
+ }
53
+ return errors;
54
+ }
55
+ export async function publish(args, cwd) {
56
+ const errors = await validatePublish(cwd);
57
+ if (errors.length > 0) {
58
+ log.error('Publish validation failed:');
59
+ for (const err of errors) {
60
+ log.error(` • ${err}`);
61
+ }
62
+ process.exit(1);
63
+ }
64
+ log.info('Publishing to npmjs.org...');
65
+ try {
66
+ const result = await npm(['publish', ...args], { cwd });
67
+ if (result.stderr)
68
+ process.stderr.write(result.stderr);
69
+ if (result.stdout)
70
+ process.stdout.write(result.stdout);
71
+ log.success('Published');
72
+ }
73
+ catch (err) {
74
+ const msg = err instanceof Error ? err.message : String(err);
75
+ log.error(`npm publish failed: ${msg}`);
76
+ process.exit(1);
77
+ }
78
+ }
79
+ //# sourceMappingURL=publish.js.map
@@ -0,0 +1 @@
1
+ export declare function sync(cwd: string): Promise<void>;
@@ -0,0 +1,8 @@
1
+ import { log } from '../utils/index.js';
2
+ import { wireSkills } from './install.js';
3
+ export async function sync(cwd) {
4
+ log.info('Re-scanning and re-wiring agent directories...');
5
+ await wireSkills(cwd);
6
+ log.success('Sync complete');
7
+ }
8
+ //# sourceMappingURL=sync.js.map
@@ -0,0 +1 @@
1
+ export declare function uninstall(args: string[], cwd: string): Promise<void>;
@@ -0,0 +1,24 @@
1
+ import { npm, log } from '../utils/index.js';
2
+ import { wireSkills } from './install.js';
3
+ export async function uninstall(args, cwd) {
4
+ if (args.length === 0) {
5
+ log.error('Usage: skillpm uninstall <skill> [skill...]');
6
+ process.exit(1);
7
+ }
8
+ log.info(`Running npm uninstall ${args.join(' ')}`);
9
+ try {
10
+ const result = await npm(['uninstall', ...args], { cwd });
11
+ if (result.stderr)
12
+ process.stderr.write(result.stderr);
13
+ if (result.stdout)
14
+ process.stdout.write(result.stdout);
15
+ }
16
+ catch (err) {
17
+ const msg = err instanceof Error ? err.message : String(err);
18
+ log.error(`npm uninstall failed: ${msg}`);
19
+ process.exit(1);
20
+ }
21
+ // Re-wire to clean up stale symlinks
22
+ await wireSkills(cwd);
23
+ }
24
+ //# sourceMappingURL=uninstall.js.map
@@ -0,0 +1,12 @@
1
+ import type { SkillPackageJson, SkillpmField } from './schema.js';
2
+ export declare function readPackageJson(pkgDir: string): Promise<SkillPackageJson | null>;
3
+ export declare function parseSkillpmField(pkg: SkillPackageJson): SkillpmField;
4
+ export interface SkillMdFrontmatter {
5
+ name: string;
6
+ description: string;
7
+ license?: string;
8
+ compatibility?: string;
9
+ metadata?: Record<string, unknown>;
10
+ 'allowed-tools'?: string;
11
+ }
12
+ export declare function readSkillMd(skillDir: string): Promise<SkillMdFrontmatter | null>;
@@ -0,0 +1,31 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import matter from 'gray-matter';
4
+ import { SkillpmFieldSchema } from './schema.js';
5
+ export async function readPackageJson(pkgDir) {
6
+ try {
7
+ const raw = await readFile(join(pkgDir, 'package.json'), 'utf-8');
8
+ return JSON.parse(raw);
9
+ }
10
+ catch {
11
+ return null;
12
+ }
13
+ }
14
+ export function parseSkillpmField(pkg) {
15
+ const result = SkillpmFieldSchema.safeParse(pkg.skillpm);
16
+ if (!result.success) {
17
+ throw new Error(`Invalid skillpm field in ${pkg.name}: ${result.error.message}`);
18
+ }
19
+ return result.data;
20
+ }
21
+ export async function readSkillMd(skillDir) {
22
+ try {
23
+ const raw = await readFile(join(skillDir, 'SKILL.md'), 'utf-8');
24
+ const { data } = matter(raw);
25
+ return data;
26
+ }
27
+ catch {
28
+ return null;
29
+ }
30
+ }
31
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,20 @@
1
+ import { z } from 'zod';
2
+ export declare const SkillpmFieldSchema: z.ZodOptional<z.ZodObject<{
3
+ mcpServers: z.ZodOptional<z.ZodArray<z.ZodString>>;
4
+ }, z.core.$strict>>;
5
+ export type SkillpmField = z.infer<typeof SkillpmFieldSchema>;
6
+ export interface SkillPackageJson {
7
+ name: string;
8
+ version: string;
9
+ keywords?: string[];
10
+ dependencies?: Record<string, string>;
11
+ skillpm?: SkillpmField;
12
+ }
13
+ export interface SkillInfo {
14
+ name: string;
15
+ version: string;
16
+ path: string;
17
+ /** Path to the skills/<name>/ subdirectory containing SKILL.md */
18
+ skillDir: string;
19
+ mcpServers: string[];
20
+ }
@@ -0,0 +1,8 @@
1
+ import { z } from 'zod';
2
+ export const SkillpmFieldSchema = z
3
+ .object({
4
+ mcpServers: z.array(z.string()).optional(),
5
+ })
6
+ .strict()
7
+ .optional();
8
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1,11 @@
1
+ import type { SkillInfo } from '../manifest/schema.js';
2
+ /**
3
+ * Scan node_modules/ for packages that contain a skills/<name>/SKILL.md file.
4
+ * Returns metadata for each discovered skill package.
5
+ */
6
+ export declare function scanNodeModules(cwd: string): Promise<SkillInfo[]>;
7
+ /**
8
+ * Collect all MCP server requirements from all discovered skills (transitive).
9
+ * Deduplicates entries.
10
+ */
11
+ export declare function collectMcpServers(skills: SkillInfo[]): string[];
@@ -0,0 +1,92 @@
1
+ import { readdir, access } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { readPackageJson, parseSkillpmField } from '../manifest/index.js';
4
+ /**
5
+ * Scan node_modules/ for packages that contain a skills/<name>/SKILL.md file.
6
+ * Returns metadata for each discovered skill package.
7
+ */
8
+ export async function scanNodeModules(cwd) {
9
+ const nodeModulesDir = join(cwd, 'node_modules');
10
+ const skills = [];
11
+ let entries;
12
+ try {
13
+ entries = await readdir(nodeModulesDir);
14
+ }
15
+ catch {
16
+ return skills;
17
+ }
18
+ for (const entry of entries) {
19
+ if (entry.startsWith('.'))
20
+ continue;
21
+ if (entry.startsWith('@')) {
22
+ // Scoped package — scan one level deeper
23
+ const scopeDir = join(nodeModulesDir, entry);
24
+ let scopedEntries;
25
+ try {
26
+ scopedEntries = await readdir(scopeDir);
27
+ }
28
+ catch {
29
+ continue;
30
+ }
31
+ for (const scopedEntry of scopedEntries) {
32
+ const pkgDir = join(scopeDir, scopedEntry);
33
+ const skill = await tryReadSkill(pkgDir);
34
+ if (skill)
35
+ skills.push(skill);
36
+ }
37
+ }
38
+ else {
39
+ const pkgDir = join(nodeModulesDir, entry);
40
+ const skill = await tryReadSkill(pkgDir);
41
+ if (skill)
42
+ skills.push(skill);
43
+ }
44
+ }
45
+ return skills;
46
+ }
47
+ async function tryReadSkill(pkgDir) {
48
+ const pkg = await readPackageJson(pkgDir);
49
+ if (!pkg)
50
+ return null;
51
+ // Look for skills/*/SKILL.md
52
+ const skillsDir = join(pkgDir, 'skills');
53
+ let skillSubdirs;
54
+ try {
55
+ skillSubdirs = await readdir(skillsDir);
56
+ }
57
+ catch {
58
+ return null;
59
+ }
60
+ for (const sub of skillSubdirs) {
61
+ const skillDir = join(skillsDir, sub);
62
+ try {
63
+ await access(join(skillDir, 'SKILL.md'));
64
+ }
65
+ catch {
66
+ continue;
67
+ }
68
+ const skillpm = parseSkillpmField(pkg);
69
+ return {
70
+ name: pkg.name,
71
+ version: pkg.version,
72
+ path: pkgDir,
73
+ skillDir,
74
+ mcpServers: skillpm?.mcpServers ?? [],
75
+ };
76
+ }
77
+ return null;
78
+ }
79
+ /**
80
+ * Collect all MCP server requirements from all discovered skills (transitive).
81
+ * Deduplicates entries.
82
+ */
83
+ export function collectMcpServers(skills) {
84
+ const servers = new Set();
85
+ for (const skill of skills) {
86
+ for (const server of skill.mcpServers) {
87
+ servers.add(server);
88
+ }
89
+ }
90
+ return [...servers];
91
+ }
92
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,13 @@
1
+ export interface ExecResult {
2
+ stdout: string;
3
+ stderr: string;
4
+ }
5
+ export declare function run(command: string, args: string[], opts?: {
6
+ cwd?: string;
7
+ }): Promise<ExecResult>;
8
+ export declare function npm(args: string[], opts?: {
9
+ cwd?: string;
10
+ }): Promise<ExecResult>;
11
+ export declare function npx(args: string[], opts?: {
12
+ cwd?: string;
13
+ }): Promise<ExecResult>;
@@ -0,0 +1,17 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { promisify } from 'node:util';
3
+ const execFileAsync = promisify(execFile);
4
+ export async function run(command, args, opts) {
5
+ const { stdout, stderr } = await execFileAsync(command, args, {
6
+ cwd: opts?.cwd,
7
+ maxBuffer: 10 * 1024 * 1024,
8
+ });
9
+ return { stdout, stderr };
10
+ }
11
+ export async function npm(args, opts) {
12
+ return run('npm', args, opts);
13
+ }
14
+ export async function npx(args, opts) {
15
+ return run('npx', ['--yes', ...args], opts);
16
+ }
17
+ //# sourceMappingURL=exec.js.map
@@ -0,0 +1,2 @@
1
+ export { run, npm, npx } from './exec.js';
2
+ export * as log from './log.js';
@@ -0,0 +1,3 @@
1
+ export { run, npm, npx } from './exec.js';
2
+ export * as log from './log.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,5 @@
1
+ export declare function info(msg: string): void;
2
+ export declare function success(msg: string): void;
3
+ export declare function warn(msg: string): void;
4
+ export declare function error(msg: string): void;
5
+ export declare function skill(name: string, version?: string): string;
@@ -0,0 +1,17 @@
1
+ import pc from 'picocolors';
2
+ export function info(msg) {
3
+ console.log(pc.blue('ℹ'), msg);
4
+ }
5
+ export function success(msg) {
6
+ console.log(pc.green('✓'), msg);
7
+ }
8
+ export function warn(msg) {
9
+ console.log(pc.yellow('⚠'), msg);
10
+ }
11
+ export function error(msg) {
12
+ console.error(pc.red('✗'), msg);
13
+ }
14
+ export function skill(name, version) {
15
+ return version ? `${pc.bold(name)}@${pc.dim(version)}` : pc.bold(name);
16
+ }
17
+ //# sourceMappingURL=log.js.map
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "skillpm",
3
+ "version": "0.0.1",
4
+ "description": "npm for Agent Skills",
5
+ "type": "module",
6
+ "bin": {
7
+ "skillpm": "./dist/cli.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "dev": "tsc --watch",
12
+ "test": "vitest run",
13
+ "test:watch": "vitest",
14
+ "lint": "eslint src/",
15
+ "format": "prettier --write 'src/**/*.ts'",
16
+ "prepublishOnly": "npm run lint && npm run build && npm test"
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "!dist/**/*.test.*",
21
+ "!dist/**/*.map",
22
+ "README.md",
23
+ "LICENSE"
24
+ ],
25
+ "keywords": [
26
+ "agent-skills",
27
+ "skills",
28
+ "ai-agents",
29
+ "cli",
30
+ "npm"
31
+ ],
32
+ "homepage": "https://skillpm.dev",
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "git+https://github.com/sbroenne/skillpm.git"
36
+ },
37
+ "bugs": {
38
+ "url": "https://github.com/sbroenne/skillpm/issues"
39
+ },
40
+ "author": "sbroenne",
41
+ "license": "MIT",
42
+ "workspaces": [
43
+ "packages/*"
44
+ ],
45
+ "engines": {
46
+ "node": ">=18"
47
+ },
48
+ "dependencies": {
49
+ "gray-matter": "^4.0.3",
50
+ "picocolors": "^1.1.1",
51
+ "zod": "^4.3.6"
52
+ },
53
+ "devDependencies": {
54
+ "@eslint/js": "^10.0.1",
55
+ "@types/node": "^25.3.0",
56
+ "eslint": "^10.0.2",
57
+ "prettier": "^3.8.1",
58
+ "typescript": "^5.9.3",
59
+ "typescript-eslint": "^8.56.1",
60
+ "vitest": "^4.0.18"
61
+ }
62
+ }