mirror of
https://github.com/bmadcode/BMAD-METHOD.git
synced 2025-12-29 16:14:59 +00:00
Major Enhancements:
- Installation path is now fully configurable, allowing users to specify custom installation directories during setup
- Default installation location changed to .bmad (hidden directory) for cleaner project root organization
Web Bundle Improvements:
- All web bundles (single agent and team) now include party mode support for multi-agent collaboration!
- Advanced elicitation capabilities integrated into standalone agents
- All bundles enhanced with party mode agent manifests
- Added default-party.csv files to bmm, bmgd, and cis module teams
- The default party file is what will be used with single agent bundles. teams can customize for different party configurations before web bundling through a setting in the team yaml file
- New web bundle outputs for all agents (analyst, architect, dev, pm, sm, tea, tech-writer, ux-designer, game-*, creative-squad)
Phase 4 Workflow Updates (In Progress):
- Initiated shift to separate phase 4 implementation artifacts from documentation
- Phase 4 implementation artifacts (stories, code review, sprint plan, context files) will move to dedicated location outside docs folder
- Installer questions and configuration added for artifact path selection
- Updated workflow.yaml files for code-review, sprint-planning, story-context, epic-tech-context, and retrospective workflows to support this, but still might require some udpates
Additional Changes:
- New agent and action command header models for standardization
- Enhanced web-bundle-activation-steps fragment
- Updated web-bundler.js to support new structure
- VS Code settings updated for new .bmad directory
- Party mode instructions and workflow enhanced for better orchestration
IDE Installer Updates:
- Show version number of installer in cli
- improved Installer UX
- Gemini TOML Improved to have clear loading instructions with @ commands
- All tools agent launcher mds improved to use a central file template critical indication isntead of hardcoding in 2 different locations.
This commit is contained in:
@@ -13,6 +13,41 @@ class ConfigCollector {
|
||||
this.currentProjectDir = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the bmad installation directory in a project
|
||||
* V6+ installations can use ANY folder name but ALWAYS have _cfg/manifest.yaml
|
||||
* @param {string} projectDir - Project directory
|
||||
* @returns {Promise<string>} Path to bmad directory
|
||||
*/
|
||||
async findBmadDir(projectDir) {
|
||||
// Check if project directory exists
|
||||
if (!(await fs.pathExists(projectDir))) {
|
||||
// Project doesn't exist yet, return default
|
||||
return path.join(projectDir, 'bmad');
|
||||
}
|
||||
|
||||
// V6+ strategy: Look for ANY directory with _cfg/manifest.yaml
|
||||
// This is the definitive marker of a V6+ installation
|
||||
try {
|
||||
const entries = await fs.readdir(projectDir, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
if (entry.isDirectory()) {
|
||||
const manifestPath = path.join(projectDir, entry.name, '_cfg', 'manifest.yaml');
|
||||
if (await fs.pathExists(manifestPath)) {
|
||||
// Found a V6+ installation
|
||||
return path.join(projectDir, entry.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Ignore errors, fall through to default
|
||||
}
|
||||
|
||||
// No V6+ installation found, return default
|
||||
// This will be used for new installations
|
||||
return path.join(projectDir, 'bmad');
|
||||
}
|
||||
|
||||
/**
|
||||
* Load existing config if it exists from module config files
|
||||
* @param {string} projectDir - Target project directory
|
||||
@@ -25,7 +60,8 @@ class ConfigCollector {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bmadDir = path.join(projectDir, 'bmad');
|
||||
// Find the actual bmad directory (handles custom folder names)
|
||||
const bmadDir = await this.findBmadDir(projectDir);
|
||||
|
||||
// Check if bmad directory exists
|
||||
if (!(await fs.pathExists(bmadDir))) {
|
||||
@@ -165,17 +201,13 @@ class ConfigCollector {
|
||||
this.allAnswers[`${moduleName}_${key}`] = value;
|
||||
}
|
||||
}
|
||||
// Show "no config" message for modules with no new questions
|
||||
CLIUtils.displayModuleNoConfig(moduleName, moduleConfig.header, moduleConfig.subheader);
|
||||
return false; // No new fields
|
||||
}
|
||||
|
||||
// If we have new fields, show prompt section and collect only new fields
|
||||
// If we have new fields, build questions first
|
||||
if (newKeys.length > 0) {
|
||||
console.log(chalk.yellow(`\n📋 New configuration options available for ${moduleName}`));
|
||||
if (moduleConfig.prompt) {
|
||||
const prompts = Array.isArray(moduleConfig.prompt) ? moduleConfig.prompt : [moduleConfig.prompt];
|
||||
CLIUtils.displayPromptSection(prompts);
|
||||
}
|
||||
|
||||
const questions = [];
|
||||
for (const key of newKeys) {
|
||||
const item = moduleConfig[key];
|
||||
@@ -186,6 +218,8 @@ class ConfigCollector {
|
||||
}
|
||||
|
||||
if (questions.length > 0) {
|
||||
// Only show header if we actually have questions
|
||||
CLIUtils.displayModuleConfigHeader(moduleName, moduleConfig.header, moduleConfig.subheader);
|
||||
console.log(); // Line break before questions
|
||||
const answers = await inquirer.prompt(questions);
|
||||
|
||||
@@ -212,6 +246,9 @@ class ConfigCollector {
|
||||
}
|
||||
this.collectedConfig[moduleName][originalKey] = result;
|
||||
}
|
||||
} else {
|
||||
// New keys exist but no questions generated - show no config message
|
||||
CLIUtils.displayModuleNoConfig(moduleName, moduleConfig.header, moduleConfig.subheader);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,12 +368,6 @@ class ConfigCollector {
|
||||
return;
|
||||
}
|
||||
|
||||
// Display module prompts using better formatting
|
||||
if (moduleConfig.prompt) {
|
||||
const prompts = Array.isArray(moduleConfig.prompt) ? moduleConfig.prompt : [moduleConfig.prompt];
|
||||
CLIUtils.displayPromptSection(prompts);
|
||||
}
|
||||
|
||||
// Process each config item
|
||||
const questions = [];
|
||||
const configKeys = Object.keys(moduleConfig).filter((key) => key !== 'prompt');
|
||||
@@ -355,7 +386,9 @@ class ConfigCollector {
|
||||
}
|
||||
}
|
||||
|
||||
// Display appropriate header based on whether there are questions
|
||||
if (questions.length > 0) {
|
||||
CLIUtils.displayModuleConfigHeader(moduleName, moduleConfig.header, moduleConfig.subheader);
|
||||
console.log(); // Line break before questions
|
||||
const answers = await inquirer.prompt(questions);
|
||||
|
||||
@@ -456,10 +489,10 @@ class ConfigCollector {
|
||||
this.collectedConfig[moduleName][originalKey] = result;
|
||||
}
|
||||
|
||||
// Display module completion message after collecting all answers (unless skipped)
|
||||
if (!skipCompletion) {
|
||||
CLIUtils.displayModuleComplete(moduleName);
|
||||
}
|
||||
// No longer display completion boxes - keep output clean
|
||||
} else {
|
||||
// No questions for this module - show completion message
|
||||
CLIUtils.displayModuleNoConfig(moduleName, moduleConfig.header, moduleConfig.subheader);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ class ManifestGenerator {
|
||||
this.updatedModules = [...new Set(['core', ...selectedModules])]; // Only these get rescanned
|
||||
this.preservedModules = preservedModules; // These stay as-is in CSVs
|
||||
this.bmadDir = bmadDir;
|
||||
this.bmadFolderName = path.basename(bmadDir); // Get the actual folder name (e.g., '.bmad' or 'bmad')
|
||||
this.allInstalledFiles = installedFiles;
|
||||
|
||||
if (!Object.prototype.hasOwnProperty.call(options, 'ides')) {
|
||||
@@ -140,8 +141,8 @@ class ManifestGenerator {
|
||||
// Build relative path for installation
|
||||
const installPath =
|
||||
moduleName === 'core'
|
||||
? `bmad/core/workflows/${relativePath}/workflow.yaml`
|
||||
: `bmad/${moduleName}/workflows/${relativePath}/workflow.yaml`;
|
||||
? `${this.bmadFolderName}/core/workflows/${relativePath}/workflow.yaml`
|
||||
: `${this.bmadFolderName}/${moduleName}/workflows/${relativePath}/workflow.yaml`;
|
||||
|
||||
// Check for standalone property (default: false)
|
||||
const standalone = workflow.standalone === true;
|
||||
@@ -241,7 +242,8 @@ class ManifestGenerator {
|
||||
const principlesMatch = content.match(/<principles>([\s\S]*?)<\/principles>/);
|
||||
|
||||
// Build relative path for installation
|
||||
const installPath = moduleName === 'core' ? `bmad/core/agents/${file}` : `bmad/${moduleName}/agents/${file}`;
|
||||
const installPath =
|
||||
moduleName === 'core' ? `${this.bmadFolderName}/core/agents/${file}` : `${this.bmadFolderName}/${moduleName}/agents/${file}`;
|
||||
|
||||
const agentName = file.replace('.md', '');
|
||||
|
||||
@@ -324,7 +326,8 @@ class ManifestGenerator {
|
||||
const standalone = !!standaloneMatch;
|
||||
|
||||
// Build relative path for installation
|
||||
const installPath = moduleName === 'core' ? `bmad/core/tasks/${file}` : `bmad/${moduleName}/tasks/${file}`;
|
||||
const installPath =
|
||||
moduleName === 'core' ? `${this.bmadFolderName}/core/tasks/${file}` : `${this.bmadFolderName}/${moduleName}/tasks/${file}`;
|
||||
|
||||
const taskName = file.replace(/\.(xml|md)$/, '');
|
||||
tasks.push({
|
||||
@@ -393,7 +396,8 @@ class ManifestGenerator {
|
||||
const standalone = !!standaloneMatch;
|
||||
|
||||
// Build relative path for installation
|
||||
const installPath = moduleName === 'core' ? `bmad/core/tools/${file}` : `bmad/${moduleName}/tools/${file}`;
|
||||
const installPath =
|
||||
moduleName === 'core' ? `${this.bmadFolderName}/core/tools/${file}` : `${this.bmadFolderName}/${moduleName}/tools/${file}`;
|
||||
|
||||
const toolName = file.replace(/\.(xml|md)$/, '');
|
||||
tools.push({
|
||||
@@ -659,7 +663,7 @@ class ManifestGenerator {
|
||||
} else {
|
||||
// Fallback: use the collected workflows/agents/tasks
|
||||
for (const file of this.files) {
|
||||
const filePath = path.join(this.bmadDir, file.path.replace('bmad/', ''));
|
||||
const filePath = path.join(this.bmadDir, file.path.replace(this.bmadFolderName + '/', ''));
|
||||
const hash = await this.calculateFileHash(filePath);
|
||||
allFiles.push({
|
||||
...file,
|
||||
|
||||
Reference in New Issue
Block a user