Compare commits

...

14 Commits

Author SHA1 Message Date
Nikita Levyankov 72a8f3248b
Merge 251ec93150 into e39aa33eea 2025-12-18 11:00:05 +00:00
Nikita Levyankov 251ec93150 refactor: remove dead code (profileModules/Agents/Workflows)
Removed unused properties that were set but never read:
- normalized.profileModules
- normalized.profileAgents
- normalized.profileWorkflows

These values were:
1. Stored as unwrapped (strings like 'all' or arrays)
2. Never accessed anywhere in the codebase
3. Created confusion - actual values used are normalized.modules/agents/workflows
4. Inconsistent with the wrapped versions actually used downstream

The profile values are already correctly processed and stored in
normalized.modules/agents/workflows (with proper array wrapping),
which are then passed to installer via config.cliOptions.

No functional change - just removing dead code that served no purpose.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-18 12:59:22 +02:00
Nikita Levyankov 3a663a9a92 chore: add _bmad-output/ to .gitignore
Added BMAD output directory to gitignore to prevent generated output files from being committed.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-18 12:43:53 +02:00
Nikita Levyankov 133181aaa9 chore: add .idea/ to .gitignore
Added IntelliJ IDEA project configuration directory to gitignore to prevent IDE-specific files from being committed.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-18 12:42:53 +02:00
Nikita Levyankov 32b104b4bc fix: handle array-wrapped 'all' value for modules
After wrapping profile string values in arrays in options-parser, the modules handling in ui.js was still only checking for string 'all', not array ['all']. This would break profiles with `modules: 'all'`.

Added checks for both cases:
- String: `options.modules === 'all'` (original case)
- Array: `options.modules.includes('all')` (new case after wrapping)

Now modules handling is consistent with agents/workflows filtering which already used `.includes('all')`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-18 12:39:07 +02:00
Nikita Levyankov e9c0aafad9 fix: wrap string profile values in arrays for consistency
The ternary operators WERE needed after all! Profile values can be:
- Arrays: ['dev', 'architect', 'pm']
- Strings: 'all' (special keyword)

Downstream code expects arrays:
- filterWorkflows() checks selectedWorkflows.includes('all')
- filterAgents() checks selectedAgents.includes('all')
- separateModifiers() iterates with for-of loop

Without wrapping strings in arrays:
- 'all' → stays as string → includes() doesn't work
- WITH fix: 'all' → becomes ['all'] → includes('all') works ✓

This fixes the profile workflow:
1. Profile defines: workflows: 'all'
2. Parser wraps: normalized.workflows = ['all']
3. Filter checks: selectedWorkflows.includes('all') → true ✓

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-18 12:36:30 +02:00
Nikita Levyankov b88845c0fe fix: remove useless ternary operators in options-parser
The ternary operators were checking `Array.isArray()` but returning the same value in both branches, making them completely pointless. Since profiles can contain both arrays (e.g., `['dev', 'architect']`) and strings (e.g., `'all'`), and both are valid, we should just assign the value directly.

Fixed lines:
- normalized.modules = profile.modules
- normalized.agents = profile.agents
- normalized.workflows = profile.workflows

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-18 12:32:04 +02:00
Nikita Levyankov 1dca5fb4b7 fix: lint and formatting issues in non-interactive installation PR
- Fix ESLint errors: use switch over else-if, remove unused catch bindings, use slice over substring, use replaceAll over replace
- Fix Prettier formatting issues across all modified files
- Fix markdown lint: wrap bare URL in angle brackets

