← Back to home@liupengyang-1008

dsh-plugin-development

Development-time skill for building DSH (DeepSeek Harness) / Cordis plugins end to end - plugin forms, templates, an API cookbook, catalogued pitfalls, packaging and publishing.

Stars
0
Language
Python
Created
Sep 11, 2026
Updated
Oct 15, 2026

Introduction

DSH 插件开发技能 · dsh-plugin-development

这是一个「用来开发 DSH 插件的技能」,它本身不是能装进 DSH 的插件。 它不会被 dsh plugin add 安装,也不进入任何 DSH profile;它帮你产出的那个 dsh.bundle 包,才是能装进 DSH 的东西。

中文 · English

一套给开发者与 AI Agent 用的 DSH 插件工程能力:选形态 → 写代码 → 排错 → 打包 → 发布,全链路都有可直接复制的模板、可执行的校验脚本,以及一份「过时会响」的版本闸门。

技能市场 · GitHub · 许可 MIT-0


目录


0. 30 秒看懂

问题答案
这是什么一套写给 AI Agent 的 DSH 插件开发手册 + 一包可执行的校验工具
谁用装了 DSH、想给它写插件的人;以及替人写插件的 AI Agent
怎么用对它说话——不是敲命令行工具。加载后它指导 Agent 完成开发全流程
装在哪装进你的 AI 客户端的 skills 目录。它遵循通用的 Agent Skills 开放格式,不绑定任何一家客户端——Claude Code / Codex / Cursor / Gemini CLI / Copilot / OpenClaw(社区昵称「小龙虾」)/ WorkBuddy 等都能用
它给你什么12 个可复制模板 · 23 条踩坑 · 9 个校验脚本 · 1 套防过时版本闸门 · 10 个结对提示词
它不给你什么❌ 现成的可装插件(那是它帮你做的东西)❌ DSH 运行时依赖
对你的环境要求① 一个能加载 SKILL.md 的 AI 客户端 ② 能执行 shell(python3 或 bash)③ 可选:一份 DSH 源码,用于核验 API——没有也能用,走降级规则

一句话判断它适不适用

用户说的是「给我写/改/修/打包一个 DSH 插件」→ 适用。 用户说的是「给我一个装进 DSH 就能用的现成插件」→ 不适用,去社区插件市场(见 references/12-community-plugins.md)。


1. 安装

1.1 它是一份通用技能,不绑定任何客户端

本技能用的是开放的 Agent Skills 格式:一个目录,里面是 SKILL.md(必需)+ references/ + scripts/ + assets/。该格式最初由 Anthropic 提出,后开放为标准,已被大量 AI 客户端实现。

判定标准只有一条:

你的 Agent 能加载 SKILL.md,它就能用这个技能。

它不依赖任何客户端的私有能力,只需要两件事:Agent 能读文件、能执行 shell 命令。已确认可用(列表不穷举):

Claude Code · OpenAI Codex · Cursor · Gemini CLI · GitHub Copilot · Windsurf · OpenCode · Kiro · OpenClaw(社区昵称「小龙虾」) · WorkBuddy · 以及任何实现该标准的客户端。

⚠️ 别把两个「层」搞混:本技能装进你的 AI 客户端;它帮你产出的那个 dsh.bundle 才装进 DSH。DSH 是它的目标平台,不是它的宿主。

1.2 安装(三种方式,任选其一)

方式 A · 用客户端自带的安装命令(最省事)

客户端命令
OpenClaw(小龙虾)· 从 Git 装(推荐,拿最新版)openclaw skills install git:liupengyang-1008/dsh-plugin-development@main
OpenClaw · 从本地目录装openclaw skills install ./dsh-plugin-development --as dsh-plugin-development
OpenClaw · 从 ClawHub 装openclaw skills install @liupengyang-1008/dsh-plugin-development
OpenClaw · 更新openclaw skills update --all(工作区)/ --all --global(全局)
skillhub.cnskillhub install dsh-plugin-development --namespace user_9d594bab
Claude Code / Codex / 其他这些客户端目前主要靠「把目录放到 skills 路径」——用方式 B

