diff --git a/bmad/_cfg/agent-party.xml b/bmad/_cfg/agent-party.xml index 4c22fc7b..71746cbb 100644 --- a/bmad/_cfg/agent-party.xml +++ b/bmad/_cfg/agent-party.xml @@ -1,7 +1,7 @@ - + Complete roster of installed BMAD agents with summarized personas for efficient multi-agent orchestration. Used by party-mode and other multi-agent coordination features. @@ -164,6 +164,6 @@ 18 bmm, cis, core, custom - 2025-10-01T00:22:43.657Z + 2025-10-01T00:47:33.050Z \ No newline at end of file diff --git a/bmad/_cfg/manifest.csv b/bmad/_cfg/manifest.csv index eb596288..fc65ae33 100644 --- a/bmad/_cfg/manifest.csv +++ b/bmad/_cfg/manifest.csv @@ -1,11 +1,11 @@ # BMAD Manifest -# Generated: 2025-10-01T00:22:43.703Z +# Generated: 2025-10-01T00:47:33.096Z ## Installation Info Property,Value Version,6.0.0-alpha.0 -InstallDate,2025-10-01T00:22:43.690Z -LastUpdated,2025-10-01T00:22:43.703Z +InstallDate,2025-10-01T00:47:33.083Z +LastUpdated,2025-10-01T00:47:33.096Z ## Modules Name,Version,ShortTitle diff --git a/bmad/_cfg/manifest.yaml b/bmad/_cfg/manifest.yaml index 21b994ce..5d50e986 100644 --- a/bmad/_cfg/manifest.yaml +++ b/bmad/_cfg/manifest.yaml @@ -1,7 +1,7 @@ installation: version: 6.0.0-alpha.0 - installDate: "2025-10-01T00:22:43.664Z" - lastUpdated: "2025-10-01T00:22:43.664Z" + installDate: "2025-10-01T00:47:33.058Z" + lastUpdated: "2025-10-01T00:47:33.058Z" modules: - name: core version: "" diff --git a/bmad/bmb/config.yaml b/bmad/bmb/config.yaml index 74bfe6ac..67ce2d40 100644 --- a/bmad/bmb/config.yaml +++ b/bmad/bmb/config.yaml @@ -1,7 +1,7 @@ # BMB Module Configuration # Generated by BMAD installer # Version: 6.0.0-alpha.0 -# Date: 2025-10-01T00:22:43.652Z +# Date: 2025-10-01T00:47:33.045Z # Core Configuration Values user_name: BMad diff --git a/bmad/bmm/config.yaml b/bmad/bmm/config.yaml index e7a2625e..5d5d9409 100644 --- a/bmad/bmm/config.yaml +++ b/bmad/bmm/config.yaml @@ -1,7 +1,7 @@ # BMM Module Configuration # Generated by BMAD installer # Version: 6.0.0-alpha.0 -# Date: 2025-10-01T00:22:43.653Z +# Date: 2025-10-01T00:47:33.046Z project_name: My Project tech_docs: "{project-root}/docs" diff --git a/bmad/cis/config.yaml b/bmad/cis/config.yaml index ed3de25f..1c7603c0 100644 --- a/bmad/cis/config.yaml +++ b/bmad/cis/config.yaml @@ -1,7 +1,7 @@ # CIS Module Configuration # Generated by BMAD installer # Version: 6.0.0-alpha.0 -# Date: 2025-10-01T00:22:43.653Z +# Date: 2025-10-01T00:47:33.046Z # Core Configuration Values user_name: BMad diff --git a/bmad/core/config.yaml b/bmad/core/config.yaml index 8aeef9f3..437994dd 100644 --- a/bmad/core/config.yaml +++ b/bmad/core/config.yaml @@ -1,7 +1,7 @@ # CORE Module Configuration # Generated by BMAD installer # Version: 6.0.0-alpha.0 -# Date: 2025-10-01T00:22:43.654Z +# Date: 2025-10-01T00:47:33.046Z user_name: BMad communication_language: English diff --git a/tools/cli/installers/lib/core/installer.js b/tools/cli/installers/lib/core/installer.js index 328bb4c3..4d82f27e 100644 --- a/tools/cli/installers/lib/core/installer.js +++ b/tools/cli/installers/lib/core/installer.js @@ -185,12 +185,61 @@ class Installer { console.log(chalk.dim(` Location: ${bmadDir}`)); console.log(chalk.dim(` Version: ${existingInstall.version}`)); - // TODO: Handle update scenario const { action } = await this.promptUpdateAction(); if (action === 'cancel') { console.log('Installation cancelled.'); return; } + + if (action === 'reinstall') { + // Warn about destructive operation + console.log(chalk.red.bold('\n⚠️ WARNING: This is a destructive operation!')); + console.log(chalk.red('All custom files and modifications in the bmad directory will be lost.')); + + const inquirer = require('inquirer'); + const { confirmReinstall } = await inquirer.prompt([ + { + type: 'confirm', + name: 'confirmReinstall', + message: chalk.yellow('Are you sure you want to delete and reinstall?'), + default: false, + }, + ]); + + if (!confirmReinstall) { + console.log('Installation cancelled.'); + return; + } + + // Remove existing installation + await fs.remove(bmadDir); + console.log(chalk.green('✓ Removed existing installation\n')); + } else if (action === 'update') { + // Store that we're updating for later processing + config._isUpdate = true; + config._existingInstall = existingInstall; + + // Detect custom files BEFORE updating (compare current files vs manifest) + const existingManifest = await this.manifest.read(bmadDir); + config._customFiles = await this.detectCustomFiles(bmadDir, existingManifest); + + // If there are custom files, back them up temporarily + if (config._customFiles.length > 0) { + const tempBackupDir = path.join(projectDir, '.bmad-custom-backup-temp'); + await fs.ensureDir(tempBackupDir); + + spinner.start(`Backing up ${config._customFiles.length} custom files...`); + for (const customFile of config._customFiles) { + const relativePath = path.relative(bmadDir, customFile); + const backupPath = path.join(tempBackupDir, relativePath); + await fs.ensureDir(path.dirname(backupPath)); + await fs.copy(customFile, backupPath); + } + spinner.succeed(`Backed up ${config._customFiles.length} custom files`); + + config._tempBackupDir = tempBackupDir; + } + } } // Create bmad directory structure @@ -352,8 +401,42 @@ class Installer { ); spinner.succeed(`Manifest created (${manifestResult.filesTracked} files tracked)`); + // If this was an update, restore custom files + let customFiles = []; + if (config._isUpdate && config._customFiles && config._customFiles.length > 0) { + spinner.start(`Restoring ${config._customFiles.length} custom files...`); + + for (const originalPath of config._customFiles) { + const relativePath = path.relative(bmadDir, originalPath); + const backupPath = path.join(config._tempBackupDir, relativePath); + + if (await fs.pathExists(backupPath)) { + await fs.ensureDir(path.dirname(originalPath)); + await fs.copy(backupPath, originalPath, { overwrite: true }); + } + } + + // Clean up temp backup + if (config._tempBackupDir && (await fs.pathExists(config._tempBackupDir))) { + await fs.remove(config._tempBackupDir); + } + + spinner.succeed(`Restored ${config._customFiles.length} custom files`); + customFiles = config._customFiles; + } + spinner.stop(); + // Report custom files if any were found + if (customFiles.length > 0) { + console.log(chalk.cyan(`\n📁 Custom files preserved: ${customFiles.length}`)); + console.log(chalk.dim('The following custom files were found and restored:\n')); + for (const file of customFiles) { + console.log(chalk.dim(` - ${path.relative(bmadDir, file)}`)); + } + console.log(''); + } + // Display completion message const { UI } = require('../../../lib/ui'); const ui = new UI(); @@ -361,6 +444,7 @@ class Installer { path: bmadDir, modules: config.modules, ides: config.ides, + customFiles: customFiles.length > 0 ? customFiles : undefined, }); return { success: true, path: bmadDir, modules: config.modules, ides: config.ides }; @@ -922,6 +1006,59 @@ class Installer { } } + /** + * Detect custom files that were not installed by the installer + * @param {string} bmadDir - BMAD installation directory + * @param {Object} existingManifest - Previous installation manifest + * @returns {Array} List of custom files found + */ + async detectCustomFiles(bmadDir, existingManifest) { + const customFiles = []; + + // Build set of previously installed files from manifest + const installedSet = new Set(); + if (existingManifest && existingManifest.files) { + for (const fileEntry of existingManifest.files) { + if (fileEntry.file) { + // Files in manifest are stored as relative paths starting with 'bmad/' + // Convert to absolute path + const relativePath = fileEntry.file.startsWith('bmad/') ? fileEntry.file.slice(5) : fileEntry.file; + const absolutePath = path.join(bmadDir, relativePath); + installedSet.add(path.normalize(absolutePath)); + } + } + } + + // Recursively scan bmadDir for all files + const scanDirectory = async (dir) => { + try { + const entries = await fs.readdir(dir, { withFileTypes: true }); + for (const entry of entries) { + const fullPath = path.join(dir, entry.name); + + if (entry.isDirectory()) { + // Skip certain directories + if (entry.name === 'node_modules' || entry.name === '.git') { + continue; + } + await scanDirectory(fullPath); + } else if (entry.isFile()) { + const normalizedPath = path.normalize(fullPath); + // If file is not in the previous manifest, it's custom + if (!installedSet.has(normalizedPath)) { + customFiles.push(fullPath); + } + } + } + } catch { + // Ignore errors scanning directories + } + }; + + await scanDirectory(bmadDir); + return customFiles; + } + /** * Private: Create agent configuration files * @param {string} bmadDir - BMAD installation directory