diff --git a/tools/installer/commands/uninstall.js b/tools/installer/commands/uninstall.js index 19b2447ad..dc2055027 100644 --- a/tools/installer/commands/uninstall.js +++ b/tools/installer/commands/uninstall.js @@ -140,14 +140,18 @@ module.exports = { await installer.uninstallModules(projectDir); s.stop('Modules & data removed'); - // Remove BMAD badge from README - const badge = require('../core/badge'); - const readmePath = await badge.findReadme(projectDir); - if (readmePath) { - const content = await fs.readFile(readmePath, 'utf8'); - if (badge.hasBadge(content)) { - await fs.writeFile(readmePath, badge.removeBadge(content), 'utf8'); + // Remove BMAD badge from README (best-effort) + try { + const badge = require('../core/badge'); + const readmePath = await badge.findReadme(projectDir); + if (readmePath) { + const content = await fs.readFile(readmePath, 'utf8'); + if (badge.hasBadge(content)) { + await fs.writeFile(readmePath, badge.removeBadge(content), 'utf8'); + } } + } catch (error) { + await prompts.log.warn(`Badge cleanup skipped: ${error.message}`); } } diff --git a/tools/installer/core/badge.js b/tools/installer/core/badge.js index 0a9b250f5..7c1274779 100644 --- a/tools/installer/core/badge.js +++ b/tools/installer/core/badge.js @@ -3,7 +3,10 @@ const { execSync } = require('node:child_process'); const fs = require('../fs-native'); const BADGE_URL = 'https://bmad-badge.vercel.app'; -const BADGE_PATTERN = /\[!\[BMAD\]\(https:\/\/bmad-badge\.vercel\.app\/[^\)]+\)\]\(https:\/\/github\.com\/bmad-code-org\/BMAD-METHOD\)/; +const escapedBadgeUrl = BADGE_URL.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +const BADGE_PATTERN = new RegExp( + `\\[!\\[BMAD\\]\\(${escapedBadgeUrl}/[^)]+\\)\\]\\(https://github\\.com/bmad-code-org/BMAD-METHOD\\)`, +); const README_NAMES = ['README.md', 'readme.md', 'README', 'readme']; function resolveGitRemote(projectDir) { @@ -15,7 +18,7 @@ function resolveGitRemote(projectDir) { }).trim(); // https://github.com/owner/repo.git - const httpsMatch = raw.match(/github\.com[:/]([^/]+)\/([^/.]+)/); + const httpsMatch = raw.match(/github\.com[:/]([^/]+)\/([^/]+?)(?:\.git)?\/?$/i); if (httpsMatch) { return { owner: httpsMatch[1], repo: httpsMatch[2] }; } diff --git a/tools/installer/core/installer.js b/tools/installer/core/installer.js index 580ecb57d..6090c2282 100644 --- a/tools/installer/core/installer.js +++ b/tools/installer/core/installer.js @@ -105,7 +105,11 @@ class Installer { // Inject BMAD badge into README if applicable if (!config.noBadge) { - await this._injectBadgeIfNeeded(paths.projectRoot, addResult); + try { + await this._injectBadgeIfNeeded(paths.projectRoot, addResult); + } catch (error) { + addResult('Badge', 'warn', `skipped: ${error.message}`); + } } // Render consolidated summary @@ -1337,6 +1341,7 @@ class Installer { directory: projectDir, modules: modulesToUpdate, ides: configuredIdes, + noBadge: config.noBadge, coreConfig: quickModules.collectedConfig.core, moduleConfigs: quickModules.collectedConfig, // Forward `--set` overrides so the post-install patch step