From 1735c08050e29597160e4de1e2b55098f453d6e3 Mon Sep 17 00:00:00 2001 From: Alex Verkhovsky Date: Fri, 27 Mar 2026 06:45:30 -0600 Subject: [PATCH] fix(installer): guard ExistingInstall.version and surface module.yaml errors Guard ExistingInstall.version access with .installed check in uninstall.js, ui.js, and installer.js to prevent throwing on empty/partial _bmad dirs. Surface invalid module.yaml parse errors as warnings instead of silently returning empty results. --- tools/installer/commands/uninstall.js | 2 +- tools/installer/core/installer.js | 2 +- tools/installer/modules/official-modules.js | 5 +++-- tools/installer/ui.js | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/tools/installer/commands/uninstall.js b/tools/installer/commands/uninstall.js index 643af1872..d0e168a15 100644 --- a/tools/installer/commands/uninstall.js +++ b/tools/installer/commands/uninstall.js @@ -62,7 +62,7 @@ module.exports = { } const existingInstall = await installer.getStatus(projectDir); - const version = existingInstall.version || 'unknown'; + const version = existingInstall.installed ? existingInstall.version : 'unknown'; const modules = existingInstall.moduleIds.join(', '); const ides = existingInstall.ides.join(', '); diff --git a/tools/installer/core/installer.js b/tools/installer/core/installer.js index 6de412247..111c88b54 100644 --- a/tools/installer/core/installer.js +++ b/tools/installer/core/installer.js @@ -1360,7 +1360,7 @@ class Installer { removed.modules = await this.uninstallModules(projectDir); } - return { success: true, removed, version: existingInstall.version }; + return { success: true, removed, version: existingInstall.installed ? existingInstall.version : null }; } /** diff --git a/tools/installer/modules/official-modules.js b/tools/installer/modules/official-modules.js index df8733fa5..5b67fc4dd 100644 --- a/tools/installer/modules/official-modules.js +++ b/tools/installer/modules/official-modules.js @@ -484,8 +484,9 @@ class OfficialModules { try { const yamlContent = await fs.readFile(moduleYamlPath, 'utf8'); moduleYaml = yaml.parse(yamlContent); - } catch { - return emptyResult; // Invalid YAML, skip + } catch (error) { + await prompts.log.warn(`Invalid module.yaml for ${moduleName}: ${error.message}`); + return emptyResult; } if (!moduleYaml || !moduleYaml.directories) { diff --git a/tools/installer/ui.js b/tools/installer/ui.js index 5e21def39..03d38e4da 100644 --- a/tools/installer/ui.js +++ b/tools/installer/ui.js @@ -72,7 +72,7 @@ class UI { const { existingInstall, bmadDir } = await this.getExistingInstallation(confirmedDirectory); const packageJsonPath = path.join(__dirname, '../../package.json'); const currentVersion = require(packageJsonPath).version; - const installedVersion = existingInstall.version || 'unknown'; + const installedVersion = existingInstall.installed ? existingInstall.version || 'unknown' : 'unknown'; // Build menu choices dynamically const choices = [];