diff --git a/tools/installer/core/installer.js b/tools/installer/core/installer.js index abd9ee9a3..a68193bc6 100644 --- a/tools/installer/core/installer.js +++ b/tools/installer/core/installer.js @@ -42,10 +42,15 @@ class Installer { const officialModules = await OfficialModules.build(config, paths); const existingInstall = await ExistingInstall.detect(paths.bmadDir); - await warnPreNativeSkillsLegacy({ - projectRoot: paths.projectRoot, - existingVersion: existingInstall.installed ? existingInstall.version : null, - }); + try { + await warnPreNativeSkillsLegacy({ + projectRoot: paths.projectRoot, + existingVersion: existingInstall.installed ? existingInstall.version : null, + }); + } catch (error) { + // Legacy-dir scan is informational; never let it abort install. + await prompts.log.warn(`Warning: Could not check for legacy BMAD entries: ${error.message}`); + } if (existingInstall.installed) { await this._removeDeselectedModules(existingInstall, config, paths); @@ -192,9 +197,15 @@ class Installer { // Pass the newly-selected list as remainingIdes so cleanupByList skips // target_dir wipes for IDEs whose directory is still owned by a peer // (e.g. removing 'cursor' while 'gemini' remains — both share .agents/skills). - await this.ideManager.cleanupByList(paths.projectRoot, toRemove, { + const results = await this.ideManager.cleanupByList(paths.projectRoot, toRemove, { remainingIdes: [...newlySelected], }); + + for (const result of results || []) { + if (result && result.success === false) { + await prompts.log.warn(`Warning: Failed to remove ${result.ide}: ${result.error || 'unknown error'}`); + } + } } /** diff --git a/tools/installer/core/legacy-warnings.js b/tools/installer/core/legacy-warnings.js index d73bbd9a1..e3098b82b 100644 --- a/tools/installer/core/legacy-warnings.js +++ b/tools/installer/core/legacy-warnings.js @@ -92,7 +92,7 @@ async function findStaleLegacyDirs(projectRoot) { const entries = await fs.readdir(resolved); const bmadEntries = entries.filter((e) => isBmadOwnedEntry(e, canonicalIds)); if (bmadEntries.length > 0) { - findings.push({ path: resolved, displayPath: legacyPath, count: bmadEntries.length }); + findings.push({ path: resolved, displayPath: legacyPath, count: bmadEntries.length, entries: bmadEntries }); } } catch { // Unreadable dir — skip @@ -127,7 +127,13 @@ async function warnPreNativeSkillsLegacy({ projectRoot, existingVersion } = {}) `Your AI tool may load these alongside the new skills, causing duplicates. Remove them manually:`, ); for (const finding of staleDirs) { - await prompts.log.message(` rm -rf "${finding.path}"/bmad* # ${finding.count} stale entr${finding.count === 1 ? 'y' : 'ies'}`); + // Print each entry by exact name. A `bmad*` glob would (a) miss + // custom-module skills the canonicalId scan now picks up, and + // (b) match bmad-os-* utility skills the user should keep. + const entries = finding.entries || []; + for (const entry of entries) { + await prompts.log.message(` rm -rf "${path.join(finding.path, entry)}"`); + } } } else if (versionTriggered) { await prompts.log.message(