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:
Brian Madison
2025-11-09 17:39:05 -06:00
parent fd2521ec69
commit 7eb52520fa
433 changed files with 125975 additions and 689 deletions

View File

@@ -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);
}
}

View File

@@ -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,