中文EN

dsh-skill-doctor

skill装不上npmMIT

诊断技能为什么没被模型选中——简介被截断、文件头损坏、引用文件缺失——并检查已安装插件之间是否存在 loader 条目冲突;按 profile 自动保存体检病历、每次复诊对比上次差值,把重复出现的问题归并为同一根因,为超长简介生成改写工单,并列出改动了技能目录配置的插件 patch。

下载量 / Downloads
1,571
测试时间 / Tested
2026-11-06
DSH 版本 / Version
0.2.2-rc.1

安装

dsh plugin --profile web add dsh-skill-doctor

沙箱测试结果

阶段结果
1 安装✗
2 列装—
3 挂载—
4 启动—
第一条错误
dsh: initialized profile web at /home/node/.dsh/profiles/web

四阶段在隔离沙箱里跑:安装(有网)→ 列装 → 挂载 → 启动(后三步断网)。

README

<div align="center">

dsh-skill-doctor

[English](README.en.md) | 中文

dsh 的技能与插件体检:告诉模型到底看到了什么、插件到底跑起来没、以及下一步该改哪里。

![npm version](https://www.npmjs.com/package/dsh-skill-doctor)

![License: MIT](LICENSE)

![Node](package.json)

![dsh](https://github.com/deepseek-ai/deepseek-harness)

[亮点](#亮点) | [安装](#安装) | [怎么用](#怎么用) | [面板](#面板) | [四个工具](#四个工具) | [规则](#规则) | [命令行](#命令行) | [它做不到什么](#它做不到什么) | [故障排查](#故障排查)

</div>

技能坏了通常是静默的:不加载、被同名覆盖、description 超长被截断、正文里引用的文件根本不存在——用户只看到“模型不用它”。

dsh-skill-doctor 把这些隐性问题变成可读的报告。它不做技能市场,也不做安全扫描器;它只回答一个问题:这个东西在 dsh 里到底能不能正常工作?

亮点

  • 告诉你模型到底看到了什么。 不是一句“简介超长”,而是把截断后那段原文原样贴出来——你一眼就能看出自己的触发词在不在里面。
  • 每条问题都给处方。 🔧 能自动修 / 🖥 一条命令 / ✍️ 只能人工改,三档分得开,不用猜“然后呢”。简介截断还能直接出改写工单(触发词骨架 + 字数预算),模型填稿、你确认后写入——诊断到修复不再断链。
  • 判断插件是真跑起来了,还是只是装了。 读运行时 fiber 阶段,failed 的插件会被点名;探测不到时明确说“没查到”,而不是当成没问题。
  • 会诊:技能和插件交叉看。 “0 个技能”未必是技能坏了——插件报告列出所有动了技能链路的 patch(谁 → 哪个条目 → 哪个键),技能报告在空手而归时反向指向插件层。
  • 输入框里就能用,不经过模型。 打 / 有一组中文条目(体检 / 查目录 / 改写 / 复诊 / 诊断 / 会诊),选中即是蓝色 chip,回车直接出结果——快,也不会被模型转述改写。面板上那六个按钮写的是同一组命令。
  • 会话里实时看模型在调什么。 输入框上方常驻一条,模型读了哪个技能、调了哪些工具,一行一条。
  • 有病历,能复诊。 修完 11 个超长简介后再跑一次,报告不该还是一堆“还有 2 个超长”——对照上次存档,它告诉你“好了 11 个、新冒出来 1 个”。默认就开着,不用配置。
  • 知道哪些技能模型从来不用。 面板的「使用统计」按加载次数排行,跨会话累计——静态检查给不出的动态证据。
  • 按根因看,不按个数看。 13 个技能都“简介超长”,如果它们都叫 nature-*,那多半是一套模板的问题——报告会把它聚成一条,告诉你改一次顶 13 次。
  • 尺子会过期,报告会说明。 “超过 500 字算长”是 dsh 当时的约定,不是常数。报告会把自己按哪个 dsh 版本校准过写出来;版本对不上时,明确提示阈值可能已过期。
  • CI 里能跑。 dsh-skill-lint 不依赖 dsh 运行时,退出码和 --json 都准备好了。

安装

dsh plugin --profile <你的profile> add dsh-skill-doctor

装的是最新版;想锁定版本就在名字后面加版本号,例如 @0.13.2。

装完完全退出并重开 dsh(面板等客户端代码只在启动时加载)。

装好后直接在会话里说话就行,不需要记工具名:

帮我体检一下技能

怎么用

最快的是斜杠命令:输入框打 /,选一条(菜单是中文的),补上路径或技能名回车——由插件在本地算,不经过模型。下面三个场景则适合直接说话,让模型挑工具。

「我怀疑某个技能没生效」

解释一下 nature-citation 为什么可能没被模型用上

得到的是:模型实际读到的那 500 字原文 + 被切掉的触发说法 + 影响大不大 + 怎么改。

「我怀疑某个插件没跑起来」

查一下 desktop profile 的插件有没有冲突

磁盘层面查条目互抢、重复安装、装了没启用;运行时层面查 fiber 阶段,failed 的直接点名。

「我想知道模型刚才在干什么」

看输入框上方那条面板,或者点开看完整调用流。

面板

会话输入框上方常驻一条,点开是本次会话的调用流:

🩺 技能医生 · 读入技能 nature-citation      3 技能 · 1 插件 · 12 工具
🩺 技能医生 · 活动                    3 技能 · 1 插件 · 12 工具   ✕
──────────────────────────────────────────────────────
→ 读入技能 nature-citation
✓ skill_doctor · 29 个技能                        本插件
✓ read · HANDOFF.md
● grep · TODO                                     执行中
──────────────────────────────────────────────────────
只显示最近 20 条。工具属于哪个插件 dsh 没有暴露,只能显示工具名。
  • → 技能加载:模型通过 skill 工具读技能,参数里带名字,所以能精确显示读的是哪个
  • 绿色 + 「本插件」 调用本插件自己的工具时标出来
  • ● 还没拿到结果,✓ 已完成
  • 右侧是本次会话累计:技能 / 插件 / 工具各多少次

面板读的是会话当前窗口,滚出窗口的更早调用看不到;见过的会攒住,所以累计数字不会倒退。底部写明了这一点。

浮层顶部有「功能 / 活动 / 使用统计」三个标签页。功能页是入口:六个按钮(体检全部、只看目录、改写工单、复诊对比、诊断一个、插件会诊),点一下高亮,并把对应的 /命令 送进输入框——补上路径或技能名回车即可,不用记提示词,也不用记命令名。拿不到输入框权限时自动退化为复制到剪贴板,并在页面上直说。使用统计是跨会话的:每个技能被读进上下文的次数、最近使用日期、覆盖的会话数——按加载次数排行的“哪些技能模型从来不用”就是诊断的动态证据。数据只存在本机浏览器存储(localStorage),不上传、不进技能文件,面板里可一键清空;模型直接 read 技能文件不计——读不到归属,宁可少记不记错。

斜杠命令(客户端最顺手的入口)

装好就有六条命令。输入框打 / 会看到一组中文条目,选中即变成蓝色 chip,后面直接写参数、回车出结果——全程不经过模型:

菜单 · 输入框 · 作用

体检 · /doctor · 检查全部技能,看有没有坏、模型能否看到,并把同类问题合并

查目录 · /scope <目录> · 只检查你指定的目录,不看别处

改写 · /rewrite <技能名> · 给简介超长的技能出改写稿,附字数预算和被切掉的触发词

复诊 · /followup · 和上次体检对比,看哪些修好了、哪些又变差了

诊断 · /why <技能名> · 查一个技能为什么没被模型选中,给出具体原因和改法

会诊 · /clash · 检查有没有插件改坏了技能目录配置,导致技能突然消失

输出是纯文本(会话流里的一个节点,不渲染 Markdown 表格),所以只写"先看什么":

技能体检 · 4 个技能(只查 D://skills)
⛔ 用不了 0 · ⚠️ 影响大 3 · 💡 影响小 0 · 💡 其他 0 · ✅ 正常 1

共性问题(先改这里最划算)
· 简介太长,后半段模型看不到:3 个(占 75%);名字都以 nature- 开头,多半同一套模板

模型看不到部分触发说法(3 个)
· nature-alpha(565 字)被切掉:分段引用、自动给出引用

想要带处方的完整报告,仍然在会话里说「体检技能」让模型跑 skill_doctor——两条路读的是同一套扫描逻辑。

为什么菜单是中文、输入框是 ASCII:菜单那一列由插件提供,没有字符集限制;输入框里把文本渲染成 chip 走的是 dsh 的 TEXT_REF_RE = /(^|\s)([/@])([\w-]+)/g,JS 的 \w 只有 [A-Za-z0-9_],中文名永远匹配不上。所以「中文名 + 蓝色 chip」在当前 dsh 上互斥,这里选了保住 chip。这些 token 由插件注册的引用源(ctx.inputTriggers.registerSource,技能的 /名字 用的就是它)提供,提交时原样交给命令执行。

面板上六个按钮写的就是这六个 token:点「复诊对比」= 输入框出现 /followup ,补完参数回车即可。

如果哪天 dsh 把上面那条正则放宽到 \p{L},把 src/source.ts 里候选的 token 换成中文即可,其余逻辑不用动。

四个工具

工具 · 一句话 · 什么时候用

skill_doctor · 技能体检,结论优先 · 默认先用它

skill_lint · 逐条规则,技术口径 · CI、想按规则 ID 排查

skill_explain · 解释单个技能为什么不生效 · 已经锁定是哪个技能

plugin_doctor · 插件装得对不对 + 跑起来没 · 工具突然不见了

skill_doctor

{ "name": "nature-citation" }

省略 name 就是全体检。范围参数有两种,语义不同:

  • roots:只查这些目录(或直接给某个 SKILL.md)。用户点名目录时必须用它,报告开头会写「本次只检查了指定范围」。
  • extraRoots:在会话可见技能之外追加(查同名遮蔽时用),原有技能仍在报告里。

加 "rewrite": true 进入改写工单模式:不带 name 时给所有简介被截断的技能出工单(同前缀的归成模板组,写一份模板 + 各自变量段);带 name 出单技能工单。工单 = 硬约束 + 模型可见/被切原文 + 提取的触发说法 + 确定性骨架——模型填稿、用户确认后才写入,写完重跑体检用复诊对比验证闭环。

工单确认之后,写入有两个参数(都必须带 name,批量写入不开放,避免一次误改一堆文件):

{ "name": "nature-citation", "setDescription": "你确认过的那段简介" }
{ "name": "demo-skill", "fix": true }
  • setDescription:只替换 frontmatter 里 description 那一行(JSON 双引号标量,引号/冒号/# 安全转义,其余字节与换行符原样保留),写前 .bak、写后复验失败自动回滚;块标量写法会明确拒绝,请手动改。工具不生成也不改写文案,写的就是你给的那段。
  • fix: true:只跑可自动修的规则(BOM / CRLF),plan → apply → 复验(重扫、错误数不得变多),失败回滚。

输出顺序固定为「结论 → 证据 → 处方」:

  • 结论:一句话说清能不能用
  • 摘要表:技能、简介长度、截断影响、建议动作
  • 模型看到的信息:模型实际读到的简介原文(截到上限为止,不做二次摘要)
  • 被切掉的部分:截断位置 + 被切掉的触发说法 + 影响大/小
  • 需要处理的问题:每条都是「现在什么情况 + 处方」,不写规则名
  • 技术细节(可跳过):provider、model-invocable、规则 ID 集中在最后

skill_lint

逐条规则的 verbose 模式,规则 ID 直接写在每条前面,按「需要修复 / 描述被截断 / 其他提示 / 正常」分组。

参数 · 说明

name? · 只查一个技能;省略则查全部

provider? · 只看某个 provider

rules? · 只跑指定规则,例如 ["SD003","SD004"]

severity? · info / warn / error,默认跟随配置

extraRoots? · 额外扫描的根目录,用于发现同名遮蔽

format? · markdown(默认)或 json

`skill_explai

…(truncated)

以上内容来自 npm 上的 README,我们只做格式化,不改内容。

版本

npm 上共发布:10,最后更新 2026-11-04

链接

GitHub · Homepage · npm

同类插件