ZhijiangTang
dsh-plugin-template
Template repository for creating DSH plugins (npm bundle + scaffold script)
- Stars
- 0
- Language
- Shell
- Created
- Aug 15, 2026
- Updated
- Aug 15, 2026
Introduction
dsh-plugin-template
DSH 组合包(bundle)插件模板:脚手架一个新工具插件的最小可用骨架。
本仓库本身不是可安装插件(package.json 里 private: true,keywords 是 dsh-plugin-template,不发布 npm)。
- 纯 ESM、零依赖、无构建步骤
- 一个
hello_world占位工具(含参数 / 输出 schema / render / execute) - mount 自检走
ctx.tools.execute真实管道(CallId 来自@deepseek-ai/dsh-llm)
开发前必读:
../notes/plugin-development.md(本 hub 的实测笔记,所有结论均已验证)。官方文档本地副本在../reference/,发布教程见 https://deepseek-harness.github.io/deepseek-harness/develop/basic/publish.html
使用步骤
以下示例以新包名
dsh-mytool为例。
1. 克隆 / 复制模板
# 方式 A:在本 hub 内用脚手架(推荐)
bash scripts/scaffold.sh dsh-mytool # 生成 ../dsh-mytool/ 并替换占位符
# 方式 B:作为 GitHub 模板仓库 clone 后手工改
git clone <模板仓库地址> dsh-mytool && cd dsh-mytool
2. 改 package.json(哪些字段要改)
| 字段 | 模板值 | 要改成 |
|---|---|---|
name | dsh-plugin-template | 你的 npm 包名(如 dsh-mytool) |
private | true | 删除(要发布 npm) |
keywords | ["dsh-plugin-template"] | ["dsh-plugin", "<你的包名>"] |
description | 模板描述 | 你的插件一句话描述 |
version | 0.1.0 | 按语义化版本维护 |
用脚手架
scaffold.sh会自动完成前两项(删private+ 改写name/keywords)。
3. 改 cordis.patch.yml 的 id / name
cordis.patch.yml 里 id: <id> 是稳定身份(不带 id 的行每次启动重新挂载);name: <你的包名> 按 npm 包名 引用代码。
4. 改 index.js 的工具
index.js 里所有「改这里」注释:插件名 export const name、工具名 name: 'hello_world'、参数 / 输出 schema / execute 逻辑。
5. 验证配方(本地实测)
export DSH_HOME="$PWD/.dsh-test"
export PATH="$PWD/.tools:$PATH"
# file: 安装(自动初始化 px-tdemo profile,仅含 base)
dsh plugin --profile px-tdemo add file:./plugins/dsh-mytool
# web-app 补丁:把 in-box bundle web-app 加进 bundles(web 组合 = base + web-app)。
# 勿用 `dsh plugin add @deepseek-ai/dsh-web-app`:in-box bundle 不该当依赖装,且
# pnpm 的 minimumReleaseAge 会解析到坏掉的旧版 0.0.1-rc.1(实测 404)。
node -e 'const fs=require("fs");const p=".dsh-test/profiles/px-tdemo/package.json";const m=JSON.parse(fs.readFileSync(p));const b=m.dsh.profile.bundles;if(!b.includes("@deepseek-ai/dsh-web-app"))b.splice(1,0,"@deepseek-ai/dsh-web-app");fs.writeFileSync(p,JSON.stringify(m,null,2)+"\n")'
# boot 并抓自检行
dsh --profile px-tdemo --port 0 2>&1 | grep -m1 'self-test ok'
# 确认自检通过后清理
rm -rf plugins/dsh-mytool .dsh-test/profiles/px-tdemo
6. 发布
cd plugins/dsh-mytool
pnpm publish # 无构建步骤则直接发源码
# 用户安装:dsh plugin --profile <name> add dsh-mytool
GitHub:建仓库并打 dsh-plugin topic(详见 ../notes/plugin-development.md §7)。
第一批实测坑速查(务必先读)
- 本地安装必须
file::dsh plugin add ./目录生成link:符号链接,Node 按真实路径解析@deepseek-ai/*→ERR_MODULE_NOT_FOUND。用file:./目录复制安装即可解析成功。 - schema DSL 三事实:
output.schema不接受顶层required数组 → 用属性级required: true;可空值用oneOf: [{...}, {type:'null'}];type:'json'是合法类型。本模板note字段演示了「属性级 required + oneOf null」姿势。 - pnpm 24h 门控:新版本发布不满 24h,
pnpm add <包>会解析到上一个「已成熟」版本。文档要写add <包>@<版本>显式安装。 - allowBuilds:含原生依赖(node-pty 等)时
dsh plugin add失败ERR_PNPM_IGNORED_BUILDS,需把包名写进 profile 的pnpm-workspace.yaml的allowBuilds。插件尽量零原生依赖。 - PENDING 静默:
inject的服务没提供方时插件静默停在 PENDING(不报错)。插件没输出先遍历ctx.registry看 fiber 状态。
工具开发硬规则(节选,全量见 ../notes/plugin-development.md §3)
- 参数已按 schema 校验(类型/必填/字面量/联合分支);schema 表达不了的约束自己查。
execute()返回规范 JSON 值(output.schema声明的),不是内容块;异常/无效值 = isError。- 遵守
exec.signal取消;exec携带不可变身份(callId/name/arguments/agent/token/parent)。 - 注册后别改 schema/回调;UI 卡片(presentCall/presentResult)必须纯函数。
- 模型可见内容由
output.render生成,与 UI 卡片是两件事。
文件结构
.
├── package.json # 清单:name/version/private/exports/files/peerDeps/dsh.bundle
├── index.js # 插件:name/inject + defineTool + mount 自检
├── cordis.patch.yml # 层 patch:insert 行引用本包
├── README.md # 本文档
├── LICENSE # MIT
├── .gitignore # node_modules/ *.log .DS_Store
├── CONTRIBUTING.md # 与 dsh-plugins hub 的关系 + batch-check 用法(模板专属)
└── scripts/scaffold.sh # 脚手架(模板专属,不复制到新插件)
License
MIT