diff --git a/tools/cli/installers/lib/core/installer.js b/tools/cli/installers/lib/core/installer.js index 6df7b66a..40daa81c 100644 --- a/tools/cli/installers/lib/core/installer.js +++ b/tools/cli/installers/lib/core/installer.js @@ -620,12 +620,88 @@ class Installer { } } + /** + * Find BMAD installation by searching up the directory tree + * Searches for both modern (bmad/) and legacy (.bmad-core, .bmad-method, .bmm, .cis) installations + * Prefers modern installation if multiple found + * @param {string} startPath - Starting directory to search from + * @returns {string|null} Path to BMAD directory, or null if not found + */ + async findInstallation(startPath) { + const resolvedPath = path.resolve(startPath); + const root = path.parse(resolvedPath).root; + let currentPath = resolvedPath; + + // Legacy folder names to check + const legacyFolders = ['.bmad-core', '.bmad-method', '.bmm', '.cis']; + + // Search up the directory tree + while (currentPath !== root) { + // First check for modern bmad/ folder + const modernPath = path.join(currentPath, 'bmad'); + if (await fs.pathExists(modernPath)) { + // Verify it's a valid BMAD installation + const status = await this.detector.detect(modernPath); + if (status.installed || status.hasCore) { + return modernPath; + } + } + + // Then check for legacy folders + for (const legacyFolder of legacyFolders) { + const legacyPath = path.join(currentPath, legacyFolder); + if (await fs.pathExists(legacyPath)) { + // Verify it's a valid BMAD installation + const status = await this.detector.detect(legacyPath); + if (status.installed || status.hasCore) { + return legacyPath; + } + } + } + + // Move up one directory + const parentPath = path.dirname(currentPath); + if (parentPath === currentPath) { + // Reached filesystem root + break; + } + currentPath = parentPath; + } + + // Not found + return null; + } + /** * Get installation status + * Searches directory tree if installation not found at specified location */ async getStatus(directory) { - const bmadDir = path.join(path.resolve(directory), 'bmad'); - return await this.detector.detect(bmadDir); + const resolvedDir = path.resolve(directory); + let bmadDir = path.join(resolvedDir, 'bmad'); + + // First check the exact path + let status = await this.detector.detect(bmadDir); + if (status.installed || status.hasCore) { + return status; + } + + // If not found at exact location, search the directory tree + const foundPath = await this.findInstallation(resolvedDir); + if (foundPath) { + return await this.detector.detect(foundPath); + } + + // Not found anywhere - return not installed + return { + installed: false, + path: bmadDir, + version: null, + hasCore: false, + modules: [], + ides: [], + manifest: null, + }; } /**