Skip to main content
P

dsh-BMAD-Method

ph1996-cn/dsh-bmad-method

Ships BMAD-Method inside the plugin: the seven modules are laid down on first open, no installer run and no download, the method's 90 agent and workflow skills come in the package, and each module can be turned off by writing the disabled flag into the profile patch.

Install

dsh plugin --profile web add github:ph1996-cn/dsh-bmad-method

README

dsh-BMAD-Method

把 BMAD-Method 的模块清单、模块开关、更新收进 dsh 的一个 bundle 插件里。

插件不重写 BMAD 的任何 agent / workflow 逻辑,只做安装器的受控桥接层:dsh 侧负责交互、状态与开关持久化,模块文件仍由官方 bmad-method 安装器落盘。

BMAD 本体随插件携带:运行时(_bmad/ 的配置、脚本、7 个模块)直接放在包的 bmad-home/ 里,装完插件就铺到 $DSH_HOME/bmad-method,46 毫秒、不联网、不用点安装;bmad-method 安装器则是普通依赖(node_modules/bmad-method),只在换版本或补外部模块时才动用。要换 BMAD 版本,在本仓库 npm run update-bmad + 重新生成 bmad-home/ 后发新版本,用户更新插件即完成更新。

五条设计约定

要求实现
封装全部模块清单 = 官方注册表(bmad-modules.yaml)+ 内置 core / bmm;bmad_update() 默认一次装齐未废弃的全部模块
工作区自动有 _bmad-output任一工具调用都会在工作区建 _bmad-output(安装器 --output-folder 的默认值)
插件内开关模块bmad_module_toggle 在 profile 补丁里写 / 去 disabled: true,模块文件一个都不动
自定义用户名与语言userName / language 两个设置项(也可作工具参数传);语言默认简体中文
工作区不落 _bmad / .agent模块装在全局主目录 $DSH_HOME/bmad-method,所有工作区共用;工作区只多一个 _bmad-output

安装

从 GitHub 装(把 ph1996-cn 换成你的 GitHub 用户名 / 组织名):

dsh plugin add github:ph1996-cn/dsh-BMAD-Method

仓库里没有任何本机绝对路径:BMAD 主目录从 $DSH_HOME(缺省 ~/.dsh)推导,profile 补丁靠扫 ~/.dsh/profiles/*/ 里 dsh.profile.bundles 认领本插件的那个,补丁路径可用 patchPath 配置覆盖。发布前只需把下面几处的 ph1996-cn 换成自己的账号。

或在 dsh 里让 agent 调 plugin_manager:

plugin_manager({ action: "install_bundle", target: "github:ph1996-cn/dsh-BMAD-Method" })

也可以钉到某个 tag / commit(可复现):

plugin_manager({ action: "install_bundle", target: "github:ph1996-cn/dsh-BMAD-Method#v0.1.0" })

开发调试用本地目录:

git clone https://github.com/ph1996-cn/dsh-BMAD-Method.git
dsh plugin add ./dsh-BMAD-Method

装完会把 bundle 记进当前 profile 的 package.json:

"dsh-BMAD-Method": "github:ph1996-cn/dsh-BMAD-Method",   // dependencies
"dsh": { "profile": { "bundles": [ ..., "dsh-BMAD-Method" ] } }

控制面行由包自带的 cordis.patch.yml 提供,不需要手写进 profile 补丁。装完重启 dsh,三个工具才会出现。

包名带大写字母,因此不能发到 npm registry(npm 只收小写名);GitHub 安装不受此限制。要同时发 npm 就把 package.json 的 name 改成小写 dsh-bmad-method,index.js 的 export const name 与 cordis.patch.yml 里的 name: 跟着改。

设置界面

插件导出 Config(Schemastery),dsh 会把它投影成 JSON Schema 渲染成表单;表单下面那半截(模块开关)是 client.js 注册到 plugins.bundle.config 槽位里自己画的,因为 dsh 的设置表单只管标量字段,管不了「一行一个开关」这种结构。

字段默认作用
userName空写进 BMAD 核心配置的用户名(留空用系统用户名)
language简体中文沟通 + 文档输出语言
outputFolder_bmad-output工作区输出目录名

只有这三项。BMAD 装在哪、补丁文件在哪,都由插件自己管,不需要你填。

卡片下半部分是模块开关

同一张卡片里,三个输入框下面就是每个模块一行 + 一个开关:显示模块名(BMAD Test Architect)、版本、描述、当前状态。勾上 = 参与 dsh,关掉 = 不参与。

它写的是和工具、和 dsh 插件开关同一份 profile 补丁(disabled: true),不是第二份状态。

为什么不直接点插件页「包含的组件」里的开关?因为 dsh 的行标题取自包名(resolveText(row.meta.title),而 row.meta 来自 packages.metaOf(row.name)),同一个包的多行标题必然一样,7 行全叫 dsh-BMAD-Method,分不出谁是谁。那 7 行开关照样能用(状态是同一份),只是没法认人。

浏览器半端调不了工具,所以插件自己开了一条同源接口:

GET  /api/bmad-method/modules          → 模块列表 + 当前 enabled(第一次打开会顺手把 BMAD 铺好,不用任何按钮)
POST /api/bmad-method/toggle           → { module, enabled },写补丁后立即回读返回新状态

只接受本机同源请求(校验 Origin 与 Host 一致),跨站来源返回 403。

改完保存后重启 dsh 生效(插件配置与补丁都在 profile 启动时读取)。工具参数优先级高于这里的配置(例如 bmad_update 传了 userName 就用参数值)。

设置页看不到这张卡片时,按顺序查:① profile 的 package.json 里 dsh.profile.bundles 有 dsh-BMAD-Method;② 插件是启用状态(plugin_manager 列表里非 disabled);③ 全新加载过插件(改过 index.js 后 set_bundle(false) → set_bundle(true));④ 刷新浏览器页面。

三个工具

工具必填参数作用
bmad_modules_list无列出全部模块:已装(读 _bmad/)+ 可装(读官方 bmad-modules.yaml),含 installed / enabled / version / type
bmad_module_togglemodule、enabled开关某模块:在 profile 补丁里写 / 去 disabled: true。不动文件、不装卸
bmad_update无调用 BMAD 安装器把模块装齐 / 更新,完成后自动刷新列表

可选参数:

  • projectRoot:覆盖自动探测的安装位置
  • registryPath:覆盖模块注册表
  • bmad_update:userName / language / modules / action / channel / dryRun
// 列出全部模块与状态
{}
// 停用 tea(只让它不参与 dsh,文件一个不删)
{ "module": "tea", "enabled": false }
// 装齐全部模块,用户名与语言一起写进去
{ "userName": "张伟", "language": "简体中文" }
// 先看命令,不执行
{ "userName": "张伟", "dryRun": true }

bmad_update 实际执行的命令(实测 6.12.4 通过):

node <插件目录>/node_modules/bmad-method/tools/installer/bmad-cli.js install \
  --directory <全局主目录> --action update --yes \
  --modules bmb,cis,tea,bmad-loop,gds,core,bmm --tools claude-code --no-shims \
  --user-name 张伟 --output-folder _bmad-output \
  --communication-language 简体中文 --document-output-language 简体中文

安装器用的是插件依赖里自带的那份(不联网拉 bmad-method@latest),所以能离线跑。 action 只接受 update / quick-update(安装器不接受 install:Invalid action: install. Valid actions: quick-update, update)。

关于 --tools claude-code:安装器的非交互模式(--yes)强制要求至少一个 IDE 标识,取值表里没有 dsh。它决定安装器把 skill 铺到哪个目录(这里是 .claude/skills)。

两个容易误会的点:

  • --no-shims 管的是「已弃用模块的兼容 shim skill」,跟铺不铺 IDE 文件无关(commands/install.js:37-38 原文)。加了它照样会铺 .claude/skills。
  • 安装器把 skill 正文只写进 IDE 目录,_bmad/ 里只有 config 和脚本(installer.js:141 注释:Skills are now in IDE directories — remove redundant copies from _bmad/)。

本插件的做法:这 90 个 skill 直接随插件包发行(skills/,见下一节),安装器铺出来的 .claude/skills 是重复品,装完就删掉——用户机器上不会多出任何 IDE 目录。

BMAD 的 90 个 skill 在哪

<插件安装目录>/skills/        ← 随包发行,不进用户目录
├─ manifest.json                    ← 归属表:skill 名 → 所属模块(90 条)
├─ bmad-agent-analyst/SKILL.md       ← 57 个 bmad-*:core 8 / bmm 21 / bmb 5 / cis 10 / tea 10 / bmad-loop 3
├─ gds-agent-game-architect/SKILL.md ← 33 个 gds-*:gds 模块(Game Dev Studio 用自己的前缀)
└─ …(brainstorming / prd / spec / review / retrospective / ux / party-mode / testarch / cis-* 等)

这些 skill 就是 BMAD 的全部能力(agent 人格 + 工作流正文)。插件通过 dsh 原生的 SkillProvider(ctx.skills.registerProvider)把它们注册给 dsh,做法和 @wenaixi/dsh-ponytail 一样:

  • 用户装完插件就自带 90 个 skill,不需要联网、不需要预装 BMAD、也不往 $DSH_HOME/skills 写任何东西;
  • 插件升级 → skill 跟着升级(更新插件即更新 BMAD);
  • 读取时把正文里的 {project-root}/_bmad/ 动态替换成全局主目录的绝对路径({project-root}/_bmad-output 保持不变,仍指向你的工作区),所以 skill 里的脚本调用能命中真实的 _bmad/scripts。

验证:

# 包内自带(发布后用户机器上就是这样)
(Get-ChildItem "<插件安装目录>\skills" -Directory | Where-Object { $_.Name -match '^(bmad|gds)-' }).Count   # 90

# 用户目录应当是 0 —— skill 不该落在这里
Get-ChildItem "$env:USERPROFILE\.dsh\skills" -Directory -Filter 'bmad*' | Measure-Object

用法举例(描述匹配就会触发):

你说触发
「用 Mary 帮我做需求分析」bmad-agent-analyst
「帮我 brainstorm 一下」bmad-brainstorming
「写个 PRD」bmad-prd
「跑一下 code review」bmad-code-review
「跑一次回顾」bmad-retrospective

模块开关管一件事:这个模块的 skill 参不参与 dsh。停用某模块后(立即生效):它的 skill 不再出现在 skill 清单里——归属优先读包内的 skills/manifest.json,本机装了模块再用 _bmad/_config/skill-manifest.csv 覆盖,core 的 8 个永远可用。skill 本身随插件包携带,不会被删。

模块清单:全部模块

官方注册表只登记外部模块,core(BMAD Core)与 bmm(BMad Method 本体)是安装器内置的,代码里补齐后一并列出,type: builtin。

core 是地基:bmad_module_toggle 对它返回 not-toggleable,包内补丁也不给它开行(跟着 bmm 一起加载)。deprecated: true 的模块(automator、wds)仍会列出,但安装时会被跳过。

外部模块需要能访问 GitHub(安装器用 git clone 拉各模块仓库)。网络不通时整次安装会失败,此时插件自动退到内置模块 bmm 重试一次,保证 BMAD 至少能用;网络恢复后再跑一次 bmad_update 即可补齐。

模块开关是怎么生效的

一个 bundle 只挂一个实例(包内 cordis.patch.yml 只有控制面一行),所以插件页「包含的组件」里只有它一个。模块开关在插件详情页的「BMAD 设置」卡片里,每个模块一行:

BMad Method          bmm · 6.12.4      启用  [●]
BMad Test Architect  tea · v1.27.2     启用  [●]
BMAD Core            core · 6.12.4     地基  [──]   ← 不参与开关

开关状态只存在 profile 的 cordis.patch.yml 里,形状和 dsh 自己的开关完全一样(顶格覆盖条目):

- id: bmad-module-tea
  disabled: true

没有这一条 = 启用。卡片里的开关、bmad_module_toggle 工具写的是同一份状态:

  • 卡片点开关 → POST /api/bmad-method/toggle → 插件按行写/删这条覆盖条目(启用时整条删掉,回到默认)。
  • 调 bmad_module_toggle → 同一个函数,行为一致。
  • 停用后真正发生的事:该模块名下的 skill 立刻不再出现在 dsh 的 skill 清单里(归属来自包内 skills/manifest.json,本机装了再用 _bmad/_config/skill-manifest.csv 覆盖)。停用 bmm,21 个 bmad-* skill 一起消失;core 的 8 个永远在。这就是"模块没参与加载"的可观测证据,比提示词里的一句说明更硬。
  • 模块文件一个都不删:_bmad/bmm/ 原样躺在磁盘上,重新打开开关就恢复。
  • 不用重启:skill 清单每次构建都是现读补丁算出来的;写完补丁插件还会调一次 provider 的 control.invalidate() 让宿主立刻重扫,所以当轮就能看到变化。

写入安全

  • cordis.patch.yml 一律按行定点编辑,不做 YAML 往返序列化 —— 你自己写的注释和条目原样保留(有测试守着)。
  • 写盘走「临时文件 + rename」,写完立刻回读校验;校验失败把原内容写回并返回 write-failed,不会出现"界面说已停用、文件没变"。
  • 工具不缓存内存态,enabled 每次从补丁文件现读。

路径与全局安装

BMAD 装在全局主目录,工作区不落 _bmad/。这些路径都由插件自己推断,不暴露成设置项:

位置取值顺序
全局主目录环境变量 DSH_BMAD_HOME → $DSH_HOME/bmad-method(无 DSH_HOME 时 ~/.dsh/bmad-method)
实际安装位置显式 projectRoot(其下有 _bmad/)→ 全局主目录有 _bmad/ → 从会话工作目录向上找 _bmad/
profile 补丁DSH_BMAD_PATCH_PATH → DSH_PROFILE_DIR/cordis.patch.yml → DSH_HOME/profiles/<DSH_PROFILE>/cordis.patch.yml
安装器插件依赖 node_modules/bmad-method → 项目内 / 全局 bmad-method → npx 兜底
注册表插件依赖里的 bmad-modules.yaml → 项目内 bmad-method → 插件内置快照
工作区输出目录<工作区>/_bmad-output,每次调用自动创建

老项目里已经有 _bmad/ 也照用(projectRoot 或自动探测命中即按项目内安装处理,返回里 global: false)。

零配置自举

装完插件什么都不用做,BMAD 已经在包里。 第一次调用任一 BMAD 工具,或者第一次打开设置卡片(卡片加载时自己触发自举),插件就会检查 $DSH_HOME/bmad-method/_bmad/:

  • 不存在 → 把包内 bmad-home/_bmad/ 的运行时直接铺过去:7 个模块的配置、脚本、custom/ 模板一次到位,实测 46 毫秒,不跑安装器、不联网。用户设置(用户名、语言、输出目录)在铺的时候写进每个模块的 config。
  • 已存在但缺了某个模块的 _bmad/<code>/ → 同样用模板补齐。
  • 已存在但版本与本插件携带的 bmad-method 不一致 → 模板版本对不上就退回跑安装器刷新,免得旧模板铺到新版本预期的目录结构上。
  • 全齐且版本一致 → 直接跳过,零开销。

也就是说:装插件 = 装 BMAD。只有需要装/更新到某个新版本、或补装外部模块时才会真正调用安装器。

// 想强制走一次真正的安装器(比如换 BMAD 版本、补外部模块)
{ "dryRun": true, "userName": "张伟" }   // 先看命令
{ "userName": "张伟", "language": "简体中文" }

模板路线的实测(v6.12.4):

  • _bmad/ 约 345 KB / 31 文件(配置 + scripts/*.py + 每个模块的 module-help.csv),打进包毫无压力。
  • 主目录只多出 _bmad/,不铺任何 IDE 目录(安装器那条路会铺 .claude/skills,插件会清掉——skill 由插件包内的 provider 提供)。
  • custom/ 是用户自己的定制:补装时先暂存再放回,不会被模板覆盖。

安装器路线的实测(只有版本不一致或手动 bmad_update 才会走):

  • 工作区完全干净,只多出 _bmad-output;没有 _bmad/、.agent/。
  • 模块文件落在 --directory 下,_bmad-output 也落在 --directory 下(工作区那个是插件建的)。
  • 非交互首次安装必须给 --tools,插件默认 claude-code(或沿用 manifest.ides);取值表里没有 dsh,它只决定安装器往哪个目录铺 skill,铺完即清理。
  • 外部模块靠 git clone,网络不通时整次安装失败 → 自动退到内置 bmm 重试,保证能用。
  • 不传 userName / language 时,安装器保留已存配置,不会覆盖。

怎么验证开关写对了

# 看模块覆盖条目(Windows PowerShell)
Get-Content "$env:DSH_PROFILE_DIR\cordis.patch.yml" | Select-String -Context 0,2 "bmad-module-tea"

判据:

  1. 停用后文件里有 - id: bmad-module-tea 且下一行是 disabled: true;
  2. 启用后整条消失(回到 bundle 默认值),文件里不再出现 bmad-module-tea;
  3. bmad_modules_list 返回里 modules[].enabled 与文件一致,patchPath 就是你改的那个文件;
  4. 不用重启:点完开关,下一次会话的 skill 清单里对应模块的 skill 就已经不见了,_bmad/ 里的文件一个不少。

更新

更新 BMAD = 更新插件。 在插件仓库里:

npm run update-bmad     # = npm install bmad-method@latest --save-exact
npm test                # 顺手验一遍

发新版本后,用户更新插件,下次调用工具时 ensureInstalled 发现版本不一致,自动把全局那份刷到同版本(_bmad/core/config.yaml 里的用户名/语言保留,安装器不覆盖已存配置)。

需要手动强制刷新时,跑 bmad_update(先 dryRun: true 看命令)即可。

插件自身走 dsh bundle 常规路径(plugin_manager 升级或 npm update dsh-BMAD-Method)。package.json 声明 engines.dsh(>=0.2.0-rc.1 <0.3.0)与 engines.node,装到不兼容的 dsh 上会被版本闸门拦下。

兼容的 dsh 版本

engines.dsh 是 >=0.2.0-rc.1 <0.3.0,整个 0.2.x 都在范围内(含 0.2.1-rc.5)。插件只依赖五处宿主契约,都在 0.2.1-rc.5 的发布包里逐条核对过:

用到的契约来自rc.5 里
ctx.tools.register(definition)@deepseek-ai/dsh-toolslib/index.js:2974
ctx.skills.registerProvider(create),宿主按 provider.name 注册@deepseek-ai/dsh-skilllib/index.js:147 / :160
candidate 的 provider 必须等于 provider 名(否则整份清单抛错)同上 validateCandidatelib/index.js:452-462,文案未变
.volatile() 字段才会进设置页(volatileForm)@deepseek-ai/dsh-settingslib/index.js:122
ctx.webServer.register({ kind: 'prefix', path, handler })宿主 webServer 服务dsh-host-open-in-app/lib/index.js:1271,1283 照旧这么用

插件不用 dsh 的私有内部模块,也不 pin 任何 @deepseek-ai/dsh-* 依赖,所以 0.2.x 内的小版本升级不需要跟着改版本号。

更新插件本身:一条命令,不用卸载重装

dsh 的 install_bundle 对已装的包会直接返回 already-installed 拒绝掉,没有"更新"这个动作。改代码后要么卸载重装,要么绕过去直接更新依赖 —— 一条命令:

dsh plugin --profile desktop update dsh-BMAD-Method

dsh plugin 就是把后面的参数原样转发给 profile 目录里的 pnpm(源码 dsh/lib/bin.js:117 的 command 描述原文就是这么写的),所以等价于:

pnpm -C "$env:USERPROFILE\.dsh\profiles\desktop" update dsh-BMAD-Method

两种都行,dsh plugin 那条不用记路径。然后重启 dsh。

⚠️ --profile 后面要写你实际在用的那个 profile。桌面版是 desktop;写成 web 会在 ~/.dsh/profiles/web 里操作,跟桌面版读的 desktop 是两个互不相干的 profile。

(pnpm add dsh-BMAD-Method@github:... 不行 —— spec 没变,pnpm 按 lockfile 里的旧 commit 装,会静默什么都不干。)

如果只是想边改边试,用开发模式更省事:

# 装本地目录而不是 GitHub 版本
dsh plugin add D:\path\to\dsh-BMAD-Method     # 或 plugin_manager install_bundle(target=<目录>)

link 安装下改完代码只需重启 dsh,不用重新装包。

目录结构

cordis.patch.yml         # bundle 补丁:只插控制面行
index.js                 # apply(ctx, config):注册三个工具 / 注入模块上下文 / 注册 skill provider
lib/patch.js             # cordis.patch.yml 按行编辑 + 原子写 + 回读校验 + 回滚
lib/bmad.js              # _bmad/ 发现、全局主目录、自举、注册表、命令组装、安装器调用
lib/skills.js            # SkillProvider:从包内 skills/ 提供 90 个 skill
lib/template.js          # 包内 bmad-home/ 模板:铺运行时 + 写用户设置
bmad-home/_bmad/         # BMAD 运行时本体(配置 + 脚本 + 7 个模块,345 KB)
skills/                  # 90 个 skill(57 bmad-* + 33 gds-*)+ manifest.json 归属表
lib/registry/            # bmad-modules.yaml 内置快照(依赖没落地时的离线兜底)
fixtures/                # 冒烟测试用的 _bmad/ 项目与补丁文件
test/                    # node --test

测试

npm test        # node --test,12 个用例

覆盖:工具注册、模块发现(已安装 + 可安装 + 内置 core/bmm + deprecated)、停用写 disabled: true、重复停用幂等、列表反映状态、重新启用移除该键、用户注释与既有条目不被破坏、core 不可停用、未装模块拒绝停用、写盘失败返回 write-failed 且原文件不变、更新命令组装(--user-name / 两个「简体中文」/--output-folder _bmad-output/--no-shims/--modules 含全部未废弃模块)、全局安装时工作区只有 _bmad-output 而无 _bmad/.agent、全新环境自动自举、包内模板 46 毫秒铺好 7 个模块并把用户名写进三处 config、模块停用后它的 skill 不再列出而 core 的仍在、插件页模块开关接口(GET/POST + 跨站 403)、打开卡片即自举(GET 一次就把 7 个模块装好,不需要任何按钮)、Config 三个字段全部 .volatile()。 端到端实测通过:全新 DSH_HOME 下第一次调 bmad_modules_list,46 毫秒铺好 7 个模块(viaTemplate: true),core/config.yaml / bmm/config.yaml / config.user.toml 都写进了用户名与「简体中文」,主目录只有 _bmad/、工作区只有 _bmad-output,provider 列出 90 个 skill;第二次调用版本一致直接跳过。测试里用 DSH_BMAD_MINIMAL=1 只装内置模块,保持离线可跑。

已知边界

  • 模块文件由插件依赖里的 bmad-method 安装器铺到全局主目录,不需要联网下载 bmad-method 本体;但外部模块(bmb/cis/tea/gds/bmad-loop)仍要 git clone,需要能访问 GitHub,装过一次后就在本地(失败会自动降级为内置模块,skill 仍可用)。
  • 模块开关是热生效的:只改 profile 补丁 + 调一次 provider invalidate(),不重启、不重装。它唯一影响的是「这个模块的 skill 参不参与 dsh」——_bmad/ 里的模块文件、包内的 skill 文件都不动,随时可以开回来。也不影响安装器行为:bmad_update 仍会按注册表把全部模块装齐(装齐 ≠ 全启用)。
  • 唯一要重启的场景是插件自身代码或包内补丁变了(装新版本、改 cordis.patch.yml)——那属于 profile 启动时读取的部分。
  • 注册表跟着 bmad-method 依赖走;上游新增模块时 npm run update-bmad 升级依赖即可,内置快照只是兜底。
  • 包内 90 个 skill 是安装时的快照:上游模块出新版 skill 时,npm run update-bmad 只会刷新安装器,skill 正文要重新从安装产物里拷一遍(_bmad-full/.claude/skills → skills/,并重生成 skills/manifest.json)。
  • 模块开关的边界:开关只控制「这个模块的 skill 参不参与 dsh」——停用后立即生效,该模块名下的 skill 从清单里消失(归属优先包内 skills/manifest.json,本机装了再用 _bmad/_config/skill-manifest.csv 覆盖;core 的 8 个永远可用)。_bmad/ 里的模块文件、包内的 skill 文件都不动,随时可以开回来。它不影响安装器行为:bmad_update 仍会按注册表把全部模块装齐(装齐 ≠ 全启用)。
  • 插件页「包含的组件」只有一行:一个 bundle 挂一个实例。早期版本给每个模块开一行想让插件页带开关,但 dsh 的行标题只能取包名,7 行会全叫 dsh-BMAD-Method,反而更乱——现在开关在设置卡片里。

Related plugins