dsh-skill-manager
English | 简体中文
为 DeepSeek Harness (DSH) 桌面版提供 skill(技能)管理界面:列出宿主实际扫描的 skill 根目录、 标注每个 skill 的来源与有效性、新建 skill、从文件夹导入、在系统文件管理器中打开目录。
本插件以独立 bundle 形式装入 DSH profile,不接管官方 skill 的扫描与热加载。
目录
问题背景
官方桌面版已具备完整的 skill 能力,但没有任何可视化入口:
| 能力 | 提供方 | 是否随官方发行 |
|---|---|---|
| 扫描 skill 根、解析 frontmatter、热加载 | @deepseek-ai/dsh-skill-filesystem |
是 |
/ 唤起 skill |
@deepseek-ai/dsh-client-ui-skill |
是 |
| 列出扫描到的根、标记无效文件、新建与导入 | —— | 否 |
因此当 frontmatter 写错、或 skill 没有被加载时,用户只能自己去翻目录与日志。
本插件补的就是这一层:它不参与加载,只在文件层面做增删查,并如实展示宿主扫描的根目录 与每个 skill 的来源。因此它与官方插件同时启用不会冲突 —— 一个负责装载,一个负责文件。
环境要求
| 项 | 要求 |
|---|---|
| Node.js | ^22.19.0 || >=24 |
| DeepSeek Harness | 官方桌面版 |
| 依赖 | yaml(安装时自动装好) |
安装
安装分两步:先装进 profile,再把包名登记进 bundle 层栈。
# 1. 进入桌面版的 profile 目录(Windows 默认:%USERPROFILE%\.dsh\profiles\desktop)
cd "<DSH_HOME>/profiles/desktop"
# 2. 用官方桌面版随包的 pnpm 装进 profile
pnpm add "file:<插件目录绝对路径>"
# 或者直接从 GitHub 装
pnpm add "github:VCPr0j3k7/dsh-skill-manager"
「随包的 pnpm」指 <安装目录>/resources/runtime/pnpm/bin/pnpm.cjs。若它不在 PATH 中,
用随包的 node 直接调用即可:
NODE="<安装目录>/resources/runtime/primary-runtime/dependencies/node/bin/node.exe"
PNPM="<安装目录>/resources/runtime/pnpm/bin/pnpm.cjs"
"$NODE" "$PNPM" add "file:<插件目录绝对路径>"
"$NODE" "$PNPM" add "github:VCPr0j3k7/dsh-skill-manager"
必须登记 bundle 层栈
pnpm add 只会写入 dependencies。DSH 只装载 dsh.profile.bundles 中列出的 bundle,
因此必须手动把包名追加进 profile 的 package.json:
{
"dsh": {
"profile": {
"bundles": [
"@deepseek-ai/dsh-base",
"@deepseek-ai/dsh-web-app",
"dsh-skill-manager" // ← 追加这一条
]
}
}
}
漏掉这一步的表现是:安装过程没有任何报错,依赖目录也在,但插件完全不加载 —— 界面上没有入口,日志里也没有记录。这是本项目最容易踩的一步。
dsh plugin --profile desktop add ...会在 pnpm 结束后自动运行reconcilePlugins(), 由它补上这一条。但官方桌面版通常不提供可直接调用的dshCLI, 走pnpm add的路径时就需要手动登记。
确认已登记:
cd "<DSH_HOME>/profiles/desktop" && cat package.json
安装完成后需重启桌面版。宿主只在启动时装配插件树,运行中的实例不会热更新。
卸载
cd "<DSH_HOME>/profiles/desktop"
"$NODE" "$PNPM" remove dsh-skill-manager
随后从 dsh.profile.bundles 中删除 "dsh-skill-manager" 一行。卸载后同样需要重启桌面版。
卸载不会删除任何 skill 文件:本插件只向 <DSH_HOME>/skills 写入,从不清理该目录。
工作原理
插件由两个半边组成,各自独立运行:
| 半边 | 运行位置 | 形态 |
|---|---|---|
| 宿主半边 | DSH 宿主进程(Node.js) | Cordis 插件,导出 name / inject / apply(ctx) |
| 客户端半边 | 桌面版窗口(浏览器) | React 页面,经 window.__ModuleLoader__.load({ id, factory }) 装载 |
宿主半边在 ctx.webServer 上注册一条 kind: 'prefix' 路由
/dsh-skill-manager/api,前缀内部的路由派发由 host/router.mjs 完成。
这样做的好处是注册点只有一处,卸载时由框架统一清理,不会在 webServer 中留下半张路由表。
所有响应统一为 { ok: true, data } 或 { ok: false, error },业务失败也返回 HTTP 200 ——
状态码只用于表达「路由不存在」这类传输层事实。
客户端半边在首次请求时探测宿主基址。页面的来源可能是宿主自身的 HTTP 地址、
外壳的自定义 scheme(dsh-app://app/),也可能是不透明来源(location.origin === "null")。
因此它按顺序尝试相对路径与官方虚拟主机名 http://dsh.internal,并以 /info 返回的
data.plugin 是否等于本插件 id 作为判定依据 —— 仅凭 HTTP 200 不够,SPA 的兜底路由
会把未知路径也返回 200 + HTML。选定后缓存复用。
共享配置:本插件与同系列的其他插件共用 <DSH_HOME>/dsh-extras.json,
通过 GET /config 读取、POST /config 深合并写回。本插件不定义自己的配置项,
但 GET /info 会带上其中的 appName 字段。
日志:宿主半边的日志写入 <DSH_HOME>/plugin-data/logs/dsh-skill-manager.log,
同时在内存中保留最近 2000 行环形缓冲。
界面与操作
侧边栏的「Skill 管理」入口打开面板,包含四项操作:
| 操作 | 行为 |
|---|---|
| 新建 Skill | 表单生成 <DSH_HOME>/skills/<名称>/SKILL.md。名称需符合 kebab-case,description 与正文必填;同名目录已存在时拒绝写入 |
| 从文件夹导入 | 弹出系统目录选择器。若所选目录含 SKILL.md,整个目录按原目录名拷入用户 skill 根;否则把该目录下的平铺 .md 逐个拷入。目标已存在时跳过,不覆盖 |
| 打开 skill 目录 | 在系统文件管理器中打开用户 skill 根;点击某条 skill 的「打开位置」则定位到该文件 |
| 重新装回内置 skill | 本插件中始终报告「不适用」,详见下文 |
面板同时展示:
- 按 rank 分组的 skill 列表,每条显示名称、描述、何时使用、来源根、正文大小与正文预览;
- 「随包内置」标记(依据记账文件判定,见下文);
- 「被忽略的文件」列表,逐条给出 frontmatter 解析失败的原因;
- 一张扫描根目录说明表(rank、路径、目录是否存在)。
关于「重新装回内置 skill」:本插件不适用
官方桌面版不随插件发行内置 skill:随包 skill 由运行时自行管理。因此宿主半边把
BUNDLED_DIR 固定为 null,POST /skills/restore-bundled 会明确返回:
官方桌面版不随插件发行内置 skill(随包 skill 由运行时自行管理),此项不适用
界面上的按钮保留,但点击后显示的就是上面这句话。列表中的「随包内置」一栏同样始终为空。
host/services/skills.mjs 中仍保留了一套完整的随包 skill 安装与记账实现
(installBundledSkills / restoreBundledSkills / bundledSkillNames,
记账文件为 <DSH_HOME>/bundled-skills/.installed.json),但在本插件中
bundledDir 恒为 null,这些函数不会安装任何内容。
skill 的发现规则
官方按 rank 顺序扫描以下根目录,同名的 skill 以 rank 较小者为准。每个根只扫描一层,
不支持嵌套的 SKILL.md:
| rank | 来源 | 路径 | 可写 |
|---|---|---|---|
| 100 | 当前项目(.dsh) |
<项目根>/.dsh/skills |
是 |
| 200 | 当前项目(.agents) |
<项目根>/.agents/skills |
是 |
| 400 | 桌面版用户目录 | <DSH_HOME>/skills |
是 |
| 500 | 共享 agent 目录 | $DSH_AGENTS_HOME/skills(默认 ~/.agents/skills) |
是 |
| 600 | 随包内置 | 官方 bundledSkillDir,本插件不涉及 |
否 |
「项目根」取最近的含 .git 的祖先目录(最多向上 24 层),没有则使用当前工作目录。
一个 skill 可以是:
- 目录 bundle:
<名称>/SKILL.md,可附带references/、scripts/、assets/等资源; - 平铺文件:
<名称>.md。
以 . 开头的条目会被跳过。SKILL.md 必须以 --- 包裹的 YAML frontmatter 开头:
| 字段 | 必填 | 说明 |
|---|---|---|
name |
是 | 仅小写字母、数字与短横线(^[a-z0-9]+(?:-[a-z0-9]+)*$) |
description |
是 | 模型据此决定是否使用该 skill |
whenToUse |
否 | 何时使用 |
user-invocable |
否 | 设为 false 时用户不可手动调用 |
disable-model-invocation |
否 | 设为 true 时模型不可自动调用 |
布尔字段按严格布尔解析:接受 true / false,也接受 yes / no / on / off / 1 / 0
等字符串写法;其余值视为未设置(与官方一致)。
frontmatter 由 yaml 包解析,并且刻意使用运行时自带的那一份 ——
换用其他 YAML 库可能对边界写法给出不同结论,从而与宿主的判断不一致。
skill 是热生效的:新建或导入后无需重启宿主,官方 watcher 会在下一个模型步骤刷新目录。
宿主接口
所有接口挂在 /dsh-skill-manager/api 下。
插件自有路由
| 方法 | 路径 | 请求体 | 返回 |
|---|---|---|---|
GET |
/skills/list |
—— | { roots, skills, invalid, home, userRoot, bundledDir } |
POST |
/skills/create |
{ input } |
{ path, name } |
POST |
/skills/import |
{ sourceDir } |
{ imported, skipped, root } |
POST |
/skills/restore-bundled |
—— | 恒为「不适用」错误 |
POST |
/skills/open-dir |
{ target? } |
{ opened },缺省打开用户 skill 根 |
公共路由
以下路由由 host/router.mjs 统一提供(本插件的页面只用到其中的一部分):
| 方法 | 路径 | 说明 |
|---|---|---|
GET |
/info |
插件 id、宿主状态、运行时目录等;客户端以 data.plugin 判定基址是否可用 |
GET |
/config |
读取 <DSH_HOME>/dsh-extras.json |
POST |
/config |
深合并写回该文件 |
POST |
/open-external |
用系统默认程序打开 http/https 链接,其他协议一律拒绝 |
POST |
/pick-directory |
系统目录选择器;优先使用官方 ctx.directoryPicker,否则在 Windows 上用 PowerShell 兜底 |
GET |
/restart-pending |
profile 的关键文件是否比宿主就绪时刻更新 |
POST |
/restart-host |
恒定拒绝:插件无法重启承载自身的进程 |
GET |
/events |
事件增量拉取(?since=<seq>),客户端每 300ms 轮询一次 |
启用条件
- 宿主半边:包名出现在 profile
package.json的dsh.profile.bundles数组中, 且 profile 已重新装载(重启桌面版)。没有环境变量开关。 - 客户端半边:页面由官方桌面版外壳提供。基址探测成功后界面可用;探测失败时界面仍会渲染, 但所有数据请求都会失败并显示错误信息。
测试
npm test
# 等价于 node test/check.mjs
test/check.mjs 是纯 Node 自检(35 项),不依赖 DSH 运行时,也不需要把插件装进 profile。
它使用临时 DSH_HOME 与临时 APPDATA,不会读写用户的真实目录。覆盖范围:
package.json清单契约:dsh.bundle.patch、dsh.client.platform、exports与files所指向的文件真实存在;- 宿主半边可被 import,导出
name/inject/apply,apply(ctx)不抛并注册前缀路由; - 路由对齐:从
client.js中提取全部request("METHOD", "PATH")调用(当前 10 条), 逐条打到真实路由表上,确认没有一条落到 404;同时校验两侧的路由前缀一致; host/config.mjs的默认值包含githubToken等字段,且配置文件路径落在当前DSH_HOME之下。
路由对齐是本项目的核心契约:客户端半边是构建产物,一旦它调用的路由在宿主侧不存在, 表现只是界面上某个按钮静默失败(宿主回 404,页面只显示一句笼统的错误)。
端到端验证:装入并重启桌面版后,侧边栏应出现「Skill 管理」入口,面板能列出
<DSH_HOME>/skills 下的 skill。
已知限制
- 「重新装回内置 skill」不适用。 官方桌面版不随插件发行内置 skill,该操作恒定返回错误(详见上文)。
- 宿主无法重启自身。
POST /restart-host恒定拒绝;需要重启时请手动退出并重新打开桌面版。 - 「打开 skill 目录」要求目录已存在。 若
<DSH_HOME>/skills尚不存在(例如全新安装且从未创建过 skill),该操作会报「路径不存在」。先用「新建 Skill」或「从文件夹导入」创建一次即可。 - 只扫描一层。 与官方一致,根目录下的子目录不会被递归查找,嵌套的
SKILL.md不会被发现。 - 不接管加载与卸载。 扫描、解析、热加载均由官方
dsh-skill-filesystem负责;本插件只做 文件层面的增删查。界面上「有效 / 被忽略」的判断是对同一套规则的重演,不是另一套判定。 - 导入不覆盖同名文件。 目标已存在时跳过并在结果中列出;需要覆盖请先手动删除。
client.js是构建产物。 它由构建脚本从桌面版前端源码切片生成,其中包含若干本页未使用的 组件(安装进度环、README 抽屉、重启提示条、若干图标等,来自同一套切片的其他面板)。 这些组件不参与本插件的渲染;直接手工修改会在下次构建时被覆盖。- 界面文案为中文。 当前版本未做多语言。
No comments yet. Be the first to write one.