From 2a8a4388a9bfd755299002a6619295179134325c Mon Sep 17 00:00:00 2001 From: Brian Madison Date: Sat, 27 Dec 2025 20:31:12 +0800 Subject: [PATCH] v4 detection cleanup --- .../workflows/document-project/checklist.md | 4 +- tools/cli/installers/lib/core/detector.js | 100 +-------------- tools/cli/installers/lib/core/installer.js | 121 +++++++----------- 3 files changed, 52 insertions(+), 173 deletions(-) diff --git a/src/modules/bmm/workflows/document-project/checklist.md b/src/modules/bmm/workflows/document-project/checklist.md index 636312b4..7b67d1e5 100644 --- a/src/modules/bmm/workflows/document-project/checklist.md +++ b/src/modules/bmm/workflows/document-project/checklist.md @@ -1,6 +1,6 @@ # Document Project Workflow - Validation Checklist -## Scan Level and Resumability (v1.2.0) +## Scan Level and Resumability - [ ] Scan level selection offered (quick/deep/exhaustive) for initial_scan and full_rescan modes - [ ] Deep-dive mode automatically uses exhaustive scan (no choice given) @@ -223,7 +223,7 @@ All items in the following sections must be checked: -- ✓ Scan Level and Resumability (v1.2.0) +- ✓ Scan Level and Resumability - ✓ Write-as-you-go Architecture - ✓ Batching Strategy (if deep/exhaustive scan) - ✓ Project Detection and Classification diff --git a/tools/cli/installers/lib/core/detector.js b/tools/cli/installers/lib/core/detector.js index a50c56e4..9bb73658 100644 --- a/tools/cli/installers/lib/core/detector.js +++ b/tools/cli/installers/lib/core/detector.js @@ -203,107 +203,17 @@ class Detector { } /** - * Detect legacy BMAD v4 footprints (case-sensitive path checks) - * V4 used _bmad-method as default folder name - * V6+ uses configurable folder names and ALWAYS has _config/manifest.yaml with installation.version + * Detect legacy BMAD v4 .bmad-method folder * @param {string} projectDir - Project directory to check * @returns {{ hasLegacyV4: boolean, offenders: string[] }} */ async detectLegacyV4(projectDir) { - // Helper: check if a directory is a V6+ installation - const isV6Installation = async (dirPath) => { - const manifestPath = path.join(dirPath, '_config', 'manifest.yaml'); - if (!(await fs.pathExists(manifestPath))) { - return false; - } - try { - const yaml = require('yaml'); - const manifestContent = await fs.readFile(manifestPath, 'utf8'); - const manifest = yaml.parse(manifestContent); - // V6+ manifest has installation.version - return manifest && manifest.installation && manifest.installation.version; - } catch { - return false; - } - }; - const offenders = []; - // Strategy: - // 1. First scan for ANY V6+ installation (_config/manifest.yaml) - // 2. If V6+ found → don't flag anything (user is already on V6+) - // 3. If NO V6+ found → flag folders with "bmad" in name as potential V4 legacy - - let hasV6Installation = false; - const potentialV4Folders = []; - - try { - const entries = await fs.readdir(projectDir, { withFileTypes: true }); - - for (const entry of entries) { - if (entry.isDirectory()) { - const name = entry.name; - const fullPath = path.join(projectDir, entry.name); - - // Check if directory is empty (skip empty leftover folders) - const dirContents = await fs.readdir(fullPath); - if (dirContents.length === 0) { - continue; // Skip empty folders - } - - // Check if it's a V6+ installation by looking for _config/manifest.yaml - // This works for ANY folder name (not just bmad-prefixed) - const isV6 = await isV6Installation(fullPath); - - if (isV6) { - // Found a V6+ installation - user is already on V6+ - hasV6Installation = true; - // Don't break - continue scanning to be thorough - } else { - // Not V6+, check if this is the exact V4 folder name "bmad-method" - if (name === 'bmad-method') { - // This is the V4 default folder - flag it as legacy - potentialV4Folders.push(fullPath); - } - } - } - } - } catch { - // Ignore errors reading directory - } - - // Only flag V4 folders if NO V6+ installation was found - if (!hasV6Installation && potentialV4Folders.length > 0) { - offenders.push(...potentialV4Folders); - } - - // Check inside various IDE command folders for legacy bmad folders - // V4 used folders like 'bmad-method' or custom names in IDE commands - // V6+ uses 'bmad' in IDE commands (hardcoded in IDE handlers) - // Legacy V4 IDE command folders won't have a corresponding V6+ installation - const ideConfigFolders = ['.opencode', '.claude', '.crush', '.continue', '.cursor', '.windsurf', '.cline', '.roo-cline']; - - for (const ideFolder of ideConfigFolders) { - const commandsDirName = ideFolder === '.opencode' ? 'command' : 'commands'; - const commandsPath = path.join(projectDir, ideFolder, commandsDirName); - if (await fs.pathExists(commandsPath)) { - try { - const commandEntries = await fs.readdir(commandsPath, { withFileTypes: true }); - for (const entry of commandEntries) { - if (entry.isDirectory()) { - const name = entry.name; - // V4 used 'bmad-method' or similar in IDE commands folders - // V6+ uses 'bmad' (hardcoded) - // So anything that's NOT 'bmad' but starts with bmad/Bmad is likely V4 - if ((name.startsWith('bmad') || name.startsWith('Bmad') || name === 'BMad') && name !== 'bmad') { - offenders.push(path.join(commandsPath, entry.name)); - } - } - } - } catch { - // Ignore errors reading commands directory - } - } + // Check for .bmad-method folder + const bmadMethodPath = path.join(projectDir, '.bmad-method'); + if (await fs.pathExists(bmadMethodPath)) { + offenders.push(bmadMethodPath); } return { hasLegacyV4: offenders.length > 0, offenders }; diff --git a/tools/cli/installers/lib/core/installer.js b/tools/cli/installers/lib/core/installer.js index bc85d2f2..816dbbbc 100644 --- a/tools/cli/installers/lib/core/installer.js +++ b/tools/cli/installers/lib/core/installer.js @@ -2152,90 +2152,59 @@ class Installer { } /** - * Handle legacy BMAD v4 migration with automatic backup - * @param {string} projectDir - Project directory - * @param {Object} legacyV4 - Legacy V4 detection result with offenders array + * Handle legacy BMAD v4 detection with simple warning + * @param {string} _projectDir - Project directory (unused in simplified version) + * @param {Object} _legacyV4 - Legacy V4 detection result (unused in simplified version) */ - async handleLegacyV4Migration(projectDir, legacyV4) { - console.log(chalk.yellow.bold('\n⚠️ Legacy BMAD v4 detected')); - console.log(chalk.dim('The installer found legacy artefacts in your project.\n')); - - // Separate _bmad* folders (auto-backup) from other offending paths (manual cleanup) - const bmadFolders = legacyV4.offenders.filter((p) => { - const name = path.basename(p); - return name.startsWith('_bmad'); // Only dot-prefixed folders get auto-backed up - }); - const otherOffenders = legacyV4.offenders.filter((p) => { - const name = path.basename(p); - return !name.startsWith('_bmad'); // Everything else is manual cleanup - }); - + async handleLegacyV4Migration(_projectDir, _legacyV4) { const inquirer = require('inquirer').default || require('inquirer'); - // Show warning for other offending paths FIRST - if (otherOffenders.length > 0) { - console.log(chalk.yellow('⚠️ Recommended cleanup:')); - console.log(chalk.dim('It is recommended to remove the following items before proceeding:\n')); - for (const p of otherOffenders) console.log(chalk.dim(` - ${p}`)); + console.log(''); + console.log(chalk.yellow.bold('⚠️ Legacy BMAD v4 detected')); + console.log(chalk.yellow('─'.repeat(80))); + console.log(chalk.yellow('Found .bmad-method folder from BMAD v4 installation.')); + console.log(''); - console.log(chalk.cyan('\nCleanup commands you can copy/paste:')); - console.log(chalk.dim('macOS/Linux:')); - for (const p of otherOffenders) console.log(chalk.dim(` rm -rf '${p}'`)); - console.log(chalk.dim('Windows:')); - for (const p of otherOffenders) console.log(chalk.dim(` rmdir /S /Q "${p}"`)); + console.log(chalk.dim('Before continuing with installation, we recommend:')); + console.log(chalk.dim(' 1. Remove the .bmad-method folder, OR')); + console.log(chalk.dim(' 2. Back it up by renaming it to another name (e.g., bmad-method-backup)')); + console.log(''); - const { cleanedUp } = await inquirer.prompt([ - { - type: 'confirm', - name: 'cleanedUp', - message: 'Have you completed the recommended cleanup? (You can proceed without it, but it is recommended)', - default: false, - }, - ]); + console.log(chalk.dim('If your v4 installation set up rules or commands, you should remove those as well.')); + console.log(''); - if (cleanedUp) { - console.log(chalk.green('✓ Cleanup acknowledged\n')); - } else { - console.log(chalk.yellow('⚠️ Proceeding without recommended cleanup\n')); - } + const { proceed } = await inquirer.prompt([ + { + type: 'list', + name: 'proceed', + message: 'What would you like to do?', + choices: [ + { + name: 'Exit and clean up manually (recommended)', + value: 'exit', + short: 'Exit installation', + }, + { + name: 'Continue with installation anyway', + value: 'continue', + short: 'Continue', + }, + ], + default: 'exit', + }, + ]); + + if (proceed === 'exit') { + console.log(''); + console.log(chalk.cyan('Please remove the .bmad-method folder and any v4 rules/commands,')); + console.log(chalk.cyan('then run the installer again.')); + console.log(''); + process.exit(0); } - // Handle _bmad* folders with automatic backup - if (bmadFolders.length > 0) { - console.log(chalk.cyan('The following legacy folders will be moved to v4-backup:')); - for (const p of bmadFolders) console.log(chalk.dim(` - ${p}`)); - - const { proceed } = await inquirer.prompt([ - { - type: 'confirm', - name: 'proceed', - message: 'Proceed with backing up legacy v4 folders?', - default: true, - }, - ]); - - if (proceed) { - const backupDir = path.join(projectDir, 'v4-backup'); - await fs.ensureDir(backupDir); - - for (const folder of bmadFolders) { - const folderName = path.basename(folder); - const backupPath = path.join(backupDir, folderName); - - // If backup already exists, add timestamp - let finalBackupPath = backupPath; - if (await fs.pathExists(backupPath)) { - const timestamp = new Date().toISOString().replaceAll(/[:.]/g, '-').split('T')[0]; - finalBackupPath = path.join(backupDir, `${folderName}-${timestamp}`); - } - - await fs.move(folder, finalBackupPath, { overwrite: false }); - console.log(chalk.green(`✓ Moved ${folderName} to ${path.relative(projectDir, finalBackupPath)}`)); - } - } else { - throw new Error('Installation cancelled by user'); - } - } + console.log(''); + console.log(chalk.yellow('⚠️ Proceeding with installation despite legacy v4 folder')); + console.log(''); } /**