Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
- `packages/skills-package-manager`: core library and `spm` CLI.
- `packages/pnpm-plugin-skills`: pnpm plugin that syncs skills during install.
- `website`: Rspress documentation site.
- Root files such as `skills.json` and `skills-lock.yaml` document example skill manifests and lock state.
- Root files such as `skills.json` document example skill manifests and pinned skill sources.

## Documentation Structure

Expand All @@ -19,7 +19,7 @@ Before you start your PR, check if you need to update the documents. The documen
- **Architecture** (`architecture/`):
- `how-it-works.mdx` - High-level system overview.
- `cli-commands.mdx` - CLI command implementation details.
- `manifest-and-lockfile.mdx` - Manifest and lockfile formats.
- `manifest.mdx` - Manifest format and pinned specifier behavior.
- `pnpm-plugin.mdx` - pnpm plugin integration details.
- **Public Assets** (`public/`): Logos and favicon files.

Expand All @@ -36,18 +36,18 @@ Before you start your PR, check if you need to update the documents. The documen
- Use TypeScript and follow the existing module style in each package.
- Match existing formatting, naming, and file organization before introducing new patterns.
- Keep changes focused; avoid adding abstractions, comments, or files unless they are necessary.
- Preserve manifest, lockfile, and CLI terminology consistently across code and docs.
- Preserve manifest, specifier, and CLI terminology consistently across code and docs.

## Testing Expectations

- Add or update tests when changing CLI behavior, specifier parsing, install flow, or lockfile behavior.
- Add or update tests when changing CLI behavior, specifier parsing, install flow, or install state behavior.
- Prefer targeted tests under `packages/*/test`, following the existing `@rstest/core` style.
- Run `pnpm test` before opening a pull request.
- For docs-only changes, build the site with `pnpm build:website` if the change affects routing, components, or MDX structure.

## Commit and Pull Request Guidelines

- Use clear, conventional commit messages such as `feat: add file specifier validation` or `fix: preserve lockfile resolution`.
- Use clear, conventional commit messages such as `feat: add file specifier validation` or `fix: preserve install state`.
- Keep pull requests scoped to one change or theme.
- Include a brief summary, testing notes, and screenshots for docs/UI changes when relevant.
- Link related issues or context, and note any manifest or lockfile changes explicitly.
- Link related issues or context, and note any manifest or install behavior changes explicitly.
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

<p>
<strong>The Next-Gen Package Manager for <a href="https://skills-package-manager.site">Agent Skills</a></strong><br>
Manage, install, and link SKILL.md-based skills with lockfile-driven reproducibility.
Manage, install, and link SKILL.md-based skills from a single `skills.json` manifest.
</p>

<p>
Expand Down Expand Up @@ -40,7 +40,7 @@

## ✨ Features

- 🔒 **Lockfile-Driven Versioning** — Ditch heavy git commits. `skills-lock.yaml` ensures every team member runs on identical skill versions.
- 📌 **Single-File Pins** — Keep exact git commits and npm versions directly in `skills.json`.
- 🌐 **Any Source, Any Skill** — Mix local `link:`, versioned `npm:`, or direct `git:` repos with ease—even sub-folders within `.tgz` archives.
- 🚀 **npx skills compatible** — A seamless, drop-in replacement. Swap `npx skills` for `npx skills-package-manager` and unlock more power.
- 🔌 **Native pnpm Integration** — The `pnpm-plugin-skills` hooks directly into your install lifecycle for zero-effort synchronization.
Expand All @@ -64,6 +64,10 @@ npx skills-package-manager add rstackjs/agent-skills

# 🎯 Direct — specify skill by name
npx skills-package-manager add rstackjs/agent-skills --skill pr-creator
npx skills-package-manager add rstackjs/agent-skills -s pr-creator -s rspress-custom-theme

# 🔎 List without installing
npx skills-package-manager add rstackjs/agent-skills --list

