mirror of
https://github.com/bmad-code-org/BMAD-METHOD.git
synced 2026-01-30 04:32:02 +00:00
Modify installation now will remove modules that get unselected, with an option to confirm the deletion
This commit is contained in:
1
docs/A-Product-Brief/.gitkeep
Normal file
1
docs/A-Product-Brief/.gitkeep
Normal file
@@ -0,0 +1 @@
|
||||
# This file ensures the directory is tracked by git
|
||||
1
docs/B-Trigger-Map/.gitkeep
Normal file
1
docs/B-Trigger-Map/.gitkeep
Normal file
@@ -0,0 +1 @@
|
||||
# This file ensures the directory is tracked by git
|
||||
1
docs/C-Platform-Requirements/.gitkeep
Normal file
1
docs/C-Platform-Requirements/.gitkeep
Normal file
@@ -0,0 +1 @@
|
||||
# This file ensures the directory is tracked by git
|
||||
1
docs/C-Scenarios/.gitkeep
Normal file
1
docs/C-Scenarios/.gitkeep
Normal file
@@ -0,0 +1 @@
|
||||
# This file ensures the directory is tracked by git
|
||||
1
docs/D-Design-System/.gitkeep
Normal file
1
docs/D-Design-System/.gitkeep
Normal file
@@ -0,0 +1 @@
|
||||
# This file ensures the directory is tracked by git
|
||||
1
docs/E-PRD/.gitkeep
Normal file
1
docs/E-PRD/.gitkeep
Normal file
@@ -0,0 +1 @@
|
||||
# This file ensures the directory is tracked by git
|
||||
1
docs/E-PRD/Design-Deliveries/.gitkeep
Normal file
1
docs/E-PRD/Design-Deliveries/.gitkeep
Normal file
@@ -0,0 +1 @@
|
||||
# This file ensures the directory is tracked by git
|
||||
1
docs/F-Testing/.gitkeep
Normal file
1
docs/F-Testing/.gitkeep
Normal file
@@ -0,0 +1 @@
|
||||
# This file ensures the directory is tracked by git
|
||||
1
docs/G-Product-Development/.gitkeep
Normal file
1
docs/G-Product-Development/.gitkeep
Normal file
@@ -0,0 +1 @@
|
||||
# This file ensures the directory is tracked by git
|
||||
@@ -444,6 +444,60 @@ class Installer {
|
||||
config._isUpdate = true;
|
||||
config._existingInstall = existingInstall;
|
||||
|
||||
// Detect modules that were previously installed but are NOT in the new selection (to be removed)
|
||||
const previouslyInstalledModules = new Set(existingInstall.modules.map((m) => m.id));
|
||||
const newlySelectedModules = new Set(config.modules || []);
|
||||
|
||||
// Find modules to remove (installed but not in new selection)
|
||||
// Exclude 'core' from being removable
|
||||
const modulesToRemove = [...previouslyInstalledModules].filter((m) => !newlySelectedModules.has(m) && m !== 'core');
|
||||
|
||||
// If there are modules to remove, ask for confirmation
|
||||
if (modulesToRemove.length > 0) {
|
||||
const prompts = require('../../../lib/prompts');
|
||||
spinner.stop();
|
||||
|
||||
console.log('');
|
||||
console.log(chalk.yellow.bold('⚠️ Modules to be removed:'));
|
||||
for (const moduleId of modulesToRemove) {
|
||||
const moduleInfo = existingInstall.modules.find((m) => m.id === moduleId);
|
||||
const displayName = moduleInfo?.name || moduleId;
|
||||
const modulePath = path.join(bmadDir, moduleId);
|
||||
console.log(chalk.red(` - ${displayName} (${modulePath})`));
|
||||
}
|
||||
console.log('');
|
||||
|
||||
const confirmRemoval = await prompts.confirm({
|
||||
message: `Remove ${modulesToRemove.length} module(s) from BMAD installation?`,
|
||||
default: false,
|
||||
});
|
||||
|
||||
if (confirmRemoval) {
|
||||
// Remove module folders
|
||||
for (const moduleId of modulesToRemove) {
|
||||
const modulePath = path.join(bmadDir, moduleId);
|
||||
try {
|
||||
if (await fs.pathExists(modulePath)) {
|
||||
await fs.remove(modulePath);
|
||||
console.log(chalk.dim(` ✓ Removed: ${moduleId}`));
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(chalk.yellow(` Warning: Failed to remove ${moduleId}: ${error.message}`));
|
||||
}
|
||||
}
|
||||
console.log(chalk.green(` ✓ Removed ${modulesToRemove.length} module(s)`));
|
||||
} else {
|
||||
console.log(chalk.dim(' → Module removal cancelled'));
|
||||
// Add the modules back to the selection since user cancelled removal
|
||||
for (const moduleId of modulesToRemove) {
|
||||
if (!config.modules) config.modules = [];
|
||||
config.modules.push(moduleId);
|
||||
}
|
||||
}
|
||||
|
||||
spinner.start('Preparing update...');
|
||||
}
|
||||
|
||||
// Detect custom and modified files BEFORE updating (compare current files vs files-manifest.csv)
|
||||
const existingFilesManifest = await this.readFilesManifest(bmadDir);
|
||||
const { customFiles, modifiedFiles } = await this.detectCustomFiles(bmadDir, existingFilesManifest);
|
||||
|
||||
@@ -252,19 +252,52 @@ class UI {
|
||||
const { installedModuleIds } = await this.getExistingInstallation(confirmedDirectory);
|
||||
|
||||
console.log(chalk.dim(` Found existing modules: ${[...installedModuleIds].join(', ')}`));
|
||||
const changeModuleSelection = await prompts.confirm({
|
||||
message: 'Modify official module selection (BMad Method, BMad Builder)?',
|
||||
default: false,
|
||||
|
||||
// Ask about BMad Method Module (bmm)
|
||||
const wantsBmm = await prompts.confirm({
|
||||
message:
|
||||
'Select the BMad Method Module for installation?\n ---> This is the Full BMad Method Agile AI Driven Development Framework Including BMad Quick Flow',
|
||||
default: installedModuleIds.has('bmm'),
|
||||
});
|
||||
|
||||
let selectedModules = [];
|
||||
if (changeModuleSelection) {
|
||||
// Show module selection with existing modules pre-selected
|
||||
const moduleChoices = await this.getModuleChoices(new Set(installedModuleIds), { hasCustomContent: false });
|
||||
selectedModules = await this.selectModules(moduleChoices, [...installedModuleIds]);
|
||||
} else {
|
||||
selectedModules = [...installedModuleIds];
|
||||
// Ask about BMad Builder Module (bmb)
|
||||
const wantsBmb = await prompts.confirm({
|
||||
message: 'Select the BMad Builder Module for installation?\n ---> Create Your Own Custom BMad Agents, Workflows and Modules',
|
||||
default: installedModuleIds.has('bmb'),
|
||||
});
|
||||
|
||||
let selectedOfficialModules = [];
|
||||
if (wantsBmm) {
|
||||
selectedOfficialModules.push('bmm');
|
||||
}
|
||||
if (wantsBmb) {
|
||||
selectedOfficialModules.push('bmb');
|
||||
}
|
||||
|
||||
// Ask about other external modules
|
||||
// Check if any external modules are already installed (not bmm, bmb, or core)
|
||||
const installedExternalModules = [...installedModuleIds].filter((id) => !['bmm', 'bmb', 'core'].includes(id));
|
||||
|
||||
let selectedExternalModules = [];
|
||||
// If external modules are already installed, skip confirm and go straight to selection
|
||||
// Otherwise ask if they want to choose external modules
|
||||
if (installedExternalModules.length > 0) {
|
||||
const externalModuleChoices = await this.getExternalModuleChoices();
|
||||
selectedExternalModules = await this.selectExternalModules(externalModuleChoices, installedExternalModules);
|
||||
} else {
|
||||
const wantsExternalModules = await prompts.confirm({
|
||||
message: 'Would you like to choose any other Recommended BMad Core Modules for installation?',
|
||||
default: false,
|
||||
});
|
||||
|
||||
if (wantsExternalModules) {
|
||||
const externalModuleChoices = await this.getExternalModuleChoices();
|
||||
selectedExternalModules = await this.selectExternalModules(externalModuleChoices, []);
|
||||
}
|
||||
}
|
||||
|
||||
// Combine official and external modules
|
||||
let selectedModules = [...selectedOfficialModules, ...selectedExternalModules];
|
||||
|
||||
// After module selection, ask about custom modules
|
||||
console.log('');
|
||||
@@ -744,16 +777,23 @@ class UI {
|
||||
/**
|
||||
* Prompt for external module selection
|
||||
* @param {Array} externalModuleChoices - Available external module choices
|
||||
* @param {Array} defaultSelections - Module codes to pre-select
|
||||
* @returns {Array} Selected external module codes
|
||||
*/
|
||||
async selectExternalModules(externalModuleChoices) {
|
||||
async selectExternalModules(externalModuleChoices, defaultSelections = []) {
|
||||
// Build a message showing available modules
|
||||
const availableNames = externalModuleChoices.map((c) => c.name).join(', ');
|
||||
const message = `Select official BMad modules to install ${availableNames ? chalk.dim(`(${availableNames})`) : ''} ${chalk.dim('(↑/↓ navigates multiselect, SPACE toggles, A to toggles All, ENTER confirm)')}:`;
|
||||
|
||||
// Mark choices as checked based on defaultSelections
|
||||
const choicesWithDefaults = externalModuleChoices.map((choice) => ({
|
||||
...choice,
|
||||
checked: defaultSelections.includes(choice.value),
|
||||
}));
|
||||
|
||||
// Add a "None" option at the end for users who changed their mind
|
||||
const choicesWithSkipOption = [
|
||||
...externalModuleChoices,
|
||||
...choicesWithDefaults,
|
||||
{
|
||||
name: '⚠ None / I changed my mind - skip external module installation',
|
||||
value: '__NONE__',
|
||||
|
||||
Reference in New Issue
Block a user