前言¶
DSH 的理念是「一切皆插件」。实际写过插件的人大多会遇到三类问题:从零起一个新插件时,规范散落在会话里,每次都靠口述;装了一个别人写的插件,想知道它注册了哪些槽位、路由和 UI 结构,只能整文件读,token 烧得很快;想把某个插件的写法搬过来或改成自己的版本,没有明确的改造路径,改完也不知道有没有破坏原行为。
wodaimnd/dsh-plugin-forge-preset 针对的就是这三件事。它是维护者 wodaimnd 写的一个 DSH agent 预设(不是普通插件),装上后模式下拉里多出一项「插件工坊」,会话里可以直接说「帮我写个插件」,也可以说「看看这个插件怎么实现的」「把这个插件改成我要的样子」。它的定位是把插件开发的规范固化在文件里,而不是活在一次会话里。
它是什么¶
一句话:一个预设容器。presets/ 下每个子目录是一个模式,plugin-forge 是正式的「插件工坊」,demo-mini 是架构验证用的演示模式(可删)。包版本 1.0.0,MIT 许可,作者 Kewei Chen。社区目录里的分类为「客户端」,星标数为 0。
它和逆向模式(reverse)的区别值得先说清楚。两者都做「拆开别人的东西」,但对象不同:
- 逆向模式拆 APK / IPA / PE / SO / 协议,产出是算法还原、脱壳产物;
- 插件工坊拆的是 DSH 插件的 JS bundle,拆的是槽位注册、路由、工具行、UI 结构,产出是能安装的新插件。
插件源码本来就是可读的 JS,所以 jadx / Frida / IDA 这套工具链在这里用不上。
四个执行角色¶
插件内部有四个角色,并发上限 3 个,都是叶子节点(maxDepth: 1),不会无限往下派任务:
| 角色 | 干什么 | 触发时机 |
|---|---|---|
| 插件测绘 | 出结构地图、契约表、「能改/慎改/别碰」三分 | 要读懂一个插件时 |
| 代码吸收 | 判断哪块值得吸、迁入并验证行为不变 | 要借别人写法时 |
| 改造分叉 | 选路线(原地改 / 复制分叉 / 补丁层)、七处替换、双验证 | 要改一个插件时 |
| 回归验证 | 桩环境结构验证 + 与原版差异对比 | 每次改造后 |
技能清单¶
说明一下:README 的小节标题写的是「六个技能」,但下面的表格实际列了 7 项,按表格为准。
| 技能 | 作用 | 关键产出 |
|---|---|---|
plugin-author |
从零写插件:对齐 → 代码 → 16 项自检 | 完整可安装插件 + 规范全文 |
preset-scaffold |
加新 agent 预设:建目录 → 写两件套 → 验证 | 新模式的完整模板与约束 |
plugin-search |
代码定位:大纲 → 结构化搜索 → 定向读取 | 精确行号,不整文件读 |
plugin-index |
跨文件盘点:符号 / 引用 / 槽位 / 路由 | SQL 级查询,替代重复扫描 |
plugin-recon |
摸清别人的插件 | 结构地图 + 契约表 + 三分清单 |
plugin-harvest |
吸收可用代码 | 吸收清单 + 迁入代码 + 验证脚本 |
plugin-fork |
改造成自己的版本 | 改造方案 + 分叉产物 + 双验证 |
技能装在 $DSH_HOME/plugin-forge/skills/,由插件启动时从包内同步。所有预设共享这一个技能库,所以给插件工坊加的技能,新预设自动可用。
检索工具与 token 预算¶
这是插件里最实用的部分。四个工具按问题类型选:单文件降维用大纲器,跨文件查库用索引库,按语法结构找用 ast-grep,找字面量用宿主 grep。
下面是资料里给出的实测数据:
| tokens / 耗时 | |
|---|---|
| 全文读 6 个真实插件文件 | 95,064 tokens |
| 只读结构大纲 | 1,470 tokens(省 98.5%) |
| 单文件最极端(222KB) | 全文 60,876 → 大纲 835 tokens |
| 建全库索引(126 文件 / 1.5MB) | 456ms |
| 索引库单次查询 | 0.05ms |
用法如下,第一步是单文件或单插件的大纲:
# ① 单文件或单插件:先出大纲(读大文件之前必做)
node tools/outline.mjs '<插件目录>' --tokens
第二步是跨文件问题,建一次索引,之后都走查询:
node tools/index.mjs build 'G:\AI PGRM\DSH\plugins'
node tools/index.mjs find PanelPage # 全库几处定义
node tools/index.mjs refs handleOperation # 谁引用了它
node tools/index.mjs slots # 哪些插件注册了什么槽位
node tools/index.mjs routes # 全部路由
node tools/index.mjs effects network # 所有网络请求点
node tools/index.mjs stats # 库规模 + 同名多定义
第三步是结构化搜索,README 强调规则:ast-grep 的 pattern 必须带 $OBJ. 前缀,否则会漏掉 ctx.slots.register(...) 这种最常见写法:
ast-grep run -p '$OBJ.slots.register({ name: $NAME, $$$REST }, $COMP)' -l js '<目录>'
ast-grep run -p '$OBJ.webServer.register({ $$$REST })' -l js '<目录>'
拿到行号后再定向读,而不是整文件读。硬规则也写在 README 里:读任何大于 20KB 的文件之前,必须先跑 outline。
ast-grep 需要自行安装,它 55MB,不在本包内,期望版本 0.51.1 或更高。不在 PATH 时插件会自动降级为「大纲器 + 宿主 grep」,功能可用但结构匹配精度下降。另外 sg.exe 是弃用别名,不要用。
索引库有三个已知边界:引用是「调用点 + 大写参数」而非全量标识符,小写变量名作参数传递时查不到,这时改用 ast-grep;动态路由标注为 (动态: route),不做猜测;增量更新依赖 mtime,改文件但 mtime 没变会滞后,删库重建即可。
提示词固化在哪¶
这是插件存在的核心目的:规范不能只活在会话里。提示词的分布如下:
| 位置 | 内容 | 大小 |
|---|---|---|
presets/plugin-forge/persona.md |
人设与判断规则(唯一真源) | 19KB |
presets/plugin-forge/agent.cordis.yml |
条目列表,人设位置用 %%PERSONA%% |
8.6KB |
skills/plugin-author/SKILL.md |
执行骨架:三步流程、判据表、输出格式 | 4.2KB(渲染 4261/8192) |
skills/plugin-author/references/ |
runtime-contract / skeletons / ux / data-and-ownership / gates / failure-modes / prohibitions | 合计约 57KB |
docs/prompt-v2.1.md |
完整规范原文(含逐条理由与变更史) | 70KB |
拆成 SKILL.md + references 的原因很实际:宿主对 SKILL.md 有 8192 字符硬上限,超了会被修剪器裁掉后半段。SKILL.md 只放骨架与导航,长文按需读 references/,不计入预算。
改人设的正确方式是改源文件再加校验:
# 1. 改 presets/plugin-forge/persona.md(唯一真源)
# 2. 校验(不写盘)
python build-composition.py --check
# 3. 重启 dsh
agent.cordis.yml 里只有一行 %%PERSONA%%,落地时自动注入全文。不要直接改 .agent-presets/plugin-forge/agent.cordis.yml 这份落地副本,下次启动会被源覆盖。
安装¶
包以 @local/dsh-plugin-forge-preset 的名字通过 link: 依赖装进 profile,README 给的是 PowerShell 步骤,不是 dsh plugin add:
# 1) 包目录放到任意位置,然后加进 profile 依赖
$profile = $env:DSH_PROFILE_DIR
$pkg = Get-Content -Raw -Encoding UTF8 "$profile\package.json" | ConvertFrom-Json
$pkg.dependencies | Add-Member -NotePropertyName '@local/dsh-plugin-forge-preset' `
-NotePropertyValue 'link:<包目录绝对路径>' -Force
$pkg.dsh.profile.bundles += '@local/dsh-plugin-forge-preset'
$pkg | ConvertTo-Json -Depth 12 | Set-Content "$profile\package.json" -Encoding UTF8
# 2) 建 junction,让 Loader 能按包名解析
New-Item -ItemType Junction -Path "$profile\node_modules\@local\dsh-plugin-forge-preset" `
-Target '<包目录绝对路径>'
重启 DSH 后,模式下拉里就会出现「插件工坊」。
运行时行为与排障¶
启动时插件依次做四件事:把 preset.yml + agent.cordis.yml 落到 $DSH_HOME/.agent-presets/plugin-forge/;把 skills/ 同步到 $DSH_HOME/plugin-forge/skills/;等 agentPresets 服务就绪(最长 20 秒),然后调 ctx.agentPresets.register() 声明进下拉;把结果写进 $DSH_HOME/plugin-forge/preset-declare.json。
第三步是必须的。Web 的模式下拉只认 register(),只把目录写到 .agent-presets 不会被扫到,README 把这称为「插件装得完整、下拉里却没有它」的唯一根因。
装完直接看 preset-declare.json 判断结果:
{
"ok": true,
"phase": "registered",
"id": "plugin-forge",
"name": "插件工坊",
"plugins": 19,
"broken": null,
"skillsDir": "...",
"presetDir": "..."
}
phase 的含义如下:
| phase | 含义 | 怎么办 |
|---|---|---|
registered |
正常 | 无 |
registered-broken |
已声明但挂载失败 | 看 broken 字段 |
waiting-registry |
等 agentPresets 服务 |
通常几秒内自动过去 |
registry-timeout |
20 秒没等到服务 | 检查 @deepseek-ai/dsh-agent-preset-registry 是否在 profile 里 |
install-failed |
预设文件落地失败 | 看 error,检查 $DSH_HOME 写权限 |
配置项表中,README 明确给出的只有 syncSkills(默认 true,控制是否把本包技能同步到 $DSH_HOME/plugin-forge/skills)。其余配置项资料里没有出现,这里不列。
包内还声明了 dsh.forge.effects:fs:read 任意绝对路径(测绘别人的插件)、fs:write 到 $DSH_HOME/.agent-presets/plugin-forge(落地预设)、fs:write 到 $DSH_HOME/plugin-forge(技能同步与状态文件)、http:loopback:/api/dsh-plugin-forge-preset(设置页数据路由)。
继续加新预设¶
加一个新预设不需要改任何 JS:
- 在
presets/下建目录,目录名 = preset id,需匹配^[a-z0-9][a-z0-9-]{0,31}$ - 写
preset.yml:name/description/order(前两个必须单行,order 必须数字) - 写
agent.cordis.yml:条目列表,至少含 persona、skill-filesystem、一个 shell 工具 - 人设长时用
%%PERSONA%%占位符 + 同目录persona.md - 重启 dsh
停用有两档:放空文件 presets/<id>/.preset-off,或者把目录改名加下划线(如 _<id>),结果都是不落地、不注册、副本保留。彻底移除就是删掉整个预设目录,下次启动会自动清掉 .agent-presets/<id>。
中文优先¶
插件约定 UI 文案、按钮、错误提示一律简体中文,插件展示名与描述一律简体中文,默认不做多语言——不创建 en.json,不留双语字典,不写 locale 切换,只有用户明确要求时才做双语。package.json 里虽然有 ./locale/*.json 的 exports 字段,但按 README 的约定,默认不带多语言资源。
适用场景与注意¶
适合两类人。一类是本来就要在 DSH 上写插件的人:需要按规范从零起插件、跑 16 项自检,省掉重复询问。另一类是想读懂或改造别人插件的人:用测绘 / 吸收 / 分叉三档流程,配合省 token 的检索工具,避免整文件读。
使用时有几点要留意:
- 插件以当前 dsh 进程的权限运行,资料里声明的
fs:read是任意绝对路径,安装前应先检查源码与许可证。 - 定制人设或规范时改
presets/下的源文件,不要改.agent-presets/里的落地副本。 - 想用满检索能力需要自行安装 ast-grep(0.51.1 及以上),不装也能降级运行。
- 索引库的引用关系不是全量标识符,跨文件结论拿不准时用 ast-grep 复核。
结尾¶
这个插件的价值不在于多了一个模式,而在于把插件开发的规范从会话里搬到了文件里,并配了大纲器、索引库和结构搜索来把 token 成本压到可承受的水平。如果要装,先看目录页和仓库里的 README,源码与许可证确认无误后再放进 profile。
- 社区目录页:https://www.skillhub.cn/plugins/wodaimnd/dsh-plugin-forge-preset
- GitHub:https://github.com/wodaimnd/dsh-plugin-forge-preset