# 技能管理 + 技能市场

**中文** | [English](skills.en.md)


本插件同时有两个能力面：**MCP** 与**技能**。面板顶部的能力层枚举用来切换，两者共享同一个悬浮窗与侧栏入口。

技能能力面解决的是 DSH 自带技能目录**看不到**的那些事：

- 技能到底装在哪个作用域（同名技能跨作用域是合法配置，但模型只会收到生效的那一份）；
- 哪些技能被**停用**了，以及一键启停（写的是 `SKILL.md` 的两个调用面字段，见[启用与停用](#启用与停用)）；
- 哪些技能**允许被模型自动调用**、哪些**允许你手动调用**，以及就地开关；
- 装市场里的技能前，你被如实告知了什么、必须确认什么（披露 + 一次显式确认，不做许可判定）。

⚠️ 一个**还没做**的事：宿主对 frontmatter 非法的技能只 `warn` 后跳过，那种技能目前**不会**出现在面板里（要报它得自己扫各根的技能文件并跑一遍校验，见 TODO row 21）。面板不会假装能看到它——所以遇到这种技能时，面板给出的调用权限是「未知」而不是一个猜的值。

## 打开技能面板

点右下角悬浮按钮或侧栏「MCP」入口打开面板，再点顶部的能力层「**技能**」。

面板分两个模式（第二行左侧的分段控件；两个能力面共用这一条、这个位置、这个控件）：

| 模式 | 用途 |
|---|---|
| **已安装** | 左列表 + 右详情。看本机已有的技能，启停、改调用权限、删除、查看正文与诊断 |
| **发现市场** | 卡片网格。**只有一个来源：skillhub.cn**（外部源、直连上游、只做披露 + 显式确认，也是唯一可安装的来源） |

**作用域药丸只在「已安装」模式出现**，而且与列表分组同源（点哪个药丸，就筛出哪一组，
数字与筛出来的行数一定相等）：市场是目录，装上之后才有作用域，所以安装时才问你装到哪。
MCP 面的市场走的是同一个模式的同一种形态（内联网格，不再是弹窗）。

## 作用域

技能可以装在多个位置，DSH 按优先级依次查找。面板里的作用域候选**来自实际发现的位置**，不是写死的清单——本机就可能没有 `~/.dsh/skills`，技能全在 `~/.agents/skills`。

| 优先级 | 位置 | 说明 |
|---|---|---|
| 100 | `<项目>/.dsh/skills` | 项目级 |
| 200 | `<项目>/.agents/skills` | 项目级 |
| 300 | 自定义目录 | 由宿主配置 |
| 400 | `<DSH_HOME>/skills` | 全局 |
| 500 | `<AGENTS_HOME>/skills` | 全局 |
| 600 | 内置（bundled） | **只读**，不可启停/删除 |

同名技能出现在多个作用域时，面板会标出**哪一份生效、哪一份被遮蔽**——被遮蔽的那份模型收不到，但你确实装了它。生效者按优先级数值**最小**者判定，与它在列表里的位置无关。⚠️ 边界：宿主按名字只报告**生效**的那一份，所以落选的副本只有在**两个调用面都关掉**（= 已停用，那种条目照样会出现在宿主目录里）时才看得见；两份都可调用时，落选那份目前不在面板数据里（要报它得自己扫各根，见 TODO row 21 旁边的同名副本扫描）。

### 同一个目录被两处指到时

优先级表里的两个位置**可能算出来是同一个目录**——最常见的是 `DSH_HOME` 就在工作区里：设 `DSH_HOME=D:\DSH\.dsh`、工作区是 `D:\DSH`（它自己是 git 根），那么项目级 `.dsh/skills` 与全局 `<DSH_HOME>/skills` 是同一个物理目录。

面板按**物理位置**去重：同一个文件只占一行，留优先级更前的那份（也就是模型实际收到的那份）；安装目标的候选里同一个目录也只出现一次。反过来，**不同目录**里的同名技能一定各占一行——那是真实的跨作用域遮蔽，合并它会把你真装了的那份藏起来。

同理，技能目录本身是**符号链接**（把它们指向外部同步库是常见做法）时，面板照样能定位到它：宿主报的展示路径与真实路径不一致这件事已经被处理，不会退化成「位置未知」。

## 启用与停用

DSH 的技能没有 `disabled` 字段（MCP 有，技能没有）。**但宿主原生支持的两个调用面字段，组合起来就是「停用」**——这也是 VS Code / Copilot 官方文档里叫 **“Disabled skills”** 的那一档：

| 你想要的效果 | `disable-model-invocation` | `user-invocable` |
|---|---|---|
| 都能用（缺省） | 不写 | 不写 |
| **仅你手动可调用**（模型不自动用） | `true` | 不写 |
| **停用**（模型不自动用，你也不能手动调用） | `true` | `false` |

所以面板的「停用」写的就是这两个字段：模型拿不到它、你也不能用 `/名称` 调用它，
而技能**仍在磁盘原处**、宿主照样报告它、面板照样列出它。

- **不搬文件**。技能文件不会被改名、移动或删除——你可以继续用编辑器打开它，
  其他也读 `~/.agents/skills` 的工具（VS Code / Copilot、Codex 等）也不会因为这里的开关而
  认为你的技能坏了。⚠️ 1.6.x 及更早的面板用的是「改名成 `SKILL.md.disabled`」那套自创做法，
  它会让你在**别的工具里**的技能显示异常；旧版本留下的这种文件面板仍能列出来并一键恢复，
  恢复后请用新的停用方式。
- **两个面在详情页各自可拨**（「模型自动调用」与「用户可调用」各一个开关），「已停用」是
  两者都为「否」时的**派生显示**——不是第三个状态，所以不会与文件里的内容对不上。
- ⚠️ 关于「启用」：它把两个面**都**恢复成可调用。若你原本只要「仅你手动可调用」那一档
  （手工设过 `disable-model-invocation: true`），一次「停用→启用」往返会把它重置——
  但**面板上就能拨回来**（把「模型自动调用」关掉即可），不必去编辑文件。
- 内置（bundled）技能只读，不可启停或删除。

## 允许/禁止模型自动调用

技能可以被**模型自动调用**（模型自己判断该用它），也可以只在你手动点名时才可用。DSH 用 frontmatter 的
`disable-model-invocation: true` 表达后者；面板把它做成详情页「技能属性」里的一个开关（**模型自动调用**），
不必再去手工翻 `SKILL.md`。列表行上那个「仅用户可调用」徽章说的就是同一件事。

它的**反面**是 `user-invocable: false`（你也不能手动调用它）。两个都关掉就是「已停用」，
见上一节。面板把两者**各自**做成一个开关，因为它们的效果不同、可以独立组合：

| 你想要的效果 | 模型自动调用 | 用户可调用 |
|---|---|---|
| 都能用（缺省） | 开 | 开 |
| 仅你手动可调用（模型不自动用） | **关** | 开 |
| 仅模型可自动调用（你别手动调） | 开 | **关** |
| **停用** | **关** | **关** |

- **状态一律取自技能文件本身**，不取宿主的发现缓存：宿主对同一个文件的改写会继续报旧值（实测过），
  而这里正是面板要**写**的东西——写盘后显示回退，用户看到的是「点了没反应」。所以行与详情里的调用权限都是
  从那个文件里解析出来的，与正文同一份字节。
- **读不出来就说「不知道」**：frontmatter 非法的技能宿主会整条跳过，此时面板显示「调用权限未知」且开关不可点。
  编一个「不可调用」出来就是撒谎。
- **定位按磁盘上的真实位置，不按技能名**：宿主允许技能文件/目录的名字与 frontmatter 里的 `name` 不一致
  （技能名取自 frontmatter），面板因此按「宿主报告过的根 + 那个根下的条目」定位。这类技能以前在面板里
  所有操作都会失败（读正文、启停、删除全报「找不到技能」），现在与其他技能一视同仁。
- **只改那一行，不重排你的文件**：关闭时把那**一行**删掉（缺省就是「允许」，所以关掉不留痕迹，开→关能逐字节还原原文）；
  打开时若已有该字段就地把值改成 `true`，没有才在 frontmatter **末尾**追加一行。字段顺序、缩进、正文、CRLF 风格都不动。
- **形状读不准就拒绝**：值是块标量或跨了多行、要在带行尾注释的那一行上**打开**它（关掉是整行删除，
  边界确定，注释随行而去）、或文件里有宿主会拒绝的驼峰键（`disableModelInvocation`）时，
  面板会拒绝并在界面上说明原因，让你手工改——猜一个改法等于改坏你的技能文件。
- **生效时机**：写的是文件，宿主发现这条技能变了之后才影响模型——**通常不到一秒**。
  宿主用文件监听盯住技能根，改动会自动生效（实测约 0.25 秒：8 次采样的中位数是 246ms），面板显示的就是磁盘上的值，
  **不必重启 DSH**。唯一例外：宿主启动时若被配置成不监听（`skill-filesystem` 的 `watch: false`），
  那才需要重启——真机默认是监听的。详见下一节。
- 内置（bundled）技能只读，开关呈**禁用**（与启停/删除同一约定：渲染出来但点不动）。

## 改了技能，多久生效

**宿主侧：自动、很快。** DSH 用文件监听盯住每个技能根，技能的新增、改名、删除与内容改动
都会让它作废自己的技能目录——**实测约 0.25 秒**（8 次采样中位数 246ms），无需重启。你在面板里启停/改权限之后，
模型下一次看技能列表时就是新的（本面板写的就是那个文件）。

**面板侧：切回标签页时重取。** 面板不能做到"被推送"——宿主有个 `skills/change` 事件，
但它**不在**宿主转发的白名单里，而那份白名单由宿主自己独占（只允许注册一个源），
第三方插件没有合法途径把它送到浏览器。所以面板采取的是：**动作后重取 + 切回标签页时重取**。
对用户的表现是「切回来就是新的」，而不是假装能实时推送。

⚠️ 如果你改的是**技能文件本身**（用编辑器），面板要等你切回标签页才刷新；而模型侧不用等，
宿主自己就发现了。

## 安装技能

市场里点「安装」会先给你一份披露（安装确认弹窗）：

- **技能正文会作为指令进入模型上下文** —— 这是技能的工作原理，也是它最主要的信任面；
- 是否**含可执行脚本**（`scripts/` 目录）；
- 技能**声明**的工具权限（`allowed-tools`）。注意 DSH 宿主并**不**强制这个字段，
  它只是作者声明的意图——正因如此才要如实披露，让你自己判断。

**市场只有 skillhub.cn 这一个来源，它当然就是唯一可一键安装的来源。** 面板下载到临时目录，落盘交给宿主的 `importSkill`。

安装确认弹窗另外把上游能提供的两件事摆出来——发布方企业认证（`publisher.certifiedName`）与腾讯安全扫描结论（`securityReports`）——并写明是否要求 API Key，明处写上「本插件未核验」。**本插件不做许可判定**：上游条目里没有 license 字段，也不替你编一个。确认是**服务端**校验的（`acknowledged` 必须上 wire），UI 里的勾选框只是它的界面。

确认后选择装到哪个作用域（全局或某个项目），再执行安装。宿主的导入层支持三种形态：

| 形态 | 装成什么 |
|---|---|
| 单个 `.md` 文件 | 扁平技能 `<name>.md` |
| 技能目录 | 目录捆绑 `<name>/`，连带 `scripts/`、`references/` 等资源 |
| 压缩包 | `.zip` / `.tar` / `.tar.gz` / `.tgz`，按**魔数**识别（下载链接可以不带扩展名） |

⚠️ 这三种形态目前**没有面板入口**（只有宿主 RPC），一键安装走的是外部源的压缩包那条路。要手动导入需要等这个入口做出来——在那之前，面板不会假装它存在。

安装按 frontmatter 里的 `name` 落盘（不是文件名）——宿主就是这么认定技能身份的。
同名技能已存在时会**拒绝而不是覆盖**；压缩包里若有多个技能则拒绝（不替你猜装哪个）。

## 市场的来源

市场只有 **skillhub.cn** 一个来源，不做来源切换。它是外部源，直连上游（`api.skillhub.cn`，公开无鉴权）实时搜索与分类，**也是唯一可一键安装的来源**。信任处理上**不做许可判定**（上游条目里没有许可信息）：安装前把发布方认证、安全扫描结论、是否需 API Key 摆出来，明确写「本插件未核验」，并要你**勾选确认**后才可安装。

请求由**宿主侧**发出——面板本身不联网，所以也不存在浏览器 CORS 问题。它的搜索是**显式触发**的（回车 / 切分类），不是随打随搜；分类 key 来自上游，标签来自它的分类接口，取不到就显示原始 key。

装 skillhub.cn 的技能时，落点仍由你选（全局或某个项目 × `.dsh` / `.agents`），并且与本地导入走**同一条**写入路径：先下载到临时目录，再按同一套校验解压、挑技能根、拒绝重名。

上游真挂了只在结果区给一条告警 + 重试，不会让市场页打不开。若你刚更新过面板却看到「外部源不可用」，多半是**宿主进程还是旧的**（它不认识 `skillHubSearch` 这个接口，wire 上就是 404）——重启 DSH 后即可；这种情况下上游一次都没被访问过，所以面板不会把它说成「连接 skillhub.cn 失败」。

## 许可

**本插件不做许可判定。** skillhub.cn 的条目里**没有 license 字段**，我们也不替你编一个。面板能拿到的只有发布方企业认证（`publisher.certifiedName`）与腾讯安全扫描结论（`securityReports`），以及是否要求 API Key——这是上游唯二可信的东西，摆出来给你看，并在明处写「本插件未核验」。

安装前需要你**显式确认**（勾选），而且确认是**服务端**校验的：`acknowledged` 必须上 wire，UI 里的勾选框只是它的界面。

原来那条**逐技能许可门禁**已随内置精选目录一起下线（2026-10-10）。安全边界因此变成「外部源不做许可判定 + 披露 + 显式确认」，不再是一条硬闸门——你会看到更少的自动判断、更多的原始信息，这是刻意的。

**收录 ≠ 背书。** 技能正文会作为指令进入模型上下文，且可能含可执行脚本。上游收录了它、安全扫描放过了它，都不等于它安全——本插件不做安全审查。安装前请自己确认来源可信。

## 搜索

- 搜索由 **skillhub.cn** 提供（市场只有这一个来源），走它自己的搜索与分类接口；
- 搜索是**显式触发**的（回车 / 切分类），不是随打随搜；分类 key 来自上游，标签来自它的分类接口，取不到就显示原始 key；
- 结果是**分页**的：每页 100 条（上游上限），滚到底自动加载下一页，底部也有「加载更多」按钮；计数显示「已显示 X / 共 Y 条」，到头就不再发请求。换关键字/分类会重置回第 1 页并整体替换；
- 远端结果**不做**许可核实（上游条目没有许可字段），别把「上游收录」当成「已核过许可」；
- 失败不会让市场页打不开：宿主进程是旧的（wire 404）就提示重启宿主，上游真挂了就在结果区告警 + 重试。

## 相关文档

- [`design.md`](design.md) —— 包结构与深层设计（共享连接模型、typert 契约等）
- [`installation.md`](installation.md) —— 安装与升级
- [`json-import.md`](json-import.md) —— 从 JSON 导入 MCP 配置