OpenClaw 默认装进当前 workspace 的 skills/;加 --global 才装进 ~/.openclaw/skills/,对所有本地 agent 可见。 版本提示:ClawHub / SkillHub 的发布版本可能落后于 GitHub,以技能内 SKILL.md 的 version 为准;要最新版就按方式 B 从 Git 装。

方式 B · 从 Git 仓库放置(最通用,任何客户端都行)

git clone https://github.com/liupengyang-1008/dsh-plugin-development.git

然后把整个目录放进你的客户端会扫描的 skills 路径。默认路径参考:

客户端用户级(对所有项目生效)项目级(只对当前项目)
WorkBuddy~/.workbuddy/skills/—
Claude Code~/.claude/skills/.claude/skills/
OpenAI Codex~/.agents/skills/(部分版本:~/.codex/skills/).agents/skills/(同上:.codex/skills/)
OpenClaw(小龙虾)~/.openclaw/skills/<workspace>/skills/
Cursor~/.cursor/skills/.cursor/skills/
Gemini CLI~/.gemini/skills/.gemini/skills/
GitHub Copilot~/.copilot/skills/.github/skills/
Windsurf~/.codeium/windsurf/skills/.windsurf/skills/
OpenCode~/.config/opencode/skills/.opencode/skills/
Kiro~/.kiro/skills/.kiro/skills/

Windows 下把 ~ 换成 C:\Users\<你的用户名>\。

🔎 路径不确定时不要猜——直接问你的 Agent: 「你的 skills 目录在哪?」 这是最可靠的办法:各客户端路径随版本变动,而 Agent 自己知道它从哪里加载技能。

⚠️ 目录名必须保持 dsh-plugin-development。 Agent Skills 规范要求 frontmatter 的 name 与父目录名一致(本技能两者都是 dsh-plugin-development)。重命名目录会让技能加载失败。

方式 C · 从发布包解压

把 dsh-plugin-development.zip 解压到上面任一路径,得到 <skills 目录>/dsh-plugin-development/。

1.3 多个客户端想共用一份?用 symlink,别复制

复制多份会各自漂移。维护一份权威副本,symlink 到各个客户端:

# 唯一权威副本
git clone https://github.com/liupengyang-1008/dsh-plugin-development.git ~/skills-src/dsh-plugin-development

# 链到你用的每个客户端
ln -s ~/skills-src/dsh-plugin-development ~/.claude/skills/dsh-plugin-development
ln -s ~/skills-src/dsh-plugin-development ~/.agents/skills/dsh-plugin-development
ln -s ~/skills-src/dsh-plugin-development ~/.openclaw/skills/dsh-plugin-development

之后升级只需一条命令:

git -C ~/skills-src/dsh-plugin-development pull

部分客户端对 symlink 有路径包含检查(要求 SKILL.md 的真实路径仍落在技能目录内)。本技能是普通目录结构,不跨出技能目录,因此不受影响。 OpenClaw 支持 git: 与本地目录安装,并明确支持 symlink 的技能文件夹。

1.4 装完怎么确认

目录里应当是这套结构(SKILL.md + 三个资源目录):

SKILL.md          ← 入口层:闸门 / 选型 / 流程 / 红线 / 验收
references/       ← 23 篇专题文档(含 6 个上游素材分册),按需读取
scripts/          ← 9 个确定性校验工具
assets/           ← 可直接复制改名的工程骨架
LICENSE / NOTICE / licenses/   ← 许可与第三方声明(零 copyleft)

结构自检(不依赖任何客户端):

python scripts/check_refs.py .        # 引用一致性:期望 exit 0

功能验证:向 AI 提一个 DSH 插件相关的问题(例如「帮我判断这个插件该用哪种形态」)。如果回答里出现「第 0 步 · 版本闸门」、模板编号(T1~T12)或「冒烟判定」这类概念,说明技能已被成功加载。

技能加载是渐进式的:Agent 启动时只读 SKILL.md 的 name + description(约 100 token),判定相关后才加载正文,references/ 与 scripts/ 只在真正需要时才读。所以装了很多技能也不会撑爆上下文。


2. 第一次使用:零学习成本

这个技能的最佳用法是不要读文档,直接对它说人话。

「帮我写一个 DSH 插件:在会话里加一条 /hello 命令,再让模型能用上一个查时间的能力。」

Agent 会自动走完:选形态 → 路由到模板 → 写最小闭环 → 立冒烟点 → 验证 → 打包。你不需要知道「T3」「defineTool」这些词——技能里有一张「用户这么说 → 模板 → 形态 → 注册在哪 → 怎么算跑通」的路由表,负责把你的话翻译成技术动作。

它会先做一件事,而且不允许跳过:

# 第 0 步 · 版本闸门:核验本技能记录的事实,是否还符合你手上的 DSH 版本
python scripts/dsh-api-probe.py <你的 DSH 仓库路径>
# 0 = 断言全部成立(48 正向 + 8 反向 = 56)
# 1 = 有 STALE —— 某条事实已过时,不要照抄模板
# 2 = 那不是 DSH 仓库(防对着错目录得出错误结论)
# 3 = 断言表为空 —— 什么都没核验,结果无效

为什么必须先跑它:DSH 处于 rc 阶段,tag 间隔中位数约 1.1 天,出现过破坏性变更。本技能的文档是某一时刻的快照,源码永远压过它。


3. 由浅入深:四个难度阶梯

不要从最难的开始。先做能跑通的最小闭环,再逐级加复杂度。

Level 1 · 零代码组合包 —— 不写一行 JavaScript

什么时候用:只是把已有能力组装到一起。典型需求是「把某个 MCP server 挂进 DSH」「改一下某个现成插件的行为」。

要点:全部行为由 cordis.patch.yml 描述,没有 src/,没有 JS 入口。骨架在 assets/minimal-bundle/。

三个文件里,package.json 有一行是新手第一大坑:

{
  "name": "dsh-my-bundle",
  "type": "module",
  "files": ["cordis.patch.yml", "README.md"],
  "exports": {
    "./cordis.patch.yml": "./cordis.patch.yml",
    "./package.json": "./package.json"
  },
  "dsh": {
    "bundle": {
      "patch": "./cordis.patch.yml"   // ← 少了它,dsh plugin add 只打一条警告然后什么都不做
    }
  }
}

补丁文件顶层是单个数组,每行 id 必须全局唯一:

- insert:
    - id: mcp-my-capability
      name: '@deepseek-ai/dsh-mcp-client'
      config:
        serverName: my-capability
        transport: stdio
        command: npx
        args: [-y, my-mcp-server]
        # 子进程环境会被清洗掉 *KEY|PASSWORD|SECRET|TOKEN* 与所有 DSH_* 变量,
        # 要传的凭据必须在这里显式列出
        MY_API_KEY: !!js process.env.MY_API_KEY ?? ''

安装与验证(--profile 是必填参数,不是可选项):

dsh plugin --profile <profile 名> add ./minimal-bundle     # 安装
dsh plugin --profile <profile 名> ls                       # 确认已登记
dsh --dump-config                                          # 不启动就能看组装结果

冒烟通过 = --dump-config 里出现你的那行,并且改配置确实生效。


Level 2 · 加一个工具 —— 最典型的函数式插件

什么时候用:「让模型会用某个能力」。骨架在 assets/minimal-tool-plugin/(纯 JS,零构建,开箱可装)。

import { defineTool } from '@deepseek-ai/dsh-tools'

export const name = 'dsh-tool-greet'
export const inject = ['tools']        // 不写它,ctx.tools 就是 undefined,插件直接报错

/** @param {import('@deepseek-ai/cordis').Context} ctx */
export function apply(ctx) {
  ctx.tools.register(defineTool({
    name: 'greet',
    description: 'Greet someone by name. Use this when ...',
    parameters: {
      name: { type: 'string', required: true, description: 'The name to greet.' }
    },
    output: {
      schema: { type: 'string' },
      render: (_args, value) => [{ type: 'text', text: value }]
    },
    async execute(args) {
      return `Hello, ${args.name}!`
    }
  }))
}

// ⚠️ 绝对不要写 export default —— 会被 Loader 当成「服务类」解析,
//    进而丢弃函数插件的命名空间。这是函数式插件的头号杀手。

这里有一个最容易写错的地方,两条路径的规则相反:

路径required 写在哪值
defineTool(DSH 自有 DSL)属性级字面量 true
裸 ctx.tools.register(标准 JSON Schema)对象级字符串数组 ['name']

套错就静默失效,不报错。

冒烟通过 = 在真实会话里让模型调用它,拿到预期结果。「装上了」不等于「生效了」。


Level 3 · 加命令、加配置 —— 让它可调参

斜杠命令(T5):

export const inject = ['commands']

export function apply(ctx) {
  ctx.commands.register({
    name: 'hello',
    description: 'Say hello',
    async run(args) { return `Hello, ${args || 'world'}!` }
  })
}

用户可调配置(T6):导出 Config,改配置后行为变化、非法值被明确拒绝。

同时加多个能力面只有两条规则:inject 取并集;需要清理的副作用包进 ctx.effect() 并返回 disposer。


Level 4 · 服务、界面、拦截、定时、HTTP —— 进入深水区

你要做的形态注册点冒烟通过 =
给别的插件提供能力服务插件export default class extends Service,构造函数首行 super(ctx, '服务名')另一个插件能 inject 并调用成功
网页界面上加东西函数式 + 浏览器半侧ctx.slots.inject(key, () => ctx.slots.register(opts, 组件))界面出现你的东西,控制台无 slot entry crashed
设置界面里加卡片同上设置命名空间 + settings.* 插槽卡片可见、改动生效
拦消息 / 改请求 / 加提示词函数式插件agent/pre-step、agent/request(waterfall)跑一次会话,行为确实被改
暴露 HTTP 接口函数式插件ctx.webServer.register({kind, path, handler})curl 真打一次,看到 200
定时 / 周期任务函数式插件ctx.interval(cb, delay)(不是 ctx.setInterval)等过一个周期,任务真的跑了

服务插件是唯一允许(且必须)写 export default 的形态——它和函数式插件是两种东西,别混。 完整签名、客户端产物契约、反面教材见 references/14-inbound-http-and-timers.md;界面细节见 references/04-ui-and-slots.md。

到了这一级,请务必通读 references/05-pitfalls.md 的 23 条坑——它们全部来自官方事故复盘或社区真实提交,每条都对应一次真实的失败。


难度阶梯速查

低  ├─ T1/T2  零代码组合包          不写 JS,只描述组装
    ├─ T5     斜杠命令              一个 register 调用
中  ├─ T3/T4  工具插件              defineTool + 参数 Schema
    ├─ T6     可配置插件            export const Config
    ├─ T7     服务插件              extends Service
高  ├─ T8/T9  UI 与设置卡片         双半侧 + 三个登记点
    ├─ T10    Agent 流程拦截        waterfall 事件
    └─ T11/T12 外部集成 / 完整工程  全套工程约定 + 门禁

⚠️ 复制模板前必读:三条安全约束

技能里的模板是可复制的骨架。其中「挂载外部 MCP / 外部进程」的示例会在 DSH 启动时下载并执行外部包 —— 这是全套模板里唯一有真实安全含义的地方。复制时守住三条:

#约束为什么
1依赖写精确版本(pkg@1.2.3),不要写 pkg / latest / ^1 / ~1.x浮动规格意味着「每次启动执行的都是当时的线上最新版」。代码在你审阅之后仍然会变——上游被攻陷、发布恶意新版本、形近包抢注,都会直接落到本机
2凭据只列真正需要的那一把,并用低配额、可随时撤销的专用 key那个被下载下来的子进程与 DSH 同权限运行:能读写文件、能发网络请求,也能读到 env: 里列出的每一个凭据
3把「下载」与「执行」分成两步先在独立步骤里显式安装并审阅解析出的依赖树,再让补丁指向本地可执行文件——这样「包里到底跑了什么」在运行前就是确定的

这三条来自本技能的一次真实安全审计(ClawHub 扫描器报告)。技能的其它部分不涉及这类风险:它自身不含运行时逻辑,不读取任何凭据,也不外传数据。


4. 它里面装了什么

位置内容什么时候读
SKILL.md入口层(≤500 行):版本闸门、形态选型、路由表、10 步工作流、冒烟判定、合同不变量 5 条、八条红线、自检清单每次开发,整体加载
references/23 篇专题:主线编号 0015 + api-claims,另 6 个上游素材分册(02b · 02c · 10a10d)。主线为 00 版本闸门 · api-claims 事实分级 · 01 心智模型 · 02 12 个模板 · 03 API 手册 · 04 UI 与插槽 · 05 23 条坑 · 06 工作流 · 07 工程约定 · 08 速查表 · 09 结对提示词 · 10 社区案例库(总索引)· 11 术语与来源 · 12 社区插件 · 13 版本史 · 14 HTTP 与定时器 · 15 边界与维护按需读取,不要一次全读
scripts/9 个确定性工具:防过时探针(py/sh 双版本)· 第二批断言核验器 · 插槽权威抽取 · 引用一致性守门 · 许可声明自检 · 上游同步 · 版本差异 · 历史矩阵见 第 5 节
assets/3 组 8 个文件,可直接复制:零代码组合包骨架 · 最小工具插件骨架 · 决策留痕模板动手时直接抄
LICENSE · NOTICE · licenses/许可与第三方声明:本技能原创内容按 MIT-0;引用的第三方片段分别适用 MIT / Apache-2.0 / BSD-3-Clause(零 copyleft),许可正文三份随包复用或再分发本技能的片段前

大文件用检索、不要整读(单文件最大 1.9k 行):

grep -rn "ctx.slots.inject" references/ | head -30      # 某 API 名出现在哪
grep -n -A 40 "^### 坑 P7" references/05-pitfalls.md    # 某个坑的全文(按编号)

5. 为什么它不承诺「不过时」

DSH 处于 pre-stable / rc 阶段,tag 间隔中位数约 1.1 天,并且出现过破坏性变更。任何写死 DSH API 的文档都会腐化——这是结构性的,不是谁疏忽。

所以本技能不承诺「不过时」,只承诺一件事:过时会响。

机制作用
第 0 步 · 版本闸门(强制)写任何 DSH 代码前先答三问 + 跑探针;拿不到源码时按降级规则显式声明「本条未核验」
事实分级 S / M / V一句话判断法:这条事实明天变了,我的代码会崩吗? 会崩 → 必须核验;不会崩 → 可直接用
dsh-api-probe.py对实时源码重验 56 条断言(48 正向 + 8 反向)
verify_absorbed_claims.py第二批 49 条(入站 HTTP / 定时器 / 客户端产物 / 插槽 / 基线推进事实 / 防编造的否定断言)
extract_slots.py从源码抽取权威插槽清单并与技能声明双向 diff——写插槽名前用它,不要凭表抄
check_refs.py证明技能里引用的每个路径都能在技能内解析,且没有指向作者机器的绝对路径

一个不能失败的校验器,比没有校验器更危险。 本项目在开发期查出了 4 次「假通过」——什么都没核验,却报告「全部成立」。因此:探针有退出码 3(断言表为空);否定断言必须带护栏模式(必须同时命中一个必然存在的样本);每个核验器都自带 --selftest,必须先证明它能失败,它的「通过」才可信。 方法论常驻于 references/00-version-gate.md §9 与 references/api-claims.md §6。


6. 出问题了去哪儿查

症状先去哪
插件装上了但完全没反应references/06-workflow.md ④:--dump-config 查组合树 + PENDING 审计
报错信息看不懂references/05-pitfalls.md 的「报错信息 / 现象对照表」
已知症状检索references/05-pitfalls.md(23 条坑,编号 P1~P22b)
改了代码不生效SKILL.md 的「改完代码怎么生效」表——只改 src/,永远不要手改 lib/
界面崩了 / slot 报错references/04-ui-and-slots.md
想追到原始社区提交与行号级出处references/10-community-casebook.md(总索引)→ 10a~10d 分方向分册

「我明明装了啊」的五大成因,全部落在 SKILL.md 的合同不变量 5 条上:缺 dsh.bundle.patch、type/main/exports 不全、UI 半侧声明不成对、名称三处不一致(package.json#name = patch 的 id = 客户端 ModuleLoader.id)、误把 react 打进 bundle。 打包前对照这 5 条逐项打勾,能省掉大部分返工。


7. 与 AI Agent 结对开发

这个技能本来就是为「Agent 替人写插件」设计的。

  • 直接给 Agent 的提示词模板:references/09-agent-pairing.md(10 个可直接复制的模板)
  • 要求 Agent 每次附出处(文件 + 行号),无出处不予采纳。 这条比提示词本身更重要——它会挡掉绝大部分凭空编造的 API
  • 交付后按 references/07-conventions.md 的门禁清单验收

8. 常见问题

Q:这是 DSH 插件吗?我 dsh plugin add 装它为什么没反应? 不是。它是开发期技能,装在你的 AI 客户端 skills 目录,不是 DSH profile。你用它产出的那个 dsh.bundle 才是 DSH 插件。

Q:我能只用它的一部分吗(比如只要模板)? 可以。assets/ 下的两个骨架是独立的、可直接复制的工程;references/02-templates.md 的 12 个模板也可以单独抄。但模板是脚手架不是权威——抄之前先跑一次探针确认它依赖的 API 还在。

Q:没有 DSH 源码,能用吗? 能,但要走降级规则:显式声明「以下 API 未对当前版本核验」,S 级骨架照给,M/V 级符号单列成「需你确认」。技能从不编造 API 名去填补空白。

Q:目标版本比技能基线新怎么办? 不要猜。scripts/dsh-sync.sh 拉源码(需确认,首次约 200 MB,绝不执行 pnpm install)→ 跑探针 → scripts/dsh-version-diff.sh 出「基线 → 最新」差异,看六个破坏性变更高频维度。

Q:为什么文档里有些数字(23 篇 / 23 条坑 / 56 条断言)看起来像在自我强调? 因为这些数字是可复现的:项目规矩是写「全部 / 仅 / 只有 N 个」必须附抽取命令与范围。历史上这条规矩已经拦下过 3 次措辞与事实不符。


9. 基线、已知边界与许可

事实基线

项值
DSH 基线tag dsh-v0.2.1-alpha.3 / commit 2fbf4359b8 / 2026-10-14
上游仓库deepseek-ai/deepseek-harness
技能版本见 SKILL.md frontmatter 的 version(唯一落点)
版本 ↔ 基线对照references/13-version-history.md §1.5

技能版本号不表示「对应哪个 DSH 版本」,只表示「本技能自己改到了什么级别」。两条独立的轴,绑定处就是上面那张对照表。

已知边界(诚实声明)

  • 探针只覆盖它断言的 56 条事实,不覆盖 V 级实现细节。探针全绿 ≠ 你的插件能跑。
  • 文档里引用的行号会漂移,请按符号名核验,不要按行号。
  • 版本史只登记「会让已有代码 / 配置失效」的变更,它不是 changelog。
  • references/10a-casebook-ui.md ~ 10d-casebook-host-bundle.md 与 12-community-plugins.md 引用了社区仓库的代码与文档片段,许可为 MIT / Apache-2.0 / BSD-3-Clause 三类(三者均为 permissive、零 copyleft;另有混合许可来源已逐包标注)。🔴 本技能不引用任何 copyleft 来源 —— 此前引用的一个 AGPL-3.0 来源已于 2026-09-16 整体排除(其通用教训改写为自撰建议保留)。复用前请自行复核上游许可。完整的「来源 × 许可 × 落点」对照见根目录 LICENSE 末段与 references/12-community-plugins.md。
  • 本技能不提供「托管静态文件」与「发布 DSH skill」两类能力面,属有意保留的空白,理由见 references/15-skill-scope-and-maintenance.md。

许可

本技能的原创内容(散文、工具脚本、模板)以 MIT-0 发布 —— 见 LICENSE。

第三方许可 / Third-party licensing

本技能引用或复现的第三方片段仍适用其各自许可(MIT / Apache-2.0 / BSD-3-Clause 三类,均为 permissive;另有混合许可来源按子包逐一标注)。Apache-2.0 来源的归属声明、变更声明与上游 NOTICE 已按 §4(b)(c)(d) 落在随包分发的 NOTICE;BSD-3-Clause 的版权声明与无背书条款同样落在 NOTICE。许可正文分别在 licenses/Apache-2.0.txt、licenses/MIT.txt、licenses/BSD-3-Clause.txt。完整的「来源 × 许可 × 落点」清单见 LICENSE 末段的来源表;版权与归属声明见 NOTICE。

本技能不是任何上游项目的官方产物,也不代表上游项目立场。