dsh-plugin-forge-preset:把「写插件、读插件、改插件」的做法固化进预设

前言

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:

  1. 在 presets/ 下建目录,目录名 = preset id,需匹配 ^[a-z0-9][a-z0-9-]{0,31}$
  2. 写 preset.yml:name / description / order(前两个必须单行,order 必须数字)
  3. 写 agent.cordis.yml:条目列表,至少含 persona、skill-filesystem、一个 shell 工具
  4. 人设长时用 %%PERSONA%% 占位符 + 同目录 persona.md
  5. 重启 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
羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