mirror of
https://github.com/bmadcode/BMAD-METHOD.git
synced 2025-12-29 16:14:59 +00:00
v4 detection cleanup
This commit is contained in:
parent
d4a94df29a
commit
2a8a4388a9
@ -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
|
||||
|
||||
@ -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 };
|
||||
|
||||
@ -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('');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user