skills-package-manager 0.9.0 → 0.11.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 CHANGED
@@ -9,7 +9,7 @@ For one-off usage, `npx skills-package-manager add ...` is the low-friction migr
9
9
  ```bash
10
10
  npx skills-package-manager --help
11
11
  npx skills-package-manager --version
12
- npx skills-package-manager add <specifier> [--skill <name>]
12
+ npx skills-package-manager add <specifier> [--skill <name>...]
13
13
  npx skills-package-manager install
14
14
  npx skills-package-manager patch <skill>
15
15
  npx skills-package-manager patch-commit <edit-dir>
@@ -37,25 +37,37 @@ npx skills-package-manager add owner/repo
37
37
  # Interactive — clone repo, discover skills, select via multiselect prompt
38
38
  npx skills-package-manager add owner/repo
39
39
  npx skills-package-manager add https://github.com/owner/repo
40
+ npx skills-package-manager add https://gitlab.com/org/repo
41
+ npx skills-package-manager add git@github.com:owner/repo.git
42
+ npx skills-package-manager add ./my-local-skills
40
43
 
41
- # Non-interactive — add a specific skill by name
44
+ # Non-interactive — add one or more specific skills by name
42
45
  npx skills-package-manager add owner/repo --skill find-skills
46
+ npx skills-package-manager add owner/repo -s frontend-design -s skill-creator
43
47
  npx skills-package-manager add owner/repo@find-skills
44
48
  npx skills-package-manager add owner/repo#main@find-skills
45
49
 
46
50
  # Direct repo subpath
47
51
  npx skills-package-manager add owner/repo/skills/my-skill
48
- npx skills-package-manager add https://github.com/owner/repo/tree/main/skills/my-skill#main
52
+ npx skills-package-manager add https://github.com/owner/repo/tree/main/skills/my-skill
53
+
54
+ # Inspect or target agents with skills CLI-compatible flags
55
+ npx skills-package-manager add owner/repo --list
56
+ npx skills-package-manager add owner/repo --all
57
+ npx skills-package-manager add owner/repo -a claude-code -a opencode
49
58
 
50
59
  # Direct specifier — skip discovery
51
- npx skills-package-manager add https://github.com/owner/repo.git#path:/skills/my-skill
60
+ npx skills-package-manager add 'github:owner/repo#abc1234&path:/skills/my-skill'
52
61
  npx skills-package-manager add link:./local-source/skills/my-skill
62
+ npx skills-package-manager add 'local:*' --skill my-skill
53
63
  npx skills-package-manager add ./local-source
54
- npx skills-package-manager add file:./skills-package.tgz#path:/skills/my-skill
55
- npx skills-package-manager add npm:@scope/skills-package#path:/skills/my-skill
64
+ npx skills-package-manager add 'file:./skills-package.tgz&path:/skills/my-skill'
65
+ npx skills-package-manager add 'npm:@scope/skills-package@1.0.0&path:/skills/my-skill'
56
66
  ```
57
67
 
58
- After `npx skills-package-manager add`, the newly added skills are resolved, materialized into `installDir`, and linked to each configured `linkTarget` immediately.
68
+ 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.
69
+ GitHub sources are written back to `skills.json` as pinned `github:owner/repo#<commit>&path:<path>` specifiers.
70
+ 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.
59
71
 
60
72
  #### How it works
61
73
 
@@ -64,7 +76,7 @@ When given `owner/repo` or a GitHub URL:
64
76
  1. Shallow-clones the repository into a temp directory
65
77
  2. Scans for `SKILL.md` files (checks root, then `skills/`, `.agents/skills/`, etc.)
