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