All tests passing: schemas, installation, validation, lint, markdown lint, and formatting checks.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-18 12:25:01 +02:00
Alex Verkhovsky 57bd2c0731
Merge branch 'main' into main 2025-12-18 01:52:00 -07:00
sjennings e39aa33eea
fix(bmgd): add workflow status update to game-architecture completion (#1161)
* fix(bmgd): add workflow status update to game-architecture completion

The game-architecture workflow was not updating the bmgd-workflow-status.yaml
file on completion, unlike other BMGD workflows (narrative, brainstorm-game).

Changes:
- Add step 4 "Update Workflow Status" to update create-architecture status
- Renumber subsequent steps (5-8 → 6-9)
- Add success metric for workflow status update
- Add failure condition for missing status update

* feat(bmgd): add generate-project-context workflow for game development

Adds a new workflow to create optimized project-context.md files for AI agent
consistency in game development projects.

New workflow files:
- workflow.md: Main workflow entry point
- project-context-template.md: Template for context file
- steps/step-01-discover.md: Context discovery & initialization
- steps/step-02-generate.md: Rules generation with A/P/C menus
- steps/step-03-complete.md: Finalization & optimization

Integration:
- Added generate-project-context trigger to game-architect agent menu
- Added project context creation option to game-architecture completion step
- Renumbered steps 6-9 → 7-10 to accommodate new step 6

Adapted from BMM generate-project-context with game-specific:
- Engine patterns (Unity, Unreal, Godot)
- Performance and frame budget rules
- Platform-specific requirements
- Game testing patterns

---------

Co-authored-by: Scott Jennings <scott.jennings+CIGINT@cloudimperiumgames.com>
Co-authored-by: Brian <bmadcode@gmail.com>
2025-12-18 16:14:18 +08:00
Alex Verkhovsky 2da9aebaa8
docs: add DigitalOcean sponsor attribution (#1162) 2025-12-18 15:58:54 +08:00
Brian Madison 5c756b6404 chore: bump version to 6.0.0-alpha.19
Bug fix:
- Fixed _bmad folder stutter with agent custom files
- Removed unnecessary backup file causing installer bloat
- Improved path handling for agent customizations
2025-12-18 12:52:10 +08:00
Brian 52bd250a8c
Merge branch 'main' into main 2025-12-18 02:47:51 +08:00
Nikita Levyankov b6ade22984 feat: introduce non-interactive cli installation 2025-12-17 11:42:35 +02:00
22 changed files with 2761 additions and 15 deletions

2
.gitignore vendored
View File

@ -29,6 +29,7 @@ Thumbs.db
.prettierrc
# IDE and editor configs
.idea/
.windsurf/
.trae/
_bmad*/.cursor/
@ -66,6 +67,7 @@ shared-modules
z*/
_bmad
_bmad-output/
.claude
.codex
.github/chatmodes

View File

@ -1,5 +1,25 @@
# Changelog
## [6.0.0-alpha.19]
**Release: December 18, 2025**
### 🐛 Bug Fixes
**Installer Stability:**
- **Fixed \_bmad Folder Stutter**: Resolved issue with duplicate \_bmad folder creation when applying agent custom files
- **Cleaner Installation**: Removed unnecessary backup file that was causing bloat in the installer
- **Streamlined Agent Customization**: Fixed path handling for agent custom files to prevent folder duplication
### 📊 Statistics
- **3 files changed** with critical fix
- **3,688 lines removed** by eliminating backup files
- **Improved installer performance** and stability
---
## [6.0.0-alpha.18]
**Release: December 18, 2025**

View File

@ -79,6 +79,8 @@ With **BMad Builder**, you can architect both simple agents and vastly complex d
### 1. Install BMad Method
#### Interactive Installation (Default)
```bash
# Install v6 Alpha (recommended)
npx bmad-method@alpha install
@ -87,6 +89,65 @@ npx bmad-method@alpha install
npx bmad-method install
```
#### Non-Interactive Installation (CI/CD, Automation)
For automated deployments and CI/CD pipelines:
```bash
# Minimal installation with defaults
npx bmad-method@alpha install -y
# Custom configuration
npx bmad-method@alpha install -y \
--user-name=Alice \
--skill-level=advanced \
--output-folder=.bmad-output
# Team-based installation (fullstack team)
npx bmad-method@alpha install -y --team=fullstack
# Team with modifications
npx bmad-method@alpha install -y --team=fullstack --agents=+dev
# Selective agents and workflows
npx bmad-method@alpha install -y \
--agents=dev,architect,pm \
--workflows=create-prd,create-tech-spec,dev-story
# Profile-based installation
npx bmad-method@alpha install -y --profile=minimal
npx bmad-method@alpha install -y --profile=solo-dev
```
**Available Options:**
- `-y, --non-interactive` - Skip all prompts, use defaults
- `--user-name <name>` - User name for configuration
- `--skill-level <level>` - beginner, intermediate, advanced
- `--output-folder <path>` - Output folder for BMAD artifacts
- `--modules <list>` - Comma-separated module list
- `--agents <list>` - Comma-separated agent list or 'all', 'none'
- `--workflows <list>` - Comma-separated workflow list or 'all', 'none'
- `--team <name>` - Install predefined team (fullstack, gamedev)
- `--profile <name>` - Installation profile (minimal, full, solo-dev, team)
**Modifiers:**
- `--agents=+dev` - Add agent to team/profile selection
- `--agents=-dev` - Remove agent from team/profile selection
**Available Teams:**
- `fullstack` - analyst, architect, pm, sm, ux-designer
- `gamedev` - game-designer, game-dev, game-architect, game-scrum-master
**Available Profiles:**
- `minimal` - Core + dev agent + essential workflows
- `full` - Everything (all modules, agents, workflows)
- `solo-dev` - Single developer setup
- `team` - Team collaboration setup
### 2. Initialize Your Project
Load any agent in your IDE and run:
@ -231,6 +292,8 @@ MIT License - See [LICENSE](LICENSE) for details.
**Trademarks:** BMad™ and BMAD-METHOD™ are trademarks of BMad Code, LLC.
Supported by:&nbsp;&nbsp;<a href="https://m.do.co/c/00f11bd932bb"><img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" height="24" alt="DigitalOcean" style="vertical-align: middle;"></a>
---
<p align="center">

View File

@ -0,0 +1,448 @@
# Non-Interactive Installation Guide
This guide helps you convert interactive BMAD installations to non-interactive CLI commands for automation, CI/CD pipelines, and scripted deployments.
## Table of Contents
- [Quick Start](#quick-start)
- [Migration from Interactive to CLI](#migration-from-interactive-to-cli)
- [Common Use Cases](#common-use-cases)
- [CLI Options Reference](#cli-options-reference)
- [Team-Based Installation](#team-based-installation)
- [Profile-Based Installation](#profile-based-installation)
- [Troubleshooting](#troubleshooting)
## Quick Start
### Minimal Non-Interactive Installation
```bash
npx bmad-method@alpha install -y
```
This installs BMAD with:
- Default user name from system (USER environment variable)
- Intermediate skill level
- Default output folder (`_bmad-output`)
- BMM module
- All agents and workflows from BMM
### Custom Non-Interactive Installation
```bash
npx bmad-method@alpha install -y \
--user-name=YourName \
--skill-level=advanced \
--output-folder=.artifacts
```
## Migration from Interactive to CLI
### Step 1: Note Your Current Configuration
If you have an existing BMAD installation, check your configuration:
```bash
# View your current configuration
cat _bmad/core/config.yaml
cat _bmad/bmm/config.yaml
```
Example output:
```yaml
user_name: Alice
user_skill_level: intermediate
output_folder: '{project-root}/_bmad-output'
communication_language: English
```
### Step 2: Convert to CLI Command
Based on your configuration, build the equivalent CLI command:
```bash
npx bmad-method@alpha install -y \
--user-name=Alice \
--skill-level=intermediate \
--output-folder=_bmad-output \
--communication-language=English
```
### Step 3: Replicate Module and Agent Selection
Check what agents and workflows you currently have:
```bash
# View installed agents
cat _bmad/_config/agents.csv
# View installed workflows
cat _bmad/_config/workflows.csv
```
If you have specific agents installed, add them to your command:
```bash
npx bmad-method@alpha install -y \
--user-name=Alice \
--agents=dev,architect,pm
```
## Common Use Cases
### 1. CI/CD Pipeline Installation
```yaml
# .github/workflows/setup-bmad.yml
name: Setup BMAD
on: [push]
jobs:
setup:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: '20'
- name: Install BMAD
run: npx bmad-method@alpha install -y --profile=minimal
```
### 2. Docker Container Setup
```dockerfile
FROM node:20-alpine
WORKDIR /app
COPY . .
# Install BMAD non-interactively
RUN npx bmad-method@alpha install -y \
--user-name=ContainerUser \
--skill-level=intermediate \
--output-folder=.bmad-output
CMD ["npm", "start"]
```
### 3. Team Onboarding Script
```bash
#!/bin/bash
# onboard-developer.sh
echo "Setting up BMAD for $USER..."
npx bmad-method@alpha install -y \
--user-name=$USER \
--skill-level=intermediate \
--team=fullstack \
--output-folder=.bmad-output
echo "BMAD installation complete!"
```
### 4. Infrastructure as Code
```bash
# terraform/setup.sh
npx bmad-method@alpha install -y \
--user-name=TerraformBot \
--skill-level=advanced \
--modules=core,bmm \
--agents=dev,architect,analyst \
--workflows=create-prd,create-architecture,dev-story
```
### 5. Minimal Developer Setup
For developers who only need code generation:
```bash
npx bmad-method@alpha install -y \
--profile=minimal \
--user-name=$USER
```
## CLI Options Reference
### Core Options
| Option | Description | Default | Example |
| --------------------------------- | ---------------------- | -------------- | ---------------------------------- |
| `-y, --non-interactive` | Skip all prompts | `false` | `install -y` |
| `--user-name <name>` | User name | System user | `--user-name=Alice` |
| `--skill-level <level>` | Skill level | `intermediate` | `--skill-level=advanced` |
| `--output-folder <path>` | Output folder | `_bmad-output` | `--output-folder=.artifacts` |
| `--communication-language <lang>` | Communication language | `English` | `--communication-language=Spanish` |
| `--document-language <lang>` | Document language | `English` | `--document-language=French` |
### Module & Selection Options
| Option | Description | Example |
| -------------------- | ------------------------- | ---------------------------------- |
| `--modules <list>` | Comma-separated modules | `--modules=core,bmm,bmbb` |
| `--agents <list>` | Comma-separated agents | `--agents=dev,architect,pm` |
| `--workflows <list>` | Comma-separated workflows | `--workflows=create-prd,dev-story` |
### Team & Profile Options
| Option | Description | Example |
| ------------------ | ----------------------- | ------------------- |
| `--team <name>` | Install predefined team | `--team=fullstack` |
| `--profile <name>` | Installation profile | `--profile=minimal` |
## Team-Based Installation
Teams are predefined bundles of agents and workflows optimized for specific use cases.
### Available Teams
#### Fullstack Team
```bash
npx bmad-method@alpha install -y --team=fullstack
```
**Includes:**
- Agents: analyst, architect, pm, sm, ux-designer
- Module: BMM
**Use for:** Full product development teams
#### Game Development Team
```bash
npx bmad-method@alpha install -y --team=gamedev
```
**Includes:**
- Agents: game-designer, game-dev, game-architect, game-scrum-master
- Workflows: brainstorm-game, game-brief, gdd, narrative
- Module: BMGD (Game Development)
**Use for:** Game development projects
### Modifying Team Selections
You can add or remove agents from a team:
```bash
# Add dev agent to fullstack team
npx bmad-method@alpha install -y --team=fullstack --agents=+dev
# Remove ux-designer from fullstack team
npx bmad-method@alpha install -y --team=fullstack --agents=-ux-designer
# Add and remove multiple
npx bmad-method@alpha install -y --team=fullstack --agents=+dev,+tea,-ux-designer
```
## Profile-Based Installation
Profiles are pre-configured installations for common scenarios.
### Available Profiles
#### Minimal Profile
```bash
npx bmad-method@alpha install -y --profile=minimal
```
**Includes:**
- Modules: core
- Agents: dev
- Workflows: create-tech-spec, quick-dev
**Use for:** Simple development, code generation only
#### Solo Developer Profile
```bash
npx bmad-method@alpha install -y --profile=solo-dev
```
**Includes:**
- Modules: core, bmm
- Agents: dev, architect, analyst, tech-writer
- Workflows: create-tech-spec, quick-dev, dev-story, code-review, create-prd, create-architecture
**Use for:** Individual developers working on full projects
#### Full Profile
```bash
npx bmad-method@alpha install -y --profile=full
```
**Includes:**
- All modules
- All agents
- All workflows
**Use for:** Maximum flexibility, exploring all BMAD features
#### Team Profile
```bash
npx bmad-method@alpha install -y --profile=team
```
**Includes:**
- Modules: core, bmm
- Agents: dev, architect, pm, sm, analyst, ux-designer
- Workflows: create-product-brief, create-prd, create-architecture, create-epics-and-stories, sprint-planning, create-story, dev-story, code-review, workflow-init
**Use for:** Team collaboration, full agile workflow
### Overriding Profile Settings
```bash
# Use minimal profile but add architect agent
npx bmad-method@alpha install -y --profile=minimal --agents=dev,architect
# Use solo-dev profile with custom output folder
npx bmad-method@alpha install -y --profile=solo-dev --output-folder=.custom
```
## Troubleshooting
### Issue: "Team not found"
**Solution:** Check available teams:
```bash
# List available teams in your installation
ls src/modules/*/teams/team-*.yaml
```
Available teams depend on installed modules. Ensure you have the required modules.
### Issue: "Agent not found in manifest"
**Solution:** The agent name might be incorrect. Check available agents:
```bash
# View all available agents
find src/modules -name "*.agent.yaml" -o -name "*-agent.md"
```
Common agent names: `dev`, `architect`, `pm`, `sm`, `analyst`, `ux-designer`, `tech-writer`
### Issue: "Installation hangs"
**Solution:** Ensure you're using the `-y` flag for non-interactive mode:
```bash
# Correct
npx bmad-method@alpha install -y
# Incorrect (will wait for input)
npx bmad-method@alpha install
```
### Issue: "Permission denied"
**Solution:** Check file permissions or run with appropriate privileges:
```bash
# Check current directory permissions
ls -la
# Ensure you have write permissions
chmod u+w .
```
### Issue: "Invalid skill level"
**Solution:** Use one of the valid skill levels:
- `beginner`
- `intermediate`
- `advanced`
```bash
# Correct
npx bmad-method@alpha install -y --skill-level=advanced
# Incorrect
npx bmad-method@alpha install -y --skill-level=expert
```
## Advanced Examples
### Reproducible Installation
Save your installation command for reproducibility:
```bash
#!/bin/bash
# install-bmad.sh - Reproducible BMAD installation
npx bmad-method@alpha install -y \
--user-name=ProjectBot \
--skill-level=intermediate \
--output-folder=_bmad-output \
--modules=core,bmm \
--agents=dev,architect,pm,analyst \
--workflows=create-prd,create-architecture,create-tech-spec,dev-story,code-review \
--communication-language=English \
--document-language=English
```
### Environment-Based Installation
Use environment variables for flexibility:
```bash
#!/bin/bash
# Detect user from environment
USER_NAME=${BMAD_USER:-$USER}
# Detect skill level from environment or default to intermediate
SKILL_LEVEL=${BMAD_SKILL_LEVEL:-intermediate}
npx bmad-method@alpha install -y \
--user-name=$USER_NAME \
--skill-level=$SKILL_LEVEL \
--output-folder=${BMAD_OUTPUT_FOLDER:-_bmad-output}
```
### Conditional Installation
```bash
#!/bin/bash
# Install different configurations based on environment
if [ "$CI" = "true" ]; then
# CI environment: minimal installation
npx bmad-method@alpha install -y --profile=minimal
elif [ "$TEAM_MODE" = "true" ]; then
# Team development: full team setup
npx bmad-method@alpha install -y --team=fullstack
else
# Local development: solo-dev profile
npx bmad-method@alpha install -y --profile=solo-dev --user-name=$USER
fi
```
## Next Steps
- Read the [main README](../README.md) for BMAD overview
- Explore [Custom Content Installation](./custom-content-installation.md)
- Join the [BMAD Discord](https://discord.gg/gk8jAdXWmj) community
## Feedback
Found an issue or have a suggestion? Please report it at:
<https://github.com/bmad-code-org/BMAD-METHOD/issues>

View File

@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "bmad-method",
"version": "6.0.0-alpha.18",
"version": "6.0.0-alpha.19",
"description": "Breakthrough Method of Agile AI-driven Development",
"keywords": [
"agile",

View File

@ -33,6 +33,10 @@ agent:
exec: "{project-root}/_bmad/bmgd/workflows/3-technical/game-architecture/workflow.md"
description: Produce a Scale Adaptive Game Architecture
- trigger: generate-project-context
exec: "{project-root}/_bmad/bmgd/workflows/3-technical/generate-project-context/workflow.md"
description: Create optimized project-context.md for AI agent consistency
- trigger: correct-course
workflow: "{project-root}/_bmad/bmgd/workflows/4-production/correct-course/workflow.yaml"
description: Course Correction Analysis (when implementation is off-track)

View File

@ -12,6 +12,7 @@ outputFile: '{output_folder}/game-architecture.md'
# Handoff References
epicWorkflow: '{project-root}/_bmad/bmgd/workflows/4-production/epic-workflow/workflow.yaml'
projectContextWorkflow: '{project-root}/_bmad/bmgd/workflows/3-technical/generate-project-context/workflow.md'
---
# Step 9: Completion
@ -131,7 +132,17 @@ platform: '{{platform}}'
---
````
### 4. Present Completion Summary
### 4. Update Workflow Status
**If not in standalone mode:**
Load `{output_folder}/bmgd-workflow-status.yaml` and:
- Update `create-architecture` status to the output file path
- Preserve all comments and structure
- Determine next workflow in sequence
### 5. Present Completion Summary
"**Architecture Complete!**
@ -158,9 +169,50 @@ platform: '{{platform}}'
**Document saved to:** `{outputFile}`
Do you want to review or adjust anything before we finalize?"
Do you want to review or adjust anything before we finalize?
### 5. Handle Review Requests
**Optional Enhancement: Project Context File**
Would you like to create a `project-context.md` file? This is a concise, optimized guide for AI agents that captures:
- Critical engine-specific rules they might miss
- Specific patterns and conventions for your game project
- Performance and optimization requirements
- Anti-patterns and edge cases to avoid
{if_existing_project_context}
I noticed you already have a project context file. Would you like to update it with your new architectural decisions?
{else}
This file helps ensure AI agents implement game code consistently with your project's unique requirements and patterns.
{/if_existing_project_context}
**Create/Update project context?** [Y/N]"
### 6. Handle Project Context Creation Choice
If user responds 'Y' or 'yes' to creating/updating project context:
"Excellent choice! Let me launch the Generate Project Context workflow to create a comprehensive guide for AI agents.
This will help ensure consistent implementation by capturing:
- Engine-specific patterns and rules
- Performance and optimization conventions from your architecture
- Testing and quality standards
- Anti-patterns to avoid
The workflow will collaborate with you to create an optimized `project-context.md` file that AI agents will read before implementing any game code."
**Execute the Generate Project Context workflow:**
- Load and execute: `{projectContextWorkflow}`
- The workflow will handle discovery, generation, and completion of the project context file
- After completion, return here for final handoff
If user responds 'N' or 'no':
"Understood! Your architecture is complete and ready for implementation. You can always create a project context file later using the Generate Project Context workflow if needed."
### 7. Handle Review Requests
**If user wants to review:**
@ -179,7 +231,7 @@ Or type 'all' to see the complete document."
**Show requested section and allow edits.**
### 6. Present Next Steps Menu
### 8. Present Next Steps Menu
**After user confirms completion:**
@ -204,7 +256,7 @@ Or type 'all' to see the complete document."
2. Proceed to Epic creation workflow
3. Exit workflow"
### 7. Handle User Selection
### 9. Handle User Selection
Based on user choice:
@ -224,7 +276,7 @@ Based on user choice:
- Confirm document is saved and complete
- Exit workflow gracefully
### 8. Provide Handoff Guidance
### 10. Provide Handoff Guidance
**For Epic Creation handoff:**
@ -270,6 +322,7 @@ This is the final step. Ensure:
- Development setup is complete
- Document status updated to 'complete'
- Frontmatter shows all steps completed
- Workflow status updated (if tracking)
- User has clear next steps
- Document saved and ready for AI agent consumption
@ -278,6 +331,7 @@ This is the final step. Ensure:
- Missing executive summary
- Incomplete development setup
- Frontmatter not updated
- Status not updated when tracking
- No clear next steps provided
- User left without actionable guidance

View File

@ -0,0 +1,20 @@
---
project_name: '{{project_name}}'
user_name: '{{user_name}}'
date: '{{date}}'
sections_completed: []
---
# Project Context for AI Agents
_This file contains critical rules and patterns that AI agents must follow when implementing game code in this project. Focus on unobvious details that agents might otherwise miss._
---
## Technology Stack & Versions
_Documented after discovery phase_
## Critical Implementation Rules
_Documented after discovery phase_

View File

@ -0,0 +1,201 @@
# Step 1: Context Discovery & Initialization
## MANDATORY EXECUTION RULES (READ FIRST):
- NEVER generate content without user input
- ALWAYS treat this as collaborative discovery between technical peers
- YOU ARE A FACILITATOR, not a content generator
- FOCUS on discovering existing project context and technology stack
- IDENTIFY critical implementation rules that AI agents need
- ABSOLUTELY NO TIME ESTIMATES
## EXECUTION PROTOCOLS:
- Show your analysis before taking any action
- Read existing project files to understand current context
- Initialize document and update frontmatter
- FORBIDDEN to load next step until discovery is complete
## CONTEXT BOUNDARIES:
- Variables from workflow.md are available in memory
- Focus on existing project files and architecture decisions
- Look for patterns, conventions, and unique requirements
- Prioritize rules that prevent implementation mistakes
## YOUR TASK:
Discover the project's game engine, technology stack, existing patterns, and critical implementation rules that AI agents must follow when writing game code.
## DISCOVERY SEQUENCE:
### 1. Check for Existing Project Context
First, check if project context already exists:
- Look for file at `{output_folder}/project-context.md`
- If exists: Read complete file to understand existing rules
- Present to user: "Found existing project context with {number_of_sections} sections. Would you like to update this or create a new one?"
### 2. Discover Game Engine & Technology Stack
Load and analyze project files to identify technologies:
**Architecture Document:**
- Look for `{output_folder}/game-architecture.md` or `{output_folder}/architecture.md`
- Extract engine choice with specific version (Unity, Unreal, Godot, custom)
- Note architectural decisions that affect implementation
**Engine-Specific Files:**
- Unity: Check for `ProjectSettings/ProjectVersion.txt`, `Packages/manifest.json`
- Unreal: Check for `.uproject` files, `Config/DefaultEngine.ini`
- Godot: Check for `project.godot`, `export_presets.cfg`
- Custom: Check for engine config files, build scripts
**Package/Dependency Files:**
- Unity: `Packages/manifest.json`, NuGet packages
- Unreal: `.Build.cs` files, plugin configs
- Godot: `addons/` directory, GDExtension configs
- Web-based: `package.json`, `requirements.txt`
**Configuration Files:**
- Build tool configs
- Linting and formatting configs
- Testing configurations
- CI/CD pipeline configs
### 3. Identify Existing Code Patterns
Search through existing codebase for patterns:
**Naming Conventions:**
- Script/class naming patterns
- Asset naming conventions
- Scene/level naming patterns
- Test file naming patterns
**Code Organization:**
- How components/scripts are structured
- Where utilities and helpers are placed
- How systems are organized
- Folder hierarchy patterns
**Engine-Specific Patterns:**
- Unity: MonoBehaviour patterns, ScriptableObject usage, serialization rules
- Unreal: Actor/Component patterns, Blueprint integration, UE macros
- Godot: Node patterns, signal usage, autoload patterns
### 4. Extract Critical Implementation Rules
Look for rules that AI agents might miss:
**Engine-Specific Rules:**
- Unity: Assembly definitions, Unity lifecycle methods, coroutine patterns
- Unreal: UPROPERTY/UFUNCTION usage, garbage collection rules, tick patterns
- Godot: `_ready` vs `_enter_tree`, node ownership, scene instancing
**Performance Rules:**
- Frame budget constraints
- Memory allocation patterns
- Hot path optimization requirements
- Object pooling patterns
**Platform-Specific Rules:**
- Target platform constraints
- Input handling conventions
- Platform-specific code patterns
- Build configuration rules
**Testing Rules:**
- Test structure requirements
- Mock usage conventions
- Integration vs unit test boundaries
- Play mode vs edit mode testing
### 5. Initialize Project Context Document
Based on discovery, create or update the context document:
#### A. Fresh Document Setup (if no existing context)
Copy template from `{installed_path}/project-context-template.md` to `{output_folder}/project-context.md`
Initialize frontmatter with:
```yaml
---
project_name: '{{project_name}}'
user_name: '{{user_name}}'
date: '{{date}}'
sections_completed: ['technology_stack']
existing_patterns_found: { { number_of_patterns_discovered } }
---
```
#### B. Existing Document Update
Load existing context and prepare for updates
Set frontmatter `sections_completed` to track what will be updated
### 6. Present Discovery Summary
Report findings to user:
"Welcome {{user_name}}! I've analyzed your game project for {{project_name}} to discover the context that AI agents need.
**Game Engine & Stack Discovered:**
{{engine_and_version}}
{{list_of_technologies_with_versions}}
**Existing Patterns Found:**
- {{number_of_patterns}} implementation patterns
- {{number_of_conventions}} coding conventions
- {{number_of_rules}} critical rules
**Key Areas for Context Rules:**
- {{area_1}} (e.g., Engine lifecycle and patterns)
- {{area_2}} (e.g., Performance and optimization)
- {{area_3}} (e.g., Platform-specific requirements)
{if_existing_context}
**Existing Context:** Found {{sections}} sections already defined. We can update or add to these.
{/if_existing_context}
Ready to create/update your project context. This will help AI agents implement game code consistently with your project's standards.
[C] Continue to context generation"
## SUCCESS METRICS:
- Existing project context properly detected and handled
- Game engine and technology stack accurately identified with versions
- Critical implementation patterns discovered
- Project context document properly initialized
- Discovery findings clearly presented to user
- User ready to proceed with context generation
## FAILURE MODES:
- Not checking for existing project context before creating new one
- Missing critical engine versions or configurations
- Overlooking important coding patterns or conventions
- Not initializing frontmatter properly
- Not presenting clear discovery summary to user
## NEXT STEP:
After user selects [C] to continue, load `./step-02-generate.md` to collaboratively generate the specific project context rules.
Remember: Do NOT proceed to step-02 until user explicitly selects [C] from the menu and discovery is confirmed!

View File

@ -0,0 +1,373 @@
# Step 2: Context Rules Generation
## MANDATORY EXECUTION RULES (READ FIRST):
- NEVER generate content without user input
- ALWAYS treat this as collaborative discovery between technical peers
- YOU ARE A FACILITATOR, not a content generator
- FOCUS on unobvious rules that AI agents need to be reminded of
- KEEP CONTENT LEAN - optimize for LLM context efficiency
- ABSOLUTELY NO TIME ESTIMATES
## EXECUTION PROTOCOLS:
- Show your analysis before taking any action
- Focus on specific, actionable rules rather than general advice
- Present A/P/C menu after each major rule category
- ONLY save when user chooses C (Continue)
- Update frontmatter with completed sections
- FORBIDDEN to load next step until all sections are complete
## COLLABORATION MENUS (A/P/C):
This step will generate content and present choices for each rule category:
- **A (Advanced Elicitation)**: Use discovery protocols to explore nuanced implementation rules
- **P (Party Mode)**: Bring multiple perspectives to identify critical edge cases
- **C (Continue)**: Save the current rules and proceed to next category
## PROTOCOL INTEGRATION:
- When 'A' selected: Execute {project-root}/\_bmad/core/tasks/advanced-elicitation.xml
- When 'P' selected: Execute {project-root}/\_bmad/core/workflows/party-mode
- PROTOCOLS always return to display this step's A/P/C menu after the A or P have completed
- User accepts/rejects protocol changes before proceeding
## CONTEXT BOUNDARIES:
- Discovery results from step-1 are available
- Game engine and existing patterns are identified
- Focus on rules that prevent implementation mistakes
- Prioritize unobvious details that AI agents might miss
## YOUR TASK:
Collaboratively generate specific, critical rules that AI agents must follow when implementing game code in this project.
## CONTEXT GENERATION SEQUENCE:
### 1. Technology Stack & Versions
Document the exact technology stack from discovery:
**Core Technologies:**
Based on user skill level, present findings:
**Expert Mode:**
"Technology stack from your architecture and project files:
{{exact_technologies_with_versions}}
Any critical version constraints I should document for agents?"
**Intermediate Mode:**
"I found your technology stack:
**Game Engine:**
{{engine_with_version}}
**Key Dependencies:**
{{important_dependencies_with_versions}}
Are there any version constraints or compatibility notes agents should know about?"
**Beginner Mode:**
"Here are the technologies you're using:
**Game Engine:**
{{friendly_description_of_engine}}
**Important Notes:**
{{key_things_agents_need_to_know_about_versions}}
Should I document any special version rules or compatibility requirements?"
### 2. Engine-Specific Rules
Focus on unobvious engine patterns agents might miss:
**Unity Rules (if applicable):**
"Based on your Unity project, I notice some specific patterns:
**Lifecycle Rules:**
{{unity_lifecycle_patterns}}
**Serialization Rules:**
{{serialization_requirements}}
**Assembly Definitions:**
{{assembly_definition_rules}}
**Coroutine/Async Patterns:**
{{async_patterns}}
Are these patterns correct? Any other Unity-specific rules agents should follow?"
**Unreal Rules (if applicable):**
"Based on your Unreal project, I notice some specific patterns:
**UPROPERTY/UFUNCTION Rules:**
{{macro_usage_patterns}}
**Blueprint Integration:**
{{blueprint_rules}}
**Garbage Collection:**
{{gc_patterns}}
**Tick Patterns:**
{{tick_optimization_rules}}
Are these patterns correct? Any other Unreal-specific rules agents should follow?"
**Godot Rules (if applicable):**
"Based on your Godot project, I notice some specific patterns:
**Node Lifecycle:**
{{node_lifecycle_patterns}}
**Signal Usage:**
{{signal_conventions}}
**Scene Instancing:**
{{scene_patterns}}
**Autoload Patterns:**
{{autoload_rules}}
Are these patterns correct? Any other Godot-specific rules agents should follow?"
### 3. Performance Rules
Document performance-critical patterns:
**Frame Budget Rules:**
"Your game has these performance requirements:
**Target Frame Rate:**
{{target_fps}}
**Frame Budget:**
{{milliseconds_per_frame}}
**Critical Systems:**
{{systems_that_must_meet_budget}}
**Hot Path Rules:**
{{hot_path_patterns}}
Any other performance rules agents must follow?"
**Memory Management:**
"Memory patterns for your project:
**Allocation Rules:**
{{allocation_patterns}}
**Pooling Requirements:**
{{object_pooling_rules}}
**Asset Loading:**
{{asset_loading_patterns}}
Are there memory constraints agents should know about?"
### 4. Code Organization Rules
Document project structure and organization:
**Folder Structure:**
"Your project organization:
**Script Organization:**
{{script_folder_structure}}
**Asset Organization:**
{{asset_folder_patterns}}
**Scene/Level Organization:**
{{scene_organization}}
Any organization rules agents must follow?"
**Naming Conventions:**
"Your naming patterns:
**Script/Class Names:**
{{class_naming_patterns}}
**Asset Names:**
{{asset_naming_patterns}}
**Variable/Method Names:**
{{variable_naming_patterns}}
Any other naming rules?"
### 5. Testing Rules
Focus on testing patterns that ensure consistency:
**Test Structure Rules:**
"Your testing setup shows these patterns:
**Test Organization:**
{{test_file_organization}}
**Test Categories:**
{{unit_vs_integration_boundaries}}
**Mocking Patterns:**
{{mock_usage_conventions}}
**Play Mode Testing:**
{{play_mode_test_patterns}}
Are there testing rules agents should always follow?"
### 6. Platform & Build Rules
Document platform-specific requirements:
**Target Platforms:**
"Your platform configuration:
**Primary Platform:**
{{primary_platform}}
**Platform-Specific Code:**
{{platform_conditional_patterns}}
**Build Configurations:**
{{build_config_rules}}
**Input Handling:**
{{input_abstraction_patterns}}
Any platform rules agents must know?"
### 7. Critical Don't-Miss Rules
Identify rules that prevent common mistakes:
**Anti-Patterns to Avoid:**
"Based on your codebase, here are critical things agents must NOT do:
{{critical_anti_patterns_with_examples}}
**Edge Cases:**
{{specific_edge_cases_agents_should_handle}}
**Common Gotchas:**
{{engine_specific_gotchas}}
**Performance Traps:**
{{performance_patterns_to_avoid}}
Are there other 'gotchas' agents should know about?"
### 8. Generate Context Content
For each category, prepare lean content for the project context file:
#### Content Structure:
```markdown
## Technology Stack & Versions
{{concise_technology_list_with_exact_versions}}
## Critical Implementation Rules
### Engine-Specific Rules
{{bullet_points_of_engine_rules}}
### Performance Rules
{{bullet_points_of_performance_requirements}}
### Code Organization Rules
{{bullet_points_of_organization_patterns}}
### Testing Rules
{{bullet_points_of_testing_requirements}}
### Platform & Build Rules
{{bullet_points_of_platform_requirements}}
### Critical Don't-Miss Rules
{{bullet_points_of_anti_patterns_and_gotchas}}
```
### 9. Present Content and Menu
After each category, show the generated rules and present choices:
"I've drafted the {{category_name}} rules for your project context.
**Here's what I'll add:**
[Show the complete markdown content for this category]
**What would you like to do?**
[A] Advanced Elicitation - Explore nuanced rules for this category
[P] Party Mode - Review from different implementation perspectives
[C] Continue - Save these rules and move to next category"
### 10. Handle Menu Selection
#### If 'A' (Advanced Elicitation):
- Execute advanced-elicitation.xml with current category rules
- Process enhanced rules that come back
- Ask user: "Accept these enhanced rules for {{category}}? (y/n)"
- If yes: Update content, then return to A/P/C menu
- If no: Keep original content, then return to A/P/C menu
#### If 'P' (Party Mode):
- Execute party-mode workflow with category rules context
- Process collaborative insights on implementation patterns
- Ask user: "Accept these changes to {{category}} rules? (y/n)"
- If yes: Update content, then return to A/P/C menu
- If no: Keep original content, then return to A/P/C menu
#### If 'C' (Continue):
- Save the current category content to project context file
- Update frontmatter: `sections_completed: [...]`
- Proceed to next category or step-03 if complete
## APPEND TO PROJECT CONTEXT:
When user selects 'C' for a category, append the content directly to `{output_folder}/project-context.md` using the structure from step 8.
## SUCCESS METRICS:
- All critical technology versions accurately documented
- Engine-specific rules cover unobvious patterns
- Performance rules capture project-specific requirements
- Code organization rules maintain project standards
- Testing rules ensure consistent test quality
- Platform rules prevent cross-platform issues
- Content is lean and optimized for LLM context
- A/P/C menu presented and handled correctly for each category
## FAILURE MODES:
- Including obvious rules that agents already know
- Making content too verbose for LLM context efficiency
- Missing critical anti-patterns or edge cases
- Not getting user validation for each rule category
- Not documenting exact versions and configurations
- Not presenting A/P/C menu after content generation
## NEXT STEP:
After completing all rule categories and user selects 'C' for the final category, load `./step-03-complete.md` to finalize the project context file.
Remember: Do NOT proceed to step-03 until all categories are complete and user explicitly selects 'C' for each!

View File

@ -0,0 +1,279 @@
# Step 3: Context Completion & Finalization
## MANDATORY EXECUTION RULES (READ FIRST):
- NEVER generate content without user input
- ALWAYS treat this as collaborative completion between technical peers
- YOU ARE A FACILITATOR, not a content generator
- FOCUS on finalizing a lean, LLM-optimized project context
- ENSURE all critical rules are captured and actionable
- ABSOLUTELY NO TIME ESTIMATES
## EXECUTION PROTOCOLS:
- Show your analysis before taking any action
- Review and optimize content for LLM context efficiency
- Update frontmatter with completion status
- NO MORE STEPS - this is the final step
## CONTEXT BOUNDARIES:
- All rule categories from step-2 are complete
- Technology stack and versions are documented
- Focus on final review, optimization, and completion
- Ensure the context file is ready for AI agent consumption
## YOUR TASK:
Complete the project context file, optimize it for LLM efficiency, and provide guidance for usage and maintenance.
## COMPLETION SEQUENCE:
### 1. Review Complete Context File
Read the entire project context file and analyze:
**Content Analysis:**
- Total length and readability for LLMs
- Clarity and specificity of rules
- Coverage of all critical areas
- Actionability of each rule
**Structure Analysis:**
- Logical organization of sections
- Consistency of formatting
- Absence of redundant or obvious information
- Optimization for quick scanning
### 2. Optimize for LLM Context
Ensure the file is lean and efficient:
**Content Optimization:**
- Remove any redundant rules or obvious information
- Combine related rules into concise bullet points
- Use specific, actionable language
- Ensure each rule provides unique value
**Formatting Optimization:**
- Use consistent markdown formatting
- Implement clear section hierarchy
- Ensure scannability with strategic use of bolding
- Maintain readability while maximizing information density
### 3. Final Content Structure
Ensure the final structure follows this optimized format:
```markdown
# Project Context for AI Agents
_This file contains critical rules and patterns that AI agents must follow when implementing game code in this project. Focus on unobvious details that agents might otherwise miss._
---
## Technology Stack & Versions
{{concise_technology_list}}
## Critical Implementation Rules
### Engine-Specific Rules
{{engine_rules}}
### Performance Rules
{{performance_requirements}}
### Code Organization Rules
{{organization_patterns}}
### Testing Rules
{{testing_requirements}}
### Platform & Build Rules
{{platform_requirements}}
### Critical Don't-Miss Rules
{{anti_patterns_and_gotchas}}
---
## Usage Guidelines
**For AI Agents:**
- Read this file before implementing any game code
- Follow ALL rules exactly as documented
- When in doubt, prefer the more restrictive option
- Update this file if new patterns emerge
**For Humans:**
- Keep this file lean and focused on agent needs
- Update when technology stack changes
- Review quarterly for outdated rules
- Remove rules that become obvious over time
Last Updated: {{date}}
```
### 4. Present Completion Summary
Based on user skill level, present the completion:
**Expert Mode:**
"Project context complete. Optimized for LLM consumption with {{rule_count}} critical rules across {{section_count}} sections.
File saved to: `{output_folder}/project-context.md`
Ready for AI agent integration."
**Intermediate Mode:**
"Your project context is complete and optimized for AI agents!
**What we created:**
- {{rule_count}} critical implementation rules
- Technology stack with exact versions
- Engine-specific patterns and conventions
- Performance and optimization guidelines
- Testing and platform requirements
**Key benefits:**
- AI agents will implement consistently with your standards
- Reduced context switching and implementation errors
- Clear guidance for unobvious project requirements
**Next steps:**
- AI agents should read this file before implementing
- Update as your project evolves
- Review periodically for optimization"
**Beginner Mode:**
"Excellent! Your project context guide is ready!
**What this does:**
Think of this as a 'rules of the road' guide for AI agents working on your game. It ensures they all follow the same patterns and avoid common mistakes.
**What's included:**
- Exact engine and technology versions to use
- Critical coding rules they might miss
- Performance and optimization standards
- Testing and platform requirements
**How AI agents use it:**
They read this file before writing any code, ensuring everything they create follows your project's standards perfectly.
Your project context is saved and ready to help agents implement consistently!"
### 5. Final File Updates
Update the project context file with completion information:
**Frontmatter Update:**
```yaml
---
project_name: '{{project_name}}'
user_name: '{{user_name}}'
date: '{{date}}'
sections_completed:
['technology_stack', 'engine_rules', 'performance_rules', 'organization_rules', 'testing_rules', 'platform_rules', 'anti_patterns']
status: 'complete'
rule_count: { { total_rules } }
optimized_for_llm: true
---
```
**Add Usage Section:**
Append the usage guidelines from step 3 to complete the document.
### 6. Completion Validation
Final checks before completion:
**Content Validation:**
- All critical technology versions documented
- Engine-specific rules are specific and actionable
- Performance rules capture project requirements
- Code organization rules maintain standards
- Testing rules ensure consistency
- Platform rules prevent cross-platform issues
- Anti-pattern rules prevent common mistakes
**Format Validation:**
- Content is lean and optimized for LLMs
- Structure is logical and scannable
- No redundant or obvious information
- Consistent formatting throughout
### 7. Completion Message
Present final completion to user:
"**Project Context Generation Complete!**
Your optimized project context file is ready at:
`{output_folder}/project-context.md`
**Context Summary:**
- {{rule_count}} critical rules for AI agents
- {{section_count}} comprehensive sections
- Optimized for LLM context efficiency
- Ready for immediate agent integration
**Key Benefits:**
- Consistent implementation across all AI agents
- Reduced common mistakes and edge cases
- Clear guidance for project-specific patterns
- Minimal LLM context usage
**Next Steps:**
1. AI agents will automatically read this file when implementing
2. Update this file when your technology stack or patterns evolve
3. Review quarterly to optimize and remove outdated rules
Your project context will help ensure high-quality, consistent game implementation across all development work. Great work capturing your project's critical implementation requirements!"
## SUCCESS METRICS:
- Complete project context file with all critical rules
- Content optimized for LLM context efficiency
- All technology versions and patterns documented
- File structure is logical and scannable
- Usage guidelines included for agents and humans
- Frontmatter properly updated with completion status
- User provided with clear next steps and benefits
## FAILURE MODES:
- Final content is too verbose for LLM consumption
- Missing critical implementation rules or patterns
- Not optimizing content for agent readability
- Not providing clear usage guidelines
- Frontmatter not properly updated
- Not validating file completion before ending
## WORKFLOW COMPLETE:
This is the final step of the Generate Project Context workflow. The user now has a comprehensive, optimized project context file that will ensure consistent, high-quality game implementation across all AI agents working on the project.
The project context file serves as the critical "rules of the road" that agents need to implement game code consistently with the project's standards and patterns.

View File

@ -0,0 +1,48 @@
---
name: generate-project-context
description: Creates a concise project-context.md file with critical rules and patterns that AI agents must follow when implementing game code. Optimized for LLM context efficiency.
---
# Generate Project Context Workflow
**Goal:** Create a concise, optimized `project-context.md` file containing critical rules, patterns, and guidelines that AI agents must follow when implementing game code. This file focuses on unobvious details that LLMs need to be reminded of.
**Your Role:** You are a technical facilitator working with a peer to capture the essential implementation rules that will ensure consistent, high-quality game code generation across all AI agents working on the project.
---
## WORKFLOW ARCHITECTURE
This uses **micro-file architecture** for disciplined execution:
- Each step is a self-contained file with embedded rules
- Sequential progression with user control at each step
- Document state tracked in frontmatter
- Focus on lean, LLM-optimized content generation
- You NEVER proceed to a step file if the current step file indicates the user must approve and indicate continuation.
---
## INITIALIZATION
### Configuration Loading
Load config from `{project-root}/_bmad/bmgd/config.yaml` and resolve:
- `project_name`, `output_folder`, `user_name`
- `communication_language`, `document_output_language`, `game_dev_experience`
- `date` as system-generated current datetime
### Paths
- `installed_path` = `{project-root}/_bmad/bmgd/workflows/3-technical/generate-project-context`
- `template_path` = `{installed_path}/project-context-template.md`
- `output_file` = `{output_folder}/project-context.md`
---
## EXECUTION
Load and execute `steps/step-01-discover.md` to begin the workflow.
**Note:** Input document discovery and initialization protocols are handled in step-01-discover.md.

View File

@ -8,11 +8,44 @@ const ui = new UI();
module.exports = {
command: 'install',
description: 'Install BMAD Core agents and tools',
options: [],
description: `Install BMAD Core agents and tools
Examples:
bmad install # Interactive installation
bmad install -y # Non-interactive with defaults
bmad install -y --user-name=Alice --skill-level=advanced
bmad install -y --team=fullstack # Install fullstack team
bmad install -y --team=fullstack --agents=+dev # Add dev to fullstack team
bmad install -y --agents=dev,architect,pm # Selective agents
bmad install -y --profile=minimal # Minimal profile
bmad install -y --workflows=create-prd,dev-story # Selective workflows
Special Values:
--agents=all, --agents=none, --agents=minimal
--workflows=all, --workflows=none, --workflows=minimal
Modifiers:
--agents=+dev Add agent to team/profile selection
--agents=-dev Remove agent from team/profile selection
Available Teams: fullstack, gamedev
Available Profiles: minimal, full, solo-dev, team`,
options: [
['-y, --non-interactive', 'Run without prompts, use defaults'],
['--user-name <name>', 'User name for configuration'],
['--skill-level <level>', 'User skill level (beginner, intermediate, advanced)'],
['--output-folder <path>', 'Output folder path for BMAD artifacts'],
['--modules <list>', 'Comma-separated list of modules to install (e.g., core,bmm)'],
['--agents <list>', 'Comma-separated list of agents to install (e.g., dev,architect,pm)'],
['--workflows <list>', 'Comma-separated list of workflows to install'],
['--team <name>', 'Install predefined team bundle (e.g., fullstack, gamedev)'],
['--profile <name>', 'Installation profile (minimal, full, solo-dev)'],
['--communication-language <lang>', 'Language for agent communication (default: English)'],
['--document-language <lang>', 'Language for generated documents (default: English)'],
],
action: async (options) => {
try {
const config = await ui.promptInstall();
const config = await ui.promptInstall(options);
// Handle cancel
if (config.actionType === 'cancel') {

View File

@ -5,6 +5,7 @@ const chalk = require('chalk');
const inquirer = require('inquirer');
const { getProjectRoot, getModulePath } = require('../../../lib/project-root');
const { CLIUtils } = require('../../../lib/cli-utils');
const { getEnvironmentDefaults, resolveValue } = require('./env-resolver');
class ConfigCollector {
constructor() {
@ -792,6 +793,195 @@ class ConfigCollector {
}
}
/**
* Collect configuration for a module in non-interactive mode
* @param {string} moduleName - Module name
* @param {string} projectDir - Target project directory
* @param {Object} cliOptions - CLI options passed by user
* @returns {Object} Collected config for the module
*/
async collectModuleConfigNonInteractive(moduleName, projectDir, cliOptions = {}) {
this.currentProjectDir = projectDir;
// Load existing config if not already loaded
if (!this.existingConfig) {
await this.loadExistingConfig(projectDir);
}
// Initialize allAnswers if not already initialized
if (!this.allAnswers) {
this.allAnswers = {};
}
// Get environment defaults
const envDefaults = getEnvironmentDefaults();
// Try to load module config schema
let installerConfigPath = null;
let moduleConfigPath = null;
if (this.customModulePaths && this.customModulePaths.has(moduleName)) {
const customPath = this.customModulePaths.get(moduleName);
installerConfigPath = path.join(customPath, '_module-installer', 'module.yaml');
moduleConfigPath = path.join(customPath, 'module.yaml');
} else {
installerConfigPath = path.join(getModulePath(moduleName), '_module-installer', 'module.yaml');
moduleConfigPath = path.join(getModulePath(moduleName), 'module.yaml');
}
// If not found, try to find via module manager
if (!(await fs.pathExists(installerConfigPath)) && !(await fs.pathExists(moduleConfigPath))) {
const { ModuleManager } = require('../modules/manager');
const moduleManager = new ModuleManager();
const moduleSourcePath = await moduleManager.findModuleSource(moduleName);
if (moduleSourcePath) {
installerConfigPath = path.join(moduleSourcePath, '_module-installer', 'module.yaml');
moduleConfigPath = path.join(moduleSourcePath, 'module.yaml');
}
}
let configPath = null;
if (await fs.pathExists(moduleConfigPath)) {
configPath = moduleConfigPath;
} else if (await fs.pathExists(installerConfigPath)) {
configPath = installerConfigPath;
} else {
// No config for this module - use existing if available
if (this.existingConfig && this.existingConfig[moduleName]) {
this.collectedConfig[moduleName] = { ...this.existingConfig[moduleName] };
} else {
this.collectedConfig[moduleName] = {};
}
return this.collectedConfig[moduleName];
}
const configContent = await fs.readFile(configPath, 'utf8');
const moduleConfig = yaml.parse(configContent);
if (!moduleConfig) {
this.collectedConfig[moduleName] = {};
return this.collectedConfig[moduleName];
}
// Initialize module config
if (!this.collectedConfig[moduleName]) {
this.collectedConfig[moduleName] = {};
}
// Process each config item
const configKeys = Object.keys(moduleConfig).filter((key) => key !== 'prompt');
for (const key of configKeys) {
const item = moduleConfig[key];
// Skip if not a config object
if (!item || typeof item !== 'object') {
continue;
}
let value = null;
// Resolution order: CLI → ENV → existing → default → hardcoded
if (moduleName === 'core') {
// Core module has special mappings
switch (key) {
case 'user_name': {
value = resolveValue(cliOptions.userName, null, envDefaults.userName);
break;
}
case 'user_skill_level': {
value = resolveValue(cliOptions.skillLevel, null, item.default || 'intermediate');
break;
}
case 'communication_language': {
value = resolveValue(cliOptions.communicationLanguage, null, envDefaults.communicationLanguage);
break;
}
case 'document_output_language': {
value = resolveValue(cliOptions.documentLanguage, null, envDefaults.documentLanguage);
break;
}
case 'output_folder': {
value = resolveValue(cliOptions.outputFolder, null, item.default);
break;
}
default: {
if (item.default !== undefined) {
value = item.default;
}
}
}
} else {
// For other modules, use defaults
if (item.default !== undefined) {
value = item.default;
} else if (this.existingConfig && this.existingConfig[moduleName]) {
value = this.existingConfig[moduleName][key];
}
}
// Process result template if present
let result;
if (item.result && typeof item.result === 'string') {
result = item.result;
if (typeof value === 'string') {
result = result.replace('{value}', value);
} else if (value !== undefined && value !== null) {
result = result.replace('{value}', String(value));
}
// Replace references to other config values
result = result.replaceAll(/{([^}]+)}/g, (match, configKey) => {
if (configKey === 'project-root' || configKey === 'value') {
return match;
}
// Look in collected config
let configValue = this.collectedConfig[moduleName]?.[configKey];
if (!configValue) {
for (const mod of Object.keys(this.collectedConfig)) {
if (mod !== '_meta' && this.collectedConfig[mod]?.[configKey]) {
configValue = this.collectedConfig[mod][configKey];
if (typeof configValue === 'string' && configValue.includes('{project-root}/')) {
configValue = configValue.replace('{project-root}/', '');
}
break;
}
}
}
return configValue || match;
});
} else if (item.result) {
result = value;
} else {
result = value;
}
// Store the result
this.collectedConfig[moduleName][key] = result;
this.allAnswers[`${moduleName}_${key}`] = result;
}
// Copy existing values for fields not in schema
if (this.existingConfig && this.existingConfig[moduleName]) {
for (const [key, value] of Object.entries(this.existingConfig[moduleName])) {
if (this.collectedConfig[moduleName][key] === undefined) {
this.collectedConfig[moduleName][key] = value;
this.allAnswers[`${moduleName}_${key}`] = value;
}
}
}
return this.collectedConfig[moduleName];
}
/**
* Replace placeholders in a string with collected config values
* @param {string} str - String with placeholders

View File

@ -0,0 +1,120 @@
const os = require('node:os');
/**
* Environment Variable Resolver
*
* Resolves configuration values from environment variables
* with fallbacks to system defaults.
*/
/**
* Get user name from environment variables
* Tries USER, USERNAME, LOGNAME in order, falls back to system username or 'User'
* @returns {string} User name
*/
function getUserName() {
// Try common environment variables
const envUser = process.env.USER || process.env.USERNAME || process.env.LOGNAME;
if (envUser) {
return envUser;
}
// Try Node.js os.userInfo()
try {
const userInfo = os.userInfo();
if (userInfo.username) {
return userInfo.username;
}
} catch {
// os.userInfo() can fail in some environments
}
// Final fallback
return 'User';
}
/**
* Get system language from environment variables
* Tries LANG, LC_ALL, falls back to 'English'
* @returns {string} Language name
*/
function getSystemLanguage() {
const lang = process.env.LANG || process.env.LC_ALL;
if (!lang) {
return 'English';
}
// Parse language from locale string (e.g., 'en_US.UTF-8' -> 'English')
const langCode = lang.split('_')[0].toLowerCase();
// Map common language codes to full names
const languageMap = {
en: 'English',
es: 'Spanish',
fr: 'French',
de: 'German',
it: 'Italian',
pt: 'Portuguese',
ru: 'Russian',
ja: 'Japanese',
zh: 'Chinese',
ko: 'Korean',
ar: 'Arabic',
hi: 'Hindi',
};
return languageMap[langCode] || 'English';
}
/**
* Get home directory from environment
* @returns {string} Home directory path
*/
function getHomeDirectory() {
return process.env.HOME || process.env.USERPROFILE || os.homedir();
}
/**
* Resolve a config value with priority: CLI > ENV > default
* @param {*} cliValue - Value from CLI argument
* @param {string} envVar - Environment variable name to check
* @param {*} defaultValue - Default value if neither CLI nor ENV is set
* @returns {*} Resolved value
*/
function resolveValue(cliValue, envVar, defaultValue) {
// CLI value has highest priority
if (cliValue !== undefined && cliValue !== null) {
return cliValue;
}
// Try environment variable
if (envVar && process.env[envVar]) {
return process.env[envVar];
}
// Use default
return defaultValue;
}
/**
* Get all environment-based defaults
* @returns {Object} Default config values from environment
*/
function getEnvironmentDefaults() {
return {
userName: getUserName(),
communicationLanguage: getSystemLanguage(),
documentLanguage: getSystemLanguage(),
homeDirectory: getHomeDirectory(),
};
}
module.exports = {
getUserName,
getSystemLanguage,
getHomeDirectory,
resolveValue,
getEnvironmentDefaults,
};

View File

@ -1041,6 +1041,9 @@ class Installer {
const manifestStats = await manifestGen.generateManifests(bmadDir, allModulesForManifest, [...this.installedFiles], {
ides: config.ides || [],
preservedModules: modulesForCsvPreserve, // Scan these from installed bmad/ dir
selectedAgents: config.cliOptions?.agents || null,
selectedWorkflows: config.cliOptions?.workflows || null,
installMode: config.cliOptions ? 'non-interactive' : 'interactive',
});
// Custom modules are now included in the main modules list - no separate tracking needed

View File

@ -65,11 +65,20 @@ class ManifestGenerator {
// Filter out any undefined/null values from IDE list
this.selectedIdes = resolvedIdes.filter((ide) => ide && typeof ide === 'string');
// Get filtering options from options
const selectedAgents = options.selectedAgents || null;
const selectedWorkflows = options.selectedWorkflows || null;
// Store installation mode and options for manifest
this.installMode = options.installMode || 'interactive';
this.selectedAgentsList = selectedAgents;
this.selectedWorkflowsList = selectedWorkflows;
// Collect workflow data
await this.collectWorkflows(selectedModules);
await this.collectWorkflows(selectedModules, selectedWorkflows);
// Collect agent data - use updatedModules which includes all installed modules
await this.collectAgents(this.updatedModules);
await this.collectAgents(this.updatedModules, selectedAgents);
// Collect task data
await this.collectTasks(this.updatedModules);
@ -100,8 +109,10 @@ class ManifestGenerator {
/**
* Collect all workflows from core and selected modules
* Scans the INSTALLED bmad directory, not the source
* @param {Array} selectedModules - Modules to scan for workflows
* @param {Array|null} selectedWorkflows - Optional array of workflow names to filter by
*/
async collectWorkflows(selectedModules) {
async collectWorkflows(selectedModules, selectedWorkflows = null) {
this.workflows = [];
// Use updatedModules which already includes deduplicated 'core' + selectedModules
@ -113,6 +124,50 @@ class ManifestGenerator {
this.workflows.push(...moduleWorkflows);
}
}
// Apply filtering if selectedWorkflows is provided
if (selectedWorkflows && Array.isArray(selectedWorkflows) && selectedWorkflows.length > 0) {
this.workflows = this.filterWorkflows(this.workflows, selectedWorkflows);
}
}
/**
* Filter workflows by name matching (supports wildcards)
* @param {Array} workflows - Array of workflow objects
* @param {Array} selectedWorkflows - Array of workflow names to filter by (supports * wildcard)
* @returns {Array} Filtered workflows
*/
filterWorkflows(workflows, selectedWorkflows) {
if (!selectedWorkflows || selectedWorkflows.length === 0) {
return workflows;
}
// Check for special values
if (selectedWorkflows.includes('all')) {
return workflows;
}
if (selectedWorkflows.includes('none')) {
return [];
}
return workflows.filter((workflow) => {
const workflowName = workflow.name;
return selectedWorkflows.some((pattern) => {
// Exact match
if (pattern === workflowName) {
return true;
}
// Wildcard matching: create-* matches create-prd, create-tech-spec, etc.
if (pattern.includes('*')) {
const regex = new RegExp('^' + pattern.replaceAll('*', '.*') + '$');
return regex.test(workflowName);
}
return false;
});
});
}
/**
@ -197,8 +252,10 @@ class ManifestGenerator {
/**
* Collect all agents from core and selected modules
* Scans the INSTALLED bmad directory, not the source
* @param {Array} selectedModules - Modules to scan for agents
* @param {Array|null} selectedAgents - Optional array of agent names to filter by
*/
async collectAgents(selectedModules) {
async collectAgents(selectedModules, selectedAgents = null) {
this.agents = [];
// Use updatedModules which already includes deduplicated 'core' + selectedModules
@ -224,6 +281,50 @@ class ManifestGenerator {
this.agents.push(...standaloneAgents);
}
}
// Apply filtering if selectedAgents is provided
if (selectedAgents && Array.isArray(selectedAgents) && selectedAgents.length > 0) {
this.agents = this.filterAgents(this.agents, selectedAgents);
}
}
/**
* Filter agents by name matching (supports wildcards)
* @param {Array} agents - Array of agent objects
* @param {Array} selectedAgents - Array of agent names to filter by (supports * wildcard)
* @returns {Array} Filtered agents
*/
filterAgents(agents, selectedAgents) {
if (!selectedAgents || selectedAgents.length === 0) {
return agents;
}
// Check for special values
if (selectedAgents.includes('all')) {
return agents;
}
if (selectedAgents.includes('none')) {
return [];
}
return agents.filter((agent) => {
const agentName = agent.name;
return selectedAgents.some((pattern) => {
// Exact match
if (pattern === agentName) {
return true;
}
// Wildcard matching: dev* matches dev, dev-story, etc.
if (pattern.includes('*')) {
const regex = new RegExp('^' + pattern.replaceAll('*', '.*') + '$');
return regex.test(agentName);
}
return false;
});
});
}
/**
@ -462,11 +563,20 @@ class ManifestGenerator {
version: packageJson.version,
installDate: new Date().toISOString(),
lastUpdated: new Date().toISOString(),
installMode: this.installMode || 'interactive',
},
modules: this.modules, // Include ALL modules (standard and custom)
ides: this.selectedIdes,
};
// Add selective installation info if filters were applied
if (this.selectedAgentsList && this.selectedAgentsList.length > 0) {
manifest.selectedAgents = this.selectedAgentsList;
}
if (this.selectedWorkflowsList && this.selectedWorkflowsList.length > 0) {
manifest.selectedWorkflows = this.selectedWorkflowsList;
}
// Clean the manifest to remove any non-serializable values
const cleanManifest = structuredClone(manifest);

View File

@ -0,0 +1,186 @@
const { getProfile } = require('../profiles/definitions');
/**
* CLI Options Parser
*
* Parses and normalizes CLI options for non-interactive installation.
* Handles profiles, comma-separated lists, special values, and validation.
*/
/**
* Parse comma-separated list into array
* @param {string|undefined} value - Comma-separated string or undefined
* @returns {string[]|null} Array of trimmed values or null if undefined
*/
function parseList(value) {
if (!value) {
return null;
}
if (typeof value !== 'string') {
return null;
}
return value
.split(',')
.map((item) => item.trim())
.filter((item) => item.length > 0);
}
/**
* Check if value is a special keyword
* @param {string|string[]|null} value - Value to check
* @returns {boolean} True if special keyword (all, none, minimal)
*/
function isSpecialValue(value) {
if (Array.isArray(value) && value.length === 1) {
value = value[0];
}
return value === 'all' || value === 'none' || value === 'minimal';
}
/**
* Separate additive (+) and subtractive (-) modifiers from list
* @param {string[]} list - Array of items, some may have +/- prefix
* @returns {Object} { base: [], add: [], remove: [] }
*/
function separateModifiers(list) {
const result = {
base: [],
add: [],
remove: [],
};
if (!list || !Array.isArray(list)) {
return result;
}
for (const item of list) {
if (item.startsWith('+')) {
result.add.push(item.slice(1));
} else if (item.startsWith('-')) {
result.remove.push(item.slice(1));
} else {
result.base.push(item);
}
}
return result;
}
/**
* Apply modifiers to a base list (additive/subtractive)
* @param {string[]} baseList - Base list of items
* @param {string[]} add - Items to add
* @param {string[]} remove - Items to remove
* @returns {string[]} Modified list
*/
function applyModifiers(baseList, add = [], remove = []) {
let result = [...baseList];
// Add items
for (const item of add) {
if (!result.includes(item)) {
result.push(item);
}
}
// Remove items
for (const item of remove) {
result = result.filter((i) => i !== item);
}
return result;
}
/**
* Parse and normalize CLI options
* @param {Object} cliOptions - Raw CLI options from commander
* @returns {Object} Normalized options
*/
function parseOptions(cliOptions) {
const normalized = {
nonInteractive: cliOptions.nonInteractive || false,
userName: cliOptions.userName,
skillLevel: cliOptions.skillLevel,
outputFolder: cliOptions.outputFolder,
communicationLanguage: cliOptions.communicationLanguage,
documentLanguage: cliOptions.documentLanguage,
modules: parseList(cliOptions.modules),
agents: parseList(cliOptions.agents),
workflows: parseList(cliOptions.workflows),
team: cliOptions.team,
profile: cliOptions.profile,
};
// Expand profile if provided
if (normalized.profile) {
const profile = getProfile(normalized.profile);
if (!profile) {
throw new Error(`Unknown profile: ${normalized.profile}. Valid profiles: minimal, full, solo-dev, team`);
}
// Use profile values as defaults when CLI options not provided
// Ensure strings like 'all' are wrapped in arrays for consistency
if (!normalized.modules) {
normalized.modules = Array.isArray(profile.modules) ? profile.modules : [profile.modules];
}
if (!normalized.agents) {
normalized.agents = Array.isArray(profile.agents) ? profile.agents : [profile.agents];
}
if (!normalized.workflows) {
normalized.workflows = Array.isArray(profile.workflows) ? profile.workflows : [profile.workflows];
}
}
return normalized;
}
/**
* Validate parsed options for conflicts and errors
* @param {Object} options - Parsed options
* @returns {Object} { valid: boolean, errors: string[] }
*/
function validateOptions(options) {
const errors = [];
// Validate skill level
if (options.skillLevel) {
const validLevels = ['beginner', 'intermediate', 'advanced'];
if (!validLevels.includes(options.skillLevel.toLowerCase())) {
errors.push(`Invalid skill level: ${options.skillLevel}. Valid values: beginner, intermediate, advanced`);
}
}
// Validate profile
if (options.profile) {
const validProfiles = ['minimal', 'full', 'solo-dev', 'team'];
if (!validProfiles.includes(options.profile.toLowerCase())) {
errors.push(`Invalid profile: ${options.profile}. Valid values: minimal, full, solo-dev, team`);
}
}
// Check for empty selections
if (options.agents && Array.isArray(options.agents) && options.agents.length === 0) {
errors.push('Agents list cannot be empty');
}
if (options.workflows && Array.isArray(options.workflows) && options.workflows.length === 0) {
errors.push('Workflows list cannot be empty');
}
return {
valid: errors.length === 0,
errors,
};
}
module.exports = {
parseList,
isSpecialValue,
separateModifiers,
applyModifiers,
parseOptions,
validateOptions,
};

View File

@ -0,0 +1,96 @@
/**
* Installation Profile Definitions
*
* Profiles are pre-defined combinations of modules, agents, and workflows
* for common use cases. Users can select a profile with --profile=<name>
* and override specific selections with CLI flags.
*/
const PROFILES = {
minimal: {
name: 'minimal',
description: 'Minimal installation - core + dev agent + essential workflows',
modules: ['core'],
agents: ['dev'],
workflows: ['create-tech-spec', 'quick-dev'],
},
full: {
name: 'full',
description: 'Full installation - all modules, agents, and workflows',
modules: 'all',
agents: 'all',
workflows: 'all',
},
'solo-dev': {
name: 'solo-dev',
description: 'Single developer setup - dev tools and planning workflows',
modules: ['core', 'bmm'],
agents: ['dev', 'architect', 'analyst', 'tech-writer'],
workflows: ['create-tech-spec', 'quick-dev', 'dev-story', 'code-review', 'create-prd', 'create-architecture'],
},
team: {
name: 'team',
description: 'Team collaboration setup - planning and execution workflows',
modules: ['core', 'bmm'],
agents: ['dev', 'architect', 'pm', 'sm', 'analyst', 'ux-designer'],
workflows: [
'create-product-brief',
'create-prd',
'create-architecture',
'create-epics-and-stories',
'sprint-planning',
'create-story',
'dev-story',
'code-review',
'workflow-init',
],
},
};
/**
* Get a profile by name
* @param {string} name - Profile name (minimal, full, solo-dev, team)
* @returns {Object|null} Profile definition or null if not found
*/
function getProfile(name) {
if (!name) {
return null;
}
const profile = PROFILES[name.toLowerCase()];
if (!profile) {
return null;
}
// Return a copy to prevent mutation
return { ...profile };
}
/**
* Get all available profile names
* @returns {string[]} Array of profile names
*/
function getProfileNames() {
return Object.keys(PROFILES);
}
/**
* Get profile descriptions for help text
* @returns {Object} Map of profile name to description
*/
function getProfileDescriptions() {
const descriptions = {};
for (const [name, profile] of Object.entries(PROFILES)) {
descriptions[name] = profile.description;
}
return descriptions;
}
module.exports = {
getProfile,
getProfileNames,
getProfileDescriptions,
};

View File

@ -0,0 +1,179 @@
const fs = require('node:fs');
const path = require('node:path');
const yaml = require('yaml');
const { glob } = require('glob');
/**
* Team Loader
*
* Discovers and loads team bundles from module definitions.
* Teams are predefined collections of agents and workflows for common use cases.
*/
/**
* Discover all available teams across modules
* @param {string} projectRoot - Project root directory
* @returns {Promise<Object[]>} Array of team metadata { name, module, path, description }
*/
async function discoverTeams(projectRoot) {
const teams = [];
const pattern = path.join(projectRoot, 'src/modules/*/teams/team-*.yaml');
try {
const files = await glob(pattern, { absolute: true });
for (const filePath of files) {
try {
const content = fs.readFileSync(filePath, 'utf8');
const teamData = yaml.parse(content);
// Extract team name from filename (team-fullstack.yaml -> fullstack)
const filename = path.basename(filePath);
const teamName = filename.replace(/^team-/, '').replace(/\.yaml$/, '');
// Extract module name from path
const moduleName = path.basename(path.dirname(path.dirname(filePath)));
teams.push({
name: teamName,
module: moduleName,
path: filePath,
description: teamData.bundle?.description || 'No description',
bundleName: teamData.bundle?.name || teamName,
icon: teamData.bundle?.icon || '👥',
});
} catch (error) {
// Skip files that can't be parsed
console.warn(`Warning: Could not parse team file ${filePath}: ${error.message}`);
}
}
return teams;
} catch {
// If glob fails, return empty array
return [];
}
}
/**
* Load a specific team by name
* @param {string} teamName - Team name (e.g., 'fullstack', 'gamedev')
* @param {string} projectRoot - Project root directory
* @returns {Promise<Object>} Team data with metadata
*/
async function loadTeam(teamName, projectRoot) {
if (!teamName) {
throw new Error('Team name is required');
}
// Discover all teams
const teams = await discoverTeams(projectRoot);
// Find matching team
const team = teams.find((t) => t.name.toLowerCase() === teamName.toLowerCase());
if (!team) {
// Provide helpful error with suggestions
const availableTeams = teams.map((t) => t.name).join(', ');
throw new Error(`Team '${teamName}' not found. Available teams: ${availableTeams || 'none'}`);
}
// Load full team definition
const content = fs.readFileSync(team.path, 'utf8');
const teamData = yaml.parse(content);
return {
name: team.name,
module: team.module,
description: team.description,
bundleName: team.bundleName,
icon: team.icon,
agents: teamData.agents || [],
workflows: teamData.workflows || [],
party: teamData.party,
};
}
/**
* Expand team definition to full agents and workflows list
* @param {string} teamName - Team name
* @param {string} projectRoot - Project root directory
* @returns {Promise<Object>} { agents: [], workflows: [], module: string }
*/
async function expandTeam(teamName, projectRoot) {
const team = await loadTeam(teamName, projectRoot);
return {
agents: team.agents || [],
workflows: team.workflows || [],
module: team.module,
description: team.description,
};
}
/**
* Apply modifiers to team selections (additive/subtractive)
* @param {Object} team - Team expansion result
* @param {string[]} agentModifiers - Agent modifiers (+agent, -agent)
* @param {string[]} workflowModifiers - Workflow modifiers (+workflow, -workflow)
* @returns {Object} Modified team with updated agents/workflows
*/
function applyTeamModifiers(team, agentModifiers = [], workflowModifiers = []) {
const result = {
...team,
agents: [...team.agents],
workflows: [...team.workflows],
};
// Parse and apply agent modifiers
for (const modifier of agentModifiers) {
if (modifier.startsWith('+')) {
const agent = modifier.slice(1);
if (!result.agents.includes(agent)) {
result.agents.push(agent);
}
} else if (modifier.startsWith('-')) {
const agent = modifier.slice(1);
result.agents = result.agents.filter((a) => a !== agent);
}
}
// Parse and apply workflow modifiers
for (const modifier of workflowModifiers) {
if (modifier.startsWith('+')) {
const workflow = modifier.slice(1);
if (!result.workflows.includes(workflow)) {
result.workflows.push(workflow);
}
} else if (modifier.startsWith('-')) {
const workflow = modifier.slice(1);
result.workflows = result.workflows.filter((w) => w !== workflow);
}
}
return result;
}
/**
* Get team descriptions for help text
* @param {string} projectRoot - Project root directory
* @returns {Promise<Object>} Map of team name to description
*/
async function getTeamDescriptions(projectRoot) {
const teams = await discoverTeams(projectRoot);
const descriptions = {};
for (const team of teams) {
descriptions[team.name] = team.description;
}
return descriptions;
}
module.exports = {
discoverTeams,
loadTeam,
expandTeam,
applyTeamModifiers,
getTeamDescriptions,
};

View File

@ -12,9 +12,15 @@ const { CustomHandler } = require('../installers/lib/custom/handler');
class UI {
/**
* Prompt for installation configuration
* @param {Object} cliOptions - CLI options for non-interactive mode
* @returns {Object} Installation configuration
*/
async promptInstall() {
async promptInstall(cliOptions = {}) {
// Handle non-interactive mode
if (cliOptions.nonInteractive) {
return await this.buildNonInteractiveConfig(cliOptions);
}
CLIUtils.displayLogo();
// Display changelog link
@ -1457,6 +1463,139 @@ class UI {
return result;
}
/**
* Build non-interactive installation configuration
* @param {Object} cliOptions - CLI options
* @returns {Object} Installation configuration
*/
async buildNonInteractiveConfig(cliOptions) {
const { parseOptions } = require('../installers/lib/core/options-parser');
const { expandTeam, applyTeamModifiers } = require('../installers/lib/teams/team-loader');
const { getProjectRoot } = require('./project-root');
const { getEnvironmentDefaults } = require('../installers/lib/core/env-resolver');
console.log(chalk.cyan('🤖 Running non-interactive installation...\n'));
// Parse and normalize options
const options = parseOptions(cliOptions);
const envDefaults = getEnvironmentDefaults();
// Determine directory
const directory = process.cwd();
// Check for existing installation
const { Installer } = require('../installers/lib/core/installer');
const installer = new Installer();
const { bmadDir, hasExistingInstall } = await installer.findBmadDir(directory);
const actionType = hasExistingInstall ? 'update' : 'install';
console.log(chalk.dim(` Directory: ${directory}`));
console.log(chalk.dim(` Action: ${actionType === 'install' ? 'New installation' : 'Update existing installation'}\n`));
// Determine modules to install
let selectedModules = [];
if (options.team) {
// Team-based installation
console.log(chalk.cyan(`📦 Loading team: ${options.team}...`));
try {
const projectRoot = getProjectRoot();
let teamExpansion = await expandTeam(options.team, projectRoot);
// Apply modifiers if present
if (options.agents || options.workflows) {
const { separateModifiers } = require('../installers/lib/core/options-parser');
const agentMods = options.agents ? separateModifiers(options.agents) : { base: [], add: [], remove: [] };
const workflowMods = options.workflows ? separateModifiers(options.workflows) : { base: [], add: [], remove: [] };
// If base is provided, replace team selections completely
if (agentMods.base.length > 0) {
teamExpansion.agents = agentMods.base;
}
if (workflowMods.base.length > 0) {
teamExpansion.workflows = workflowMods.base;
}
// Apply modifiers
teamExpansion = applyTeamModifiers(
teamExpansion,
[...agentMods.add.map((a) => `+${a}`), ...agentMods.remove.map((a) => `-${a}`)],
[...workflowMods.add.map((w) => `+${w}`), ...workflowMods.remove.map((w) => `-${w}`)],
);
}
options.agents = teamExpansion.agents;
options.workflows = teamExpansion.workflows;
// Determine module from team
if (teamExpansion.module && !selectedModules.includes(teamExpansion.module)) {
selectedModules.push(teamExpansion.module);
}
console.log(chalk.green(` ✓ Team loaded: ${options.team}`));
console.log(chalk.dim(` Agents: ${teamExpansion.agents.join(', ')}`));
if (teamExpansion.workflows && teamExpansion.workflows.length > 0) {
console.log(chalk.dim(` Workflows: ${teamExpansion.workflows.join(', ')}`));
}
console.log('');
} catch (error) {
console.error(chalk.red(` ✗ Failed to load team: ${error.message}`));
process.exit(1);
}
} else if (options.modules) {
// Module-based installation
if (options.modules === 'all' || (Array.isArray(options.modules) && options.modules.includes('all'))) {
selectedModules = ['bmm', 'bmbb', 'cis', 'bmgd'];
} else if (Array.isArray(options.modules)) {
selectedModules = options.modules.filter((m) => m !== 'core');
}
} else if (options.profile) {
// Profile-based installation
const { getProfile } = require('../installers/lib/profiles/definitions');
const profile = getProfile(options.profile);
if (profile.modules === 'all' || (Array.isArray(profile.modules) && profile.modules.includes('all'))) {
selectedModules = ['bmm', 'bmbb', 'cis', 'bmgd'];
} else if (Array.isArray(profile.modules)) {
selectedModules = profile.modules.filter((m) => m !== 'core');
}
} else {
// Default: install bmm
selectedModules = ['bmm'];
}
console.log(chalk.cyan(`📦 Modules: ${selectedModules.length > 0 ? selectedModules.join(', ') : 'core only'}\n`));
// Build core configuration
const coreConfig = {
user_name: options.userName || envDefaults.userName,
user_skill_level: options.skillLevel || 'intermediate',
communication_language: options.communicationLanguage || envDefaults.communicationLanguage,
document_output_language: options.documentLanguage || envDefaults.documentLanguage,
output_folder: options.outputFolder || '_bmad-output',
};
console.log(chalk.cyan('⚙️ Configuration:'));
console.log(chalk.dim(` User: ${coreConfig.user_name}`));
console.log(chalk.dim(` Skill Level: ${coreConfig.user_skill_level}`));
console.log(chalk.dim(` Language: ${coreConfig.communication_language}\n`));
// Return installation configuration
return {
actionType,
directory,
installCore: true,
modules: selectedModules,
ides: ['claude-code'], // Default to Claude Code for non-interactive
skipIde: false,
coreConfig,
customContent: { hasCustomContent: false },
enableAgentVibes: false,
agentVibesInstalled: false,
// Pass through CLI options for downstream use
cliOptions: options,
};
}
}
module.exports = { UI };

View File

@ -0,0 +1,178 @@
#!/bin/bash
# Test script for non-interactive BMAD installation
# Tests various CLI options and validates installation
set -e # Exit on error
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
TEST_DIR="/tmp/bmad-test-$(date +%s)"
echo "🧪 BMAD Non-Interactive Installation Test Suite"
echo "================================================"
echo "Test directory: $TEST_DIR"
echo ""
# Colors for output
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Track test results
TESTS_PASSED=0
TESTS_FAILED=0
# Helper function to run a test
run_test() {
local test_name="$1"
local test_dir="$TEST_DIR/$test_name"
shift
echo -e "${YELLOW}▶ Running: $test_name${NC}"
mkdir -p "$test_dir"
cd "$test_dir"
if "$@"; then
echo -e "${GREEN}✓ PASSED: $test_name${NC}"
TESTS_PASSED=$((TESTS_PASSED + 1))
return 0
else
echo -e "${RED}✗ FAILED: $test_name${NC}"
TESTS_FAILED=$((TESTS_FAILED + 1))
return 1
fi
}
# Helper to verify installation
verify_installation() {
local dir="$1"
local expected_agents="$2" # comma-separated list
local expected_workflows="$3" # comma-separated list
# Check _bmad directory exists
if [ ! -d "$dir/_bmad" ]; then
echo "❌ _bmad directory not found"
return 1
fi
# Check manifest exists
if [ ! -f "$dir/_bmad/_config/manifest.yaml" ]; then
echo "❌ manifest.yaml not found"
return 1
fi
# Check agents CSV if expected agents provided
if [ -n "$expected_agents" ]; then
if [ ! -f "$dir/_bmad/_config/agents.csv" ]; then
echo "❌ agents.csv not found"
return 1
fi
IFS=',' read -ra AGENTS <<< "$expected_agents"
for agent in "${AGENTS[@]}"; do
if ! grep -q "$agent" "$dir/_bmad/_config/agents.csv"; then
echo "❌ Agent '$agent' not found in agents.csv"
return 1
fi
done
fi
# Check workflows CSV if expected workflows provided
if [ -n "$expected_workflows" ]; then
if [ ! -f "$dir/_bmad/_config/workflows.csv" ]; then
echo "❌ workflows.csv not found"
return 1
fi
IFS=',' read -ra WORKFLOWS <<< "$expected_workflows"
for workflow in "${WORKFLOWS[@]}"; do
if ! grep -q "$workflow" "$dir/_bmad/_config/workflows.csv"; then
echo "❌ Workflow '$workflow' not found in workflows.csv"
return 1
fi
done
fi
echo "✓ Installation verified"
return 0
}
# Test 1: Minimal non-interactive installation
run_test "test-01-minimal-install" bash -c "
node $PROJECT_ROOT/tools/bmad-npx-wrapper.js install -y
verify_installation . '' ''
"
# Test 2: Non-interactive with custom user name
run_test "test-02-custom-user" bash -c "
node $PROJECT_ROOT/tools/bmad-npx-wrapper.js install -y --user-name=TestUser
verify_installation . '' ''
grep -q 'user_name: TestUser' _bmad/core/config.yaml
"
# Test 3: Selective agent installation
run_test "test-03-selective-agents" bash -c "
node $PROJECT_ROOT/tools/bmad-npx-wrapper.js install -y --agents=dev,architect
verify_installation . 'dev,architect' ''
"
# Test 4: Selective workflow installation
run_test "test-04-selective-workflows" bash -c "
node $PROJECT_ROOT/tools/bmad-npx-wrapper.js install -y --workflows=create-prd,create-tech-spec
verify_installation . '' 'create-prd,create-tech-spec'
"
# Test 5: Team-based installation (fullstack)
run_test "test-05-team-fullstack" bash -c "
node $PROJECT_ROOT/tools/bmad-npx-wrapper.js install -y --team=fullstack
verify_installation . 'analyst,architect,pm,sm,ux-designer' ''
"
# Test 6: Profile-based installation (minimal)
run_test "test-06-profile-minimal" bash -c "
node $PROJECT_ROOT/tools/bmad-npx-wrapper.js install -y --profile=minimal
verify_installation . 'dev' 'create-tech-spec,quick-dev'
"
# Test 7: Multiple CLI options
run_test "test-07-multiple-options" bash -c "
node $PROJECT_ROOT/tools/bmad-npx-wrapper.js install -y \
--user-name=FullTest \
--skill-level=advanced \
--output-folder=.output \
--agents=dev,architect
verify_installation . 'dev,architect' ''
grep -q 'user_name: FullTest' _bmad/core/config.yaml
grep -q 'user_skill_level: advanced' _bmad/core/config.yaml
"
# Test 8: Manifest tracking
run_test "test-08-manifest-tracking" bash -c "
node $PROJECT_ROOT/tools/bmad-npx-wrapper.js install -y --agents=dev
verify_installation . 'dev' ''
grep -q 'installMode: non-interactive' _bmad/_config/manifest.yaml
grep -q 'selectedAgents:' _bmad/_config/manifest.yaml
"
# Cleanup
echo ""
echo "🧹 Cleaning up test directory: $TEST_DIR"
rm -rf "$TEST_DIR"
# Summary
echo ""
echo "================================================"
echo "Test Summary"
echo "================================================"
echo -e "${GREEN}Passed: $TESTS_PASSED${NC}"
echo -e "${RED}Failed: $TESTS_FAILED${NC}"
echo ""
if [ $TESTS_FAILED -eq 0 ]; then
echo -e "${GREEN}✓ All tests passed!${NC}"
exit 0
else
echo -e "${RED}✗ Some tests failed${NC}"
exit 1
fi