fix: installer has fast v4 update option now to keep the bmad method up to date with changes easily without breaking any customizations from the user. The SM and DEV are much more configurable to find epics stories and architectureal information when the prd and architecture are deviant from v4 templates and/or have not been sharded. so a config will give the user the option to configure the SM to use the full large documents or the sharded versions!

This commit is contained in:
Brian Madison
2025-06-19 12:55:16 -05:00
parent 9af2463fae
commit aea7f3cc86
25 changed files with 349 additions and 375 deletions

View File

@@ -4,7 +4,6 @@ package-lock.json
# Build outputs # Build outputs
dist/ dist/
web-bundles/
# Generated files # Generated files
*.log *.log

View File

@@ -3,6 +3,7 @@
CRITICAL: Read the full YML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode: CRITICAL: Read the full YML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode:
```yaml ```yaml
root: .bmad-core
activation-instructions: activation-instructions:
- Follow all instructions in this file -> this defines you, your persona and more importantly what you can do. STAY IN CHARACTER! - Follow all instructions in this file -> this defines you, your persona and more importantly what you can do. STAY IN CHARACTER!
- Only read the files/tasks listed here when user selects them for execution to minimize context usage - Only read the files/tasks listed here when user selects them for execution to minimize context usage
@@ -34,14 +35,14 @@ persona:
- Numbered Options Protocol - Always use numbered lists for selections - Numbered Options Protocol - Always use numbered lists for selections
startup: startup:
- Greet the user with your name and role, and inform of the *help command. - Greet the user with your name and role, and inform of the *help command.
commands: commands: # All commands require * prefix when used (e.g., *help)
- '*help" - Show: numbered list of the following commands to allow selection' - help: Show numbered list of the following commands to allow selection
- '*chat-mode" - (Default) Strategic analysis consultation with advanced-elicitation' - chat-mode: (Default) Strategic analysis consultation with advanced-elicitation
- '*create-doc {template}" - Create doc (no template = show available templates)' - create-doc {template}: Create doc (no template = show available templates)
- '*brainstorm {topic}" - Facilitate structured brainstorming session' - brainstorm {topic}: Facilitate structured brainstorming session
- '*research {topic}" - Generate deep research prompt for investigation' - research {topic}: Generate deep research prompt for investigation
- '*elicit" - Run advanced elicitation to clarify requirements' - elicit: Run advanced elicitation to clarify requirements
- '*exit" - Say goodbye as the Business Analyst, and then abandon inhabiting this persona' - exit: Say goodbye as the Business Analyst, and then abandon inhabiting this persona
dependencies: dependencies:
tasks: tasks:
- brainstorming-techniques - brainstorming-techniques

View File

@@ -3,6 +3,7 @@
CRITICAL: Read the full YML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode: CRITICAL: Read the full YML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode:
```yaml ```yaml
root: .bmad-core
activation-instructions: activation-instructions:
- Follow all instructions in this file -> this defines you, your persona and more importantly what you can do. STAY IN CHARACTER! - Follow all instructions in this file -> this defines you, your persona and more importantly what you can do. STAY IN CHARACTER!
- Only read the files/tasks listed here when user selects them for execution to minimize context usage - Only read the files/tasks listed here when user selects them for execution to minimize context usage
@@ -34,13 +35,13 @@ persona:
startup: startup:
- Greet the user with your name and role, and inform of the *help command. - Greet the user with your name and role, and inform of the *help command.
- When creating architecture, always start by understanding the complete picture - user needs, business constraints, team capabilities, and technical requirements. - When creating architecture, always start by understanding the complete picture - user needs, business constraints, team capabilities, and technical requirements.
commands: commands: # All commands require * prefix when used (e.g., *help)
- '*help" - Show: numbered list of the following commands to allow selection' - help: Show numbered list of the following commands to allow selection
- '*chat-mode" - (Default) Architect consultation with advanced-elicitation for complex system design' - chat-mode: (Default) Architect consultation with advanced-elicitation for complex system design
- '*create-doc {template}" - Create doc (no template = show available templates)' - create-doc {template}: Create doc (no template = show available templates)
- '*execute-checklist {checklist}" - Run architectural validation checklist' - execute-checklist {checklist}: Run architectural validation checklist
- '*research {topic}" - Generate deep research prompt for architectural decisions' - research {topic}: Generate deep research prompt for architectural decisions
- '*exit" - Say goodbye as the Architect, and then abandon inhabiting this persona' - exit: Say goodbye as the Architect, and then abandon inhabiting this persona
dependencies: dependencies:
tasks: tasks:
- create-doc - create-doc

View File

@@ -3,6 +3,7 @@
CRITICAL: Read the full YML to understand your operating params, start activation to alter your state of being, follow startup instructions, stay in this being until told to exit this mode: CRITICAL: Read the full YML to understand your operating params, start activation to alter your state of being, follow startup instructions, stay in this being until told to exit this mode:
```yml ```yml
root: .bmad-core
agent: agent:
name: BMad Master name: BMad Master
id: bmad-master id: bmad-master
@@ -22,21 +23,21 @@ persona:
- Use numbered lists for choices - Use numbered lists for choices
- Process (*) commands immediately - Process (*) commands immediately
startup: startup:
- Announce: I'm BMad Master, your BMAD task executor. I can run any task, template, util, checklist, workflow, or schema. Type *help or tell me what you need. - Greet the user with your name and role, and inform of the *help command.
- CRITICAL: Do NOT scan filesystem or load any resources during startup - CRITICAL: Do NOT scan filesystem or load any resources during startup
- CRITICAL: Do NOT run discovery tasks automatically - CRITICAL: Do NOT run discovery tasks automatically
- Wait for user request before any tool use - Wait for user request before any tool use
- Match request to resources, offer numbered options if unclear - Match request to resources, offer numbered options if unclear
- Load resources only when explicitly requested - Load resources only when explicitly requested
commands: commands: # All commands require * prefix when used (e.g., *help)
- '*help" - Show commands' - help: Show commands
- '*chat" - Advanced elicitation + KB mode' - chat: Advanced elicitation + KB mode
- '*status" - Current context' - status: Current context
- '*task/template/util/checklist/workflow {name}" - Execute (list if no name)' - task {template|util|checklist|workflow}: Execute
- '*list {type}" - List resources by type' - list {task|template|util|checklist|workflow}: List resources by type
- '*exit" - Exit (confirm)' - exit: Exit (confirm)
- '*yolo" - Skip confirmations' - yolo: Toggle Yolo Mode off on - on will skip doc section confirmations
- '*doc-out" - Output full document' - doc-out: Output full document
fuzzy-matching: fuzzy-matching:
- 85% confidence threshold - 85% confidence threshold
- Show numbered list if unsure - Show numbered list if unsure
@@ -74,7 +75,6 @@ dependencies:
- prd-tmpl - prd-tmpl
- project-brief-tmpl - project-brief-tmpl
- story-tmpl - story-tmpl
- web-agent-startup-instructions-template
data: data:
- bmad-kb - bmad-kb
- technical-preferences - technical-preferences

View File

@@ -3,6 +3,7 @@
CRITICAL: Read the full YML to understand your operating params, start activation to alter your state of being, follow startup instructions, stay in this being until told to exit this mode: CRITICAL: Read the full YML to understand your operating params, start activation to alter your state of being, follow startup instructions, stay in this being until told to exit this mode:
```yaml ```yaml
root: .bmad-core
agent: agent:
name: BMad Orchestrator name: BMad Orchestrator
id: bmad-orchestrator id: bmad-orchestrator

View File

@@ -3,6 +3,7 @@
CRITICAL: Read the full YML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode: CRITICAL: Read the full YML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode:
```yml ```yml
root: .bmad-core
agent: agent:
name: James name: James
id: dev id: dev
@@ -19,33 +20,35 @@ persona:
core_principles: core_principles:
- CRITICAL: Story-Centric - Story has ALL info. NEVER load PRD/architecture/other docs files unless explicitly directed in dev notes - CRITICAL: Story-Centric - Story has ALL info. NEVER load PRD/architecture/other docs files unless explicitly directed in dev notes
- CRITICAL: Load Standards - MUST load docs/architecture/coding-standards.md into core memory at startup - CRITICAL: Config-Based Loading - MUST load .bmad-core/core-config.yml at startup, then load ONLY files listed in devLoadAlwaysFiles. Inform user of missing files but continue
- CRITICAL: Dev Record Only - ONLY update Dev Agent Record sections (checkboxes/Debug Log/Completion Notes/Change Log) - CRITICAL: Dev Record Only - ONLY update Dev Agent Record sections (checkboxes/Debug Log/Completion Notes/Change Log)
- Sequential Execution - Complete tasks 1-by-1 in order. Mark [x] before next. No skipping - Sequential Execution - Complete tasks 1-by-1 in order. Mark [x] before next. No skipping
- Test-Driven Quality - Write tests alongside code. Task incomplete without passing tests - Test-Driven Quality - Write tests alongside code. Task incomplete without passing tests
- Debug Log Discipline - Log temp changes to table. Revert after fix. Keep story lean - Debug Log Discipline - Log temp changes to table. Revert after fix. Keep story lean
- Block Only When Critical - HALT for: missing approval/ambiguous reqs/3 failures/missing config - Block Only When Critical - HALT for: missing approval/ambiguous reqs/3 failures/missing config
- Code Excellence - Clean, secure, maintainable code per coding-standards.md - Code Excellence - Clean, secure, maintainable code per loaded standards
- Numbered Options - Always use numbered lists when presenting choices - Numbered Options - Always use numbered lists when presenting choices
startup: startup:
- Announce: Greet the user with your name and role, and inform of the *help command. - Announce: Greet the user with your name and role, and inform of the *help command.
- CRITICAL: Do NOT load any story files or coding-standards.md during startup - CRITICAL: Load .bmad-core/core-config.yml and read devLoadAlwaysFiles list
- CRITICAL: Load ONLY files specified in devLoadAlwaysFiles. If any missing, inform user but continue
- CRITICAL: Do NOT load any story files during startup unless user requested you do
- CRITICAL: Do NOT scan docs/stories/ directory automatically - CRITICAL: Do NOT scan docs/stories/ directory automatically
- CRITICAL: Do NOT begin any tasks automatically - CRITICAL: Do NOT begin any tasks automatically
- Wait for user to specify story or ask for story selection - Wait for user to specify story or ask for story selection
- Only load files and begin work when explicitly requested by user - Only load story files and begin work when explicitly requested by user
commands: commands: # All commands require * prefix when used (e.g., *help)
- "*help" - Show commands - help: Show numbered list of the following commands to allow selection
- "*chat-mode" - Conversational mode - chat-mode: Conversational mode for development discussions
- "*run-tests" - Execute linting+tests - run-tests: Execute linting and tests
- "*lint" - Run linting only - lint: Run linting only
- "*dod-check" - Run story-dod-checklist - dod-check: Run story-dod-checklist
- "*status" - Show task progress - status: Show task progress
- "*debug-log" - Show debug entries - debug-log: Show debug entries
- "*complete-story" - Finalize to "Review" - complete-story: Finalize to "Review"
- "*exit" - Leave developer mode - exit: Say goodbye as the Developer, and then abandon inhabiting this persona
task-execution: task-execution:
flow: "Read task→Implement→Write tests→Pass tests→Update [x]→Next task" flow: "Read task→Implement→Write tests→Pass tests→Update [x]→Next task"