# 📁 Local skill directory
npx skills-package-manager add link:./my-skills/my-skill
Expand All @@ -75,14 +79,11 @@ npx skills-package-manager add link:./my-skills/my-skill
npx skills-package-manager install
```

> 💡 **Tip:** Use `--frozen-lockfile` in CI/CD to ensure reproducible installs without modifying the lockfile.

## 🏗️ How It Works

SPM uses two simple files to manage your agent's capabilities:
SPM uses one manifest file to manage your agent's capabilities:

1. **`skills.json` (The Manifest)**: The single source of truth where you declare your requirements across any protocol.
2. **`skills-lock.yaml` (The Lockfile)**: Deterministically locks every dependency to ensure every installation is identical.
1. **`skills.json`**: The single source of truth where you declare pinned skill specifiers, `installDir`, and `linkTargets`.

## 📚 Documentation

Expand Down
6 changes: 3 additions & 3 deletions packages/pnpm-plugin-skills/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ A pnpm plugin that automatically installs agent skills during `pnpm install`.
This plugin hooks into pnpm's `preResolution` lifecycle to run skill installation before dependency resolution. On every `pnpm install`, it:

1. Reads `skills.json` from the workspace root
2. Resolves and syncs `skills-lock.yaml`
2. Resolves the manifest into an in-memory installation plan
3. Materializes skills into the configured `installDir`
4. Creates symlinks for configured `linkTargets`
5. Skips if the lockfile hasn't changed (fast path)
5. Updates the internal install state for future incremental runs

## Setup

Expand All @@ -27,7 +27,7 @@ Then create a `skills.json` in your project root:
"installDir": ".agents/skills",
"linkTargets": [".claude/skills"],
"skills": {
"my-skill": "https://github.com/owner/repo.git#path:/skills/my-skill"
"my-skill": "github:owner/repo#abc1234&path:/skills/my-skill"
}
}
```
Expand Down
10 changes: 5 additions & 5 deletions packages/pnpm-plugin-skills/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@ import { installCommand } from 'skills-package-manager'
export async function preResolution(
options: { lockfileDir?: string; workspaceRoot?: string } = {},
) {
const lockfileDir = options.lockfileDir
if (!lockfileDir) {
const projectRoot = options.lockfileDir
if (!projectRoot) {
return undefined
}

await installCommand({ cwd: lockfileDir })
await installCommand({ cwd: projectRoot })
return undefined
}

export function afterAllResolved(
lockfile: Record<string, unknown>,
pnpmLockfile: Record<string, unknown>,
_context: { log?: (message: string) => void } = {},
) {
return lockfile
return pnpmLockfile
}
21 changes: 3 additions & 18 deletions packages/pnpm-plugin-skills/test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ async function loadPreResolution() {
}

describe('preResolution', () => {
it('installs skills from workspace root when manifest and lock exist', async () => {
it('installs skills from workspace root when only skills.json exists', async () => {
const root = mkdtempSync(path.join(tmpdir(), 'pnpm-plugin-skills-'))
mkdirSync(path.join(root, 'skills-source/skills/hello-skill'), { recursive: true })
writeFileSync(
Expand All @@ -48,23 +48,6 @@ describe('preResolution', () => {
2,
),
)
writeFileSync(
path.join(root, 'skills-lock.yaml'),
[
"lockfileVersion: '0.1'",
'installDir: .agents/skills',
'linkTargets:',
' - .claude/skills',
'skills:',
' hello-skill:',
' specifier: link:./skills-source/skills/hello-skill',
' resolution:',
' type: link',
` path: ${JSON.stringify(path.join(root, 'skills-source/skills/hello-skill'))}`,
' digest: test-digest',
].join('\n'),
)

const preResolution = await loadPreResolution()

const result = await preResolution({
Expand All @@ -78,6 +61,8 @@ describe('preResolution', () => {
'Hello from plugin',
)
expect(existsSync(path.join(root, '.claude/skills/hello-skill'))).toBe(true)
expect(existsSync(path.join(root, 'skills-lock.yaml'))).toBe(false)
expect(existsSync(path.join(root, '.agents/skills/lock.yaml'))).toBe(false)
})
})

Expand Down
50 changes: 30 additions & 20 deletions packages/skills-package-manager/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ For one-off usage, `npx skills-package-manager add ...` is the low-friction migr
```bash
npx skills-package-manager --help
npx skills-package-manager --version
npx skills-package-manager add <specifier> [--skill <name>]
npx skills-package-manager add <specifier> [--skill <name>...]
npx skills-package-manager install
npx skills-package-manager patch <skill>
npx skills-package-manager patch-commit <edit-dir>
Expand Down Expand Up @@ -37,26 +37,37 @@ npx skills-package-manager add owner/repo
# Interactive — clone repo, discover skills, select via multiselect prompt
npx skills-package-manager add owner/repo
npx skills-package-manager add https://github.com/owner/repo
npx skills-package-manager add https://gitlab.com/org/repo
npx skills-package-manager add git@github.com:owner/repo.git
npx skills-package-manager add ./my-local-skills

# Non-interactive — add a specific skill by name
# Non-interactive — add one or more specific skills by name
npx skills-package-manager add owner/repo --skill find-skills
npx skills-package-manager add owner/repo -s frontend-design -s skill-creator
npx skills-package-manager add owner/repo@find-skills
npx skills-package-manager add owner/repo#main@find-skills

# Direct repo subpath
npx skills-package-manager add owner/repo/skills/my-skill
npx skills-package-manager add https://github.com/owner/repo/tree/main/skills/my-skill#main
npx skills-package-manager add https://github.com/owner/repo/tree/main/skills/my-skill

# Inspect or target agents with skills CLI-compatible flags
npx skills-package-manager add owner/repo --list
npx skills-package-manager add owner/repo --all
npx skills-package-manager add owner/repo -a claude-code -a opencode

# Direct specifier — skip discovery
npx skills-package-manager add https://github.com/owner/repo.git#path:/skills/my-skill
npx skills-package-manager add github:owner/repo#abc1234&path:/skills/my-skill
npx skills-package-manager add link:./local-source/skills/my-skill
npx skills-package-manager add local:./.agents/skills/my-skill
npx skills-package-manager add local:*
npx skills-package-manager add ./local-source
npx skills-package-manager add file:./skills-package.tgz#path:/skills/my-skill
npx skills-package-manager add npm:@scope/skills-package#path:/skills/my-skill
npx skills-package-manager add file:./skills-package.tgz&path:/skills/my-skill
npx skills-package-manager add npm:@scope/skills-package@1.0.0&path:/skills/my-skill
Comment on lines +60 to +65
```

After `npx skills-package-manager add`, the newly added skills are resolved, installed or registered according to their protocol, and linked to each configured `linkTarget` immediately.
GitHub sources are written back to `skills.json` as pinned `github:owner/repo#<commit>&path:<path>` specifiers.
The `--copy` flag is accepted for `npx skills add` command-line compatibility; SPM still keeps one canonical install directory and links configured agent targets from there.

#### How it works

Expand All @@ -65,7 +76,7 @@ When given `owner/repo` or a GitHub URL:
1. Shallow-clones the repository into a temp directory
2. Scans for `SKILL.md` files (checks root, then `skills/`, `.agents/skills/`, etc.)
3. Presents an interactive multiselect prompt (powered by [@clack/prompts](https://github.com/bombshell-dev/clack))
4. Writes selected skills to `skills.json` and resolves `skills-lock.yaml`
4. Writes selected, pinned skill specifiers to `skills.json`
5. Cleans up the temp directory

### `npx skills-package-manager init`
Expand Down Expand Up @@ -106,7 +117,7 @@ npx skills-package-manager install
```

This resolves each skill from its specifier, installs managed skills into `installDir` (default `.agents/skills/`), registers `local:` skills in place, and creates symlinks for each `linkTarget`.
When `selfSkill` is `true`, `npx skills-package-manager install` also installs the bundled `skills-package-manager-cli` skill so users get guidance for `skills.json`, `skills-lock.yaml`, and `npx skills-package-manager` commands. This helper skill is not written to `skills-lock.yaml`.
When `selfSkill` is `true`, `npx skills-package-manager install` also installs the bundled `skills-package-manager-cli` skill so users get guidance for `skills.json` and `npx skills-package-manager` commands. This helper skill is injected at install time and is not written to `skills.json`.
If `patchedSkills` contains an entry for a managed skill, the corresponding patch file is applied after the skill is materialized. `local:` skills cannot be patched because their source directories are user-owned.

### `npx skills-package-manager patch`
Expand All @@ -120,7 +131,7 @@ npx skills-package-manager patch hello-skill --edit-dir ./tmp/hello-skill

Behavior:

- Resolves the currently locked content for the target skill
- Resolves the current manifest content for the target skill
- Extracts an editable copy into a temporary directory by default
- Reapplies any committed patch for that skill unless `--ignore-existing` is passed
- Writes patch edit metadata so `patch-commit` can generate a new patch file later
Expand All @@ -139,12 +150,11 @@ Behavior:
- Compares the edited directory with the original resolved skill content
- Writes a unified diff patch file to `patches/<skill>.patch` by default
- Updates `skills.json` through the `patchedSkills` field
- Updates `skills-lock.yaml` with patch path and digest metadata
- Reinstalls and relinks the patched skill so the working tree reflects the committed patch

### `npx skills-package-manager update`

Refresh resolvable skills declared in `skills.json` without changing the manifest:
Refresh resolvable skills declared in `skills.json` and write the updated pins back to the manifest:

```bash
npx skills-package-manager update
Expand All @@ -154,10 +164,10 @@ npx skills-package-manager update find-skills rspress-custom-theme
Behavior:

- Uses `skills.json` as the source of truth
- Re-resolves git refs and npm package targets
- Skips local `link:` and `local:` skills, including the bundled self skill
- Updates git skills to the latest `main` commit and npm skills to the registry `latest` version
- Skips local `link:`, `local:`, and `file:` skills
- Fails immediately for unknown skill names
- Writes `skills-lock.yaml` only after fetch and link succeed
- Writes `skills.json` only after the updated install succeeds

## Programmatic API

Expand All @@ -182,14 +192,15 @@ const skills = await listRepoSkills('vercel-labs', 'skills')
## Specifier Format

```text
git/file/npm: <source>#[ref&]path:<skill-path>
git/file/npm: <source>[#ref][&path:<skill-path>]
link: link:<path-to-skill-dir>
local: local:<path-to-existing-skill-dir>
local shorthand: local:*
```

| Part | Description | Example |
|------|-------------|---------|
| `source` | Git URL, direct `link:` or `local:` skill path, `file:` tarball, or `npm:` package name | `https://github.com/o/r.git`, `link:./local/skills/my-skill`, `local:./.agents/skills/my-skill`, `file:./skills.tgz`, `npm:@scope/pkg` |
| `source` | Git URL or `github:` shorthand, direct `link:` or `local:` skill path, `file:` tarball, or `npm:` package name | `github:o/r`, `https://github.com/o/r.git`, `link:./local/skills/my-skill`, `local:*`, `file:./skills.tgz`, `npm:@scope/pkg@1.0.0` |
| `ref` | Optional git ref | `main`, `v1.0.0`, `HEAD`, `6cb0992`, `6cb0992a176f2ca142e19f64dca8ac12025b035e` |
| `path` | Path to skill directory within source | `/skills/my-skill` |

Expand All @@ -201,7 +212,7 @@ local: local:<path-to-existing-skill-dir>
- **`link`** — Symlinks a local skill directory into `installDir`
- **`local`** — Uses an existing user-owned skill directory in place
- **`file`** — Extracts a local `tgz` package and copies the selected skill
- **`npm`** — Resolves a package from the configured npm registry, locks the tarball URL/version/integrity, and installs from the downloaded tarball
- **`npm`** — Resolves a package from the configured npm registry and installs from the downloaded tarball

`npm:` reads `registry` and scoped `@scope:registry` values from `.npmrc`. Matching `:_authToken`, `:_auth`, or `username` + `:_password` entries are also used for private registry requests.

Expand All @@ -212,7 +223,7 @@ src/
├── bin/ # CLI entry points
├── cli/ # CLI runner and interactive prompts
├── commands/ # add, install, patch command implementations
├── config/ # skills.json / skills-lock.yaml read/write
├── config/ # skills.json read/write and in-memory install plan resolution
├── github/ # Git clone + skill discovery (listSkills)
├── install/ # Skill materialization, linking, pruning
├── patches/ # Patch edit state, diff generation, patch application
Expand All @@ -231,4 +242,3 @@ pnpm build # Builds with Rslib (ESM output + DTS)
```bash
pnpm test # Runs tests with Rstest
```
``
Loading
Loading