Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
62c5d92089 | ||
|
|
c48f200727 | ||
|
|
c151bda938 | ||
|
|
ab70b8dc73 |
7
.gitignore
vendored
7
.gitignore
vendored
@@ -19,4 +19,9 @@ Thumbs.db
|
|||||||
CLAUDE.md
|
CLAUDE.md
|
||||||
.ai/*
|
.ai/*
|
||||||
test-project-install/*
|
test-project-install/*
|
||||||
sample-project/*
|
sample-project/*
|
||||||
|
.claude
|
||||||
|
.bmad-core
|
||||||
|
.bmad-creator-tools
|
||||||
|
.gemini
|
||||||
|
.bmad*/
|
||||||
12
CHANGELOG.md
12
CHANGELOG.md
@@ -1,3 +1,15 @@
|
|||||||
|
# [4.21.0](https://github.com/bmadcode/BMAD-METHOD/compare/v4.20.0...v4.21.0) (2025-06-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* remove unneeded files ([c48f200](https://github.com/bmadcode/BMAD-METHOD/commit/c48f200727384f37a42f4c6b1a946cb90f2445fe))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* massive installer improvement update ([c151bda](https://github.com/bmadcode/BMAD-METHOD/commit/c151bda93833aa310ccc7c0eabcf483376f9e82a))
|
||||||
|
|
||||||
# [4.20.0](https://github.com/bmadcode/BMAD-METHOD/compare/v4.19.2...v4.20.0) (2025-06-29)
|
# [4.20.0](https://github.com/bmadcode/BMAD-METHOD/compare/v4.19.2...v4.20.0) (2025-06-29)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,11 @@ Thank you for considering contributing to this project! This document outlines t
|
|||||||
|
|
||||||
Also note, we use the discussions feature in GitHub to have a community to discuss potential ideas, uses, additions and enhancements.
|
Also note, we use the discussions feature in GitHub to have a community to discuss potential ideas, uses, additions and enhancements.
|
||||||
|
|
||||||
|
💬 **Discord Community**: Join our [Discord server](https://discord.gg/g6ypHytrCB) for real-time discussions:
|
||||||
|
|
||||||
|
- **#general-dev** - Technical discussions, feature ideas, and development questions
|
||||||
|
- **#bugs-issues** - Bug reports and issue discussions
|
||||||
|
|
||||||
## Code of Conduct
|
## Code of Conduct
|
||||||
|
|
||||||
By participating in this project, you agree to abide by our Code of Conduct. Please read it before participating.
|
By participating in this project, you agree to abide by our Code of Conduct. Please read it before participating.
|
||||||
@@ -16,16 +21,35 @@ By participating in this project, you agree to abide by our Code of Conduct. Ple
|
|||||||
|
|
||||||
### Reporting Bugs
|
### Reporting Bugs
|
||||||
|
|
||||||
- Check if the bug has already been reported in the Issues section
|
1. **Check existing issues** first to avoid duplicates
|
||||||
- Include detailed steps to reproduce the bug
|
2. **Use the bug report template** when creating a new issue - it will guide you through providing:
|
||||||
- Include any relevant logs or screenshots
|
- Clear bug description
|
||||||
|
- Steps to reproduce
|
||||||
|
- Expected vs actual behavior
|
||||||
|
- Model/IDE/BMad version details
|
||||||
|
- Screenshots or links if applicable
|
||||||
|
3. **Consider discussing in Discord** (#bugs-issues channel) for quick help
|
||||||
|
4. **Indicate if you're working on a fix** to avoid duplicate efforts
|
||||||
|
|
||||||
### Suggesting Features
|
### Suggesting Features
|
||||||
|
|
||||||
- Check if the feature has already been suggested in the Issues section, and consider using the discussions tab in GitHub also. Explain the feature in detail and why it would be valuable.
|
1. **Discuss first in Discord** (#general-dev channel) - the feature request template asks if you've done this
|
||||||
|
2. **Check existing issues and discussions** to avoid duplicates
|
||||||
|
3. **Use the feature request template** when creating an issue - it will guide you through:
|
||||||
|
- Confirming Discord discussion
|
||||||
|
- Describing the problem it solves
|
||||||
|
- Explaining your solution
|
||||||
|
- Listing alternatives considered
|
||||||
|
4. **Be specific** about why this feature would benefit the BMad community
|
||||||
|
|
||||||
### Pull Request Process
|
### Pull Request Process
|
||||||
|
|
||||||
|
⚠️ **Before starting work:**
|
||||||
|
|
||||||
|
1. **For bugs**: Check if an issue exists (create one using the bug template if not)
|
||||||
|
2. **For features**: Ensure you've discussed in Discord (#general-dev) AND created a feature request issue
|
||||||
|
3. **For large changes**: Always open an issue first to discuss alignment
|
||||||
|
|
||||||
Please only propose small granular commits! If its large or significant, please discuss in the discussions tab and open up an issue first. I do not want you to waste your time on a potentially very large PR to have it rejected because it is not aligned or deviates from other planned changes. Communicate and lets work together to build and improve this great community project!
|
Please only propose small granular commits! If its large or significant, please discuss in the discussions tab and open up an issue first. I do not want you to waste your time on a potentially very large PR to have it rejected because it is not aligned or deviates from other planned changes. Communicate and lets work together to build and improve this great community project!
|
||||||
|
|
||||||
**Important**: All contributions must align with our [Guiding Principles](GUIDING-PRINCIPLES.md). Key points:
|
**Important**: All contributions must align with our [Guiding Principles](GUIDING-PRINCIPLES.md). Key points:
|
||||||
@@ -95,6 +119,15 @@ Example breakdown:
|
|||||||
6. Push to your branch (`git push origin feature/your-feature-name`)
|
6. Push to your branch (`git push origin feature/your-feature-name`)
|
||||||
7. Open a Pull Request against the main branch
|
7. Open a Pull Request against the main branch
|
||||||
|
|
||||||
|
## Issue Templates
|
||||||
|
|
||||||
|
We use GitHub issue templates to ensure all necessary information is provided:
|
||||||
|
|
||||||
|
- **Bug Reports**: Automatically guides you through providing reproduction steps, environment details, and expected behavior
|
||||||
|
- **Feature Requests**: Requires Discord discussion confirmation and asks for problem/solution descriptions
|
||||||
|
|
||||||
|
Using these templates helps maintainers understand and address your contribution faster.
|
||||||
|
|
||||||
## Pull Request Description Guidelines
|
## Pull Request Description Guidelines
|
||||||
|
|
||||||
Keep PR descriptions short and to the point following this template:
|
Keep PR descriptions short and to the point following this template:
|
||||||
@@ -111,6 +144,7 @@ Keep your PR description concise and focused. Use this template:
|
|||||||
## Why
|
## Why
|
||||||
|
|
||||||
[1-2 sentences explaining WHY this change is needed]
|
[1-2 sentences explaining WHY this change is needed]
|
||||||
|
Fixes #[issue number] (if applicable)
|
||||||
|
|
||||||
## How
|
## How
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
version: 4.20.1
|
||||||
markdownExploder: true
|
markdownExploder: true
|
||||||
prd:
|
prd:
|
||||||
prdFile: docs/prd.md
|
prdFile: docs/prd.md
|
||||||
|
|||||||
2
dist/agents/dev.txt
vendored
2
dist/agents/dev.txt
vendored
@@ -83,7 +83,7 @@ task-execution:
|
|||||||
updates-ONLY:
|
updates-ONLY:
|
||||||
- 'Checkboxes: [ ] not started | [-] in progress | [x] complete'
|
- 'Checkboxes: [ ] not started | [-] in progress | [x] complete'
|
||||||
- 'Debug Log: | Task | File | Change | Reverted? |'
|
- 'Debug Log: | Task | File | Change | Reverted? |'
|
||||||
- 'Completion Notes: Deviations only, <50 words'
|
- 'Completion Notes: Deviations from AC or tasks during execution only, <50 words'
|
||||||
- 'Change Log: Requirement changes only'
|
- 'Change Log: Requirement changes only'
|
||||||
- 'File List: CRITICAL - Maintain complete list of ALL files created/modified during implementation'
|
- 'File List: CRITICAL - Maintain complete list of ALL files created/modified during implementation'
|
||||||
blocking: Unapproved deps | Ambiguous after story check | 3 failures | Missing config | Failing validations
|
blocking: Unapproved deps | Ambiguous after story check | 3 failures | Missing config | Failing validations
|
||||||
|
|||||||
2
dist/teams/team-all.txt
vendored
2
dist/teams/team-all.txt
vendored
@@ -353,7 +353,7 @@ task-execution:
|
|||||||
updates-ONLY:
|
updates-ONLY:
|
||||||
- 'Checkboxes: [ ] not started | [-] in progress | [x] complete'
|
- 'Checkboxes: [ ] not started | [-] in progress | [x] complete'
|
||||||
- 'Debug Log: | Task | File | Change | Reverted? |'
|
- 'Debug Log: | Task | File | Change | Reverted? |'
|
||||||
- 'Completion Notes: Deviations only, <50 words'
|
- 'Completion Notes: Deviations from AC or tasks during execution only, <50 words'
|
||||||
- 'Change Log: Requirement changes only'
|
- 'Change Log: Requirement changes only'
|
||||||
- 'File List: CRITICAL - Maintain complete list of ALL files created/modified during implementation'
|
- 'File List: CRITICAL - Maintain complete list of ALL files created/modified during implementation'
|
||||||
blocking: Unapproved deps | Ambiguous after story check | 3 failures | Missing config | Failing validations
|
blocking: Unapproved deps | Ambiguous after story check | 3 failures | Missing config | Failing validations
|
||||||
|
|||||||
2
dist/teams/team-ide-minimal.txt
vendored
2
dist/teams/team-ide-minimal.txt
vendored
@@ -335,7 +335,7 @@ task-execution:
|
|||||||
updates-ONLY:
|
updates-ONLY:
|
||||||
- 'Checkboxes: [ ] not started | [-] in progress | [x] complete'
|
- 'Checkboxes: [ ] not started | [-] in progress | [x] complete'
|
||||||
- 'Debug Log: | Task | File | Change | Reverted? |'
|
- 'Debug Log: | Task | File | Change | Reverted? |'
|
||||||
- 'Completion Notes: Deviations only, <50 words'
|
- 'Completion Notes: Deviations from AC or tasks during execution only, <50 words'
|
||||||
- 'Change Log: Requirement changes only'
|
- 'Change Log: Requirement changes only'
|
||||||
- 'File List: CRITICAL - Maintain complete list of ALL files created/modified during implementation'
|
- 'File List: CRITICAL - Maintain complete list of ALL files created/modified during implementation'
|
||||||
blocking: Unapproved deps | Ambiguous after story check | 3 failures | Missing config | Failing validations
|
blocking: Unapproved deps | Ambiguous after story check | 3 failures | Missing config | Failing validations
|
||||||
|
|||||||
@@ -8,7 +8,15 @@ A pull request (PR) is how you propose changes to a project on GitHub. Think of
|
|||||||
|
|
||||||
## Before You Start
|
## Before You Start
|
||||||
|
|
||||||
⚠️ **Important**: Please keep your contributions small and focused! We prefer many small, clear changes rather than one massive change. If you're planning something big, please [open an issue](https://github.com/bmadcode/bmad-method/issues) or start a [discussion](https://github.com/bmadcode/bmad-method/discussions) first.
|
⚠️ **Important**: Please keep your contributions small and focused! We prefer many small, clear changes rather than one massive change.
|
||||||
|
|
||||||
|
**Required before submitting PRs:**
|
||||||
|
|
||||||
|
- **For bug fixes**: Create an issue using the [bug report template](https://github.com/bmadcode/bmad-method/issues/new?template=bug_report.md)
|
||||||
|
- **For new features**:
|
||||||
|
1. Discuss in Discord [#general-dev channel](https://discord.gg/g6ypHytrCB)
|
||||||
|
2. Create an issue using the [feature request template](https://github.com/bmadcode/bmad-method/issues/new?template=feature_request.md)
|
||||||
|
- **For large changes**: Always open an issue first to discuss alignment
|
||||||
|
|
||||||
## Step-by-Step Guide
|
## Step-by-Step Guide
|
||||||
|
|
||||||
@@ -82,9 +90,15 @@ git push origin fix/typo-in-readme
|
|||||||
|
|
||||||
1. Go to your fork on GitHub
|
1. Go to your fork on GitHub
|
||||||
2. You'll see a green "Compare & pull request" button - click it
|
2. You'll see a green "Compare & pull request" button - click it
|
||||||
3. Fill out the PR template:
|
3. Select the correct target branch:
|
||||||
- **Title**: Clear, descriptive title
|
- **`next` branch** for most contributions (features, docs, enhancements)
|
||||||
- **Description**: Explain what you changed and why
|
- **`main` branch** only for critical fixes
|
||||||
|
4. Fill out the PR description using the template in CONTRIBUTING.md:
|
||||||
|
- **What**: 1-2 sentences describing what changed
|
||||||
|
- **Why**: 1-2 sentences explaining why
|
||||||
|
- **How**: 2-3 bullets on implementation
|
||||||
|
- **Testing**: How you tested
|
||||||
|
5. Reference the related issue number (e.g., "Fixes #123")
|
||||||
|
|
||||||
### 8. Wait for Review
|
### 8. Wait for Review
|
||||||
|
|
||||||
@@ -117,9 +131,12 @@ git push origin fix/typo-in-readme
|
|||||||
|
|
||||||
## Need Help?
|
## Need Help?
|
||||||
|
|
||||||
- 💬 Join our [Discord Community](https://discord.gg/g6ypHytrCB) for real-time help
|
- 💬 Join our [Discord Community](https://discord.gg/g6ypHytrCB) for real-time help:
|
||||||
- 💬 Ask questions in [Discussions](https://github.com/bmadcode/bmad-method/discussions)
|
- **#general-dev** - Technical questions and feature discussions
|
||||||
- 🐛 Report bugs in [Issues](https://github.com/bmadcode/bmad-method/issues)
|
- **#bugs-issues** - Get help with bugs before filing issues
|
||||||
|
- 💬 Ask questions in [GitHub Discussions](https://github.com/bmadcode/bmad-method/discussions)
|
||||||
|
- 🐛 Report bugs using the [bug report template](https://github.com/bmadcode/bmad-method/issues/new?template=bug_report.md)
|
||||||
|
- 💡 Suggest features using the [feature request template](https://github.com/bmadcode/bmad-method/issues/new?template=feature_request.md)
|
||||||
- 📖 Read the full [Contributing Guidelines](../CONTRIBUTING.md)
|
- 📖 Read the full [Contributing Guidelines](../CONTRIBUTING.md)
|
||||||
|
|
||||||
## Example: Good vs Bad PRs
|
## Example: Good vs Bad PRs
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
name: bmad-2d-phaser-game-dev
|
name: bmad-2d-phaser-game-dev
|
||||||
version: 1.1.0
|
version: 1.1.2
|
||||||
short-title: 2D game development with Phaser 3 & TypeScript
|
short-title: 2D game development with Phaser 3 & TypeScript
|
||||||
description: 2D Game Development expansion pack for BMAD Method - Phaser 3 & TypeScript focused
|
description: >-
|
||||||
|
2D Game Development expansion pack for BMAD Method - Phaser 3 & TypeScript
|
||||||
|
focused
|
||||||
author: Brian (BMad)
|
author: Brian (BMad)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
name: bmad-creator-tools
|
name: bmad-creator-tools
|
||||||
version: 1.0.0
|
version: 1.0.2
|
||||||
short-title: Tools for creating BMAD framework components
|
short-title: Tools for creating BMAD framework components
|
||||||
description: Tools for creating and extending BMAD framework components.
|
description: Tools for creating and extending BMAD framework components.
|
||||||
author: Brian (BMad)
|
author: Brian (BMad)
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
name: bmad-infrastructure-devops
|
name: bmad-infrastructure-devops
|
||||||
version: 1.0.0
|
version: 1.0.2
|
||||||
short-title: Infrastructure and DevOps capabilities
|
short-title: Infrastructure and DevOps capabilities
|
||||||
description: This expansion pack extends BMAD Method with comprehensive infrastructure and DevOps capabilities. It's designed for teams that need to define, implement, and manage cloud infrastructure alongside their application development.
|
description: >-
|
||||||
|
This expansion pack extends BMAD Method with comprehensive infrastructure and
|
||||||
|
DevOps capabilities. It's designed for teams that need to define, implement,
|
||||||
|
and manage cloud infrastructure alongside their application development.
|
||||||
author: Brian (BMad)
|
author: Brian (BMad)
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "bmad-method",
|
"name": "bmad-method",
|
||||||
"version": "4.20.0",
|
"version": "4.21.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "bmad-method",
|
"name": "bmad-method",
|
||||||
"version": "4.20.0",
|
"version": "4.21.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@kayvan/markdown-tree-parser": "^1.5.0",
|
"@kayvan/markdown-tree-parser": "^1.5.0",
|
||||||
|
|||||||
16
package.json
16
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "bmad-method",
|
"name": "bmad-method",
|
||||||
"version": "4.20.0",
|
"version": "4.21.0",
|
||||||
"description": "Breakthrough Method of Agile AI-driven Development",
|
"description": "Breakthrough Method of Agile AI-driven Development",
|
||||||
"main": "tools/cli.js",
|
"main": "tools/cli.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -18,6 +18,20 @@
|
|||||||
"version:patch": "node tools/version-bump.js patch",
|
"version:patch": "node tools/version-bump.js patch",
|
||||||
"version:minor": "node tools/version-bump.js minor",
|
"version:minor": "node tools/version-bump.js minor",
|
||||||
"version:major": "node tools/version-bump.js major",
|
"version:major": "node tools/version-bump.js major",
|
||||||
|
"version:core": "node tools/bump-core-version.js",
|
||||||
|
"version:core:major": "node tools/bump-core-version.js major",
|
||||||
|
"version:core:minor": "node tools/bump-core-version.js minor",
|
||||||
|
"version:core:patch": "node tools/bump-core-version.js patch",
|
||||||
|
"version:expansion": "node tools/bump-expansion-version.js",
|
||||||
|
"version:expansion:set": "node tools/update-expansion-version.js",
|
||||||
|
"version:all": "node tools/bump-all-versions.js",
|
||||||
|
"version:all:minor": "node tools/bump-all-versions.js minor",
|
||||||
|
"version:all:major": "node tools/bump-all-versions.js major",
|
||||||
|
"version:all:patch": "node tools/bump-all-versions.js patch",
|
||||||
|
"version:expansion:all": "node tools/bump-all-versions.js",
|
||||||
|
"version:expansion:all:minor": "node tools/bump-all-versions.js minor",
|
||||||
|
"version:expansion:all:major": "node tools/bump-all-versions.js major",
|
||||||
|
"version:expansion:all:patch": "node tools/bump-all-versions.js patch",
|
||||||
"release": "semantic-release",
|
"release": "semantic-release",
|
||||||
"release:test": "semantic-release --dry-run --no-ci || echo 'Config test complete - authentication errors are expected locally'",
|
"release:test": "semantic-release --dry-run --no-ci || echo 'Config test complete - authentication errors are expected locally'",
|
||||||
"prepare": "husky"
|
"prepare": "husky"
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
// Test script to verify IDE setup paths for expansion pack agents
|
|
||||||
const path = require('path');
|
|
||||||
const fs = require('fs-extra');
|
|
||||||
|
|
||||||
// Simulate the findAgentPath logic
|
|
||||||
function simulateFindAgentPath(agentId, installDir) {
|
|
||||||
const possiblePaths = [
|
|
||||||
path.join(installDir, ".bmad-core", "agents", `${agentId}.md`),
|
|
||||||
path.join(installDir, "agents", `${agentId}.md`),
|
|
||||||
// Expansion pack paths
|
|
||||||
path.join(installDir, ".bmad-2d-phaser-game-dev", "agents", `${agentId}.md`),
|
|
||||||
path.join(installDir, ".bmad-infrastructure-devops", "agents", `${agentId}.md`),
|
|
||||||
path.join(installDir, ".bmad-creator-tools", "agents", `${agentId}.md`)
|
|
||||||
];
|
|
||||||
|
|
||||||
// Simulate finding the agent in an expansion pack
|
|
||||||
if (agentId === 'game-developer') {
|
|
||||||
return path.join(installDir, ".bmad-2d-phaser-game-dev", "agents", `${agentId}.md`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default to core
|
|
||||||
return path.join(installDir, ".bmad-core", "agents", `${agentId}.md`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test different scenarios
|
|
||||||
const testDir = '/project';
|
|
||||||
const agents = ['dev', 'game-developer', 'infra-devops-platform'];
|
|
||||||
|
|
||||||
console.log('Testing IDE path references:\n');
|
|
||||||
|
|
||||||
agents.forEach(agentId => {
|
|
||||||
const agentPath = simulateFindAgentPath(agentId, testDir);
|
|
||||||
const relativePath = path.relative(testDir, agentPath).replace(/\\/g, '/');
|
|
||||||
|
|
||||||
console.log(`Agent: ${agentId}`);
|
|
||||||
console.log(` Full path: ${agentPath}`);
|
|
||||||
console.log(` Relative path: ${relativePath}`);
|
|
||||||
console.log(` Roo customInstructions: CRITICAL Read the full YML from ${relativePath} ...`);
|
|
||||||
console.log(` Cursor MDC reference: [${relativePath}](mdc:${relativePath})`);
|
|
||||||
console.log('');
|
|
||||||
});
|
|
||||||
107
tools/bump-all-versions.js
Executable file
107
tools/bump-all-versions.js
Executable file
@@ -0,0 +1,107 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const yaml = require('js-yaml');
|
||||||
|
|
||||||
|
const args = process.argv.slice(2);
|
||||||
|
const bumpType = args[0] || 'minor'; // default to minor
|
||||||
|
|
||||||
|
if (!['major', 'minor', 'patch'].includes(bumpType)) {
|
||||||
|
console.log('Usage: node bump-all-versions.js [major|minor|patch]');
|
||||||
|
console.log('Default: minor');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function bumpVersion(currentVersion, type) {
|
||||||
|
const [major, minor, patch] = currentVersion.split('.').map(Number);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case 'major':
|
||||||
|
return `${major + 1}.0.0`;
|
||||||
|
case 'minor':
|
||||||
|
return `${major}.${minor + 1}.0`;
|
||||||
|
case 'patch':
|
||||||
|
return `${major}.${minor}.${patch + 1}`;
|
||||||
|
default:
|
||||||
|
return currentVersion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function bumpAllVersions() {
|
||||||
|
const updatedItems = [];
|
||||||
|
|
||||||
|
// First, bump the core version
|
||||||
|
const coreConfigPath = path.join(__dirname, '..', 'bmad-core', 'core-config.yml');
|
||||||
|
try {
|
||||||
|
const coreConfigContent = fs.readFileSync(coreConfigPath, 'utf8');
|
||||||
|
const coreConfig = yaml.load(coreConfigContent);
|
||||||
|
const oldCoreVersion = coreConfig.version || '1.0.0';
|
||||||
|
const newCoreVersion = bumpVersion(oldCoreVersion, bumpType);
|
||||||
|
|
||||||
|
coreConfig.version = newCoreVersion;
|
||||||
|
|
||||||
|
const updatedCoreYaml = yaml.dump(coreConfig, { indent: 2 });
|
||||||
|
fs.writeFileSync(coreConfigPath, updatedCoreYaml);
|
||||||
|
|
||||||
|
updatedItems.push({ type: 'core', name: 'BMad Core', oldVersion: oldCoreVersion, newVersion: newCoreVersion });
|
||||||
|
console.log(`✓ BMad Core: ${oldCoreVersion} → ${newCoreVersion}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`✗ Failed to update BMad Core: ${error.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then, bump all expansion packs
|
||||||
|
const expansionPacksDir = path.join(__dirname, '..', 'expansion-packs');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const entries = fs.readdirSync(expansionPacksDir, { withFileTypes: true });
|
||||||
|
|
||||||
|
for (const entry of entries) {
|
||||||
|
if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'README.md') {
|
||||||
|
const packId = entry.name;
|
||||||
|
const configPath = path.join(expansionPacksDir, packId, 'config.yml');
|
||||||
|
|
||||||
|
if (fs.existsSync(configPath)) {
|
||||||
|
try {
|
||||||
|
const configContent = fs.readFileSync(configPath, 'utf8');
|
||||||
|
const config = yaml.load(configContent);
|
||||||
|
const oldVersion = config.version || '1.0.0';
|
||||||
|
const newVersion = bumpVersion(oldVersion, bumpType);
|
||||||
|
|
||||||
|
config.version = newVersion;
|
||||||
|
|
||||||
|
const updatedYaml = yaml.dump(config, { indent: 2 });
|
||||||
|
fs.writeFileSync(configPath, updatedYaml);
|
||||||
|
|
||||||
|
updatedItems.push({ type: 'expansion', name: packId, oldVersion, newVersion });
|
||||||
|
console.log(`✓ ${packId}: ${oldVersion} → ${newVersion}`);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`✗ Failed to update ${packId}: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updatedItems.length > 0) {
|
||||||
|
const coreCount = updatedItems.filter(i => i.type === 'core').length;
|
||||||
|
const expansionCount = updatedItems.filter(i => i.type === 'expansion').length;
|
||||||
|
|
||||||
|
console.log(`\n✓ Successfully bumped ${updatedItems.length} item(s) with ${bumpType} version bump`);
|
||||||
|
if (coreCount > 0) console.log(` - ${coreCount} core`);
|
||||||
|
if (expansionCount > 0) console.log(` - ${expansionCount} expansion pack(s)`);
|
||||||
|
|
||||||
|
console.log('\nNext steps:');
|
||||||
|
console.log('1. Test the changes');
|
||||||
|
console.log('2. Commit: git add -A && git commit -m "chore: bump all versions (' + bumpType + ')"');
|
||||||
|
} else {
|
||||||
|
console.log('No items found to update');
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error reading expansion packs directory:', error.message);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bumpAllVersions();
|
||||||
57
tools/bump-core-version.js
Normal file
57
tools/bump-core-version.js
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const yaml = require('js-yaml');
|
||||||
|
|
||||||
|
const args = process.argv.slice(2);
|
||||||
|
const bumpType = args[0] || 'minor'; // default to minor
|
||||||
|
|
||||||
|
if (!['major', 'minor', 'patch'].includes(bumpType)) {
|
||||||
|
console.log('Usage: node bump-core-version.js [major|minor|patch]');
|
||||||
|
console.log('Default: minor');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function bumpVersion(currentVersion, type) {
|
||||||
|
const [major, minor, patch] = currentVersion.split('.').map(Number);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case 'major':
|
||||||
|
return `${major + 1}.0.0`;
|
||||||
|
case 'minor':
|
||||||
|
return `${major}.${minor + 1}.0`;
|
||||||
|
case 'patch':
|
||||||
|
return `${major}.${minor}.${patch + 1}`;
|
||||||
|
default:
|
||||||
|
return currentVersion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function bumpCoreVersion() {
|
||||||
|
try {
|
||||||
|
const coreConfigPath = path.join(__dirname, '..', 'bmad-core', 'core-config.yml');
|
||||||
|
|
||||||
|
const coreConfigContent = fs.readFileSync(coreConfigPath, 'utf8');
|
||||||
|
const coreConfig = yaml.load(coreConfigContent);
|
||||||
|
const oldVersion = coreConfig.version || '1.0.0';
|
||||||
|
const newVersion = bumpVersion(oldVersion, bumpType);
|
||||||
|
|
||||||
|
coreConfig.version = newVersion;
|
||||||
|
|
||||||
|
const updatedYaml = yaml.dump(coreConfig, { indent: 2 });
|
||||||
|
fs.writeFileSync(coreConfigPath, updatedYaml);
|
||||||
|
|
||||||
|
console.log(`✓ BMad Core: ${oldVersion} → ${newVersion}`);
|
||||||
|
console.log(`\n✓ Successfully bumped BMad Core with ${bumpType} version bump`);
|
||||||
|
console.log('\nNext steps:');
|
||||||
|
console.log('1. Test the changes');
|
||||||
|
console.log('2. Commit: git add -A && git commit -m "chore: bump core version (' + bumpType + ')"');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error updating core version:', error.message);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bumpCoreVersion();
|
||||||
78
tools/bump-expansion-version.js
Normal file
78
tools/bump-expansion-version.js
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const yaml = require('js-yaml');
|
||||||
|
|
||||||
|
const args = process.argv.slice(2);
|
||||||
|
|
||||||
|
if (args.length < 1 || args.length > 2) {
|
||||||
|
console.log('Usage: node bump-expansion-version.js <expansion-pack-id> [major|minor|patch]');
|
||||||
|
console.log('Default: minor');
|
||||||
|
console.log('Example: node bump-expansion-version.js bmad-creator-tools patch');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const packId = args[0];
|
||||||
|
const bumpType = args[1] || 'minor'; // default to minor
|
||||||
|
|
||||||
|
if (!['major', 'minor', 'patch'].includes(bumpType)) {
|
||||||
|
console.error('Error: Bump type must be major, minor, or patch');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function bumpVersion(currentVersion, type) {
|
||||||
|
const [major, minor, patch] = currentVersion.split('.').map(Number);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case 'major':
|
||||||
|
return `${major + 1}.0.0`;
|
||||||
|
case 'minor':
|
||||||
|
return `${major}.${minor + 1}.0`;
|
||||||
|
case 'patch':
|
||||||
|
return `${major}.${minor}.${patch + 1}`;
|
||||||
|
default:
|
||||||
|
return currentVersion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateVersion() {
|
||||||
|
try {
|
||||||
|
const configPath = path.join(__dirname, '..', 'expansion-packs', packId, 'config.yml');
|
||||||
|
|
||||||
|
if (!fs.existsSync(configPath)) {
|
||||||
|
console.error(`Error: Expansion pack '${packId}' not found`);
|
||||||
|
console.log('\nAvailable expansion packs:');
|
||||||
|
const expansionPacksDir = path.join(__dirname, '..', 'expansion-packs');
|
||||||
|
const entries = fs.readdirSync(expansionPacksDir, { withFileTypes: true });
|
||||||
|
entries.forEach(entry => {
|
||||||
|
if (entry.isDirectory() && !entry.name.startsWith('.')) {
|
||||||
|
console.log(` - ${entry.name}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const configContent = fs.readFileSync(configPath, 'utf8');
|
||||||
|
const config = yaml.load(configContent);
|
||||||
|
const oldVersion = config.version || '1.0.0';
|
||||||
|
const newVersion = bumpVersion(oldVersion, bumpType);
|
||||||
|
|
||||||
|
config.version = newVersion;
|
||||||
|
|
||||||
|
const updatedYaml = yaml.dump(config, { indent: 2 });
|
||||||
|
fs.writeFileSync(configPath, updatedYaml);
|
||||||
|
|
||||||
|
console.log(`✓ ${packId}: ${oldVersion} → ${newVersion}`);
|
||||||
|
console.log(`\n✓ Successfully bumped ${packId} with ${bumpType} version bump`);
|
||||||
|
console.log('\nNext steps:');
|
||||||
|
console.log('1. Test the changes');
|
||||||
|
console.log('2. Commit: git add -A && git commit -m "chore: bump ' + packId + ' version (' + bumpType + ')"');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error updating version:', error.message);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateVersion();
|
||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
const { program } = require('commander');
|
const { program } = require('commander');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
const fs = require('fs').promises;
|
||||||
|
const yaml = require('js-yaml');
|
||||||
|
|
||||||
// Dynamic imports for ES modules
|
// Dynamic imports for ES modules
|
||||||
let chalk, inquirer;
|
let chalk, inquirer;
|
||||||
@@ -45,17 +47,15 @@ program
|
|||||||
program
|
program
|
||||||
.command('install')
|
.command('install')
|
||||||
.description('Install BMAD Method agents and tools')
|
.description('Install BMAD Method agents and tools')
|
||||||
.option('-f, --full', 'Install complete .bmad-core folder')
|
.option('-f, --full', 'Install complete BMAD Method')
|
||||||
.option('-a, --agent <agent>', 'Install specific agent with dependencies')
|
|
||||||
.option('-t, --team <team>', 'Install specific team with required agents and dependencies')
|
|
||||||
.option('-x, --expansion-only', 'Install only expansion packs (no bmad-core)')
|
.option('-x, --expansion-only', 'Install only expansion packs (no bmad-core)')
|
||||||
.option('-d, --directory <path>', 'Installation directory (default: .bmad-core)')
|
.option('-d, --directory <path>', 'Installation directory')
|
||||||
.option('-i, --ide <ide...>', 'Configure for specific IDE(s) - can specify multiple (cursor, claude-code, windsurf, roo, cline, gemini, other)')
|
.option('-i, --ide <ide...>', 'Configure for specific IDE(s) - can specify multiple (cursor, claude-code, windsurf, roo, cline, gemini, other)')
|
||||||
.option('-e, --expansion-packs <packs...>', 'Install specific expansion packs (can specify multiple)')
|
.option('-e, --expansion-packs <packs...>', 'Install specific expansion packs (can specify multiple)')
|
||||||
.action(async (options) => {
|
.action(async (options) => {
|
||||||
try {
|
try {
|
||||||
await initializeModules();
|
await initializeModules();
|
||||||
if (!options.full && !options.agent && !options.team && !options.expansionOnly) {
|
if (!options.full && !options.expansionOnly) {
|
||||||
// Interactive mode
|
// Interactive mode
|
||||||
const answers = await promptInstallation();
|
const answers = await promptInstallation();
|
||||||
if (!answers._alreadyInstalled) {
|
if (!answers._alreadyInstalled) {
|
||||||
@@ -64,15 +64,11 @@ program
|
|||||||
} else {
|
} else {
|
||||||
// Direct mode
|
// Direct mode
|
||||||
let installType = 'full';
|
let installType = 'full';
|
||||||
if (options.agent) installType = 'single-agent';
|
if (options.expansionOnly) installType = 'expansion-only';
|
||||||
else if (options.team) installType = 'team';
|
|
||||||
else if (options.expansionOnly) installType = 'expansion-only';
|
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
installType,
|
installType,
|
||||||
agent: options.agent,
|
directory: options.directory || '.',
|
||||||
team: options.team,
|
|
||||||
directory: options.directory || '.bmad-core',
|
|
||||||
ides: (options.ide || []).filter(ide => ide !== 'other'),
|
ides: (options.ide || []).filter(ide => ide !== 'other'),
|
||||||
expansionPacks: options.expansionPacks || []
|
expansionPacks: options.expansionPacks || []
|
||||||
};
|
};
|
||||||
@@ -100,19 +96,6 @@ program
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
program
|
|
||||||
.command('list')
|
|
||||||
.description('List available agents')
|
|
||||||
.action(async () => {
|
|
||||||
try {
|
|
||||||
await installer.listAgents();
|
|
||||||
} catch (error) {
|
|
||||||
if (!chalk) await initializeModules();
|
|
||||||
console.error(chalk.red('Error:'), error.message);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
program
|
program
|
||||||
.command('list:expansions')
|
.command('list:expansions')
|
||||||
.description('List available expansion packs')
|
.description('List available expansion packs')
|
||||||
@@ -145,7 +128,7 @@ async function promptInstallation() {
|
|||||||
|
|
||||||
const answers = {};
|
const answers = {};
|
||||||
|
|
||||||
// Ask for installation directory
|
// Ask for installation directory first
|
||||||
const { directory } = await inquirer.prompt([
|
const { directory } = await inquirer.prompt([
|
||||||
{
|
{
|
||||||
type: 'input',
|
type: 'input',
|
||||||
@@ -161,147 +144,85 @@ async function promptInstallation() {
|
|||||||
]);
|
]);
|
||||||
answers.directory = directory;
|
answers.directory = directory;
|
||||||
|
|
||||||
// Check if this is an existing v4 installation
|
// Detect existing installations
|
||||||
const installDir = path.resolve(answers.directory);
|
const installDir = path.resolve(directory);
|
||||||
const state = await installer.detectInstallationState(installDir);
|
const state = await installer.detectInstallationState(installDir);
|
||||||
|
|
||||||
|
// Check for existing expansion packs
|
||||||
|
const existingExpansionPacks = state.expansionPacks || {};
|
||||||
|
|
||||||
|
// Get available expansion packs
|
||||||
|
const availableExpansionPacks = await installer.getAvailableExpansionPacks();
|
||||||
|
|
||||||
|
// Build choices list
|
||||||
|
const choices = [];
|
||||||
|
|
||||||
|
// Load core config to get short-title
|
||||||
|
const coreConfigPath = path.join(__dirname, '..', '..', '..', 'bmad-core', 'core-config.yml');
|
||||||
|
const coreConfig = yaml.load(await fs.readFile(coreConfigPath, 'utf8'));
|
||||||
|
const coreShortTitle = coreConfig['short-title'] || 'BMad Agile Core System';
|
||||||
|
|
||||||
|
// Add BMAD core option
|
||||||
|
let bmadOptionText;
|
||||||
if (state.type === 'v4_existing') {
|
if (state.type === 'v4_existing') {
|
||||||
console.log(chalk.yellow('\n🔍 Found existing BMAD v4 installation'));
|
const currentVersion = state.manifest?.version || 'unknown';
|
||||||
console.log(` Directory: ${installDir}`);
|
const newVersion = coreConfig.version || 'unknown'; // Use version from core-config.yml
|
||||||
console.log(` Version: ${state.manifest?.version || 'Unknown'}`);
|
const versionInfo = currentVersion === newVersion
|
||||||
console.log(` Installed: ${state.manifest?.installed_at ? new Date(state.manifest.installed_at).toLocaleDateString() : 'Unknown'}`);
|
? `(v${currentVersion} - reinstall)`
|
||||||
|
: `(v${currentVersion} → v${newVersion})`;
|
||||||
const { shouldUpdate } = await inquirer.prompt([
|
bmadOptionText = `Update ${coreShortTitle} ${versionInfo} .bmad-core`;
|
||||||
{
|
} else {
|
||||||
type: 'confirm',
|
bmadOptionText = `Install ${coreShortTitle} (v${coreConfig.version || version}) .bmad-core`;
|
||||||
name: 'shouldUpdate',
|
|
||||||
message: 'Would you like to update your existing BMAD v4 installation?',
|
|
||||||
default: true
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (shouldUpdate) {
|
|
||||||
// Skip other prompts and go directly to update
|
|
||||||
answers.installType = 'update';
|
|
||||||
answers._alreadyInstalled = true; // Flag to prevent double installation
|
|
||||||
await installer.install(answers);
|
|
||||||
return answers; // Return the answers object
|
|
||||||
}
|
|
||||||
// If user doesn't want to update, continue with normal flow
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ask for installation type
|
choices.push({
|
||||||
const { installType } = await inquirer.prompt([
|
name: bmadOptionText,
|
||||||
|
value: 'bmad-core',
|
||||||
|
checked: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add expansion pack options
|
||||||
|
for (const pack of availableExpansionPacks) {
|
||||||
|
const existing = existingExpansionPacks[pack.id];
|
||||||
|
let packOptionText;
|
||||||
|
|
||||||
|
if (existing) {
|
||||||
|
const currentVersion = existing.manifest?.version || 'unknown';
|
||||||
|
const newVersion = pack.version;
|
||||||
|
const versionInfo = currentVersion === newVersion
|
||||||
|
? `(v${currentVersion} - reinstall)`
|
||||||
|
: `(v${currentVersion} → v${newVersion})`;
|
||||||
|
packOptionText = `Update ${pack.description} ${versionInfo} .${pack.id}`;
|
||||||
|
} else {
|
||||||
|
packOptionText = `Install ${pack.description} (v${pack.version}) .${pack.id}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
choices.push({
|
||||||
|
name: packOptionText,
|
||||||
|
value: pack.id,
|
||||||
|
checked: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ask what to install
|
||||||
|
const { selectedItems } = await inquirer.prompt([
|
||||||
{
|
{
|
||||||
type: 'list',
|
type: 'checkbox',
|
||||||
name: 'installType',
|
name: 'selectedItems',
|
||||||
message: 'How would you like to install BMAD?',
|
message: 'Select what to install/update (use space to select, enter to continue):',
|
||||||
choices: [
|
choices: choices,
|
||||||
{
|
validate: (selected) => {
|
||||||
name: 'Complete installation (recommended) - All agents and tools',
|
if (selected.length === 0) {
|
||||||
value: 'full'
|
return 'Please select at least one item to install';
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Team installation - Install a specific team with required agents',
|
|
||||||
value: 'team'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Single agent - Choose one agent to install',
|
|
||||||
value: 'single-agent'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Expansion packs only - Install expansion packs without bmad-core',
|
|
||||||
value: 'expansion-only'
|
|
||||||
}
|
}
|
||||||
]
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
answers.installType = installType;
|
|
||||||
|
// Process selections
|
||||||
// If single agent, ask which one
|
answers.installType = selectedItems.includes('bmad-core') ? 'full' : 'expansion-only';
|
||||||
if (installType === 'single-agent') {
|
answers.expansionPacks = selectedItems.filter(item => item !== 'bmad-core');
|
||||||
const agents = await installer.getAvailableAgents();
|
|
||||||
const { agent } = await inquirer.prompt([
|
|
||||||
{
|
|
||||||
type: 'list',
|
|
||||||
name: 'agent',
|
|
||||||
message: 'Select an agent to install:',
|
|
||||||
choices: agents.map(a => ({
|
|
||||||
name: `${a.id} - ${a.name} (${a.description})`,
|
|
||||||
value: a.id
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
answers.agent = agent;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If team installation, ask which team
|
|
||||||
if (installType === 'team') {
|
|
||||||
const teams = await installer.getAvailableTeams();
|
|
||||||
const { team } = await inquirer.prompt([
|
|
||||||
{
|
|
||||||
type: 'list',
|
|
||||||
name: 'team',
|
|
||||||
message: 'Select a team to install in your IDE project folder:',
|
|
||||||
choices: teams.map(t => ({
|
|
||||||
name: `${t.icon || '📋'} ${t.name}: ${t.description}`,
|
|
||||||
value: t.id
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
answers.team = team;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ask for expansion pack selection
|
|
||||||
if (installType === 'full' || installType === 'team' || installType === 'expansion-only') {
|
|
||||||
try {
|
|
||||||
const availableExpansionPacks = await installer.getAvailableExpansionPacks();
|
|
||||||
|
|
||||||
if (availableExpansionPacks.length > 0) {
|
|
||||||
let choices;
|
|
||||||
let message;
|
|
||||||
|
|
||||||
if (installType === 'expansion-only') {
|
|
||||||
message = 'Select expansion packs to install (required):'
|
|
||||||
choices = availableExpansionPacks.map(pack => ({
|
|
||||||
name: `${pack.name} - ${pack.description}`,
|
|
||||||
value: pack.id
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
message = 'Select expansion packs to install (press Enter to skip, or check any to install):';
|
|
||||||
choices = availableExpansionPacks.map(pack => ({
|
|
||||||
name: `${pack.name} - ${pack.description}`,
|
|
||||||
value: pack.id
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
const { expansionPacks } = await inquirer.prompt([
|
|
||||||
{
|
|
||||||
type: 'checkbox',
|
|
||||||
name: 'expansionPacks',
|
|
||||||
message,
|
|
||||||
choices,
|
|
||||||
validate: installType === 'expansion-only' ? (answer) => {
|
|
||||||
if (answer.length < 1) {
|
|
||||||
return 'You must select at least one expansion pack for expansion-only installation.';
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} : undefined
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Use selected expansion packs directly
|
|
||||||
answers.expansionPacks = expansionPacks;
|
|
||||||
} else {
|
|
||||||
answers.expansionPacks = [];
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.warn(chalk.yellow('Warning: Could not load expansion packs. Continuing without them.'));
|
|
||||||
answers.expansionPacks = [];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
answers.expansionPacks = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ask for IDE configuration
|
// Ask for IDE configuration
|
||||||
const { ides } = await inquirer.prompt([
|
const { ides } = await inquirer.prompt([
|
||||||
|
|||||||
@@ -83,12 +83,22 @@ class FileManager {
|
|||||||
this.manifestFile
|
this.manifestFile
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Read version from core-config.yml
|
||||||
|
const coreConfigPath = path.join(__dirname, "../../../bmad-core/core-config.yml");
|
||||||
|
let coreVersion = "unknown";
|
||||||
|
try {
|
||||||
|
const coreConfigContent = await fs.readFile(coreConfigPath, "utf8");
|
||||||
|
const coreConfig = yaml.load(coreConfigContent);
|
||||||
|
coreVersion = coreConfig.version || "unknown";
|
||||||
|
} catch (error) {
|
||||||
|
console.warn("Could not read version from core-config.yml, using 'unknown'");
|
||||||
|
}
|
||||||
|
|
||||||
const manifest = {
|
const manifest = {
|
||||||
version: require("../../../package.json").version,
|
version: coreVersion,
|
||||||
installed_at: new Date().toISOString(),
|
installed_at: new Date().toISOString(),
|
||||||
install_type: config.installType,
|
install_type: config.installType,
|
||||||
agent: config.agent || null,
|
agent: config.agent || null,
|
||||||
ide_setup: config.ide || null,
|
|
||||||
ides_setup: config.ides || [],
|
ides_setup: config.ides || [],
|
||||||
expansion_packs: config.expansionPacks || [],
|
expansion_packs: config.expansionPacks || [],
|
||||||
files: [],
|
files: [],
|
||||||
@@ -128,6 +138,21 @@ class FileManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async readExpansionPackManifest(installDir, packId) {
|
||||||
|
const manifestPath = path.join(
|
||||||
|
installDir,
|
||||||
|
`.${packId}`,
|
||||||
|
this.manifestFile
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const content = await fs.readFile(manifestPath, "utf8");
|
||||||
|
return yaml.load(content);
|
||||||
|
} catch (error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async checkModifiedFiles(installDir, manifest) {
|
async checkModifiedFiles(installDir, manifest) {
|
||||||
const modified = [];
|
const modified = [];
|
||||||
|
|
||||||
@@ -143,6 +168,33 @@ class FileManager {
|
|||||||
return modified;
|
return modified;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async checkFileIntegrity(installDir, manifest) {
|
||||||
|
const result = {
|
||||||
|
missing: [],
|
||||||
|
modified: []
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const file of manifest.files) {
|
||||||
|
const filePath = path.join(installDir, file.path);
|
||||||
|
|
||||||
|
// Skip checking the manifest file itself - it will always be different due to timestamps
|
||||||
|
if (file.path.endsWith('install-manifest.yml')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(await this.pathExists(filePath))) {
|
||||||
|
result.missing.push(file.path);
|
||||||
|
} else {
|
||||||
|
const currentHash = await this.calculateFileHash(filePath);
|
||||||
|
if (currentHash && currentHash !== file.hash) {
|
||||||
|
result.modified.push(file.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
async backupFile(filePath) {
|
async backupFile(filePath) {
|
||||||
const backupPath = filePath + ".bak";
|
const backupPath = filePath + ".bak";
|
||||||
let counter = 1;
|
let counter = 1;
|
||||||
@@ -183,6 +235,42 @@ class FileManager {
|
|||||||
async removeDirectory(dirPath) {
|
async removeDirectory(dirPath) {
|
||||||
await fs.remove(dirPath);
|
await fs.remove(dirPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async createExpansionPackManifest(installDir, packId, config, files) {
|
||||||
|
const manifestPath = path.join(
|
||||||
|
installDir,
|
||||||
|
`.${packId}`,
|
||||||
|
this.manifestFile
|
||||||
|
);
|
||||||
|
|
||||||
|
const manifest = {
|
||||||
|
version: config.expansionPackVersion || require("../../../package.json").version,
|
||||||
|
installed_at: new Date().toISOString(),
|
||||||
|
install_type: config.installType,
|
||||||
|
expansion_pack_id: config.expansionPackId,
|
||||||
|
expansion_pack_name: config.expansionPackName,
|
||||||
|
ides_setup: config.ides || [],
|
||||||
|
files: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add file information
|
||||||
|
for (const file of files) {
|
||||||
|
const filePath = path.join(installDir, file);
|
||||||
|
const hash = await this.calculateFileHash(filePath);
|
||||||
|
|
||||||
|
manifest.files.push({
|
||||||
|
path: file,
|
||||||
|
hash: hash,
|
||||||
|
modified: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write manifest
|
||||||
|
await fs.ensureDir(path.dirname(manifestPath));
|
||||||
|
await fs.writeFile(manifestPath, yaml.dump(manifest, { indent: 2 }));
|
||||||
|
|
||||||
|
return manifest;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = new FileManager();
|
module.exports = new FileManager();
|
||||||
|
|||||||
@@ -16,6 +16,20 @@ async function initializeModules() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Installer {
|
class Installer {
|
||||||
|
async getCoreVersion() {
|
||||||
|
const yaml = require("js-yaml");
|
||||||
|
const fs = require("fs-extra");
|
||||||
|
const coreConfigPath = path.join(__dirname, "../../../bmad-core/core-config.yml");
|
||||||
|
try {
|
||||||
|
const coreConfigContent = await fs.readFile(coreConfigPath, "utf8");
|
||||||
|
const coreConfig = yaml.load(coreConfigContent);
|
||||||
|
return coreConfig.version || "unknown";
|
||||||
|
} catch (error) {
|
||||||
|
console.warn("Could not read version from core-config.yml, using 'unknown'");
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async install(config) {
|
async install(config) {
|
||||||
// Initialize ES modules
|
// Initialize ES modules
|
||||||
await initializeModules();
|
await initializeModules();
|
||||||
@@ -161,6 +175,7 @@ class Installer {
|
|||||||
hasBmadCore: false,
|
hasBmadCore: false,
|
||||||
hasOtherFiles: false,
|
hasOtherFiles: false,
|
||||||
manifest: null,
|
manifest: null,
|
||||||
|
expansionPacks: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check if directory exists
|
// Check if directory exists
|
||||||
@@ -209,10 +224,14 @@ class Installer {
|
|||||||
state.hasOtherFiles = true;
|
state.hasOtherFiles = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for expansion packs (folders starting with .)
|
||||||
|
const expansionPacks = await this.detectExpansionPacks(installDir);
|
||||||
|
state.expansionPacks = expansionPacks;
|
||||||
|
|
||||||
return state; // clean install
|
return state; // clean install
|
||||||
}
|
}
|
||||||
|
|
||||||
async performFreshInstall(config, installDir, spinner) {
|
async performFreshInstall(config, installDir, spinner, options = {}) {
|
||||||
// Ensure modules are initialized
|
// Ensure modules are initialized
|
||||||
await initializeModules();
|
await initializeModules();
|
||||||
spinner.text = "Installing BMAD Method...";
|
spinner.text = "Installing BMAD Method...";
|
||||||
@@ -328,41 +347,13 @@ class Installer {
|
|||||||
const commonFiles = await this.copyCommonItems(installDir, ".bmad-core", spinner);
|
const commonFiles = await this.copyCommonItems(installDir, ".bmad-core", spinner);
|
||||||
files.push(...commonFiles);
|
files.push(...commonFiles);
|
||||||
} else if (config.installType === "expansion-only") {
|
} else if (config.installType === "expansion-only") {
|
||||||
// Expansion-only installation - create minimal .bmad-core structure
|
// Expansion-only installation - DO NOT create .bmad-core
|
||||||
spinner.text = "Creating minimal .bmad-core structure for expansion packs...";
|
// Only install expansion packs
|
||||||
|
spinner.text = "Installing expansion packs only...";
|
||||||
const bmadCoreDestDir = path.join(installDir, ".bmad-core");
|
|
||||||
await fileManager.ensureDirectory(bmadCoreDestDir);
|
|
||||||
|
|
||||||
// Create basic directory structure
|
|
||||||
const dirs = ['agents', 'agent-teams', 'templates', 'tasks', 'checklists', 'workflows', 'data', 'utils', 'schemas'];
|
|
||||||
for (const dir of dirs) {
|
|
||||||
await fileManager.ensureDirectory(path.join(bmadCoreDestDir, dir));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy minimal required files (schemas, utils, etc.)
|
|
||||||
const sourceBase = configLoader.getBmadCorePath();
|
|
||||||
const essentialFiles = [
|
|
||||||
'schemas/**/*',
|
|
||||||
'utils/**/*'
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const pattern of essentialFiles) {
|
|
||||||
const copiedFiles = await fileManager.copyGlobPattern(
|
|
||||||
pattern,
|
|
||||||
sourceBase,
|
|
||||||
bmadCoreDestDir
|
|
||||||
);
|
|
||||||
files.push(...copiedFiles.map(f => `.bmad-core/${f}`));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy common/ items to .bmad-core
|
|
||||||
spinner.text = "Copying common utilities...";
|
|
||||||
await this.copyCommonItems(installDir, ".bmad-core", spinner);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Install expansion packs if requested
|
// Install expansion packs if requested
|
||||||
const expansionFiles = await this.installExpansionPacks(installDir, config.expansionPacks, spinner);
|
const expansionFiles = await this.installExpansionPacks(installDir, config.expansionPacks, spinner, config);
|
||||||
files.push(...expansionFiles);
|
files.push(...expansionFiles);
|
||||||
|
|
||||||
// Install web bundles if requested
|
// Install web bundles if requested
|
||||||
@@ -385,12 +376,14 @@ class Installer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create manifest
|
// Create manifest (skip for expansion-only installations)
|
||||||
spinner.text = "Creating installation manifest...";
|
if (config.installType !== "expansion-only") {
|
||||||
await fileManager.createManifest(installDir, config, files);
|
spinner.text = "Creating installation manifest...";
|
||||||
|
await fileManager.createManifest(installDir, config, files);
|
||||||
|
}
|
||||||
|
|
||||||
spinner.succeed("Installation complete!");
|
spinner.succeed("Installation complete!");
|
||||||
this.showSuccessMessage(config, installDir);
|
this.showSuccessMessage(config, installDir, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleExistingV4Installation(config, installDir, state, spinner) {
|
async handleExistingV4Installation(config, installDir, state, spinner) {
|
||||||
@@ -398,33 +391,137 @@ class Installer {
|
|||||||
await initializeModules();
|
await initializeModules();
|
||||||
spinner.stop();
|
spinner.stop();
|
||||||
|
|
||||||
|
const currentVersion = state.manifest.version;
|
||||||
|
const newVersion = await this.getCoreVersion();
|
||||||
|
const versionCompare = this.compareVersions(currentVersion, newVersion);
|
||||||
|
|
||||||
console.log(chalk.yellow("\n🔍 Found existing BMAD v4 installation"));
|
console.log(chalk.yellow("\n🔍 Found existing BMAD v4 installation"));
|
||||||
console.log(` Directory: ${installDir}`);
|
console.log(` Directory: ${installDir}`);
|
||||||
console.log(` Version: ${state.manifest.version}`);
|
console.log(` Current version: ${currentVersion}`);
|
||||||
|
console.log(` Available version: ${newVersion}`);
|
||||||
console.log(
|
console.log(
|
||||||
` Installed: ${new Date(
|
` Installed: ${new Date(
|
||||||
state.manifest.installed_at
|
state.manifest.installed_at
|
||||||
).toLocaleDateString()}`
|
).toLocaleDateString()}`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Check file integrity
|
||||||
|
spinner.start("Checking installation integrity...");
|
||||||
|
const integrity = await fileManager.checkFileIntegrity(installDir, state.manifest);
|
||||||
|
spinner.stop();
|
||||||
|
|
||||||
|
const hasMissingFiles = integrity.missing.length > 0;
|
||||||
|
const hasModifiedFiles = integrity.modified.length > 0;
|
||||||
|
const hasIntegrityIssues = hasMissingFiles || hasModifiedFiles;
|
||||||
|
|
||||||
|
if (hasIntegrityIssues) {
|
||||||
|
console.log(chalk.red("\n⚠️ Installation issues detected:"));
|
||||||
|
if (hasMissingFiles) {
|
||||||
|
console.log(chalk.red(` Missing files: ${integrity.missing.length}`));
|
||||||
|
if (integrity.missing.length <= 5) {
|
||||||
|
integrity.missing.forEach(file => console.log(chalk.dim(` - ${file}`)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hasModifiedFiles) {
|
||||||
|
console.log(chalk.yellow(` Modified files: ${integrity.modified.length}`));
|
||||||
|
if (integrity.modified.length <= 5) {
|
||||||
|
integrity.modified.forEach(file => console.log(chalk.dim(` - ${file}`)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show existing expansion packs
|
||||||
|
if (Object.keys(state.expansionPacks).length > 0) {
|
||||||
|
console.log(chalk.cyan("\n📦 Installed expansion packs:"));
|
||||||
|
for (const [packId, packInfo] of Object.entries(state.expansionPacks)) {
|
||||||
|
if (packInfo.hasManifest && packInfo.manifest) {
|
||||||
|
console.log(` - ${packId} (v${packInfo.manifest.version || 'unknown'})`);
|
||||||
|
} else {
|
||||||
|
console.log(` - ${packId} (no manifest)`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let choices = [];
|
||||||
|
|
||||||
|
if (versionCompare < 0) {
|
||||||
|
console.log(chalk.cyan("\n⬆️ Upgrade available for BMAD core"));
|
||||||
|
choices.push({ name: `Upgrade BMAD core (v${currentVersion} → v${newVersion})`, value: "upgrade" });
|
||||||
|
} else if (versionCompare === 0) {
|
||||||
|
if (hasIntegrityIssues) {
|
||||||
|
// Offer repair option when files are missing or modified
|
||||||
|
choices.push({
|
||||||
|
name: "Repair installation (restore missing/modified files)",
|
||||||
|
value: "repair"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.log(chalk.yellow("\n⚠️ Same version already installed"));
|
||||||
|
choices.push({ name: `Force reinstall BMAD core (v${currentVersion} - reinstall)`, value: "reinstall" });
|
||||||
|
} else {
|
||||||
|
console.log(chalk.yellow("\n⬇️ Installed version is newer than available"));
|
||||||
|
choices.push({ name: `Downgrade BMAD core (v${currentVersion} → v${newVersion})`, value: "reinstall" });
|
||||||
|
}
|
||||||
|
|
||||||
|
choices.push(
|
||||||
|
{ name: "Add/update expansion packs only", value: "expansions" },
|
||||||
|
{ name: "Cancel", value: "cancel" }
|
||||||
|
);
|
||||||
|
|
||||||
const { action } = await inquirer.prompt([
|
const { action } = await inquirer.prompt([
|
||||||
{
|
{
|
||||||
type: "list",
|
type: "list",
|
||||||
name: "action",
|
name: "action",
|
||||||
message: "What would you like to do?",
|
message: "What would you like to do?",
|
||||||
choices: [
|
choices: choices,
|
||||||
{ name: "Update existing installation", value: "update" },
|
|
||||||
{ name: "Reinstall (overwrite)", value: "reinstall" },
|
|
||||||
{ name: "Cancel", value: "cancel" },
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case "update":
|
case "upgrade":
|
||||||
return await this.performUpdate(config, installDir, state.manifest, spinner);
|
return await this.performUpdate(config, installDir, state.manifest, spinner);
|
||||||
|
case "repair":
|
||||||
|
// For repair, restore missing/modified files while backing up modified ones
|
||||||
|
return await this.performRepair(config, installDir, state.manifest, integrity, spinner);
|
||||||
case "reinstall":
|
case "reinstall":
|
||||||
|
// For reinstall, don't check for modifications - just overwrite
|
||||||
return await this.performReinstall(config, installDir, spinner);
|
return await this.performReinstall(config, installDir, spinner);
|
||||||
|
case "expansions":
|
||||||
|
// Ask which expansion packs to install
|
||||||
|
const availableExpansionPacks = await this.getAvailableExpansionPacks();
|
||||||
|
|
||||||
|
if (availableExpansionPacks.length === 0) {
|
||||||
|
console.log(chalk.yellow("No expansion packs available."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { selectedPacks } = await inquirer.prompt([
|
||||||
|
{
|
||||||
|
type: 'checkbox',
|
||||||
|
name: 'selectedPacks',
|
||||||
|
message: 'Select expansion packs to install/update:',
|
||||||
|
choices: availableExpansionPacks.map(pack => ({
|
||||||
|
name: `${pack.name} v${pack.version} - ${pack.description}`,
|
||||||
|
value: pack.id,
|
||||||
|
checked: state.expansionPacks[pack.id] !== undefined
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (selectedPacks.length === 0) {
|
||||||
|
console.log(chalk.yellow("No expansion packs selected."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
spinner.start("Installing expansion packs...");
|
||||||
|
const expansionFiles = await this.installExpansionPacks(installDir, selectedPacks, spinner, { ides: config.ides || [] });
|
||||||
|
spinner.succeed("Expansion packs installed successfully!");
|
||||||
|
|
||||||
|
console.log(chalk.green("\n✓ Installation complete!"));
|
||||||
|
console.log(chalk.green(`✓ Expansion packs installed/updated:`));
|
||||||
|
for (const packId of selectedPacks) {
|
||||||
|
console.log(chalk.green(` - ${packId} → .${packId}/`));
|
||||||
|
}
|
||||||
|
return;
|
||||||
case "cancel":
|
case "cancel":
|
||||||
console.log("Installation cancelled.");
|
console.log("Installation cancelled.");
|
||||||
return;
|
return;
|
||||||
@@ -525,12 +622,20 @@ class Installer {
|
|||||||
spinner.start("Checking for updates...");
|
spinner.start("Checking for updates...");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Check for modified files
|
// Get current and new versions
|
||||||
spinner.text = "Checking for modified files...";
|
const currentVersion = manifest.version;
|
||||||
const modifiedFiles = await fileManager.checkModifiedFiles(
|
const newVersion = await this.getCoreVersion();
|
||||||
installDir,
|
const versionCompare = this.compareVersions(currentVersion, newVersion);
|
||||||
manifest
|
|
||||||
);
|
// Only check for modified files if it's an actual version upgrade
|
||||||
|
let modifiedFiles = [];
|
||||||
|
if (versionCompare !== 0) {
|
||||||
|
spinner.text = "Checking for modified files...";
|
||||||
|
modifiedFiles = await fileManager.checkModifiedFiles(
|
||||||
|
installDir,
|
||||||
|
manifest
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (modifiedFiles.length > 0) {
|
if (modifiedFiles.length > 0) {
|
||||||
spinner.warn("Found modified files");
|
spinner.warn("Found modified files");
|
||||||
@@ -570,35 +675,117 @@ class Installer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Perform update by re-running installation
|
// Perform update by re-running installation
|
||||||
spinner.text = "Updating files...";
|
spinner.text = versionCompare === 0 ? "Reinstalling files..." : "Updating files...";
|
||||||
const config = {
|
const config = {
|
||||||
installType: manifest.install_type,
|
installType: manifest.install_type,
|
||||||
agent: manifest.agent,
|
agent: manifest.agent,
|
||||||
directory: installDir,
|
directory: installDir,
|
||||||
ide: newConfig?.ide || manifest.ide_setup, // Use new IDE choice if provided
|
|
||||||
ides: newConfig?.ides || manifest.ides_setup || [],
|
ides: newConfig?.ides || manifest.ides_setup || [],
|
||||||
};
|
};
|
||||||
|
|
||||||
await this.performFreshInstall(config, installDir, spinner);
|
await this.performFreshInstall(config, installDir, spinner, { isUpdate: true });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
spinner.fail("Update failed");
|
spinner.fail("Update failed");
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async performRepair(config, installDir, manifest, integrity, spinner) {
|
||||||
|
spinner.start("Preparing to repair installation...");
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Back up modified files
|
||||||
|
if (integrity.modified.length > 0) {
|
||||||
|
spinner.text = "Backing up modified files...";
|
||||||
|
for (const file of integrity.modified) {
|
||||||
|
const filePath = path.join(installDir, file);
|
||||||
|
if (await fileManager.pathExists(filePath)) {
|
||||||
|
const backupPath = await fileManager.backupFile(filePath);
|
||||||
|
console.log(chalk.dim(` Backed up: ${file} → ${path.basename(backupPath)}`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore missing and modified files
|
||||||
|
spinner.text = "Restoring files...";
|
||||||
|
const sourceBase = configLoader.getBmadCorePath();
|
||||||
|
const filesToRestore = [...integrity.missing, ...integrity.modified];
|
||||||
|
|
||||||
|
for (const file of filesToRestore) {
|
||||||
|
// Skip the manifest file itself
|
||||||
|
if (file.endsWith('install-manifest.yml')) continue;
|
||||||
|
|
||||||
|
const relativePath = file.replace('.bmad-core/', '');
|
||||||
|
const destPath = path.join(installDir, file);
|
||||||
|
|
||||||
|
// Check if this is a common/ file that needs special processing
|
||||||
|
const commonBase = path.dirname(path.dirname(path.dirname(path.dirname(__filename))));
|
||||||
|
const commonSourcePath = path.join(commonBase, 'common', relativePath);
|
||||||
|
|
||||||
|
if (await fileManager.pathExists(commonSourcePath)) {
|
||||||
|
// This is a common/ file - needs template processing
|
||||||
|
const fs = require('fs').promises;
|
||||||
|
const content = await fs.readFile(commonSourcePath, 'utf8');
|
||||||
|
const updatedContent = content.replace(/\{root\}/g, '.bmad-core');
|
||||||
|
await fileManager.ensureDirectory(path.dirname(destPath));
|
||||||
|
await fs.writeFile(destPath, updatedContent, 'utf8');
|
||||||
|
spinner.text = `Restored: ${file}`;
|
||||||
|
} else {
|
||||||
|
// Regular file from bmad-core
|
||||||
|
const sourcePath = path.join(sourceBase, relativePath);
|
||||||
|
if (await fileManager.pathExists(sourcePath)) {
|
||||||
|
await fileManager.copyFile(sourcePath, destPath);
|
||||||
|
spinner.text = `Restored: ${file}`;
|
||||||
|
} else {
|
||||||
|
console.warn(chalk.yellow(` Warning: Source file not found: ${file}`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spinner.succeed("Repair completed successfully!");
|
||||||
|
|
||||||
|
// Show summary
|
||||||
|
console.log(chalk.green("\n✓ Installation repaired!"));
|
||||||
|
if (integrity.missing.length > 0) {
|
||||||
|
console.log(chalk.green(` Restored ${integrity.missing.length} missing files`));
|
||||||
|
}
|
||||||
|
if (integrity.modified.length > 0) {
|
||||||
|
console.log(chalk.green(` Restored ${integrity.modified.length} modified files (backups created)`));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warning for Cursor custom modes if agents were repaired
|
||||||
|
const ides = manifest.ides_setup || [];
|
||||||
|
if (ides.includes('cursor')) {
|
||||||
|
console.log(chalk.yellow.bold("\n⚠️ IMPORTANT: Cursor Custom Modes Update Required"));
|
||||||
|
console.log(chalk.yellow("Since agent files have been repaired, you need to manually update your Cursor custom modes:"));
|
||||||
|
console.log(chalk.yellow("1. Open Cursor Settings (Cmd/Ctrl + ,)"));
|
||||||
|
console.log(chalk.yellow("2. Go to: Features > Cursor Tab > Custom Modes"));
|
||||||
|
console.log(chalk.yellow("3. Update each custom mode with the latest agent templates from:"));
|
||||||
|
console.log(chalk.yellow(` ${path.join(installDir, '.bmad-core', 'agents')}`));
|
||||||
|
console.log(chalk.yellow("4. Copy the full content of each agent file into the corresponding custom mode"));
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
spinner.fail("Repair failed");
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async performReinstall(config, installDir, spinner) {
|
async performReinstall(config, installDir, spinner) {
|
||||||
spinner.start("Reinstalling BMAD Method...");
|
spinner.start("Preparing to reinstall BMAD Method...");
|
||||||
|
|
||||||
// Remove existing .bmad-core
|
// Remove existing .bmad-core
|
||||||
const bmadCorePath = path.join(installDir, ".bmad-core");
|
const bmadCorePath = path.join(installDir, ".bmad-core");
|
||||||
if (await fileManager.pathExists(bmadCorePath)) {
|
if (await fileManager.pathExists(bmadCorePath)) {
|
||||||
|
spinner.text = "Removing existing installation...";
|
||||||
await fileManager.removeDirectory(bmadCorePath);
|
await fileManager.removeDirectory(bmadCorePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
return await this.performFreshInstall(config, installDir, spinner);
|
spinner.text = "Installing fresh copy...";
|
||||||
|
return await this.performFreshInstall(config, installDir, spinner, { isUpdate: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
showSuccessMessage(config, installDir) {
|
showSuccessMessage(config, installDir, options = {}) {
|
||||||
console.log(chalk.green("\n✓ BMAD Method installed successfully!\n"));
|
console.log(chalk.green("\n✓ BMAD Method installed successfully!\n"));
|
||||||
|
|
||||||
const ides = config.ides || (config.ide ? [config.ide] : []);
|
const ides = config.ides || (config.ide ? [config.ide] : []);
|
||||||
@@ -622,7 +809,9 @@ class Installer {
|
|||||||
|
|
||||||
// Information about installation components
|
// Information about installation components
|
||||||
console.log(chalk.bold("\n🎯 Installation Summary:"));
|
console.log(chalk.bold("\n🎯 Installation Summary:"));
|
||||||
console.log(chalk.green("✓ .bmad-core framework installed with all agents and workflows"));
|
if (config.installType !== "expansion-only") {
|
||||||
|
console.log(chalk.green("✓ .bmad-core framework installed with all agents and workflows"));
|
||||||
|
}
|
||||||
|
|
||||||
if (config.expansionPacks && config.expansionPacks.length > 0) {
|
if (config.expansionPacks && config.expansionPacks.length > 0) {
|
||||||
console.log(chalk.green(`✓ Expansion packs installed:`));
|
console.log(chalk.green(`✓ Expansion packs installed:`));
|
||||||
@@ -668,6 +857,17 @@ class Installer {
|
|||||||
chalk.dim("Need everything? Run: npx bmad-method install --full")
|
chalk.dim("Need everything? Run: npx bmad-method install --full")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Warning for Cursor custom modes if agents were updated
|
||||||
|
if (options.isUpdate && ides.includes('cursor')) {
|
||||||
|
console.log(chalk.yellow.bold("\n⚠️ IMPORTANT: Cursor Custom Modes Update Required"));
|
||||||
|
console.log(chalk.yellow("Since agents have been updated, you need to manually update your Cursor custom modes:"));
|
||||||
|
console.log(chalk.yellow("1. Open Cursor Settings (Cmd/Ctrl + ,)"));
|
||||||
|
console.log(chalk.yellow("2. Go to: Features > Cursor Tab > Custom Modes"));
|
||||||
|
console.log(chalk.yellow("3. Update each custom mode with the latest agent templates from:"));
|
||||||
|
console.log(chalk.yellow(` ${path.join(installDir, '.bmad-core', 'agents')}`));
|
||||||
|
console.log(chalk.yellow("4. Copy the full content of each agent file into the corresponding custom mode"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Legacy method for backward compatibility
|
// Legacy method for backward compatibility
|
||||||
@@ -767,8 +967,8 @@ class Installer {
|
|||||||
console.log(` Agent: ${manifest.agent}`);
|
console.log(` Agent: ${manifest.agent}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (manifest.ide_setup) {
|
if (manifest.ides_setup && manifest.ides_setup.length > 0) {
|
||||||
console.log(` IDE Setup: ${manifest.ide_setup}`);
|
console.log(` IDE Setup: ${manifest.ides_setup.join(', ')}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(` Total Files: ${manifest.files.length}`);
|
console.log(` Total Files: ${manifest.files.length}`);
|
||||||
@@ -797,7 +997,7 @@ class Installer {
|
|||||||
return configLoader.getAvailableTeams();
|
return configLoader.getAvailableTeams();
|
||||||
}
|
}
|
||||||
|
|
||||||
async installExpansionPacks(installDir, selectedPacks, spinner) {
|
async installExpansionPacks(installDir, selectedPacks, spinner, config = {}) {
|
||||||
if (!selectedPacks || selectedPacks.length === 0) {
|
if (!selectedPacks || selectedPacks.length === 0) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@@ -816,11 +1016,112 @@ class Installer {
|
|||||||
console.warn(`Expansion pack ${packId} not found, skipping...`);
|
console.warn(`Expansion pack ${packId} not found, skipping...`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if expansion pack already exists
|
||||||
|
let expansionDotFolder = path.join(installDir, `.${packId}`);
|
||||||
|
const existingManifestPath = path.join(expansionDotFolder, 'install-manifest.yml');
|
||||||
|
|
||||||
|
if (await fileManager.pathExists(existingManifestPath)) {
|
||||||
|
spinner.stop();
|
||||||
|
const existingManifest = await fileManager.readExpansionPackManifest(installDir, packId);
|
||||||
|
|
||||||
|
console.log(chalk.yellow(`\n🔍 Found existing ${pack.name} installation`));
|
||||||
|
console.log(` Current version: ${existingManifest.version || 'unknown'}`);
|
||||||
|
console.log(` New version: ${pack.version}`);
|
||||||
|
|
||||||
|
// Check integrity of existing expansion pack
|
||||||
|
const packIntegrity = await fileManager.checkFileIntegrity(installDir, existingManifest);
|
||||||
|
const hasPackIntegrityIssues = packIntegrity.missing.length > 0 || packIntegrity.modified.length > 0;
|
||||||
|
|
||||||
|
if (hasPackIntegrityIssues) {
|
||||||
|
console.log(chalk.red(" ⚠️ Installation issues detected:"));
|
||||||
|
if (packIntegrity.missing.length > 0) {
|
||||||
|
console.log(chalk.red(` Missing files: ${packIntegrity.missing.length}`));
|
||||||
|
}
|
||||||
|
if (packIntegrity.modified.length > 0) {
|
||||||
|
console.log(chalk.yellow(` Modified files: ${packIntegrity.modified.length}`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const versionCompare = this.compareVersions(existingManifest.version || '0.0.0', pack.version);
|
||||||
|
|
||||||
|
if (versionCompare === 0) {
|
||||||
|
console.log(chalk.yellow(' ⚠️ Same version already installed'));
|
||||||
|
|
||||||
|
const choices = [];
|
||||||
|
if (hasPackIntegrityIssues) {
|
||||||
|
choices.push({ name: 'Repair (restore missing/modified files)', value: 'repair' });
|
||||||
|
}
|
||||||
|
choices.push(
|
||||||
|
{ name: 'Force reinstall (overwrite)', value: 'overwrite' },
|
||||||
|
{ name: 'Skip this expansion pack', value: 'skip' },
|
||||||
|
{ name: 'Cancel installation', value: 'cancel' }
|
||||||
|
);
|
||||||
|
|
||||||
|
const { action } = await inquirer.prompt([{
|
||||||
|
type: 'list',
|
||||||
|
name: 'action',
|
||||||
|
message: `${pack.name} v${pack.version} is already installed. What would you like to do?`,
|
||||||
|
choices: choices
|
||||||
|
}]);
|
||||||
|
|
||||||
|
if (action === 'skip') {
|
||||||
|
spinner.start();
|
||||||
|
continue;
|
||||||
|
} else if (action === 'cancel') {
|
||||||
|
console.log(chalk.red('Installation cancelled.'));
|
||||||
|
process.exit(0);
|
||||||
|
} else if (action === 'repair') {
|
||||||
|
// Repair the expansion pack
|
||||||
|
await this.repairExpansionPack(installDir, packId, pack, packIntegrity, spinner);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else if (versionCompare < 0) {
|
||||||
|
console.log(chalk.cyan(' ⬆️ Upgrade available'));
|
||||||
|
|
||||||
|
const { proceed } = await inquirer.prompt([{
|
||||||
|
type: 'confirm',
|
||||||
|
name: 'proceed',
|
||||||
|
message: `Upgrade ${pack.name} from v${existingManifest.version} to v${pack.version}?`,
|
||||||
|
default: true
|
||||||
|
}]);
|
||||||
|
|
||||||
|
if (!proceed) {
|
||||||
|
spinner.start();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log(chalk.yellow(' ⬇️ Installed version is newer than available version'));
|
||||||
|
|
||||||
|
const { action } = await inquirer.prompt([{
|
||||||
|
type: 'list',
|
||||||
|
name: 'action',
|
||||||
|
message: 'What would you like to do?',
|
||||||
|
choices: [
|
||||||
|
{ name: 'Keep current version', value: 'skip' },
|
||||||
|
{ name: 'Downgrade to available version', value: 'downgrade' },
|
||||||
|
{ name: 'Cancel installation', value: 'cancel' }
|
||||||
|
]
|
||||||
|
}]);
|
||||||
|
|
||||||
|
if (action === 'skip') {
|
||||||
|
spinner.start();
|
||||||
|
continue;
|
||||||
|
} else if (action === 'cancel') {
|
||||||
|
console.log(chalk.red('Installation cancelled.'));
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we get here, we're proceeding with installation
|
||||||
|
spinner.start(`Removing old ${pack.name} installation...`);
|
||||||
|
await fileManager.removeDirectory(expansionDotFolder);
|
||||||
|
}
|
||||||
|
|
||||||
const expansionPackDir = pack.packPath;
|
const expansionPackDir = pack.packPath;
|
||||||
|
|
||||||
// Create dedicated dot folder for this expansion pack
|
// Ensure dedicated dot folder exists for this expansion pack
|
||||||
const expansionDotFolder = path.join(installDir, `.${packId}`);
|
expansionDotFolder = path.join(installDir, `.${packId}`);
|
||||||
await fileManager.ensureDirectory(expansionDotFolder);
|
await fileManager.ensureDirectory(expansionDotFolder);
|
||||||
|
|
||||||
// Define the folders to copy from expansion packs
|
// Define the folders to copy from expansion packs
|
||||||
@@ -888,9 +1189,28 @@ class Installer {
|
|||||||
// Check and resolve core agents referenced by teams
|
// Check and resolve core agents referenced by teams
|
||||||
await this.resolveExpansionPackCoreAgents(installDir, expansionDotFolder, packId, spinner);
|
await this.resolveExpansionPackCoreAgents(installDir, expansionDotFolder, packId, spinner);
|
||||||
|
|
||||||
|
// Create manifest for this expansion pack
|
||||||
|
spinner.text = `Creating manifest for ${packId}...`;
|
||||||
|
const expansionConfig = {
|
||||||
|
installType: 'expansion-pack',
|
||||||
|
expansionPackId: packId,
|
||||||
|
expansionPackName: pack.name,
|
||||||
|
expansionPackVersion: pack.version,
|
||||||
|
ides: config.ides || [] // Use ides_setup instead of ide_setup
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get all files installed in this expansion pack
|
||||||
|
const expansionPackFiles = glob.sync('**/*', {
|
||||||
|
cwd: expansionDotFolder,
|
||||||
|
nodir: true
|
||||||
|
}).map(f => path.join(`.${packId}`, f));
|
||||||
|
|
||||||
|
await fileManager.createExpansionPackManifest(installDir, packId, expansionConfig, expansionPackFiles);
|
||||||
|
|
||||||
console.log(chalk.green(`✓ Installed expansion pack: ${pack.name} to ${`.${packId}`}`));
|
console.log(chalk.green(`✓ Installed expansion pack: ${pack.name} to ${`.${packId}`}`));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(chalk.red(`Failed to install expansion pack ${packId}: ${error.message}`));
|
console.error(chalk.red(`Failed to install expansion pack ${packId}: ${error.message}`));
|
||||||
|
console.error(chalk.red(`Stack trace: ${error.stack}`));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1034,7 +1354,8 @@ class Installer {
|
|||||||
console.log(chalk.dim(` Added agent dependency: ${depType}/${depFileName}`));
|
console.log(chalk.dim(` Added agent dependency: ${depType}/${depFileName}`));
|
||||||
} else {
|
} else {
|
||||||
// Try common folder
|
// Try common folder
|
||||||
const commonDepPath = path.join(this.rootDir, 'common', depType, depFileName);
|
const sourceBase = path.dirname(path.dirname(path.dirname(path.dirname(__filename)))); // Go up to project root
|
||||||
|
const commonDepPath = path.join(sourceBase, 'common', depType, depFileName);
|
||||||
if (await fileManager.pathExists(commonDepPath)) {
|
if (await fileManager.pathExists(commonDepPath)) {
|
||||||
const destDepPath = path.join(expansionDotFolder, depType, depFileName);
|
const destDepPath = path.join(expansionDotFolder, depType, depFileName);
|
||||||
await fileManager.copyFile(commonDepPath, destDepPath);
|
await fileManager.copyFile(commonDepPath, destDepPath);
|
||||||
@@ -1157,6 +1478,9 @@ class Installer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async copyCommonItems(installDir, targetSubdir, spinner) {
|
async copyCommonItems(installDir, targetSubdir, spinner) {
|
||||||
|
// Ensure modules are initialized
|
||||||
|
await initializeModules();
|
||||||
|
|
||||||
const glob = require('glob');
|
const glob = require('glob');
|
||||||
const fs = require('fs').promises;
|
const fs = require('fs').promises;
|
||||||
const sourceBase = path.dirname(path.dirname(path.dirname(path.dirname(__filename)))); // Go up to project root
|
const sourceBase = path.dirname(path.dirname(path.dirname(path.dirname(__filename)))); // Go up to project root
|
||||||
@@ -1198,6 +1522,133 @@ class Installer {
|
|||||||
return copiedFiles;
|
return copiedFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async detectExpansionPacks(installDir) {
|
||||||
|
const expansionPacks = {};
|
||||||
|
const glob = require("glob");
|
||||||
|
|
||||||
|
// Find all dot folders that might be expansion packs
|
||||||
|
const dotFolders = glob.sync(".*", {
|
||||||
|
cwd: installDir,
|
||||||
|
ignore: [".git", ".git/**", ".bmad-core", ".bmad-core/**"],
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const folder of dotFolders) {
|
||||||
|
const folderPath = path.join(installDir, folder);
|
||||||
|
const stats = await fileManager.pathExists(folderPath);
|
||||||
|
|
||||||
|
if (stats) {
|
||||||
|
// Check if it has a manifest
|
||||||
|
const manifestPath = path.join(folderPath, "install-manifest.yml");
|
||||||
|
if (await fileManager.pathExists(manifestPath)) {
|
||||||
|
const manifest = await fileManager.readExpansionPackManifest(installDir, folder.substring(1));
|
||||||
|
if (manifest) {
|
||||||
|
expansionPacks[folder.substring(1)] = {
|
||||||
|
path: folderPath,
|
||||||
|
manifest: manifest,
|
||||||
|
hasManifest: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Check if it has a config.yml (expansion pack without manifest)
|
||||||
|
const configPath = path.join(folderPath, "config.yml");
|
||||||
|
if (await fileManager.pathExists(configPath)) {
|
||||||
|
expansionPacks[folder.substring(1)] = {
|
||||||
|
path: folderPath,
|
||||||
|
manifest: null,
|
||||||
|
hasManifest: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return expansionPacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
async repairExpansionPack(installDir, packId, pack, integrity, spinner) {
|
||||||
|
spinner.start(`Repairing ${pack.name}...`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const expansionDotFolder = path.join(installDir, `.${packId}`);
|
||||||
|
|
||||||
|
// Back up modified files
|
||||||
|
if (integrity.modified.length > 0) {
|
||||||
|
spinner.text = "Backing up modified files...";
|
||||||
|
for (const file of integrity.modified) {
|
||||||
|
const filePath = path.join(installDir, file);
|
||||||
|
if (await fileManager.pathExists(filePath)) {
|
||||||
|
const backupPath = await fileManager.backupFile(filePath);
|
||||||
|
console.log(chalk.dim(` Backed up: ${file} → ${path.basename(backupPath)}`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore missing and modified files
|
||||||
|
spinner.text = "Restoring files...";
|
||||||
|
const filesToRestore = [...integrity.missing, ...integrity.modified];
|
||||||
|
|
||||||
|
for (const file of filesToRestore) {
|
||||||
|
// Skip the manifest file itself
|
||||||
|
if (file.endsWith('install-manifest.yml')) continue;
|
||||||
|
|
||||||
|
const relativePath = file.replace(`.${packId}/`, '');
|
||||||
|
const sourcePath = path.join(pack.packPath, relativePath);
|
||||||
|
const destPath = path.join(installDir, file);
|
||||||
|
|
||||||
|
// Check if this is a common/ file that needs special processing
|
||||||
|
const commonBase = path.dirname(path.dirname(path.dirname(path.dirname(__filename))));
|
||||||
|
const commonSourcePath = path.join(commonBase, 'common', relativePath);
|
||||||
|
|
||||||
|
if (await fileManager.pathExists(commonSourcePath)) {
|
||||||
|
// This is a common/ file - needs template processing
|
||||||
|
const fs = require('fs').promises;
|
||||||
|
const content = await fs.readFile(commonSourcePath, 'utf8');
|
||||||
|
const updatedContent = content.replace(/\{root\}/g, `.${packId}`);
|
||||||
|
await fileManager.ensureDirectory(path.dirname(destPath));
|
||||||
|
await fs.writeFile(destPath, updatedContent, 'utf8');
|
||||||
|
spinner.text = `Restored: ${file}`;
|
||||||
|
} else if (await fileManager.pathExists(sourcePath)) {
|
||||||
|
// Regular file from expansion pack
|
||||||
|
await fileManager.copyFile(sourcePath, destPath);
|
||||||
|
spinner.text = `Restored: ${file}`;
|
||||||
|
} else {
|
||||||
|
console.warn(chalk.yellow(` Warning: Source file not found: ${file}`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spinner.succeed(`${pack.name} repaired successfully!`);
|
||||||
|
|
||||||
|
// Show summary
|
||||||
|
console.log(chalk.green(`\n✓ ${pack.name} repaired!`));
|
||||||
|
if (integrity.missing.length > 0) {
|
||||||
|
console.log(chalk.green(` Restored ${integrity.missing.length} missing files`));
|
||||||
|
}
|
||||||
|
if (integrity.modified.length > 0) {
|
||||||
|
console.log(chalk.green(` Restored ${integrity.modified.length} modified files (backups created)`));
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
spinner.fail(`Failed to repair ${pack.name}`);
|
||||||
|
console.error(chalk.red(`Error: ${error.message}`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compareVersions(v1, v2) {
|
||||||
|
// Simple semver comparison
|
||||||
|
const parts1 = v1.split('.').map(Number);
|
||||||
|
const parts2 = v2.split('.').map(Number);
|
||||||
|
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
const part1 = parts1[i] || 0;
|
||||||
|
const part2 = parts2[i] || 0;
|
||||||
|
|
||||||
|
if (part1 > part2) return 1;
|
||||||
|
if (part1 < part2) return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
async findInstallation() {
|
async findInstallation() {
|
||||||
// Look for .bmad-core in current directory or parent directories
|
// Look for .bmad-core in current directory or parent directories
|
||||||
let currentDir = process.cwd();
|
let currentDir = process.cwd();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "bmad-method",
|
"name": "bmad-method",
|
||||||
"version": "4.20.0",
|
"version": "4.21.0",
|
||||||
"description": "BMAD Method installer - AI-powered Agile development framework",
|
"description": "BMAD Method installer - AI-powered Agile development framework",
|
||||||
"main": "lib/installer.js",
|
"main": "lib/installer.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|||||||
54
tools/update-expansion-version.js
Executable file
54
tools/update-expansion-version.js
Executable file
@@ -0,0 +1,54 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const yaml = require('js-yaml');
|
||||||
|
|
||||||
|
const args = process.argv.slice(2);
|
||||||
|
|
||||||
|
if (args.length < 2) {
|
||||||
|
console.log('Usage: node update-expansion-version.js <expansion-pack-id> <new-version>');
|
||||||
|
console.log('Example: node update-expansion-version.js bmad-creator-tools 1.1.0');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const [packId, newVersion] = args;
|
||||||
|
|
||||||
|
// Validate version format
|
||||||
|
if (!/^\d+\.\d+\.\d+$/.test(newVersion)) {
|
||||||
|
console.error('Error: Version must be in format X.Y.Z (e.g., 1.2.3)');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateVersion() {
|
||||||
|
try {
|
||||||
|
// Update in config.yml
|
||||||
|
const configPath = path.join(__dirname, '..', 'expansion-packs', packId, 'config.yml');
|
||||||
|
|
||||||
|
if (!fs.existsSync(configPath)) {
|
||||||
|
console.error(`Error: Expansion pack '${packId}' not found`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const configContent = fs.readFileSync(configPath, 'utf8');
|
||||||
|
const config = yaml.load(configContent);
|
||||||
|
const oldVersion = config.version || 'unknown';
|
||||||
|
|
||||||
|
config.version = newVersion;
|
||||||
|
|
||||||
|
const updatedYaml = yaml.dump(config, { indent: 2 });
|
||||||
|
fs.writeFileSync(configPath, updatedYaml);
|
||||||
|
|
||||||
|
console.log(`✓ Updated ${packId}/config.yml: ${oldVersion} → ${newVersion}`);
|
||||||
|
console.log(`\n✓ Successfully updated ${packId} to version ${newVersion}`);
|
||||||
|
console.log('\nNext steps:');
|
||||||
|
console.log('1. Test the changes');
|
||||||
|
console.log('2. Commit: git add -A && git commit -m "chore: bump ' + packId + ' to v' + newVersion + '"');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error updating version:', error.message);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateVersion();
|
||||||
Reference in New Issue
Block a user