@klhapp/skillmux 0.2.0 → 0.4.2

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
@@ -5,6 +5,76 @@ All notable changes to this project are documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.4.2](https://github.com/klhq/skillmux/compare/v0.4.1...v0.4.2) (2026-07-23)
9
+
10
+
11
+ ### Fixed
12
+
13
+ * **release:** validate explicit release tag ([#46](https://github.com/klhq/skillmux/issues/46)) ([ec11057](https://github.com/klhq/skillmux/commit/ec11057d18162ace7944b1ee07bd0fb1f282efcc))
14
+
15
+ ## [0.4.1](https://github.com/klhq/skillmux/compare/v0.4.0...v0.4.1) (2026-07-22)
16
+
17
+
18
+ ### Fixed
19
+
20
+ * **release:** publish immediately from release please ([#44](https://github.com/klhq/skillmux/issues/44)) ([4569cf3](https://github.com/klhq/skillmux/commit/4569cf3eed7ded5d4cb032f622a6a92e3eb28b21))
21
+
22
+ ## [0.4.0](https://github.com/klhq/skillmux/compare/v0.3.0...v0.4.0) (2026-07-22)
23
+
24
+
25
+ ### Added
26
+
27
+ * **cli:** add 'skillmux local-vault init <path>' to write the local_vault marker ([7648145](https://github.com/klhq/skillmux/commit/76481452994f98d6ef01fb1302344fc76a547492))
28
+ * **cli:** add 'skillmux which <skill_id>' to show which root resolves a skill ([0dfd0ed](https://github.com/klhq/skillmux/commit/0dfd0ed9e067f12dfce6cd9a3bc5fe61eeedf4d8))
29
+ * **cli:** add skill visibility command ([a00d248](https://github.com/klhq/skillmux/commit/a00d24811b3b91e947a529fe87f742acf6cb4ce7))
30
+ * **cli:** filter [project.*] groups by each target's project_groups ([e039920](https://github.com/klhq/skillmux/commit/e039920349769e65d01555a5681a4b1724fddf88))
31
+ * **completions:** list 'which' in bash and zsh command completions ([fc8151f](https://github.com/klhq/skillmux/commit/fc8151fc83a0e57b2eb57decdf78d716cdf7a8fa))
32
+ * **config:** add local_vault_paths alongside the unchanged vault_path ([4d8c95e](https://github.com/klhq/skillmux/commit/4d8c95e600a6607e8e83b9fb5a6344677003d3c3))
33
+ * **doctor:** report local_vault_paths marker presence and vault_path drift ([eb8e8a3](https://github.com/klhq/skillmux/commit/eb8e8a304847c366f2ba057e1d9b9741516e75c2))
34
+ * **doctor:** report shadowed skills across vault_path and local_vault_paths ([eac168d](https://github.com/klhq/skillmux/commit/eac168d3c729d7a610632c73fa573ad4947964d9))
35
+ * **manifest:** add pin/unpin --core to skillmux manifest CLI ([59a4ceb](https://github.com/klhq/skillmux/commit/59a4ceb8a25867c4bc73932294aae6a8a1f9edd8))
36
+ * **manifest:** add pin/unpin --project <group> --repo to skillmux manifest CLI ([3bf98e9](https://github.com/klhq/skillmux/commit/3bf98e983bd05b35d15b25f4effd1fb33cf03b8f))
37
+ * **manifest:** add pin/unpin and local-vault markers ([f1329ae](https://github.com/klhq/skillmux/commit/f1329ae48b1c7603201f039b80ac2b8489101cf8))
38
+ * **manifest:** add the AC6 portability guard to validateManifest ([fb83299](https://github.com/klhq/skillmux/commit/fb8329969228029d7d9561884a1f0f0df3aa7a77))
39
+ * **manifest:** project_groups + local_vault_paths overlay support ([#40](https://github.com/klhq/skillmux/issues/40)) ([411ecfb](https://github.com/klhq/skillmux/commit/411ecfb243695465aea44dc4a9192190ecde6086))
40
+ * **manifest:** replace targets.*.project boolean with project_groups ([ccd246e](https://github.com/klhq/skillmux/commit/ccd246e8238c00eebac6abfde4d4250f87d770db))
41
+ * **sync:** add role field to SkillmuxMarker with read-compat for absent role ([79af610](https://github.com/klhq/skillmux/commit/79af610c4fd598e458a947eb01f7c01358f38402))
42
+ * **vault:** add findShadowedSkills to surface cross-root skill_id collisions ([7d1a328](https://github.com/klhq/skillmux/commit/7d1a3287ea47044e39371d38a360c3acc3d2f2c0))
43
+ * **vault:** resolve skills through local_vault_paths before vault_path ([0c1f603](https://github.com/klhq/skillmux/commit/0c1f60328b30681dd739224a5a53cdb178413a18))
44
+
45
+
46
+ ### Fixed
47
+
48
+ * **manifest:** reject invalid [project.*] group names in pinProject before writing ([153d0ac](https://github.com/klhq/skillmux/commit/153d0ac39a548ca47aafb0ddd309cbb4da63fda8))
49
+ * **router-core:** tolerate broken content when a root wins on existence alone ([6cb4d0d](https://github.com/klhq/skillmux/commit/6cb4d0d64bd0dd98d7fd44ac382ea2e9e3f2e125))
50
+
51
+ ## [0.3.0](https://github.com/klhq/skillmux/compare/v0.2.1...v0.3.0) (2026-07-21)
52
+
53
+
54
+ ### Added
55
+
56
+ * **adapters:** add unified local and remote target adapters (AC3, AC7, AC10) ([552b7d8](https://github.com/klhq/skillmux/commit/552b7d8f26ece4ab4a98f14326e508e35aeb57d5))
57
+ * **admin:** add authenticated /admin/v1 HTTP control plane (AC7, AC8, AC9, AC10) ([7b7958f](https://github.com/klhq/skillmux/commit/7b7958fb0e248e2c33a0ca1e566286617a8a0d84))
58
+ * **calibrate:** Automated calibration-tuning pipeline, evidence store, and live reload ([#35](https://github.com/klhq/skillmux/issues/35)) ([7ccd79f](https://github.com/klhq/skillmux/commit/7ccd79f9d25d901aa3d7aec43953b79896ebfa24))
59
+ * **cli:** add output formatting, exit code mapping, and shell completions (AC11, AC12) ([0099395](https://github.com/klhq/skillmux/commit/009939501192f81dc8fbe2e15c33bce22beb1456))
60
+ * **cli:** CLI polish, target resolution, config/calibrate parity, and admin control plane ([a4fb67d](https://github.com/klhq/skillmux/commit/a4fb67d3e8a09ae6ef8151ad4892ec3d94d261e0))
61
+ * **cli:** connect CLI dispatcher with context, config, calibrate, and completions (cli-polish) ([bd3fd14](https://github.com/klhq/skillmux/commit/bd3fd140443e4523fcaf163b2fe5e57d5c51ac9c))
62
+ * **config:** add source-aware config service and status tracking (AC4, AC5, AC6) ([325342a](https://github.com/klhq/skillmux/commit/325342a55b5bac4a7b3aee7d7740e2fc22f7aee7))
63
+ * **context:** add context management and target resolution service (AC1, AC2) ([34435c9](https://github.com/klhq/skillmux/commit/34435c934aaa5ad69af90b7b42e761c674d6ac85))
64
+
65
+
66
+ ### Fixed
67
+
68
+ * **clients:** handle trailing /v1 in embedding base_url ([26d8158](https://github.com/klhq/skillmux/commit/26d8158138b390ffb3e5c3f54777273673db0a73))
69
+ * **types:** resolve TypeScript compilation errors in CLI, adapters, and server ([e4ad7d2](https://github.com/klhq/skillmux/commit/e4ad7d2c059157e52a9f584f15eeaed6a8091fec))
70
+
71
+
72
+ ### Changed
73
+
74
+ * **config:** remove deprecated environment variable shims ([#34](https://github.com/klhq/skillmux/issues/34)) ([e046f45](https://github.com/klhq/skillmux/commit/e046f45c3133b2e91b4920513975107b025b19f5))
75
+
76
+ ## [0.2.1](https://github.com/klhq/skillmux/compare/skillmux-v0.2.0...skillmux-v0.2.1) (2026-07-21)
77
+
8
78
  ## [0.2.0](https://github.com/klhq/skillmux/compare/skillmux-v0.1.1...skillmux-v0.2.0) (2026-07-21)
9
79
 
10
80
 
package/README.md CHANGED
@@ -1,4 +1,6 @@
1
- # skillmux
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/klhq/skillmux/main/docs/assets/logo.png" alt="skillmux" width="400">
3
+ </p>
2
4
 
3
5
  A local, read-only [MCP](https://modelcontextprotocol.io) stdio server that gives agents **on-demand skill discovery**: route a natural-language task description to the right skill in your vault and deliver its `SKILL.md` byte-for-byte, verified by SHA-256.
4
6
 
@@ -11,11 +13,18 @@ Built for agents that lack native skill triggering (Goose recipe workers, openco
11
13
  - [Pinning skills across surfaces](#pinning-skills-across-surfaces) — optional: statically load a curated set across multiple agents
12
14
  - [Docker Usage](#docker-usage)
13
15
  - [Configuration](#configuration) — inference modes, security scanning, installing skills, env vars
16
+ - [CLI & Automation](docs/cli.md) — context management, remote target resolution, policy calibration, JSON envelopes
17
+ - [Benchmarks & Evaluation](#benchmarks--evaluation)
18
+ - [FAQ & Troubleshooting](#faq--troubleshooting)
14
19
  - [Guarantees](#guarantees)
15
20
  - [Development](#development)
16
21
 
17
22
  ## How it works
18
23
 
24
+ <p align="center">
25
+ <img src="docs/assets/architecture.svg" alt="skillmux Architecture &amp; Hybrid Routing Flow" width="100%">
26
+ </p>
27
+
19
28
  ```
20
29
  resolve_skill("convert this spreadsheet to markdown")
21
30
 
@@ -60,10 +69,11 @@ Download the latest release for your architecture:
60
69
  ```sh
61
70
  # AMD64
62
71
  gh release download --repo klhq/skillmux \
63
- --pattern 'skillmux-linux-*' \
64
- --pattern 'SHA256SUMS'
72
+ --pattern 'skillmux-linux-*'
73
+
74
+ # Optional: verify build provenance
75
+ gh attestation verify skillmux-linux-amd64 --repo klhq/skillmux
65
76
 
66
- sha256sum --check SHA256SUMS
67
77
  # Install the binary matching your machine (amd64 or arm64)
68
78
  chmod +x skillmux-linux-amd64
69
79
  sudo install skillmux-linux-amd64 /usr/local/bin/skillmux
@@ -192,7 +202,15 @@ This writes `skillmux.toml` at the vault root and marks `~/.claude/skills` as sk
192
202
 
193
203
  ### 2. Pick which skills are pinned
194
204
 
195
- `init` always starts `[core]` empty — there's no heuristic yet for which skills belong there. Edit `skillmux.toml` by hand:
205
+ `init` always starts `[core]` empty — there's no heuristic yet for which skills belong there. Pin with the CLI instead of hand-editing `skillmux.toml`:
206
+
207
+ ```sh
208
+ skillmux manifest pin csv-formatter --core
209
+ ```
210
+
211
+ Unpin the same way: `skillmux manifest unpin csv-formatter --core`. `--project <group>` pins into a `[project.<group>]` tier instead (add `--repo <path>` the first time, to create the group). Every pin is validated before writing — the skill must actually resolve from the vault, and `[core]` stays under its 25-skill cap.
212
+
213
+ Hand-editing `skillmux.toml` still works if you prefer it:
196
214
 
197
215
  ```toml
198
216
  [core]
@@ -200,10 +218,10 @@ skills = ["csv-formatter"]
200
218
 
201
219
  [targets.claude]
202
220
  dir = "/Users/you/.claude/skills"
203
- project = false
221
+ project_groups = []
204
222
  ```
205
223
 
206
- Cap: 25 skills in `[core]`. Full manifest schema, including `[project.<group>]` pins scoped to one repo, is in [`docs/configuration.md`](docs/configuration.md#tiers-and-the-manifest).
224
+ Full manifest schema, including `[project.<group>]` pins scoped to one repo and machine-local overlay vaults via `local_vault_paths`, is in [`docs/configuration.md`](docs/configuration.md#tiers-and-the-manifest).
207
225
 
208
226
  ### 3. Materialize
209
227
 
@@ -384,6 +402,10 @@ All core settings can be overridden via environment variables (handy for Docker)
384
402
 
385
403
  Remote API keys are read from the environment variables named by `inference.embedding.api_key_env` and `inference.reranker.api_key_env`; no secret ever lives in the config file.
386
404
 
405
+ ## Benchmarks & Evaluation
406
+
407
+ Skillmux includes a built-in evaluation framework to benchmark retrieval accuracy (lexical vs. hybrid vector search) against labeled intent datasets.
408
+
387
409
  Evaluate lexical and local hybrid retrieval against the checked-in labeled queries:
388
410
 
389
411
  ```sh
@@ -393,6 +415,56 @@ bun run src/cli.ts eval
393
415
  # hybrid recall@5: 1.000
394
416
  ```
395
417
 
418
+ Custom policy calibration can also be performed against domain-specific query logs using `skillmux calibrate` (see [`docs/cli.md`](docs/cli.md#policy-calibration-skillmux-calibrate)).
419
+
420
+ ## FAQ & Troubleshooting
421
+
422
+ <details>
423
+ <summary><b>Why did my query return <code>ambiguous</code> instead of <code>matched</code>?</b></summary>
424
+
425
+ <br>
426
+
427
+ The router returns `"outcome": "ambiguous"` when multiple candidate skills meet retrieval confidence thresholds, or when no single candidate dominates by a sufficient score margin. In this state, up to 10 candidate skill summaries (`skill_id`, `title`, `description`) are returned so the calling LLM can choose the exact skill and invoke `fetch_skill`.
428
+
429
+ </details>
430
+
431
+ <details>
432
+ <summary><b>Does skillmux require an active internet connection?</b></summary>
433
+
434
+ <br>
435
+
436
+ No. In default local inference mode (`inference.mode = "local"`), skillmux operates 100% offline. The default GTE-small embedding model is quantized to q8 and bundled within the binary/Docker image.
437
+
438
+ </details>
439
+
440
+ <details>
441
+ <summary><b>What happens when remote embedding or reranking endpoints fail?</b></summary>
442
+
443
+ <br>
444
+
445
+ Skillmux features automatic fallback degradation. If remote embedding APIs or rerankers become unreachable, the router gracefully falls back to SQLite FTS5 lexical search rather than throwing an error to the calling agent.
446
+
447
+ </details>
448
+
449
+ <details>
450
+ <summary><b>When should I use routed skills vs. pinned skills?</b></summary>
451
+
452
+ <br>
453
+
454
+ - **Routed (Default)**: Best for expanding vaults with dozens or hundreds of skills. Skills are loaded dynamically on demand via `resolve_skill`, keeping the agent's initial context window clean.
455
+ - **Pinned (`skillmux sync`)**: Best when running multiple agent surfaces (e.g. Claude Code, opencode) that all require the same core set of 2–5 skills loaded statically at agent startup.
456
+
457
+ </details>
458
+
459
+ <details>
460
+ <summary><b>How do I verify server readiness and routing health?</b></summary>
461
+
462
+ <br>
463
+
464
+ Run `skillmux doctor` locally or hit the HTTP readiness endpoint (`GET /health/ready`). `doctor` checks vault accessibility, state directory permissions, ONNX runtime binding status, and active retrieval lane status.
465
+
466
+ </details>
467
+
396
468
  ## Guarantees
397
469
 
398
470
  - **Read-only vault** — no code path writes under the vault; all state is confined to `state_dir`. Covered by tests.
@@ -3,3 +3,8 @@
3
3
  # vault_path is a neutral path, never a harness's static-loading scan
4
4
  # surface (e.g. ~/.agents/skills) — see docs/sdd/skr-cli/think.md.
5
5
  vault_path = "~/skills"
6
+
7
+ # Optional, default []: machine-local override-only vaults, checked before
8
+ # vault_path for a given skill_id. Never holds skillmux.toml or the sync git
9
+ # hook — those always stay in vault_path. See docs/configuration.md#local-vault-overlays.
10
+ # local_vault_paths = ["~/skills-local"]
@@ -2,6 +2,8 @@
2
2
 
3
3
  Skillmux defaults to FTS5 plus local GTE-small semantic retrieval. Most users need no config file.
4
4
 
5
+ For detailed CLI command reference, target resolution, policy calibration, and automation envelopes, see [`docs/cli.md`](cli.md).
6
+
5
7
  ## Local mode
6
8
 
7
9
  ```toml
@@ -88,13 +90,65 @@ skills = ["pdf-extractor"] # must not overlap [core]
88
90
 
89
91
  [targets.claude]
90
92
  dir = "/Users/you/.claude/skills"
91
- project = false # true = also apply [project.*] groups scoped to this target
93
+ project_groups = ["repo1"] # which [project.*] groups materialize into this target — [] means none
92
94
  ```
93
95
 
94
96
  - `[core].skills` — symlinked into every `[targets.*]` dir on `sync`. Capped at 25 skills; `sync` fails if a listed skill id isn't actually in the vault.
95
- - `[project.<group>].skills` — symlinked only into `<repo>/<relative path from $HOME to the target dir>`, for each `repos` entry, and only for targets with `project = true`. `repos` entries must resolve under `$HOME` (that's how the pin path is derived). A skill can't appear in both `[core]` and the same `[project.*]` group.
96
- - `[targets.<name>]` — one entry per adopted surface. `skillmux init --target <name> --yes` writes these; hand-editing is fine as long as `sync` is still allowed to own the directory (see below).
97
+ - `[project.<group>].skills` — symlinked only into `<repo>/<relative path from $HOME to the target dir>`, for each `repos` entry, and only for targets whose `project_groups` names that group. `repos` entries must resolve under `$HOME` (that's how the pin path is derived). A skill can't appear in both `[core]` and the same `[project.*]` group.
98
+ - `[targets.<name>]` — one entry per adopted surface. `skillmux init --target <name> --yes` writes these; hand-editing is fine as long as `sync` is still allowed to own the directory (see below). `project_groups` is an explicit list, not a boolean — a target only receives the specific groups it names, never every group in the manifest.
99
+
100
+ **Pin/unpin without hand-editing.** `skillmux manifest pin`/`unpin` mutate `[core]`/`[project.*]` for you, validating with the same rules `sync`/`doctor` enforce (skill must resolve from `vault_path`, no duplicate pins, `[core]` stays under the 25-skill cap) before writing anything:
101
+
102
+ ```sh
103
+ skillmux manifest pin csv-formatter --core # add to [core]
104
+ skillmux manifest pin pdf-extractor --project repo1 # add to an existing group
105
+ skillmux manifest pin pdf-extractor --project repo2 --repo ~/code/repo2 # create a new group
106
+ skillmux manifest unpin csv-formatter --core # remove from [core]
107
+ skillmux manifest unpin pdf-extractor --project repo1 # remove from a group (group stays, even if empty)
108
+ ```
109
+
110
+ `--repo` is only accepted when `--project <group>` names a group that doesn't exist yet — passing it for an existing group is rejected, since changing an existing group's `repos` isn't this command's job. Hand-editing `skillmux.toml` directly is still fully supported; these commands are a convenience layer over the same file, not a replacement for it.
111
+
112
+ > **Breaking change:** `[targets.<name>].project` (a boolean) has been replaced by `project_groups` (an array of `[project.*]` names). A manifest still using the old field fails to parse with an error pointing at the new one. To migrate, replace `project = true` with `project_groups = [...]` listing every group that target previously received (previously *all* groups, unconditionally); replace `project = false` with `project_groups = []`.
113
+
114
+ Every `[core]`/`[project.*]` skill_id must resolve from the canonical `vault_path` — pinning a skill that only exists in a `local_vault_paths` entry (see below) fails `sync`/`doctor` with a distinct error, since the manifest is meant to be portable across machines and a machine-local override wouldn't exist elsewhere.
97
115
 
98
116
  ### Ownership marker
99
117
 
100
- Every directory `sync` manages gets a `.skillmux` marker file (`{"managed_by": "skillmux", "target": "<name>", "created_at": ...}`). `sync` refuses to touch a directory that exists but has no marker — run `skillmux init --target <name> --yes` first, which either creates the directory fresh or adopts an existing one in place (contents untouched). This is also why `sync --restore-monolith` (which deletes the marker and replaces the whole directory with one symlink straight to the vault) requires re-running `init` before that target can be `sync`'d again.
118
+ Every directory `sync` manages gets a `.skillmux` marker file (`{"managed_by": "skillmux", "role": "target", "target": "<name>", "created_at": ...}`). `sync` refuses to touch a directory that exists but has no marker — run `skillmux init --target <name> --yes` first, which either creates the directory fresh or adopts an existing one in place (contents untouched). This is also why `sync --restore-monolith` (which deletes the marker and replaces the whole directory with one symlink straight to the vault) requires re-running `init` before that target can be `sync`'d again.
119
+
120
+ The same `.skillmux` marker filename and JSON shape is also used for `local_vault_paths` entries (see below), distinguished by `role: "local_vault"` with a `vault_path` field instead of `target`. Markers written before this `role` field existed have no `role` key at all — they're read back as `role: "target"`, so nothing already on disk needs migrating.
121
+
122
+ ### Local vault overlays
123
+
124
+ `local_vault_paths` (in `config.toml`, alongside `vault_path`) lets one machine layer override-only skills on top of the shared vault — a skill being authored locally, a machine-specific script, or a patched copy of an upstream skill — without touching `vault_path` itself:
125
+
126
+ ```toml
127
+ vault_path = "~/skills" # unchanged: canonical, owns skillmux.toml and the sync git hook
128
+ local_vault_paths = ["~/skills-local"] # optional, default []: override-only, checked first
129
+ ```
130
+
131
+ - **Resolution order**: for any given `skill_id`, `local_vault_paths` entries are checked first, in array order; `vault_path` is the fallback. This applies everywhere a skill's on-disk location matters — indexing, `resolve_skill`/`fetch_skill` delivery, and `sync`'s symlink target.
132
+ - **`vault_path` keeps its exact existing meaning.** `skillmux.toml` and the `sync --install-hook` git hook only ever live in `vault_path`; `skillmux doctor` warns if it finds a stray manifest inside a `local_vault_paths` entry instead.
133
+ - **`[core]`/`[project.*]` pins must resolve from `vault_path`.** Since the manifest is meant to be portable, `sync`/`doctor` reject a pin backed only by a `local_vault_paths` entry — see the manifest section above.
134
+ - **Not yet covered**: `startVaultWatcher`'s live filesystem watch still only watches `vault_path`; a change inside a `local_vault_paths` entry is picked up lazily (on the next `resolve_skill`/`fetch_skill`/`sync` call, via the same mtime staleness check `vault_path` already uses), not instantly.
135
+
136
+ **Visibility.** A `skill_id` present in more than one root is silently resolved via the precedence above with no output during normal use — two commands make that resolution visible on demand:
137
+
138
+ - `skillmux which <skill_id>` — prints which root actually serves that skill, and names every root it shadows:
139
+ ```
140
+ $ skillmux which my-skill
141
+ my-skill: serving from /home/user/skills-local
142
+ shadows: /home/user/skills
143
+ ```
144
+ Exits non-zero with `<skill_id>: not found in vault_path or local_vault_paths` if no root has it.
145
+ - `skillmux doctor` reports every shadowed skill_id as an informational check (`shadowed:<skill_id>`, always `ok`) alongside its existing vault/manifest/embedding checks — so a scan of `doctor` output surfaces every override in one place, not just the one you thought to ask about.
146
+
147
+ **Discoverability.** A `local_vault_paths` entry is otherwise just a bare directory — nothing on disk says it belongs to skillmux or which `vault_path` it overlays. `skillmux local-vault init <path>` writes a `.skillmux` marker recording that relationship:
148
+
149
+ ```sh
150
+ skillmux local-vault init ~/skills-local
151
+ # wrote /home/user/skills-local/.skillmux (role: local_vault, vault_path: /home/user/skills)
152
+ ```
153
+
154
+ `<path>` must already be one of the configured `local_vault_paths` entries and must exist on disk — the command only ever writes the marker, it never adds the path to `config.toml` for you. `skillmux doctor` reports each entry's marker status (`local_vault_marker:<path>`): `ok: false` if no marker exists yet (with the exact `local-vault init` command to fix it), or if the marker's recorded `vault_path` no longer matches the one currently configured (drift — e.g. after copying the directory to a machine with a different `vault_path`).
package/docs/releasing.md CHANGED
@@ -1,55 +1,76 @@
1
1
  # Releasing
2
2
 
3
- Releases are created from version tags after `main` is green.
3
+ Release Please creates releases after `main` is green. When its release PR is
4
+ merged, it creates the matching version tag and directly calls the reusable
5
+ release workflow. The pipeline does not rely on a second workflow being
6
+ triggered by a tag created with `GITHUB_TOKEN`.
4
7
 
5
8
  ## Prepare
6
9
 
7
- 1. Update `version` in `package.json`.
8
- 2. Move relevant entries from `Unreleased` into a dated version section in `CHANGELOG.md`.
9
- 3. Merge those changes through a pull request and confirm CI passes.
10
+ 1. Merge changes to `main` using Conventional Commit titles.
11
+ 2. Review the Release Please PR, including its version and changelog.
12
+ 3. Confirm CI passes, then merge the Release Please PR.
10
13
 
11
- ## Publish
14
+ ## Publish automatically
12
15
 
13
- Create and push the matching tag:
16
+ After the Release Please PR merges, the same workflow run creates the tag and
17
+ GitHub Release, then calls the publishing workflow with the exact tag and commit
18
+ SHA returned by Release Please. The package version, tag, and commit must all
19
+ match before publishing begins.
14
20
 
15
- ```bash
16
- git switch main
17
- git pull --ff-only
18
- git tag v0.1.1
19
- git push origin v0.1.1
20
- ```
21
+ There is intentionally no tag-push or `workflow_dispatch` entry point. GitHub
22
+ does not start a second workflow for a tag created with the default
23
+ `GITHUB_TOKEN`; directly calling the publisher avoids that event-suppression
24
+ edge case without adding a PAT or GitHub App token.
25
+
26
+ ## Exceptional recovery
21
27
 
22
- The tag must exactly match `v` plus the `package.json` version. A mismatch stops the release.
28
+ Recover missed historical publications manually. Do not feed an old tag into
29
+ the current automated publisher.
23
30
 
24
31
  The release workflow publishes:
25
32
 
26
33
  - `skillmux-linux-amd64`
27
34
  - `skillmux-linux-arm64`
28
- - `SHA256SUMS`
29
35
  - GitHub build provenance attestations when the repository is public
30
- - Full image: `:<version>`, `:<major>.<minor>`, `:<major>`, and `:latest`
31
- - Slim image: `:<version>-slim`, `:<major>.<minor>-slim`, `:<major>-slim`, and `:latest-slim`
32
- - Multi-architecture `linux/amd64` and `linux/arm64` images with SBOM and provenance
33
-
34
- Each Docker tag is a multi-architecture manifest. Users run the same tag on AMD64 and ARM64; Docker automatically pulls the matching image.
35
-
36
- Container images are published to GitHub Container Registry only; the release workflow does not require external registry credentials.
37
- Private repositories still publish BuildKit SBOM/provenance with container images, but GitHub artifact attestations are skipped because GitHub does not support them for user-owned private repositories.
38
-
39
- Verify downloaded binaries with:
36
+ - Full image to GHCR and Docker Hub: `:<version>`, `:<major>.<minor>`,
37
+ and `:latest`
38
+ - Slim image to GHCR and Docker Hub: `:<version>-slim`,
39
+ `:<major>.<minor>-slim`, and `:latest-slim`
40
+ - Multi-architecture `linux/amd64` and `linux/arm64` images with SBOM and
41
+ provenance
42
+
43
+ Each Docker tag is a multi-architecture manifest. Users run the same tag on
44
+ AMD64 and ARM64; Docker automatically pulls the matching image.
45
+
46
+ The two variants are separate multi-architecture manifests rather than
47
+ architecture-suffixed public tags. Docker selects the correct AMD64 or ARM64
48
+ image for the host automatically. Pin `:<version>` or `:<version>-slim` for
49
+ reproducible deployments; use the floating `latest` tags only when automatic
50
+ upgrades are intentional.
51
+
52
+ Container images are published to:
53
+
54
+ - `ghcr.io/klhq/skillmux`
55
+ - `${DOCKERHUB_USERNAME}/skillmux` on Docker Hub
56
+
57
+ The `production-release` GitHub environment provides the
58
+ `DOCKERHUB_USERNAME` variable and `DOCKERHUB_TOKEN` secret. npm publishes with
59
+ Trusted Publishing through the calling `release-please.yml` workflow, so no
60
+ long-lived npm token is required. GitHub Packages uses the workflow's scoped
61
+ `GITHUB_TOKEN`.
62
+ Private repositories still publish BuildKit SBOM/provenance with container
63
+ images, but GitHub artifact attestations are skipped because GitHub does not
64
+ support them for user-owned private repositories.
65
+
66
+ Verify downloaded binaries with build provenance attestation:
40
67
 
41
68
  ```bash
42
- sha256sum --check SHA256SUMS
69
+ gh release download v0.1.1 --repo klhq/skillmux --pattern 'skillmux-linux-*'
70
+ gh attestation verify skillmux-linux-amd64 --repo klhq/skillmux
43
71
  ./skillmux-linux-amd64 config show
44
72
  ```
45
73
 
46
- Verify release assets from GitHub without cloning the repository:
47
-
48
- ```bash
49
- gh release download v0.1.1 --repo klhq/skillmux
50
- sha256sum --check SHA256SUMS
51
- ```
52
-
53
74
  Verify the container with a read-only vault mount:
54
75
 
55
76
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@klhapp/skillmux",
3
- "version": "0.2.0",
3
+ "version": "0.4.2",
4
4
  "description": "Local read-only MCP server routing natural-language task queries to skills in a SKILL.md vault, with zero-loss delivery",
5
5
  "type": "module",
6
6
  "private": false,