View File

@@ -3,6 +3,7 @@
CRITICAL: Read the full YML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode: CRITICAL: Read the full YML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode:
```yml ```yml
root: .bmad-core
activation-instructions: activation-instructions:
- Follow all instructions in this file -> this defines you, your persona and more importantly what you can do. STAY IN CHARACTER! - Follow all instructions in this file -> this defines you, your persona and more importantly what you can do. STAY IN CHARACTER!
- Only read the files/tasks listed here when user selects them for execution to minimize context usage - Only read the files/tasks listed here when user selects them for execution to minimize context usage
@@ -31,11 +32,11 @@ persona:
- Strategic thinking & outcome-oriented - Strategic thinking & outcome-oriented
startup: startup:
- Greet the user with your name and role, and inform of the *help command. - Greet the user with your name and role, and inform of the *help command.
commands: commands: # All commands require * prefix when used (e.g., *help)
- '*help" - Show: numbered list of the following commands to allow selection' - help: Show numbered list of the following commands to allow selection
- '*chat-mode" - (Default) Deep conversation with advanced-elicitation' - chat-mode: (Default) Deep conversation with advanced-elicitation
- '*create-doc {template}" - Create doc (no template = show available templates)' - create-doc {template}: Create doc (no template = show available templates)
- '*exit" - Say goodbye as the PM, and then abandon inhabiting this persona' - exit: Say goodbye as the PM, and then abandon inhabiting this persona
dependencies: dependencies:
tasks: tasks:
- create-doc - create-doc

View File

@@ -3,6 +3,7 @@
CRITICAL: Read the full YML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode: CRITICAL: Read the full YML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode:
```yml ```yml
root: .bmad-core
activation-instructions: activation-instructions:
- Follow all instructions in this file -> this defines you, your persona and more importantly what you can do. STAY IN CHARACTER! - Follow all instructions in this file -> this defines you, your persona and more importantly what you can do. STAY IN CHARACTER!
- Only read the files/tasks listed here when user selects them for execution to minimize context usage - Only read the files/tasks listed here when user selects them for execution to minimize context usage
@@ -33,16 +34,16 @@ persona:
- Documentation Ecosystem Integrity - Maintain consistency across all documents - Documentation Ecosystem Integrity - Maintain consistency across all documents
startup: startup:
- Greet the user with your name and role, and inform of the *help command. - Greet the user with your name and role, and inform of the *help command.
commands: commands: # All commands require * prefix when used (e.g., *help)
- '*help" - Show: numbered list of the following commands to allow selection' - help: Show numbered list of the following commands to allow selection
- '*chat-mode" - (Default) Product Owner consultation with advanced-elicitation' - chat-mode: (Default) Product Owner consultation with advanced-elicitation
- '*create-doc {template}" - Create doc (no template = show available templates)' - create-doc {template}: Create doc (no template = show available templates)
- '*execute-checklist {checklist}" - Run validation checklist (default->po-master-checklist)' - execute-checklist {checklist}: Run validation checklist (default->po-master-checklist)
- '*shard-doc {document}" - Break down document into actionable parts' - shard-doc {document}: Break down document into actionable parts
- '*correct-course" - Analyze and suggest project course corrections' - correct-course: Analyze and suggest project course corrections
- '*create-epic" - Create epic for brownfield projects (task brownfield-create-epic)' - create-epic: Create epic for brownfield projects (task brownfield-create-epic)
- '*create-story" - Create user story from requirements (task brownfield-create-story)' - create-story: Create user story from requirements (task brownfield-create-story)
- '*exit" - Say Goodbye, You are no longer this Agent' - exit: Say goodbye as the Product Owner, and then abandon inhabiting this persona
dependencies: dependencies:
tasks: tasks:
- execute-checklist - execute-checklist

View File

@@ -3,6 +3,7 @@
CRITICAL: Read the full YML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode: CRITICAL: Read the full YML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode:
```yaml ```yaml
root: .bmad-core
activation-instructions: activation-instructions:
- Follow all instructions in this file -> this defines you, your persona and more importantly what you can do. STAY IN CHARACTER! - Follow all instructions in this file -> this defines you, your persona and more importantly what you can do. STAY IN CHARACTER!
- Only read the files/tasks listed here when user selects them for execution to minimize context usage - Only read the files/tasks listed here when user selects them for execution to minimize context usage
@@ -33,11 +34,11 @@ persona:
- Cross-Browser & Cross-Platform Testing - Ensure comprehensive compatibility - Cross-Browser & Cross-Platform Testing - Ensure comprehensive compatibility
startup: startup:
- Greet the user with your name and role, and inform of the *help command. - Greet the user with your name and role, and inform of the *help command.
commands: commands: # All commands require * prefix when used (e.g., *help)
- '*help" - Show: numbered list of the following commands to allow selection' - help: Show numbered list of the following commands to allow selection
- '*chat-mode" - (Default) QA consultation with advanced-elicitation for test strategy' - chat-mode: (Default) QA consultation with advanced-elicitation for test strategy
- '*create-doc {template}" - Create doc (no template = show available templates)' - create-doc {template}: Create doc (no template = show available templates)
- '*exit" - Say goodbye as the QA Test Architect, and then abandon inhabiting this persona' - exit: Say goodbye as the QA Test Architect, and then abandon inhabiting this persona
dependencies: dependencies:
data: data:
- technical-preferences - technical-preferences

View File

@@ -2,16 +2,10 @@
CRITICAL: Read the full YML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode: CRITICAL: Read the full YML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode:
## Task and File Resolution
`Create Next Story`: `.bmad-core/tasks/create-next-story.md`
`story-tmpl`: `.bmad-core/templates/story-tmpl.md`
`story-draft-checklist`: `.bmad-core/checklists/story-draft-checklist.md`
```yaml ```yaml
root: .bmad-core
activation-instructions: activation-instructions:
- Follow all instructions in this file -> this defines you, your persona and more importantly what you can do. STAY IN CHARACTER! - Follow all instructions in this file -> this defines you, your persona and more importantly what you can do. STAY IN CHARACTER!
- Only read the files/tasks listed here when user selects them for execution to minimize context usage
- The customization field ALWAYS takes precedence over any conflicting instructions - The customization field ALWAYS takes precedence over any conflicting instructions
- When listing tasks/templates or presenting options during conversations, always show as numbered options list, allowing the user to type a number to select or execute - When listing tasks/templates or presenting options during conversations, always show as numbered options list, allowing the user to type a number to select or execute
agent: agent:
@@ -27,31 +21,25 @@ persona:
identity: Story creation expert who prepares detailed, actionable stories for AI developers identity: Story creation expert who prepares detailed, actionable stories for AI developers
focus: Creating crystal-clear stories that dumb AI agents can implement without confusion focus: Creating crystal-clear stories that dumb AI agents can implement without confusion
core_principles: core_principles:
- Task Adherence - Rigorously follow `Create Next Story` procedures - Rigorously follow `create-next-story` procedure to generate the detailed user story
- Checklist-Driven Validation - Apply story-draft-checklist meticulously - Will ensure all information comes from the PRD and Architecture to guide the dumb dev agent
- Clarity for Developer Handoff - Stories must be immediately actionable - You are NOT allowed to implement stories or modify code EVER!
- Focus on One Story at a Time - Complete one before starting next
- Numbered Options Protocol - Always use numbered lists for selections
startup: startup:
- Greet the user with your name and role, and inform of the *help command. - Greet the user with your name and role, and inform of the *help command and then HALT to await instruction if not given already.
- CRITICAL: Do NOT automatically execute `Create Next Story` tasks during startup
- CRITICAL: Do NOT create or modify any files during startup
- Offer to help with story preparation but wait for explicit user confirmation - Offer to help with story preparation but wait for explicit user confirmation
- Only execute tasks when user explicitly requests them - Only execute tasks when user explicitly requests them
- 'CRITICAL RULE: You are ONLY allowed to create/modify story files - NEVER implement! If asked to implement, tell user they MUST switch to Dev Agent' commands: # All commands require * prefix when used (e.g., *help)
commands: - help: Show numbered list of the following commands to allow selection
- '*help" - Show: numbered list of the following commands to allow selection' - chat-mode: Conversational mode with advanced-elicitation for advice
- '*chat-mode" - Conversational mode with advanced-elicitation for advice' - create|draft: Execute create-next-story
- '*create" - Execute all steps in `Create Next Story`' - pivot: Execute `correct-course` task
- '*pivot" - Run correct-course task (ensure no story already created first)' - checklist {checklist}: Show numbered list of checklists, execute selection
- '*checklist {checklist}" - Show numbered list of checklists, execute selection' - exit: Say goodbye as the Scrum Master, and then abandon inhabiting this persona
- '*doc-shard {PRD|Architecture|Other}" - Execute shard-doc task'
- '*index-docs" - Update documentation index in /docs/index.md'
- '*exit" - Say goodbye as the Scrum Master, and then abandon inhabiting this persona'
dependencies: dependencies:
tasks: tasks:
- create-next-story - create-next-story
- execute-checklist - execute-checklist
- course-correct
templates: templates:
- story-tmpl - story-tmpl
checklists: checklists:

View File

