opencode-skills-doctor 0.6.0 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +87 -495
- package/docs/README.zh-CN.md +23 -11
- package/docs/REFERENCE.md +541 -0
- package/docs/assets/banner.svg +49 -0
- package/docs/assets/report.png +0 -0
- package/docs/assets/social-preview.png +0 -0
- package/package.json +1 -1
- package/src/dsh.mjs +110 -47
- package/src/i18n.mjs +2 -2
package/docs/README.zh-CN.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/wangyuchen5110/opencodeplugginprogram/main/docs/assets/banner.svg" alt="skills-doctor" width="100%">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<b>审计、修复、更新你的 coding agent 加载的 skill。</b><br>
|
|
7
|
+
量出每步 token 税,找出遮蔽重复和断链,再安全地修掉。
|
|
8
|
+
</p>
|
|
9
|
+
|
|
1
10
|
# skills-doctor
|
|
2
11
|
|
|
12
|
+
[English](../README.md) · [完整英文参考](REFERENCE.md)
|
|
13
|
+
|
|
3
14
|
[](https://www.npmjs.com/package/opencode-skills-doctor)
|
|
4
15
|
[](https://www.npmjs.com/package/opencode-skills-doctor)
|
|
5
16
|
[](https://www.npmjs.com/package/opencode-skills-doctor)
|
|
@@ -23,7 +34,7 @@ OpenCode skill 的**本地检测器 + 更新器 + 修复器**,并附带 **Open
|
|
|
23
34
|
| **规划** | 精确 token 节省:精简描述 / 隐藏 skill,按 N 步折算 |
|
|
24
35
|
| **成本** | CLI 0 token;插件工具定义约 191/步,另有每会话一次预警 |
|
|
25
36
|
| **要求** | Node ≥ 20,零运行时依赖 |
|
|
26
|
-
| **质量** |
|
|
37
|
+
| **质量** | 58 个测试,ubuntu/macos/windows × Node 20/22/24 CI |
|
|
27
38
|
|
|
28
39
|
## 为什么需要它
|
|
29
40
|
|
|
@@ -348,22 +359,23 @@ args = ["-y", "opencode-skills-doctor", "mcp"]
|
|
|
348
359
|
npx -y opencode-skills-doctor install --client dsh
|
|
349
360
|
```
|
|
350
361
|
|
|
351
|
-
它会找到 `~/.dsh/profiles/<profile>/cordis.patch.yml`(默认 `desktop`,否则用 `--profile <name
|
|
362
|
+
它会找到 `~/.dsh/profiles/<profile>/cordis.patch.yml`(默认 `desktop`,否则用 `--profile <name>`),把 `mcp-skills-doctor` 这一行放在 `- insert:` 块里幂等写入,并把 `skills-doctor-audit` 技能复制到 `~/.dsh/skills/`。重启 DSH(启用 HMR 时会自动重载 patch)加载 MCP 工具;skill 目录有监听,无需重启。
|
|
352
363
|
|
|
353
364
|
- 工具名 `mcp__skills_doctor__skills_doctor`,参数 `directory`、`budget`、`lang`、`global`、`html`;
|
|
354
365
|
- `uninstall --client dsh` 同时移除配置条目和 skill;
|
|
355
366
|
- `--dsh-home <dir>` 用于非默认的 DSH 主目录。
|
|
356
367
|
|
|
357
|
-
手动兜底 ——
|
|
368
|
+
手动兜底 —— DSH 的 patch 里新增条目必须放在 `insert` 下,追加这一块:
|
|
358
369
|
|
|
359
370
|
```yaml
|
|
360
|
-
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
371
|
+
- insert:
|
|
372
|
+
- id: mcp-skills-doctor
|
|
373
|
+
name: "@deepseek-ai/dsh-mcp-client"
|
|
374
|
+
config:
|
|
375
|
+
serverName: skills_doctor
|
|
376
|
+
transport: stdio
|
|
377
|
+
command: npx
|
|
378
|
+
args: ["-y", "opencode-skills-doctor", "mcp"]
|
|
367
379
|
```
|
|
368
380
|
|
|
369
381
|
Windows 上如果 stdio 起不来 `npx`,改成 `command: cmd`、`args: ["/c", "npx", "-y", "opencode-skills-doctor", "mcp"]`。
|
|
@@ -517,7 +529,7 @@ token 是启发式估算,1200 是建议预算。可以用 `--budget` 或 `.ski
|
|
|
517
529
|
## 开发与贡献
|
|
518
530
|
|
|
519
531
|
```powershell
|
|
520
|
-
npm test #
|
|
532
|
+
npm test # 58 个测试(node test/doctor.test.mjs)
|
|
521
533
|
npm run bench # 100/500/1000 个 skill 的 lint 耗时
|
|
522
534
|
```
|
|
523
535
|
|
|
@@ -0,0 +1,541 @@
|
|
|
1
|
+
# skills-doctor — full reference
|
|
2
|
+
|
|
3
|
+
Local scanner, linter, fixer, updater and **OpenCode plugin** for agent skills — zero dependencies, Node ≥ 20.
|
|
4
|
+
|
|
5
|
+
[← Back to README](../README.md) · [简体中文](README.zh-CN.md) · [Changelog](../CHANGELOG.md) · [Security](../SECURITY.md)
|
|
6
|
+
|
|
7
|
+
## At a glance
|
|
8
|
+
|
|
9
|
+
| | |
|
|
10
|
+
|---|---|
|
|
11
|
+
| **What it is** | Audit, fix and update the skills your coding agent loads |
|
|
12
|
+
| **Interfaces** | CLI · OpenCode plugin (1.x / 2.x) · MCP server (Claude Code, Codex, DSH, …) · Grok CLI compatible |
|
|
13
|
+
| **Skill sources** | `.opencode/skills`, `.claude/skills`, `.agents/skills`, `.grok/skills`, `.cursor/skills`, `opencode.json(c)` entries, HTTP catalogs |
|
|
14
|
+
| **What it finds** | shadowed IDs, duplicates, long descriptions, oversized bodies, per-step token budget, broken file references, invalid frontmatter, empty bodies, stale skills |
|
|
15
|
+
| **What it fixes** | archives shadowed definitions (reversible), hides low-frequency skills, syncs catalogs with drift detection |
|
|
16
|
+
| **Reports** | terminal, JSON, or a self-contained HTML report (`--html [file] --open`) |
|
|
17
|
+
| **Plan** | exact token savings: trim descriptions and/or hide skills, projected over N steps |
|
|
18
|
+
| **Cost** | CLI: 0 tokens. Plugin: ~191 tokens/step for the tool definition, plus a one-time warning per session |
|
|
19
|
+
| **Requirements** | Node ≥ 20, zero runtime dependencies |
|
|
20
|
+
| **Quality** | 58 tests, CI on ubuntu/macos/windows × Node 20/22/24 |
|
|
21
|
+
|
|
22
|
+
## Why
|
|
23
|
+
|
|
24
|
+
On every model step, OpenCode advertises every skill that has a `description` — the model sees `ID + name + description` for each of them. Skills are not free: they are a permanent context tax on every request.
|
|
25
|
+
|
|
26
|
+
**What this costs.** 29 skills with long descriptions ≈ **3,000+ tokens every step**. Over a 100-step session that is ~300k tokens of pure overhead — spent before the model reads a single line of your code. And that is only 29 skills: many users have far more they downloaded without noticing, some already out of date. This tool helps you understand your skill repository much better.
|
|
27
|
+
|
|
28
|
+
**What can go wrong.** As skills pile up:
|
|
29
|
+
- duplicate IDs silently shadow each other — you may be editing a file that never loads;
|
|
30
|
+
- descriptions drift until the model picks the wrong skill;
|
|
31
|
+
- half the catalog is dead weight nobody noticed.
|
|
32
|
+
|
|
33
|
+
**What `skills-doctor` does.** It measures the cost, shows which definition actually wins, and fixes it:
|
|
34
|
+
|
|
35
|
+
- `check` — report duplicates/shadowing, long descriptions, oversized bodies and the per-step token budget, with fix hints;
|
|
36
|
+
- `scan` — list every skill with its source, precedence and cost;
|
|
37
|
+
- `fix` — archive shadowed duplicates (reversible) or hide low-frequency skills (`autoinvoke: false`, still loadable with `@id`);
|
|
38
|
+
- `update` — sync skills from HTTP catalogs by version, with a lockfile and drift detection;
|
|
39
|
+
- plugin — expose the audit as a `skills_doctor` tool / `/skills-doctor` command inside OpenCode.
|
|
40
|
+
|
|
41
|
+
Works as a CLI and a plugin on **OpenCode 1.x and 2.x**.
|
|
42
|
+
|
|
43
|
+
## Quick start
|
|
44
|
+
|
|
45
|
+
```powershell
|
|
46
|
+
# Run straight from the repo (no install)
|
|
47
|
+
node bin/skills-doctor.mjs check
|
|
48
|
+
|
|
49
|
+
# Or install it locally
|
|
50
|
+
npm link
|
|
51
|
+
skills-doctor check
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
What a run looks like:
|
|
55
|
+
|
|
56
|
+
```text
|
|
57
|
+
skills-doctor 0.4.1
|
|
58
|
+
sources: 5 (2 exist) definitions: 29 unique IDs: 29 advertised: 29 description budget: ~3074 tokens
|
|
59
|
+
|
|
60
|
+
[E] token-budget Advertised skill descriptions cost about 3074 tokens per model step, over 2x the 1200 budget
|
|
61
|
+
[W] long-description "deep-research" description is 917 chars; it is paid on every model step
|
|
62
|
+
[W] large-body "academic-paper" body is about 9980 tokens; invoking it pastes the whole body into context
|
|
63
|
+
...
|
|
64
|
+
|
|
65
|
+
1 error, 18 warn, 0 info
|
|
66
|
+
|
|
67
|
+
Hints:
|
|
68
|
+
- Trim descriptions, or hide low-frequency skills with metadata.opencode/autoinvoke: false.
|
|
69
|
+
- Move details into references/ and keep SKILL.md to the workflow.
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Commands
|
|
73
|
+
|
|
74
|
+
All commands accept `--lang en|zh` (default: auto from the system locale) and `--json`.
|
|
75
|
+
|
|
76
|
+
### `check` (default)
|
|
77
|
+
|
|
78
|
+
Scans every skill source, reports problems, the context budget and fix hints.
|
|
79
|
+
|
|
80
|
+
```powershell
|
|
81
|
+
skills-doctor check
|
|
82
|
+
skills-doctor check --json
|
|
83
|
+
skills-doctor check --budget 800 --strict # for CI
|
|
84
|
+
skills-doctor check --root .\some-project --no-global
|
|
85
|
+
skills-doctor check --html report.html # self-contained HTML report
|
|
86
|
+
skills-doctor check --html report.html --open
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### HTML report
|
|
90
|
+
|
|
91
|
+
`--html [file]` writes a single self-contained HTML file (no external assets):
|
|
92
|
+
|
|
93
|
+
- summary cards and a context-budget bar;
|
|
94
|
+
- findings filterable by severity and text;
|
|
95
|
+
- a skill table with scope, token cost, description preview and file path;
|
|
96
|
+
- default path `./skills-report.html` when `--html` is given without a value; `--open` launches the browser.
|
|
97
|
+
|
|
98
|
+
The plugin and MCP tool accept the same thing as an `html` argument, so the agent can ask you where to save it — for example: *"save the report to D:\reports\skills.html"*.
|
|
99
|
+
|
|
100
|
+
### `plan` (how many tokens can I actually save?)
|
|
101
|
+
|
|
102
|
+
```powershell
|
|
103
|
+
skills-doctor plan
|
|
104
|
+
skills-doctor plan --budget 800 --desc-target 160 --steps 200
|
|
105
|
+
skills-doctor plan --json
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
It answers "how much can I cut, exactly?":
|
|
109
|
+
|
|
110
|
+
- only **advertised** definitions cost tokens — archiving shadowed duplicates saves 0, and the plan says so instead of pretending;
|
|
111
|
+
- **Option A** trims every description longer than `--desc-target` (default 160 chars, cut at sentence boundaries) and computes the exact saving;
|
|
112
|
+
- **Option B** hides the fewest skills needed to reach the budget (still loadable with `@id`; picking the largest savings first is optimal for "minimal hides");
|
|
113
|
+
- **Recommended** trims first, then hides, with no double counting, and projects the saving over `--steps` model steps (default 100).
|
|
114
|
+
|
|
115
|
+
`check` prints a one-line pointer when you are over budget, and the HTML report shows the same potential-savings card.
|
|
116
|
+
|
|
117
|
+
### `scan`
|
|
118
|
+
|
|
119
|
+
Lists every skill with its source, precedence, whether the model can see it, and token cost.
|
|
120
|
+
|
|
121
|
+
```powershell
|
|
122
|
+
skills-doctor scan
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
`*` marks the definition that actually wins (later registration = higher precedence).
|
|
126
|
+
|
|
127
|
+
### `fix`
|
|
128
|
+
|
|
129
|
+
Archives definitions that are shadowed — files that never load because another source defines the same ID. The move is reversible: files go into `.skills-doctor-archive/<timestamp>/` inside the same source, which OpenCode ignores (dot-directory).
|
|
130
|
+
|
|
131
|
+
```powershell
|
|
132
|
+
skills-doctor fix --dry-run # preview what would be archived
|
|
133
|
+
skills-doctor fix
|
|
134
|
+
skills-doctor fix --hide academic-paper,deep-research --dry-run # hide low-frequency skills
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
- Only shadowed duplicates are touched; the effective definition is never moved;
|
|
138
|
+
- Files installed by `update` are skipped — use `update` to bring them back;
|
|
139
|
+
- Default scope is project-only; pass `--all` to also archive global sources;
|
|
140
|
+
- After fixing, `check` should report no `shadowed` findings.
|
|
141
|
+
|
|
142
|
+
### `update` (updater)
|
|
143
|
+
|
|
144
|
+
Reads HTTP catalogs from the `skills` array in `opencode.json(c)`, compares `version`, and downloads whatever changed.
|
|
145
|
+
|
|
146
|
+
```powershell
|
|
147
|
+
skills-doctor update # installs into .opencode/skills by default (where opencode reads)
|
|
148
|
+
skills-doctor update --check-only # only report available updates
|
|
149
|
+
skills-doctor update --into .\team-skills
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
- The output directory is a real OpenCode skill directory, so **`check` picks the update up immediately**;
|
|
153
|
+
- Overwrites files in place and **deletes files that the new version no longer lists** (no stale leftovers from the old version);
|
|
154
|
+
- Writes `.skills-doctor.lock.json` (schema v2: a sha1 for every file);
|
|
155
|
+
- `check` reads that lock: shows managed versions, detects hand edits (`managed-drift`) and missing files (`managed-missing`);
|
|
156
|
+
- Running `update` again re-downloads drifted files (self-healing);
|
|
157
|
+
- Use `--prune` to delete skills that disappeared from the catalog (by default they are reported but kept).
|
|
158
|
+
|
|
159
|
+
## Does the plugin cost tokens?
|
|
160
|
+
|
|
161
|
+
Local execution is free, but any tool registered with the model is visible on every request. Measured on this repo:
|
|
162
|
+
|
|
163
|
+
| Component | Tokens | When |
|
|
164
|
+
|---|---|---|
|
|
165
|
+
| Scan / lint / fix / update (local work) | **0** | always |
|
|
166
|
+
| `skills_doctor` tool definition | **~191** | every request |
|
|
167
|
+
| Budget warning injected into the system prompt | **~60–80** | once per session, only when over budget |
|
|
168
|
+
| Audit report returned by the tool | **~940** | only when the tool is actually called |
|
|
169
|
+
|
|
170
|
+
So the plugin trades a small fixed overhead for the agent being able to audit by itself. In the example above the skill descriptions cost 3,074 tokens per step; dropping that to the 1,200 budget saves more than 10× the plugin overhead. If you want **zero** context cost, don't install the plugin — run `npx opencode-skills-doctor check` from the terminal or CI instead.
|
|
171
|
+
|
|
172
|
+
## Self-update
|
|
173
|
+
|
|
174
|
+
`skills-doctor` checks npm for a newer version of itself at most once per 24 hours (skipped in CI, non-TTY and `--json` runs):
|
|
175
|
+
|
|
176
|
+
```powershell
|
|
177
|
+
skills-doctor upgrade # check now
|
|
178
|
+
skills-doctor upgrade --open # check and open the GitHub release page
|
|
179
|
+
skills-doctor upgrade --apply # npm install -g opencode-skills-doctor@latest
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Opt out permanently in `.skills-doctor.json`:
|
|
183
|
+
|
|
184
|
+
```json
|
|
185
|
+
{ "checkUpdates": "off" }
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
| Value | Behavior |
|
|
189
|
+
|---|---|
|
|
190
|
+
| `"notify"` (default) | print a one-line notice when a newer version exists |
|
|
191
|
+
| `"open"` | same, plus opens the release page in the browser |
|
|
192
|
+
| `"off"` | never check |
|
|
193
|
+
|
|
194
|
+
Per run: `--no-update-check`. Per environment: `SKILLS_DOCTOR_UPDATE_CHECK=0`. The plugin injects the same notice into the system prompt once per session when an update exists. Cached state lives in `~/.skills-doctor/self-update.json`.
|
|
195
|
+
|
|
196
|
+
## Configuration
|
|
197
|
+
|
|
198
|
+
Optional `.skills-doctor.json` in the working directory (or pass `--config <file>`):
|
|
199
|
+
|
|
200
|
+
```json
|
|
201
|
+
{
|
|
202
|
+
"budget": 1200,
|
|
203
|
+
"staleDays": 180,
|
|
204
|
+
"lang": "en",
|
|
205
|
+
"checkUpdates": "notify",
|
|
206
|
+
"rules": { "long-description": "off", "hidden-skill": "info" },
|
|
207
|
+
"ignore": ["legacy-skill"]
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
`rules` accepts `off`, `info`, `warn` or `error`; `ignore` drops findings that only involve those IDs; `checkUpdates` is `notify` / `open` / `off`. CLI flags override the file.
|
|
212
|
+
|
|
213
|
+
## OpenCode plugin
|
|
214
|
+
|
|
215
|
+
The package also exports an OpenCode 2.x plugin:
|
|
216
|
+
|
|
217
|
+
```jsonc
|
|
218
|
+
// opencode.jsonc
|
|
219
|
+
{
|
|
220
|
+
"plugins": ["opencode-skills-doctor"]
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
It registers:
|
|
225
|
+
|
|
226
|
+
- the `skills_doctor` tool (namespace `skills`) — the agent can audit with `{ directory?, budget?, html? }` and gets a markdown report;
|
|
227
|
+
- the `/skills-doctor` command — asks the agent to run the audit and summarize fixes;
|
|
228
|
+
- `/skills-report` — asks where to save an HTML report, then writes it;
|
|
229
|
+
- `/skills-upgrade` — checks whether skills-doctor itself has a newer version.
|
|
230
|
+
|
|
231
|
+
`@opencode/plugin` is an optional peer dependency; OpenCode provides it at runtime.
|
|
232
|
+
|
|
233
|
+
### OpenCode 1.x
|
|
234
|
+
|
|
235
|
+
1.x uses a different plugin API: it resolves the `./server` export for npm plugins and needs `@opencode-ai/plugin` at runtime. The reliable setup is a one-line local plugin file.
|
|
236
|
+
|
|
237
|
+
1. Install the package where OpenCode resolves plugins:
|
|
238
|
+
|
|
239
|
+
```powershell
|
|
240
|
+
npm i --prefix ~/.config/opencode opencode-skills-doctor
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
2. Create `~/.config/opencode/plugins/skills-doctor.js`:
|
|
244
|
+
|
|
245
|
+
```js
|
|
246
|
+
export { SkillsDoctorPlugin } from "opencode-skills-doctor/v1"
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
3. Reference it from `opencode.json` (1.x uses `plugin`, not `plugins`):
|
|
250
|
+
|
|
251
|
+
```jsonc
|
|
252
|
+
{
|
|
253
|
+
"plugin": ["C:\\Users\\<you>\\.config\\opencode\\plugins\\skills-doctor.js"]
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
It registers the same `skills_doctor` tool. 1.x plugins cannot register commands, so `skills-doctor install` writes a `/skills-doctor` markdown command.
|
|
258
|
+
|
|
259
|
+
> The package root entry is the V2 plugin and requires OpenCode 2.x. The CLI works on both lines.
|
|
260
|
+
|
|
261
|
+
### OpenCode Desktop
|
|
262
|
+
|
|
263
|
+
The desktop app bundles the same OpenCode core and reads the **same config directory** as the CLI (`~/.config/opencode`; its UI settings live elsewhere), so one install covers both:
|
|
264
|
+
|
|
265
|
+
```powershell
|
|
266
|
+
npx opencode-skills-doctor install --client desktop
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
- detects the app in the standard install locations and reads its bundled core version from `resources/app.asar` (e.g. 1.17.11), then uses the matching plugin API;
|
|
270
|
+
- writes the same plugin entry and `/skills-doctor`, `/skills-report`, `/skills-upgrade` commands as the CLI install — restart the desktop app afterwards;
|
|
271
|
+
- `--client auto` (default) prefers the CLI version when both are installed; `--client opencode` skips desktop detection;
|
|
272
|
+
- `uninstall --client desktop` reverses it.
|
|
273
|
+
|
|
274
|
+
Verified on Windows: app at `%LOCALAPPDATA%\Programs\@opencode-aidesktop\OpenCode.exe`, sharing config with the `opencode` CLI.
|
|
275
|
+
|
|
276
|
+
### Deep integration (one command)
|
|
277
|
+
|
|
278
|
+
```powershell
|
|
279
|
+
npx opencode-skills-doctor install
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
The installer detects your OpenCode version and wires everything up:
|
|
283
|
+
|
|
284
|
+
- **2.x** — adds `"plugins": ["opencode-skills-doctor"]` to `opencode.json(c)`;
|
|
285
|
+
- **1.x** — installs the package into `~/.config/opencode`, writes the local plugin entry, generates `/skills-doctor`, `/skills-report` and `/skills-upgrade` markdown commands, sets `"type": "module"`;
|
|
286
|
+
- backs up the config file as `<config>.bak-<timestamp>` before editing.
|
|
287
|
+
|
|
288
|
+
Use `--dry-run` to preview, `--config-dir <dir>` for a custom config location, and `--no-npm` to skip the package install.
|
|
289
|
+
|
|
290
|
+
Once installed you get:
|
|
291
|
+
|
|
292
|
+
- the `skills_doctor` tool (with an optional `html` argument to write a report to a path you choose);
|
|
293
|
+
- the `/skills-doctor` command (1.x: generated markdown command; 2.x: plugin command);
|
|
294
|
+
- the `/skills-report` command (2.x) that asks where to save the HTML report;
|
|
295
|
+
- the `/skills-upgrade` command (2.x) that checks for a newer skills-doctor;
|
|
296
|
+
- a one-time system-prompt warning when the skill description budget is exceeded.
|
|
297
|
+
|
|
298
|
+
### Uninstall
|
|
299
|
+
|
|
300
|
+
```powershell
|
|
301
|
+
npx opencode-skills-doctor uninstall
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
- **2.x** — removes `opencode-skills-doctor` from `plugins`;
|
|
305
|
+
- **1.x** — removes the config entry, deletes the generated plugin entry and `/skills-doctor` command, and uninstalls the package (`--no-npm` skips that);
|
|
306
|
+
- only files generated by the installer are deleted — anything you edited is kept and reported;
|
|
307
|
+
- config files are backed up before editing; `--dry-run` previews, `--config-dir` targets a custom location.
|
|
308
|
+
|
|
309
|
+
Your data is never touched: `.skills-doctor.json`, `.skills-doctor.lock.json` and `.skills-doctor-archive/` stay until you delete them. MCP entries in other clients (Claude Code, Codex, DSH) are per-client configs and must be removed there.
|
|
310
|
+
|
|
311
|
+
## MCP server (Claude Code, Codex, Kimi Code, any MCP client)
|
|
312
|
+
|
|
313
|
+
`skills-doctor mcp` speaks the Model Context Protocol over stdio and exposes one tool: `skills_doctor` with arguments `directory?`, `budget?`, `lang?` and `global?`. It does not need OpenCode installed.
|
|
314
|
+
|
|
315
|
+
Claude Code (`.mcp.json` in the project, or your MCP config):
|
|
316
|
+
|
|
317
|
+
```json
|
|
318
|
+
{
|
|
319
|
+
"mcpServers": {
|
|
320
|
+
"skills-doctor": { "command": "npx", "args": ["-y", "opencode-skills-doctor", "mcp"] }
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
Codex (`~/.codex/config.toml`):
|
|
326
|
+
|
|
327
|
+
```toml
|
|
328
|
+
[mcp_servers.skills-doctor]
|
|
329
|
+
command = "npx"
|
|
330
|
+
args = ["-y", "opencode-skills-doctor", "mcp"]
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
Any other MCP client: run `npx -y opencode-skills-doctor mcp` and allow the `skills_doctor` tool.
|
|
334
|
+
|
|
335
|
+
It scans `.opencode/skills`, `.claude/skills` and `.agents/skills` (project and global), so it can audit skills used by other agents too.
|
|
336
|
+
|
|
337
|
+
### DeepSeek Harness (DSH)
|
|
338
|
+
|
|
339
|
+
One command installs both the MCP tool and a skill that tells the model when to audit:
|
|
340
|
+
|
|
341
|
+
```powershell
|
|
342
|
+
npx -y opencode-skills-doctor install --client dsh
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
It locates `~/.dsh/profiles/<profile>/cordis.patch.yml` (default `desktop`; otherwise `--profile <name>`), inserts the `mcp-skills-doctor` row under an `- insert:` block idempotently, and copies the `skills-doctor-audit` skill into `~/.dsh/skills/`. Restart DSH (or let HMR reload the patch) to load the MCP tool; the skill is watched and appears without a restart.
|
|
346
|
+
|
|
347
|
+
- tool name `mcp__skills_doctor__skills_doctor`, arguments `directory`, `budget`, `lang`, `global`, `html`;
|
|
348
|
+
- `uninstall --client dsh` removes both the patch entry and the skill;
|
|
349
|
+
- `--dsh-home <dir>` for a non-standard DSH home.
|
|
350
|
+
|
|
351
|
+
Manual fallback — DSH patches add new entries under `insert`, so append this block:
|
|
352
|
+
|
|
353
|
+
```yaml
|
|
354
|
+
- insert:
|
|
355
|
+
- id: mcp-skills-doctor
|
|
356
|
+
name: "@deepseek-ai/dsh-mcp-client"
|
|
357
|
+
config:
|
|
358
|
+
serverName: skills_doctor
|
|
359
|
+
transport: stdio
|
|
360
|
+
command: npx
|
|
361
|
+
args: ["-y", "opencode-skills-doctor", "mcp"]
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
On Windows, if the stdio spawn cannot resolve `npx`, use `command: cmd` and `args: ["/c", "npx", "-y", "opencode-skills-doctor", "mcp"]`.
|
|
365
|
+
|
|
366
|
+
## How it works
|
|
367
|
+
|
|
368
|
+
**Audit pipeline**
|
|
369
|
+
|
|
370
|
+
1. Discover every skill source (see the precedence list below).
|
|
371
|
+
2. Parse each `SKILL.md` / flat `.md` frontmatter and estimate its description/body cost.
|
|
372
|
+
3. Resolve which definition wins for each ID (later registration wins; `SKILL.md` directory form wins ties).
|
|
373
|
+
4. Run the rules, compute the per-step token budget, and emit findings with fix hints.
|
|
374
|
+
|
|
375
|
+
**Fix pipeline**
|
|
376
|
+
|
|
377
|
+
`fix` only touches definitions that are shadowed (never loaded): it moves them into `.skills-doctor-archive/<timestamp>/` inside the same source. The effective definition is never moved, and files managed by `update` are skipped. Scope defaults to the project; `--all` extends it to global sources.
|
|
378
|
+
|
|
379
|
+
**Update pipeline**
|
|
380
|
+
|
|
381
|
+
1. Fetch `<catalog>/index.json` (3 attempts with backoff on 5xx/network errors).
|
|
382
|
+
2. Compare each skill's `version` with the lockfile.
|
|
383
|
+
3. Download changed files (same-origin only, path traversal blocked) and write them atomically.
|
|
384
|
+
4. Delete files the new version no longer lists, and clean empty directories.
|
|
385
|
+
5. Verify hashes, write the lockfile, and — with `--prune` — delete skills removed from the catalog.
|
|
386
|
+
|
|
387
|
+
`check` reads the lockfile and reports drift (`managed-drift`), missing files (`managed-missing`) and unknown lock versions (`lock-version-unsupported`). Re-running `update` re-downloads drifted files.
|
|
388
|
+
|
|
389
|
+
### Grok CLI
|
|
390
|
+
|
|
391
|
+
Grok discovers skills from `.grok/skills` (project, repo and `~/.grok/skills`), plus `.claude/skills`, `.cursor/skills` and `.agents/skills`. `skills-doctor` scans exactly the same locations, so:
|
|
392
|
+
|
|
393
|
+
```powershell
|
|
394
|
+
skills-doctor check --root . # covers your Grok project skills
|
|
395
|
+
skills-doctor check --root $HOME # covers ~/.grok/skills and other user-level skills
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
No install step is needed — Grok loads `SKILL.md` packages natively. If your Grok build has MCP enabled, you can additionally register the MCP server above to let the model call `skills_doctor` itself.
|
|
399
|
+
|
|
400
|
+
## Rules
|
|
401
|
+
|
|
402
|
+
| Rule | Level | Meaning |
|
|
403
|
+
|---|---|---|
|
|
404
|
+
| `shadowed` | warn | Same ID defined in multiple sources; only one wins, the rest is dead weight and easy to edit by mistake |
|
|
405
|
+
| `flat-dir-collision` | warn | Both `<id>.md` and `<id>/SKILL.md` exist in the same source |
|
|
406
|
+
| `duplicate-content` | warn | Two skills share identical body text |
|
|
407
|
+
| `near-duplicate-description` | warn | Descriptions are ≥ 75% similar; the model may pick the wrong one |
|
|
408
|
+
| `missing-description` | info | No description, so the model never discovers it |
|
|
409
|
+
| `long-description` | warn | Description > 240 chars (paid on every model step) |
|
|
410
|
+
| `large-body` | warn | Body > 4000 tokens (pasted into context when invoked) |
|
|
411
|
+
| `token-budget` | warn/error | Total advertised description tokens exceed the budget (default 1200) |
|
|
412
|
+
| `too-many-skills` | warn/error | More than 60 / 100 skills are advertised |
|
|
413
|
+
| `invalid-id` | info | ID is not kebab-case |
|
|
414
|
+
| `hidden-skill` | info | Hidden via `opencode/autoinvoke: false` or `disable-model-invocation: true` |
|
|
415
|
+
| `stale-skill` | info | Not updated within `--stale-days` |
|
|
416
|
+
| `broken-reference` | warn | `references/`, `scripts/`, `assets/`, `templates/` or `examples/` path does not exist |
|
|
417
|
+
| `reference-outside-skill` | info | A referenced file lives outside the skill directory (hurts portability) |
|
|
418
|
+
| `invalid-frontmatter` | warn | Unclosed `---` fence, or non-string name/description / non-object metadata |
|
|
419
|
+
| `empty-body` | info | Body is nearly empty, so the skill carries no instructions |
|
|
420
|
+
| `managed-drift` | warn | A file installed by `update` was modified afterwards |
|
|
421
|
+
| `managed-missing` | warn | Recorded in the lock, but the local file is gone |
|
|
422
|
+
| `managed-unsafe` | warn | Path traversal or another unsafe path in the lock |
|
|
423
|
+
| `lock-version-unsupported` | info | Lock is newer than this tool; parsed in compatibility mode |
|
|
424
|
+
|
|
425
|
+
## Forward compatibility
|
|
426
|
+
|
|
427
|
+
- **Lock**: supports v1 (string versions, no hashes) and v2 (per-file sha1). A newer lock version is parsed in compatibility mode, unknown fields are ignored, and a single `lock-version-unsupported` notice is emitted.
|
|
428
|
+
- **Catalog `index.json`**: only `skills[].name/version/files` is read; any future fields are ignored.
|
|
429
|
+
- **Skill frontmatter**: only `name`, `description`, `metadata.opencode/autoinvoke` and `disable-model-invocation` are read; everything else is ignored.
|
|
430
|
+
- **Paths**: both OpenCode 2.0 locations (`.opencode/skills`, `~/.config/opencode/skills`) and compatibility locations (`.claude/skills`, `.agents/skills`, searched upward level by level).
|
|
431
|
+
- The updater always writes the highest lock version this tool supports, so older readers never break on new fields.
|
|
432
|
+
|
|
433
|
+
## Discovery sources (low → high precedence)
|
|
434
|
+
|
|
435
|
+
1. `~/.claude/skills`
|
|
436
|
+
2. project `.claude/skills` (from the project root up to the current directory)
|
|
437
|
+
3. `~/.agents/skills`
|
|
438
|
+
4. project `.agents/skills`
|
|
439
|
+
5. `~/.cursor/skills`
|
|
440
|
+
6. project `.cursor/skills`
|
|
441
|
+
7. `~/.grok/skills`
|
|
442
|
+
8. project `.grok/skills`
|
|
443
|
+
9. `~/.config/opencode/skills`
|
|
444
|
+
10. project `.opencode/skills`
|
|
445
|
+
11. the `skills` array in `opencode.json(c)` (2.x) or `skills.paths` (1.x); relative paths resolve from the working directory; supports `~/`, absolute paths and HTTP catalogs
|
|
446
|
+
|
|
447
|
+
## JSON output
|
|
448
|
+
|
|
449
|
+
`check --json`, `scan --json` and `update --json` all print structured JSON, ready for CI or a web report. Fields include `summary`, `findings[]` and `skills[]` (with `managed: { version, url, root }`).
|
|
450
|
+
|
|
451
|
+
## Security
|
|
452
|
+
|
|
453
|
+
This tool reads and writes skill files. Before using it against a real setup:
|
|
454
|
+
|
|
455
|
+
- `update` downloads catalog content **without signature verification** — treat catalogs as code;
|
|
456
|
+
- `fix` / `--prune` move or delete files — preview with `--dry-run` first;
|
|
457
|
+
- it is **not a sandbox** and does not constrain what an agent can do.
|
|
458
|
+
|
|
459
|
+
See [SECURITY.md](../SECURITY.md) for the full threat model, hardening checklist and private vulnerability reporting.
|
|
460
|
+
|
|
461
|
+
## Caveats and known limitations
|
|
462
|
+
|
|
463
|
+
**Install / plugin**
|
|
464
|
+
|
|
465
|
+
- OpenCode **2.x**: add `"plugins": ["opencode-skills-doctor"]` to `opencode.jsonc` — done.
|
|
466
|
+
- OpenCode **1.x**: needs `npm i --prefix ~/.config/opencode opencode-skills-doctor` plus a one-line local plugin file (`export { SkillsDoctorPlugin } from "opencode-skills-doctor/v1"`), because 1.x resolves plugins differently and `@opencode-ai/plugin` is an optional peer.
|
|
467
|
+
- 1.x has no plugin-registered `/skills-doctor` command; `skills-doctor install` writes a markdown command file instead.
|
|
468
|
+
|
|
469
|
+
**Safety / behavior**
|
|
470
|
+
|
|
471
|
+
- `fix` defaults to **project-only**; pass `--all` to touch global sources. Archived files go to `.skills-doctor-archive/<timestamp>/` (reversible; originals are moved, not deleted).
|
|
472
|
+
- `fix` only touches **shadowed** definitions. It never edits the effective skill and never rewrites descriptions or bodies.
|
|
473
|
+
- `fix --hide` only adds `metadata.opencode/autoinvoke: false`; the skill stays loadable with `@id`.
|
|
474
|
+
- Shadowing follows OpenCode's precedence: later registration wins; on a tie, the `SKILL.md` directory form wins. If the runtime disagrees, trust the runtime.
|
|
475
|
+
- This is **not** a security sandbox: it audits configuration and files, it does not constrain what the agent can do.
|
|
476
|
+
|
|
477
|
+
**Measurement**
|
|
478
|
+
|
|
479
|
+
- The default `budget` (1200 tokens) is a recommendation, not a hard limit. Tune it with `.skills-doctor.json` or `--budget`.
|
|
480
|
+
- Token counts are heuristic (CJK ≈ 1.7 chars/token, the rest ≈ 4 chars/token) — good for comparisons, not for billing.
|
|
481
|
+
- `check` is offline; ask a catalog for new versions with `update --check-only`.
|
|
482
|
+
- Config precedence is assumed: global config < project config < `skills` array order.
|
|
483
|
+
|
|
484
|
+
## FAQ and troubleshooting
|
|
485
|
+
|
|
486
|
+
**A skill I created does not appear.**
|
|
487
|
+
Check: does the file have a `description`? Is it hidden via `opencode/autoinvoke: false` or `disable-model-invocation`? Is the ID derived from the path (directory name for `SKILL.md`, filename for flat `.md`)? Is an earlier/later source shadowing it? `skills-doctor scan` shows the winning definition for each ID.
|
|
488
|
+
|
|
489
|
+
**`fix` did nothing.**
|
|
490
|
+
`fix` only archives **shadowed** duplicates and defaults to project-only. Run `skills-doctor fix --dry-run` to see what it would do, and `--all` to include global sources. Content issues (long descriptions, large bodies) are reported, not rewritten.
|
|
491
|
+
|
|
492
|
+
**`check` says the budget is exceeded but the agent feels fine.**
|
|
493
|
+
The token numbers are heuristics, and the 1,200-token budget is a recommendation. Tune it with `--budget` or `.skills-doctor.json`; treat the report as a relative comparison, not billing.
|
|
494
|
+
|
|
495
|
+
**Where did `fix` move my files?**
|
|
496
|
+
Into `.skills-doctor-archive/<timestamp>/` inside the same skill source. Move them back to restore.
|
|
497
|
+
|
|
498
|
+
**Does it update skills automatically in the background?**
|
|
499
|
+
No daemon. Run `skills-doctor update` when you want to sync; use `--check-only` to just list available updates.
|
|
500
|
+
|
|
501
|
+
**Does it work on OpenCode 1.x?**
|
|
502
|
+
Yes — the CLI works on both lines, and `skills-doctor install` wires up the 1.x plugin (local plugin entry + generated command). 1.x has no `/skills-doctor` command registration from plugins, so the installer writes a command file instead.
|
|
503
|
+
|
|
504
|
+
**Why does the plugin cost tokens if it runs locally?**
|
|
505
|
+
Executing code is free, but the tool definition is part of the model's context. That is why the CLI mode exists: zero context cost.
|
|
506
|
+
|
|
507
|
+
## Roadmap
|
|
508
|
+
|
|
509
|
+
- v0.6: CI governance (baseline that only reports new issues, official GitHub Action), content-aware suggestions (which skills are never used), catalog signature verification
|
|
510
|
+
- Later: V1/Claude skill migration report
|
|
511
|
+
|
|
512
|
+
## Development and contributing
|
|
513
|
+
|
|
514
|
+
```powershell
|
|
515
|
+
npm test # 58 tests (node test/doctor.test.mjs)
|
|
516
|
+
npm run bench # lint timing for 100/500/1000 skills
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
- CI runs on ubuntu/macos/windows × Node 20/22/24; `prepublishOnly` re-runs the tests before every publish.
|
|
520
|
+
- Issues and PRs are welcome: for behavior changes, open an issue first with the scenario and expected output. Link the issue in the PR.
|
|
521
|
+
- Keep the CLI dependency-free and the output bilingual (`--lang en|zh`) — both are project invariants.
|
|
522
|
+
- Security reports: see [SECURITY.md](../SECURITY.md); do not open a public issue for vulnerabilities.
|
|
523
|
+
- Release notes live in [CHANGELOG.md](../CHANGELOG.md).
|
|
524
|
+
|
|
525
|
+
### Project layout
|
|
526
|
+
|
|
527
|
+
| Path | Purpose |
|
|
528
|
+
|---|---|
|
|
529
|
+
| `src/audit.mjs` | one-call audit used by CLI, plugin and MCP |
|
|
530
|
+
| `src/discover.mjs` | source discovery, precedence, JSONC parsing |
|
|
531
|
+
| `src/lint.mjs` | rules, token budget, near-duplicate detection |
|
|
532
|
+
| `src/fix.mjs` | archive / hide |
|
|
533
|
+
| `src/catalog.mjs` | updater, lockfile, retries, atomic writes |
|
|
534
|
+
| `src/managed.mjs` | lock verification and drift detection |
|
|
535
|
+
| `src/cli.mjs` | command dispatch and output |
|
|
536
|
+
| `src/i18n.mjs` | English/Chinese strings |
|
|
537
|
+
| `src/mcp.mjs` | MCP stdio server |
|
|
538
|
+
| `src/plugin.mjs` / `src/plugin-v1.mjs` | OpenCode 2.x / 1.x entries |
|
|
539
|
+
| `src/install.mjs` | one-command OpenCode integration |
|
|
540
|
+
|
|
541
|
+
MIT
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="300" viewBox="0 0 1200 300" role="img" aria-label="skills-doctor — audit the hidden token cost of agent skills">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
|
|
4
|
+
<stop offset="0" stop-color="#0b1220"/>
|
|
5
|
+
<stop offset="0.55" stop-color="#101a2e"/>
|
|
6
|
+
<stop offset="1" stop-color="#0d2033"/>
|
|
7
|
+
</linearGradient>
|
|
8
|
+
<linearGradient id="accent" x1="0" y1="0" x2="1" y2="0">
|
|
9
|
+
<stop offset="0" stop-color="#3fb950"/>
|
|
10
|
+
<stop offset="1" stop-color="#58a6ff"/>
|
|
11
|
+
</linearGradient>
|
|
12
|
+
</defs>
|
|
13
|
+
|
|
14
|
+
<rect width="1200" height="300" fill="url(#bg)"/>
|
|
15
|
+
<circle cx="1070" cy="-40" r="260" fill="#1f6feb" opacity="0.10"/>
|
|
16
|
+
<circle cx="120" cy="330" r="220" fill="#3fb950" opacity="0.08"/>
|
|
17
|
+
|
|
18
|
+
<text x="64" y="112" font-family="-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif" font-size="62" font-weight="700" fill="#f0f6fc">skills-doctor</text>
|
|
19
|
+
<text x="64" y="156" font-family="-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif" font-size="23" fill="#9fb0c9">Audit the hidden token cost of your agent's skills</text>
|
|
20
|
+
|
|
21
|
+
<g font-family="-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif" font-size="15">
|
|
22
|
+
<rect x="64" y="196" width="112" height="34" rx="17" fill="#16233a" stroke="#2b3f5f"/>
|
|
23
|
+
<text x="120" y="218" fill="#c9d6ea" text-anchor="middle">OpenCode</text>
|
|
24
|
+
<rect x="186" y="196" width="122" height="34" rx="17" fill="#16233a" stroke="#2b3f5f"/>
|
|
25
|
+
<text x="247" y="218" fill="#c9d6ea" text-anchor="middle">Claude Code</text>
|
|
26
|
+
<rect x="318" y="196" width="86" height="34" rx="17" fill="#16233a" stroke="#2b3f5f"/>
|
|
27
|
+
<text x="361" y="218" fill="#c9d6ea" text-anchor="middle">Codex</text>
|
|
28
|
+
<rect x="414" y="196" width="78" height="34" rx="17" fill="#16233a" stroke="#2b3f5f"/>
|
|
29
|
+
<text x="453" y="218" fill="#c9d6ea" text-anchor="middle">DSH</text>
|
|
30
|
+
<rect x="502" y="196" width="86" height="34" rx="17" fill="#16233a" stroke="#2b3f5f"/>
|
|
31
|
+
<text x="545" y="218" fill="#c9d6ea" text-anchor="middle">Grok</text>
|
|
32
|
+
</g>
|
|
33
|
+
|
|
34
|
+
<g transform="translate(760,44)">
|
|
35
|
+
<rect width="392" height="212" rx="14" fill="#0a1120" stroke="#2b3f5f"/>
|
|
36
|
+
<circle cx="22" cy="20" r="5" fill="#ff5f56"/>
|
|
37
|
+
<circle cx="40" cy="20" r="5" fill="#ffbd2e"/>
|
|
38
|
+
<circle cx="58" cy="20" r="5" fill="#27c93f"/>
|
|
39
|
+
<g font-family="ui-monospace,SFMono-Regular,Menlo,Consolas,monospace" font-size="13.5">
|
|
40
|
+
<text x="22" y="62" fill="#8b9bb4">$ npx opencode-skills-doctor check</text>
|
|
41
|
+
<text x="22" y="96" fill="#3fb950">✓ 29 skills · ~3,074 tokens/step</text>
|
|
42
|
+
<text x="22" y="128" fill="#d29922">! over budget ×2</text>
|
|
43
|
+
<text x="22" y="160" fill="#58a6ff">plan → save ~1,940 tokens/step</text>
|
|
44
|
+
<text x="22" y="192" fill="#8b9bb4">HTML report ready →</text>
|
|
45
|
+
</g>
|
|
46
|
+
</g>
|
|
47
|
+
|
|
48
|
+
<rect x="64" y="262" width="72" height="3" rx="1.5" fill="url(#accent)"/>
|
|
49
|
+
</svg>
|
|
Binary file
|
|
Binary file
|