插件工坊 · plugin-forge
DSH agent 预设:原创插件生成 + 他人插件测绘 / 吸收 / 改造。
装上后模式下拉里多出一项「插件工坊」,会话里可以直接说:
- 「帮我写个插件:……」
- 「看看这个插件怎么实现的」
- 「照它的面板写法做一个」
- 「把这个插件改成我要的样子」
它与「逆向模式」的区别
两者都做「拆开别人的东西」,但对象不同:
| 逆向模式(reverse) | 插件工坊(plugin-forge) | |
|---|---|---|
| 对象 | APK / IPA / PE / SO / 协议 / 游戏客户端 | DSH 插件(JS bundle) |
| 拆什么 | 二进制、壳、算法、通信协议 | 槽位注册、路由、工具行、UI 结构 |
| 产出 | 算法还原、脱壳产物、协议表 | 能安装的新插件 |
| 验证 | 产物能跑 / 算法能复现 | 桩环境注册 + 与原版回归对比 |
逆向模式的工具链(jadx / Frida / IDA)在这里用不上——插件的源码本来就是可读的 JS。
四个执行角色
| 角色 | 干什么 | 触发时机 |
|---|---|---|
| 插件测绘 | 出结构地图、契约表、「能改/慎改/别碰」三分 | 要读懂一个插件时 |
| 代码吸收 | 判断哪块值得吸、迁入并验证行为不变 | 要借别人写法时 |
| 改造分叉 | 选路线(原地改/复制分叉/补丁层)、七处替换、双验证 | 要改一个插件时 |
| 回归验证 | 桩环境结构验证 + 与原版差异对比 | 每次改造后 |
并发上限 3 个。四个角色都是叶子节点(maxDepth: 1),不会无限往下派。
六个技能
| 技能 | 作用 | 关键产出 |
|---|---|---|
plugin-author |
从零写插件:对齐 → 代码 → 16 项自检 | 完整可安装插件 + 规范全文 |
preset-scaffold |
加新 agent 预设:建目录 → 写两件套 → 验证 | 新模式的完整模板与约束 |
plugin-search |
代码定位:大纲 → 结构化搜索 → 定向读取 | 精确行号,不整文件读 |
plugin-index |
跨文件盘点:符号 / 引用 / 槽位 / 路由 | SQL 级查询,替代重复扫描 |
plugin-recon |
摸清别人的插件 | 结构地图 + 契约表 + 三分清单 |
plugin-harvest |
吸收可用代码 | 吸收清单 + 迁入代码 + 验证脚本 |
plugin-fork |
改造成自己的版本 | 改造方案 + 分叉产物 + 双验证 |
技能装在哪:$DSH_HOME/plugin-forge/skills/,由本插件启动时从包内同步。
多预设架构(怎么继续加)
本插件不是「一个预设的插件」,而是预设容器:presets/ 下每个子目录就是一个模式。
presets/
plugin-forge/ 插件工坊(正式)
preset.yml
agent.cordis.yml
persona.md 人设源文件,用 %%PERSONA%% 注入
demo-mini/ 演示模式(架构验证用,可删)
加一个新预设,不需要改任何 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
细节与模板见 skills/preset-scaffold/SKILL.md。
停用与移除
| 方式 | 做法 | 结果 |
|---|---|---|
| 临时停用 | 放空文件 presets/<id>/.preset-off |
不落地、不注册,副本保留 |
| 约定停用 | 目录改名加下划线,如 _<id> |
同上 |
| 彻底移除 | 删掉整个预设目录 | 下次启动自动清掉 .agent-presets/<id> |
条目标注册实例
所有预设共享同一个技能库($DSH_HOME/plugin-forge/skills)。所以给插件工坊加的技能,新预设自动可用。
状态文件(多预设版)
{
"ok": true,
"phase": "registered",
"presets": [
{ "id": "demo-mini", "name": "演示模式", "plugins": 11, "ok": true, "broken": null },
{ "id": "plugin-forge", "name": "插件工坊", "plugins": 19, "ok": true, "broken": null }
],
"skipped": [],
"skillsDir": "...",
"source": "...\\presets"
}
phase 取值:registered(全部成功)/ registered-partial(部分失败,看 presets[].broken)/ no-presets / waiting-registry / registry-timeout。
提示词固化在哪(防丢)
这是本插件存在的核心目的:规范不能只活在会话里。
| 位置 | 内容 | 大小 |
|---|---|---|
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.md |
双半、manifest、patch、槽位、路由、token、持久化、桩验证 | 10.4KB |
.../references/skeletons.md |
六份可直接抄的代码骨架(含 verify-client.mjs) |
21.3KB |
.../references/ux.md |
七态、反馈时效、可访问性 10 条、性能、跨页草稿 | 9.6KB |
.../references/data-and-ownership.md |
触发方式分级、所有权分级、合规路径表 | 4.5KB |
.../references/gates.md |
终极五问 + 16 项自检 + 中文化检查 + 质量线 | 5.0KB |
.../references/failure-modes.md |
10 个结构坑 + 12 个实战场坑 + 5 个实测坑 | 4.5KB |
.../references/prohibitions.md |
18 条禁止事项 | 2.4KB |
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)——下次启动会被源覆盖。
检索工具(省 token 的核心)
四个工具,按问题类型选:单文件降维 → 跨文件查库 → 结构搜索 → 字面量搜索。
| 工具 | 路径 | 用在什么问题上 |
|---|---|---|
| 大纲器 | tools/outline.mjs |
这个文件里有什么(符号 + 行区间) |
| 索引库 | tools/index.mjs |
跨文件关系:谁引用了它、哪些插件注册了同一槽位 |
| ast-grep | PATH 上的 ast-grep(用户自装) |
按语法结构找,排除注释与字符串 |
| 宿主 grep | 内置 | 找任意字面量(后端已是 ripgrep,无需装 rg) |
实测数据
| 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 # 库规模 + 同名多定义
# ③ 结构化搜索(注意必须带 $OBJ. 前缀)
ast-grep run -p '$OBJ.slots.register({ name: $NAME, $$$REST }, $COMP)' -l js '<目录>'
ast-grep run -p '$OBJ.webServer.register({ $$$REST })' -l js '<目录>'
# ④ 拿到行号后再定向读
# read client.js offset=279 limit=238
ast-grep 需要自行安装
它 55MB,不在本包内。安装后加进 PATH:
ast-grep --version # 期望 ast-grep 0.51.1 或更高
Windows 官方包是 app-x86_64-pc-windows-msvc.zip,解压后把含 ast-grep.exe 的目录加入用户 PATH。注意 sg.exe 是弃用别名,不要用。
不在 PATH 时自动降级为「大纲器 + 宿主 grep」,功能可用但结构匹配精度下降。
索引库的三个已知边界
- 引用是「调用点 + 大写参数」,不是全量标识符。小写变量名作参数传递(
fn(handler))查不到 —— 这时改用 ast-grep。 - 动态路由标注为
(动态: route):config.route这类运行期值静态还原不了,如实标注而非猜测。 - 增量更新依赖 mtime:改文件但 mtime 没变时会滞后,删库重建即可。
三个实测踩过的坑
- ast-grep 必须带
$OBJ.前缀。slots.register($$$A)只匹配独立调用,漏掉ctx.slots.register(...)这种最常见写法。 - 找注册点别用裸 grep。同一份测试代码,grep 命中 5 行其中 3 行是注释与字符串误报;ast-grep 只命中 1 行真实代码。
- 建索引必须开事务。不开事务时 SQLite 逐行同步落盘,1.5MB 语料从 456ms 涨到 2 分钟以上。
预算判据
| 动作 | 预期成本 |
|---|---|
| outline 单文件 | 100–900 tokens |
| outline 整个插件目录 | 300–1500 tokens |
| 索引查询(find / refs / slots) | 20–200 tokens |
| ast-grep 单条查询 | 20–200 tokens |
| 全文读 200KB 文件 | 约 60000 tokens ← 禁止 |
硬规则:读任何大于 20KB 的文件之前,必须先跑 outline。
中文优先
- UI 文案、按钮、错误提示:一律简体中文
- 插件展示名与描述:一律简体中文
- 默认不做多语言:不创建
en.json,不留双语字典,不写 locale 切换 - 只有用户明确要求时才做双语
安装
已在 profile 里装好。若要重装或换机器:
# 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
第 3 步是必须的:Web 的模式下拉只认 register(),只把目录写到 .agent-presets 不会被扫到——这是「插件装得完整、下拉里却没有它」的唯一根因。
状态文件
{
"ok": true,
"phase": "registered",
"id": "plugin-forge",
"name": "插件工坊",
"plugins": 19,
"broken": null,
"skillsDir": "...",
"presetDir": "..."
}
装完直接看这个文件判断是 registered 还是 broken。
| phase | 含义 | 怎么办 |
|---|---|---|
registered |
正常 | 无 |
registered-broken |
已声明但挂载失败 | 看 broken 字段 |
waiting-registry |
等 agentPresets 服务 |
通常几秒内自动过去 |
registry-timeout |
20 秒没等到服务 | 检查 @deepseek-ai/dsh-agent-preset-registry 是否在 profile 里 |
install-failed |
预设文件落地失败 | 看 error,检查 $DSH_HOME 写权限 |
配置项
| 字段 | 默认 | 含义 |
|---|---|---|
syncSkills |
true |
是否把本包技能同步到 $DSH_HOME/plugin-forge/skills |
force |
false |
是否覆盖你改过的预设副本(默认只在源文件更新时覆盖) |
registryTimeoutMs |
20000 |
等 registry 就绪的最长时间 |
关掉技能同步(想自己维护那份技能库时):
- id: dsh-plugin-forge-preset
name: '@local/dsh-plugin-forge-preset'
config:
syncSkills: false
改人设
人设正文在 persona.md。改完重新生成 composition:
python build-composition.py
不要直接改 agent.cordis.yml 里的人设块——它是生成产物,下次生成会被覆盖。
重新生成后需要重启 DSH 才生效(预设只影响之后创建的会话)。
回滚
# 1) 从 profile 拿掉
$profile = $env:DSH_PROFILE_DIR
$pkg = Get-Content -Raw -Encoding UTF8 "$profile\package.json" | ConvertFrom-Json
$pkg.dependencies.PSObject.Properties.Remove('@local/dsh-plugin-forge-preset')
$pkg.dsh.profile.bundles = @($pkg.dsh.profile.bundles | Where-Object { $_ -ne '@local/dsh-plugin-forge-preset' })
$pkg | ConvertTo-Json -Depth 12 | Set-Content "$profile\package.json" -Encoding UTF8
# 2) 删 junction
Remove-Item "$profile\node_modules\@local\dsh-plugin-forge-preset" -Force
# 3) 删落地的预设与技能(可选:保留则重新装回来会复用)
Remove-Item "$env:DSH_HOME\.agent-presets\plugin-forge" -Recurse -Force
Remove-Item "$env:DSH_HOME\plugin-forge" -Recurse -Force
profile 的 package.json 旁边有安装时的备份(package.json.bak-forge-*),可以直接还原。
依赖
| 依赖 | 用途 |
|---|---|
@deepseek-ai/dsh-agent-preset-registry |
模式注册表(profile 里已有) |
@deepseek-ai/* 各工具行 |
由 composition 声明,宿主内置 |
@local/dsh-forge(可选) |
装了就有 forge_check / forge_verify 两个体检工具 |
没有 @local/dsh-forge 也能正常加载——本 preset 不注册它的行。
验收记录
本次安装实测:
dsh-forge体检:14 项 PASS,0 error 0 warn- composition 解析(真实 fail-closed 解析器):19 行、3 个 group、无缺 name 行
- 声明链路(假 registry):
registered,ok: true,broken: null - 生产路径(经 profile junction):exports / files / patch / entry / lib 全通
- 技能预算:三份 SKILL.md 渲染后 4214–5862 字符,均 < 8192
- 人设无
{{...}}变量(会被当 prompt 变量解析失败)
No comments yet. Be the first to write one.