@@ -3,6 +3,7 @@
CRITICAL: Read the full YML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode: CRITICAL: Read the full YML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode:
```yaml ```yaml
root: .bmad-core
activation-instructions: activation-instructions:
- Follow all instructions in this file -> this defines you, your persona and more importantly what you can do. STAY IN CHARACTER! - Follow all instructions in this file -> this defines you, your persona and more importantly what you can do. STAY IN CHARACTER!
- Only read the files/tasks listed here when user selects them for execution to minimize context usage - Only read the files/tasks listed here when user selects them for execution to minimize context usage
@@ -37,14 +38,14 @@ persona:
startup: startup:
- Greet the user with your name and role, and inform of the *help command. - Greet the user with your name and role, and inform of the *help command.
- Always start by understanding the user's context, goals, and constraints before proposing solutions. - Always start by understanding the user's context, goals, and constraints before proposing solutions.
commands: commands: # All commands require * prefix when used (e.g., *help)
- '*help" - Show: numbered list of the following commands to allow selection' - help: Show numbered list of the following commands to allow selection
- '*chat-mode" - (Default) UX consultation with advanced-elicitation for design decisions' - chat-mode: (Default) UX consultation with advanced-elicitation for design decisions
- '*create-doc {template}" - Create doc (no template = show available templates)' - create-doc {template}: Create doc (no template = show available templates)
- '*generate-ui-prompt" - Create AI frontend generation prompt' - generate-ui-prompt: Create AI frontend generation prompt
- '*research {topic}" - Generate deep research prompt for UX investigation' - research {topic}: Generate deep research prompt for UX investigation
- '*execute-checklist {checklist}" - Run design validation checklist' - execute-checklist {checklist}: Run design validation checklist
- '*exit" - Say goodbye as the UX Expert, and then abandon inhabiting this persona' - exit: Say goodbye as the UX Expert, and then abandon inhabiting this persona
dependencies: dependencies:
tasks: tasks:
- generate-ai-frontend-prompt - generate-ai-frontend-prompt

24
bmad-core/core-config.yml Normal file
View File

@@ -0,0 +1,24 @@
core-project-information:
dev-story-location: docs/stories # alternate could be .ai/stories if preferred for example
prd:
prd-file: docs/prd.md
prdVersion: v4
prdSharded: true
prdShardedLocation: docs/prd
epicFilePattern: epic-{n}*.md
architecture:
architecture-file: docs/architecture.md
architectureVersion: v4
architectureSharded: true
architectureShardedLocation: docs/architecture
# if you have a front-end architecture document, uncomment the following and validate the file path
# front-end-architecture:
# front-end-architecture-file: docs/front-end-architecture.md
# architectureVersion: v4
# architectureSharded: true
# architectureShardedLocation: docs/architecture
customTechnicalDocuments: null # list other documents only if you want the SM to read them when creating stories
devLoadAlwaysFiles:
- docs/architecture/coding-standards.md
- docs/architecture/tech-stack.md
- docs/architecture/project-structure.md

View File

