@klhapp/skillmux 1.2.0 → 1.3.1

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
@@ -2,617 +2,258 @@
2
2
  <img src="https://raw.githubusercontent.com/klhq/skillmux/main/docs/assets/logo.png" alt="skillmux" width="400">
3
3
  </p>
4
4
 
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.
6
-
7
- Built for agents that lack native skill triggering (Goose recipe workers, opencode, and friends). Agents that already trigger skills natively (e.g. Claude Code) don't need it.
8
-
9
- - [How it works](#how-it-works)
10
- - [Tiers: routed vs. pinned](#tiers-routed-vs-pinned)
11
- - [Install](#install)
12
- - [Quick start](#quick-start) — the fastest path to seeing it respond
13
- - [Pinning skills across surfaces](#pinning-skills-across-surfaces) — optional: statically load a curated set across multiple agents
14
- - [Docker Usage](#docker-usage)
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
- - [Policy Calibration](docs/calibration.md) — labelled datasets, certification, apply lifecycle, and reference profile
18
- - [Benchmarks & Evaluation](#benchmarks--evaluation)
19
- - [FAQ & Troubleshooting](#faq--troubleshooting)
20
- - [Guarantees](#guarantees)
21
- - [Development](#development)
22
-
23
- ## How it works
24
-
25
5
  <p align="center">
26
- <img src="docs/assets/architecture.svg" alt="skillmux Architecture &amp; Hybrid Routing Flow" width="100%">
6
+ <a href="https://github.com/klhq/skillmux/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/klhq/skillmux/actions/workflows/ci.yml/badge.svg"></a>
7
+ <a href="https://github.com/klhq/skillmux/releases/latest"><img alt="GitHub release" src="https://img.shields.io/github/v/release/klhq/skillmux"></a>
8
+ <a href="https://www.npmjs.com/package/@klhapp/skillmux"><img alt="npm" src="https://img.shields.io/npm/v/@klhapp/skillmux"></a>
9
+ <a href="LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-blue.svg"></a>
27
10
  </p>
28
11
 
29
- ```
30
- resolve_skill("convert this spreadsheet to markdown")
31
-
32
-
33
- hybrid recall: SQLite FTS5 (BM25) ∪ embedding cosine (brute-force)
34
-
35
-
36
- reciprocal-rank fusion → shortlist
37
- optional reranker → matched | ambiguous | no_match
38
- ```
12
+ Skillmux manages [`SKILL.md`](https://agentskills.io) collections across AI
13
+ coding clients. Keep one vault source of truth, pin a small set into native
14
+ skill directories, and retrieve the rest through MCP. On one machine, the
15
+ source and checkout can be the same directory. In a shared deployment, each
16
+ machine that manages native skills and the server use their own checkout; Git
17
+ or your deployment process keeps those checkouts current.
39
18
 
40
- - **matched** one skill clearly wins: full `SKILL.md` delivered inline, `sha256(body) == content_sha256 ==` hash of the file on disk at delivery time. Stale index? It re-indexes and delivers fresh bytes never stale ones.
41
- - **ambiguous** up to 5 candidates (id, title, description). The calling LLM picks and calls `fetch_skill`.
42
- - **no_match** — proceed under your normal workflow; don't load an unrelated skill.
19
+ The same Skillmux CLI manages native skills and can serve local stdio MCP.
20
+ Most individual users need only the CLI. Add Docker when you need a shared or
21
+ always-on HTTP service.
43
22
 
44
- If embeddings are unavailable, the router remains ready with FTS5 lexical retrieval. If an optional reranker is unavailable, it preserves the hybrid shortlist instead of failing.
23
+ Choose a setup by the job:
45
24
 
46
- ### Tools
25
+ 1. Need native skills or local MCP for one client? Install the **Skillmux CLI**.
26
+ 2. On Linux without Bun? Install the standalone Linux executable instead; it is
27
+ the same CLI.
28
+ 3. Need one shared HTTP MCP service? Deploy the **full Docker image**, the
29
+ self-contained default with GTE-small.
30
+ 4. Already have remote embeddings, or intentionally want lexical-only
31
+ retrieval? Use the **slim** image; see [Deployment](docs/deployment.md).
32
+ 5. Need native pins and shared retrieval? Run the CLI on the machines that own
33
+ client directories and one shared server for routed retrieval. MCP-only
34
+ clients connect over HTTP and do not need the CLI.
47
35
 
48
- | Tool | Input | Returns |
49
- | --------------- | ---------- | ---------------------------------------------------------------------------------------------------------------- |
50
- | `resolve_skill` | `query` | outcome + metadata in `structuredContent`; on match the verbatim body as text content (exactly once on the wire) |
51
- | `fetch_skill` | `skill_id` | verbatim body, `content_sha256`, supporting-file paths |
36
+ Manage the server's vault checkout outside the container. Use the CLI for
37
+ Skillmux operations and Git or your deployment process for replication.
52
38
 
53
- The full contract lives in [`docs/schema.json`](docs/schema.json) (JSON Schema 2020-12, language-neutral).
39
+ ## One vault source of truth, three ways to use it
54
40
 
55
- ## Tiers: routed vs. pinned
41
+ <p align="center">
42
+ <img src="docs/assets/architecture.svg" alt="Three ways to use Skillmux: manage native skills, add local MCP retrieval, or run a shared MCP service" width="100%">
43
+ </p>
56
44
 
57
- Skills live in one vault, but there are two ways an agent gets one:
45
+ “Local” describes where Skillmux runs. “Local inference” means the embedding
46
+ model runs in the Skillmux process. Both stdio and HTTP expose the same
47
+ `resolve_skill` and `fetch_skill` MCP tools.
58
48
 
59
- - **routed** the default, described above. Nothing is loaded up front; the agent calls `resolve_skill` on demand and gets back exactly the skill that matches. This scales to hundreds of skills at zero standing cost.
60
- - **pinned** (`core` / `project`) — a small, hand-picked set of skills symlinked directly into an agent's own skill directory (e.g. `~/.claude/skills`), so they load the same way any other skill on that agent does — no MCP round-trip, no query. `core` pins apply everywhere; `project` pins apply only inside one repo.
49
+ ## Install the CLI
61
50
 
62
- Pinning is optional and orthogonal to serving: `skillmux init`/`sync` manage what's pinned; `skillmux serve` is what answers `resolve_skill` for everything else. Most single-agent setups never need pinning — reach for it once you're running the same small set of skills across multiple agent surfaces (Claude Code, opencode, ...) and don't want to maintain that list by hand in each one. See [Pinning skills across surfaces](#pinning-skills-across-surfaces).
51
+ The Bun package supports macOS, Linux, and Windows and requires
52
+ [Bun 1.3 or newer](https://bun.sh/docs/installation):
63
53
 
64
- ## Install
54
+ ```sh
55
+ bun add -g @klhapp/skillmux
56
+ skillmux --help
57
+ ```
65
58
 
66
- ### Linux Binary
59
+ Native target sync needs permission to create directory symlinks on Windows.
67
60
 
68
- Download the latest release for your architecture:
61
+ On Linux, you can install a standalone AMD64 or ARM64 executable instead:
69
62
 
70
63
  ```sh
71
- # AMD64
72
- gh release download --repo klhq/skillmux \
73
- --pattern 'skillmux-linux-*'
74
-
75
- # Optional: verify build provenance
64
+ gh release download --repo klhq/skillmux --pattern 'skillmux-linux-*'
76
65
  gh attestation verify skillmux-linux-amd64 --repo klhq/skillmux
77
-
78
- # Install the binary matching your machine (amd64 or arm64)
79
66
  chmod +x skillmux-linux-amd64
80
67
  sudo install skillmux-linux-amd64 /usr/local/bin/skillmux
81
- skillmux config show
82
68
  ```
83
69
 
84
- Release assets are also available at <https://github.com/klhq/skillmux/releases/latest>.
85
-
86
- Requirements at runtime:
70
+ Use `skillmux-linux-arm64` on ARM64. See [Deployment](docs/deployment.md) for
71
+ the full and slim Docker images.
87
72
 
88
- - A skill vault: one directory per skill with a `SKILL.md` in [agentskills.io](https://agentskills.io) format. Default: `~/skills`.
89
- - Optional remote OpenAI-compatible embeddings and Infinity-native reranking. The full binary uses local GTE-small embeddings by default.
73
+ ## Quick starts
90
74
 
91
- ## Quick start
75
+ Skillmux uses `~/skills` as its default vault:
92
76
 
93
- No config is required — the vault defaults to `~/skills`, and the full binary embeds its own local model, so there's nothing to download or provision first.
94
-
95
- ### 1. Put a skill in your vault
96
-
97
- A skill is just a directory with a `SKILL.md`. Author one by hand to try against:
98
-
99
- ```sh
100
- mkdir -p ~/skills/csv-formatter
101
- cat > ~/skills/csv-formatter/SKILL.md <<'EOF'
102
- ---
103
- name: CSV Formatter
104
- description: Converts CSV or spreadsheet data into clean, aligned Markdown tables. Use whenever the user asks to convert, format, or clean up tabular/CSV/spreadsheet data into Markdown.
105
- ---
106
-
107
- # CSV Formatter
108
-
109
- Given raw CSV input, emit a well-aligned Markdown table: infer column headers
110
- from the first row, right-align numeric columns, left-align text columns.
111
- EOF
77
+ ```text
78
+ ~/skills/
79
+ └── csv-formatter/
80
+ └── SKILL.md
112
81
  ```
113
82
 
114
- (Or fetch an existing skill from a git repo instead — see [Installing skills](#installing-skills) below.)
83
+ ### Manage native skills
115
84
 
116
- ### 2. Index and verify
85
+ Run the setup planner, then verify its managed links:
117
86
 
118
87
  ```sh
119
- skillmux index
88
+ skillmux init
89
+ skillmux sync
120
90
  skillmux doctor
121
91
  ```
122
92
 
123
- `doctor` should report `routing capability: hybrid` and every check `ok`. If something's `fail`, the `detail` column names the exact path or setting to fix.
124
-
125
- ### 3. Serve it
93
+ The planner detects clients, asks which skills belong in the core tier, and
94
+ shows every write before confirmation. Use explicit flags for automation:
126
95
 
127
96
  ```sh
128
- skillmux serve
97
+ skillmux init \
98
+ --client claude-code \
99
+ --client codex \
100
+ --core csv-formatter \
101
+ --dry-run
102
+
103
+ skillmux init \
104
+ --client claude-code \
105
+ --client codex \
106
+ --core csv-formatter \
107
+ --yes
129
108
  ```
130
109
 
131
- Register with your MCP client directly, e.g.:
132
-
133
- ```json
134
- {
135
- "mcpServers": {
136
- "skillmux": {
137
- "command": "skillmux",
138
- "args": ["serve"]
139
- }
140
- }
141
- }
142
- ```
143
-
144
- ### Try it without an MCP client
145
-
146
- To see `resolve_skill` respond without wiring up a client, run the HTTP transport instead (`skillmux serve --transport http`, default port `3000`) and speak MCP's Streamable HTTP protocol directly:
147
-
148
- ```sh
149
- # 1. Initialize a session, capture the session id from the response header
150
- SESSION=$(curl -sS -D - -o /dev/null http://127.0.0.1:3000/mcp \
151
- -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \
152
- -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"try-it","version":"1.0.0"}}}' \
153
- | grep -i '^mcp-session-id:' | tr -d '\r' | cut -d' ' -f2)
154
-
155
- # 2. Complete the handshake
156
- curl -sS -o /dev/null -X POST http://127.0.0.1:3000/mcp \
157
- -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \
158
- -H "mcp-session-id: $SESSION" \
159
- -d '{"jsonrpc":"2.0","method":"notifications/initialized"}'
160
-
161
- # 3. Call resolve_skill
162
- curl -sS -X POST http://127.0.0.1:3000/mcp \
163
- -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \
164
- -H "mcp-session-id: $SESSION" \
165
- -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"resolve_skill","arguments":{"query":"convert this spreadsheet to markdown"}}}'
166
- ```
167
-
168
- Against the `csv-formatter` skill authored above, that returns a real match — trimmed here for length:
169
-
170
- ```json
171
- {
172
- "result": {
173
- "structuredContent": {
174
- "outcome": "ambiguous",
175
- "retrieval": "hybrid",
176
- "candidates": [
177
- {
178
- "skill_id": "csv-formatter",
179
- "title": "CSV Formatter",
180
- "description": "Converts CSV or spreadsheet data..."
181
- }
182
- ]
183
- }
184
- }
185
- }
186
- ```
187
-
188
- `outcome` is `"ambiguous"` here specifically because the vault only has one skill in it — with more skills installed, a clear top match returns `"matched"` with the full `SKILL.md` body inline instead of a candidate list.
189
-
190
- ### Run from source instead
191
-
192
- ```sh
193
- bun install --frozen-lockfile
194
- bun run src/cli.ts index
195
- bun run src/cli.ts serve
196
- ```
197
-
198
- ## Pinning skills across surfaces
199
-
200
- Optional — skip this if `resolve_skill` alone is enough (most setups). Use it once you want a small set of skills loaded _statically_ in every agent that reads from a given directory, instead of routed on demand — see [Tiers](#tiers-routed-vs-pinned).
201
-
202
- Run `skillmux init` with no arguments in a terminal for guided setup. It checks
203
- the vault, preselects clients with concrete filesystem evidence, asks for core
204
- skills, shows one review, and applies after one confirmation. The wizard uses
205
- line-oriented prompts and does not take over the terminal screen.
206
-
207
- ### 1. Check the plan
208
-
209
- ```sh
210
- skillmux init --client claude-code --client codex --core csv-formatter --dry-run
211
- ```
212
-
213
- `--client` names the tool you use. Skillmux maps it to the correct user-level
214
- skill directory and merges clients that share one directory. Gemini CLI,
215
- OpenCode, GitHub Copilot, and Windsurf share `~/.agents/skills`. Claude Code
216
- uses `~/.claude/skills`; Codex uses `$CODEX_HOME/skills` or
217
- `~/.codex/skills`.
218
-
219
- Supported clients are `claude-code`, `codex`, `gemini-cli`, `opencode`,
220
- `github-copilot`, `windsurf`, `antigravity`, `goose`, `hermes`, and
221
- `skillmux-mcp`. Goose, Hermes, and Skillmux MCP report the manual registration
222
- work they need instead of inventing a target directory.
223
-
224
- The dry run prints target classification, config changes, instruction-file
225
- changes, readiness, and core pins. It does not prompt or write.
226
-
227
- ### 2. Apply the plan
228
-
229
- ```sh
230
- skillmux init --client claude-code --client codex --core csv-formatter --yes
231
- ```
232
-
233
- Skillmux writes `skillmux.toml`, adds host-scoped targets, adopts each selected
234
- directory, and installs one managed discovery block for each client with a
235
- known safe durable instruction file. Other clients report manual setup.
236
- Existing manifest entries and instruction text stay intact.
237
- Explicit flags keep the command deterministic. Without `--yes`, an interactive
238
- terminal reviews the plan before applying; a pipe or CI job must pass `--yes`.
239
- Use `--no-instructions` to leave instruction files untouched or `--no-sync` to
240
- save setup for a later `skillmux sync`.
241
-
242
- Use direct targets when you want paths rather than clients:
243
-
244
- ```sh
245
- skillmux init --target agent-skills --yes
246
- skillmux init --target claude-code --yes
247
- skillmux init --target codex --yes
248
- skillmux init --target custom --dir /srv/my-agent/skills --yes
249
- ```
250
-
251
- The old `agents` and `claude` target names still work and print a deprecation
252
- warning. Skillmux keeps those names in an existing manifest.
253
-
254
- If a target points at the whole vault, Skillmux classifies it as `full-vault`
255
- and refuses managed-pin adoption. Review the visibility change before
256
- converting it:
257
-
258
- ```sh
259
- skillmux init --client claude-code --migrate-full-vault \
260
- --core csv-formatter --dry-run
261
- skillmux init --client claude-code --migrate-full-vault \
262
- --core csv-formatter --yes
263
- ```
264
-
265
- After conversion and `skillmux sync`, the client sees the selected core pins
266
- instead of every vault skill.
267
-
268
- ### 3. Set up project-scoped skills
269
-
270
- From a project directory:
110
+ Core pins apply to each configured target and stay capped at 25 skills. Add
111
+ project-specific skills from a repository root:
271
112
 
272
113
  ```sh
273
114
  skillmux project init
274
115
  ```
275
116
 
276
- The guided flow detects the Git root, suggests a project-group name, asks which
277
- configured clients should receive the group, and accepts project skill IDs.
278
- For automation:
279
-
280
- ```sh
281
- skillmux project init ~/code/skillmux --name skillmux \
282
- --client claude-code --client codex \
283
- --skill sdd-tdd --skill code-context --yes
284
- ```
285
-
286
- Re-running the command adds missing paths, skills, and target attachments
287
- without duplicating existing entries. Project setup runs `sync` unless
288
- `--no-sync` is supplied.
117
+ ### Add local MCP retrieval
289
118
 
290
- ### 4. Add or remove pins later
291
-
292
- `--core` seeds only the skill IDs you name. Without it, `init` preserves
293
- existing core pins and makes no guesses. Add another pin with:
119
+ Prefetch the default GTE-small model, index the vault, and start stdio MCP:
294
120
 
295
121
  ```sh
296
- skillmux core pin csv-formatter --yes
297
- ```
298
-
299
- Pin several skills to `[core]` in one atomic call by passing more than one `skill_id`:
300
-
301
- ```sh
302
- skillmux core pin csv-formatter pdf-extractor terraform-plans --yes
303
- ```
304
-
305
- Unpin the same way: `skillmux core unpin csv-formatter --yes` (or `skillmux core unpin csv-formatter pdf-extractor --yes` for several at once). `skillmux project pin <group> <skill_id>...` pins into an existing `[project.<group>]` tier instead — create the group first with `skillmux project add-path <group> <path> --yes` if it doesn't exist yet. Every pin is validated before writing — the skill must actually resolve from the vault, and `[core]` stays under its 25-skill cap. When multiple `skill_id`s are given, they're applied to a single in-memory manifest and written once: if any one of them is already pinned elsewhere (or already unpinned, for `unpin`), the whole call fails and nothing is written.
306
-
307
- Hand-editing `skillmux.toml` still works if you prefer it:
308
-
309
- ```toml
310
- [core]
311
- skills = ["csv-formatter"]
312
-
313
- [targets.claude]
314
- dir = "/Users/you/.claude/skills"
315
- host = "workhorse" # optional; sync skips this target on every other hostname
316
- project_groups = []
317
- ```
318
-
319
- Full manifest schema, including `[project.<group>]` pins scoped to one or more local paths and machine-local overlay vaults via `local_vault_paths`, is in [`docs/configuration.md`](docs/configuration.md#tiers-and-the-manifest).
320
-
321
- ### 4. Materialize
322
-
323
- ```sh
324
- skillmux sync
325
- # claude: +1 -0
326
- ```
327
-
328
- Each pinned skill becomes a symlink from the target dir into the vault. Re-running `sync` is idempotent (`+0 -0` once nothing changed); removing a skill from `[core]` removes its symlink on the next sync.
329
-
330
- ```sh
331
- skillmux sync --dry-run # preview +added/-removed without touching disk
332
- skillmux sync --install-hook # add a git post-merge hook in the vault that runs `skillmux sync` automatically
333
- skillmux sync --restore-monolith # undo: replace a target dir with one symlink straight to the vault
334
- ```
335
-
336
- `--restore-monolith` drops the `.skillmux` marker along with the per-skill symlinks — re-adopt with `skillmux init --target <name> --yes` before that target can be `sync`'d again.
337
-
338
- ### 5. See what's actually getting used
339
-
340
- `skillmux report` reads the same audit log `resolve_skill` writes to (see [Guarantees](#guarantees)) — useful for deciding what belongs in `[core]` versus staying routed:
341
-
342
- ```sh
343
- skillmux report --since 7d # local: reads state_dir's audit db
344
- skillmux report --server http://host:3000 --since 7d # remote: hits a running server's /stats
345
- ```
346
-
347
- ```
348
- window: 2026-07-14T00:00:00Z .. 2026-07-21T00:00:00Z
349
- outcomes: matched=0 ambiguous=2 no_match=0 (ambiguous_rate=1.000)
350
- skills:
351
- csv-formatter matched=0 candidate=2
352
- pdf-extractor matched=0 candidate=2
353
- top no_match queries:
354
- (none)
122
+ skillmux models download
123
+ skillmux index
124
+ skillmux doctor
125
+ skillmux serve
355
126
  ```
356
127
 
357
- `--since` accepts a relative window (`1h`, `7d`, `1m`) or an absolute date/timestamp. A skill matched often but never pinned is a `[core]` candidate; a query that keeps showing up in `top_no_match_queries` means the vault is missing something.
128
+ The model cache lives at `~/.cache/skillmux/models`. If you skip the prefetch,
129
+ Skillmux downloads the model when local inference first needs it.
358
130
 
359
- ## Docker Usage
131
+ ### Run a shared MCP service
360
132
 
361
- The `skillmux` is packaged and distributed as a Docker image in two variants:
362
-
363
- 1. **`skillmux:latest`**: Bundles the small quantized GTE embedding model for local hybrid retrieval.
364
- 2. **`skillmux:latest-slim`**: Excludes model weights and supports configured remote embeddings or lexical fallback.
365
-
366
- Both tags are multi-architecture manifests for Linux AMD64 and ARM64; Docker selects the correct image automatically. Images are published to both [`ghcr.io/klhq/skillmux`](https://github.com/klhq/skillmux/pkgs/container/skillmux) and [`docker.io/klhq/skillmux`](https://hub.docker.com/r/klhq/skillmux) — either registry works, examples below use GHCR.
367
-
368
- ### Running HTTP Server (Docker Default)
369
-
370
- To run as an HTTP MCP service (default in Docker):
133
+ The full image includes GTE-small and serves Streamable HTTP on `/mcp`:
371
134
 
372
135
  ```sh
373
- # Battery-included (runs local in-process ONNX models)
374
136
  docker run -d \
375
137
  --name skillmux \
376
138
  -v ~/skills:/vault:ro \
377
139
  -v skillmux-data:/data \
378
140
  -p 3000:3000 \
379
141
  ghcr.io/klhq/skillmux:latest
380
-
381
- # Slim (configured remote embeddings, or lexical fallback)
382
- docker run -d \
383
- --name skillmux-slim \
384
- -v ~/skills:/vault:ro \
385
- -v skillmux-data:/data \
386
- -p 3000:3000 \
387
- -e EMBED_ENDPOINT="http://embeddings-host:8080/v1/embeddings" \
388
- ghcr.io/klhq/skillmux:latest-slim
389
142
  ```
390
143
 
391
- Connect your MCP client to the HTTP endpoint (e.g. standard Streamable HTTP transport):
144
+ Use `ghcr.io/klhq/skillmux:latest-slim` when you want remote embeddings or
145
+ lexical fallback instead of a bundled model. Docker Hub mirrors both variants
146
+ under `docker.io/klhq/skillmux`.
392
147
 
393
- - POST messages to `http://localhost:3000/mcp`
148
+ The [getting-started guide](docs/getting-started.md) provides complete recipes
149
+ for all three setups.
394
150
 
395
- #### HTTP server: auth, CORS, rate limiting
151
+ ## Add and inspect skills
396
152
 
397
- All of the below is `[server]` config in `config.toml`, overridable by environment variable — see [Environment Variable Overrides](#environment-variable-overrides).
398
-
399
- - **Bind address** (`hostname`, default `127.0.0.1`) — HTTP transport binds loopback-only by default, so a zero-config `skillmux serve --transport http` isn't reachable from the network. Inside Docker (`RUNNING_IN_DOCKER=true`) this defaults to `0.0.0.0` instead, since the container's own loopback isn't reachable through port-mapping. Set `hostname` (or `HTTP_HOSTNAME`) explicitly to expose the server beyond localhost.
400
- - **Bearer token auth** (off by default) — set `auth_enabled = true` and the token via the env var named by `auth_token_env` (default `SKILLMUX_AUTH_TOKEN`). Requests need `Authorization: Bearer <token>`; missing/mismatched tokens get `401`, and a configured-but-empty token env var gets `500`.
401
- - **CORS** — `allowed_origins` (default `[]`, deny-by-default) is checked against the request's `Origin` header; disallowed origins get `403`. Requests with no `Origin` header (curl, MCP clients, server-to-server) are unaffected either way — only browser-issued cross-origin requests are gated. `/health` and `/metrics` are excluded from auth but still CORS-checked.
402
- - **Rate limiting** (off by default) — per-token (when auth is enabled) or per-IP (`server.requestIP`) token-bucket limiting. Enable with `rate_limit.enabled = true` and set `rate_limit.requests_per_minute` (default `60`). The `X-Forwarded-For` header is ignored unless `rate_limit.trust_proxy = true` — it's client-supplied and spoofable, so only opt in when a trusted reverse proxy sets it. Every response carries `X-RateLimit-Limit`/`X-RateLimit-Remaining`/`X-RateLimit-Reset`; over-limit requests get `429` plus `Retry-After`.
403
- - **`GET /health/live`** — lightweight liveness check. Legacy `GET /health` remains an alias.
404
- - **`GET /health/ready`** — readiness with active retrieval capability, skill count, index state, and inference status.
405
- - **`GET /metrics`** — Prometheus text exposition: `skill_router_requests_total`, `skill_router_resolve_outcomes_total`, `skill_router_resolve_latency_seconds` (histogram), `skill_router_errors_total`, `skill_router_rate_limits_exceeded_total`.
406
-
407
- ### Running Stdio Server in Docker
408
-
409
- If your agent runs locally and expects a piped stdio process:
153
+ Install a skill from a Git repository:
410
154
 
411
155
  ```sh
412
- docker run -i --rm \
413
- -v ~/skills:/vault:ro \
414
- ghcr.io/klhq/skillmux:latest serve --transport stdio
156
+ skillmux install owner/repo
157
+ skillmux install owner/repo/path/to/skill
415
158
  ```
416
159
 
417
- ## Configuration
418
-
419
- No config is required for the battery-included local ONNX mode. See [`config.example.toml`](config.example.toml) for the minimal local setup, [`config.remote.example.toml`](config.remote.example.toml) for bring-your-own endpoints, and [`docs/configuration.md`](docs/configuration.md) for advanced settings.
160
+ Skillmux validates `SKILL.md` and scans candidate content before copying it into the vault. Use `--fail-on low|medium|high` to turn selected findings into an install gate.
420
161
 
421
- ### Inference Modes
422
-
423
- - The zero-config default combines SQLite FTS5 with the small `Xenova/gte-small` embedding model and returns an ordered shortlist.
424
- - Configured OpenAI-compatible embeddings replace the local embedder. An optional versioned reranker protocol adapter enables confident automatic matches without coupling configuration to a server product or URL shape.
425
-
426
- Run `skillmux doctor` to verify routing capability. Run `skillmux config show` to inspect effective configuration; it prints credential variable names, never values.
427
-
428
- ### Security scanning
429
-
430
- `skillmux scan [<path>]` inspects skill content for prompt-injection and data-exfiltration risk indicators
431
- before it's served over MCP or HTTP. It's offline (no network call, no inference config needed),
432
- read-only, and advisory-only — it never blocks `skillmux index`/`sync`/`init`, which don't call it
433
- automatically.
162
+ Useful management commands:
434
163
 
435
164
  ```sh
436
- skillmux scan # scan the configured vault_path
437
- skillmux scan ~/skills/some-skill # scan a single candidate skill dir before adding it
438
- skillmux scan --format json # machine-readable { scanned, findings } for CI
439
- skillmux scan --fail-on high # exit 1 if any finding is high severity (for CI gating)
165
+ skillmux scan ~/skills
166
+ skillmux core pin csv-formatter --yes
167
+ skillmux project pin my-project code-context --yes
168
+ skillmux skill which csv-formatter
169
+ skillmux report --since 7d
440
170
  ```
441
171
 
442
- The v1 rule set covers four categories, each attached to the finding as `rule_id` with a fixed
443
- `severity`:
172
+ Read [Managing skills](docs/skill-management.md) for target ownership, project groups, local overrides, recovery, and reporting.
444
173
 
445
- | `rule_id` | `severity` | Flags |
446
- | ------------------------- | ---------- | ------------------------------------------------------------------------------------------------- |
447
- | `prompt-injection-phrase` | `high` | Known instruction-override phrases (e.g. "ignore previous instructions") |
448
- | `invisible-unicode` | `high` | Zero-width/invisible Unicode code points, including hidden tag-character payloads |
449
- | `secret-pattern` | `high` | Hardcoded-credential-shaped strings (AWS-style keys, PEM blocks, `api_key=`/`token=` assignments) |
450
- | `suspicious-url` | `medium` | Bare-IP-address URLs, or URLs paired with exfiltration-suggesting text |
174
+ ## MCP retrieval
451
175
 
452
- `skillmux scan` is unrelated to the `audit` SQLite table / `skillmux report` — that's query telemetry (what got
453
- routed where); `skillmux scan` is content security (what's in the vault).
176
+ Register it with an MCP client:
454
177
 
455
- ### Installing skills
456
-
457
- `skillmux install <repo>[/path]` fetches one skill from a git repo (GitHub shorthand, full HTTPS/SSH URL,
458
- or `file://`) into the configured `vault_path`, so onboarding doesn't require a second CLI just to
459
- pull a skill in. It's a convenience fetch, not a distribution system:
460
-
461
- ```sh
462
- skillmux install owner/repo # repo root must itself be a skill
463
- skillmux install owner/repo/path/to/skill # select one skill out of a multi-skill repo
464
- skillmux install owner/repo --dry-run # preview id, target path, and scan findings
465
- skillmux install owner/repo --force # overwrite an existing skill_id
466
- skillmux install owner/repo --fail-on high # abort the install if scan findings meet the threshold
467
- ```
468
-
469
- The fetched skill is validated the same way `skillmux scan` validates the vault — malformed `SKILL.md`
470
- aborts the install, and scan findings are printed before anything is written (advisory by default;
471
- `--fail-on` opts into blocking, matching `skillmux scan`'s severity levels). Materialization is a plain
472
- file copy, not a symlink — the temporary clone is deleted once the install completes.
473
-
474
- `skillmux install` intentionally does **not** handle updates, uninstalls, version pinning, or
475
- core/project/routed tier assignment (that's `skillmux sync`'s domain) — it only ever fetches one skill,
476
- once. Use `skillmux sync` afterward if the installed skill needs to be pinned into a tier.
477
-
478
- ### Environment Variable Overrides
479
-
480
- All core settings can be overridden via environment variables (handy for Docker):
481
-
482
- - `VAULT_PATH` — overrides `vault_path` (defaults to `/vault` inside Docker)
483
- - `STATE_DIR` — overrides `state_dir` (defaults to `/data` inside Docker)
484
- - `EMBED_ENDPOINT` / `SKILLMUX_EMBED_ENDPOINT` — overrides the complete remote `inference.embedding.endpoint`
485
- - `EMBED_MODEL` / `SKILLMUX_EMBED_MODEL` — overrides `embedding.model`
486
- - `EMBED_DIMENSION` / `SKILLMUX_EMBED_DIMENSION` — overrides `embedding.dimension`
487
- - `EMBED_DEVICE` / `EMBED_DTYPE` — overrides local `inference.embedding.device` / `inference.embedding.dtype`
488
- - `RERANK_ENDPOINT` / `SKILLMUX_RERANK_ENDPOINT` — overrides the complete remote `inference.reranker.endpoint`
489
- - `RERANK_ADAPTER` / `SKILLMUX_RERANK_ADAPTER` — overrides `inference.reranker.adapter`
490
- - `RERANK_MODEL` / `SKILLMUX_RERANK_MODEL` — overrides `inference.reranker.model`
491
- - `SKILLMUX_CONFIG` — path to custom `config.toml` (default `~/.config/skillmux/config.toml`)
492
- - `SKILLMUX_MODELS_DIR` — path to directory storing downloaded local models (default `~/.cache/skillmux/models`, `/models` inside Docker)
493
- - `PORT` — HTTP listen port (default `3000`, HTTP transport only)
494
- - `HTTP_HOSTNAME` — overrides `server.hostname` (default `127.0.0.1`, `0.0.0.0` inside Docker)
495
- - `HTTP_AUTH_ENABLED` — overrides `server.auth_enabled` (`"true"` to enable)
496
- - `HTTP_AUTH_TOKEN_ENV` — overrides `server.auth_token_env`
497
- - `HTTP_ALLOWED_ORIGINS` — comma-separated list, overrides `server.allowed_origins`
498
- - `HTTP_RATE_LIMIT_ENABLED` / `SKILLMUX_HTTP_RATE_LIMIT_ENABLED` — overrides `server.rate_limit.enabled` (`"true"` to enable)
499
- - `HTTP_RATE_LIMIT_RPM` / `SKILLMUX_HTTP_RATE_LIMIT_RPM` — overrides `server.rate_limit.requests_per_minute`
500
- - `HTTP_RATE_LIMIT_TRUST_PROXY` / `SKILLMUX_HTTP_RATE_LIMIT_TRUST_PROXY` — overrides `server.rate_limit.trust_proxy` (`"true"` to trust `X-Forwarded-For`)
501
-
502
- Remote API keys are read independently from the environment variables named by
503
- `inference.embedding.api_key_env` and `inference.reranker.api_key_env`. Omit
504
- `api_key_env` for an intentionally unauthenticated endpoint. If it is present,
505
- the named variable must be non-empty before clients are created and is sent as
506
- a Bearer token. Secret values never live in the config file or diagnostics.
507
-
508
- Rerankers use an exact endpoint plus an explicit adapter. `jina-v1` sends
509
- string documents; `bifrost-v1` sends Bifrost document objects. Skillmux never
510
- infers an adapter from the URL and never adds or removes path components.
511
-
512
- Embeddings likewise use an exact endpoint and the OpenAI-compatible
513
- `{ model, input }` contract. Skillmux never adds `/v1/embeddings` or rewrites
514
- the configured path or query string. Replace the removed `base_url` setting
515
- with the complete endpoint, for example `base_url = "http://host"` or
516
- `base_url = "http://host/v1"` becomes
517
- `endpoint = "http://host/v1/embeddings"`. The removed `EMBED_BASE_URL`,
518
- `SKILLMUX_EMBED_BASE_URL`, and `SKILL_ROUTER_EMBED_BASE_URL` variables are
519
- startup errors with migration guidance.
520
-
521
- > **Breaking reranker migration:** replace `provider = "infinity"` with
522
- > `adapter = "jina-v1"`, and replace `base_url` with the complete `endpoint`.
523
- > The old client appended `/rerank`. For example,
524
- > `base_url = "http://host/v1"` becomes
525
- > `endpoint = "http://host/v1/rerank"`; a bare `http://host` becomes
526
- > `http://host/rerank`. The removed `RERANK_BASE_URL`,
527
- > `SKILLMUX_RERANK_BASE_URL`, and `SKILL_ROUTER_RERANK_BASE_URL` variables are
528
- > startup errors with migration guidance.
529
-
530
- Verified reranker contracts:
531
-
532
- | Implementation | Tested version | Endpoint | Adapter | Verification |
533
- | ------------------- | -------------: | -------------------- | ------------ | ----------------------------------------------------------------- |
534
- | Bifrost | 1.6.6 | `/v1/rerank` | `bifrost-v1` | Live end-to-end request and recorded contract fixture, 2026-07-28 |
535
- | Jina-style contract | fixture | exact configured URL | `jina-v1` | Automated request/response contract suite |
536
-
537
- ## Benchmarks & Evaluation
538
-
539
- Skillmux includes a built-in evaluation framework to benchmark retrieval accuracy (lexical vs. hybrid vector search) against labeled intent datasets.
540
-
541
- Evaluate lexical and local hybrid retrieval against the checked-in labeled queries:
542
-
543
- ```sh
544
- bun run src/cli.ts eval
545
- # holdout queries: 8
546
- # lexical recall@5: 1.000
547
- # hybrid recall@5: 1.000
178
+ ```json
179
+ {
180
+ "mcpServers": {
181
+ "skillmux": {
182
+ "command": "skillmux",
183
+ "args": ["serve"]
184
+ }
185
+ }
186
+ }
548
187
  ```
549
188
 
550
- Custom policy calibration can also be performed against reviewed,
551
- domain-specific query datasets using `skillmux calibrate`. See the
552
- [calibration guide](docs/calibration.md) for the full operator lifecycle and
553
- the [CLI reference](docs/cli.md#policy-calibration-skillmux-calibrate) for
554
- command syntax.
555
-
556
- ## FAQ & Troubleshooting
557
-
558
- <details>
559
- <summary><b>Why did my query return <code>ambiguous</code> instead of <code>matched</code>?</b></summary>
560
-
561
- <br>
562
-
563
- 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 5 candidate skill summaries (`skill_id`, `title`, `description`) are returned so the calling LLM can choose the exact skill and invoke `fetch_skill`.
564
-
565
- </details>
566
-
567
- <details>
568
- <summary><b>Does skillmux require an active internet connection?</b></summary>
569
-
570
- <br>
571
-
572
- 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.
189
+ Skillmux exposes two tools:
573
190
 
574
- </details>
191
+ | Tool | Input | Result |
192
+ | --- | --- | --- |
193
+ | `resolve_skill` | Natural-language task description | A matched skill, an ordered shortlist, or no match |
194
+ | `fetch_skill` | Exact `skill_id` | The current `SKILL.md` body, SHA-256 digest, and supporting-file paths |
575
195
 
576
- <details>
577
- <summary><b>What happens when remote embedding or reranking endpoints fail?</b></summary>
196
+ Skillmux uses the best available capability:
578
197
 
579
- <br>
198
+ 1. SQLite FTS5 provides lexical retrieval and offline fallback.
199
+ 2. Local or remote embeddings add semantic recall.
200
+ 3. An optional reranker orders candidates and can produce calibrated automatic matches.
580
201
 
581
- 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.
202
+ Without calibrated reranker thresholds, Skillmux returns an ordered shortlist and lets the calling model choose. Endpoint failures degrade to a healthy lower retrieval mode instead of taking the MCP server down.
582
203
 
583
- </details>
204
+ Read [MCP routing](docs/mcp-routing.md) for transports, outcomes, client instructions, retrieval modes, and the wire contract.
584
205
 
585
- <details>
586
- <summary><b>When should I use routed skills vs. pinned skills?</b></summary>
206
+ ## Supported clients
587
207
 
588
- <br>
208
+ | Client | Native skill delivery | MCP setup |
209
+ | --- | --- | --- |
210
+ | Claude Code | `~/.claude/skills` | Configure in the client |
211
+ | Codex | `$CODEX_HOME/skills` or `~/.codex/skills` | Configure in the client |
212
+ | Gemini CLI, OpenCode, GitHub Copilot, Windsurf | `~/.agents/skills` | Configure in the client |
213
+ | Antigravity | `~/.gemini/config/skills` | Configure in the client |
214
+ | Goose, Hermes | Manual full-vault setup | Manual registration |
215
+ | Custom clients | Any directory through a custom target | Stdio or Streamable HTTP |
589
216
 
590
- - **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.
591
- - **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.
592
-
593
- </details>
594
-
595
- <details>
596
- <summary><b>How do I verify server readiness and routing health?</b></summary>
597
-
598
- <br>
599
-
600
- 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.
601
-
602
- </details>
217
+ Skillmux preserves existing instruction files and unmanaged target content. Run `skillmux init --dry-run` to inspect every planned filesystem change.
603
218
 
604
219
  ## Guarantees
605
220
 
606
- - **Read-only vault** no code path writes under the vault; all state is confined to `state_dir`. Covered by tests.
607
- - **Zero-loss delivery** delivered bytes always hash-match the file on disk at delivery time.
608
- - **Live index** a running server folds vault changes (create/modify/delete) into the index within seconds; an unparseable write keeps the previous good entry rather than evicting the skill.
609
- - **Audit log** every `resolve_skill` call is appended to a SQLite table in `state_dir` (timestamp, query, outcome, candidates with scores, latency).
221
+ - **Controlled sources:** pins come from the configured vault checkout, while routed delivery follows the configured overlay order.
222
+ - **Scoped writes:** management commands write only to documented config, vault, state, and adopted target paths.
223
+ - **Managed ownership:** sync removes only entries recorded in the target's `.skillmux` marker.
224
+ - **Current bytes:** MCP delivery hashes the file on disk and never serves a stale indexed body.
225
+ - **Graceful retrieval:** embedding and reranker failures fall back without hiding the active capability.
226
+ - **Auditable decisions:** each `resolve_skill` call records its outcome, candidates, scores, and latency in the state database.
227
+
228
+ ## Documentation
229
+
230
+ Start with the [documentation hub](docs/README.md).
231
+
232
+ | Guide | Covers |
233
+ | --- | --- |
234
+ | [Getting started](docs/getting-started.md) | Native management, local MCP, and shared-service recipes |
235
+ | [Concepts](docs/concepts.md) | Delivery tiers, deployment topologies, retrieval modes, and ownership |
236
+ | [Managing skills](docs/skill-management.md) | Install, scan, pin, sync, report, overlays, and recovery |
237
+ | [MCP routing](docs/mcp-routing.md) | Tools, outcomes, transports, retrieval, fallback, and integrity |
238
+ | [Deployment](docs/deployment.md) | Docker, HTTP, auth, CORS, rate limits, health, and metrics |
239
+ | [Configuration](docs/configuration.md) | Machine config, inference, manifests, and overlays |
240
+ | [CLI reference](docs/cli.md) | Commands, contexts, automation, JSON output, and exit codes |
241
+ | [Policy calibration](docs/calibration.md) | Labelled datasets, certification, and threshold application |
242
+ | [Troubleshooting](docs/troubleshooting.md) | `doctor`, common failures, and migration notes |
243
+ | [MCP schema](docs/schema.json) | JSON Schema 2020-12 tool contract |
610
244
 
611
245
  ## Development
612
246
 
247
+ Skillmux uses Bun for development:
248
+
613
249
  ```sh
614
- bun test # full suite (contract, hybrid recall, stdio e2e, watcher, eval)
615
- bun run build # single-file binary via bun build --compile
250
+ bun install --frozen-lockfile
251
+ bun test
252
+ bun run build
616
253
  ```
617
254
 
618
- Reference: [`docs/configuration.md`](docs/configuration.md) and [`docs/schema.json`](docs/schema.json).
255
+ Read [CONTRIBUTING.md](CONTRIBUTING.md) for the development workflow and [SECURITY.md](SECURITY.md) for vulnerability reporting.
256
+
257
+ ## License
258
+
259
+ [MIT](LICENSE)