66
78
  3. Presents an interactive multiselect prompt (powered by [@clack/prompts](https://github.com/bombshell-dev/clack))
67
- 4. Writes selected skills to `skills.json` and resolves `skills-lock.yaml`
79
+ 4. Writes selected, pinned skill specifiers to `skills.json`
68
80
  5. Cleans up the temp directory
69
81
 
70
82
  ### `npx skills-package-manager init`
@@ -104,9 +116,9 @@ Install all skills declared in `skills.json`:
104
116
  npx skills-package-manager install
105
117
  ```
106
118
 
107
- This resolves each skill from its specifier, materializes it into `installDir` (default `.agents/skills/`), and creates symlinks for each `linkTarget`.
108
- 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`.
109
- If `patchedSkills` contains an entry for a skill, the corresponding patch file is applied after the skill is materialized.
119
+ 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`.
120
+ 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`.
121
+ 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.
110
122
 
111
123
  ### `npx skills-package-manager patch`
112
124
 
@@ -119,7 +131,7 @@ npx skills-package-manager patch hello-skill --edit-dir ./tmp/hello-skill
119
131
 
120
132
  Behavior:
121
133
 
122
- - Resolves the currently locked content for the target skill
134
+ - Resolves the current manifest content for the target skill
123
135
  - Extracts an editable copy into a temporary directory by default
124
136
  - Reapplies any committed patch for that skill unless `--ignore-existing` is passed
125
137
  - Writes patch edit metadata so `patch-commit` can generate a new patch file later
@@ -138,12 +150,11 @@ Behavior:
138
150
  - Compares the edited directory with the original resolved skill content
139
151
  - Writes a unified diff patch file to `patches/<skill>.patch` by default
140
152
  - Updates `skills.json` through the `patchedSkills` field
141
- - Updates `skills-lock.yaml` with patch path and digest metadata
142
153
  - Reinstalls and relinks the patched skill so the working tree reflects the committed patch
143
154
 
144
155
  ### `npx skills-package-manager update`
145
156
 
146
- Refresh resolvable skills declared in `skills.json` without changing the manifest:
157
+ Refresh resolvable skills declared in `skills.json` and write the updated pins back to the manifest:
147
158
 
148
159
  ```bash
149
160
  npx skills-package-manager update
@@ -153,10 +164,10 @@ npx skills-package-manager update find-skills rspress-custom-theme
153
164
  Behavior:
154
165
 
155
166
  - Uses `skills.json` as the source of truth
156
- - Re-resolves git refs and npm package targets
157
- - Skips `link:` skills, including the bundled self skill
167
+ - Updates git skills to the latest `main` commit and npm skills to the registry `latest` version
168
+ - Skips local `link:`, `local:`, and `file:` skills
158
169
  - Fails immediately for unknown skill names
159
- - Writes `skills-lock.yaml` only after fetch and link succeed
170
+ - Writes `skills.json` only after the updated install succeeds
160
171
 
161
172
  ## Programmatic API
162
173
 
@@ -181,13 +192,15 @@ const skills = await listRepoSkills('vercel-labs', 'skills')
181
192
  ## Specifier Format
182
193
 
183
194
  ```text
184
- git/file/npm: <source>#[ref&]path:<skill-path>
195
+ git/file/npm: <source>[#ref][&path:<skill-path>]
185
196
  link: link:<path-to-skill-dir>
197
+ local: local:<path-to-existing-skill-dir>
198
+ local shorthand: local:*
186
199
  ```
187
200
 
188
201
  | Part | Description | Example |
189
202
  |------|-------------|---------|
190
- | `source` | Git URL, direct `link:` skill path, `file:` tarball, or `npm:` package name | `https://github.com/o/r.git`, `link:./local/skills/my-skill`, `file:./skills.tgz`, `npm:@scope/pkg` |
203
+ | `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` |
191
204
  | `ref` | Optional git ref | `main`, `v1.0.0`, `HEAD`, `6cb0992`, `6cb0992a176f2ca142e19f64dca8ac12025b035e` |
192
205
  | `path` | Path to skill directory within source | `/skills/my-skill` |
193
206
 
@@ -196,9 +209,10 @@ link: link:<path-to-skill-dir>
196
209
  ### Resolution Types
197
210
 
198
211
  - **`git`** — Clones the repo, resolves commit hash, copies skill files
199
- - **`link`** — Reads from a local directory and copies the selected skill
212
+ - **`link`** — Symlinks a local skill directory into `installDir`
213
+ - **`local`** — Uses an existing user-owned skill directory in place
200
214
  - **`file`** — Extracts a local `tgz` package and copies the selected skill
201
- - **`npm`** — Resolves a package from the configured npm registry, locks the tarball URL/version/integrity, and installs from the downloaded tarball
215
+ - **`npm`** — Resolves a package from the configured npm registry and installs from the downloaded tarball
202
216
 
203
217
  `npm:` reads `registry` and scoped `@scope:registry` values from `.npmrc`. Matching `:_authToken`, `:_auth`, or `username` + `:_password` entries are also used for private registry requests.
204
218
 
@@ -209,7 +223,7 @@ src/
209
223
  ├── bin/ # CLI entry points
210
224
  ├── cli/ # CLI runner and interactive prompts
211
225
  ├── commands/ # add, install, patch command implementations
212
- ├── config/ # skills.json / skills-lock.yaml read/write
226
+ ├── config/ # skills.json read/write and in-memory install plan resolution
213
227
  ├── github/ # Git clone + skill discovery (listSkills)
214
228
  ├── install/ # Skill materialization, linking, pruning
215
229
  ├── patches/ # Patch edit state, diff generation, patch application
@@ -228,4 +242,3 @@ pnpm build # Builds with Rslib (ESM output + DTS)
228
242
  ```bash
229
243
  pnpm test # Runs tests with Rstest
230
244
  ```
231
- ``
package/dist/index.d.ts CHANGED
@@ -4,15 +4,21 @@ export declare function addCommand(options: AddCommandOptions): Promise<{
4
4
  } | {
5
5
  skillName: string;
6
6
  specifier: string;
7
- }[]>;
7
+ }[] | {
8
+ status: "listed";
9
+ skills: SkillInfo[];
10
+ }>;
8
11
 
9
12
  export declare type AddCommandOptions = {
10
13
  cwd: string;
11
14
  specifier: string;
12
- skill?: string;
15
+ skill?: string | string[];
13
16
  global?: boolean;
14
17
  yes?: boolean;
15
18
  agent?: string[];
19
+ list?: boolean;
20
+ copy?: boolean;
21
+ all?: boolean;
16
22
  };
17
23
 
18
24
  /**
@@ -58,8 +64,6 @@ export declare enum ErrorCode {
58
64
  YAML_PARSE_ERROR = "EYAMLPARSE",
59
65
  INVALID_SPECIFIER = "EINVALIDSPEC",
60
66
  MANIFEST_NOT_FOUND = "EMANIFEST",
61
- LOCKFILE_NOT_FOUND = "ELOCKFILE",
62
- LOCKFILE_OUTDATED = "ELOCKOUTDATED",
63
67
  MANIFEST_EXISTS = "EMANIFESTEXISTS",
64
68
  MANIFEST_VALIDATION_ERROR = "EMANIFESTVAL",
65
69
  NETWORK_ERROR = "ENETWORK",
@@ -74,18 +78,6 @@ export declare enum ErrorCode {
74
78
 
75
79
  export declare function expandSkillsManifest(_rootDir: string, manifest: SkillsManifest): Promise<NormalizedSkillsManifest>;
76
80
 
77
- export declare function fetchSkillsFromLock(rootDir: string, manifest: SkillsManifest, lockfile: SkillsLock, options?: {
78
- onProgress?: InstallProgressListener;
79
- }): Promise<{
80
- readonly status: "skipped";
81
- readonly reason: "up-to-date";
82
- readonly fetched?: undefined;
83
- } | {
84
- readonly status: "fetched";
85
- readonly fetched: string[];
86
- reason?: undefined;
87
- }>;
88
-
89
81
  /**
90
82
  * Error thrown when file system operations fail
91
83
  */
@@ -156,7 +148,6 @@ export declare function installCommand(options: InstallCommandOptions): Promise<
156
148
 
157
149
  export declare type InstallCommandOptions = {
158
150
  cwd: string;
159
- frozenLockfile?: boolean;
160
151
  onProgress?: InstallProgressListener;
161
152
  };
162
153
 
@@ -190,38 +181,26 @@ declare type InstallProgressReporter = {
190
181
  };
191
182
 
192
183
  export declare function installSkills(rootDir: string, options?: {
193
- frozenLockfile?: boolean;
194
184
  onProgress?: InstallProgressListener;
195
185
  }): Promise<{
196
- readonly status: "skipped";
197
- readonly reason: "manifest-missing";
186
+ status: "skipped";
187
+ reason: string;
198
188
  installed?: undefined;
199
189
  } | {
200
- readonly status: "installed";
201
- readonly installed: string[];
190
+ status: "installed";
191
+ installed: string[];
202
192
  reason?: undefined;
203
193
  }>;
204
194
 
205
195
  export declare const installStageHooks: {
206
- beforeFetch: (_rootDir: string, _manifest: SkillsManifest, _lockfile: SkillsLock) => Promise<void>;
196
+ beforeFetch: (_rootDir: string, _manifest: SkillsManifest, _plan: ResolvedSkillsPlan) => Promise<void>;
207
197
  };
208
198
 
209
- export declare function isLockInSync(rootDir: string, manifest: NormalizedSkillsManifest, lock: SkillsLock | null, manifestStat?: ManifestStat | null, installState?: {
210
- manifestStat?: ManifestStat;
211
- } | null): Promise<boolean>;
212
-
213
199
  /**
214
200
  * Checks if an error is a known SPM error
215
201
  */
216
202
  export declare function isSpmError(error: unknown): error is SpmError;
217
203
 
218
- export declare function linkSkillsFromLock(rootDir: string, manifest: SkillsManifest, lockfile: SkillsLock, options?: {
219
- onProgress?: InstallProgressListener;
220
- }): Promise<{
221
- readonly status: "linked";
222
- readonly linked: string[];
223
- }>;
224
-
225
204
  /**
226
205
  * List skills in a GitHub repo by cloning and scanning.
227
206
  * This avoids GitHub API rate limits.
@@ -229,23 +208,18 @@ export declare function linkSkillsFromLock(rootDir: string, manifest: SkillsMani
229
208
  export declare function listRepoSkills(owner: string, repo: string, ref?: string): Promise<SkillInfo[]>;
230
209
 
231
210
  /**
232
- * Error thrown when manifest or lockfile operations fail
211
+ * Error thrown when manifest operations fail
233
212
  */
234
213
  export declare class ManifestError extends SpmError {
235
214
  readonly filePath: string;
236
215
  constructor(options: {
237
- code: ErrorCode.MANIFEST_NOT_FOUND | ErrorCode.LOCKFILE_NOT_FOUND | ErrorCode.LOCKFILE_OUTDATED | ErrorCode.MANIFEST_EXISTS | ErrorCode.MANIFEST_VALIDATION_ERROR;
216
+ code: ErrorCode.MANIFEST_NOT_FOUND | ErrorCode.MANIFEST_EXISTS | ErrorCode.MANIFEST_VALIDATION_ERROR;
238
217
  filePath: string;
239
218
  message?: string;
240
219
  cause?: Error;
241
220
  });
242
221
  }
243
222
 
244
- declare interface ManifestStat {
245
- mtimeMs: number;
246
- size: number;
247
- }
248
-
249
223
  /**
250
224
  * Error thrown when network operations fail
251
225
  */
@@ -273,7 +247,7 @@ declare type NormalizedSkillsManifest = {
273
247
  };
274
248
 
275
249
  export declare type NormalizedSpecifier = {
276
- type: 'git' | 'link' | 'file' | 'npm';
250
+ type: 'git' | 'link' | 'local' | 'file' | 'npm';
277
251
  source: string;
278
252
  ref: string | null;
279
253
  path: string;
@@ -283,7 +257,12 @@ export declare type NormalizedSpecifier = {
283
257
 
284
258
  export declare function normalizeSkillsManifest(manifest: Partial<SkillsManifest>): NormalizedSkillsManifest;
285
259
 
286
- export declare function normalizeSpecifier(specifier: string): NormalizedSpecifier;
260
+ export declare function normalizeSpecifier(specifier: string, options?: NormalizeSpecifierOptions): NormalizedSpecifier;
261
+
262
+ declare type NormalizeSpecifierOptions = {
263
+ installDir?: string;
264
+ skillName?: string;
265
+ };
287
266
 
288
267
  /**
289
268
  * Error thrown when parsing fails (JSON, YAML, specifiers)
@@ -352,50 +331,16 @@ declare type ProgressReporterOptions = {
352
331
  info?: (text: string) => void;
353
332
  };
354
333
 
355
- export declare function readSkillsLock(rootDir: string): Promise<SkillsLock | null>;
356
-
357
334
  export declare function readSkillsManifest(rootDir: string): Promise<NormalizedSkillsManifest | null>;
358
335
 
359
- export declare function resolveLockEntry(cwd: string, specifier: string, skillName?: string): Promise<{
360
- skillName: string;
361
- entry: SkillsLockEntry;
362
- }>;
363
-
364
- export declare function runCli(argv: string[], context?: {
365
- cwd?: string;
366
- }): Promise<unknown>;
367
-
368
- /**
369
- * Error thrown when a skill operation fails
370
- */
371
- export declare class SkillError extends SpmError {
372
- readonly skillName: string;
373
- constructor(options: {
374
- code: ErrorCode.SKILL_NOT_FOUND | ErrorCode.SKILL_EXISTS | ErrorCode.VALIDATION_ERROR;
375
- skillName: string;
376
- message?: string;
377
- cause?: Error;
378
- });
379
- }
380
-
381
- export declare type SkillInfo = {
382
- name: string;
383
- description: string;
384
- path: string;
385
- };
386
-
387
- export declare type SkillsLock = {
388
- lockfileVersion: '0.1';
389
- installDir: string;
390
- linkTargets: string[];
391
- skills: Record<string, SkillsLockEntry>;
392
- };
393
-
394
- export declare type SkillsLockEntry = {
336
+ export declare type ResolvedSkillEntry = {
395
337
  specifier: string;
396
338
  resolution: {
397
339
  type: 'link';
398
340
  path: string;
341
+ } | {
342
+ type: 'local';
343
+ path: string;
399
344
  } | {
400
345
  type: 'file';
401
346
  tarball: string;
@@ -421,6 +366,46 @@ export declare type SkillsLockEntry = {
421
366
  };
422
367
  };
423
368
 
369
+ export declare type ResolvedSkillsPlan = {
370
+ installDir: string;
371
+ linkTargets: string[];
372
+ skills: Record<string, ResolvedSkillEntry>;
373
+ };
374
+
375
+ export declare function resolveSkillEntry(cwd: string, specifier: string, skillName?: string, options?: {
376
+ installDir?: string;
377
+ }): Promise<{
378
+ skillName: string;
379
+ entry: ResolvedSkillEntry;
380
+ }>;
381
+
382
+ export declare function resolveSkillsPlan(cwd: string, manifest: NormalizedSkillsManifest, options?: {
383
+ onProgress?: InstallProgressListener;
384
+ }): Promise<ResolvedSkillsPlan>;
385
+
386
+ export declare function runCli(argv: string[], context?: {
387
+ cwd?: string;
388
+ }): Promise<unknown>;
389
+
390
+ /**
391
+ * Error thrown when a skill operation fails
392
+ */
393
+ export declare class SkillError extends SpmError {
394
+ readonly skillName: string;
395
+ constructor(options: {
396
+ code: ErrorCode.SKILL_NOT_FOUND | ErrorCode.SKILL_EXISTS | ErrorCode.VALIDATION_ERROR;
397
+ skillName: string;
398
+ message?: string;
399
+ cause?: Error;
400
+ });
401
+ }
402
+
403
+ export declare type SkillInfo = {
404
+ name: string;
405
+ description: string;
406
+ path: string;
407
+ };
408
+
424
409
  /**
425
410
  * Skills manifest input type used for authoring/writing manifests.
426
411
  * This preserves optionality for fields with defaults.
@@ -471,7 +456,7 @@ export declare type UpdateCommandResult = {
471
456
  unchanged: string[];
472
457
  skipped: Array<{
473
458
  name: string;
474
- reason: 'link-specifier';
459
+ reason: 'link-specifier' | 'local-specifier' | 'file-specifier';
475
460
  }>;
476
461
  failed: Array<{
477
462
  name: string;
@@ -479,10 +464,6 @@ export declare type UpdateCommandResult = {
479
464
  }>;
480
465
  };
481
466
 
482
- export declare function withBundledSelfSkillLock(rootDir: string, manifest: SkillsManifest, lockfile: SkillsLock): Promise<SkillsLock>;
483
-
484
- export declare function writeSkillsLock(rootDir: string, lockfile: SkillsLock): Promise<void>;
485
-
486
467
  export declare function writeSkillsManifest(rootDir: string, manifest: SkillsManifest): Promise<void>;
487
468
 
488
469
  export { }