Compare commits
5 Commits
8eb935914c
...
ecd4d91e0d
| Author | SHA1 | Date |
|---|---|---|
|
|
ecd4d91e0d | |
|
|
cfe40fccd5 | |
|
|
090bfea9b2 | |
|
|
c3486a8844 | |
|
|
2eb509d246 |
|
|
@ -106,6 +106,9 @@ jobs:
|
|||
- name: Test agent compilation components
|
||||
run: npm run test:install
|
||||
|
||||
- name: Test module extension installer
|
||||
run: npm run test:install:extensions
|
||||
|
||||
- name: Validate file references
|
||||
run: npm run validate:refs
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ sidebar:
|
|||
做规格、方案或计划时,先跑一次“事前复盘”通常收益最高,容易提前暴露隐藏风险。
|
||||
:::
|
||||
|
||||
如果你还处在方向发散阶段,可先用 [头脑风暴](./brainstorming.md);如果你需要多角色权衡讨论,可用 [派对模式](./party-mode.md)。在进入实现前做问题发现时,可结合 [对抗性评审](./adversarial-review.md)。
|
||||
|
||||
## 与相近模式的区别
|
||||
|
||||
| 模式 | 核心目标 | 典型输入 | 典型输出 |
|
||||
|
|
|
|||
|
|
@ -43,9 +43,11 @@ sidebar:
|
|||
所以它本质上是**高召回、需人工分诊**的策略,而不是“自动真理机”。
|
||||
|
||||
:::caution[关键心法]
|
||||
把发现分成三类:必须修、可延后、可忽略。评审质量的关键不在“发现数量”,而在分诊质量。
|
||||
把发现分成三类:必须修、可延后、可忽略。评审质量的关键不在”发现数量”,而在分诊质量。
|
||||
:::
|
||||
|
||||
如果你想把该策略放进快速实现节奏中,可参见 [快速开发](./quick-dev.md);若要做多轮推理补强,可参见 [高级启发](./advanced-elicitation.md)。整体流程位置请见 [工作流地图](../reference/workflow-map.md)。
|
||||
|
||||
## 与 Quick Dev 的关系
|
||||
|
||||
`bmad-quick-dev` 关注执行效率与边界控制;对抗性评审关注问题发现质量。
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ sidebar:
|
|||
|
||||
## 它是什么
|
||||
|
||||
头脑风暴(brainstorming)适合“我有方向,但还不够清晰”的阶段。你会和 AI 进行来回探索:
|
||||
头脑风暴(brainstorming)适合”我有方向,但还不够清晰”的阶段。你会和 AI 进行来回探索:
|
||||
- 明确问题和约束
|
||||
- 生成备选想法
|
||||
- 对想法分组和优先级排序
|
||||
|
|
@ -46,6 +46,8 @@ sidebar:
|
|||
想法来源于你,workflow 负责构建“更容易产生好想法”的过程。
|
||||
:::
|
||||
|
||||
想继续深化现有输出,可参考 [高级启发](./advanced-elicitation.md);需要多角色协同讨论,可参考 [派对模式](./party-mode.md)。若要查看它在整体流程中的位置,请参见 [工作流地图](../reference/workflow-map.md)。
|
||||
|
||||
## 与相近模式的区别
|
||||
|
||||
| 模式 | 核心目标 | 输入状态 | 典型输出 |
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ sidebar:
|
|||
|
||||
### 如果我忘了运行文档梳理怎么办?
|
||||
|
||||
可以随时补跑,不影响你继续推进当前任务。很多团队会在迭代中期或里程碑后再运行一次,用来把“代码现状”回写到文档里。
|
||||
可以随时补跑,不影响你继续推进当前任务。很多团队会在迭代中期或里程碑后再运行一次,用来把”代码现状”回写到文档里。
|
||||
|
||||
### 既有项目可以直接用 Quick Flow 吗?
|
||||
|
||||
|
|
@ -55,6 +55,8 @@ BMad Method 不会强制“立即现代化”,而是把决策权交给你。
|
|||
|
||||
**还有问题?** 欢迎在 [GitHub Issues](https://github.com/bmad-code-org/BMAD-METHOD/issues) 或 [Discord](https://discord.gg/gk8jAdXWmj) 提问。
|
||||
|
||||
如果你想了解这套接入方式的操作步骤,可继续阅读 [How-to:既有项目](../how-to/established-projects.md) 与 [How-to:项目上下文](../how-to/project-context.md)。想理解快速流程在方法论中的定位,可参见 [快速开发](./quick-dev.md)。
|
||||
|
||||
## 继续阅读
|
||||
|
||||
- [既有项目(How-to)](../how-to/established-projects.md)
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ sidebar:
|
|||
|
||||
## 它是什么
|
||||
|
||||
Party Mode 不是单角色问答,也不是单文档改写。它更像一次“有主持人的多方评审会”:
|
||||
Party Mode 不是单角色问答,也不是单文档改写。它更像一次”有主持人的多方评审会”:
|
||||
- BMad Master 根据你的问题调度相关角色
|
||||
- 各角色以自身关注点回应
|
||||
- 角色间会互相补充、质疑、修正
|
||||
|
|
@ -35,7 +35,7 @@ Party Mode 不是单角色问答,也不是单文档改写。它更像一次“
|
|||
|
||||
## 价值与边界
|
||||
|
||||
Party Mode 的价值在于“更快看见盲区”:
|
||||
Party Mode 的价值在于”更快看见盲区”:
|
||||
- 优势:视角多、分歧显性、对齐速度快
|
||||
- 代价:讨论信息量大,需要你主动控节奏和收敛
|
||||
|
||||
|
|
@ -43,6 +43,8 @@ Party Mode 的价值在于“更快看见盲区”:
|
|||
先给清晰议题,再给决策约束(时间、风险、成本、成功标准),讨论质量会明显更高。
|
||||
:::
|
||||
|
||||
若你的目标是结构化发散创意,可先参考 [头脑风暴](./brainstorming.md);若你已经有初稿并想做二次推理补强,可参考 [高级启发](./advanced-elicitation.md)。完整阶段位置见 [工作流地图](../reference/workflow-map.md)。
|
||||
|
||||
## 与相近模式的区别
|
||||
|
||||
| 模式 | 核心目标 | 最佳场景 | 输出形态 |
|
||||
|
|
|
|||
|
|
@ -104,11 +104,13 @@ architecture: "如何做"
|
|||
|
||||
:::tip[更稳妥的做法]
|
||||
- 先记录跨 `epic`、高冲突概率的决策
|
||||
- 把精力放在“会影响多个 story 的规则”
|
||||
- 把精力放在”会影响多个 story 的规则”
|
||||
- 随着项目演进持续更新架构文档
|
||||
- 出现重大偏移时使用 `bmad-correct-course`
|
||||
:::
|
||||
|
||||
如需先理解为什么要在实施前做 solutioning,可阅读 [为什么解决方案设计很重要](./why-solutioning-matters.md);如果你想把这些约束落地到项目执行,可继续看 [项目上下文](./project-context.md)。流程全景见 [工作流地图](../reference/workflow-map.md)。
|
||||
|
||||
## 继续阅读
|
||||
|
||||
- [为什么解决方案阶段很重要](./why-solutioning-matters.md)
|
||||
|
|
|
|||
|
|
@ -89,6 +89,8 @@ sidebar:
|
|||
|
||||
## 继续阅读
|
||||
|
||||
如需可执行步骤说明,请阅读 [How-to:项目上下文](../how-to/project-context.md);如果你在既有项目落地这套机制,可参考 [既有项目常见问题](./established-projects-faq.md)。整体流程定位见 [工作流地图](../reference/workflow-map.md)。
|
||||
|
||||
- [管理项目上下文(How-to)](../how-to/project-context.md)
|
||||
- [既有项目常见问题](./established-projects-faq.md)
|
||||
- [工作流地图](../reference/workflow-map.md)
|
||||
|
|
|
|||
|
|
@ -81,6 +81,8 @@ Quick Dev 是执行节奏设计;`adversarial review` 是审查策略。二者
|
|||
|
||||
## 继续阅读
|
||||
|
||||
想进一步理解审查策略,可继续阅读 [对抗性评审](./adversarial-review.md);需要对已有输出进行第二轮推理时,可参考 [高级启发](./advanced-elicitation.md)。若要查看它在完整流程中的位置,请参见 [工作流地图](../reference/workflow-map.md)。
|
||||
|
||||
- [对抗性评审](./adversarial-review.md)
|
||||
- [高级启发](./advanced-elicitation.md)
|
||||
- [工作流地图](../reference/workflow-map.md)
|
||||
|
|
|
|||
|
|
@ -75,6 +75,8 @@ solutioning 的本质不是“多写一份文档”,而是把高冲突风险
|
|||
在 solutioning 阶段发现对齐问题,通常比在实施中后期才发现更快、更便宜。
|
||||
:::
|
||||
|
||||
想进一步理解冲突是如何发生并被架构约束消除的,可继续阅读 [防止智能体冲突](./preventing-agent-conflicts.md)。如果你要把这些约束落到执行层,请结合 [项目上下文](./project-context.md) 与 [工作流地图](../reference/workflow-map.md) 一起阅读。
|
||||
|
||||
## 继续阅读
|
||||
|
||||
- [防止智能体冲突](./preventing-agent-conflicts.md)
|
||||
|
|
|
|||
|
|
@ -32,6 +32,9 @@ export default [
|
|||
'tools/template-test-generator/test-scenarios/**',
|
||||
'src/modules/*/sub-modules/**',
|
||||
'.bundler-temp/**',
|
||||
// Lock files — generated, gitignored, not project code
|
||||
'pnpm-lock.yaml',
|
||||
'bun.lock',
|
||||
// Augment vendor config — not project code, naming conventions
|
||||
// are dictated by Augment and can't be changed, so exclude
|
||||
// the entire directory from linting
|
||||
|
|
|
|||
|
|
@ -41,8 +41,9 @@
|
|||
"prepare": "command -v husky >/dev/null 2>&1 && husky || exit 0",
|
||||
"quality": "npm run format:check && npm run lint && npm run lint:md && npm run docs:build && npm run test:install && npm run validate:refs && npm run validate:skills",
|
||||
"rebundle": "node tools/cli/bundlers/bundle-web.js rebundle",
|
||||
"test": "npm run test:refs && npm run test:install && npm run lint && npm run lint:md && npm run format:check",
|
||||
"test": "npm run test:refs && npm run test:install && npm run test:install:extensions && npm run lint && npm run lint:md && npm run format:check",
|
||||
"test:install": "node test/test-installation-components.js",
|
||||
"test:install:extensions": "node test/test-installation-extensions.js",
|
||||
"test:refs": "node test/test-file-refs-csv.js",
|
||||
"validate:refs": "node tools/validate-file-refs.js --strict",
|
||||
"validate:skills": "node tools/validate-skills.js --strict"
|
||||
|
|
|
|||
|
|
@ -1,30 +1,30 @@
|
|||
module,phase,name,code,sequence,workflow-file,command,required,agent,options,description,output-location,outputs,
|
||||
bmm,anytime,Document Project,DP,,skill:bmad-document-project,bmad-bmm-document-project,false,analyst,Create Mode,"Analyze an existing project to produce useful documentation",project-knowledge,*,
|
||||
bmm,anytime,Generate Project Context,GPC,,skill:bmad-generate-project-context,bmad-bmm-generate-project-context,false,analyst,Create Mode,"Scan existing codebase to generate a lean LLM-optimized project-context.md containing critical implementation rules patterns and conventions for AI agents. Essential for brownfield projects.",output_folder,"project context",
|
||||
bmm,anytime,Quick Dev,QQ,,skill:bmad-quick-dev,bmad-bmm-quick-dev,false,quick-flow-solo-dev,Create Mode,"Unified intent-in code-out workflow: clarify plan implement review and present",implementation_artifacts,"spec and project implementation",
|
||||
bmm,anytime,Correct Course,CC,,skill:bmad-correct-course,bmad-bmm-correct-course,false,sm,Create Mode,"Anytime: Navigate significant changes. May recommend start over update PRD redo architecture sprint planning or correct epics and stories",planning_artifacts,"change proposal",
|
||||
bmm,anytime,Write Document,WD,,skill:bmad-agent-tech-writer,,false,tech-writer,,"Describe in detail what you want, and the agent will follow the documentation best practices defined in agent memory. Multi-turn conversation with subprocess for research/review.",project-knowledge,"document",
|
||||
bmm,anytime,Update Standards,US,,skill:bmad-agent-tech-writer,,false,tech-writer,,"Update agent memory documentation-standards.md with your specific preferences if you discover missing document conventions.",_bmad/_memory/tech-writer-sidecar,"standards",
|
||||
bmm,anytime,Mermaid Generate,MG,,skill:bmad-agent-tech-writer,,false,tech-writer,,"Create a Mermaid diagram based on user description. Will suggest diagram types if not specified.",planning_artifacts,"mermaid diagram",
|
||||
bmm,anytime,Validate Document,VD,,skill:bmad-agent-tech-writer,,false,tech-writer,,"Review the specified document against documentation standards and best practices. Returns specific actionable improvement suggestions organized by priority.",planning_artifacts,"validation report",
|
||||
bmm,anytime,Explain Concept,EC,,skill:bmad-agent-tech-writer,,false,tech-writer,,"Create clear technical explanations with examples and diagrams for complex concepts. Breaks down into digestible sections using task-oriented approach.",project_knowledge,"explanation",
|
||||
bmm,1-analysis,Brainstorm Project,BP,10,skill:bmad-brainstorming,bmad-brainstorming,false,analyst,,"Expert Guided Facilitation through a single or multiple techniques",planning_artifacts,"brainstorming session",
|
||||
bmm,1-analysis,Market Research,MR,20,skill:bmad-market-research,bmad-bmm-market-research,false,analyst,Create Mode,"Market analysis competitive landscape customer needs and trends","planning_artifacts|project-knowledge","research documents",
|
||||
bmm,1-analysis,Domain Research,DR,21,skill:bmad-domain-research,bmad-bmm-domain-research,false,analyst,Create Mode,"Industry domain deep dive subject matter expertise and terminology","planning_artifacts|project_knowledge","research documents",
|
||||
bmm,1-analysis,Technical Research,TR,22,skill:bmad-technical-research,bmad-bmm-technical-research,false,analyst,Create Mode,"Technical feasibility architecture options and implementation approaches","planning_artifacts|project_knowledge","research documents",
|
||||
bmm,1-analysis,Create Brief,CB,30,skill:bmad-product-brief,bmad-bmm-product-brief,false,analyst,Create Mode,"A guided experience to nail down your product idea",planning_artifacts,"product brief",
|
||||
bmm,2-planning,Create PRD,CP,10,skill:bmad-create-prd,bmad-bmm-create-prd,true,pm,Create Mode,"Expert led facilitation to produce your Product Requirements Document",planning_artifacts,prd,
|
||||
bmm,2-planning,Validate PRD,VP,20,skill:bmad-validate-prd,bmad-bmm-validate-prd,false,pm,Validate Mode,"Validate PRD is comprehensive lean well organized and cohesive",planning_artifacts,"prd validation report",
|
||||
bmm,2-planning,Edit PRD,EP,25,skill:bmad-edit-prd,bmad-bmm-edit-prd,false,pm,Edit Mode,"Improve and enhance an existing PRD",planning_artifacts,"updated prd",
|
||||
bmm,2-planning,Create UX,CU,30,skill:bmad-create-ux-design,bmad-bmm-create-ux-design,false,ux-designer,Create Mode,"Guidance through realizing the plan for your UX, strongly recommended if a UI is a primary piece of the proposed project",planning_artifacts,"ux design",
|
||||
bmm,3-solutioning,Create Architecture,CA,10,skill:bmad-create-architecture,bmad-bmm-create-architecture,true,architect,Create Mode,"Guided Workflow to document technical decisions",planning_artifacts,architecture,
|
||||
bmm,3-solutioning,Create Epics and Stories,CE,30,skill:bmad-create-epics-and-stories,bmad-bmm-create-epics-and-stories,true,pm,Create Mode,"Create the Epics and Stories Listing",planning_artifacts,"epics and stories",
|
||||
bmm,3-solutioning,Check Implementation Readiness,IR,70,skill:bmad-check-implementation-readiness,bmad-bmm-check-implementation-readiness,true,architect,Validate Mode,"Ensure PRD UX Architecture and Epics Stories are aligned",planning_artifacts,"readiness report",
|
||||
bmm,4-implementation,Sprint Planning,SP,10,skill:bmad-sprint-planning,bmad-bmm-sprint-planning,true,sm,Create Mode,"Generate sprint plan for development tasks - this kicks off the implementation phase by producing a plan the implementation agents will follow in sequence for every story in the plan.",implementation_artifacts,"sprint status",
|
||||
bmm,4-implementation,Sprint Status,SS,20,skill:bmad-sprint-status,bmad-bmm-sprint-status,false,sm,Create Mode,"Anytime: Summarize sprint status and route to next workflow",,,
|
||||
bmm,4-implementation,Validate Story,VS,35,skill:bmad-create-story,bmad-bmm-create-story,false,sm,Validate Mode,"Validates story readiness and completeness before development work begins",implementation_artifacts,"story validation report",
|
||||
bmm,4-implementation,Create Story,CS,30,skill:bmad-create-story,bmad-bmm-create-story,true,sm,Create Mode,"Story cycle start: Prepare first found story in the sprint plan that is next, or if the command is run with a specific epic and story designation with context. Once complete, then VS then DS then CR then back to DS if needed or next CS or ER",implementation_artifacts,story,
|
||||
bmm,4-implementation,Dev Story,DS,40,skill:bmad-dev-story,bmad-bmm-dev-story,true,dev,Create Mode,"Story cycle: Execute story implementation tasks and tests then CR then back to DS if fixes needed",,,
|
||||
bmm,4-implementation,Code Review,CR,50,skill:bmad-code-review,bmad-bmm-code-review,false,dev,Create Mode,"Story cycle: If issues back to DS if approved then next CS or ER if epic complete",,,
|
||||
bmm,4-implementation,QA Automation Test,QA,45,skill:bmad-qa-generate-e2e-tests,bmad-bmm-qa-automate,false,qa,Create Mode,"Generate automated API and E2E tests for implemented code using the project's existing test framework (detects existing well known in use test frameworks). Use after implementation to add test coverage. NOT for code review or story validation - use CR for that.",implementation_artifacts,"test suite",
|
||||
bmm,4-implementation,Retrospective,ER,60,skill:bmad-retrospective,bmad-bmm-retrospective,false,sm,Create Mode,"Optional at epic end: Review completed work lessons learned and next epic or if major issues consider CC",implementation_artifacts,retrospective,
|
||||
module,skill,display-name,menu-code,description,action,args,phase,after,before,required,output-location,outputs
|
||||
BMad Method,bmad-document-project,Document Project,DP,Analyze an existing project to produce useful documentation.,,anytime,,,false,project-knowledge,*
|
||||
BMad Method,bmad-generate-project-context,Generate Project Context,GPC,Scan existing codebase to generate a lean LLM-optimized project-context.md. Essential for brownfield projects.,,anytime,,,false,output_folder,project context
|
||||
BMad Method,bmad-quick-dev,Quick Dev,QQ,Unified intent-in code-out workflow: clarify plan implement review and present.,,anytime,,,false,implementation_artifacts,spec and project implementation
|
||||
BMad Method,bmad-correct-course,Correct Course,CC,Navigate significant changes. May recommend start over update PRD redo architecture sprint planning or correct epics and stories.,,anytime,,,false,planning_artifacts,change proposal
|
||||
BMad Method,bmad-agent-tech-writer,Write Document,WD,"Describe in detail what you want, and the agent will follow documentation best practices. Multi-turn conversation with subprocess for research/review.",write,,anytime,,,false,project-knowledge,document
|
||||
BMad Method,bmad-agent-tech-writer,Update Standards,US,Update agent memory documentation-standards.md with your specific preferences if you discover missing document conventions.,update-standards,,anytime,,,false,_bmad/_memory/tech-writer-sidecar,standards
|
||||
BMad Method,bmad-agent-tech-writer,Mermaid Generate,MG,Create a Mermaid diagram based on user description. Will suggest diagram types if not specified.,mermaid,,anytime,,,false,planning_artifacts,mermaid diagram
|
||||
BMad Method,bmad-agent-tech-writer,Validate Document,VD,Review the specified document against documentation standards and best practices. Returns specific actionable improvement suggestions organized by priority.,validate,[path],anytime,,,false,planning_artifacts,validation report
|
||||
BMad Method,bmad-agent-tech-writer,Explain Concept,EC,Create clear technical explanations with examples and diagrams for complex concepts.,explain,[topic],anytime,,,false,project_knowledge,explanation
|
||||
BMad Method,bmad-brainstorming,Brainstorm Project,BP,Expert guided facilitation through a single or multiple techniques.,,1-analysis,,,false,planning_artifacts,brainstorming session
|
||||
BMad Method,bmad-market-research,Market Research,MR,"Market analysis competitive landscape customer needs and trends.",,1-analysis,,,false,"planning_artifacts|project-knowledge",research documents
|
||||
BMad Method,bmad-domain-research,Domain Research,DR,Industry domain deep dive subject matter expertise and terminology.,,1-analysis,,,false,"planning_artifacts|project_knowledge",research documents
|
||||
BMad Method,bmad-technical-research,Technical Research,TR,Technical feasibility architecture options and implementation approaches.,,1-analysis,,,false,"planning_artifacts|project_knowledge",research documents
|
||||
BMad Method,bmad-product-brief,Create Brief,CB,A guided experience to nail down your product idea.,,1-analysis,,,false,planning_artifacts,product brief
|
||||
BMad Method,bmad-create-prd,Create PRD,CP,Expert led facilitation to produce your Product Requirements Document.,,2-planning,,,true,planning_artifacts,prd
|
||||
BMad Method,bmad-validate-prd,Validate PRD,VP,,,[path],2-planning,bmad-create-prd,,false,planning_artifacts,prd validation report
|
||||
BMad Method,bmad-edit-prd,Edit PRD,EP,,,[path],2-planning,bmad-validate-prd,,false,planning_artifacts,updated prd
|
||||
BMad Method,bmad-create-ux-design,Create UX,CU,"Guidance through realizing the plan for your UX, strongly recommended if a UI is a primary piece of the proposed project.",,2-planning,bmad-create-prd,,false,planning_artifacts,ux design
|
||||
BMad Method,bmad-create-architecture,Create Architecture,CA,Guided workflow to document technical decisions.,,3-solutioning,,,true,planning_artifacts,architecture
|
||||
BMad Method,bmad-create-epics-and-stories,Create Epics and Stories,CE,,,3-solutioning,bmad-create-architecture,,true,planning_artifacts,epics and stories
|
||||
BMad Method,bmad-check-implementation-readiness,Check Implementation Readiness,IR,Ensure PRD UX Architecture and Epics Stories are aligned.,,3-solutioning,bmad-create-epics-and-stories,,true,planning_artifacts,readiness report
|
||||
BMad Method,bmad-sprint-planning,Sprint Planning,SP,Kicks off implementation by producing a plan the implementation agents will follow in sequence for every story.,,4-implementation,,,true,implementation_artifacts,sprint status
|
||||
BMad Method,bmad-sprint-status,Sprint Status,SS,Anytime: Summarize sprint status and route to next workflow.,,4-implementation,bmad-sprint-planning,,false,,
|
||||
BMad Method,bmad-create-story,Create Story,CS,"Story cycle start: Prepare first found story in the sprint plan that is next or a specific epic/story designation.",create,,4-implementation,bmad-sprint-planning,bmad-create-story:validate,true,implementation_artifacts,story
|
||||
BMad Method,bmad-create-story,Validate Story,VS,Validates story readiness and completeness before development work begins.,validate,,4-implementation,bmad-create-story:create,bmad-dev-story,false,implementation_artifacts,story validation report
|
||||
BMad Method,bmad-dev-story,Dev Story,DS,Story cycle: Execute story implementation tasks and tests then CR then back to DS if fixes needed.,,4-implementation,bmad-create-story:validate,,true,,
|
||||
BMad Method,bmad-code-review,Code Review,CR,Story cycle: If issues back to DS if approved then next CS or ER if epic complete.,,4-implementation,bmad-dev-story,,false,,
|
||||
BMad Method,bmad-qa-generate-e2e-tests,QA Automation Test,QA,Generate automated API and E2E tests for implemented code. NOT for code review or story validation — use CR for that.,,4-implementation,bmad-dev-story,,false,implementation_artifacts,test suite
|
||||
BMad Method,bmad-retrospective,Retrospective,ER,Optional at epic end: Review completed work lessons learned and next epic or if major issues consider CC.,,4-implementation,bmad-code-review,,false,implementation_artifacts,retrospective
|
||||
|
|
|
|||
|
Can't render this file because it has a wrong number of fields in line 2.
|
|
|
@ -1,92 +1,73 @@
|
|||
---
|
||||
name: bmad-help
|
||||
description: 'Analyzes current state and user query to answer BMad questions or recommend the next workflow or agent. Use when user says what should I do next, what do I do now, or asks a question about BMad'
|
||||
description: 'Analyzes current state and user query to answer BMad questions or recommend the next skill(s) to use. Use when user asks for help, bmad help, what to do next, or what to start with in BMad.'
|
||||
---
|
||||
|
||||
# Task: BMAD Help
|
||||
# BMad Help
|
||||
|
||||
## ROUTING RULES
|
||||
## Purpose
|
||||
|
||||
- **Empty `phase` = anytime** — Universal tools work regardless of workflow state
|
||||
- **Numbered phases indicate sequence** — Phases like `1-discover` → `2-define` → `3-build` → `4-ship` flow in order (naming varies by module)
|
||||
- **Phase with no Required Steps** - If an entire phase has no required, true items, the entire phase is optional. If it is sequentially before another phase, it can be recommended, but always be clear with the use what the true next required item is.
|
||||
- **Stay in module** — Guide through the active module's workflow based on phase+sequence ordering
|
||||
- **Descriptions contain routing** — Read for alternate paths (e.g., "back to previous if fixes needed")
|
||||
- **`required=true` blocks progress** — Required workflows must complete before proceeding to later phases
|
||||
- **Artifacts reveal completion** — Search resolved output paths for `outputs` patterns, fuzzy-match found files to workflow rows
|
||||
Help the user understand where they are in their BMad workflow and what to do next. Answer BMad questions when asked.
|
||||
|
||||
## DISPLAY RULES
|
||||
## Desired Outcomes
|
||||
|
||||
### Command-Based Workflows
|
||||
When `command` field has a value:
|
||||
- Show the command as a skill name in backticks (e.g., `bmad-bmm-create-prd`)
|
||||
When this skill completes, the user should:
|
||||
|
||||
### Skill-Referenced Workflows
|
||||
When `workflow-file` starts with `skill:`:
|
||||
- The value is a skill reference (e.g., `skill:bmad-quick-dev`), NOT a file path
|
||||
- Do NOT attempt to resolve or load it as a file path
|
||||
- Display using the `command` column value as a skill name in backticks (same as command-based workflows)
|
||||
1. **Know where they are** — which module and phase they're in, what's already been completed
|
||||
2. **Know what to do next** — the next recommended and/or required step, with clear reasoning
|
||||
3. **Know how to invoke it** — skill name, menu code, action context, and any args that shortcut the conversation
|
||||
4. **Get offered a quick start** — when a single skill is the clear next step, offer to run it for the user right now rather than just listing it
|
||||
5. **Feel oriented, not overwhelmed** — surface only what's relevant to their current position; don't dump the entire catalog
|
||||
|
||||
### Agent-Based Workflows
|
||||
When `command` field is empty:
|
||||
- User loads agent first by invoking the agent skill (e.g., `bmad-pm`)
|
||||
- Then invokes by referencing the `code` field or describing the `name` field
|
||||
- Do NOT show a slash command — show the code value and agent load instruction instead
|
||||
## Data Sources
|
||||
|
||||
- **Catalog**: `{project-root}/_bmad/_config/bmad-help.csv` — assembled manifest of all installed module skills
|
||||
- **Config**: `config.yaml` and `user-config.yaml` files in `{project-root}/_bmad/` and its subfolders — resolve `output-location` variables, provide `communication_language` and `project_knowledge`
|
||||
- **Artifacts**: Files matching `outputs` patterns at resolved `output-location` paths reveal which steps are possibly completed; their content may also provide grounding context for recommendations
|
||||
- **Project knowledge**: If `project_knowledge` resolves to an existing path, read it for grounding context. Never fabricate project-specific details.
|
||||
|
||||
## CSV Interpretation
|
||||
|
||||
The catalog uses this format:
|
||||
|
||||
Example presentation for empty command:
|
||||
```
|
||||
Explain Concept (EC)
|
||||
Load: tech-writer agent skill, then ask to "EC about [topic]"
|
||||
Agent: Tech Writer
|
||||
Description: Create clear technical explanations with examples...
|
||||
module,skill,display-name,menu-code,description,action,args,phase,after,before,required,output-location,outputs
|
||||
```
|
||||
|
||||
## MODULE DETECTION
|
||||
**Phases** determine the high-level flow:
|
||||
- `anytime` — available regardless of workflow state
|
||||
- Numbered phases (`1-analysis`, `2-planning`, etc.) flow in order; naming varies by module
|
||||
|
||||
- **Empty `module` column** → universal tools (work across all modules)
|
||||
- **Named `module`** → module-specific workflows
|
||||
**Dependencies** determine ordering within and across phases:
|
||||
- `after` — skills that should ideally complete before this one
|
||||
- `before` — skills that should run after this one
|
||||
- Format: `skill-name` for single-action skills, `skill-name:action` for multi-action skills
|
||||
|
||||
Detect the active module from conversation context, recent workflows, or user query keywords. If ambiguous, ask the user.
|
||||
**Required gates**:
|
||||
- `required=true` items must complete before the user can meaningfully proceed to later phases
|
||||
- A phase with no required items is entirely optional — recommend it but be clear about what's actually required next
|
||||
|
||||
## INPUT ANALYSIS
|
||||
**Completion detection**:
|
||||
- Search resolved output paths for `outputs` patterns
|
||||
- Fuzzy-match found files to catalog rows
|
||||
- User may also state completion explicitly, or it may be evident from the current conversation
|
||||
|
||||
Determine what was just completed:
|
||||
- Explicit completion stated by user
|
||||
- Workflow completed in current conversation
|
||||
- Artifacts found matching `outputs` patterns
|
||||
- If `index.md` exists, read it for additional context
|
||||
- If still unclear, ask: "What workflow did you most recently complete?"
|
||||
**Descriptions carry routing context** — some contain cycle info and alternate paths (e.g., "back to DS if fixes needed"). Read them as navigation hints, not just display text.
|
||||
|
||||
## EXECUTION
|
||||
## Response Format
|
||||
|
||||
1. **Load catalog** — Load `{project-root}/_bmad/_config/bmad-help.csv`
|
||||
For each recommended item, present:
|
||||
- `[menu-code]` **Display name** — e.g., "[CP] Create PRD"
|
||||
- Skill name in backticks — e.g., `bmad-create-prd`
|
||||
- For multi-action skills: action invocation context — e.g., "tech-writer lets create a mermaid diagram!"
|
||||
- Description if present in CSV; otherwise your existing knowledge of the skill suffices
|
||||
- Args if available
|
||||
|
||||
2. **Resolve output locations and config** — Scan each folder under `{project-root}/_bmad/` (except `_config`) for `config.yaml`. For each workflow row, resolve its `output-location` variables against that module's config so artifact paths can be searched. Also extract `communication_language` and `project_knowledge` from each scanned module's config.
|
||||
**Ordering**: Show optional items first, then the next required item. Make it clear which is which.
|
||||
|
||||
3. **Ground in project knowledge** — If `project_knowledge` resolves to an existing path, read available documentation files (architecture docs, project overview, tech stack references) for grounding context. Use discovered project facts when composing any project-specific output. Never fabricate project-specific details — if documentation is unavailable, state so.
|
||||
## Constraints
|
||||
|
||||
4. **Detect active module** — Use MODULE DETECTION above
|
||||
|
||||
5. **Analyze input** — Task may provide a workflow name/code, conversational phrase, or nothing. Infer what was just completed using INPUT ANALYSIS above.
|
||||
|
||||
6. **Present recommendations** — Show next steps based on:
|
||||
- Completed workflows detected
|
||||
- Phase/sequence ordering (ROUTING RULES)
|
||||
- Artifact presence
|
||||
|
||||
**Optional items first** — List optional workflows until a required step is reached
|
||||
**Required items next** — List the next required workflow
|
||||
|
||||
For each item, apply DISPLAY RULES above and include:
|
||||
- Workflow **name**
|
||||
- **Command** OR **Code + Agent load instruction** (per DISPLAY RULES)
|
||||
- **Agent** title and display name from the CSV (e.g., "🎨 Alex (Designer)")
|
||||
- Brief **description**
|
||||
|
||||
7. **Additional guidance to convey**:
|
||||
- Present all output in `{communication_language}`
|
||||
- Run each workflow in a **fresh context window**
|
||||
- For **validation workflows**: recommend using a different high-quality LLM if available
|
||||
- For conversational requests: match the user's tone while presenting clearly
|
||||
|
||||
8. Return to the calling process after presenting recommendations.
|
||||
- Present all output in `{communication_language}`
|
||||
- Recommend running each skill in a **fresh context window**
|
||||
- Match the user's tone — conversational when they're casual, structured when they want specifics
|
||||
- If the active module is ambiguous, ask rather than guess
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
module,phase,name,code,sequence,workflow-file,command,required,agent,options,description,output-location,outputs
|
||||
core,anytime,Brainstorming,BSP,,skill:bmad-brainstorming,bmad-brainstorming,false,analyst,,"Generate diverse ideas through interactive techniques. Use early in ideation phase or when stuck generating ideas.",{output_folder}/brainstorming/brainstorming-session-{{date}}.md,,
|
||||
core,anytime,Party Mode,PM,,skill:bmad-party-mode,bmad-party-mode,false,party-mode facilitator,,"Orchestrate multi-agent discussions. Use when you need multiple agent perspectives or want agents to collaborate.",,
|
||||
core,anytime,bmad-help,BH,,skill:bmad-help,bmad-help,false,,,"Get unstuck by showing what workflow steps come next or answering BMad Method questions.",,
|
||||
core,anytime,Index Docs,ID,,skill:bmad-index-docs,bmad-index-docs,false,,,"Create lightweight index for quick LLM scanning. Use when LLM needs to understand available docs without loading everything.",,
|
||||
core,anytime,Shard Document,SD,,skill:bmad-shard-doc,bmad-shard-doc,false,,,"Split large documents into smaller files by sections. Use when doc becomes too large (>500 lines) to manage effectively.",,
|
||||
core,anytime,Editorial Review - Prose,EP,,skill:bmad-editorial-review-prose,bmad-editorial-review-prose,false,,,"Review prose for clarity, tone, and communication issues. Use after drafting to polish written content.",report located with target document,"three-column markdown table with suggested fixes",
|
||||
core,anytime,Editorial Review - Structure,ES,,skill:bmad-editorial-review-structure,bmad-editorial-review-structure,false,,,"Propose cuts, reorganization, and simplification while preserving comprehension. Use when doc produced from multiple subprocesses or needs structural improvement.",report located with target document,
|
||||
core,anytime,Adversarial Review (General),AR,,skill:bmad-review-adversarial-general,bmad-review-adversarial-general,false,,,"Review content critically to find issues and weaknesses. Use for quality assurance or before finalizing deliverables. Code Review in other modules run this automatically, but its useful also for document reviews",,
|
||||
core,anytime,Edge Case Hunter Review,ECH,,skill:bmad-review-edge-case-hunter,bmad-review-edge-case-hunter,false,,,"Walk every branching path and boundary condition in code, report only unhandled edge cases. Use alongside adversarial review for orthogonal coverage - method-driven not attitude-driven.",,
|
||||
core,anytime,Distillator,DG,,skill:bmad-distillator,bmad-distillator,false,,,"Lossless LLM-optimized compression of source documents. Use when you need token-efficient distillates that preserve all information for downstream LLM consumption.",adjacent to source document or specified output_path,distillate markdown file(s)
|
||||
module,skill,display-name,menu-code,description,action,args,phase,after,before,required,output-location,outputs
|
||||
Core,bmad-brainstorming,Brainstorming,BSP,Use early in ideation or when stuck generating ideas.,,anytime,,,false,{output_folder}/brainstorming,brainstorming session
|
||||
Core,bmad-party-mode,Party Mode,PM,Orchestrate multi-agent discussions when you need multiple perspectives or want agents to collaborate.,,anytime,,,false,,
|
||||
Core,bmad-help,BMad Help,BH,,,anytime,,,false,,
|
||||
Core,bmad-index-docs,Index Docs,ID,Use when LLM needs to understand available docs without loading everything.,,anytime,,,false,,
|
||||
Core,bmad-shard-doc,Shard Document,SD,Use when doc becomes too large (>500 lines) to manage effectively.,[path],anytime,,,false,,
|
||||
Core,bmad-editorial-review-prose,Editorial Review - Prose,EP,Use after drafting to polish written content.,[path],anytime,,,false,report located with target document,three-column markdown table with suggested fixes
|
||||
Core,bmad-editorial-review-structure,Editorial Review - Structure,ES,Use when doc produced from multiple subprocesses or needs structural improvement.,[path],anytime,,,false,report located with target document,
|
||||
Core,bmad-review-adversarial-general,Adversarial Review,AR,"Use for quality assurance or before finalizing deliverables. Code Review in other modules runs this automatically, but also useful for document reviews.",[path],anytime,,,false,,
|
||||
Core,bmad-review-edge-case-hunter,Edge Case Hunter Review,ECH,Use alongside adversarial review for orthogonal coverage — method-driven not attitude-driven.,[path],anytime,,,false,,
|
||||
Core,bmad-distillator,Distillator,DG,Use when you need token-efficient distillates that preserve all information for downstream LLM consumption.,[path],anytime,,,false,adjacent to source document or specified output_path,distillate markdown file(s)
|
||||
|
|
|
|||
|
Can't render this file because it has a wrong number of fields in line 2.
|
|
|
@ -0,0 +1,369 @@
|
|||
/**
|
||||
* Extension Module Merge Tests — Issue #1667
|
||||
*
|
||||
* Verifies that a custom extension module with `code: bmm` in its module.yaml
|
||||
* merges its files into the base BMM installation instead of replacing the
|
||||
* entire directory.
|
||||
*
|
||||
* Expected behavior (file-level merge):
|
||||
* - Files with the same name → extension overrides base
|
||||
* - Files with unique names → extension adds alongside base
|
||||
*
|
||||
* Usage: node test/test-installation-extensions.js
|
||||
*/
|
||||
|
||||
const path = require('node:path');
|
||||
const os = require('node:os');
|
||||
const fs = require('fs-extra');
|
||||
const { ModuleManager } = require('../tools/cli/installers/lib/modules/manager');
|
||||
|
||||
// ANSI colors (match existing test files)
|
||||
const colors = {
|
||||
reset: '\u001B[0m',
|
||||
green: '\u001B[32m',
|
||||
red: '\u001B[31m',
|
||||
yellow: '\u001B[33m',
|
||||
cyan: '\u001B[36m',
|
||||
dim: '\u001B[2m',
|
||||
};
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
function assert(condition, testName, errorMessage = '') {
|
||||
if (condition) {
|
||||
console.log(`${colors.green}✓${colors.reset} ${testName}`);
|
||||
passed++;
|
||||
} else {
|
||||
console.log(`${colors.red}✗${colors.reset} ${testName}`);
|
||||
if (errorMessage) console.log(` ${colors.dim}${errorMessage}${colors.reset}`);
|
||||
failed++;
|
||||
throw new Error(`${testName}${errorMessage ? ': ' + errorMessage : ''}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a minimal compiled agent .md file (no YAML compilation needed).
|
||||
*/
|
||||
function minimalAgentMd(name) {
|
||||
return ['---', `name: "${name}"`, `description: "Test agent: ${name}"`, '---', '', `You are ${name}.`].join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a fake base module source directory that looks like src/bmm/.
|
||||
* Only files relevant to the merge test are created (agents directory).
|
||||
*/
|
||||
async function createBaseModuleSource(tmpDir) {
|
||||
const src = path.join(tmpDir, 'src-base');
|
||||
|
||||
// module.yaml
|
||||
await fs.ensureDir(src);
|
||||
await fs.writeFile(path.join(src, 'module.yaml'), 'name: BMM\ncode: bmm\nversion: 6.0.0\n');
|
||||
|
||||
// agents: analyst + pm (standard base agents)
|
||||
await fs.ensureDir(path.join(src, 'agents'));
|
||||
await fs.writeFile(path.join(src, 'agents', 'analyst.md'), minimalAgentMd('analyst'));
|
||||
await fs.writeFile(path.join(src, 'agents', 'pm.md'), minimalAgentMd('pm'));
|
||||
|
||||
return src;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a fake extension module source directory (simulates a user's
|
||||
* custom module with code: bmm in its module.yaml).
|
||||
*
|
||||
* Includes:
|
||||
* - pm.md → should OVERRIDE the base pm.md
|
||||
* - my-agent.md → unique name, should be ADDED alongside base agents
|
||||
*/
|
||||
async function createExtensionModuleSource(tmpDir) {
|
||||
const src = path.join(tmpDir, 'src-extension');
|
||||
|
||||
await fs.ensureDir(src);
|
||||
await fs.writeFile(path.join(src, 'module.yaml'), 'name: My Extension\ncode: bmm\nversion: 1.0.0\n');
|
||||
|
||||
await fs.ensureDir(path.join(src, 'agents'));
|
||||
await fs.writeFile(path.join(src, 'agents', 'pm.md'), minimalAgentMd('pm-override'));
|
||||
await fs.writeFile(path.join(src, 'agents', 'my-agent.md'), minimalAgentMd('my-agent'));
|
||||
|
||||
return src;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a minimal manifest so manager.install() can record module metadata.
|
||||
*/
|
||||
async function createMinimalManifest(bmadDir) {
|
||||
await fs.ensureDir(path.join(bmadDir, '_config'));
|
||||
await fs.writeFile(
|
||||
path.join(bmadDir, '_config', 'manifest.yaml'),
|
||||
'installation:\n version: "0.0.0"\n installDate: "2026-01-01T00:00:00.000Z"\n lastUpdated: "2026-01-01T00:00:00.000Z"\nmodules: []\nides: []\n',
|
||||
);
|
||||
}
|
||||
|
||||
async function runTests() {
|
||||
console.log(`${colors.cyan}========================================`);
|
||||
console.log('Extension Module Merge — Issue #1667');
|
||||
console.log(`========================================${colors.reset}\n`);
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Test 1: isExtension:false (default) — second install replaces directory
|
||||
// (confirms the bug existed: without the fix, extension would wipe base)
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
console.log(`${colors.yellow}Scenario A: install without isExtension (replacement mode)${colors.reset}\n`);
|
||||
|
||||
{
|
||||
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'bmad-ext-test-'));
|
||||
try {
|
||||
const baseSrc = await createBaseModuleSource(tmpDir);
|
||||
const extSrc = await createExtensionModuleSource(tmpDir);
|
||||
const bmadDir = path.join(tmpDir, '_bmad');
|
||||
const targetPath = path.join(bmadDir, 'bmm');
|
||||
|
||||
await createMinimalManifest(bmadDir);
|
||||
const manager = new ModuleManager();
|
||||
|
||||
// Step 1: Install base module via manager.install()
|
||||
manager.setCustomModulePaths(new Map([['bmm', baseSrc]]));
|
||||
await manager.install('bmm', bmadDir, null, { silent: true, skipModuleInstaller: true });
|
||||
|
||||
// Step 2: Install extension WITHOUT isExtension flag (old/broken behavior: wipes directory)
|
||||
manager.setCustomModulePaths(new Map([['bmm', extSrc]]));
|
||||
await manager.install('bmm', bmadDir, null, { silent: true, skipModuleInstaller: true });
|
||||
|
||||
const analystExists = await fs.pathExists(path.join(targetPath, 'agents', 'analyst.md'));
|
||||
const myAgentExists = await fs.pathExists(path.join(targetPath, 'agents', 'my-agent.md'));
|
||||
|
||||
assert(!analystExists, 'Without fix: base analyst.md is GONE (replacement behavior confirmed)');
|
||||
assert(myAgentExists, 'Without fix: extension my-agent.md is present');
|
||||
} finally {
|
||||
await fs.remove(tmpDir);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('');
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Test 2: isExtension:true (the fix) — extension merges on top of base
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
console.log(`${colors.yellow}Scenario B: install with isExtension:true (merge mode — the fix)${colors.reset}\n`);
|
||||
|
||||
{
|
||||
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'bmad-ext-test-'));
|
||||
try {
|
||||
const baseSrc = await createBaseModuleSource(tmpDir);
|
||||
const extSrc = await createExtensionModuleSource(tmpDir);
|
||||
const bmadDir = path.join(tmpDir, '_bmad');
|
||||
const targetPath = path.join(bmadDir, 'bmm');
|
||||
|
||||
await createMinimalManifest(bmadDir);
|
||||
const manager = new ModuleManager();
|
||||
|
||||
// Step 1: Install base module via manager.install()
|
||||
manager.setCustomModulePaths(new Map([['bmm', baseSrc]]));
|
||||
await manager.install('bmm', bmadDir, null, { silent: true, skipModuleInstaller: true });
|
||||
|
||||
// Step 2: Install extension with isExtension:true → should MERGE on top of base
|
||||
manager.setCustomModulePaths(new Map([['bmm', extSrc]]));
|
||||
await manager.install('bmm', bmadDir, null, { isExtension: true, silent: true, skipModuleInstaller: true });
|
||||
|
||||
const analystExists = await fs.pathExists(path.join(targetPath, 'agents', 'analyst.md'));
|
||||
const myAgentExists = await fs.pathExists(path.join(targetPath, 'agents', 'my-agent.md'));
|
||||
const pmContent = await fs.readFile(path.join(targetPath, 'agents', 'pm.md'), 'utf8');
|
||||
|
||||
assert(analystExists, 'Base analyst.md is PRESERVED after extension install');
|
||||
assert(myAgentExists, 'Extension my-agent.md is ADDED alongside base agents');
|
||||
assert(pmContent.includes('pm-override'), 'Extension pm.md OVERRIDES base pm.md (same-name wins)');
|
||||
} finally {
|
||||
await fs.remove(tmpDir);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('');
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Test 3: ModuleManager.install() with isExtension:true via options
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
console.log(`${colors.yellow}Scenario C: ModuleManager.install() respects isExtension option${colors.reset}\n`);
|
||||
|
||||
{
|
||||
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'bmad-ext-test-'));
|
||||
try {
|
||||
const baseSrc = await createBaseModuleSource(tmpDir);
|
||||
const bmadDir = path.join(tmpDir, '_bmad');
|
||||
const targetPath = path.join(bmadDir, 'bmm');
|
||||
|
||||
await createMinimalManifest(bmadDir);
|
||||
const manager = new ModuleManager();
|
||||
manager.setCustomModulePaths(new Map([['bmm', baseSrc]]));
|
||||
|
||||
// Install base so target directory exists
|
||||
await manager.install('bmm', bmadDir, null, { silent: true, skipModuleInstaller: true });
|
||||
|
||||
// Add a sentinel file that simulates a file the user placed in the installed module
|
||||
await fs.writeFile(path.join(targetPath, 'sentinel.txt'), 'user data');
|
||||
assert(await fs.pathExists(path.join(targetPath, 'sentinel.txt')), 'Pre-condition: sentinel.txt exists before extension install');
|
||||
|
||||
// With isExtension:true → fs.remove is skipped; sentinel must survive
|
||||
await manager.install('bmm', bmadDir, null, { isExtension: true, silent: true, skipModuleInstaller: true });
|
||||
assert(
|
||||
await fs.pathExists(path.join(targetPath, 'sentinel.txt')),
|
||||
'With isExtension:true: sentinel.txt PRESERVED (fs.remove was skipped)',
|
||||
);
|
||||
|
||||
// Without isExtension → fs.remove runs; sentinel must be gone
|
||||
await manager.install('bmm', bmadDir, null, { silent: true, skipModuleInstaller: true });
|
||||
assert(
|
||||
!(await fs.pathExists(path.join(targetPath, 'sentinel.txt'))),
|
||||
'Without isExtension: sentinel.txt REMOVED (directory was cleared by fs.remove)',
|
||||
);
|
||||
} finally {
|
||||
await fs.remove(tmpDir);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('');
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Test 4 (E2E): installModuleWithDependencies → base install, then
|
||||
// moduleManager.install with isExtension:true → merge
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
console.log(
|
||||
`${colors.yellow}Scenario D: E2E install — base via installModuleWithDependencies, extension via moduleManager.install${colors.reset}\n`,
|
||||
);
|
||||
|
||||
{
|
||||
const { Installer } = require('../tools/cli/installers/lib/core/installer');
|
||||
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'bmad-e2e-test-'));
|
||||
try {
|
||||
const baseSrc = await createBaseModuleSource(tmpDir);
|
||||
const extSrc = await createExtensionModuleSource(tmpDir);
|
||||
const bmadDir = path.join(tmpDir, '_bmad');
|
||||
|
||||
// Pre-create a minimal manifest so moduleManager.install can record the module
|
||||
await createMinimalManifest(bmadDir);
|
||||
|
||||
const installer = new Installer();
|
||||
|
||||
// Stub the resolver: point 'bmm' at the fake base source
|
||||
installer.moduleManager.setCustomModulePaths(new Map([['bmm', baseSrc]]));
|
||||
|
||||
// Step 1: Install base module via the public installModuleWithDependencies
|
||||
await installer.installModuleWithDependencies('bmm', bmadDir, {});
|
||||
|
||||
const targetPath = path.join(bmadDir, 'bmm');
|
||||
assert(
|
||||
await fs.pathExists(path.join(targetPath, 'agents', 'analyst.md')),
|
||||
'E2E: after base install via installModuleWithDependencies — analyst.md is present',
|
||||
);
|
||||
assert(
|
||||
await fs.pathExists(path.join(targetPath, 'agents', 'pm.md')),
|
||||
'E2E: after base install via installModuleWithDependencies — pm.md is present',
|
||||
);
|
||||
|
||||
// Step 2: Re-stub the resolver to point to the extension source, then install
|
||||
// the extension with isExtension:true so it merges on top of the base
|
||||
installer.moduleManager.setCustomModulePaths(new Map([['bmm', extSrc]]));
|
||||
await installer.moduleManager.install('bmm', bmadDir, null, {
|
||||
isExtension: true,
|
||||
skipModuleInstaller: true,
|
||||
silent: true,
|
||||
});
|
||||
|
||||
// Assert merged output: base files preserved, extension files added/overridden
|
||||
assert(
|
||||
await fs.pathExists(path.join(targetPath, 'agents', 'analyst.md')),
|
||||
'E2E: base analyst.md is PRESERVED after extension install (merge, not replace)',
|
||||
);
|
||||
assert(
|
||||
await fs.pathExists(path.join(targetPath, 'agents', 'my-agent.md')),
|
||||
'E2E: extension my-agent.md is ADDED alongside base agents',
|
||||
);
|
||||
const pmContent = await fs.readFile(path.join(targetPath, 'agents', 'pm.md'), 'utf8');
|
||||
assert(pmContent.includes('pm-override'), 'E2E: extension pm.md OVERRIDES base pm.md (same-name wins)');
|
||||
|
||||
// Verify manifest has exactly one 'bmm' entry (no duplicates)
|
||||
const yaml = require('yaml');
|
||||
const manifestRaw = await fs.readFile(path.join(bmadDir, '_config', 'manifest.yaml'), 'utf8');
|
||||
const manifest = yaml.parse(manifestRaw);
|
||||
const bmmEntries = (manifest.modules || []).filter((m) => m.name === 'bmm');
|
||||
assert(bmmEntries.length === 1, 'E2E: manifest contains exactly one bmm entry (no duplicates)');
|
||||
} finally {
|
||||
await fs.remove(tmpDir);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('');
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Test 5 (E2E update): user-modified files are preserved when extension
|
||||
// overlay is applied with isExtension:true
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
console.log(`${colors.yellow}Scenario E: user-modified sentinel file is preserved during extension overlay${colors.reset}\n`);
|
||||
|
||||
{
|
||||
const { Installer } = require('../tools/cli/installers/lib/core/installer');
|
||||
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'bmad-e2e-update-'));
|
||||
try {
|
||||
const baseSrc = await createBaseModuleSource(tmpDir);
|
||||
const extSrc = await createExtensionModuleSource(tmpDir);
|
||||
const bmadDir = path.join(tmpDir, '_bmad');
|
||||
|
||||
await createMinimalManifest(bmadDir);
|
||||
const installer = new Installer();
|
||||
installer.moduleManager.setCustomModulePaths(new Map([['bmm', baseSrc]]));
|
||||
|
||||
// Step 1: Install base module
|
||||
await installer.installModuleWithDependencies('bmm', bmadDir, {});
|
||||
|
||||
const targetPath = path.join(bmadDir, 'bmm');
|
||||
|
||||
// Step 2: Simulate a user adding a file to the installed module
|
||||
await fs.writeFile(path.join(targetPath, 'user-modified.txt'), 'my custom content');
|
||||
// Also record the original pm.md content so we can confirm the extension overrides it
|
||||
assert(
|
||||
await fs.pathExists(path.join(targetPath, 'agents', 'analyst.md')),
|
||||
'Update pre-condition: base analyst.md is present before extension overlay',
|
||||
);
|
||||
|
||||
// Step 3: Apply extension overlay (isExtension:true → skip fs.remove)
|
||||
installer.moduleManager.setCustomModulePaths(new Map([['bmm', extSrc]]));
|
||||
await installer.moduleManager.install('bmm', bmadDir, null, {
|
||||
isExtension: true,
|
||||
skipModuleInstaller: true,
|
||||
silent: true,
|
||||
});
|
||||
|
||||
// User-added file must survive because isExtension skips directory removal
|
||||
assert(
|
||||
await fs.pathExists(path.join(targetPath, 'user-modified.txt')),
|
||||
'Update: user-modified.txt is PRESERVED (extension overlay did not wipe the directory)',
|
||||
);
|
||||
// Base file must also survive
|
||||
assert(
|
||||
await fs.pathExists(path.join(targetPath, 'agents', 'analyst.md')),
|
||||
'Update: base analyst.md is PRESERVED after extension overlay',
|
||||
);
|
||||
// Extension-specific file must be present
|
||||
assert(await fs.pathExists(path.join(targetPath, 'agents', 'my-agent.md')), 'Update: extension my-agent.md is ADDED by the overlay');
|
||||
// Extension pm.md overrides the base version
|
||||
const pmUpdated = await fs.readFile(path.join(targetPath, 'agents', 'pm.md'), 'utf8');
|
||||
assert(pmUpdated.includes('pm-override'), 'Update: extension pm.md OVERRIDES base pm.md during overlay');
|
||||
} finally {
|
||||
await fs.remove(tmpDir);
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Summary
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
console.log(`\n${colors.cyan}========================================${colors.reset}`);
|
||||
console.log(`Results: ${colors.green}${passed} passed${colors.reset}, ${failed > 0 ? colors.red : ''}${failed} failed${colors.reset}`);
|
||||
console.log(`${colors.cyan}========================================${colors.reset}\n`);
|
||||
|
||||
if (failed > 0) process.exit(1);
|
||||
}
|
||||
|
||||
runTests().catch((error) => {
|
||||
console.error(`${colors.red}Unexpected error:${colors.reset}`, error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
@ -997,6 +997,45 @@ class Installer {
|
|||
this.moduleManager.setCustomModulePaths(customModulePaths);
|
||||
}
|
||||
|
||||
// Check if this custom module extends a built-in/external base module.
|
||||
// If a base source exists at a different path, install it first so the
|
||||
// extension merges on top instead of replacing the entire directory (#1667).
|
||||
const customSourcePath = customModulePaths.get(moduleName);
|
||||
// Temporarily remove the custom path so findModuleSource resolves the real
|
||||
// base, including external official modules that getModulePath() misses.
|
||||
customModulePaths.delete(moduleName);
|
||||
this.moduleManager.setCustomModulePaths(customModulePaths);
|
||||
let hasBaseModule = false;
|
||||
try {
|
||||
const baseSourcePath = await this.moduleManager.findModuleSource(moduleName, { silent: true });
|
||||
hasBaseModule =
|
||||
!!customSourcePath &&
|
||||
!!baseSourcePath &&
|
||||
baseSourcePath !== customSourcePath &&
|
||||
(await fs.pathExists(path.join(baseSourcePath, 'module.yaml')));
|
||||
|
||||
if (hasBaseModule) {
|
||||
// Install the base module first (clean install, removes any prior version).
|
||||
// Custom path is already cleared above so findModuleSource resolves to the base.
|
||||
if (resolution && resolution.byModule && resolution.byModule[moduleName]) {
|
||||
await this.installModuleWithDependencies(moduleName, bmadDir, resolution.byModule[moduleName]);
|
||||
} else {
|
||||
await this.moduleManager.install(
|
||||
moduleName,
|
||||
bmadDir,
|
||||
(filePath) => {
|
||||
this.installedFiles.add(filePath);
|
||||
},
|
||||
{ installer: this, silent: true, moduleConfig: {} },
|
||||
);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
// Always restore the custom path so the extension overlay resolves correctly.
|
||||
customModulePaths.set(moduleName, customSourcePath);
|
||||
this.moduleManager.setCustomModulePaths(customModulePaths);
|
||||
}
|
||||
|
||||
const collectedModuleConfig = moduleConfigs[moduleName] || {};
|
||||
await this.moduleManager.install(
|
||||
moduleName,
|
||||
|
|
@ -1006,6 +1045,7 @@ class Installer {
|
|||
},
|
||||
{
|
||||
isCustom: true,
|
||||
isExtension: hasBaseModule, // merge on top of base when extending
|
||||
moduleConfig: collectedModuleConfig,
|
||||
isQuickUpdate: isQuickUpdate,
|
||||
installer: this,
|
||||
|
|
|
|||
|
|
@ -453,7 +453,10 @@ class ModuleManager {
|
|||
}
|
||||
|
||||
// Check if already installed
|
||||
if (await fs.pathExists(targetPath)) {
|
||||
// When isExtension is true, the caller is merging an extension module on top
|
||||
// of an already-installed base module (same code). Skip removal so that base
|
||||
// files are preserved and the extension's files overlay at the file level.
|
||||
if ((await fs.pathExists(targetPath)) && !options.isExtension) {
|
||||
await fs.remove(targetPath);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue