installer fixes
This commit is contained in:
parent
93c3100bde
commit
9b265818ff
|
|
@ -47,7 +47,7 @@ dev_story_location:
|
||||||
# TEA Agent Configuration
|
# TEA Agent Configuration
|
||||||
tea_use_mcp_enhancements:
|
tea_use_mcp_enhancements:
|
||||||
prompt: "Enable Playwright MCP capabilities (healing, exploratory, verification)?"
|
prompt: "Enable Playwright MCP capabilities (healing, exploratory, verification)?"
|
||||||
default: true
|
default: false
|
||||||
result: "{value}"
|
result: "{value}"
|
||||||
# kb_location:
|
# kb_location:
|
||||||
# prompt: "Where should bmad knowledge base articles be stored?"
|
# prompt: "Where should bmad knowledge base articles be stored?"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,152 @@
|
||||||
|
const path = require('node:path');
|
||||||
|
const fs = require('fs-extra');
|
||||||
|
const yaml = require('js-yaml');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages IDE configuration persistence
|
||||||
|
* Saves and loads IDE-specific configurations to/from bmad/_cfg/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, '_cfg', '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 || {},
|
||||||
|
};
|
||||||
|
|
||||||
|
const yamlContent = yaml.dump(configData, {
|
||||||
|
indent: 2,
|
||||||
|
lineWidth: -1,
|
||||||
|
noRefs: true,
|
||||||
|
sortKeys: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
await fs.writeFile(configPath, yamlContent, '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.load(content);
|
||||||
|
return config;
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`Warning: 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) {
|
||||||
|
console.warn('Warning: 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 };
|
||||||
|
|
@ -16,6 +16,7 @@ const { getProjectRoot, getSourcePath, getModulePath } = require('../../../lib/p
|
||||||
const { AgentPartyGenerator } = require('../../../lib/agent-party-generator');
|
const { AgentPartyGenerator } = require('../../../lib/agent-party-generator');
|
||||||
const { CLIUtils } = require('../../../lib/cli-utils');
|
const { CLIUtils } = require('../../../lib/cli-utils');
|
||||||
const { ManifestGenerator } = require('./manifest-generator');
|
const { ManifestGenerator } = require('./manifest-generator');
|
||||||
|
const { IdeConfigManager } = require('./ide-config-manager');
|
||||||
|
|
||||||
class Installer {
|
class Installer {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|
@ -28,6 +29,7 @@ class Installer {
|
||||||
this.xmlHandler = new XmlHandler();
|
this.xmlHandler = new XmlHandler();
|
||||||
this.dependencyResolver = new DependencyResolver();
|
this.dependencyResolver = new DependencyResolver();
|
||||||
this.configCollector = new ConfigCollector();
|
this.configCollector = new ConfigCollector();
|
||||||
|
this.ideConfigManager = new IdeConfigManager();
|
||||||
this.installedFiles = []; // Track all installed files
|
this.installedFiles = []; // Track all installed files
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -59,9 +61,19 @@ class Installer {
|
||||||
previouslyConfiguredIdes = existingInstall.ides || [];
|
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
|
// Collect IDE-specific configurations if any were selected
|
||||||
const ideConfigurations = {};
|
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) {
|
if (!toolConfig.skipIde && toolConfig.ides && toolConfig.ides.length > 0) {
|
||||||
// Determine which IDEs are newly selected (not previously configured)
|
// Determine which IDEs are newly selected (not previously configured)
|
||||||
const newlySelectedIdes = toolConfig.ides.filter((ide) => !previouslyConfiguredIdes.includes(ide));
|
const newlySelectedIdes = toolConfig.ides.filter((ide) => !previouslyConfiguredIdes.includes(ide));
|
||||||
|
|
@ -358,11 +370,17 @@ class Installer {
|
||||||
spinner.stop();
|
spinner.stop();
|
||||||
let toolSelection;
|
let toolSelection;
|
||||||
if (config._quickUpdate) {
|
if (config._quickUpdate) {
|
||||||
// Quick update already has IDEs configured, skip prompting
|
// Quick update already has IDEs configured, use saved configurations
|
||||||
// Set a flag to indicate all IDEs are pre-configured
|
|
||||||
const preConfiguredIdes = {};
|
const preConfiguredIdes = {};
|
||||||
|
const savedIdeConfigs = config._savedIdeConfigs || {};
|
||||||
|
|
||||||
for (const ide of config.ides || []) {
|
for (const ide of config.ides || []) {
|
||||||
preConfiguredIdes[ide] = { _alreadyConfigured: true };
|
// Use saved config if available, otherwise mark as already configured (legacy)
|
||||||
|
if (savedIdeConfigs[ide]) {
|
||||||
|
preConfiguredIdes[ide] = savedIdeConfigs[ide];
|
||||||
|
} else {
|
||||||
|
preConfiguredIdes[ide] = { _alreadyConfigured: true };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
toolSelection = {
|
toolSelection = {
|
||||||
ides: config.ides || [],
|
ides: config.ides || [],
|
||||||
|
|
@ -467,7 +485,12 @@ class Installer {
|
||||||
|
|
||||||
// Configure IDEs and copy documentation
|
// Configure IDEs and copy documentation
|
||||||
if (!config.skipIde && config.ides && config.ides.length > 0) {
|
if (!config.skipIde && config.ides && config.ides.length > 0) {
|
||||||
spinner.start('Configuring IDEs...');
|
// Check if any IDE might need prompting (no pre-collected config)
|
||||||
|
const needsPrompting = config.ides.some((ide) => !ideConfigurations[ide]);
|
||||||
|
|
||||||
|
if (!needsPrompting) {
|
||||||
|
spinner.start('Configuring IDEs...');
|
||||||
|
}
|
||||||
|
|
||||||
// Temporarily suppress console output if not verbose
|
// Temporarily suppress console output if not verbose
|
||||||
const originalLog = console.log;
|
const originalLog = console.log;
|
||||||
|
|
@ -476,7 +499,16 @@ class Installer {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const ide of config.ides) {
|
for (const ide of config.ides) {
|
||||||
spinner.text = `Configuring ${ide}...`;
|
// Only show spinner if we have pre-collected config (no prompts expected)
|
||||||
|
if (ideConfigurations[ide] && !needsPrompting) {
|
||||||
|
spinner.text = `Configuring ${ide}...`;
|
||||||
|
} else if (!ideConfigurations[ide]) {
|
||||||
|
// Stop spinner before prompting
|
||||||
|
if (spinner.isSpinning) {
|
||||||
|
spinner.stop();
|
||||||
|
}
|
||||||
|
console.log(chalk.cyan(`\nConfiguring ${ide}...`));
|
||||||
|
}
|
||||||
|
|
||||||
// Pass pre-collected configuration to avoid re-prompting
|
// Pass pre-collected configuration to avoid re-prompting
|
||||||
await this.ideManager.setup(ide, projectDir, bmadDir, {
|
await this.ideManager.setup(ide, projectDir, bmadDir, {
|
||||||
|
|
@ -484,12 +516,26 @@ class Installer {
|
||||||
preCollectedConfig: ideConfigurations[ide] || null,
|
preCollectedConfig: ideConfigurations[ide] || null,
|
||||||
verbose: config.verbose,
|
verbose: config.verbose,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Save IDE configuration for future updates
|
||||||
|
if (ideConfigurations[ide] && !ideConfigurations[ide]._alreadyConfigured) {
|
||||||
|
await this.ideConfigManager.saveIdeConfig(bmadDir, ide, ideConfigurations[ide]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restart spinner if we stopped it
|
||||||
|
if (!ideConfigurations[ide] && !spinner.isSpinning) {
|
||||||
|
spinner.start('Configuring IDEs...');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore console.log
|
// Restore console.log
|
||||||
console.log = originalLog;
|
console.log = originalLog;
|
||||||
|
|
||||||
spinner.succeed(`Configured ${config.ides.length} IDE${config.ides.length > 1 ? 's' : ''}`);
|
if (spinner.isSpinning) {
|
||||||
|
spinner.succeed(`Configured ${config.ides.length} IDE${config.ides.length > 1 ? 's' : ''}`);
|
||||||
|
} else {
|
||||||
|
console.log(chalk.green(`✓ Configured ${config.ides.length} IDE${config.ides.length > 1 ? 's' : ''}`));
|
||||||
|
}
|
||||||
|
|
||||||
// Copy IDE-specific documentation
|
// Copy IDE-specific documentation
|
||||||
spinner.start('Copying IDE documentation...');
|
spinner.start('Copying IDE documentation...');
|
||||||
|
|
@ -1447,6 +1493,9 @@ class Installer {
|
||||||
const installedModules = existingInstall.modules.map((m) => m.id);
|
const installedModules = existingInstall.modules.map((m) => m.id);
|
||||||
const configuredIdes = existingInstall.ides || [];
|
const configuredIdes = existingInstall.ides || [];
|
||||||
|
|
||||||
|
// Load saved IDE configurations
|
||||||
|
const savedIdeConfigs = await this.ideConfigManager.loadAllIdeConfigs(bmadDir);
|
||||||
|
|
||||||
// Get available modules (what we have source for)
|
// Get available modules (what we have source for)
|
||||||
const availableModules = await this.moduleManager.listAvailable();
|
const availableModules = await this.moduleManager.listAvailable();
|
||||||
const availableModuleIds = new Set(availableModules.map((m) => m.id));
|
const availableModuleIds = new Set(availableModules.map((m) => m.id));
|
||||||
|
|
@ -1506,6 +1555,7 @@ class Installer {
|
||||||
actionType: 'install', // Use regular install flow
|
actionType: 'install', // Use regular install flow
|
||||||
_quickUpdate: true, // Flag to skip certain prompts
|
_quickUpdate: true, // Flag to skip certain prompts
|
||||||
_preserveModules: skippedModules, // Preserve these in manifest even though we didn't update them
|
_preserveModules: skippedModules, // Preserve these in manifest even though we didn't update them
|
||||||
|
_savedIdeConfigs: savedIdeConfigs, // Pass saved IDE configs to installer
|
||||||
};
|
};
|
||||||
|
|
||||||
// Call the standard install method
|
// Call the standard install method
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue