refactor(installer): remove IdeConfigManager and dead IDE config flow
Delete IdeConfigManager — IDE configs it persisted were empty
markers ({_noConfigNeeded: true}) that no code consumed.
ExistingInstall already tracks which IDEs are installed via the
manifest. Remove _loadIdeConfigurations, collectToolConfigurations
(never called), and the ideConfigurations plumbing from install().
This commit is contained in:
parent
d0f03869ca
commit
c31e334dd8
|
|
@ -1,157 +0,0 @@
|
|||
const path = require('node:path');
|
||||
const fs = require('fs-extra');
|
||||
const yaml = require('yaml');
|
||||
const prompts = require('../../../lib/prompts');
|
||||
|
||||
/**
|
||||
* Manages IDE configuration persistence
|
||||
* Saves and loads IDE-specific configurations to/from bmad/_config/ides/
|
||||
*/
|
||||
class IdeConfigManager {
|
||||
constructor() {}
|
||||
|
||||
/**
|
||||
* Get path to IDE config directory
|
||||
* @param {string} bmadDir - BMAD installation directory
|
||||
* @returns {string} Path to IDE config directory
|
||||
*/
|
||||
getIdeConfigDir(bmadDir) {
|
||||
return path.join(bmadDir, '_config', 'ides');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get path to specific IDE config file
|
||||
* @param {string} bmadDir - BMAD installation directory
|
||||
* @param {string} ideName - IDE name (e.g., 'claude-code')
|
||||
* @returns {string} Path to IDE config file
|
||||
*/
|
||||
getIdeConfigPath(bmadDir, ideName) {
|
||||
return path.join(this.getIdeConfigDir(bmadDir), `${ideName}.yaml`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save IDE configuration
|
||||
* @param {string} bmadDir - BMAD installation directory
|
||||
* @param {string} ideName - IDE name
|
||||
* @param {Object} configuration - IDE-specific configuration object
|
||||
*/
|
||||
async saveIdeConfig(bmadDir, ideName, configuration) {
|
||||
const configDir = this.getIdeConfigDir(bmadDir);
|
||||
await fs.ensureDir(configDir);
|
||||
|
||||
const configPath = this.getIdeConfigPath(bmadDir, ideName);
|
||||
const now = new Date().toISOString();
|
||||
|
||||
// Check if config already exists to preserve configured_date
|
||||
let configuredDate = now;
|
||||
if (await fs.pathExists(configPath)) {
|
||||
try {
|
||||
const existing = await this.loadIdeConfig(bmadDir, ideName);
|
||||
if (existing && existing.configured_date) {
|
||||
configuredDate = existing.configured_date;
|
||||
}
|
||||
} catch {
|
||||
// Ignore errors reading existing config
|
||||
}
|
||||
}
|
||||
|
||||
const configData = {
|
||||
ide: ideName,
|
||||
configured_date: configuredDate,
|
||||
last_updated: now,
|
||||
configuration: configuration || {},
|
||||
};
|
||||
|
||||
// Clean the config to remove any non-serializable values (like functions)
|
||||
const cleanConfig = structuredClone(configData);
|
||||
|
||||
const yamlContent = yaml.stringify(cleanConfig, {
|
||||
indent: 2,
|
||||
lineWidth: 0,
|
||||
sortKeys: false,
|
||||
});
|
||||
|
||||
// Ensure POSIX-compliant final newline
|
||||
const content = yamlContent.endsWith('\n') ? yamlContent : yamlContent + '\n';
|
||||
await fs.writeFile(configPath, content, 'utf8');
|
||||
}
|
||||
|
||||
/**
|
||||
* Load IDE configuration
|
||||
* @param {string} bmadDir - BMAD installation directory
|
||||
* @param {string} ideName - IDE name
|
||||
* @returns {Object|null} IDE configuration or null if not found
|
||||
*/
|
||||
async loadIdeConfig(bmadDir, ideName) {
|
||||
const configPath = this.getIdeConfigPath(bmadDir, ideName);
|
||||
|
||||
if (!(await fs.pathExists(configPath))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const content = await fs.readFile(configPath, 'utf8');
|
||||
const config = yaml.parse(content);
|
||||
return config;
|
||||
} catch (error) {
|
||||
await prompts.log.warn(`Failed to load IDE config for ${ideName}: ${error.message}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all IDE configurations
|
||||
* @param {string} bmadDir - BMAD installation directory
|
||||
* @returns {Object} Map of IDE name to configuration
|
||||
*/
|
||||
async loadAllIdeConfigs(bmadDir) {
|
||||
const configDir = this.getIdeConfigDir(bmadDir);
|
||||
const configs = {};
|
||||
|
||||
if (!(await fs.pathExists(configDir))) {
|
||||
return configs;
|
||||
}
|
||||
|
||||
try {
|
||||
const files = await fs.readdir(configDir);
|
||||
for (const file of files) {
|
||||
if (file.endsWith('.yaml')) {
|
||||
const ideName = file.replace('.yaml', '');
|
||||
const config = await this.loadIdeConfig(bmadDir, ideName);
|
||||
if (config) {
|
||||
configs[ideName] = config.configuration;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
await prompts.log.warn(`Failed to load IDE configs: ${error.message}`);
|
||||
}
|
||||
|
||||
return configs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if IDE has saved configuration
|
||||
* @param {string} bmadDir - BMAD installation directory
|
||||
* @param {string} ideName - IDE name
|
||||
* @returns {boolean} True if configuration exists
|
||||
*/
|
||||
async hasIdeConfig(bmadDir, ideName) {
|
||||
const configPath = this.getIdeConfigPath(bmadDir, ideName);
|
||||
return await fs.pathExists(configPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete IDE configuration
|
||||
* @param {string} bmadDir - BMAD installation directory
|
||||
* @param {string} ideName - IDE name
|
||||
*/
|
||||
async deleteIdeConfig(bmadDir, ideName) {
|
||||
const configPath = this.getIdeConfigPath(bmadDir, ideName);
|
||||
if (await fs.pathExists(configPath)) {
|
||||
await fs.remove(configPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { IdeConfigManager };
|
||||
|
|
@ -8,7 +8,6 @@ const { FileOps } = require('../../../lib/file-ops');
|
|||
const { Config } = require('./config');
|
||||
const { getProjectRoot, getSourcePath, getModulePath } = require('../../../lib/project-root');
|
||||
const { ManifestGenerator } = require('./manifest-generator');
|
||||
const { IdeConfigManager } = require('./ide-config-manager');
|
||||
const { CustomHandler } = require('../custom-handler');
|
||||
const prompts = require('../../../lib/prompts');
|
||||
const { BMAD_FOLDER_NAME } = require('../ide/shared/path-utils');
|
||||
|
|
@ -24,7 +23,6 @@ class Installer {
|
|||
this.customModules = new CustomModules();
|
||||
this.ideManager = new IdeManager();
|
||||
this.fileOps = new FileOps();
|
||||
this.ideConfigManager = new IdeConfigManager();
|
||||
this.installedFiles = new Set(); // Track all installed files
|
||||
this.bmadFolderName = BMAD_FOLDER_NAME;
|
||||
}
|
||||
|
|
@ -53,11 +51,10 @@ class Installer {
|
|||
await this._prepareUpdateState(paths, config, customConfig, existingInstall, officialModules);
|
||||
}
|
||||
|
||||
const ideConfigurations = await this._loadIdeConfigurations(config, customConfig, paths);
|
||||
await this._validateIdeSelection(config);
|
||||
|
||||
if (customConfig._isUpdate && customConfig._existingInstall) {
|
||||
await this._removeDeselectedIdes(customConfig._existingInstall, config, ideConfigurations, paths);
|
||||
await this._removeDeselectedIdes(customConfig._existingInstall, config, paths);
|
||||
}
|
||||
|
||||
// Results collector for consolidated summary
|
||||
|
|
@ -69,7 +66,7 @@ class Installer {
|
|||
const { officialModuleIds, allModules } = await this._buildModuleLists(config, customConfig, paths);
|
||||
await this._installAndConfigure(config, customConfig, paths, officialModuleIds, allModules, addResult, officialModules);
|
||||
|
||||
await this._setupIdes(config, ideConfigurations, allModules, paths, addResult);
|
||||
await this._setupIdes(config, allModules, paths, addResult);
|
||||
|
||||
await this._restoreUserFiles(paths, customConfig);
|
||||
|
||||
|
|
@ -131,38 +128,6 @@ class Installer {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load IDE configurations from saved state. No prompts.
|
||||
*/
|
||||
async _loadIdeConfigurations(config, customConfig, paths) {
|
||||
const ideConfigurations = {};
|
||||
|
||||
if (config.isQuickUpdate()) {
|
||||
const savedIdeConfigs = customConfig._savedIdeConfigs || {};
|
||||
for (const ide of config.ides || []) {
|
||||
ideConfigurations[ide] = savedIdeConfigs[ide] || { _alreadyConfigured: true };
|
||||
}
|
||||
} else {
|
||||
// Load saved configs for previously configured IDEs
|
||||
await this.ideManager.ensureInitialized();
|
||||
const bmadDir = paths.bmadDir;
|
||||
const savedIdeConfigs = await this.ideConfigManager.loadAllIdeConfigs(bmadDir);
|
||||
|
||||
const existingInstall = await ExistingInstall.detect(bmadDir);
|
||||
const previouslyConfigured = existingInstall.ides;
|
||||
|
||||
for (const ide of config.ides || []) {
|
||||
if (previouslyConfigured.includes(ide) && savedIdeConfigs[ide]) {
|
||||
ideConfigurations[ide] = savedIdeConfigs[ide];
|
||||
} else {
|
||||
ideConfigurations[ide] = { _noConfigNeeded: true };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ideConfigurations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fail fast if all selected IDEs are suspended.
|
||||
*/
|
||||
|
|
@ -190,7 +155,7 @@ class Installer {
|
|||
* Remove IDEs that were previously installed but are no longer selected.
|
||||
* No confirmation — the user's IDE selection is the decision.
|
||||
*/
|
||||
async _removeDeselectedIdes(existingInstall, config, ideConfigurations, paths) {
|
||||
async _removeDeselectedIdes(existingInstall, config, paths) {
|
||||
const previouslyInstalled = new Set(existingInstall.ides);
|
||||
const newlySelected = new Set(config.ides || []);
|
||||
const toRemove = [...previouslyInstalled].filter((ide) => !newlySelected.has(ide));
|
||||
|
|
@ -204,7 +169,6 @@ class Installer {
|
|||
if (handler) {
|
||||
await handler.cleanup(paths.projectRoot);
|
||||
}
|
||||
await this.ideConfigManager.deleteIdeConfig(paths.bmadDir, ide);
|
||||
} catch (error) {
|
||||
await prompts.log.warn(`Warning: Failed to remove ${ide}: ${error.message}`);
|
||||
}
|
||||
|
|
@ -406,7 +370,7 @@ class Installer {
|
|||
/**
|
||||
* Set up IDE integrations for each selected IDE.
|
||||
*/
|
||||
async _setupIdes(config, ideConfigurations, allModules, paths, addResult) {
|
||||
async _setupIdes(config, allModules, paths, addResult) {
|
||||
if (config.skipIde || !config.ides || config.ides.length === 0) return;
|
||||
|
||||
await this.ideManager.ensureInitialized();
|
||||
|
|
@ -418,30 +382,15 @@ class Installer {
|
|||
}
|
||||
|
||||
for (const ide of validIdes) {
|
||||
const ideHasConfig = Boolean(ideConfigurations[ide]);
|
||||
const originalLog = console.log;
|
||||
if (!config.verbose && ideHasConfig) {
|
||||
console.log = () => {};
|
||||
}
|
||||
try {
|
||||
const setupResult = await this.ideManager.setup(ide, paths.projectRoot, paths.bmadDir, {
|
||||
selectedModules: allModules || [],
|
||||
preCollectedConfig: ideConfigurations[ide] || null,
|
||||
verbose: config.verbose,
|
||||
silent: ideHasConfig,
|
||||
});
|
||||
const setupResult = await this.ideManager.setup(ide, paths.projectRoot, paths.bmadDir, {
|
||||
selectedModules: allModules || [],
|
||||
verbose: config.verbose,
|
||||
});
|
||||
|
||||
if (ideConfigurations[ide] && !ideConfigurations[ide]._alreadyConfigured) {
|
||||
await this.ideConfigManager.saveIdeConfig(paths.bmadDir, ide, ideConfigurations[ide]);
|
||||
}
|
||||
|
||||
if (setupResult.success) {
|
||||
addResult(ide, 'ok', setupResult.detail || '');
|
||||
} else {
|
||||
addResult(ide, 'error', setupResult.error || 'failed');
|
||||
}
|
||||
} finally {
|
||||
console.log = originalLog;
|
||||
if (setupResult.success) {
|
||||
addResult(ide, 'ok', setupResult.detail || '');
|
||||
} else {
|
||||
addResult(ide, 'error', setupResult.error || 'failed');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -768,115 +717,6 @@ class Installer {
|
|||
/**
|
||||
* Collect Tool/IDE configurations after module configuration
|
||||
* @param {string} projectDir - Project directory
|
||||
* @param {Array} selectedModules - Selected modules from configuration
|
||||
* @param {boolean} isFullReinstall - Whether this is a full reinstall
|
||||
* @param {Array} previousIdes - Previously configured IDEs (for reinstalls)
|
||||
* @param {Array} preSelectedIdes - Pre-selected IDEs from early prompt (optional)
|
||||
* @param {boolean} skipPrompts - Skip prompts and use defaults (for --yes flag)
|
||||
* @returns {Object} Tool/IDE selection and configurations
|
||||
*/
|
||||
async collectToolConfigurations(
|
||||
projectDir,
|
||||
selectedModules,
|
||||
isFullReinstall = false,
|
||||
previousIdes = [],
|
||||
preSelectedIdes = null,
|
||||
skipPrompts = false,
|
||||
) {
|
||||
// Use pre-selected IDEs if provided, otherwise prompt
|
||||
let toolConfig;
|
||||
if (preSelectedIdes === null) {
|
||||
// Fallback: prompt for tool selection (backwards compatibility)
|
||||
const { UI } = require('../../../lib/ui');
|
||||
const ui = new UI();
|
||||
toolConfig = await ui.promptToolSelection(projectDir);
|
||||
} else {
|
||||
// IDEs were already selected during initial prompts
|
||||
toolConfig = {
|
||||
ides: preSelectedIdes,
|
||||
skipIde: !preSelectedIdes || preSelectedIdes.length === 0,
|
||||
};
|
||||
}
|
||||
|
||||
// Check for already configured IDEs
|
||||
const bmadDir = path.join(projectDir, BMAD_FOLDER_NAME);
|
||||
|
||||
// During full reinstall, use the saved previous IDEs since bmad dir was deleted
|
||||
// Otherwise detect from existing installation
|
||||
let previouslyConfiguredIdes;
|
||||
if (isFullReinstall) {
|
||||
previouslyConfiguredIdes = [];
|
||||
} else {
|
||||
const existingInstall = await ExistingInstall.detect(bmadDir);
|
||||
previouslyConfiguredIdes = existingInstall.ides;
|
||||
}
|
||||
|
||||
// Load saved IDE configurations for already-configured IDEs
|
||||
const savedIdeConfigs = await this.ideConfigManager.loadAllIdeConfigs(bmadDir);
|
||||
|
||||
// Collect IDE-specific configurations if any were selected
|
||||
const ideConfigurations = {};
|
||||
|
||||
// First, add saved configs for already-configured IDEs
|
||||
for (const ide of toolConfig.ides || []) {
|
||||
if (previouslyConfiguredIdes.includes(ide) && savedIdeConfigs[ide]) {
|
||||
ideConfigurations[ide] = savedIdeConfigs[ide];
|
||||
}
|
||||
}
|
||||
|
||||
if (!toolConfig.skipIde && toolConfig.ides && toolConfig.ides.length > 0) {
|
||||
// Ensure IDE manager is initialized
|
||||
await this.ideManager.ensureInitialized();
|
||||
|
||||
// Determine which IDEs are newly selected (not previously configured)
|
||||
const newlySelectedIdes = toolConfig.ides.filter((ide) => !previouslyConfiguredIdes.includes(ide));
|
||||
|
||||
if (newlySelectedIdes.length > 0) {
|
||||
// Collect configuration for IDEs that support it
|
||||
for (const ide of newlySelectedIdes) {
|
||||
try {
|
||||
const handler = this.ideManager.handlers.get(ide);
|
||||
|
||||
if (!handler) {
|
||||
await prompts.log.warn(`Warning: IDE '${ide}' handler not found`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if this IDE handler has a collectConfiguration method
|
||||
// (custom installers like Codex, Kilo may have this)
|
||||
if (typeof handler.collectConfiguration === 'function') {
|
||||
await prompts.log.info(`Configuring ${ide}...`);
|
||||
ideConfigurations[ide] = await handler.collectConfiguration({
|
||||
selectedModules: selectedModules || [],
|
||||
projectDir,
|
||||
bmadDir,
|
||||
skipPrompts,
|
||||
});
|
||||
} else {
|
||||
// Config-driven IDEs don't need configuration - mark as ready
|
||||
ideConfigurations[ide] = { _noConfigNeeded: true };
|
||||
}
|
||||
} catch (error) {
|
||||
// IDE doesn't support configuration or has an error
|
||||
await prompts.log.warn(`Warning: Could not load configuration for ${ide}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Log which IDEs are already configured and being kept
|
||||
const keptIdes = toolConfig.ides.filter((ide) => previouslyConfiguredIdes.includes(ide));
|
||||
if (keptIdes.length > 0) {
|
||||
await prompts.log.message(`Keeping existing configuration for: ${keptIdes.join(', ')}`);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
ides: toolConfig.ides,
|
||||
skipIde: toolConfig.skipIde,
|
||||
configurations: ideConfigurations,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Private: Prompt for update action
|
||||
*/
|
||||
|
|
@ -1490,9 +1330,6 @@ class Installer {
|
|||
}
|
||||
}
|
||||
|
||||
// Load saved IDE configurations
|
||||
const savedIdeConfigs = await this.ideConfigManager.loadAllIdeConfigs(bmadDir);
|
||||
|
||||
// Get available modules (what we have source for)
|
||||
const availableModulesData = await new OfficialModules().listAvailable();
|
||||
const availableModules = [...availableModulesData.modules, ...availableModulesData.customModules];
|
||||
|
|
@ -1614,7 +1451,6 @@ class Installer {
|
|||
actionType: 'install', // Use regular install flow
|
||||
_quickUpdate: true, // Flag to skip certain prompts
|
||||
_preserveModules: skippedModules, // Preserve these in manifest even though we didn't update them
|
||||
_savedIdeConfigs: savedIdeConfigs, // Pass saved IDE configs to installer
|
||||
_customModuleSources: customModuleSources, // Pass custom module sources for updates
|
||||
_existingModules: installedModules, // Pass all installed modules for manifest generation
|
||||
customContent: config.customContent, // Pass through for re-caching from source
|
||||
|
|
|
|||
Loading…
Reference in New Issue