Modify installation now will remove modules that get unselected, with an option to confirm the deletion
This commit is contained in:
parent
577c1aa218
commit
b952d28fb3
|
|
@ -0,0 +1 @@
|
||||||
|
# This file ensures the directory is tracked by git
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
# This file ensures the directory is tracked by git
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
# This file ensures the directory is tracked by git
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
# This file ensures the directory is tracked by git
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
# This file ensures the directory is tracked by git
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
# This file ensures the directory is tracked by git
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
# This file ensures the directory is tracked by git
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
# This file ensures the directory is tracked by git
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
# This file ensures the directory is tracked by git
|
||||||
|
|
@ -444,6 +444,60 @@ class Installer {
|
||||||
config._isUpdate = true;
|
config._isUpdate = true;
|
||||||
config._existingInstall = existingInstall;
|
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)
|
// Detect custom and modified files BEFORE updating (compare current files vs files-manifest.csv)
|
||||||
const existingFilesManifest = await this.readFilesManifest(bmadDir);
|
const existingFilesManifest = await this.readFilesManifest(bmadDir);
|
||||||
const { customFiles, modifiedFiles } = await this.detectCustomFiles(bmadDir, existingFilesManifest);
|
const { customFiles, modifiedFiles } = await this.detectCustomFiles(bmadDir, existingFilesManifest);
|
||||||
|
|
|
||||||
|
|
@ -252,19 +252,52 @@ class UI {
|
||||||
const { installedModuleIds } = await this.getExistingInstallation(confirmedDirectory);
|
const { installedModuleIds } = await this.getExistingInstallation(confirmedDirectory);
|
||||||
|
|
||||||
console.log(chalk.dim(` Found existing modules: ${[...installedModuleIds].join(', ')}`));
|
console.log(chalk.dim(` Found existing modules: ${[...installedModuleIds].join(', ')}`));
|
||||||
const changeModuleSelection = await prompts.confirm({
|
|
||||||
message: 'Modify official module selection (BMad Method, BMad Builder)?',
|
// 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'),
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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,
|
default: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
let selectedModules = [];
|
if (wantsExternalModules) {
|
||||||
if (changeModuleSelection) {
|
const externalModuleChoices = await this.getExternalModuleChoices();
|
||||||
// Show module selection with existing modules pre-selected
|
selectedExternalModules = await this.selectExternalModules(externalModuleChoices, []);
|
||||||
const moduleChoices = await this.getModuleChoices(new Set(installedModuleIds), { hasCustomContent: false });
|
|
||||||
selectedModules = await this.selectModules(moduleChoices, [...installedModuleIds]);
|
|
||||||
} else {
|
|
||||||
selectedModules = [...installedModuleIds];
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Combine official and external modules
|
||||||
|
let selectedModules = [...selectedOfficialModules, ...selectedExternalModules];
|
||||||
|
|
||||||
// After module selection, ask about custom modules
|
// After module selection, ask about custom modules
|
||||||
console.log('');
|
console.log('');
|
||||||
|
|
@ -744,16 +777,23 @@ class UI {
|
||||||
/**
|
/**
|
||||||
* Prompt for external module selection
|
* Prompt for external module selection
|
||||||
* @param {Array} externalModuleChoices - Available external module choices
|
* @param {Array} externalModuleChoices - Available external module choices
|
||||||
|
* @param {Array} defaultSelections - Module codes to pre-select
|
||||||
* @returns {Array} Selected external module codes
|
* @returns {Array} Selected external module codes
|
||||||
*/
|
*/
|
||||||
async selectExternalModules(externalModuleChoices) {
|
async selectExternalModules(externalModuleChoices, defaultSelections = []) {
|
||||||
// Build a message showing available modules
|
// Build a message showing available modules
|
||||||
const availableNames = externalModuleChoices.map((c) => c.name).join(', ');
|
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)')}:`;
|
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
|
// Add a "None" option at the end for users who changed their mind
|
||||||
const choicesWithSkipOption = [
|
const choicesWithSkipOption = [
|
||||||
...externalModuleChoices,
|
...choicesWithDefaults,
|
||||||
{
|
{
|
||||||
name: '⚠ None / I changed my mind - skip external module installation',
|
name: '⚠ None / I changed my mind - skip external module installation',
|
||||||
value: '__NONE__',
|
value: '__NONE__',
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue