refactor(installer): extract InstallPaths class for path init and validation
Replace inline path construction in install() with a dedicated class that ensures all structural directories exist, validates permissions, and provides derived-path methods for manifests and modules.
This commit is contained in:
parent
efca29485e
commit
1a1909a7f9
|
|
@ -0,0 +1,129 @@
|
||||||
|
const path = require('node:path');
|
||||||
|
const fs = require('fs-extra');
|
||||||
|
const { getProjectRoot } = require('../../../lib/project-root');
|
||||||
|
const { BMAD_FOLDER_NAME } = require('../ide/shared/path-utils');
|
||||||
|
|
||||||
|
class InstallPaths {
|
||||||
|
static async create(config) {
|
||||||
|
const srcDir = getProjectRoot();
|
||||||
|
await assertReadableDir(srcDir, 'BMAD source root');
|
||||||
|
|
||||||
|
const pkgPath = path.join(srcDir, 'package.json');
|
||||||
|
await assertReadableFile(pkgPath, 'package.json');
|
||||||
|
const version = require(pkgPath).version;
|
||||||
|
|
||||||
|
const projectRoot = path.resolve(config.directory);
|
||||||
|
await ensureWritableDir(projectRoot, 'project root');
|
||||||
|
|
||||||
|
const bmadDir = path.join(projectRoot, BMAD_FOLDER_NAME);
|
||||||
|
const isUpdate = await fs.pathExists(bmadDir);
|
||||||
|
|
||||||
|
const configDir = path.join(bmadDir, '_config');
|
||||||
|
const agentsDir = path.join(configDir, 'agents');
|
||||||
|
const customCacheDir = path.join(configDir, 'custom');
|
||||||
|
const coreDir = path.join(bmadDir, 'core');
|
||||||
|
|
||||||
|
for (const [dir, label] of [
|
||||||
|
[bmadDir, 'bmad directory'],
|
||||||
|
[configDir, 'config directory'],
|
||||||
|
[agentsDir, 'agents config directory'],
|
||||||
|
[customCacheDir, 'custom modules cache'],
|
||||||
|
[coreDir, 'core module directory'],
|
||||||
|
]) {
|
||||||
|
await ensureWritableDir(dir, label);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new InstallPaths({
|
||||||
|
srcDir,
|
||||||
|
version,
|
||||||
|
projectRoot,
|
||||||
|
bmadDir,
|
||||||
|
configDir,
|
||||||
|
agentsDir,
|
||||||
|
customCacheDir,
|
||||||
|
coreDir,
|
||||||
|
isUpdate,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
Object.assign(this, props);
|
||||||
|
Object.freeze(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
manifestFile() {
|
||||||
|
return path.join(this.configDir, 'manifest.yaml');
|
||||||
|
}
|
||||||
|
agentManifest() {
|
||||||
|
return path.join(this.configDir, 'agent-manifest.csv');
|
||||||
|
}
|
||||||
|
filesManifest() {
|
||||||
|
return path.join(this.configDir, 'files-manifest.csv');
|
||||||
|
}
|
||||||
|
helpCatalog() {
|
||||||
|
return path.join(this.configDir, 'bmad-help.csv');
|
||||||
|
}
|
||||||
|
moduleDir(name) {
|
||||||
|
return path.join(this.bmadDir, name);
|
||||||
|
}
|
||||||
|
moduleConfig(name) {
|
||||||
|
return path.join(this.bmadDir, name, 'config.yaml');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function assertReadableDir(dirPath, label) {
|
||||||
|
const stat = await fs.stat(dirPath).catch(() => null);
|
||||||
|
if (!stat) {
|
||||||
|
throw new Error(`${label} does not exist: ${dirPath}`);
|
||||||
|
}
|
||||||
|
if (!stat.isDirectory()) {
|
||||||
|
throw new Error(`${label} is not a directory: ${dirPath}`);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await fs.access(dirPath, fs.constants.R_OK);
|
||||||
|
} catch {
|
||||||
|
throw new Error(`${label} is not readable: ${dirPath}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function assertReadableFile(filePath, label) {
|
||||||
|
const stat = await fs.stat(filePath).catch(() => null);
|
||||||
|
if (!stat) {
|
||||||
|
throw new Error(`${label} does not exist: ${filePath}`);
|
||||||
|
}
|
||||||
|
if (!stat.isFile()) {
|
||||||
|
throw new Error(`${label} is not a file: ${filePath}`);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await fs.access(filePath, fs.constants.R_OK);
|
||||||
|
} catch {
|
||||||
|
throw new Error(`${label} is not readable: ${filePath}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function ensureWritableDir(dirPath, label) {
|
||||||
|
const stat = await fs.stat(dirPath).catch(() => null);
|
||||||
|
if (stat && !stat.isDirectory()) {
|
||||||
|
throw new Error(`${label} exists but is not a directory: ${dirPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await fs.ensureDir(dirPath);
|
||||||
|
} catch (error) {
|
||||||
|
if (error.code === 'EACCES') {
|
||||||
|
throw new Error(`${label}: permission denied creating directory: ${dirPath}`);
|
||||||
|
}
|
||||||
|
if (error.code === 'ENOSPC') {
|
||||||
|
throw new Error(`${label}: no space left on device: ${dirPath}`);
|
||||||
|
}
|
||||||
|
throw new Error(`${label}: cannot create directory: ${dirPath} (${error.message})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await fs.access(dirPath, fs.constants.R_OK | fs.constants.W_OK);
|
||||||
|
} catch {
|
||||||
|
throw new Error(`${label} is not writable: ${dirPath}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { InstallPaths };
|
||||||
|
|
@ -15,6 +15,7 @@ const { IdeConfigManager } = require('./ide-config-manager');
|
||||||
const { CustomHandler } = require('../custom/handler');
|
const { CustomHandler } = require('../custom/handler');
|
||||||
const prompts = require('../../../lib/prompts');
|
const prompts = require('../../../lib/prompts');
|
||||||
const { BMAD_FOLDER_NAME } = require('../ide/shared/path-utils');
|
const { BMAD_FOLDER_NAME } = require('../ide/shared/path-utils');
|
||||||
|
const { InstallPaths } = require('./install-paths');
|
||||||
|
|
||||||
class Installer {
|
class Installer {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|
@ -228,23 +229,14 @@ class Installer {
|
||||||
// Clone config to avoid mutating the caller's object
|
// Clone config to avoid mutating the caller's object
|
||||||
const config = { ...originalConfig };
|
const config = { ...originalConfig };
|
||||||
|
|
||||||
// Check if core config was already collected in UI
|
// if core config isn't collected, we haven't run the UI -> display logo/version
|
||||||
const hasCoreConfig = config.coreConfig && Object.keys(config.coreConfig).length > 0;
|
const hasCoreConfig = config.coreConfig && Object.keys(config.coreConfig).length > 0;
|
||||||
|
|
||||||
// Only display logo if core config wasn't already collected (meaning we're not continuing from UI)
|
|
||||||
if (!hasCoreConfig) {
|
if (!hasCoreConfig) {
|
||||||
// Display BMAD logo
|
|
||||||
await CLIUtils.displayLogo();
|
await CLIUtils.displayLogo();
|
||||||
|
|
||||||
// Display welcome message
|
|
||||||
await CLIUtils.displaySection('BMad™ Installation', 'Version ' + require(path.join(getProjectRoot(), 'package.json')).version);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: Legacy V4 detection now happens earlier in UI.promptInstall()
|
const paths = await InstallPaths.create(config);
|
||||||
// before any config collection, so we don't need to check again here
|
const { projectRoot, bmadDir, srcDir } = paths;
|
||||||
|
|
||||||
const projectDir = path.resolve(config.directory);
|
|
||||||
const bmadDir = path.join(projectDir, BMAD_FOLDER_NAME);
|
|
||||||
|
|
||||||
// If core config was pre-collected (from interactive mode), use it
|
// If core config was pre-collected (from interactive mode), use it
|
||||||
if (config.coreConfig && Object.keys(config.coreConfig).length > 0) {
|
if (config.coreConfig && Object.keys(config.coreConfig).length > 0) {
|
||||||
|
|
@ -285,7 +277,7 @@ class Installer {
|
||||||
// If no sourcePath but we have relativePath, convert it
|
// If no sourcePath but we have relativePath, convert it
|
||||||
else if (!absoluteSourcePath && customModule.relativePath) {
|
else if (!absoluteSourcePath && customModule.relativePath) {
|
||||||
// relativePath is relative to the project root (parent of bmad dir)
|
// relativePath is relative to the project root (parent of bmad dir)
|
||||||
absoluteSourcePath = path.resolve(projectDir, customModule.relativePath);
|
absoluteSourcePath = path.resolve(projectRoot, customModule.relativePath);
|
||||||
}
|
}
|
||||||
// Ensure sourcePath is absolute for anything else
|
// Ensure sourcePath is absolute for anything else
|
||||||
else if (absoluteSourcePath && !path.isAbsolute(absoluteSourcePath)) {
|
else if (absoluteSourcePath && !path.isAbsolute(absoluteSourcePath)) {
|
||||||
|
|
@ -304,7 +296,7 @@ class Installer {
|
||||||
if (config.customContent && config.customContent.selected && config.customContent.selectedFiles) {
|
if (config.customContent && config.customContent.selected && config.customContent.selectedFiles) {
|
||||||
const customHandler = new CustomHandler();
|
const customHandler = new CustomHandler();
|
||||||
for (const customFile of config.customContent.selectedFiles) {
|
for (const customFile of config.customContent.selectedFiles) {
|
||||||
const customInfo = await customHandler.getCustomInfo(customFile, path.resolve(config.directory));
|
const customInfo = await customHandler.getCustomInfo(customFile, projectRoot);
|
||||||
if (customInfo && customInfo.id) {
|
if (customInfo && customInfo.id) {
|
||||||
customModulePaths.set(customInfo.id, customInfo.path);
|
customModulePaths.set(customInfo.id, customInfo.path);
|
||||||
}
|
}
|
||||||
|
|
@ -357,13 +349,13 @@ class Installer {
|
||||||
if (config.coreConfig && Object.keys(config.coreConfig).length > 0) {
|
if (config.coreConfig && Object.keys(config.coreConfig).length > 0) {
|
||||||
// Core already collected, skip it in config collection
|
// Core already collected, skip it in config collection
|
||||||
const modulesWithoutCore = allModulesForConfig.filter((m) => m !== 'core');
|
const modulesWithoutCore = allModulesForConfig.filter((m) => m !== 'core');
|
||||||
moduleConfigs = await this.configCollector.collectAllConfigurations(modulesWithoutCore, path.resolve(config.directory), {
|
moduleConfigs = await this.configCollector.collectAllConfigurations(modulesWithoutCore, projectRoot, {
|
||||||
customModulePaths,
|
customModulePaths,
|
||||||
skipPrompts: config.skipPrompts,
|
skipPrompts: config.skipPrompts,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Core not collected yet, include it
|
// Core not collected yet, include it
|
||||||
moduleConfigs = await this.configCollector.collectAllConfigurations(allModulesForConfig, path.resolve(config.directory), {
|
moduleConfigs = await this.configCollector.collectAllConfigurations(allModulesForConfig, projectRoot, {
|
||||||
customModulePaths,
|
customModulePaths,
|
||||||
skipPrompts: config.skipPrompts,
|
skipPrompts: config.skipPrompts,
|
||||||
});
|
});
|
||||||
|
|
@ -382,26 +374,6 @@ class Installer {
|
||||||
spinner.start('Preparing installation...');
|
spinner.start('Preparing installation...');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Create a project directory if it doesn't exist (user already confirmed)
|
|
||||||
if (!(await fs.pathExists(projectDir))) {
|
|
||||||
spinner.message('Creating installation directory...');
|
|
||||||
try {
|
|
||||||
// fs.ensureDir handles platform-specific directory creation
|
|
||||||
// It will recursively create all necessary parent directories
|
|
||||||
await fs.ensureDir(projectDir);
|
|
||||||
} catch (error) {
|
|
||||||
spinner.error('Failed to create installation directory');
|
|
||||||
await prompts.log.error(`Error: ${error.message}`);
|
|
||||||
// More detailed error for common issues
|
|
||||||
if (error.code === 'EACCES') {
|
|
||||||
await prompts.log.error('Permission denied. Check parent directory permissions.');
|
|
||||||
} else if (error.code === 'ENOSPC') {
|
|
||||||
await prompts.log.error('No space left on device.');
|
|
||||||
}
|
|
||||||
throw new Error(`Cannot create directory: ${projectDir}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check existing installation
|
// Check existing installation
|
||||||
spinner.message('Checking for existing installation...');
|
spinner.message('Checking for existing installation...');
|
||||||
const existingInstall = await this.detector.detect(bmadDir);
|
const existingInstall = await this.detector.detect(bmadDir);
|
||||||
|
|
@ -457,7 +429,7 @@ class Installer {
|
||||||
for (const moduleId of modulesToRemove) {
|
for (const moduleId of modulesToRemove) {
|
||||||
const moduleInfo = existingInstall.modules.find((m) => m.id === moduleId);
|
const moduleInfo = existingInstall.modules.find((m) => m.id === moduleId);
|
||||||
const displayName = moduleInfo?.name || moduleId;
|
const displayName = moduleInfo?.name || moduleId;
|
||||||
const modulePath = path.join(bmadDir, moduleId);
|
const modulePath = paths.moduleDir(moduleId);
|
||||||
await prompts.log.error(` - ${displayName} (${modulePath})`);
|
await prompts.log.error(` - ${displayName} (${modulePath})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -469,7 +441,7 @@ class Installer {
|
||||||
if (confirmRemoval) {
|
if (confirmRemoval) {
|
||||||
// Remove module folders
|
// Remove module folders
|
||||||
for (const moduleId of modulesToRemove) {
|
for (const moduleId of modulesToRemove) {
|
||||||
const modulePath = path.join(bmadDir, moduleId);
|
const modulePath = paths.moduleDir(moduleId);
|
||||||
try {
|
try {
|
||||||
if (await fs.pathExists(modulePath)) {
|
if (await fs.pathExists(modulePath)) {
|
||||||
await fs.remove(modulePath);
|
await fs.remove(modulePath);
|
||||||
|
|
@ -502,7 +474,7 @@ class Installer {
|
||||||
|
|
||||||
// Preserve existing core configuration during updates
|
// Preserve existing core configuration during updates
|
||||||
// Read the current core config.yaml to maintain user's settings
|
// Read the current core config.yaml to maintain user's settings
|
||||||
const coreConfigPath = path.join(bmadDir, 'core', 'config.yaml');
|
const coreConfigPath = paths.moduleConfig('core');
|
||||||
if ((await fs.pathExists(coreConfigPath)) && (!config.coreConfig || Object.keys(config.coreConfig).length === 0)) {
|
if ((await fs.pathExists(coreConfigPath)) && (!config.coreConfig || Object.keys(config.coreConfig).length === 0)) {
|
||||||
try {
|
try {
|
||||||
const yaml = require('yaml');
|
const yaml = require('yaml');
|
||||||
|
|
@ -520,7 +492,7 @@ class Installer {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also check cache directory for custom modules (like quick update does)
|
// Also check cache directory for custom modules (like quick update does)
|
||||||
const cacheDir = path.join(bmadDir, '_config', 'custom');
|
const cacheDir = paths.customCacheDir;
|
||||||
if (await fs.pathExists(cacheDir)) {
|
if (await fs.pathExists(cacheDir)) {
|
||||||
const cachedModules = await fs.readdir(cacheDir, { withFileTypes: true });
|
const cachedModules = await fs.readdir(cacheDir, { withFileTypes: true });
|
||||||
|
|
||||||
|
|
@ -558,7 +530,7 @@ class Installer {
|
||||||
|
|
||||||
// If there are custom files, back them up temporarily
|
// If there are custom files, back them up temporarily
|
||||||
if (customFiles.length > 0) {
|
if (customFiles.length > 0) {
|
||||||
const tempBackupDir = path.join(projectDir, '_bmad-custom-backup-temp');
|
const tempBackupDir = path.join(projectRoot, '_bmad-custom-backup-temp');
|
||||||
await fs.ensureDir(tempBackupDir);
|
await fs.ensureDir(tempBackupDir);
|
||||||
|
|
||||||
spinner.start(`Backing up ${customFiles.length} custom files...`);
|
spinner.start(`Backing up ${customFiles.length} custom files...`);
|
||||||
|
|
@ -575,7 +547,7 @@ class Installer {
|
||||||
|
|
||||||
// For modified files, back them up to temp directory (will be restored as .bak files after install)
|
// For modified files, back them up to temp directory (will be restored as .bak files after install)
|
||||||
if (modifiedFiles.length > 0) {
|
if (modifiedFiles.length > 0) {
|
||||||
const tempModifiedBackupDir = path.join(projectDir, '_bmad-modified-backup-temp');
|
const tempModifiedBackupDir = path.join(projectRoot, '_bmad-modified-backup-temp');
|
||||||
await fs.ensureDir(tempModifiedBackupDir);
|
await fs.ensureDir(tempModifiedBackupDir);
|
||||||
|
|
||||||
spinner.start(`Backing up ${modifiedFiles.length} modified files...`);
|
spinner.start(`Backing up ${modifiedFiles.length} modified files...`);
|
||||||
|
|
@ -604,7 +576,7 @@ class Installer {
|
||||||
config._modifiedFiles = modifiedFiles;
|
config._modifiedFiles = modifiedFiles;
|
||||||
|
|
||||||
// Also check cache directory for custom modules (like quick update does)
|
// Also check cache directory for custom modules (like quick update does)
|
||||||
const cacheDir = path.join(bmadDir, '_config', 'custom');
|
const cacheDir = paths.customCacheDir;
|
||||||
if (await fs.pathExists(cacheDir)) {
|
if (await fs.pathExists(cacheDir)) {
|
||||||
const cachedModules = await fs.readdir(cacheDir, { withFileTypes: true });
|
const cachedModules = await fs.readdir(cacheDir, { withFileTypes: true });
|
||||||
|
|
||||||
|
|
@ -642,7 +614,7 @@ class Installer {
|
||||||
|
|
||||||
// Back up custom files
|
// Back up custom files
|
||||||
if (customFiles.length > 0) {
|
if (customFiles.length > 0) {
|
||||||
const tempBackupDir = path.join(projectDir, '_bmad-custom-backup-temp');
|
const tempBackupDir = path.join(projectRoot, '_bmad-custom-backup-temp');
|
||||||
await fs.ensureDir(tempBackupDir);
|
await fs.ensureDir(tempBackupDir);
|
||||||
|
|
||||||
spinner.start(`Backing up ${customFiles.length} custom files...`);
|
spinner.start(`Backing up ${customFiles.length} custom files...`);
|
||||||
|
|
@ -658,7 +630,7 @@ class Installer {
|
||||||
|
|
||||||
// Back up modified files
|
// Back up modified files
|
||||||
if (modifiedFiles.length > 0) {
|
if (modifiedFiles.length > 0) {
|
||||||
const tempModifiedBackupDir = path.join(projectDir, '_bmad-modified-backup-temp');
|
const tempModifiedBackupDir = path.join(projectRoot, '_bmad-modified-backup-temp');
|
||||||
await fs.ensureDir(tempModifiedBackupDir);
|
await fs.ensureDir(tempModifiedBackupDir);
|
||||||
|
|
||||||
spinner.start(`Backing up ${modifiedFiles.length} modified files...`);
|
spinner.start(`Backing up ${modifiedFiles.length} modified files...`);
|
||||||
|
|
@ -701,7 +673,7 @@ class Installer {
|
||||||
// Use config.ides if it's an array (even if empty), null means prompt
|
// Use config.ides if it's an array (even if empty), null means prompt
|
||||||
const preSelectedIdes = Array.isArray(config.ides) ? config.ides : null;
|
const preSelectedIdes = Array.isArray(config.ides) ? config.ides : null;
|
||||||
toolSelection = await this.collectToolConfigurations(
|
toolSelection = await this.collectToolConfigurations(
|
||||||
path.resolve(config.directory),
|
projectRoot,
|
||||||
config.modules,
|
config.modules,
|
||||||
config._isFullReinstall || false,
|
config._isFullReinstall || false,
|
||||||
config._previouslyConfiguredIdes || [],
|
config._previouslyConfiguredIdes || [],
|
||||||
|
|
@ -774,7 +746,7 @@ class Installer {
|
||||||
try {
|
try {
|
||||||
const handler = this.ideManager.handlers.get(ide);
|
const handler = this.ideManager.handlers.get(ide);
|
||||||
if (handler) {
|
if (handler) {
|
||||||
await handler.cleanup(projectDir);
|
await handler.cleanup(projectRoot);
|
||||||
}
|
}
|
||||||
await this.ideConfigManager.deleteIdeConfig(bmadDir, ide);
|
await this.ideConfigManager.deleteIdeConfig(bmadDir, ide);
|
||||||
await prompts.log.message(` Removed: ${ide}`);
|
await prompts.log.message(` Removed: ${ide}`);
|
||||||
|
|
@ -811,10 +783,6 @@ class Installer {
|
||||||
spinner.start('Preparing installation...');
|
spinner.start('Preparing installation...');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create bmad directory structure
|
|
||||||
spinner.message('Creating directory structure...');
|
|
||||||
await this.createDirectoryStructure(bmadDir);
|
|
||||||
|
|
||||||
// Cache custom modules if any
|
// Cache custom modules if any
|
||||||
if (customModulePaths && customModulePaths.size > 0) {
|
if (customModulePaths && customModulePaths.size > 0) {
|
||||||
spinner.message('Caching custom modules...');
|
spinner.message('Caching custom modules...');
|
||||||
|
|
@ -835,8 +803,6 @@ class Installer {
|
||||||
addResult('Custom modules cached', 'ok');
|
addResult('Custom modules cached', 'ok');
|
||||||
}
|
}
|
||||||
|
|
||||||
const projectRoot = getProjectRoot();
|
|
||||||
|
|
||||||
// Custom content is already handled in UI before module selection
|
// Custom content is already handled in UI before module selection
|
||||||
const finalCustomContent = config.customContent;
|
const finalCustomContent = config.customContent;
|
||||||
|
|
||||||
|
|
@ -867,7 +833,7 @@ class Installer {
|
||||||
// Add custom modules to the installation list
|
// Add custom modules to the installation list
|
||||||
const customHandler = new CustomHandler();
|
const customHandler = new CustomHandler();
|
||||||
for (const customFile of finalCustomContent.selectedFiles) {
|
for (const customFile of finalCustomContent.selectedFiles) {
|
||||||
const customInfo = await customHandler.getCustomInfo(customFile, projectDir);
|
const customInfo = await customHandler.getCustomInfo(customFile, projectRoot);
|
||||||
if (customInfo && customInfo.id) {
|
if (customInfo && customInfo.id) {
|
||||||
allModules.push(customInfo.id);
|
allModules.push(customInfo.id);
|
||||||
}
|
}
|
||||||
|
|
@ -932,7 +898,7 @@ class Installer {
|
||||||
bmadDir: bmadDir,
|
bmadDir: bmadDir,
|
||||||
});
|
});
|
||||||
|
|
||||||
taskResolution = await this.dependencyResolver.resolve(projectRoot, regularModulesForResolution, {
|
taskResolution = await this.dependencyResolver.resolve(srcDir, regularModulesForResolution, {
|
||||||
verbose: config.verbose,
|
verbose: config.verbose,
|
||||||
moduleManager: tempModuleManager,
|
moduleManager: tempModuleManager,
|
||||||
});
|
});
|
||||||
|
|
@ -982,7 +948,7 @@ class Installer {
|
||||||
if (!isCustomModule && finalCustomContent && finalCustomContent.selected && finalCustomContent.selectedFiles) {
|
if (!isCustomModule && finalCustomContent && finalCustomContent.selected && finalCustomContent.selectedFiles) {
|
||||||
const customHandler = new CustomHandler();
|
const customHandler = new CustomHandler();
|
||||||
for (const customFile of finalCustomContent.selectedFiles) {
|
for (const customFile of finalCustomContent.selectedFiles) {
|
||||||
const info = await customHandler.getCustomInfo(customFile, projectDir);
|
const info = await customHandler.getCustomInfo(customFile, projectRoot);
|
||||||
if (info && info.id === moduleName) {
|
if (info && info.id === moduleName) {
|
||||||
isCustomModule = true;
|
isCustomModule = true;
|
||||||
customInfo = info;
|
customInfo = info;
|
||||||
|
|
@ -1122,9 +1088,8 @@ class Installer {
|
||||||
addResult('Configurations', 'ok', 'generated');
|
addResult('Configurations', 'ok', 'generated');
|
||||||
|
|
||||||
// Pre-register manifest files
|
// Pre-register manifest files
|
||||||
const cfgDir = path.join(bmadDir, '_config');
|
this.installedFiles.add(paths.manifestFile());
|
||||||
this.installedFiles.add(path.join(cfgDir, 'manifest.yaml'));
|
this.installedFiles.add(paths.agentManifest());
|
||||||
this.installedFiles.add(path.join(cfgDir, 'agent-manifest.csv'));
|
|
||||||
|
|
||||||
// Generate CSV manifests for agents, skills AND ALL FILES with hashes
|
// Generate CSV manifests for agents, skills AND ALL FILES with hashes
|
||||||
// This must happen BEFORE mergeModuleHelpCatalogs because it depends on agent-manifest.csv
|
// This must happen BEFORE mergeModuleHelpCatalogs because it depends on agent-manifest.csv
|
||||||
|
|
@ -1215,7 +1180,7 @@ class Installer {
|
||||||
console.log = () => {};
|
console.log = () => {};
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const setupResult = await this.ideManager.setup(ide, projectDir, bmadDir, {
|
const setupResult = await this.ideManager.setup(ide, projectRoot, bmadDir, {
|
||||||
selectedModules: allModules || [],
|
selectedModules: allModules || [],
|
||||||
preCollectedConfig: ideConfigurations[ide] || null,
|
preCollectedConfig: ideConfigurations[ide] || null,
|
||||||
verbose: config.verbose,
|
verbose: config.verbose,
|
||||||
|
|
@ -1333,7 +1298,7 @@ class Installer {
|
||||||
path: bmadDir,
|
path: bmadDir,
|
||||||
modules: config.modules,
|
modules: config.modules,
|
||||||
ides: config.ides,
|
ides: config.ides,
|
||||||
projectDir: projectDir,
|
projectDir: projectRoot,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue