PerryLink/dsh-plugin-doctor ↗★ 2
@perrylink/dsh-plugin-doctor
Zero-dependency static + sandbox smoke detector for DeepSeek Harness (dsh) plugins: package-structure gates (R), cordis contract scans (K), keyless-headless sandbox smoke (D), and ecosystem-listing checks (CC). 适合需要对DSH插件进行结构、契约及沙箱运行安全检测的开发者。
インストール
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:PerryLink/dsh-plugin-doctorドキュメント
README 全文を読む ↗Usage
node doctor.mjs --repo # 全量(含动态冒烟,需网络 + pnpm)
node doctor.mjs --repo --no-smoke # 仅静态 + 清单
node doctor.mjs --repo --dsh 0.1.7-alpha.1 # 冒烟宿主版本(默认 npm latest 已发布线)
node doctor.mjs --repo --only R,K # 只跑静态两层(推荐用 ASCII 别名)
node doctor.mjs --repo --json report.json
node doctor.mjs --repo --json - # JSON 写 stdout(此时抑制人类可读报告)
node doctor.mjs --repo --format check --json - # 生态三值契约视图(PASS/WARN/FAIL + 0/1/2)
node doctor.mjs --repo --workspace # 指定兄弟仓所在工作区(CC 组核对用)
node doctor.mjs --repo --allow-degraded # 显式接受「整组未真跑」(默认 exit 6)
node doctor.mjs --purge # 清理本工具产生的隔离目录(仅 doctor-quarantine-*)
Target shapes and coverage (new in 0.2.0)
--repo may be a source tree or an installed package directory / unpacked tarball artifact (the latter is common at node_modules/ ). The criteria change with the shape:
| Shape | K group (Cordis contract) | Notes |
|---|---|---|
Source tree with src/ | scans src/** plus root-level JS (mode: src) | complete |
No src/, main points at lib/ | fallback scan of lib/** (mode: lib-fallback) | fixed in 0.2.0: the old implementation keyed on "no build script", but published packages keep their build script → the fallback never fired, all nine K checks were skipped, and it still exited 0 (false green) |
Neither src/ nor lib/ | all nine skipped (mode: none) | the whole group never really ran → exit code 6, no more false green |
Coverage is written to coverage.K in the JSON ({filesInspected, mode}) and summarised in groups.K.
--only groups and ASCII aliases
| Alias | Full group name | Contents |
|---|---|---|
R | static · package structure | R0–R8 |
K | static · Cordis contract scan | K1–K9 |
D | dynamic · sandbox smoke | D0–D3, D9 |
CC | ecosystem · directory listings | CC1–CC5 |
Aliases are case-insensitive, and the Chinese full names still work. Use the aliases in workflows: if an editor or script round-trips a Chinese group name through the wrong encoding, --only matches no group at all.
Exit-code contract
| Code | Meaning | Introduced |
|---|---|---|
0 | no fail/error (warn/skip allowed), and every requested group really ran | 0.1.x |
1 | fail/error present (a plugin defect) | 0.1.x |
2 | usage error, unknown group, unknown option | 0.1.x |
3 | infrastructure error (missing npm/pnpm and the like) | 0.2.0 |
4 | unsupported host version (the host itself failed to install; not a plugin verdict) | 0.2.0 |
5 | unstable result (a step timed out or was killed by a signal) | 0.2.0 |
6 | degraded: a requested group never really ran (e.g. no source files to scan) | 0.2.0 |
Guarding against silent passes (two layers):
- If even one group name in
--onlyfails to match → immediately2. Versions 0.1.4 and earlier would "check nothing + exit 0" when a group name was mangled, which once turned the CI gates of 35 repos into false green (measured 2026-09-09:checks_run=0,exit=0). - From 0.2.0: a requested group whose checks all skipped →
6. The old implementation only covered "not a single check ran", not "it ran but everything skipped" — the latter let "K group with zero coverage" count as a pass. To accept that explicitly, use--allow-degraded(exit code drops to 0, butdegradedstays non-empty in the JSON).
⚠️ The existing gates in 41 family repos do not read the exit code (their workflows use
set +e/out="$(…)"/set -e) and only parse theR0/K1prefixes on stdout andresults[].namein the JSON. So 0.2.0's new exit codes change nothing for those pipelines; they serve interactive use and future integrators.
- The smoke run keeps its temporary
DSH_HOME/DSH_AGENTS_HOMEinside a self-made%TEMP%sandbox (prefixdoctor-, which does not overlap the host-protected%TEMP%\dsh-*template) and never touches the real~/.dsh(red line 3). dsh plugin addis always passed--ignore-scripts: the tested package's install/prepare scripts never execute on the host. A pnpm ignored-builds block is classified asenvironment(it counts neither as a pass nor as a plugin defect).- Every step's subprocess stdout/stderr is written to
%TEMP%\doctor-run-*\logs\; at the end the run quarantines instead of deleting (renames to%TEMP%\doctor-quarantine-*), prints that path in the report tail, and leaves removal to--purgeafter a human confirms (red line 4, the three-stage rule). - Absolute paths seen at runtime are placeholder-ised to
before they reach the JSON or the rendered text, so a report can be committed into someone else's repo without tripping its path-leak gate.