@@ -27,7 +27,7 @@ BMAD-METHOD (Breakthrough Method of Agile AI-driven Development) is a framework
#### Option 1: Web UI #### Option 1: Web UI
**Best for**: ChatGPT, Claude, Gemini users who want to start immediately **Best for**: ChatGPT, Claude, Gemini users who want to start immediately
1. Navigate to `.bmad-core/web-bundles/teams/` 1. Navigate to `dist/teams/`
2. Copy `team-fullstack.txt` content 2. Copy `team-fullstack.txt` content
3. Create new Gemini Gem or CustomGPT 3. Create new Gemini Gem or CustomGPT
4. Upload file with instructions: "Your critical operating instructions are attached, do not break character as directed" 4. Upload file with instructions: "Your critical operating instructions are attached, do not break character as directed"
@@ -200,14 +200,16 @@ The BMAD-Method is built around a modular architecture centered on the `bmad-cor
### Dual Environment Architecture ### Dual Environment Architecture
#### IDE Environment #### IDE Environment
- Users interact directly with agent markdown files - Users interact directly with agent markdown files
- Agents can access all dependencies dynamically - Agents can access all dependencies dynamically
- Supports real-time file operations and project integration - Supports real-time file operations and project integration
- Optimized for development workflow execution - Optimized for development workflow execution
#### Web UI Environment #### Web UI Environment
- Uses pre-built bundles from `bmad-core/web-bundles/`
- Single text files containing all agent dependencies - Uses pre-built bundles from `dist/teams` for stand alone 1 upload files for all agents and their assest with an orchestrating agent
- Single text files containing all agent dependencies are in `dist/agents/` - these are unnecessary unless you want to create a web agent that is only a single agent and not a team
- Created by the web-builder tool for upload to web interfaces - Created by the web-builder tool for upload to web interfaces
- Provides complete context in one package - Provides complete context in one package
@@ -220,6 +222,7 @@ BMAD employs a sophisticated template system with three key components:
3. **Advanced Elicitation** (`tasks/advanced-elicitation.md`): Provides interactive refinement through structured brainstorming 3. **Advanced Elicitation** (`tasks/advanced-elicitation.md`): Provides interactive refinement through structured brainstorming
**Template Features**: **Template Features**:
- **Self-contained**: Templates embed both output structure and processing instructions - **Self-contained**: Templates embed both output structure and processing instructions
- **Variable Substitution**: `{{placeholders}}` for dynamic content - **Variable Substitution**: `{{placeholders}}` for dynamic content
- **AI Processing Directives**: `[[LLM: instructions]]` for AI-only processing - **AI Processing Directives**: `[[LLM: instructions]]` for AI-only processing

View File

@@ -4,45 +4,42 @@
To identify the next logical story based on project progress and epic definitions, and then to prepare a comprehensive, self-contained, and actionable story file using the `Story Template`. This task ensures the story is enriched with all necessary technical context, requirements, and acceptance criteria, making it ready for efficient implementation by a Developer Agent with minimal need for additional research. To identify the next logical story based on project progress and epic definitions, and then to prepare a comprehensive, self-contained, and actionable story file using the `Story Template`. This task ensures the story is enriched with all necessary technical context, requirements, and acceptance criteria, making it ready for efficient implementation by a Developer Agent with minimal need for additional research.
## Inputs for this Task
- Access to the project's documentation repository, specifically:
- `docs/index.md` (hereafter "Index Doc")
- All Epic files - located in one of these locations:
- Primary: `docs/prd/epic-{n}-{description}.md` (e.g., `epic-1-foundation-core-infrastructure.md`)
- Secondary: `docs/epics/epic-{n}-{description}.md`
- User-specified location if not found in above paths
- Existing story files in `docs/stories/`
- Main PRD (hereafter "PRD Doc")
- Main Architecture Document (hereafter "Main Arch Doc")
- Frontend Architecture Document (hereafter "Frontend Arch Doc," if relevant)
- Project Structure Guide (`docs/project-structure.md`)
- Operational Guidelines Document (`docs/operational-guidelines.md`)
- Technology Stack Document (`docs/tech-stack.md`)
- Data Models Document (as referenced in Index Doc)
- API Reference Document (as referenced in Index Doc)
- UI/UX Specifications, Style Guides, Component Guides (if relevant, as referenced in Index Doc)
- The `bmad-core/templates/story-tmpl.md` (hereafter "Story Template")
- The `bmad-core/checklists/story-draft-checklist.md` (hereafter "Story Draft Checklist")
- User confirmation to proceed with story identification and, if needed, to override warnings about incomplete prerequisite stories.
## Task Execution Instructions ## Task Execution Instructions
### 0. Load Core Configuration
[[LLM: CRITICAL - This MUST be your first step]]
- Load `.bmad-core/core-config.yml` from the project root
- If the file does not exist:
- HALT and inform the user: "core-config.yml not found. This file is required for story creation. You can:
1. Copy it from GITHUB BMAD-METHOD/bmad-core/core-config.yml and configure it for your project
2. Run the BMAD installer against your project to upgrade and add the file automatically
Please add and configure core-config.yml before proceeding."
- Extract the following key configurations:
- `dev-story-location`: Where to save story files
- `prd.prdSharded`: Whether PRD is sharded or monolithic
- `prd.prd-file`: Location of monolithic PRD (if not sharded)
- `prd.prdShardedLocation`: Location of sharded epic files
- `prd.epicFilePattern`: Pattern for epic files (e.g., `epic-{n}*.md`)
- `architecture.architectureVersion`: Architecture document version
- `architecture.architectureSharded`: Whether architecture is sharded
- `architecture.architecture-file`: Location of monolithic architecture
- `architecture.architectureShardedLocation`: Location of sharded architecture files
### 1. Identify Next Story for Preparation ### 1. Identify Next Story for Preparation
#### 1.1 Locate Epic Files #### 1.1 Locate Epic Files
- First, determine where epic files are located: - Based on `prdSharded` from config:
- Check `docs/prd/` for files matching pattern `epic-{n}-*.md` - **If `prdSharded: true`**: Look for epic files in `prdShardedLocation` using `epicFilePattern`
- If not found, check `docs/epics/` for files matching pattern `epic-{n}-*.md` - **If `prdSharded: false`**: Load the full PRD from `prd-file` and extract epics from section headings (## Epic N or ### Epic N)
- If still not found, ask user: "Unable to locate epic files. Please specify the path where epic files are stored."
- Note: Epic files follow naming convention `epic-{n}-{description}.md` (e.g., `epic-1-foundation-core-infrastructure.md`)
#### 1.2 Review Existing Stories #### 1.2 Review Existing Stories
- Review `docs/stories/` to find the highest-numbered story file. - Check `dev-story-location` from config (e.g., `docs/stories/`) for existing story files
- If the directory exists and has at least 1 file, find the highest-numbered story file.
- **If a highest story file exists (`{lastEpicNum}.{lastStoryNum}.story.md`):** - **If a highest story file exists (`{lastEpicNum}.{lastStoryNum}.story.md`):**
- Verify its `Status` is 'Done' (or equivalent). - Verify its `Status` is 'Done' (or equivalent).
- If not 'Done', present an alert to the user: - If not 'Done', present an alert to the user:
@@ -60,17 +57,17 @@ To identify the next logical story based on project progress and epic definition
``` ```
- Proceed only if user selects option 3 (Override) or if the last story was 'Done'. - Proceed only if user selects option 3 (Override) or if the last story was 'Done'.
- If proceeding: Look for the Epic File for `{lastEpicNum}` (e.g., `epic-{lastEpicNum}-*.md`) and check for a story numbered `{lastStoryNum + 1}`. If it exists and its prerequisites (per Epic File) are met, this is the next story. - If proceeding: Look for the Epic File for `{lastEpicNum}` (e.g., `epic-{lastEpicNum}*.md`) and check for a story numbered `{lastStoryNum + 1}`. If it exists and its prerequisites (per Epic File) are met, this is the next story.
- Else (story not found or prerequisites not met): The next story is the first story in the next Epic File (e.g., look for `epic-{lastEpicNum + 1}-*.md`, then `epic-{lastEpicNum + 2}-*.md`, etc.) whose prerequisites are met. - Else (story not found or prerequisites not met): The next story is the first story in the next Epic File (e.g., look for `epic-{lastEpicNum + 1}*.md`, then `epic-{lastEpicNum + 2}*.md`, etc.) whose prerequisites are met.
- **If no story files exist in `docs/stories/`:** - **If no story files exist in `docs/stories/`:**
- The next story is the first story in the first epic file (look for `epic-1-*.md`, then `epic-2-*.md`, etc.) whose prerequisites are met. - The next story is the first story in the first epic file (look for `epic-1-*.md`, then `epic-2-*.md`, etc.) whose prerequisites are met.
- If no suitable story with met prerequisites is found, report to the user that story creation is blocked, specifying what prerequisites are pending. HALT task. - If no suitable story with met prerequisites is found, report to the user that story creation is blocked, specifying what prerequisites are pending. HALT task.
- Announce the identified story to the user: "Identified next story for preparation: {epicNum}.{storyNum} - {Story Title}". - Announce the identified story to the user: "Identified next story for preparation: {epicNum}.{storyNum} - {Story Title}".
### 2. Gather Core Story Requirements (from Epic File) ### 2. Gather Core Story Requirements (from Epic)
- For the identified story, open its parent Epic File (e.g., `epic-{epicNum}-*.md` from the location identified in step 1.1). - For the identified story, review its parent Epic (e.g., `epic-{epicNum}*.md` from the location identified in step 1.1).
- Extract: Exact Title, full Goal/User Story statement, initial list of Requirements, all Acceptance Criteria (ACs), and any predefined high-level Tasks. - Extract: Exact Title, full Goal/User Story statement, initial list of Requirements, all Acceptance Criteria (ACs), and any predefined high-level Tasks.
- Keep a record of this original epic-defined scope for later deviation analysis. - Keep a record of this original epic-defined scope for later deviation analysis.
@@ -79,7 +76,7 @@ To identify the next logical story based on project progress and epic definition
[[LLM: This step is CRITICAL for continuity and learning from implementation experience]] [[LLM: This step is CRITICAL for continuity and learning from implementation experience]]
- If this is not the first story (i.e., previous story exists): - If this is not the first story (i.e., previous story exists):
- Read the previous story file: `docs/stories/{prevEpicNum}.{prevStoryNum}.story.md` - Read the previous sequential story from `docs/stories`
- Pay special attention to: - Pay special attention to:
- Dev Agent Record sections (especially Completion Notes and Debug Log References) - Dev Agent Record sections (especially Completion Notes and Debug Log References)
- Any deviations from planned implementation - Any deviations from planned implementation
@@ -88,18 +85,30 @@ To identify the next logical story based on project progress and epic definition
- Any "lessons learned" or notes for future stories - Any "lessons learned" or notes for future stories
- Extract relevant insights that might inform the current story's preparation - Extract relevant insights that might inform the current story's preparation
### 4. Gather & Synthesize Architecture Context from Sharded Docs ### 4. Gather & Synthesize Architecture Context
[[LLM: CRITICAL - You MUST gather technical details from the sharded architecture documents. NEVER make up technical details not found in these documents.]] [[LLM: CRITICAL - You MUST gather technical details from the architecture documents. NEVER make up technical details not found in these documents.]]
#### 4.1 Start with Architecture Index #### 4.1 Determine Architecture Document Strategy
- Read `docs/architecture/index.md` to understand the full scope of available documentation Based on configuration loaded in Step 0:
- Identify which sharded documents are most relevant to the current story
#### 4.2 Recommended Reading Order Based on Story Type - **If `architectureVersion: v4` and `architectureSharded: true`**:
- Read `{architectureShardedLocation}/index.md` to understand available documentation
- Follow the structured reading order in section 4.2 below
[[LLM: Read documents in this order, but ALWAYS verify relevance to the specific story. Skip irrelevant sections but NEVER skip documents that contain information needed for the story.]] - **If `architectureVersion: v4` and `architectureSharded: false`**:
- Load the monolithic architecture from `architecture-file`
- Extract relevant sections based on v4 structure (tech stack, project structure, etc.)
- **If `architectureVersion` is NOT v4**:
- Inform user: "Architecture document is not v4 format. Will use best judgment to find relevant information."
- If `architectureSharded: true`: Search sharded files by filename relevance
- If `architectureSharded: false`: Search within monolithic `architecture-file` for relevant sections
#### 4.2 Recommended Reading Order Based on Story Type (v4 Sharded Only)
[[LLM: Use this structured approach ONLY for v4 sharded architecture. For other versions, use best judgment based on file names and content.]]
**For ALL Stories:** **For ALL Stories:**
@@ -108,9 +117,18 @@ To identify the next logical story based on project progress and epic definition
3. `docs/architecture/coding-standards.md` - Ensure dev follows project conventions 3. `docs/architecture/coding-standards.md` - Ensure dev follows project conventions
4. `docs/architecture/testing-strategy.md` - Include testing requirements in tasks 4. `docs/architecture/testing-strategy.md` - Include testing requirements in tasks
**For Backend/API Stories, additionally read:** 5. `docs/architecture/data-models.md` - Data structures and validation rules 6. `docs/architecture/database-schema.md` - Database design and relationships 7. `docs/architecture/backend-architecture.md` - Service patterns and structure 8. `docs/architecture/rest-api-spec.md` - API endpoint specifications 9. `docs/architecture/external-apis.md` - Third-party integrations (if relevant) **For Backend/API Stories, additionally read:**
5. `docs/architecture/data-models.md` - Data structures and validation rules
6. `docs/architecture/database-schema.md` - Database design and relationships
7. `docs/architecture/backend-architecture.md` - Service patterns and structure
8. `docs/architecture/rest-api-spec.md` - API endpoint specifications
9. `docs/architecture/external-apis.md` - Third-party integrations (if relevant)
**For Frontend/UI Stories, additionally read:** 5. `docs/architecture/frontend-architecture.md` - Component structure and patterns 6. `docs/architecture/components.md` - Specific component designs 7. `docs/architecture/core-workflows.md` - User interaction flows 8. `docs/architecture/data-models.md` - Frontend data handling **For Frontend/UI Stories, additionally read:**
5. `docs/architecture/frontend-architecture.md` - Component structure and patterns
6. `docs/architecture/components.md` - Specific component designs
7. `docs/architecture/core-workflows.md` - User interaction flows
8. `docs/architecture/data-models.md` - Frontend data handling
**For Full-Stack Stories:** **For Full-Stack Stories:**
@@ -143,7 +161,7 @@ Format references as: `[Source: architecture/{filename}.md#{section}]`
### 6. Populate Story Template with Full Context ### 6. Populate Story Template with Full Context
- Create a new story file: `docs/stories/{epicNum}.{storyNum}.story.md`. - Create a new story file: `{dev-story-location}/{epicNum}.{storyNum}.story.md` (using location from config).
- Use the Story Template to structure the file. - Use the Story Template to structure the file.
- Fill in: - Fill in:
- Story `{EpicNum}.{StoryNum}: {Short Title Copied from Epic File}` - Story `{EpicNum}.{StoryNum}: {Short Title Copied from Epic File}`
@@ -190,7 +208,7 @@ Format references as: `[Source: architecture/{filename}.md#{section}]`
- Verify all source references are included for technical details - Verify all source references are included for technical details
- Ensure tasks align with both epic requirements and architecture constraints - Ensure tasks align with both epic requirements and architecture constraints
- Update status to "Draft" - Update status to "Draft"
- Save the story file to `docs/stories/{epicNum}.{storyNum}.story.md` - Save the story file to `{dev-story-location}/{epicNum}.{storyNum}.story.md` (using location from config)
### 9. Report Completion ### 9. Report Completion

View File

@@ -37,7 +37,7 @@ Use Google's Gemini for collaborative planning with the full team:
2. **Create a new Gem**: 2. **Create a new Gem**:
- Give it a title and description (e.g., "BMAD Team Fullstack") - Give it a title and description (e.g., "BMAD Team Fullstack")
3. **Load team-fullstack**: 3. **Load team-fullstack**:
- Copy contents of: `.bmad-core/web-bundles/teams/team-fullstack.txt` from your project - Copy contents of: `dist/teams/team-fullstack.txt` from your project
- Paste this content into the Gem setup to configure the team - Paste this content into the Gem setup to configure the team
4. **Collaborate with the team**: 4. **Collaborate with the team**:
- Business Analyst: Requirements gathering - Business Analyst: Requirements gathering

View File

@@ -23,7 +23,7 @@ For ideation and planning, use Google's Gemini Custom Gem with the team-fullstac
1. Open [Google gems](https://gemini.google.com/gems/view) 1. Open [Google gems](https://gemini.google.com/gems/view)
2. Create a new Gem - give it a title and description 2. Create a new Gem - give it a title and description
3. Copy the contents of `.<install location>/web-bundles/teams/team-fullstack.txt` 3. Copy the contents of `.<install location>/<web-bundles>/teams/team-fullstack.txt` (location can vary if you chose a non default installation location for the bundles) - or just use the bundle premade from the repo dist folder.
4. Paste this content into Gemini to set up the team 4. Paste this content into Gemini to set up the team
### Gemini Planning Phase ### Gemini Planning Phase

View File

@@ -16,7 +16,7 @@ The system facilitates a full development lifecycle:
The entire BMAD-Method ecosystem is designed around the `.bmad-core` directory, which acts as the brain of the operation. The `tools` directory provides the means to process and package this brain for different environments. The entire BMAD-Method ecosystem is designed around the `.bmad-core` directory, which acts as the brain of the operation. The `tools` directory provides the means to process and package this brain for different environments.
```mermaid ````mermaid
graph TD graph TD
subgraph BMAD Method Project subgraph BMAD Method Project
subgraph Core Framework subgraph Core Framework
@@ -35,7 +35,7 @@ graph TD
end end
subgraph Outputs subgraph Outputs
J[".bmad-core/web-bundles"] J["dist"]
end end
B -- defines dependencies for --> E B -- defines dependencies for --> E
@@ -133,17 +133,17 @@ The framework is designed for two primary environments: local IDEs and web-based
### 4.1. Web Builder (`tools/builders/web-builder.js`) ### 4.1. Web Builder (`tools/builders/web-builder.js`)
- **Purpose**: This Node.js script is responsible for creating the `.txt` bundles found in `.bmad-core/web-bundles/`. - **Purpose**: This Node.js script is responsible for creating the `.txt` bundles found in `dist`.
- **Process**: - **Process**:
1. **Resolves Dependencies**: For a given agent or team, the script reads its definition file. 1. **Resolves Dependencies**: For a given agent or team, the script reads its definition file.
2. It recursively finds all dependent resources (tasks, templates, etc.) that the agent/team needs. 2. It recursively finds all dependent resources (tasks, templates, etc.) that the agent/team needs.
3. **Bundles Content**: It reads the content of all these files and concatenates them into a single, large text file, with clear separators indicating the original file path of each section. 3. **Bundles Content**: It reads the content of all these files and concatenates them into a single, large text file, with clear separators indicating the original file path of each section.
4. **Outputs Bundle**: The final `.txt` file is saved in the `web-bundles` directory, ready to be uploaded to a web UI. 4. **Outputs Bundle**: The final `.txt` file is saved in the `dist` directory, ready to be uploaded to a web UI.
### 4.2. Environment-Specific Usage ### 4.2. Environment-Specific Usage
- **For IDEs**: Users interact with the agents directly via their markdown files in `.bmad-core/agents/`. The IDE integration (for Cursor, Claude Code, etc.) knows how to call these agents. - **For IDEs**: Users interact with the agents directly via their markdown files in `.bmad-core/agents/`. The IDE integration (for Cursor, Claude Code, etc.) knows how to call these agents.
- **For Web UIs**: Users upload a pre-built bundle from `.bmad-core/web-bundles/`. This single file provides the AI with the context of the entire team and all their required tools and knowledge. - **For Web UIs**: Users upload a pre-built bundle from `dist`. This single file provides the AI with the context of the entire team and all their required tools and knowledge.
## 5. BMAD Workflows ## 5. BMAD Workflows
@@ -173,7 +173,7 @@ graph TD
style G fill:#f9ab00,color:#fff style G fill:#f9ab00,color:#fff
style L fill:#1a73e8,color:#fff style L fill:#1a73e8,color:#fff
style N fill:#34a853,color:#fff style N fill:#34a853,color:#fff
``` ````
**Key Planning Phases:** **Key Planning Phases:**

View File

@@ -23,7 +23,7 @@ For ideation and planning, use Google's Gemini Custom Gem with the team-fullstac
1. Open [Google gems](https://gemini.google.com/gems/view) 1. Open [Google gems](https://gemini.google.com/gems/view)
2. Create a new Gem - give it a title and description 2. Create a new Gem - give it a title and description
3. Copy the contents of `.<install location>/web-bundles/teams/team-fullstack.txt` 3. Copy the contents of `.<install location>/<web-bundles>/teams/team-fullstack.txt` (location can vary if you chose a non default installation location for the bundles) - or just use the bundle premade from the repo dist folder.
4. Paste this content into Gemini to set up the team 4. Paste this content into Gemini to set up the team
### Gemini Planning Phase ### Gemini Planning Phase

View File

@@ -23,7 +23,7 @@ For ideation and planning, use Google's Gemini Custom Gem with the team-fullstac
1. Open [Google gems](https://gemini.google.com/gems/view) 1. Open [Google gems](https://gemini.google.com/gems/view)
2. Create a new Gem - give it a title and description 2. Create a new Gem - give it a title and description
3. Copy the contents of `.<install location>/web-bundles/teams/team-fullstack.txt` 3. Copy the contents of `.<install location>/<web-bundles>/teams/team-fullstack.txt` (location can vary if you chose a non default installation location for the bundles) - or just use the bundle premade from the repo dist folder.
4. Paste this content into Gemini to set up the team 4. Paste this content into Gemini to set up the team
### Gemini Planning Phase ### Gemini Planning Phase

View File

@@ -46,7 +46,7 @@ BMAD-METHOD (Breakthrough Method of Agile AI-Driven Development) is an AI agent
Best for: ChatGPT, Claude, Gemini users Best for: ChatGPT, Claude, Gemini users
1. Navigate to `.bmad-core/web-bundles/teams/` 1. Navigate to `dist/teams/`
2. Copy `team-fullstack.txt` content 2. Copy `team-fullstack.txt` content
3. Create new Gemini Gem or CustomGPT 3. Create new Gemini Gem or CustomGPT
4. Upload file with instructions: "Your critical operating instructions are attached, do not break character as directed" 4. Upload file with instructions: "Your critical operating instructions are attached, do not break character as directed"

View File

@@ -23,7 +23,7 @@ For ideation and planning, use Google's Gemini Custom Gem with the team-fullstac
1. Open [Google gems](https://gemini.google.com/gems/view) 1. Open [Google gems](https://gemini.google.com/gems/view)
2. Create a new Gem - give it a title and description 2. Create a new Gem - give it a title and description
3. Copy the contents of `.<install location>/web-bundles/teams/team-fullstack.txt` 3. Copy the contents of `.<install location>/<web-bundles>/teams/team-fullstack.txt` (location can vary if you chose a non default installation location for the bundles) - or just use the bundle premade from the repo dist folder.
4. Paste this content into Gemini to set up the team 4. Paste this content into Gemini to set up the team
### Gemini Planning Phase ### Gemini Planning Phase

View File

@@ -10,12 +10,13 @@
CRITICAL: Read the full YML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode: CRITICAL: Read the full YML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode:
```yml ````yml
activation-instructions: activation-instructions:
- Follow all instructions in this file -> this defines you, your persona and more importantly what you can do. STAY IN CHARACTER! - Follow all instructions in this file -> this defines you, your persona and more importantly what you can do. STAY IN CHARACTER!
- Only read the files/tasks listed here when user selects them for execution to minimize context usage - Only read the files/tasks listed here when user selects them for execution to minimize context usage
- The customization field ALWAYS takes precedence over any conflicting instructions - The customization field ALWAYS takes precedence over any conflicting instructions
- When listing tasks/templates or presenting options during conversations, always show as numbered options list, allowing the user to type a number to select or execute - When listing tasks/templates or presenting options during conversations, always show as numbered options list, allowing the user to type a number to select or execute
- Command
agent: agent:
name: [AGENT_NAME] name: [AGENT_NAME]
@@ -36,7 +37,9 @@ persona:
# Add more principles as needed # Add more principles as needed
startup: startup:
- [STARTUP_INSTRUCTIONS] - Greet the user with your name and role, and inform of the *help command.
- [STARTUP_INSTRUCTION]
- [STARTUP_INSTRUCTION]...
commands: commands:
- "*help" - Show: numbered list of the following commands to allow selection - "*help" - Show: numbered list of the following commands to allow selection
@@ -137,4 +140,4 @@ dependencies:
- 'materials-guide.md # Construction materials specs' - 'materials-guide.md # Construction materials specs'
utils: utils:
- 'template-format # For template processing' - 'template-format # For template processing'
``` ````

View File

@@ -1,19 +1,22 @@
const fs = require('node:fs').promises; const fs = require("node:fs").promises;
const path = require('node:path'); const path = require("node:path");
const DependencyResolver = require('../lib/dependency-resolver'); const DependencyResolver = require("../lib/dependency-resolver");
class WebBuilder { class WebBuilder {
constructor(options = {}) { constructor(options = {}) {
this.rootDir = options.rootDir || process.cwd(); this.rootDir = options.rootDir || process.cwd();
this.outputDirs = options.outputDirs || [ this.outputDirs = options.outputDirs || [path.join(this.rootDir, "dist")];
path.join(this.rootDir, 'dist')
];
this.resolver = new DependencyResolver(this.rootDir); this.resolver = new DependencyResolver(this.rootDir);
this.templatePath = path.join(this.rootDir, 'bmad-core', 'templates', 'web-agent-startup-instructions-template.md'); this.templatePath = path.join(
this.rootDir,
"bmad-core",
"utils",
"web-agent-startup-instructions.md"
);
} }
parseYaml(content) { parseYaml(content) {
const yaml = require('js-yaml'); const yaml = require("js-yaml");
return yaml.load(content); return yaml.load(content);
} }
@@ -38,10 +41,10 @@ class WebBuilder {
// Write to all output directories // Write to all output directories
for (const outputDir of this.outputDirs) { for (const outputDir of this.outputDirs) {
const outputPath = path.join(outputDir, 'agents'); const outputPath = path.join(outputDir, "agents");
await fs.mkdir(outputPath, { recursive: true }); await fs.mkdir(outputPath, { recursive: true });
const outputFile = path.join(outputPath, `${agentId}.txt`); const outputFile = path.join(outputPath, `${agentId}.txt`);
await fs.writeFile(outputFile, bundle, 'utf8'); await fs.writeFile(outputFile, bundle, "utf8");
} }
} }
@@ -57,10 +60,10 @@ class WebBuilder {
// Write to all output directories // Write to all output directories
for (const outputDir of this.outputDirs) { for (const outputDir of this.outputDirs) {
const outputPath = path.join(outputDir, 'teams'); const outputPath = path.join(outputDir, "teams");
await fs.mkdir(outputPath, { recursive: true }); await fs.mkdir(outputPath, { recursive: true });
const outputFile = path.join(outputPath, `${teamId}.txt`); const outputFile = path.join(outputPath, `${teamId}.txt`);
await fs.writeFile(outputFile, bundle, 'utf8'); await fs.writeFile(outputFile, bundle, "utf8");
} }
} }
@@ -69,7 +72,7 @@ class WebBuilder {
async buildAgentBundle(agentId) { async buildAgentBundle(agentId) {
const dependencies = await this.resolver.resolveAgentDependencies(agentId); const dependencies = await this.resolver.resolveAgentDependencies(agentId);
const template = await fs.readFile(this.templatePath, 'utf8'); const template = await fs.readFile(this.templatePath, "utf8");
const sections = [template]; const sections = [template];
@@ -81,12 +84,12 @@ class WebBuilder {
sections.push(this.formatSection(resource.path, resource.content)); sections.push(this.formatSection(resource.path, resource.content));
} }
return sections.join('\n'); return sections.join("\n");
} }
async buildTeamBundle(teamId) { async buildTeamBundle(teamId) {
const dependencies = await this.resolver.resolveTeamDependencies(teamId); const dependencies = await this.resolver.resolveTeamDependencies(teamId);
const template = await fs.readFile(this.templatePath, 'utf8'); const template = await fs.readFile(this.templatePath, "utf8");
const sections = [template]; const sections = [template];
@@ -103,21 +106,21 @@ class WebBuilder {
sections.push(this.formatSection(resource.path, resource.content)); sections.push(this.formatSection(resource.path, resource.content));
} }
return sections.join('\n'); return sections.join("\n");
} }
formatSection(path, content) { formatSection(path, content) {
const separator = '===================='; const separator = "====================";
return [ return [
`${separator} START: ${path} ${separator}`, `${separator} START: ${path} ${separator}`,
content.trim(), content.trim(),
`${separator} END: ${path} ${separator}`, `${separator} END: ${path} ${separator}`,
'' "",
].join('\n'); ].join("\n");
} }
async validate() { async validate() {
console.log('Validating agent configurations...'); console.log("Validating agent configurations...");
const agents = await this.resolver.listAgents(); const agents = await this.resolver.listAgents();
for (const agentId of agents) { for (const agentId of agents) {
try { try {
@@ -129,7 +132,7 @@ class WebBuilder {
} }
} }
console.log('\nValidating team configurations...'); console.log("\nValidating team configurations...");
const teams = await this.resolver.listTeams(); const teams = await this.resolver.listTeams();
for (const teamId of teams) { for (const teamId of teams) {
try { try {
@@ -154,10 +157,8 @@ class WebBuilder {
} }
async buildExpansionPack(packName, options = {}) { async buildExpansionPack(packName, options = {}) {
const packDir = path.join(this.rootDir, 'expansion-packs', packName); const packDir = path.join(this.rootDir, "expansion-packs", packName);
const outputDirs = [ const outputDirs = [path.join(this.rootDir, "dist", "expansion-packs", packName)];
path.join(this.rootDir, 'dist', 'expansion-packs', packName)
];
// Clean output directories if requested // Clean output directories if requested
if (options.clean !== false) { if (options.clean !== false) {
@@ -171,16 +172,16 @@ class WebBuilder {
} }
// Build individual agents first // Build individual agents first
const agentsDir = path.join(packDir, 'agents'); const agentsDir = path.join(packDir, "agents");
try { try {
const agentFiles = await fs.readdir(agentsDir); const agentFiles = await fs.readdir(agentsDir);
const agentMarkdownFiles = agentFiles.filter(f => f.endsWith('.md')); const agentMarkdownFiles = agentFiles.filter((f) => f.endsWith(".md"));
if (agentMarkdownFiles.length > 0) { if (agentMarkdownFiles.length > 0) {
console.log(` Building individual agents for ${packName}:`); console.log(` Building individual agents for ${packName}:`);
for (const agentFile of agentMarkdownFiles) { for (const agentFile of agentMarkdownFiles) {
const agentName = agentFile.replace('.md', ''); const agentName = agentFile.replace(".md", "");
console.log(` - ${agentName}`); console.log(` - ${agentName}`);
// Build individual agent bundle // Build individual agent bundle
@@ -188,10 +189,10 @@ class WebBuilder {
// Write to all output directories // Write to all output directories
for (const outputDir of outputDirs) { for (const outputDir of outputDirs) {
const agentsOutputDir = path.join(outputDir, 'agents'); const agentsOutputDir = path.join(outputDir, "agents");
await fs.mkdir(agentsOutputDir, { recursive: true }); await fs.mkdir(agentsOutputDir, { recursive: true });
const outputFile = path.join(agentsOutputDir, `${agentName}.txt`); const outputFile = path.join(agentsOutputDir, `${agentName}.txt`);
await fs.writeFile(outputFile, bundle, 'utf8'); await fs.writeFile(outputFile, bundle, "utf8");
} }
} }
} }
@@ -200,10 +201,10 @@ class WebBuilder {
} }
// Build team bundle // Build team bundle
const agentTeamsDir = path.join(packDir, 'agent-teams'); const agentTeamsDir = path.join(packDir, "agent-teams");
try { try {
const teamFiles = await fs.readdir(agentTeamsDir); const teamFiles = await fs.readdir(agentTeamsDir);
const teamFile = teamFiles.find(f => f.endsWith('.yml')); const teamFile = teamFiles.find((f) => f.endsWith(".yml"));
if (teamFile) { if (teamFile) {
console.log(` Building team bundle for ${packName}`); console.log(` Building team bundle for ${packName}`);
@@ -214,10 +215,10 @@ class WebBuilder {
// Write to all output directories // Write to all output directories
for (const outputDir of outputDirs) { for (const outputDir of outputDirs) {
const teamsOutputDir = path.join(outputDir, 'teams'); const teamsOutputDir = path.join(outputDir, "teams");
await fs.mkdir(teamsOutputDir, { recursive: true }); await fs.mkdir(teamsOutputDir, { recursive: true });
const outputFile = path.join(teamsOutputDir, teamFile.replace('.yml', '.txt')); const outputFile = path.join(teamsOutputDir, teamFile.replace(".yml", ".txt"));
await fs.writeFile(outputFile, bundle, 'utf8'); await fs.writeFile(outputFile, bundle, "utf8");
console.log(` ✓ Created bundle: ${path.relative(this.rootDir, outputFile)}`); console.log(` ✓ Created bundle: ${path.relative(this.rootDir, outputFile)}`);
} }
} else { } else {
@@ -229,19 +230,19 @@ class WebBuilder {
} }
async buildExpansionAgentBundle(packName, packDir, agentName) { async buildExpansionAgentBundle(packName, packDir, agentName) {
const template = await fs.readFile(this.templatePath, 'utf8'); const template = await fs.readFile(this.templatePath, "utf8");
const sections = [template]; const sections = [template];
// Add agent configuration // Add agent configuration
const agentPath = path.join(packDir, 'agents', `${agentName}.md`); const agentPath = path.join(packDir, "agents", `${agentName}.md`);
const agentContent = await fs.readFile(agentPath, 'utf8'); const agentContent = await fs.readFile(agentPath, "utf8");
sections.push(this.formatSection(`agents#${agentName}`, agentContent)); sections.push(this.formatSection(`agents#${agentName}`, agentContent));
// Resolve and add agent dependencies // Resolve and add agent dependencies
const agentYaml = agentContent.match(/```yaml\n([\s\S]*?)\n```/); const agentYaml = agentContent.match(/```yaml\n([\s\S]*?)\n```/);
if (agentYaml) { if (agentYaml) {
try { try {
const yaml = require('js-yaml'); const yaml = require("js-yaml");
const agentConfig = yaml.load(agentYaml[1]); const agentConfig = yaml.load(agentYaml[1]);
if (agentConfig.dependencies) { if (agentConfig.dependencies) {
@@ -250,14 +251,16 @@ class WebBuilder {
if (Array.isArray(resources)) { if (Array.isArray(resources)) {
for (const resourceName of resources) { for (const resourceName of resources) {
let found = false; let found = false;
const extensions = ['.md', '.yml', '.yaml']; const extensions = [".md", ".yml", ".yaml"];
// Try expansion pack first // Try expansion pack first
for (const ext of extensions) { for (const ext of extensions) {
const resourcePath = path.join(packDir, resourceType, `${resourceName}${ext}`); const resourcePath = path.join(packDir, resourceType, `${resourceName}${ext}`);
try { try {
const resourceContent = await fs.readFile(resourcePath, 'utf8'); const resourceContent = await fs.readFile(resourcePath, "utf8");
sections.push(this.formatSection(`${resourceType}#${resourceName}`, resourceContent)); sections.push(
this.formatSection(`${resourceType}#${resourceName}`, resourceContent)
);
found = true; found = true;
break; break;
} catch (error) { } catch (error) {
@@ -268,10 +271,17 @@ class WebBuilder {
// If not found in expansion pack, try core // If not found in expansion pack, try core
if (!found) { if (!found) {
for (const ext of extensions) { for (const ext of extensions) {
const corePath = path.join(this.rootDir, 'bmad-core', resourceType, `${resourceName}${ext}`); const corePath = path.join(
this.rootDir,
"bmad-core",
resourceType,
`${resourceName}${ext}`
);
try { try {
const coreContent = await fs.readFile(corePath, 'utf8'); const coreContent = await fs.readFile(corePath, "utf8");
sections.push(this.formatSection(`${resourceType}#${resourceName}`, coreContent)); sections.push(
this.formatSection(`${resourceType}#${resourceName}`, coreContent)
);
found = true; found = true;
break; break;
} catch (error) { } catch (error) {
@@ -281,7 +291,9 @@ class WebBuilder {
} }
if (!found) { if (!found) {
console.warn(` ⚠ Dependency ${resourceType}#${resourceName} not found in expansion pack or core`); console.warn(
` ⚠ Dependency ${resourceType}#${resourceName} not found in expansion pack or core`
);
} }
} }
} }
@@ -292,27 +304,27 @@ class WebBuilder {
} }
} }
return sections.join('\n'); return sections.join("\n");
} }
async buildExpansionTeamBundle(packName, packDir, teamConfigPath) { async buildExpansionTeamBundle(packName, packDir, teamConfigPath) {
const template = await fs.readFile(this.templatePath, 'utf8'); const template = await fs.readFile(this.templatePath, "utf8");
const sections = [template]; const sections = [template];
// Add team configuration and parse to get agent list // Add team configuration and parse to get agent list
const teamContent = await fs.readFile(teamConfigPath, 'utf8'); const teamContent = await fs.readFile(teamConfigPath, "utf8");
const teamFileName = path.basename(teamConfigPath, '.yml'); const teamFileName = path.basename(teamConfigPath, ".yml");
const teamConfig = this.parseYaml(teamContent); const teamConfig = this.parseYaml(teamContent);
sections.push(this.formatSection(`agent-teams#${teamFileName}`, teamContent)); sections.push(this.formatSection(`agent-teams#${teamFileName}`, teamContent));
// Get list of expansion pack agents // Get list of expansion pack agents
const expansionAgents = new Set(); const expansionAgents = new Set();
const agentsDir = path.join(packDir, 'agents'); const agentsDir = path.join(packDir, "agents");
try { try {
const agentFiles = await fs.readdir(agentsDir); const agentFiles = await fs.readdir(agentsDir);
for (const agentFile of agentFiles.filter(f => f.endsWith('.md'))) { for (const agentFile of agentFiles.filter((f) => f.endsWith(".md"))) {
const agentName = agentFile.replace('.md', ''); const agentName = agentFile.replace(".md", "");
expansionAgents.add(agentName); expansionAgents.add(agentName);
} }
} catch (error) { } catch (error) {
@@ -321,13 +333,15 @@ class WebBuilder {
// Build a map of all available expansion pack resources for override checking // Build a map of all available expansion pack resources for override checking
const expansionResources = new Map(); const expansionResources = new Map();
const resourceDirs = ['templates', 'tasks', 'checklists', 'workflows', 'data']; const resourceDirs = ["templates", "tasks", "checklists", "workflows", "data"];
for (const resourceDir of resourceDirs) { for (const resourceDir of resourceDirs) {
const resourcePath = path.join(packDir, resourceDir); const resourcePath = path.join(packDir, resourceDir);
try { try {
const resourceFiles = await fs.readdir(resourcePath); const resourceFiles = await fs.readdir(resourcePath);
for (const resourceFile of resourceFiles.filter(f => f.endsWith('.md') || f.endsWith('.yml'))) { for (const resourceFile of resourceFiles.filter(
const fileName = resourceFile.replace(/\.(md|yml)$/, ''); (f) => f.endsWith(".md") || f.endsWith(".yml")
)) {
const fileName = resourceFile.replace(/\.(md|yml)$/, "");
expansionResources.set(`${resourceDir}#${fileName}`, true); expansionResources.set(`${resourceDir}#${fileName}`, true);
} }
} catch (error) { } catch (error) {
@@ -339,20 +353,19 @@ class WebBuilder {
const agentsToProcess = teamConfig.agents || []; const agentsToProcess = teamConfig.agents || [];
// Ensure bmad-orchestrator is always included for teams // Ensure bmad-orchestrator is always included for teams
if (!agentsToProcess.includes('bmad-orchestrator')) { if (!agentsToProcess.includes("bmad-orchestrator")) {
console.warn(` ⚠ Team ${teamFileName} missing bmad-orchestrator, adding automatically`); console.warn(` ⚠ Team ${teamFileName} missing bmad-orchestrator, adding automatically`);
agentsToProcess.unshift('bmad-orchestrator'); agentsToProcess.unshift("bmad-orchestrator");
} }
// Track all dependencies from all agents (deduplicated) // Track all dependencies from all agents (deduplicated)
const allDependencies = new Map(); const allDependencies = new Map();
for (const agentId of agentsToProcess) { for (const agentId of agentsToProcess) {
if (expansionAgents.has(agentId)) { if (expansionAgents.has(agentId)) {
// Use expansion pack version (override) // Use expansion pack version (override)
const agentPath = path.join(agentsDir, `${agentId}.md`); const agentPath = path.join(agentsDir, `${agentId}.md`);
const agentContent = await fs.readFile(agentPath, 'utf8'); const agentContent = await fs.readFile(agentPath, "utf8");
sections.push(this.formatSection(`agents#${agentId}`, agentContent)); sections.push(this.formatSection(`agents#${agentId}`, agentContent));
// Parse and collect dependencies from expansion agent // Parse and collect dependencies from expansion agent
@@ -379,8 +392,8 @@ class WebBuilder {
} else { } else {
// Use core BMAD version // Use core BMAD version
try { try {
const coreAgentPath = path.join(this.rootDir, 'bmad-core', 'agents', `${agentId}.md`); const coreAgentPath = path.join(this.rootDir, "bmad-core", "agents", `${agentId}.md`);
const coreAgentContent = await fs.readFile(coreAgentPath, 'utf8'); const coreAgentContent = await fs.readFile(coreAgentPath, "utf8");
sections.push(this.formatSection(`agents#${agentId}`, coreAgentContent)); sections.push(this.formatSection(`agents#${agentId}`, coreAgentContent));
// Parse and collect dependencies from core agent // Parse and collect dependencies from core agent
@@ -389,7 +402,7 @@ class WebBuilder {
try { try {
// Clean up the YAML to handle command descriptions after dashes // Clean up the YAML to handle command descriptions after dashes
let yamlContent = agentYaml[1]; let yamlContent = agentYaml[1];
yamlContent = yamlContent.replace(/^(\s*-)(\s*"[^"]+")(\s*-\s*.*)$/gm, '$1$2'); yamlContent = yamlContent.replace(/^(\s*-)(\s*"[^"]+")(\s*-\s*.*)$/gm, "$1$2");
const agentConfig = this.parseYaml(yamlContent); const agentConfig = this.parseYaml(yamlContent);
if (agentConfig.dependencies) { if (agentConfig.dependencies) {
@@ -418,7 +431,7 @@ class WebBuilder {
// Always prefer expansion pack versions if they exist // Always prefer expansion pack versions if they exist
for (const [key, dep] of allDependencies) { for (const [key, dep] of allDependencies) {
let found = false; let found = false;
const extensions = ['.md', '.yml', '.yaml']; const extensions = [".md", ".yml", ".yaml"];
// Always check expansion pack first, even if the dependency came from a core agent // Always check expansion pack first, even if the dependency came from a core agent
if (expansionResources.has(key)) { if (expansionResources.has(key)) {
@@ -426,7 +439,7 @@ class WebBuilder {
for (const ext of extensions) { for (const ext of extensions) {
const expansionPath = path.join(packDir, dep.type, `${dep.name}${ext}`); const expansionPath = path.join(packDir, dep.type, `${dep.name}${ext}`);
try { try {
const content = await fs.readFile(expansionPath, 'utf8'); const content = await fs.readFile(expansionPath, "utf8");
sections.push(this.formatSection(key, content)); sections.push(this.formatSection(key, content));
console.log(` ✓ Using expansion override for ${key}`); console.log(` ✓ Using expansion override for ${key}`);
found = true; found = true;
@@ -440,9 +453,9 @@ class WebBuilder {
// If not found in expansion pack (or doesn't exist there), try core // If not found in expansion pack (or doesn't exist there), try core
if (!found) { if (!found) {
for (const ext of extensions) { for (const ext of extensions) {
const corePath = path.join(this.rootDir, 'bmad-core', dep.type, `${dep.name}${ext}`); const corePath = path.join(this.rootDir, "bmad-core", dep.type, `${dep.name}${ext}`);
try { try {
const content = await fs.readFile(corePath, 'utf8'); const content = await fs.readFile(corePath, "utf8");
sections.push(this.formatSection(key, content)); sections.push(this.formatSection(key, content));
found = true; found = true;
break; break;
@@ -462,10 +475,12 @@ class WebBuilder {
const resourcePath = path.join(packDir, resourceDir); const resourcePath = path.join(packDir, resourceDir);
try { try {
const resourceFiles = await fs.readdir(resourcePath); const resourceFiles = await fs.readdir(resourcePath);
for (const resourceFile of resourceFiles.filter(f => f.endsWith('.md') || f.endsWith('.yml'))) { for (const resourceFile of resourceFiles.filter(
(f) => f.endsWith(".md") || f.endsWith(".yml")
)) {
const filePath = path.join(resourcePath, resourceFile); const filePath = path.join(resourcePath, resourceFile);
const fileContent = await fs.readFile(filePath, 'utf8'); const fileContent = await fs.readFile(filePath, "utf8");
const fileName = resourceFile.replace(/\.(md|yml)$/, ''); const fileName = resourceFile.replace(/\.(md|yml)$/, "");
// Only add if not already included as a dependency // Only add if not already included as a dependency
const resourceKey = `${resourceDir}#${fileName}`; const resourceKey = `${resourceDir}#${fileName}`;
@@ -478,18 +493,16 @@ class WebBuilder {
} }
} }
return sections.join('\n'); return sections.join("\n");
} }
async listExpansionPacks() { async listExpansionPacks() {
const expansionPacksDir = path.join(this.rootDir, 'expansion-packs'); const expansionPacksDir = path.join(this.rootDir, "expansion-packs");
try { try {
const entries = await fs.readdir(expansionPacksDir, { withFileTypes: true }); const entries = await fs.readdir(expansionPacksDir, { withFileTypes: true });
return entries return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name);
.filter(entry => entry.isDirectory())
.map(entry => entry.name);
} catch (error) { } catch (error) {
console.warn('No expansion-packs directory found'); console.warn("No expansion-packs directory found");
return []; return [];
} }
} }

View File

@@ -39,20 +39,13 @@ class IdeSetup {
async setupCursor(installDir, selectedAgent) { async setupCursor(installDir, selectedAgent) {
const cursorRulesDir = path.join(installDir, ".cursor", "rules"); const cursorRulesDir = path.join(installDir, ".cursor", "rules");
const agents = selectedAgent const agents = selectedAgent ? [selectedAgent] : await this.getAllAgentIds(installDir);
? [selectedAgent]
: await this.getAllAgentIds(installDir);
await fileManager.ensureDirectory(cursorRulesDir); await fileManager.ensureDirectory(cursorRulesDir);
for (const agentId of agents) { for (const agentId of agents) {
// Check if .bmad-core is a subdirectory (full install) or if agents are in root (single agent install) // Check if .bmad-core is a subdirectory (full install) or if agents are in root (single agent install)
let agentPath = path.join( let agentPath = path.join(installDir, ".bmad-core", "agents", `${agentId}.md`);
installDir,
".bmad-core",
"agents",
`${agentId}.md`
);
if (!(await fileManager.pathExists(agentPath))) { if (!(await fileManager.pathExists(agentPath))) {
agentPath = path.join(installDir, "agents", `${agentId}.md`); agentPath = path.join(installDir, "agents", `${agentId}.md`);
} }
@@ -103,20 +96,13 @@ class IdeSetup {
async setupClaudeCode(installDir, selectedAgent) { async setupClaudeCode(installDir, selectedAgent) {
const commandsDir = path.join(installDir, ".claude", "commands"); const commandsDir = path.join(installDir, ".claude", "commands");
const agents = selectedAgent const agents = selectedAgent ? [selectedAgent] : await this.getAllAgentIds(installDir);
? [selectedAgent]
: await this.getAllAgentIds(installDir);
await fileManager.ensureDirectory(commandsDir); await fileManager.ensureDirectory(commandsDir);
for (const agentId of agents) { for (const agentId of agents) {
// Check if .bmad-core is a subdirectory (full install) or if agents are in root (single agent install) // Check if .bmad-core is a subdirectory (full install) or if agents are in root (single agent install)
let agentPath = path.join( let agentPath = path.join(installDir, ".bmad-core", "agents", `${agentId}.md`);
installDir,
".bmad-core",
"agents",
`${agentId}.md`
);
if (!(await fileManager.pathExists(agentPath))) { if (!(await fileManager.pathExists(agentPath))) {
agentPath = path.join(installDir, "agents", `${agentId}.md`); agentPath = path.join(installDir, "agents", `${agentId}.md`);
} }
@@ -136,29 +122,20 @@ class IdeSetup {
} }
} }
console.log( console.log(chalk.green(`\n✓ Created Claude Code commands in ${commandsDir}`));
chalk.green(`\n✓ Created Claude Code commands in ${commandsDir}`)
);
return true; return true;
} }
async setupWindsurf(installDir, selectedAgent) { async setupWindsurf(installDir, selectedAgent) {
const windsurfRulesDir = path.join(installDir, ".windsurf", "rules"); const windsurfRulesDir = path.join(installDir, ".windsurf", "rules");
const agents = selectedAgent const agents = selectedAgent ? [selectedAgent] : await this.getAllAgentIds(installDir);
? [selectedAgent]
: await this.getAllAgentIds(installDir);
await fileManager.ensureDirectory(windsurfRulesDir); await fileManager.ensureDirectory(windsurfRulesDir);
for (const agentId of agents) { for (const agentId of agents) {
// Check if .bmad-core is a subdirectory (full install) or if agents are in root (single agent install) // Check if .bmad-core is a subdirectory (full install) or if agents are in root (single agent install)
let agentPath = path.join( let agentPath = path.join(installDir, ".bmad-core", "agents", `${agentId}.md`);
installDir,
".bmad-core",
"agents",
`${agentId}.md`
);
if (!(await fileManager.pathExists(agentPath))) { if (!(await fileManager.pathExists(agentPath))) {
agentPath = path.join(installDir, "agents", `${agentId}.md`); agentPath = path.join(installDir, "agents", `${agentId}.md`);
} }
@@ -197,9 +174,7 @@ class IdeSetup {
} }
} }
console.log( console.log(chalk.green(`\n✓ Created Windsurf rules in ${windsurfRulesDir}`));
chalk.green(`\n✓ Created Windsurf rules in ${windsurfRulesDir}`)
);
return true; return true;
} }
@@ -233,13 +208,7 @@ class IdeSetup {
} }
async setupRoo(installDir, selectedAgent) { async setupRoo(installDir, selectedAgent) {
const agents = selectedAgent const agents = selectedAgent ? [selectedAgent] : await this.getAllAgentIds(installDir);
? [selectedAgent]
: await this.getAllAgentIds(installDir);
// Create .roo directory first
const rooDir = path.join(installDir, ".roo");
await fileManager.ensureDirectory(rooDir);
// Check for existing .roomodes file in project root // Check for existing .roomodes file in project root
const roomodesPath = path.join(installDir, ".roomodes"); const roomodesPath = path.join(installDir, ".roomodes");
@@ -253,11 +222,7 @@ class IdeSetup {
for (const match of modeMatches) { for (const match of modeMatches) {
existingModes.push(match[1]); existingModes.push(match[1]);
} }
console.log( console.log(chalk.yellow(`Found existing .roomodes file with ${existingModes.length} modes`));
chalk.yellow(
`Found existing .roomodes file with ${existingModes.length} modes`
)
);
} }
// Create new modes content // Create new modes content
@@ -265,55 +230,48 @@ class IdeSetup {
// Define file permissions for each agent type // Define file permissions for each agent type
const agentPermissions = { const agentPermissions = {
'analyst': { analyst: {
fileRegex: '\\.(md|txt)$', fileRegex: "\\.(md|txt)$",
description: 'Documentation and text files' description: "Documentation and text files",
}, },
'pm': { pm: {
fileRegex: '\\.(md|txt)$', fileRegex: "\\.(md|txt)$",
description: 'Product documentation' description: "Product documentation",
}, },
'architect': { architect: {
fileRegex: '\\.(md|txt|yml|yaml|json)$', fileRegex: "\\.(md|txt|yml|yaml|json)$",
description: 'Architecture docs and configs' description: "Architecture docs and configs",
}, },
'dev': null, // Full edit access dev: null, // Full edit access
'qa': { qa: {
fileRegex: '\\.(test|spec)\\.(js|ts|jsx|tsx)$|\\.md$', fileRegex: "\\.(test|spec)\\.(js|ts|jsx|tsx)$|\\.md$",
description: 'Test files and documentation' description: "Test files and documentation",
}, },
'ux-expert': { "ux-expert": {
fileRegex: '\\.(md|css|scss|html|jsx|tsx)$', fileRegex: "\\.(md|css|scss|html|jsx|tsx)$",
description: 'Design-related files' description: "Design-related files",
}, },
'po': { po: {
fileRegex: '\\.(md|txt)$', fileRegex: "\\.(md|txt)$",
description: 'Story and requirement docs' description: "Story and requirement docs",
}, },
'sm': { sm: {
fileRegex: '\\.(md|txt)$', fileRegex: "\\.(md|txt)$",
description: 'Process and planning docs' description: "Process and planning docs",
}, },
'bmad-orchestrator': null, // Full edit access "bmad-orchestrator": null, // Full edit access
'bmad-master': null // Full edit access "bmad-master": null, // Full edit access
}; };
for (const agentId of agents) { for (const agentId of agents) {
// Skip if already exists // Skip if already exists
if (existingModes.includes(`bmad-${agentId}`)) { if (existingModes.includes(`bmad-${agentId}`)) {
console.log( console.log(chalk.dim(`Skipping ${agentId} - already exists in .roomodes`));
chalk.dim(`Skipping ${agentId} - already exists in .roomodes`)
);
continue; continue;
} }
// Read agent file to extract all information // Read agent file to extract all information
let agentPath = path.join( let agentPath = path.join(installDir, ".bmad-core", "agents", `${agentId}.md`);
installDir,
".bmad-core",
"agents",
`${agentId}.md`
);
if (!(await fileManager.pathExists(agentPath))) { if (!(await fileManager.pathExists(agentPath))) {
agentPath = path.join(installDir, "agents", `${agentId}.md`); agentPath = path.join(installDir, "agents", `${agentId}.md`);
} }
@@ -334,9 +292,7 @@ class IdeSetup {
const title = titleMatch ? titleMatch[1].trim() : this.getAgentTitle(agentId); const title = titleMatch ? titleMatch[1].trim() : this.getAgentTitle(agentId);
const icon = iconMatch ? iconMatch[1].trim() : "🤖"; const icon = iconMatch ? iconMatch[1].trim() : "🤖";
const whenToUse = whenToUseMatch const whenToUse = whenToUseMatch ? whenToUseMatch[1].trim() : `Use for ${title} tasks`;
? whenToUseMatch[1].trim()
: `Use for ${title} tasks`;
const roleDefinition = roleDefinitionMatch const roleDefinition = roleDefinitionMatch
? roleDefinitionMatch[1].trim() ? roleDefinitionMatch[1].trim()
: `You are a ${title} specializing in ${title.toLowerCase()} tasks and responsibilities.`; : `You are a ${title} specializing in ${title.toLowerCase()} tasks and responsibilities.`;
@@ -360,9 +316,7 @@ class IdeSetup {
newModesContent += ` - edit\n`; newModesContent += ` - edit\n`;
} }
console.log( console.log(chalk.green(`✓ Added mode: bmad-${agentId} (${icon} ${title})`));
chalk.green(`✓ Added mode: bmad-${agentId} (${icon} ${title})`)
);
} }
} }
} }
@@ -381,46 +335,8 @@ class IdeSetup {
await fileManager.writeFile(roomodesPath, roomodesContent); await fileManager.writeFile(roomodesPath, roomodesContent);
console.log(chalk.green("✓ Created .roomodes file in project root")); console.log(chalk.green("✓ Created .roomodes file in project root"));
// Create README in .roo directory
const rooReadme = `# Roo Code Custom Modes for BMAD-METHOD
This directory contains custom mode configurations for Roo Code to enable BMAD agent personalities.
## Setup
The \`.roomodes\` file defines all BMAD agents as custom modes using the proper \`customModes:\` structure. Modes are automatically available in Roo Code when you open this project.
## Available Modes
${agents.map((id) => `- **bmad-${id}** - ${this.getAgentTitle(id)}`).join("\n")}
## Usage
In Roo Code:
1. Open the mode selector (usually in the status bar)
2. Select any BMAD agent mode
3. The AI will adopt that agent's personality and expertise
## File Permissions
Each agent has specific file access permissions:
- **Analysts, PM, PO, SM**: Limited to documentation files (.md, .txt)
- **Architect**: Architecture docs and configs (.md, .txt, .yml, .yaml, .json)
- **QA**: Test files and documentation
- **UX Expert**: Design-related files (.md, .css, .scss, .html, .jsx, .tsx)
- **Developer, Orchestrator, Master**: Full edit access to all files
`;
const readmePath = path.join(rooDir, "README.md");
await fileManager.writeFile(readmePath, rooReadme);
console.log(chalk.green("✓ Created .roo/README.md"));
console.log(chalk.green(`\n✓ Roo Code setup complete!`)); console.log(chalk.green(`\n✓ Roo Code setup complete!`));
console.log( console.log(chalk.dim("Custom modes will be available when you open this project in Roo Code"));
chalk.dim(
"Custom modes will be available when you open this project in Roo Code"
)
);
return true; return true;
} }