@ssheleg/make-skill 0.23.5 → 0.24.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/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # Changelog
2
2
 
3
+ ## v0.24.0 — the plugin surface as it is now, and how updates reach a machine
4
+
5
+ - **Four plugin features that landed after this skill last read the docs.** Re-read
6
+ 2026-08-28 against Claude Code 2.1.236; the previous pass was 2026-07-30 against 2.1.212.
7
+ `archive` sources install a zip over HTTPS with no git and no npm (2.1.224); `command`
8
+ sources let a local command print the plugin directory (2.1.229) and are the one class an
9
+ organisation can switch off wholesale with `disableCommandPluginSources`; entry-level
10
+ `headersHelper` mints registry headers and **requires `strict: false`** (2.1.238); a
11
+ `skills` path of `"."` scans the plugin root instead of `skills/` (2.1.221). Bare-name
12
+ sources under `metadata.pluginRoot` are in too (2.1.239).
13
+ - The `headersHelper` contract is written out because every clause of it is a way to fail
14
+ silently — ten seconds, 500 ASCII characters, stdout JSON — and because Claude Code
15
+ **strips every environment variable whose name contains `TOKEN`, `SECRET`, `KEY` or
16
+ `AUTH`** before running it. A helper that reads its credential from one of those is handed
17
+ nothing, and the failure surfaces as a permissions error at the registry.
18
+ - **`distribution.md` gains "How updates reach the machine — decide it, then SAY it".** An
19
+ installer that never mentions updates has still chosen a model, and the model is *never*.
20
+ Claude Code carries a per-marketplace `autoUpdate` flag in `known_marketplaces.json` that
21
+ is **not in the documented settings surface**; `/plugin` writes it and third-party
22
+ installers write it directly. Measured on one machine 2026-08-28: of 20 marketplaces, the
23
+ 2 installed by `vercel-labs/plugins` carry it and 18 do not, because
24
+ `claude plugin marketplace add` never sets it.
25
+ - The section states the trap rather than a preference: for packs that **compose**,
26
+ per-marketplace auto-update moves each on its own clock and produces a combination nobody
27
+ tested — the same reason a family launcher refuses a per-member argument. For packs that
28
+ stand alone it is the better answer. Either way the installer's last line says how the
29
+ next version arrives, because "Installed" is not a complete sentence.
30
+
3
31
  ## v0.23.5 — the channel that sends the installs, on npm too
4
32
 
5
33
  - The `skills.sh` badge and the canonical `homepage` reached GitHub in the previous cycle and stopped
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ssheleg/make-skill",
3
- "version": "0.23.5",
3
+ "version": "0.24.0",
4
4
  "description": "Create, retrofit, audit, and ship agent skills & Claude Code plugins the proven ssheleg way — conformance to the Agent Skills open standard AND Anthropic's platform rules (front-matter limits, disclosure budgets, per-surface runtime limits, the Skills API, evals) plus the Claude Code plugin reference (manifest schemas, component layout, claude plugin validate --strict), marketplace repo layout, version sync, validator + CI, multi-channel distribution (plugin, vercel skills CLI, npx, Cursor), npm gotchas, the review checklist for third-party skills, and MCP / A2A rules for protocol-connected skills. This package is the installer CLI.",
5
5
  "keywords": [
6
6
  "skill",
@@ -3,7 +3,7 @@
3
3
  "name": "make-skill",
4
4
  "displayName": "Make Skill",
5
5
  "description": "Create, retrofit, audit, and ship agent skills & Claude Code plugins the proven ssheleg way: conformance to the Agent Skills open standard, Anthropic's platform rules (surfaces, Skills API, evals) and the Claude Code plugin reference, marketplace repo layout, version sync, validator + CI, multi-channel distribution (plugin, vercel skills CLI, npx, Cursor), npm gotchas, end-to-end first publish, the review checklist for third-party skills, plus MCP / A2A references for protocol-connected skills.",
6
- "version": "0.23.5",
6
+ "version": "0.24.0",
7
7
  "author": {
8
8
  "name": "ssheleg",
9
9
  "url": "https://x.com/sshlg93"
@@ -5,7 +5,7 @@ license: MIT
5
5
  compatibility: Authoring works on any agent. The bundled scripts/ need python3. Publishing steps need git, gh, node and npm; the plugin gates need the claude CLI. Not usable on the Claude API surface, which has no network and no runtime package install.
6
6
  metadata:
7
7
  author: ssheleg
8
- version: "0.23.5"
8
+ version: "0.24.0"
9
9
  homepage: https://github.com/ssheleg/make-skill
10
10
  ---
11
11
 
@@ -10,7 +10,10 @@ Upstream (read against these, not from memory — the schema grows every release
10
10
  - Marketplaces — <https://code.claude.com/docs/en/plugin-marketplaces>
11
11
  - Skills — <https://code.claude.com/docs/en/skills>
12
12
 
13
- *Read from the docs on 2026-07-30 against Claude Code 2.1.212. Re-read before
13
+ *Read from the docs on 2026-07-30 against Claude Code 2.1.212, and again 2026-08-28
14
+ against 2.1.236 — that pass added the `archive` and `command` sources, entry-level
15
+ `headersHelper`, bare-name sources under `metadata.pluginRoot`, and `"skills": ["."]`,
16
+ all of which landed between the two readings. Re-read before
14
17
  trusting a version-gated field in a new quarter.*
15
18
 
16
19
  The [Agent Skills spec](https://agentskills.io/specification) (see
@@ -128,6 +131,12 @@ marketplace-only `source`, `category`, `tags`, `strict`, `relevance`,
128
131
  | `url` | `{"source":"url","url":"https://…​.git","ref?":…,"sha?":…}` |
129
132
  | `git-subdir` | `{"source":"git-subdir","url":"…","path":"packages/x","ref?":…,"sha?":…}` — sparse clone |
130
133
  | `npm` | `{"source":"npm","package":"@scope/pkg","version?":…,"registry?":…}` — no git SHA to fall back on, so set `version` in the manifest or the entry, else it resolves to `unknown` and updates never fire |
134
+ | `archive` **2.1.224+** | `{"source":"archive","url":"https://…/x-2.1.0.zip","sha256?":"<64 hex>"}` — a zip over HTTPS, **no git and no npm on the machine**. The digest is optional and is the only thing standing between a URL and whatever is behind it today; set it |
135
+ | `command` **2.1.229+** | `{"source":"command","command":"my-tool plugin-path","timeout?":60,"mode?":"copy"}` — a local command prints the directory. `timeout` defaults to 60s, max 600; `mode` is `copy` (default) or `link`. An organisation can be configured to refuse this entire class with `disableCommandPluginSources`, so it is the one source that may be unavailable for reasons no error in your repo explains |
136
+
137
+ **Bare-name sources, 2.1.239+.** With `metadata.pluginRoot` set on the marketplace,
138
+ an entry may write `"source": "my-plugin"` and have it resolve under that root — the
139
+ `./` requirement above applies only without it.
131
140
 
132
141
  `sha` beats `ref` when both are set. **Marketplace source ≠ plugin source**: the
133
142
  first says where `marketplace.json` lives (supports `ref` only), the second where
@@ -137,6 +146,23 @@ each plugin comes from (`ref` and `sha`).
137
146
  to it. `strict: false` makes the entry the entire definition — and a
138
147
  `plugin.json` that also declares components is then a load error.
139
148
 
149
+ **`headersHelper` on an entry, 2.1.238+ — and it needs `strict: false`.** Beside
150
+ `source`, it names a command that mints HTTP headers for a private registry, and it runs
151
+ **only** when that one plugin is installed or updated. The marketplace-level form is
152
+ different: declared in `extraKnownMarketplaces` on a `url` marketplace, it runs before
153
+ every catalog fetch and archive download on that origin, and one run is reused for 60
154
+ seconds. Both are separate from the MCP `headersHelper` documented later in this file.
155
+
156
+ The command's contract, because each clause is a way to fail silently: one JSON object of
157
+ header names to string values on stdout, exit 0, **within 10 seconds**; at most 500
158
+ printable ASCII characters with no run of four spaces; run through `sh` (or `cmd.exe`)
159
+ from `~/.claude`. Claude Code **strips every variable whose name contains `TOKEN`,
160
+ `SECRET`, `KEY` or `AUTH`** from its environment — so a helper that reads its credential
161
+ from one of those is handed nothing, and the failure looks like a permissions problem at
162
+ the registry. It receives `CLAUDE_CODE_MARKETPLACE_URL`, `CLAUDE_CODE_MARKETPLACE_NAME`,
163
+ `CLAUDE_CODE_PLUGIN_NAME` and `CLAUDE_CODE_PLUGIN_ARCHIVE_URL` instead. The user has to
164
+ accept the command before it runs at all.
165
+
140
166
  Version resolution order: `plugin.json` → marketplace entry → git SHA →
141
167
  `unknown`.
142
168
 
@@ -145,7 +171,7 @@ Version resolution order: `plugin.json` → marketplace entry → git SHA →
145
171
  | Component | Default location |
146
172
  |---|---|
147
173
  | manifest | `.claude-plugin/plugin.json` |
148
- | skills | `skills/<name>/SKILL.md` |
174
+ | skills | `skills/<name>/SKILL.md` — and **`"skills": ["."]` is legal from 2.1.221**: it scans the plugin ROOT for `<name>/SKILL.md` instead of `skills/`, which is what a repository whose skills sit at the top level needs |
149
175
  | commands | `commands/*.md` (flat skills; prefer `skills/` for new plugins) |
150
176
  | agents | `agents/*.md` |
151
177
  | workflows | `workflows/*.js` |
@@ -212,7 +212,40 @@ package's own repo, `npx` resolves the local package and reports a false
212
212
  - **`npx` inside the package's own repo** resolves the local package → a false
213
213
  `command not found`. Always e2e-test from another cwd.
214
214
 
215
- ### Installer implementation traps (read while WRITING the CLI)
215
+ ### How updates reach the machine — decide it, then SAY it
216
+
217
+ An installer that never mentions updates has still chosen an update model: **never**. The
218
+ operator installs once, the pack drifts, and nothing tells them. Decide between three, and
219
+ print the answer at the end of the install.
220
+
221
+ **Claude Code's own switch is a per-marketplace `autoUpdate` flag** in
222
+ `~/.claude/plugins/known_marketplaces.json`. It is real and it is **not in the documented
223
+ settings surface** — not in the settings reference, not in the marketplace page; `/plugin`
224
+ writes it, and third-party installers write it directly. `vercel-labs/plugins` prompts
225
+ `Enable auto-updates? [Y/n]`, defaults to yes, and only asks when the marketplace is new,
226
+ the target is Claude Code or Cursor, and the install did **not** go through the official
227
+ CLI. Measured on one machine 2026-08-28: of 20 marketplaces, the 2 installed by that tool
228
+ carry `autoUpdate: true` and the other 18 have no such key — **`claude plugin marketplace
229
+ add` does not set it**, so a plugin installed the official way never auto-updates unless
230
+ somebody turns it on.
231
+
232
+ | Model | Who updates | Right when |
233
+ |---|---|---|
234
+ | **Per-marketplace `autoUpdate`** | Claude Code, in the background, each marketplace on its own clock | packs are **independent** — one can move without the others meaning anything |
235
+ | **A launcher command** (`npx <name> update`) | the operator, in one pass, whole set | packs **compose** — they are released and tested together |
236
+ | **Nothing** | nobody | never; this is the default you get by not choosing |
237
+
238
+ **The trap, and it is the whole reason this section exists.** For a FAMILY that composes,
239
+ per-marketplace auto-update is actively wrong: each member moves on its own schedule and
240
+ the machine ends up in a combination the maintainers never tested together. That is the
241
+ same reason a family launcher refuses a per-member argument. If your packs are
242
+ independent, `autoUpdate` is the better answer and you should offer it; if they compose,
243
+ offer the launcher and say plainly that auto-update is off **on purpose**.
244
+
245
+ Either way the last thing the installer prints is how the next version arrives. "Installed"
246
+ is not a complete sentence.
247
+
248
+ ## Installer implementation traps (read while WRITING the CLI)
216
249
 
217
250
  - **Piped stdin + readline:** sequential `rl.question()` drops buffered lines.
218
251
  Use ONE persistent-listener prompter for the whole flow (super-ux