@camunda8/spm 0.1.0 → 0.2.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.
Files changed (2) hide show
  1. package/README.md +77 -23
  2. package/package.json +6 -6
package/README.md CHANGED
@@ -1,28 +1,27 @@
1
1
  # spm — skill package manager
2
2
 
3
3
  Declare AI skills as git dependencies in `ai.json`, and `spm` wires them into your
4
- AI tool (Claude Code and GitHub Copilot CLI) **without ever copying skills into
5
- your repo**. No symlinks in the project, no fragile `.gitignore` rules.
4
+ AI tool (Claude Code and GitHub Copilot CLI) **without ever committing skills to
5
+ your repo**. Anything spm materializes into the working tree is gitignored — no
6
+ symlinks, no skills under version control.
6
7
 
7
8
  ## How it works
8
9
 
9
10
  ```
10
11
  ai.json ──resolve──▶ ai.lock ──fetch──▶ ~/.spm/store/<repo>@<sha> (global cache, one clone per commit)
11
12
 
12
- └─project──▶ ~/.spm/vendors/<target>/<project>/ (assembled marketplace)
13
-
14
- └─register─▶ vendor picks it up (see below)
13
+ └─project──▶ materialized where the vendor expects it (see below)
15
14
  ```
16
15
 
17
16
  - **`ai.json`** — you author it, commit it. Declares target vendors + skill deps.
18
17
  - **`ai.lock`** — generated, commit it. Pins every version selector to an immutable commit SHA → reproducible installs.
19
- - **Global store** (`~/.spm/store`) — each repo@commit fetched once, shared across all projects.
20
- - **Vendor projection** (`~/.spm/vendors`) a self-contained plugin marketplace assembled outside your repo. Both vendors require skills to physically live inside a plugin dir, so spm copies them **here**, never into your project tree.
18
+ - **Global store** (`~/.spm/store`) — a **fetch cache only**: each repo@commit is cloned once and shared across all projects. Nothing is *registered* or *materialized* here — it exists purely so repeated installs don't re-clone.
19
+ - **Vendor projection** spm copies the store's skills into a **project-local** directory wherever each vendor loads them from. Nothing spm generates is committed to your repo, and nothing is written into a user-global vendor location.
21
20
  - **Registration** differs per vendor:
22
- - **Claude** — spm writes a pointer to the marketplace into `.claude/settings.local.json` (gitignored by convention). Declarative, per-project, zero VCS footprint.
23
- - **Copilot CLI** — spm shells out to `copilot plugin marketplace add` + `copilot plugin install`. Copilot marketplaces/plugins are **user-global** (no project-local config), so registration is global. spm names the registration by a **stable, path-independent project id** stored in `ai.lock` (`spm-xxxxxxxx`), so a moved or re-cloned checkout re-registers the *same* entry instead of leaving a duplicate. Orphaned registrations (whose local dir no longer exists) are pruned automatically on each `spm install`/`clean`. Requires the `copilot` CLI on PATH.
21
+ - **Claude** — spm assembles a self-contained plugin marketplace in the **project-local**, gitignored `.spm/claude/` dir and writes a pointer to it into `.claude/settings.local.json` (gitignored by convention). The dir sits outside `.agents/skills/` so Copilot's scanner never picks it up. Declarative, per-project, zero VCS footprint.
22
+ - **Copilot CLI** — spm copies the resolved skills into a **project-local** directory, `.agents/skills/spm-managed-skills/<name>/`, where Copilot CLI auto-discovers them (`.agents/skills/**/SKILL.md`). That directory is added to the project's `.gitignore` (with an explanatory comment) so the materialized skills stay truly local and are never committed. No user-global state, no `copilot` CLI required.
24
23
 
25
- On a fresh clone, teammates run `spm install` — it rebuilds their own store and re-registers from `ai.lock`. Same model as `node_modules`.
24
+ On a fresh clone, teammates run `spm install` — it repopulates their own fetch cache and re-materializes the project-local skills from `ai.lock`. Same model as `node_modules`.
26
25
 
27
26
  ## ai.json
28
27
 
@@ -85,11 +84,10 @@ instead of hanging on a prompt (helpers and ssh-agent still work).
85
84
  ## Installation
86
85
 
87
86
  `spm` ships as a single self-contained binary (needs the system `git` on `PATH`
88
- at runtime, plus the `copilot` CLI if you target `copilot`).
87
+ at runtime).
89
88
 
90
- **From npm (recommended)** — _pending the initial publish ([#6](https://github.com/camunda/spm-cli/issues/6))_.
91
- Once published, this is the zero-setup path on every platform it puts `spm` on
92
- your `PATH` with no manual steps:
89
+ **From npm (recommended)** — the zero-setup path on every platform. It puts `spm`
90
+ on your `PATH` with no manual steps:
93
91
 
94
92
  ```bash
95
93
  npm i -g @camunda8/spm
@@ -101,6 +99,13 @@ your OS/CPU via an optional dependency (`@camunda8/spm-<os>-<cpu>`), so nothing
101
99
  compiled or downloaded outside npm. Supported: `darwin-x64`, `darwin-arm64`,
102
100
  `linux-x64`, `linux-arm64`, `win32-x64`. Update with `npm i -g @camunda8/spm@latest`.
103
101
 
102
+ **From crates.io** — build and install from source via Cargo (needs a Rust
103
+ toolchain). The crate is `spm-cli`; the installed binary is `spm`:
104
+
105
+ ```bash
106
+ cargo install spm-cli
107
+ ```
108
+
104
109
  **Prebuilt binary** — the repo is **internal**, so release assets require
105
110
  authentication. Download with the [GitHub CLI](https://cli.github.com/) (you must
106
111
  be signed in via `gh auth login` and be a Camunda org member), then put the binary
@@ -120,13 +125,6 @@ Assets: `spm-x86_64-unknown-linux-gnu`, `spm-aarch64-unknown-linux-gnu`,
120
125
  `spm-x86_64-apple-darwin`, `spm-aarch64-apple-darwin`,
121
126
  `spm-x86_64-pc-windows-msvc.exe`.
122
127
 
123
- **From crates.io** — _not published yet_. The crate name `spm-cli` is reserved
124
- (the binary is `spm`); once it's published you'll be able to run:
125
-
126
- ```bash
127
- cargo install spm-cli
128
- ```
129
-
130
128
  **From source:**
131
129
 
132
130
  ```bash
@@ -146,19 +144,75 @@ spm remove <name> # drop a skill
146
144
  spm update [name] # re-resolve branches/tags to latest
147
145
  spm install # rebuild from ai.lock (after clone)
148
146
  spm list # show skills + pinned commits
147
+ spm status # check skills are materialized in this checkout
149
148
  spm clean # remove generated vendor config
150
149
  ```
151
150
 
151
+ ## Worktrees & fresh clones
152
+
153
+ spm materializes skills into **gitignored** project-local dirs (`.spm/claude/`,
154
+ `.agents/skills/spm-managed-skills/`). Git **worktrees** have their own working
155
+ tree and don't share those untracked files, so — exactly like `node_modules` —
156
+ **each checkout needs its own `spm install`**:
157
+
158
+ ```bash
159
+ git worktree add ../feature -b feature
160
+ cd ../feature && spm install # materialize this worktree's skills
161
+ ```
162
+
163
+ Skipping this is the usual reason an agent doesn't see a declared skill in a new
164
+ worktree or a fresh clone. `spm status` tells you at a glance and **exits
165
+ non-zero** when anything is missing, so it works in scripts too:
166
+
167
+ ```bash
168
+ spm status
169
+ # [claude] 0/1 installed .../.spm/claude/plugin/skills
170
+ # reviewer MISSING
171
+ # error: some declared skills are not materialized in this checkout — run `spm install` here
172
+ ```
173
+
174
+ To install automatically on every branch checkout and new worktree, add a
175
+ `post-checkout` git hook (worktrees share the repo's `.git/hooks`):
176
+
177
+ ```sh
178
+ # .git/hooks/post-checkout — then: chmod +x .git/hooks/post-checkout
179
+ #!/bin/sh
180
+ # Re-materialize spm skills so Claude/Copilot always see the declared set.
181
+ [ -f ai.lock ] && command -v spm >/dev/null 2>&1 && spm install >/dev/null 2>&1
182
+ exit 0
183
+ ```
184
+
185
+ > **Claude note:** `spm install` writes the *absolute* path of the current
186
+ > checkout's `.spm/claude/` into that checkout's `.claude/settings.local.json`.
187
+ > Since that file is gitignored, a new worktree either has no registration at all
188
+ > or — if it was copied over — one still pointing at the checkout it came from.
189
+ > Either way, run `spm install` inside the worktree and start (or
190
+ > `/reload-plugins` in) the Claude session from that same worktree; discovery is
191
+ > snapshotted at session start. `spm status` reports a stale pointer explicitly:
192
+ >
193
+ > ```
194
+ > ! .claude/settings.local.json marketplace points at /repo/.spm/claude, not this checkout (/repo-feature/.spm/claude)
195
+ > ```
196
+
197
+ To see what each harness actually loaded: `claude plugin list` /
198
+ `claude plugin marketplace list` for Claude; `copilot skill list` for Copilot.
199
+
152
200
  ## Design notes
153
201
 
154
202
  - **Cross-OS**: shells out to the system `git` (no libgit2 build deps); no symlinks; all paths via `std::path`. Runs on Linux, macOS, Windows.
155
- - **`SPM_HOME`** overrides the store/vendor root (default `~/.spm`) — used by tests.
156
- - **Vendor adapters**: adding a target means implementing one `Vendor` trait (`src/vendor/`). Both `claude` and `copilot` assemble the same plugin-marketplace layout (`marketplace.json` → `plugin.json` → `skills/<name>/SKILL.md`); they differ only in how the marketplace is registered.
203
+ - **`SPM_HOME`** overrides the store root (default `~/.spm`, holding only the fetch cache) — used by tests. Vendor output is always project-local and is not affected by `SPM_HOME`.
204
+ - **Vendor adapters**: adding a target means implementing one `Vendor` trait (`src/vendor/`). `claude` assembles a plugin-marketplace layout (`marketplace.json` → `plugin.json` → `skills/<name>/SKILL.md`) into the gitignored project-local `.spm/claude/` and points to it; `copilot` copies skills into the gitignored project-local `.agents/skills/spm-managed-skills/`. Both keep their materialized files out of VCS via the shared `src/gitignore.rs` helper.
157
205
 
158
206
  ## Development
159
207
 
160
208
  `make check` runs the full CI gate locally (`fmt-check` + `clippy` + `test`).
161
209
 
210
+ To cut a release, bump the crate version (the single source of truth for
211
+ crates.io, npm, and the GitHub Release) with `make bump` — `PART=patch|minor|major`
212
+ (default `patch`) or `VERSION=X.Y.Z`. Since `main` is protected, `make bump-pr`
213
+ does the bump on a branch and opens the PR for you. See [`RELEASE.md`](RELEASE.md)
214
+ for the full procedure.
215
+
162
216
  A **pre-commit hook** (fmt + clippy) installs itself automatically via
163
217
  [`cargo-husky`](https://github.com/rhysd/cargo-husky) — just run `cargo test`
164
218
  (or `cargo build`) once after cloning and the hook lands in `.git/hooks`. The
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda8/spm",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Skill package manager (spm): declare AI skills in ai.json and materialize them for Claude/Copilot.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/camunda/spm-cli#readme",
@@ -33,10 +33,10 @@
33
33
  "node": ">=18"
34
34
  },
35
35
  "optionalDependencies": {
36
- "@camunda8/spm-linux-x64": "0.1.0",
37
- "@camunda8/spm-linux-arm64": "0.1.0",
38
- "@camunda8/spm-darwin-x64": "0.1.0",
39
- "@camunda8/spm-darwin-arm64": "0.1.0",
40
- "@camunda8/spm-win32-x64": "0.1.0"
36
+ "@camunda8/spm-linux-x64": "0.2.0",
37
+ "@camunda8/spm-linux-arm64": "0.2.0",
38
+ "@camunda8/spm-darwin-x64": "0.2.0",
39
+ "@camunda8/spm-darwin-arm64": "0.2.0",
40
+ "@camunda8/spm-win32-x64": "0.2.0"
41
41
  }
42
42
  }