@klhapp/skillmux 1.7.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,20 @@ 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
+ ## [1.8.0](https://github.com/klhq/skillmux/compare/v1.7.1...v1.8.0) (2026-08-28)
9
+
10
+
11
+ ### Added
12
+
13
+ * **audit:** add fetch-outcome routing quality flywheel ([#131](https://github.com/klhq/skillmux/issues/131)) ([c55e5ec](https://github.com/klhq/skillmux/commit/c55e5ec794453ccbdbf7ffe9263684356d2a71de))
14
+
15
+ ## [1.7.1](https://github.com/klhq/skillmux/compare/v1.7.0...v1.7.1) (2026-08-21)
16
+
17
+
18
+ ### Changed
19
+
20
+ * **config:** neutralize migration error wording ([#128](https://github.com/klhq/skillmux/issues/128)) ([1fb130e](https://github.com/klhq/skillmux/commit/1fb130e3a6c3c3c084ca183c67ab7c8f984595a7))
21
+
8
22
  ## [1.7.0](https://github.com/klhq/skillmux/compare/v1.6.0...v1.7.0) (2026-08-21)
9
23
 
10
24
 
package/README.md CHANGED
@@ -1,5 +1,8 @@
1
1
  <p align="center">
2
- <img src="https://raw.githubusercontent.com/klhq/skillmux/main/docs/assets/logo.png" alt="skillmux" width="400">
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/klhq/skillmux/main/docs/assets/logo-dark.png">
4
+ <img src="https://raw.githubusercontent.com/klhq/skillmux/main/docs/assets/logo-light.png" alt="skillmux" width="400">
5
+ </picture>
3
6
  </p>
4
7
 
5
8
  <p align="center">
@@ -9,9 +12,12 @@
9
12
  <a href="LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-blue.svg"></a>
10
13
  </p>
11
14
 
12
- Skillmux manages [`SKILL.md`](https://agentskills.io) collections across AI
13
- coding clients. Keep one **vault source of truth**—the logical skill
14
- collection—pin a small set into native skill directories, and retrieve the
15
+ <p align="center"><strong>One skill vault. Every AI coding client. Nothing lost in translation.</strong></p>
16
+
17
+ Every AI coding client wants its own skill folder and its own format. Skillmux
18
+ manages [`SKILL.md`](https://agentskills.io) collections across all of them
19
+ from one place. Keep one **vault source of truth** (the logical skill
20
+ collection), pin a small set into native skill directories, and retrieve the
15
21
  rest through MCP. A **vault checkout** is a physical copy of that collection.
16
22
  On one machine, `~/skills` can be both the source of truth and its checkout.
17
23
 
@@ -55,7 +61,10 @@ command to run; see the [container command contract](docs/deployment.md#containe
55
61
  ## One vault source of truth, three ways to use it
56
62
 
57
63
  <p align="center">
58
- <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%">
64
+ <picture>
65
+ <source media="(prefers-color-scheme: dark)" srcset="docs/assets/architecture-dark.svg">
66
+ <img src="docs/assets/architecture-light.svg" alt="Three ways to use Skillmux: manage native skills, add local MCP retrieval, or run a shared MCP service" width="100%">
67
+ </picture>
59
68
  </p>
60
69
 
61
70
  “Local” describes where Skillmux runs. “Local inference” means the embedding
@@ -0,0 +1,160 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 720" role="img" aria-labelledby="title desc">
2
+ <title id="title">One vault source of truth, three ways to use Skillmux</title>
3
+ <desc id="desc">One Git-backed vault source of truth holds one SKILL.md per child directory and feeds a client checkout and a server checkout. On a client machine, Skillmux CLI manages native skills and serves local stdio MCP; a server checkout serves shared Streamable HTTP MCP. resolve_skill fuses lexical and vector results and optionally reranks them; fetch_skill returns the current SKILL.md bytes with a SHA-256. Git and deployment own checkout replication and freshness.</desc>
4
+
5
+ <defs>
6
+ <filter id="shadow" x="-15%" y="-15%" width="130%" height="140%">
7
+ <feDropShadow dx="0" dy="5" stdDeviation="8" flood-color="#000000" flood-opacity="0.35"/>
8
+ </filter>
9
+ <marker id="arrow-teal" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto">
10
+ <path d="M0 0L10 5L0 10Z" fill="#2dd4bf"/>
11
+ </marker>
12
+ <marker id="arrow-blue" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto">
13
+ <path d="M0 0L10 5L0 10Z" fill="#60a5fa"/>
14
+ </marker>
15
+ <marker id="arrow-violet" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto">
16
+ <path d="M0 0L10 5L0 10Z" fill="#a78bfa"/>
17
+ </marker>
18
+ <style>
19
+ text {
20
+ font-family: "DejaVu Sans", Arial, sans-serif;
21
+ }
22
+ .title {
23
+ font-size: 27px;
24
+ font-weight: 750;
25
+ fill: #e2e8f0;
26
+ }
27
+ .subtitle {
28
+ font-size: 16px;
29
+ font-weight: 400;
30
+ fill: #94a3b8;
31
+ }
32
+ .card-title {
33
+ font-size: 21px;
34
+ font-weight: 750;
35
+ fill: #e2e8f0;
36
+ }
37
+ .label {
38
+ font-size: 15px;
39
+ font-weight: 650;
40
+ fill: #cbd5e1;
41
+ }
42
+ .body {
43
+ font-size: 14px;
44
+ font-weight: 400;
45
+ fill: #94a3b8;
46
+ }
47
+ .mono {
48
+ font-family: "DejaVu Sans Mono", Consolas, monospace;
49
+ font-size: 14px;
50
+ font-weight: 650;
51
+ fill: #cbd5e1;
52
+ }
53
+ .mono-light {
54
+ fill: #ffffff;
55
+ }
56
+ .chip {
57
+ font-size: 12px;
58
+ font-weight: 750;
59
+ letter-spacing: .5px;
60
+ }
61
+ </style>
62
+ </defs>
63
+
64
+ <rect width="1200" height="720" rx="24" fill="#0d1117"/>
65
+ <rect x="1" y="1" width="1198" height="718" rx="23" fill="none" stroke="#30363d" stroke-width="2"/>
66
+
67
+ <text x="48" y="50" class="title">One vault source of truth, three ways to use Skillmux</text>
68
+ <text x="48" y="78" class="subtitle">Git and deployment replicate checkouts; Skillmux uses each checkout where it runs.</text>
69
+
70
+ <g filter="url(#shadow)">
71
+ <rect x="280" y="106" width="640" height="118" rx="18" fill="#161b22" stroke="#30363d" stroke-width="2"/>
72
+ <circle cx="324" cy="147" r="18" fill="#334155"/>
73
+ <path d="M314 141H327L333 147L327 153H314M320 135V159" fill="none" stroke="#ffffff" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
74
+ <text x="358" y="144" class="card-title">Git-backed vault source of truth</text>
75
+ <text x="358" y="172" class="mono">SKILL.md per child directory</text>
76
+ <text x="358" y="200" class="body">A checkout is a physical copy of it on one machine.</text>
77
+ <rect x="751" y="155" width="137" height="34" rx="9" fill="#1e293b" stroke="#475569"/>
78
+ <text x="819.5" y="177" text-anchor="middle" class="mono">skillmux.toml</text>
79
+ </g>
80
+
81
+ <path d="M520 224V252H410V277" fill="none" stroke="#2dd4bf" stroke-width="3" marker-end="url(#arrow-teal)"/>
82
+ <path d="M720 224V252H980V277" fill="none" stroke="#a78bfa" stroke-width="3" marker-end="url(#arrow-violet)"/>
83
+ <text x="394" y="270" text-anchor="end" class="body">client checkout</text>
84
+ <text x="996" y="270" text-anchor="start" class="body">server checkout</text>
85
+
86
+ <g filter="url(#shadow)">
87
+ <rect x="40" y="284" width="740" height="302" rx="18" fill="#161b22" stroke="#0f766e" stroke-width="2"/>
88
+ <rect x="62" y="306" width="124" height="26" rx="13" fill="#134e4a"/>
89
+ <text x="124" y="324" text-anchor="middle" class="chip" fill="#5eead4">SKILLMUX CLI</text>
90
+ <text x="62" y="365" class="card-title">On one client machine</text>
91
+ <text x="62" y="390" class="body">Manage native skills, add local MCP retrieval, or do both.</text>
92
+
93
+ <rect x="62" y="414" width="276" height="48" rx="10" fill="#0f2e2b" stroke="#0f766e"/>
94
+ <circle cx="85" cy="438" r="11" fill="#2dd4bf"/>
95
+ <text x="85" y="442.5" text-anchor="middle" style="font-size: 13px; font-weight: 750" fill="#0d1117">1</text>
96
+ <text x="200" y="433" text-anchor="middle" class="label" style="font-size: 17px">Manage native skills</text>
97
+ <text x="200" y="453" text-anchor="middle" class="mono">skillmux init · sync</text>
98
+ <path d="M200 462V487" fill="none" stroke="#2dd4bf" stroke-width="2.5" marker-end="url(#arrow-teal)"/>
99
+ <rect x="62" y="493" width="276" height="49" rx="10" fill="#1e293b" stroke="#475569"/>
100
+ <text x="200" y="514" text-anchor="middle" class="label">Native skill directories</text>
101
+ <text x="200" y="533" text-anchor="middle" class="body">managed core and project links</text>
102
+
103
+ <text x="62" y="570" class="body">Install Skillmux CLI with</text>
104
+ <rect x="270" y="550" width="85" height="27" rx="8" fill="#0f172a" stroke="#334155"/>
105
+ <text x="312.5" y="568" text-anchor="middle" class="chip" style="font-size: 10px" fill="#ffffff">BUN PACKAGE</text>
106
+ <rect x="361" y="550" width="87" height="27" rx="8" fill="#1e293b" stroke="#475569"/>
107
+ <text x="404.5" y="561" text-anchor="middle" class="chip" style="font-size: 10px" fill="#cbd5e1"><tspan x="404.5">LINUX</tspan><tspan x="404.5" dy="11">EXECUTABLE</tspan></text>
108
+ </g>
109
+
110
+ <g filter="url(#shadow)">
111
+ <rect x="420" y="414" width="338" height="128" rx="10" fill="#172554" stroke="#3b82f6"/>
112
+ <circle cx="453" cy="435" r="11" fill="#60a5fa"/>
113
+ <text x="453" y="439.5" text-anchor="middle" style="font-size: 13px; font-weight: 750" fill="#0d1117">2</text>
114
+ <text x="476" y="441" class="label" style="font-size: 17px">Add local MCP retrieval</text>
115
+ <text x="442" y="463" class="body">Reaches the whole indexed vault on demand.</text>
116
+
117
+ <rect x="442" y="475" width="92" height="48" rx="10" fill="#1e293b" stroke="#475569"/>
118
+ <text x="488" y="505" text-anchor="middle" class="label">AI client</text>
119
+ <path d="M534 499H578" fill="none" stroke="#60a5fa" stroke-width="2.5" marker-end="url(#arrow-blue)"/>
120
+ <text x="559" y="489" text-anchor="middle" class="body">stdio</text>
121
+ <rect x="584" y="470" width="152" height="62" rx="10" fill="#161b22" stroke="#3b82f6"/>
122
+ <text x="660" y="492" text-anchor="middle" class="mono">skillmux serve</text>
123
+ <text x="660" y="511" text-anchor="middle" class="body">default: FTS5 +</text>
124
+ <text x="660" y="527" text-anchor="middle" class="body">local GTE-small</text>
125
+
126
+ </g>
127
+
128
+ <g filter="url(#shadow)">
129
+ <rect x="800" y="284" width="360" height="302" rx="18" fill="#161b22" stroke="#7c3aed" stroke-width="2"/>
130
+ <rect x="822" y="306" width="136" height="26" rx="13" fill="#312e81"/>
131
+ <text x="890" y="324" text-anchor="middle" class="chip" fill="#c4b5fd">SKILLMUX SERVER</text>
132
+ <circle cx="833" cy="358" r="11" fill="#a78bfa"/>
133
+ <text x="833" y="362.5" text-anchor="middle" style="font-size: 13px; font-weight: 750" fill="#0d1117">3</text>
134
+ <text x="856" y="365" class="card-title">Run a shared MCP service</text>
135
+ <text x="822" y="390" class="body">Read a server checkout to serve several clients.</text>
136
+
137
+ <rect x="822" y="414" width="92" height="48" rx="10" fill="#1e293b" stroke="#475569"/>
138
+ <text x="868" y="435" text-anchor="middle" class="label">AI clients</text>
139
+ <text x="868" y="453" text-anchor="middle" class="body">one or more</text>
140
+ <path d="M914 438H963" fill="none" stroke="#a78bfa" stroke-width="2.5" marker-end="url(#arrow-violet)"/>
141
+ <text x="939" y="428" text-anchor="middle" class="body">HTTP</text>
142
+ <rect x="969" y="414" width="169" height="48" rx="10" fill="#2e1065" stroke="#7c3aed"/>
143
+ <text x="1053.5" y="435" text-anchor="middle" class="label">Skillmux server</text>
144
+ <text x="1053.5" y="453" text-anchor="middle" class="body" style="font-size: 13px">Streamable HTTP /mcp</text>
145
+
146
+ <rect x="822" y="484" width="148" height="58" rx="10" fill="#2e1065" stroke="#7c3aed"/>
147
+ <text x="896" y="507" text-anchor="middle" class="label">Full image</text>
148
+ <text x="896" y="530" text-anchor="middle" class="body">GTE-small included</text>
149
+ <rect x="982" y="484" width="156" height="58" rx="10" fill="#1e293b" stroke="#475569"/>
150
+ <text x="1060" y="507" text-anchor="middle" class="label">Slim image</text>
151
+ <text x="1060" y="530" text-anchor="middle" class="body">remote or lexical</text>
152
+
153
+ <text x="822" y="570" class="body">Full image is the default deployment.</text>
154
+ </g>
155
+
156
+ <rect x="420" y="608" width="740" height="82" rx="14" fill="#1e293b"/>
157
+ <text x="444" y="633" class="chip" fill="#cbd5e1">MCP TOOL CONTRACT · STDIO + HTTP</text>
158
+ <text x="444" y="655" class="mono mono-light" style="font-size: 12px">resolve_skill → lexical + vector → RRF → optional rerank → top_k → ranked candidates</text>
159
+ <text x="444" y="677" class="mono mono-light" style="font-size: 12px">fetch_skill → current SKILL.md bytes + SHA-256</text>
160
+ </svg>
@@ -1,6 +1,6 @@
1
1
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 720" role="img" aria-labelledby="title desc">
2
2
  <title id="title">One vault source of truth, three ways to use Skillmux</title>
3
- <desc id="desc">One Git-backed vault source of truth has a client checkout for Skillmux CLI native management and local MCP retrieval, plus a server checkout for shared Streamable HTTP MCP. Resolve uses retrieval, fusion, and optional reranking; fetch returns the current SKILL.md directly. Git and deployment own checkout replication and freshness.</desc>
3
+ <desc id="desc">One Git-backed vault source of truth holds one SKILL.md per child directory and feeds a client checkout and a server checkout. On a client machine, Skillmux CLI manages native skills and serves local stdio MCP; a server checkout serves shared Streamable HTTP MCP. resolve_skill fuses lexical and vector results and optionally reranks them; fetch_skill returns the current SKILL.md bytes with a SHA-256. Git and deployment own checkout replication and freshness.</desc>
4
4
 
5
5
  <defs>
6
6
  <filter id="shadow" x="-15%" y="-15%" width="130%" height="140%">
@@ -68,14 +68,14 @@
68
68
  <text x="48" y="78" class="subtitle">Git and deployment replicate checkouts; Skillmux uses each checkout where it runs.</text>
69
69
 
70
70
  <g filter="url(#shadow)">
71
- <rect x="330" y="106" width="540" height="118" rx="18" fill="#ffffff" stroke="#cbd5e1" stroke-width="2"/>
72
- <circle cx="374" cy="147" r="18" fill="#0f172a"/>
73
- <path d="M364 141H377L383 147L377 153H364M370 135V159" fill="none" stroke="#ffffff" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
74
- <text x="408" y="144" class="card-title">Git-backed vault source of truth</text>
75
- <text x="408" y="172" class="mono">logical SKILL.md collection</text>
76
- <text x="408" y="200" class="body">Git + deployment own replication and freshness</text>
77
- <rect x="701" y="139" width="137" height="34" rx="9" fill="#f1f5f9" stroke="#cbd5e1"/>
78
- <text x="769.5" y="161" text-anchor="middle" class="mono">skillmux.toml</text>
71
+ <rect x="280" y="106" width="640" height="118" rx="18" fill="#ffffff" stroke="#cbd5e1" stroke-width="2"/>
72
+ <circle cx="324" cy="147" r="18" fill="#0f172a"/>
73
+ <path d="M314 141H327L333 147L327 153H314M320 135V159" fill="none" stroke="#ffffff" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
74
+ <text x="358" y="144" class="card-title">Git-backed vault source of truth</text>
75
+ <text x="358" y="172" class="mono">SKILL.md per child directory</text>
76
+ <text x="358" y="200" class="body">A checkout is a physical copy of it on one machine.</text>
77
+ <rect x="751" y="155" width="137" height="34" rx="9" fill="#f1f5f9" stroke="#cbd5e1"/>
78
+ <text x="819.5" y="177" text-anchor="middle" class="mono">skillmux.toml</text>
79
79
  </g>
80
80
 
81
81
  <path d="M520 224V252H410V277" fill="none" stroke="#0f766e" stroke-width="3" marker-end="url(#arrow-teal)"/>
@@ -87,11 +87,13 @@
87
87
  <rect x="40" y="284" width="740" height="302" rx="18" fill="#ffffff" stroke="#99d5cc" stroke-width="2"/>
88
88
  <rect x="62" y="306" width="124" height="26" rx="13" fill="#ccfbf1"/>
89
89
  <text x="124" y="324" text-anchor="middle" class="chip" fill="#115e59">SKILLMUX CLI</text>
90
- <text x="62" y="365" class="card-title">Skillmux CLI</text>
91
- <text x="62" y="390" class="body">A client-machine checkout supports native management and/or local MCP.</text>
90
+ <text x="62" y="365" class="card-title">On one client machine</text>
91
+ <text x="62" y="390" class="body">Manage native skills, add local MCP retrieval, or do both.</text>
92
92
 
93
93
  <rect x="62" y="414" width="276" height="48" rx="10" fill="#f0fdfa" stroke="#99d5cc"/>
94
- <text x="200" y="435" text-anchor="middle" class="label">Native management</text>
94
+ <circle cx="85" cy="438" r="11" fill="#0f766e"/>
95
+ <text x="85" y="442.5" text-anchor="middle" style="font-size: 13px; font-weight: 750" fill="#ffffff">1</text>
96
+ <text x="200" y="433" text-anchor="middle" class="label" style="font-size: 17px">Manage native skills</text>
95
97
  <text x="200" y="453" text-anchor="middle" class="mono">skillmux init · sync</text>
96
98
  <path d="M200 462V487" fill="none" stroke="#0f766e" stroke-width="2.5" marker-end="url(#arrow-teal)"/>
97
99
  <rect x="62" y="493" width="276" height="49" rx="10" fill="#f8fafc" stroke="#cbd5e1"/>
@@ -107,17 +109,19 @@
107
109
 
108
110
  <g filter="url(#shadow)">
109
111
  <rect x="420" y="414" width="338" height="128" rx="10" fill="#eff6ff" stroke="#93c5fd"/>
110
- <text x="442" y="441" class="label">Local MCP retrieval</text>
111
- <text x="442" y="463" class="body">Skillmux CLI beside one MCP client.</text>
112
-
113
- <rect x="442" y="475" width="102" height="48" rx="10" fill="#f8fafc" stroke="#cbd5e1"/>
114
- <text x="493" y="505" text-anchor="middle" class="label">AI client</text>
115
- <path d="M544 499H573" fill="none" stroke="#2563eb" stroke-width="2.5" marker-end="url(#arrow-blue)"/>
116
- <text x="558" y="489" text-anchor="middle" class="body">stdio</text>
117
- <rect x="579" y="470" width="157" height="62" rx="10" fill="#ffffff" stroke="#93c5fd"/>
118
- <text x="657.5" y="492" text-anchor="middle" class="label">Skillmux CLI</text>
119
- <text x="657.5" y="511" text-anchor="middle" class="body">default: FTS5 +</text>
120
- <text x="657.5" y="527" text-anchor="middle" class="body">local GTE-small</text>
112
+ <circle cx="453" cy="435" r="11" fill="#2563eb"/>
113
+ <text x="453" y="439.5" text-anchor="middle" style="font-size: 13px; font-weight: 750" fill="#ffffff">2</text>
114
+ <text x="476" y="441" class="label" style="font-size: 17px">Add local MCP retrieval</text>
115
+ <text x="442" y="463" class="body">Reaches the whole indexed vault on demand.</text>
116
+
117
+ <rect x="442" y="475" width="92" height="48" rx="10" fill="#f8fafc" stroke="#cbd5e1"/>
118
+ <text x="488" y="505" text-anchor="middle" class="label">AI client</text>
119
+ <path d="M534 499H578" fill="none" stroke="#2563eb" stroke-width="2.5" marker-end="url(#arrow-blue)"/>
120
+ <text x="559" y="489" text-anchor="middle" class="body">stdio</text>
121
+ <rect x="584" y="470" width="152" height="62" rx="10" fill="#ffffff" stroke="#93c5fd"/>
122
+ <text x="660" y="492" text-anchor="middle" class="mono">skillmux serve</text>
123
+ <text x="660" y="511" text-anchor="middle" class="body">default: FTS5 +</text>
124
+ <text x="660" y="527" text-anchor="middle" class="body">local GTE-small</text>
121
125
 
122
126
  </g>
123
127
 
@@ -125,17 +129,19 @@
125
129
  <rect x="800" y="284" width="360" height="302" rx="18" fill="#ffffff" stroke="#c4b5fd" stroke-width="2"/>
126
130
  <rect x="822" y="306" width="136" height="26" rx="13" fill="#ede9fe"/>
127
131
  <text x="890" y="324" text-anchor="middle" class="chip" fill="#6d28d9">SKILLMUX SERVER</text>
128
- <text x="822" y="365" class="card-title">Run a shared MCP service</text>
132
+ <circle cx="833" cy="358" r="11" fill="#7c3aed"/>
133
+ <text x="833" y="362.5" text-anchor="middle" style="font-size: 13px; font-weight: 750" fill="#ffffff">3</text>
134
+ <text x="856" y="365" class="card-title">Run a shared MCP service</text>
129
135
  <text x="822" y="390" class="body">Read a server checkout to serve several clients.</text>
130
136
 
131
- <rect x="822" y="414" width="102" height="48" rx="10" fill="#f8fafc" stroke="#cbd5e1"/>
132
- <text x="873" y="435" text-anchor="middle" class="label">AI clients</text>
133
- <text x="873" y="453" text-anchor="middle" class="body">one or more</text>
134
- <path d="M924 438H953" fill="none" stroke="#7c3aed" stroke-width="2.5" marker-end="url(#arrow-violet)"/>
135
- <text x="938" y="428" text-anchor="middle" class="body">HTTP</text>
136
- <rect x="959" y="414" width="179" height="48" rx="10" fill="#f5f3ff" stroke="#c4b5fd"/>
137
- <text x="1048.5" y="435" text-anchor="middle" class="label">Skillmux server</text>
138
- <text x="1048.5" y="453" text-anchor="middle" class="body" style="font-size: 13px">Streamable HTTP /mcp</text>
137
+ <rect x="822" y="414" width="92" height="48" rx="10" fill="#f8fafc" stroke="#cbd5e1"/>
138
+ <text x="868" y="435" text-anchor="middle" class="label">AI clients</text>
139
+ <text x="868" y="453" text-anchor="middle" class="body">one or more</text>
140
+ <path d="M914 438H963" fill="none" stroke="#7c3aed" stroke-width="2.5" marker-end="url(#arrow-violet)"/>
141
+ <text x="939" y="428" text-anchor="middle" class="body">HTTP</text>
142
+ <rect x="969" y="414" width="169" height="48" rx="10" fill="#f5f3ff" stroke="#c4b5fd"/>
143
+ <text x="1053.5" y="435" text-anchor="middle" class="label">Skillmux server</text>
144
+ <text x="1053.5" y="453" text-anchor="middle" class="body" style="font-size: 13px">Streamable HTTP /mcp</text>
139
145
 
140
146
  <rect x="822" y="484" width="148" height="58" rx="10" fill="#f5f3ff" stroke="#c4b5fd"/>
141
147
  <text x="896" y="507" text-anchor="middle" class="label">Full image</text>
@@ -144,11 +150,11 @@
144
150
  <text x="1060" y="507" text-anchor="middle" class="label">Slim image</text>
145
151
  <text x="1060" y="530" text-anchor="middle" class="body">remote or lexical</text>
146
152
 
147
- <text x="822" y="570" class="body">Recommended for a shared HTTP deployment</text>
153
+ <text x="822" y="570" class="body">Full image is the default deployment.</text>
148
154
  </g>
149
155
 
150
156
  <rect x="420" y="608" width="740" height="82" rx="14" fill="#0f172a"/>
151
157
  <text x="444" y="633" class="chip" fill="#cbd5e1">MCP TOOL CONTRACT · STDIO + HTTP</text>
152
158
  <text x="444" y="655" class="mono mono-light" style="font-size: 12px">resolve_skill → lexical + vector → RRF → optional rerank → top_k → ranked candidates</text>
153
- <text x="444" y="677" class="mono mono-light" style="font-size: 12px">fetch_skill → exact current SKILL.md + SHA-256</text>
159
+ <text x="444" y="677" class="mono mono-light" style="font-size: 12px">fetch_skill → current SKILL.md bytes + SHA-256</text>
154
160
  </svg>
Binary file
Binary file
package/docs/cli.md CHANGED
@@ -21,10 +21,11 @@ or [Managing skills](skill-management.md).
21
21
 
22
22
  The Docker image is a shared-server runtime, not a replacement for the host
23
23
  CLI. Its `skillmux --help` surface is intentionally limited to `serve`,
24
- `index`, `doctor`, `report`, `scan`, `skill which`, and read-only `config`
25
- inspection (`show`, `get`, `validate`, `diff`, and `status`). Run `init`,
26
- `install`, pinning, `sync`, project or target management, model downloads,
27
- contexts, evaluation, and configuration changes on the host.
24
+ `index`, `doctor`, `report`, `audit prune`, `eval promote`, `scan`,
25
+ `skill which`, and read-only `config` inspection (`show`, `get`, `validate`,
26
+ `diff`, and `status`). Run `init`, `install`, pinning, `sync`, project or
27
+ target management, model downloads, contexts, and bare `eval` (vault ranking
28
+ evaluation, which needs local embeddings and the vault) on the host.
28
29
 
29
30
  When the image rejects one of those commands, it exits with code 2. JSON mode
30
31
  uses `CONTAINER_COMMAND_UNSUPPORTED` and includes `rejected_command`,
@@ -247,7 +248,7 @@ command prints the preserved path so cleanup remains an explicit user action.
247
248
 
248
249
  ## Core skills (`skillmux core`)
249
250
 
250
- Pin or unpin skills into `[core]` the tier every target receives by
251
+ Pin or unpin skills into `[core]`, the tier every target receives by
251
252
  default, capped at 25 skills:
252
253
 
253
254
  ```sh
@@ -287,6 +288,63 @@ skillmux skill which csv-formatter
287
288
 
288
289
  ---
289
290
 
291
+ ## Observability and evaluation (`skillmux report`, `audit`, `eval`)
292
+
293
+ `resolve_skill` records every request to an audit log; `fetch_skill` records
294
+ what was actually opened and, when the caller passes back the `request_id`
295
+ from a prior resolve, correlates the fetch to that resolve and its rank in
296
+ the shortlist. `skillmux report` summarizes this data, `skillmux audit prune`
297
+ reclaims space, and `skillmux eval promote` turns correlated fetches into
298
+ eval cases.
299
+
300
+ ```sh
301
+ # Summarize activity from the local state or a remote server
302
+ skillmux report --since 7d
303
+ skillmux report --server https://skillmux.internal:3000 --since 24h
304
+ skillmux report --db ~/.local/state/skillmux/audit.sqlite3 --since 2026-08-01
305
+
306
+ # Prune audit rows older than the configured retention window (default 90 days)
307
+ skillmux audit prune --yes
308
+ skillmux audit prune --older-than 30d --dry-run
309
+ skillmux audit prune --older-than 30d --json
310
+
311
+ # Promote observed, correlated fetches into an eval case file
312
+ skillmux eval promote --since 7d --dry-run
313
+ skillmux eval promote --since 7d --yes
314
+ skillmux eval promote --since 7d --target eval/observed.json --yes
315
+ ```
316
+
317
+ `report` reads `--server <url>`, `--db <path>` (an explicit SQLite file,
318
+ opened read-only), or the configured local `state_dir` by default;
319
+ `--server` and `--db` are mutually exclusive. Alongside request totals,
320
+ empty-shortlist rate, retrieval-lane totals, degradation counts, and
321
+ per-skill candidate counts, `report` prints an acceptance signal derived
322
+ from correlated fetches: `acceptance_rate`, `observed_mrr` (reciprocal rank
323
+ of the first fetched candidate), and `top1_acceptance_rate`, each computed
324
+ over resolves that returned at least one candidate. When a window has no
325
+ correlated fetches, `report` marks the signal `unavailable` and states the
326
+ uncorrelated fetch count instead of printing a misleading `0.000`. It also
327
+ lists the top queries that returned candidates but received no correlated
328
+ fetch, distinct from the existing top empty-shortlist list.
329
+
330
+ `audit prune` deletes resolve and fetch rows older than `--older-than` (same
331
+ window syntax as `--since`), or `audit.retention_days` from configuration
332
+ (default 90; `0` disables pruning). `--dry-run` reports counts without
333
+ writing. Non-interactive runs require `--yes`. The server also prunes
334
+ automatically once at startup and at most once per 24 hours while running;
335
+ manual pruning is for on-demand cleanup or a tighter window.
336
+
337
+ `eval promote` reads correlated fetches since `--since`, deduplicates them by
338
+ normalized query, and writes `{ query, split: "observed", relevant_skill_ids
339
+ }` cases to `--target` (default an `eval-observed.json` file under
340
+ `state_dir`; never the hand-curated `eval/queries.json` unless given
341
+ explicitly). It never rewrites a case for a query already present in the
342
+ target file; skipped counts are reported in the summary. Because promoted
343
+ cases carry raw user queries, `eval promote` always prints a stderr warning.
344
+ Both `--dry-run` and `--yes` behave as elsewhere in the CLI.
345
+
346
+ ---
347
+
290
348
  ## Administrative HTTP API (`/admin/v1/*`)
291
349
 
292
350
  The HTTP server has two separate surfaces:
package/docs/concepts.md CHANGED
@@ -63,6 +63,16 @@ checkout over HTTP.
63
63
 
64
64
  ## Deployment topologies
65
65
 
66
+ ```mermaid
67
+ flowchart TD
68
+ V[Git-backed vault source of truth]
69
+ V -->|client checkout| CLI[Skillmux CLI]
70
+ V -->|server checkout| SRV[Skillmux server]
71
+ CLI --> NM[Native management: filesystem links]
72
+ CLI --> LM[Local MCP: stdio]
73
+ SRV --> SM[Shared MCP: Streamable HTTP]
74
+ ```
75
+
66
76
  | Topology | Process location | Transport | Typical installation |
67
77
  | --- | --- | --- | --- |
68
78
  | Native management | Client machine | Filesystem links | Skillmux CLI |
@@ -203,9 +203,9 @@ enabled = false
203
203
  token_env = "SKILLMUX_ADMIN_TOKEN"
204
204
  ```
205
205
 
206
- Defaults are loopback-only (`hostname = "127.0.0.1"`) with CORS deny-by-default (`allowed_origins = []`) a zero-config `skillmux serve --transport http` is not reachable from the network or from a browser tab on another origin. Docker sets `hostname` to `0.0.0.0` automatically (`RUNNING_IN_DOCKER=true`) since port-mapping needs the container to accept connections on all interfaces.
206
+ Defaults are loopback-only (`hostname = "127.0.0.1"`) with CORS deny-by-default (`allowed_origins = []`), so a zero-config `skillmux serve --transport http` is not reachable from the network or from a browser tab on another origin. Docker sets `hostname` to `0.0.0.0` automatically (`RUNNING_IN_DOCKER=true`) since port-mapping needs the container to accept connections on all interfaces.
207
207
 
208
- Before exposing HTTP beyond localhost, set `hostname` to a reachable interface, `auth_enabled = true` with a token, and populate `allowed_origins` with the specific origins that need browser access. `rate_limit.trust_proxy` should stay `false` unless a trusted reverse proxy sets `X-Forwarded-For` it's otherwise a client-controlled, spoofable header and trusting it defeats per-client rate limiting.
208
+ Before exposing HTTP beyond localhost, set `hostname` to a reachable interface, `auth_enabled = true` with a token, and populate `allowed_origins` with the specific origins that need browser access. `rate_limit.trust_proxy` should stay `false` unless a trusted reverse proxy sets `X-Forwarded-For`: it's otherwise a client-controlled, spoofable header, and trusting it defeats per-client rate limiting.
209
209
 
210
210
  `server.auth_token_env` names the MCP token for AI clients calling `/mcp`.
211
211
  `server.admin.token_env` names a distinct administrative token for operators
@@ -242,13 +242,13 @@ skills = ["pdf-extractor"] # must not overlap [core]
242
242
  [targets.claude-code]
243
243
  dir = "/Users/you/.claude/skills"
244
244
  host = "workhorse" # optional; init adds the current hostname
245
- project_groups = ["repo1"] # which [project.*] groups materialize into this target [] means none
245
+ project_groups = ["repo1"] # which [project.*] groups materialize into this target; [] means none
246
246
  ```
247
247
 
248
- - `[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.
249
- - `[project.<group>].skills` symlinked only into `<path>/<relative path from $HOME to the target dir>`, for each `paths` entry, and only for targets whose `project_groups` names that group. `paths` 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.
250
- - `[project.<group>].paths` can list the same project's checkout on more than one machine (e.g. `["/home/alice/code/repo1", "/Users/alice/code/repo1"]`) `sync` silently skips any entry that doesn't exist on the machine it's running on (see below), so one shared manifest can span machines with different checkout locations without needing per-machine manifests.
251
- - `[targets.<name>]` one entry per adopted surface. `skillmux init --target <name> --yes` writes these and scopes newly added targets to the current hostname. Hand-editing is fine as long as `sync` is still allowed to own the directory (see below). An optional `host` limits the target to an exact machine-hostname match; omit it for a global, backward-compatible target. A host mismatch is reported and skipped before any target filesystem operation. `project_groups` is an explicit list, not a boolean a target only receives the specific groups it names, never every group in the manifest.
248
+ - `[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.
249
+ - `[project.<group>].skills`: symlinked only into `<path>/<relative path from $HOME to the target dir>`, for each `paths` entry, and only for targets whose `project_groups` names that group. `paths` 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.
250
+ - `[project.<group>].paths` can list the same project's checkout on more than one machine (e.g. `["/home/alice/code/repo1", "/Users/alice/code/repo1"]`). `sync` silently skips any entry that doesn't exist on the machine it's running on (see below), so one shared manifest can span machines with different checkout locations without needing per-machine manifests.
251
+ - `[targets.<name>]`: one entry per adopted surface. `skillmux init --target <name> --yes` writes these and scopes newly added targets to the current hostname. Hand-editing is fine as long as `sync` is still allowed to own the directory (see below). An optional `host` limits the target to an exact machine-hostname match; omit it for a global, backward-compatible target. A host mismatch is reported and skipped before any target filesystem operation. `project_groups` is an explicit list, not a boolean: a target only receives the specific groups it names, never every group in the manifest.
252
252
 
253
253
  **Pin/unpin without hand-editing.** `skillmux core pin`/`unpin` mutate `[core]` for you, and `skillmux project pin`/`unpin` mutate `[project.*]`, validating with the same rules `sync` enforces (skill must resolve from `vault_path`, no duplicate pins, `[core]` stays under the 25-skill cap) before writing anything:
254
254
 
@@ -261,16 +261,16 @@ skillmux core unpin csv-formatter pdf-extractor --yes # unpin
261
261
  skillmux project unpin repo1 pdf-extractor --yes # remove from a group (group stays, even if empty)
262
262
  ```
263
263
 
264
- Both commands accept one or more `skill_id` arguments per call; all of them are validated and applied against a single in-memory manifest before anything is written, so if any one of them is already pinned elsewhere (or, for unpin, not currently pinned), the whole call fails and the manifest file is left untouched no partial pins. To pin into a `[project.<group>]` tier that doesn't exist yet, create it first with `skillmux project add-path <group> <path> --yes`. Hand-editing `skillmux.toml` directly is still fully supported; these commands are a convenience layer over the same file, not a replacement for it.
264
+ Both commands accept one or more `skill_id` arguments per call; all of them are validated and applied against a single in-memory manifest before anything is written, so if any one of them is already pinned elsewhere (or, for unpin, not currently pinned), the whole call fails and the manifest file is left untouched: no partial pins. To pin into a `[project.<group>]` tier that doesn't exist yet, create it first with `skillmux project add-path <group> <path> --yes`. Hand-editing `skillmux.toml` directly is still fully supported; these commands are a convenience layer over the same file, not a replacement for it.
265
265
 
266
- > **Breaking change:** `skillmux manifest pin`/`unpin` is removed. `[core]` pinning is now `skillmux core pin`/`unpin`; `[project.*]` pinning was already available as `skillmux project pin`/`unpin` and is now the only way to do it — there's no more `--path`-based inline group creation from a pin call, use `project add-path` to create the group first.
266
+ > **Breaking change:** `skillmux manifest pin`/`unpin` is removed. `[core]` pinning is now `skillmux core pin`/`unpin`; `[project.*]` pinning was already available as `skillmux project pin`/`unpin` and is now the only way to do it. There's no more `--path`-based inline group creation from a pin call; use `project add-path` to create the group first.
267
267
  >
268
268
  > **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 = []`.
269
269
  >
270
- > **Breaking change:** `[project.<group>].repos` has been renamed to `paths` it was never required to be a git repository, just a local directory, and the old name collided in meaning with `skillmux install <repo>`'s unrelated git-source `repo` concept. A manifest still using `repos` fails to parse with an error pointing at `paths`; migrate by renaming the key (values are unchanged).
270
+ > **Breaking change:** `[project.<group>].repos` has been renamed to `paths`. It was never required to be a git repository, just a local directory, and the old name collided in meaning with `skillmux install <repo>`'s unrelated git-source `repo` concept. A manifest still using `repos` fails to parse with an error pointing at `paths`; migrate by renaming the key (values are unchanged).
271
271
 
272
272
  Every `[core]`/`[project.*]` skill_id must resolve from the configured
273
- `vault_path` checkout pinning a skill that only exists in a
273
+ `vault_path` checkout. Pinning a skill that only exists in a
274
274
  `local_vault_paths` entry (see below) fails `sync` with a distinct error, since
275
275
  the manifest is meant to be portable across machines and a machine-local
276
276
  override wouldn't exist elsewhere. `doctor` validates the manifest as part of
@@ -286,7 +286,7 @@ Skillmux created. Sync removes only those tracked entries, preserves unrelated
286
286
  content, and rejects a desired skill that collides with an unmanaged entry
287
287
  before changing anything.
288
288
 
289
- `sync` refuses to touch a directory that exists but has no marker run
289
+ `sync` refuses to touch a directory that exists but has no marker; run
290
290
  `skillmux init --target <name> --yes` first, which either creates the
291
291
  directory fresh or adopts an existing one in place (contents untouched).
292
292
  `sync --restore-monolith` likewise refuses a `local_vault` marker or any
@@ -302,34 +302,34 @@ cannot be inferred.
302
302
 
303
303
  ### Local vault overlays
304
304
 
305
- `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:
305
+ `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:
306
306
 
307
307
  ```toml
308
308
  vault_path = "~/skills" # configured checkout; owns skillmux.toml and the sync git hook
309
309
  local_vault_paths = ["~/skills-local"] # optional, default []: override-only, checked first
310
310
  ```
311
311
 
312
- - **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.
312
+ - **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.
313
313
  - **`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.
314
- - **`[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.
314
+ - **`[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.
315
315
  - **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.
316
316
 
317
- **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:
317
+ **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:
318
318
 
319
- - `skillmux skill which <skill_id>` prints which root actually serves that skill, and names every root it shadows:
319
+ - `skillmux skill which <skill_id>`: prints which root actually serves that skill, and names every root it shadows:
320
320
  ```
321
321
  $ skillmux skill which my-skill
322
322
  my-skill: serving from /home/user/skills-local
323
323
  shadows: /home/user/skills
324
324
  ```
325
325
  Exits non-zero with `<skill_id>: not found in vault_path or local_vault_paths` if no root has it.
326
- - `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.
326
+ - `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.
327
327
 
328
- **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:
328
+ **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:
329
329
 
330
330
  ```sh
331
331
  skillmux local-vault init ~/skills-local
332
332
  # wrote /home/user/skills-local/.skillmux (role: local_vault, vault_path: /home/user/skills)
333
333
  ```
334
334
 
335
- `<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`).
335
+ `<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`).