auto upgrader from v3-> v4 and readme updates
This commit is contained in:
188
.bmad-core/tasks/doc-migration-task.md
Normal file
188
.bmad-core/tasks/doc-migration-task.md
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
# Document Migration Task (V3 to V4)
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Migrate BMAD-METHOD V3 documents to V4 format by aligning section headings, structure, and content organization with V4 templates while preserving all user-generated content.
|
||||||
|
|
||||||
|
## When to Use This Task
|
||||||
|
|
||||||
|
[[LLM: This task should be used after a V3 to V4 upgrade when the user has existing PRD and Architecture documents that need to be aligned with V4 templates. The task handles:
|
||||||
|
|
||||||
|
- PRD migration to V4 format
|
||||||
|
- Architecture migration (single backend or full-stack)
|
||||||
|
- Merging separate front-end and backend architecture docs into full-stack-architecture.md
|
||||||
|
]]
|
||||||
|
|
||||||
|
Use this task when:
|
||||||
|
|
||||||
|
- You've upgraded from BMAD V3 to V4
|
||||||
|
- Your documents have V3 section headings that differ from V4
|
||||||
|
- You have separate front-end and backend architecture documents
|
||||||
|
- You need to align your documents with V4 agent expectations
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Completed V3 to V4 upgrade
|
||||||
|
- Documents located in `docs/` folder
|
||||||
|
- Access to V4 templates in `.bmad-core/templates/`
|
||||||
|
|
||||||
|
## Process
|
||||||
|
|
||||||
|
### 1. Analyze Existing Documents
|
||||||
|
|
||||||
|
[[LLM: First, examine what documents exist in the docs/ folder:
|
||||||
|
|
||||||
|
- Look for prd.md or similar
|
||||||
|
- Look for architecture.md or similar
|
||||||
|
- Check for front-end-architecture.md or similar
|
||||||
|
- Note which documents need migration
|
||||||
|
]]
|
||||||
|
|
||||||
|
Identify which documents need migration:
|
||||||
|
|
||||||
|
- PRD document
|
||||||
|
- Architecture document(s)
|
||||||
|
- Any front-end specific architecture
|
||||||
|
|
||||||
|
### 2. PRD Migration
|
||||||
|
|
||||||
|
[[LLM: When migrating the PRD:
|
||||||
|
|
||||||
|
1. Read the existing PRD content
|
||||||
|
2. Compare with `.bmad-core/templates/prd-tmpl.md`
|
||||||
|
3. Map V3 sections to V4 sections:
|
||||||
|
- Keep all user content
|
||||||
|
- Update section headings to match V4
|
||||||
|
- Add any missing V4 sections with placeholder content
|
||||||
|
- Preserve any custom sections at the end
|
||||||
|
|
||||||
|
V3 → V4 Common Mappings:
|
||||||
|
|
||||||
|
- "Executive Summary" → "Goals and Background Context"
|
||||||
|
- "Goals" → "Goals and Background Context > Goals"
|
||||||
|
- "Requirements" → Keep as is
|
||||||
|
- "User Stories" → "Epics and User Stories"
|
||||||
|
]]
|
||||||
|
|
||||||
|
**PRD Section Mapping:**
|
||||||
|
|
||||||
|
1. Update main heading structure to match V4 template
|
||||||
|
2. Preserve all existing content under new headings
|
||||||
|
3. Add missing sections with appropriate prompts for completion
|
||||||
|
4. Maintain any project-specific custom sections
|
||||||
|
|
||||||
|
### 3. Architecture Migration
|
||||||
|
|
||||||
|
[[LLM: Determine architecture migration path:
|
||||||
|
|
||||||
|
1. If ONLY backend architecture exists:
|
||||||
|
- Use `.bmad-core/templates/architecture-tmpl.md`
|
||||||
|
- Map sections appropriately
|
||||||
|
2. If BOTH backend AND front-end architecture exist:
|
||||||
|
- Use `.bmad-core/templates/full-stack-architecture-tmpl.md`
|
||||||
|
- Merge content from both documents
|
||||||
|
- Create unified architecture document
|
||||||
|
3. Section mapping approach:
|
||||||
|
- Preserve all technical decisions and content
|
||||||
|
- Update headings to match V4 structure
|
||||||
|
- Combine overlapping sections intelligently
|
||||||
|
]]
|
||||||
|
|
||||||
|
**Architecture Migration Paths:**
|
||||||
|
|
||||||
|
#### Single Architecture → architecture.md
|
||||||
|
|
||||||
|
- Update section headings to V4 format
|
||||||
|
- Add any missing V4 sections
|
||||||
|
- Preserve all technical content
|
||||||
|
|
||||||
|
#### Multiple Architecture Files → full-stack-architecture.md
|
||||||
|
|
||||||
|
- Merge backend and front-end content
|
||||||
|
- Organize under V4 full-stack sections
|
||||||
|
- Eliminate redundancy while preserving all unique content
|
||||||
|
- Create unified system view
|
||||||
|
|
||||||
|
### 4. Content Preservation Rules
|
||||||
|
|
||||||
|
[[LLM: CRITICAL preservation rules:
|
||||||
|
|
||||||
|
1. NEVER delete user content
|
||||||
|
2. If unsure where content belongs, add it to the most relevant section
|
||||||
|
3. Preserve all:
|
||||||
|
- Technical decisions
|
||||||
|
- Diagrams and code blocks
|
||||||
|
- Lists and tables
|
||||||
|
- Custom sections
|
||||||
|
- Links and references
|
||||||
|
4. Add [[LLM: migration note]] comments where manual review might be needed
|
||||||
|
]]
|
||||||
|
|
||||||
|
**Always Preserve:**
|
||||||
|
|
||||||
|
- All user-written content
|
||||||
|
- Technical specifications
|
||||||
|
- Diagrams (Mermaid, ASCII, etc.)
|
||||||
|
- Code examples
|
||||||
|
- Custom sections not in V4 template
|
||||||
|
|
||||||
|
### 5. Create Migration Report
|
||||||
|
|
||||||
|
[[LLM: After migration, create a brief report:
|
||||||
|
|
||||||
|
- List all documents migrated
|
||||||
|
- Note any sections that need manual review
|
||||||
|
- Highlight any content that was moved significantly
|
||||||
|
- Suggest next steps
|
||||||
|
]]
|
||||||
|
|
||||||
|
Generate `migration-report.md` with:
|
||||||
|
|
||||||
|
- Documents migrated
|
||||||
|
- Section mappings performed
|
||||||
|
- Any content requiring review
|
||||||
|
- Recommendations for manual updates
|
||||||
|
|
||||||
|
## Validation Checklist
|
||||||
|
|
||||||
|
Before completing migration:
|
||||||
|
|
||||||
|
- [ ] All original content is preserved
|
||||||
|
- [ ] Section headings match V4 templates
|
||||||
|
- [ ] No duplicate content in merged documents
|
||||||
|
- [ ] Documents are properly formatted
|
||||||
|
- [ ] Any uncertain mappings are marked for review
|
||||||
|
|
||||||
|
## Example Migrations
|
||||||
|
|
||||||
|
### V3 PRD Section → V4 PRD Section
|
||||||
|
|
||||||
|
```
|
||||||
|
V3: ## Executive Summary → V4: ## Goals and Background Context
|
||||||
|
V3: ## Goals → V4: ### Goals
|
||||||
|
V3: ## Success Metrics → V4: ## Success Metrics and KPIs
|
||||||
|
```
|
||||||
|
|
||||||
|
### Architecture Merge Example
|
||||||
|
|
||||||
|
```
|
||||||
|
backend-architecture.md + front-end-architecture.md → full-stack-architecture.md
|
||||||
|
|
||||||
|
V3 Backend: ## Technology Stack → V4: ## Technology Stack > ### Backend
|
||||||
|
V3 Frontend: ## UI Architecture → V4: ## Technology Stack > ### Frontend
|
||||||
|
```
|
||||||
|
|
||||||
|
## Post-Migration Steps
|
||||||
|
|
||||||
|
1. Review migrated documents for accuracy
|
||||||
|
2. Fill in any placeholder sections added
|
||||||
|
3. Run document sharding if needed
|
||||||
|
4. Update any cross-references between documents
|
||||||
|
|
||||||
|
## Important Notes
|
||||||
|
|
||||||
|
- This task requires careful analysis of existing content
|
||||||
|
- When in doubt, preserve content and mark for review
|
||||||
|
- The goal is structure alignment, not content rewriting
|
||||||
|
- Custom sections should be retained even if not in V4 template
|
||||||
|
- MOST CRITICAL - The ONLY LEVEL 2 HEADINGS should be what are in the V4 templates. so content preservation might mean making some content that was level 2 a level 3 now. UNDERSTAND that the LEVEL 2 headings result in file names
|
||||||
53
README.md
53
README.md
@@ -3,9 +3,14 @@
|
|||||||
[](docs/versions.md)
|
[](docs/versions.md)
|
||||||
[](LICENSE)
|
[](LICENSE)
|
||||||
[](https://nodejs.org)
|
[](https://nodejs.org)
|
||||||
|
[](https://discord.gg/g6ypHytrCB)
|
||||||
|
|
||||||
**AI-Powered Agile Development Framework** - Transform your software development with specialized AI agents that work as your complete Agile team.
|
**AI-Powered Agile Development Framework** - Transform your software development with specialized AI agents that work as your complete Agile team.
|
||||||
|
|
||||||
|
📺 **[Subscribe to BMadCode on YouTube](https://www.youtube.com/@BMadCode?sub_confirmation=1)** - V4 walkthrough and comprehensive guide coming soon!
|
||||||
|
|
||||||
|
⭐ **If you find this project helpful or useful, please give it a star!** It helps others discover BMAD-METHOD and you will be notified of updates!
|
||||||
|
|
||||||
## 🚀 Quick Start
|
## 🚀 Quick Start
|
||||||
|
|
||||||
### Fastest Start: Web UI (2 minutes) 🏃♂️
|
### Fastest Start: Web UI (2 minutes) 🏃♂️
|
||||||
@@ -98,6 +103,7 @@ The BMad Method works with any IDE, but has built-in integration for:
|
|||||||
- `cursor` - Cursor IDE with @agent commands
|
- `cursor` - Cursor IDE with @agent commands
|
||||||
- `claude-code` - Claude Code with /agent commands
|
- `claude-code` - Claude Code with /agent commands
|
||||||
- `windsurf` - Windsurf with @agent commands
|
- `windsurf` - Windsurf with @agent commands
|
||||||
|
- More coming soon - BUT ITS easy to use with ANY IDE!
|
||||||
|
|
||||||
## Available Agents
|
## Available Agents
|
||||||
|
|
||||||
@@ -116,10 +122,10 @@ The BMad Method works with any IDE, but has built-in integration for:
|
|||||||
|
|
||||||
### Meta Agents
|
### Meta Agents
|
||||||
|
|
||||||
| Agent | Role | Specialty |
|
| Agent | Role | Specialty |
|
||||||
| ------------------- | ---------------- | ------------------------------------- |
|
| ------------------- | ---------------- | ------------------------------------------------------------------- |
|
||||||
| `bmad-orchestrator` | Team Coordinator | Multi-agent workflows, role switching |
|
| `bmad-orchestrator` | Team Coordinator | Multi-agent workflows, role switching, is part of every team bundle |
|
||||||
| `bmad-master` | Universal Expert | All capabilities without switching |
|
| `bmad-master` | Universal Expert | All capabilities without switching |
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
@@ -155,6 +161,33 @@ npx bmad-method update
|
|||||||
npx bmad-method status
|
npx bmad-method status
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Upgrading from V3 to V4
|
||||||
|
|
||||||
|
If you have an existing BMAD-METHOD V3 project:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run the upgrade command and follow the prompts including entering existing project location
|
||||||
|
npm run upgrade
|
||||||
|
|
||||||
|
# Run the upgrade command and follow the prompts including entering existing project location
|
||||||
|
npx bmad-method upgrade
|
||||||
|
```
|
||||||
|
|
||||||
|
The upgrade process will:
|
||||||
|
|
||||||
|
1. Create a backup of your V3 files in `.bmad-v3-backup/`
|
||||||
|
2. Install the new V4 `.bmad-core/` structure
|
||||||
|
3. Migrate your documents (PRD, Architecture, Stories, Epics)
|
||||||
|
4. Set up IDE integration for all V4 agents (Cursor Windsurf and Claude-Code - others coming soon)
|
||||||
|
|
||||||
|
After upgrading:
|
||||||
|
|
||||||
|
1. Review your documents in the `docs/` folder
|
||||||
|
2. Use `@bmad-master` agent to run the `doc-migration-task` to align your documents with V4 templates
|
||||||
|
3. If you have separate front-end and backend architecture docs, the migration task will help merge them into a unified `full-stack-architecture.md`
|
||||||
|
|
||||||
|
**Note**: The agents in .bmad-core fully replace the items in bmad-agent.
|
||||||
|
|
||||||
## Teams & Workflows
|
## Teams & Workflows
|
||||||
|
|
||||||
### Pre-Configured Teams
|
### Pre-Configured Teams
|
||||||
@@ -211,14 +244,9 @@ Rich templates for all document types:
|
|||||||
- Test Plans
|
- Test Plans
|
||||||
- And more...
|
- And more...
|
||||||
|
|
||||||
### Slash Commands
|
### Slash Star Commands
|
||||||
|
|
||||||
Quick actions and role switching:
|
Ask the agent you are using for help with /help (in the web) or \*help in the ide to see what commands are available!
|
||||||
|
|
||||||
- `/help` - Show available commands
|
|
||||||
- `/pm` - Switch to Product Manager
|
|
||||||
- `*create-doc` - Create from template
|
|
||||||
- `*validate` - Run validations
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
@@ -230,12 +258,11 @@ We welcome contributions! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|||||||
git clone https://github.com/bmadcode/bmad-method.git
|
git clone https://github.com/bmadcode/bmad-method.git
|
||||||
cd bmad-method
|
cd bmad-method
|
||||||
npm install
|
npm install
|
||||||
npm run validate # Check configurations
|
|
||||||
npm test # Run tests
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
|
- 💬 [Discord Community](https://discord.gg/g6ypHytrCB)
|
||||||
- 📖 [Documentation](docs/)
|
- 📖 [Documentation](docs/)
|
||||||
- 🐛 [Issue Tracker](https://github.com/bmadcode/bmad-method/issues)
|
- 🐛 [Issue Tracker](https://github.com/bmadcode/bmad-method/issues)
|
||||||
- 💬 [Discussions](https://github.com/bmadcode/bmad-method/discussions)
|
- 💬 [Discussions](https://github.com/bmadcode/bmad-method/discussions)
|
||||||
|
|||||||
@@ -13,7 +13,9 @@
|
|||||||
"build:teams": "node tools/cli.js build --teams-only",
|
"build:teams": "node tools/cli.js build --teams-only",
|
||||||
"list:agents": "node tools/cli.js list:agents",
|
"list:agents": "node tools/cli.js list:agents",
|
||||||
"validate": "node tools/cli.js validate",
|
"validate": "node tools/cli.js validate",
|
||||||
"install:bmad": "node tools/installer/bin/bmad.js install"
|
"install:bmad": "node tools/installer/bin/bmad.js install",
|
||||||
|
"upgrade": "node tools/cli.js upgrade",
|
||||||
|
"upgrade:dry-run": "node tools/cli.js upgrade --dry-run"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"commander": "^9.4.1",
|
"commander": "^9.4.1",
|
||||||
|
|||||||
16
tools/cli.js
16
tools/cli.js
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
const { Command } = require('commander');
|
const { Command } = require('commander');
|
||||||
const WebBuilder = require('./builders/web-builder');
|
const WebBuilder = require('./builders/web-builder');
|
||||||
|
const V3ToV4Upgrader = require('./upgraders/v3-to-v4-upgrader');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
const program = new Command();
|
const program = new Command();
|
||||||
@@ -84,4 +85,19 @@ program
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
program
|
||||||
|
.command('upgrade')
|
||||||
|
.description('Upgrade a BMAD-METHOD V3 project to V4')
|
||||||
|
.option('-p, --project <path>', 'Path to V3 project (defaults to current directory)')
|
||||||
|
.option('--dry-run', 'Show what would be changed without making changes')
|
||||||
|
.option('--no-backup', 'Skip creating backup (not recommended)')
|
||||||
|
.action(async (options) => {
|
||||||
|
const upgrader = new V3ToV4Upgrader();
|
||||||
|
await upgrader.upgrade({
|
||||||
|
projectPath: options.project,
|
||||||
|
dryRun: options.dryRun,
|
||||||
|
backup: options.backup
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
program.parse();
|
program.parse();
|
||||||
@@ -43,7 +43,7 @@ class IdeSetup {
|
|||||||
const mdcPath = path.join(cursorRulesDir, `${agentId}.mdc`);
|
const mdcPath = path.join(cursorRulesDir, `${agentId}.mdc`);
|
||||||
|
|
||||||
// Create MDC content with proper format
|
// Create MDC content with proper format
|
||||||
let mdcContent = '\n---\n';
|
let mdcContent = '---\n';
|
||||||
mdcContent += 'description: \n';
|
mdcContent += 'description: \n';
|
||||||
mdcContent += 'globs: []\n';
|
mdcContent += 'globs: []\n';
|
||||||
mdcContent += 'alwaysApply: false\n';
|
mdcContent += 'alwaysApply: false\n';
|
||||||
|
|||||||
530
tools/upgraders/v3-to-v4-upgrader.js
Normal file
530
tools/upgraders/v3-to-v4-upgrader.js
Normal file
@@ -0,0 +1,530 @@
|
|||||||
|
const fs = require('fs').promises;
|
||||||
|
const path = require('path');
|
||||||
|
const chalk = require('chalk');
|
||||||
|
const ora = require('ora');
|
||||||
|
const glob = require('glob');
|
||||||
|
const inquirer = require('inquirer');
|
||||||
|
const { promisify } = require('util');
|
||||||
|
const globAsync = promisify(glob);
|
||||||
|
|
||||||
|
class V3ToV4Upgrader {
|
||||||
|
constructor() {
|
||||||
|
// Constructor remains empty
|
||||||
|
}
|
||||||
|
|
||||||
|
async upgrade(options = {}) {
|
||||||
|
try {
|
||||||
|
// Keep readline open throughout the process
|
||||||
|
process.stdin.resume();
|
||||||
|
|
||||||
|
// 1. Welcome message
|
||||||
|
console.log(chalk.bold('\nWelcome to BMAD-METHOD V3 to V4 Upgrade Tool\n'));
|
||||||
|
console.log('This tool will help you upgrade your BMAD-METHOD V3 project to V4.\n');
|
||||||
|
console.log(chalk.cyan('What this tool does:'));
|
||||||
|
console.log('- Creates a backup of your V3 files (.bmad-v3-backup/)');
|
||||||
|
console.log('- Installs the new V4 .bmad-core structure');
|
||||||
|
console.log('- Preserves your PRD, Architecture, and Stories in the new format\n');
|
||||||
|
console.log(chalk.yellow('What this tool does NOT do:'));
|
||||||
|
console.log('- Modify your document content (use doc-migration-task after upgrade)');
|
||||||
|
console.log('- Touch any files outside bmad-agent/ and docs/\n');
|
||||||
|
|
||||||
|
// 2. Get project path
|
||||||
|
const projectPath = await this.getProjectPath(options.projectPath);
|
||||||
|
|
||||||
|
// 3. Validate V3 structure
|
||||||
|
const validation = await this.validateV3Project(projectPath);
|
||||||
|
if (!validation.isValid) {
|
||||||
|
console.error(chalk.red('\nError: This doesn\'t appear to be a V3 project.'));
|
||||||
|
console.error('Expected to find:');
|
||||||
|
console.error('- bmad-agent/ directory');
|
||||||
|
console.error('- docs/ directory\n');
|
||||||
|
console.error('Please check you\'re in the correct directory and try again.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Pre-flight check
|
||||||
|
const analysis = await this.analyzeProject(projectPath);
|
||||||
|
await this.showPreflightCheck(analysis, options);
|
||||||
|
|
||||||
|
if (!options.dryRun) {
|
||||||
|
const { confirm } = await inquirer.prompt([{
|
||||||
|
type: 'confirm',
|
||||||
|
name: 'confirm',
|
||||||
|
message: 'Continue with upgrade?',
|
||||||
|
default: true
|
||||||
|
}]);
|
||||||
|
|
||||||
|
if (!confirm) {
|
||||||
|
console.log('Upgrade cancelled.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Create backup
|
||||||
|
if (options.backup !== false && !options.dryRun) {
|
||||||
|
await this.createBackup(projectPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. Install V4 structure
|
||||||
|
if (!options.dryRun) {
|
||||||
|
await this.installV4Structure(projectPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. Migrate documents
|
||||||
|
if (!options.dryRun) {
|
||||||
|
await this.migrateDocuments(projectPath, analysis);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8. Setup IDE
|
||||||
|
if (!options.dryRun) {
|
||||||
|
await this.setupIDE(projectPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 9. Show completion report
|
||||||
|
this.showCompletionReport(projectPath, analysis);
|
||||||
|
|
||||||
|
process.exit(0);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(chalk.red('\nUpgrade error:'), error.message);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getProjectPath(providedPath) {
|
||||||
|
if (providedPath) {
|
||||||
|
return path.resolve(providedPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { projectPath } = await inquirer.prompt([{
|
||||||
|
type: 'input',
|
||||||
|
name: 'projectPath',
|
||||||
|
message: 'Please enter the path to your V3 project:',
|
||||||
|
default: process.cwd()
|
||||||
|
}]);
|
||||||
|
|
||||||
|
return path.resolve(projectPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
async validateV3Project(projectPath) {
|
||||||
|
const spinner = ora('Validating project structure...').start();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const bmadAgentPath = path.join(projectPath, 'bmad-agent');
|
||||||
|
const docsPath = path.join(projectPath, 'docs');
|
||||||
|
|
||||||
|
const hasBmadAgent = await this.pathExists(bmadAgentPath);
|
||||||
|
const hasDocs = await this.pathExists(docsPath);
|
||||||
|
|
||||||
|
if (hasBmadAgent) {
|
||||||
|
spinner.text = '✓ Found bmad-agent/ directory';
|
||||||
|
console.log(chalk.green('\n✓ Found bmad-agent/ directory'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasDocs) {
|
||||||
|
console.log(chalk.green('✓ Found docs/ directory'));
|
||||||
|
}
|
||||||
|
|
||||||
|
const isValid = hasBmadAgent && hasDocs;
|
||||||
|
|
||||||
|
if (isValid) {
|
||||||
|
spinner.succeed('This appears to be a valid V3 project');
|
||||||
|
} else {
|
||||||
|
spinner.fail('Invalid V3 project structure');
|
||||||
|
}
|
||||||
|
|
||||||
|
return { isValid, hasBmadAgent, hasDocs };
|
||||||
|
} catch (error) {
|
||||||
|
spinner.fail('Validation failed');
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async analyzeProject(projectPath) {
|
||||||
|
const docsPath = path.join(projectPath, 'docs');
|
||||||
|
const bmadAgentPath = path.join(projectPath, 'bmad-agent');
|
||||||
|
|
||||||
|
// Find PRD
|
||||||
|
const prdCandidates = ['prd.md', 'PRD.md', 'product-requirements.md'];
|
||||||
|
let prdFile = null;
|
||||||
|
for (const candidate of prdCandidates) {
|
||||||
|
const candidatePath = path.join(docsPath, candidate);
|
||||||
|
if (await this.pathExists(candidatePath)) {
|
||||||
|
prdFile = candidate;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find Architecture
|
||||||
|
const archCandidates = ['architecture.md', 'Architecture.md', 'technical-architecture.md'];
|
||||||
|
let archFile = null;
|
||||||
|
for (const candidate of archCandidates) {
|
||||||
|
const candidatePath = path.join(docsPath, candidate);
|
||||||
|
if (await this.pathExists(candidatePath)) {
|
||||||
|
archFile = candidate;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find Front-end Architecture (V3 specific)
|
||||||
|
const frontEndCandidates = ['front-end-architecture.md', 'frontend-architecture.md', 'ui-architecture.md'];
|
||||||
|
let frontEndArchFile = null;
|
||||||
|
for (const candidate of frontEndCandidates) {
|
||||||
|
const candidatePath = path.join(docsPath, candidate);
|
||||||
|
if (await this.pathExists(candidatePath)) {
|
||||||
|
frontEndArchFile = candidate;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find epic files
|
||||||
|
const epicFiles = await globAsync('epic*.md', { cwd: docsPath });
|
||||||
|
|
||||||
|
// Find story files
|
||||||
|
const storiesPath = path.join(docsPath, 'stories');
|
||||||
|
let storyFiles = [];
|
||||||
|
if (await this.pathExists(storiesPath)) {
|
||||||
|
storyFiles = await globAsync('*.md', { cwd: storiesPath });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count custom files in bmad-agent
|
||||||
|
const bmadAgentFiles = await globAsync('**/*.md', {
|
||||||
|
cwd: bmadAgentPath,
|
||||||
|
ignore: ['node_modules/**']
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
prdFile,
|
||||||
|
archFile,
|
||||||
|
frontEndArchFile,
|
||||||
|
epicFiles,
|
||||||
|
storyFiles,
|
||||||
|
customFileCount: bmadAgentFiles.length
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async showPreflightCheck(analysis, options) {
|
||||||
|
console.log(chalk.bold('\nProject Analysis:'));
|
||||||
|
console.log(`- PRD found: ${analysis.prdFile ? `docs/${analysis.prdFile}` : chalk.yellow('Not found')}`);
|
||||||
|
console.log(`- Architecture found: ${analysis.archFile ? `docs/${analysis.archFile}` : chalk.yellow('Not found')}`);
|
||||||
|
if (analysis.frontEndArchFile) {
|
||||||
|
console.log(`- Front-end Architecture found: docs/${analysis.frontEndArchFile}`);
|
||||||
|
}
|
||||||
|
console.log(`- Epic files found: ${analysis.epicFiles.length} files (epic*.md)`);
|
||||||
|
console.log(`- Stories found: ${analysis.storyFiles.length} files in docs/stories/`);
|
||||||
|
console.log(`- Custom files in bmad-agent/: ${analysis.customFileCount}`);
|
||||||
|
|
||||||
|
if (!options.dryRun) {
|
||||||
|
console.log('\nThe following will be backed up to .bmad-v3-backup/:');
|
||||||
|
console.log('- bmad-agent/ (entire directory)');
|
||||||
|
console.log('- docs/ (entire directory)');
|
||||||
|
|
||||||
|
if (analysis.epicFiles.length > 0) {
|
||||||
|
console.log(chalk.green('\nNote: Epic files found! They will be placed in docs/prd/ with an index.md file.'));
|
||||||
|
console.log(chalk.green('Since epic files exist, you won\'t need to shard the PRD after upgrade.'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async createBackup(projectPath) {
|
||||||
|
const spinner = ora('Creating backup...').start();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const backupPath = path.join(projectPath, '.bmad-v3-backup');
|
||||||
|
|
||||||
|
// Check if backup already exists
|
||||||
|
if (await this.pathExists(backupPath)) {
|
||||||
|
spinner.fail('Backup directory already exists');
|
||||||
|
console.error(chalk.red('\nError: Backup directory .bmad-v3-backup/ already exists.'));
|
||||||
|
console.error('\nThis might mean an upgrade was already attempted.');
|
||||||
|
console.error('Please remove or rename the existing backup and try again.');
|
||||||
|
throw new Error('Backup already exists');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create backup directory
|
||||||
|
await fs.mkdir(backupPath, { recursive: true });
|
||||||
|
spinner.text = '✓ Created .bmad-v3-backup/';
|
||||||
|
console.log(chalk.green('\n✓ Created .bmad-v3-backup/'));
|
||||||
|
|
||||||
|
// Move bmad-agent
|
||||||
|
const bmadAgentSrc = path.join(projectPath, 'bmad-agent');
|
||||||
|
const bmadAgentDest = path.join(backupPath, 'bmad-agent');
|
||||||
|
await fs.rename(bmadAgentSrc, bmadAgentDest);
|
||||||
|
console.log(chalk.green('✓ Moved bmad-agent/ to backup'));
|
||||||
|
|
||||||
|
// Move docs
|
||||||
|
const docsSrc = path.join(projectPath, 'docs');
|
||||||
|
const docsDest = path.join(backupPath, 'docs');
|
||||||
|
await fs.rename(docsSrc, docsDest);
|
||||||
|
console.log(chalk.green('✓ Moved docs/ to backup'));
|
||||||
|
|
||||||
|
spinner.succeed('Backup created successfully');
|
||||||
|
} catch (error) {
|
||||||
|
spinner.fail('Backup failed');
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async installV4Structure(projectPath) {
|
||||||
|
const spinner = ora('Installing V4 structure...').start();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Get the source .bmad-core directory
|
||||||
|
const sourcePath = path.join(__dirname, '..', '..', '.bmad-core');
|
||||||
|
const destPath = path.join(projectPath, '.bmad-core');
|
||||||
|
|
||||||
|
// Copy .bmad-core
|
||||||
|
await this.copyDirectory(sourcePath, destPath);
|
||||||
|
spinner.text = '✓ Copied fresh .bmad-core/ directory from V4';
|
||||||
|
console.log(chalk.green('\n✓ Copied fresh .bmad-core/ directory from V4'));
|
||||||
|
|
||||||
|
// Create docs directory
|
||||||
|
const docsPath = path.join(projectPath, 'docs');
|
||||||
|
await fs.mkdir(docsPath, { recursive: true });
|
||||||
|
console.log(chalk.green('✓ Created new docs/ directory'));
|
||||||
|
|
||||||
|
console.log(chalk.yellow('\nNote: Your V3 bmad-agent content has been backed up and NOT migrated.'));
|
||||||
|
console.log(chalk.yellow('The new V4 agents are completely different and look for different file structures.'));
|
||||||
|
|
||||||
|
spinner.succeed('V4 structure installed successfully');
|
||||||
|
} catch (error) {
|
||||||
|
spinner.fail('V4 installation failed');
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async migrateDocuments(projectPath, analysis) {
|
||||||
|
const spinner = ora('Migrating your project documents...').start();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const backupDocsPath = path.join(projectPath, '.bmad-v3-backup', 'docs');
|
||||||
|
const newDocsPath = path.join(projectPath, 'docs');
|
||||||
|
let copiedCount = 0;
|
||||||
|
|
||||||
|
// Copy PRD
|
||||||
|
if (analysis.prdFile) {
|
||||||
|
const src = path.join(backupDocsPath, analysis.prdFile);
|
||||||
|
const dest = path.join(newDocsPath, analysis.prdFile);
|
||||||
|
await fs.copyFile(src, dest);
|
||||||
|
console.log(chalk.green(`\n✓ Copied PRD to docs/${analysis.prdFile}`));
|
||||||
|
copiedCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy Architecture
|
||||||
|
if (analysis.archFile) {
|
||||||
|
const src = path.join(backupDocsPath, analysis.archFile);
|
||||||
|
const dest = path.join(newDocsPath, analysis.archFile);
|
||||||
|
await fs.copyFile(src, dest);
|
||||||
|
console.log(chalk.green(`✓ Copied Architecture to docs/${analysis.archFile}`));
|
||||||
|
copiedCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy Front-end Architecture if exists
|
||||||
|
if (analysis.frontEndArchFile) {
|
||||||
|
const src = path.join(backupDocsPath, analysis.frontEndArchFile);
|
||||||
|
const dest = path.join(newDocsPath, analysis.frontEndArchFile);
|
||||||
|
await fs.copyFile(src, dest);
|
||||||
|
console.log(chalk.green(`✓ Copied Front-end Architecture to docs/${analysis.frontEndArchFile}`));
|
||||||
|
console.log(chalk.yellow('Note: V4 uses a single full-stack-architecture.md - use doc-migration-task to merge'));
|
||||||
|
copiedCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy stories
|
||||||
|
if (analysis.storyFiles.length > 0) {
|
||||||
|
const storiesDir = path.join(newDocsPath, 'stories');
|
||||||
|
await fs.mkdir(storiesDir, { recursive: true });
|
||||||
|
|
||||||
|
for (const storyFile of analysis.storyFiles) {
|
||||||
|
const src = path.join(backupDocsPath, 'stories', storyFile);
|
||||||
|
const dest = path.join(storiesDir, storyFile);
|
||||||
|
await fs.copyFile(src, dest);
|
||||||
|
}
|
||||||
|
console.log(chalk.green(`✓ Copied ${analysis.storyFiles.length} story files to docs/stories/`));
|
||||||
|
copiedCount += analysis.storyFiles.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy epic files to prd subfolder
|
||||||
|
if (analysis.epicFiles.length > 0) {
|
||||||
|
const prdDir = path.join(newDocsPath, 'prd');
|
||||||
|
await fs.mkdir(prdDir, { recursive: true });
|
||||||
|
|
||||||
|
for (const epicFile of analysis.epicFiles) {
|
||||||
|
const src = path.join(backupDocsPath, epicFile);
|
||||||
|
const dest = path.join(prdDir, epicFile);
|
||||||
|
await fs.copyFile(src, dest);
|
||||||
|
}
|
||||||
|
console.log(chalk.green(`✓ Found and copied ${analysis.epicFiles.length} epic files to docs/prd/`));
|
||||||
|
|
||||||
|
// Create index.md for the prd folder
|
||||||
|
await this.createPrdIndex(projectPath, analysis);
|
||||||
|
console.log(chalk.green('✓ Created index.md in docs/prd/'));
|
||||||
|
|
||||||
|
console.log(chalk.green('\nNote: Epic files detected! These are compatible with V4 and have been copied.'));
|
||||||
|
console.log(chalk.green('You won\'t need to shard the PRD since epics already exist.'));
|
||||||
|
copiedCount += analysis.epicFiles.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
spinner.succeed(`Migrated ${copiedCount} documents successfully`);
|
||||||
|
} catch (error) {
|
||||||
|
spinner.fail('Document migration failed');
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async setupIDE(projectPath) {
|
||||||
|
const { ide } = await inquirer.prompt([{
|
||||||
|
type: 'list',
|
||||||
|
name: 'ide',
|
||||||
|
message: 'Which IDE are you using?',
|
||||||
|
choices: [
|
||||||
|
{ name: 'Cursor', value: 'cursor' },
|
||||||
|
{ name: 'Claude Code', value: 'claude-code' },
|
||||||
|
{ name: 'Windsurf', value: 'windsurf' },
|
||||||
|
{ name: 'VS Code', value: 'skip' },
|
||||||
|
{ name: 'Other/Skip', value: 'skip' }
|
||||||
|
]
|
||||||
|
}]);
|
||||||
|
|
||||||
|
const selectedIde = ide === 'skip' ? null : ide;
|
||||||
|
|
||||||
|
if (selectedIde) {
|
||||||
|
const ideSetup = require('../installer/lib/ide-setup');
|
||||||
|
const spinner = ora('Setting up IDE rules for all agents...').start();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await ideSetup.setup(selectedIde, projectPath);
|
||||||
|
spinner.succeed('IDE setup complete!');
|
||||||
|
|
||||||
|
const ideMessages = {
|
||||||
|
'cursor': 'Rules created in .cursor/rules/',
|
||||||
|
'claude-code': 'Commands created in .claude/commands/',
|
||||||
|
'windsurf': 'Rules created in .windsurf/rules/'
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(chalk.green(`- ${ideMessages[selectedIde]}`));
|
||||||
|
} catch (error) {
|
||||||
|
spinner.fail('IDE setup failed');
|
||||||
|
console.error(chalk.yellow('IDE setup failed, but upgrade is complete.'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showCompletionReport(projectPath, analysis) {
|
||||||
|
console.log(chalk.bold.green('\n✓ Upgrade Complete!\n'));
|
||||||
|
console.log(chalk.bold('Summary:'));
|
||||||
|
console.log(`- V3 files backed up to: .bmad-v3-backup/`);
|
||||||
|
console.log(`- V4 structure installed: .bmad-core/ (fresh from V4)`);
|
||||||
|
|
||||||
|
const totalDocs = (analysis.prdFile ? 1 : 0) +
|
||||||
|
(analysis.archFile ? 1 : 0) +
|
||||||
|
(analysis.frontEndArchFile ? 1 : 0) +
|
||||||
|
analysis.storyFiles.length;
|
||||||
|
console.log(`- Documents migrated: ${totalDocs} files${analysis.epicFiles.length > 0 ? ` + ${analysis.epicFiles.length} epics` : ''}`);
|
||||||
|
|
||||||
|
console.log(chalk.bold('\nImportant Changes:'));
|
||||||
|
console.log('- The V4 agents (sm, dev, etc.) expect different file structures than V3');
|
||||||
|
console.log('- Your V3 bmad-agent content was NOT migrated (it\'s incompatible)');
|
||||||
|
if (analysis.epicFiles.length > 0) {
|
||||||
|
console.log('- Epic files were found and copied - no PRD sharding needed!');
|
||||||
|
}
|
||||||
|
if (analysis.frontEndArchFile) {
|
||||||
|
console.log('- Front-end architecture found - V4 uses full-stack-architecture.md, migration needed');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(chalk.bold('\nNext Steps:'));
|
||||||
|
console.log('1. Review your documents in the new docs/ folder');
|
||||||
|
console.log('2. Use @bmad-master agent to run the doc-migration-task to align your documents with V4 templates');
|
||||||
|
if (analysis.epicFiles.length === 0) {
|
||||||
|
console.log('3. Use @bmad-master agent to shard the PRD to create epic files');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(chalk.dim('\nYour V3 backup is preserved in .bmad-v3-backup/ and can be restored if needed.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
async pathExists(filePath) {
|
||||||
|
try {
|
||||||
|
await fs.access(filePath);
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async copyDirectory(src, dest) {
|
||||||
|
await fs.mkdir(dest, { recursive: true });
|
||||||
|
const entries = await fs.readdir(src, { withFileTypes: true });
|
||||||
|
|
||||||
|
for (const entry of entries) {
|
||||||
|
const srcPath = path.join(src, entry.name);
|
||||||
|
const destPath = path.join(dest, entry.name);
|
||||||
|
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
await this.copyDirectory(srcPath, destPath);
|
||||||
|
} else {
|
||||||
|
await fs.copyFile(srcPath, destPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async createPrdIndex(projectPath, analysis) {
|
||||||
|
const prdIndexPath = path.join(projectPath, 'docs', 'prd', 'index.md');
|
||||||
|
const prdPath = path.join(projectPath, 'docs', analysis.prdFile || 'prd.md');
|
||||||
|
|
||||||
|
let indexContent = '# Product Requirements Document\n\n';
|
||||||
|
|
||||||
|
// Try to read the PRD to get the title and intro content
|
||||||
|
if (analysis.prdFile && await this.pathExists(prdPath)) {
|
||||||
|
try {
|
||||||
|
const prdContent = await fs.readFile(prdPath, 'utf8');
|
||||||
|
const lines = prdContent.split('\n');
|
||||||
|
|
||||||
|
// Find the first heading
|
||||||
|
const titleMatch = lines.find(line => line.startsWith('# '));
|
||||||
|
if (titleMatch) {
|
||||||
|
indexContent = titleMatch + '\n\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get any content before the first ## section
|
||||||
|
let introContent = '';
|
||||||
|
let foundFirstSection = false;
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.startsWith('## ')) {
|
||||||
|
foundFirstSection = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!line.startsWith('# ')) {
|
||||||
|
introContent += line + '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (introContent.trim()) {
|
||||||
|
indexContent += introContent.trim() + '\n\n';
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// If we can't read the PRD, just use default content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add sections list
|
||||||
|
indexContent += '## Sections\n\n';
|
||||||
|
|
||||||
|
// Sort epic files for consistent ordering
|
||||||
|
const sortedEpics = [...analysis.epicFiles].sort();
|
||||||
|
|
||||||
|
for (const epicFile of sortedEpics) {
|
||||||
|
// Extract epic name from filename
|
||||||
|
const epicName = epicFile
|
||||||
|
.replace(/\.md$/, '')
|
||||||
|
.replace(/^epic-?/i, '')
|
||||||
|
.replace(/-/g, ' ')
|
||||||
|
.replace(/^\d+\s*/, '') // Remove leading numbers
|
||||||
|
.trim();
|
||||||
|
|
||||||
|
const displayName = epicName.charAt(0).toUpperCase() + epicName.slice(1);
|
||||||
|
indexContent += `- [${displayName || epicFile.replace('.md', '')}](./${epicFile})\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
await fs.writeFile(prdIndexPath, indexContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = V3ToV4Upgrader;
|
||||||
Reference in New Issue
Block a user