v4 detection cleanup
This commit is contained in:
parent
d4a94df29a
commit
2a8a4388a9
|
|
@ -1,6 +1,6 @@
|
||||||
# Document Project Workflow - Validation Checklist
|
# 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
|
- [ ] Scan level selection offered (quick/deep/exhaustive) for initial_scan and full_rescan modes
|
||||||
- [ ] Deep-dive mode automatically uses exhaustive scan (no choice given)
|
- [ ] Deep-dive mode automatically uses exhaustive scan (no choice given)
|
||||||
|
|
@ -223,7 +223,7 @@
|
||||||
|
|
||||||
All items in the following sections must be checked:
|
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
|
- ✓ Write-as-you-go Architecture
|
||||||
- ✓ Batching Strategy (if deep/exhaustive scan)
|
- ✓ Batching Strategy (if deep/exhaustive scan)
|
||||||
- ✓ Project Detection and Classification
|
- ✓ Project Detection and Classification
|
||||||
|
|
|
||||||
|
|
@ -203,107 +203,17 @@ class Detector {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Detect legacy BMAD v4 footprints (case-sensitive path checks)
|
* Detect legacy BMAD v4 .bmad-method folder
|
||||||
* V4 used _bmad-method as default folder name
|
|
||||||
* V6+ uses configurable folder names and ALWAYS has _config/manifest.yaml with installation.version
|
|
||||||
* @param {string} projectDir - Project directory to check
|
* @param {string} projectDir - Project directory to check
|
||||||
* @returns {{ hasLegacyV4: boolean, offenders: string[] }}
|
* @returns {{ hasLegacyV4: boolean, offenders: string[] }}
|
||||||
*/
|
*/
|
||||||
async detectLegacyV4(projectDir) {
|
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 = [];
|
const offenders = [];
|
||||||
|
|
||||||
// Strategy:
|
// Check for .bmad-method folder
|
||||||
// 1. First scan for ANY V6+ installation (_config/manifest.yaml)
|
const bmadMethodPath = path.join(projectDir, '.bmad-method');
|
||||||
// 2. If V6+ found → don't flag anything (user is already on V6+)
|
if (await fs.pathExists(bmadMethodPath)) {
|
||||||
// 3. If NO V6+ found → flag folders with "bmad" in name as potential V4 legacy
|
offenders.push(bmadMethodPath);
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return { hasLegacyV4: offenders.length > 0, offenders };
|
return { hasLegacyV4: offenders.length > 0, offenders };
|
||||||
|
|
|
||||||
|
|
@ -2152,90 +2152,59 @@ class Installer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle legacy BMAD v4 migration with automatic backup
|
* Handle legacy BMAD v4 detection with simple warning
|
||||||
* @param {string} projectDir - Project directory
|
* @param {string} _projectDir - Project directory (unused in simplified version)
|
||||||
* @param {Object} legacyV4 - Legacy V4 detection result with offenders array
|
* @param {Object} _legacyV4 - Legacy V4 detection result (unused in simplified version)
|
||||||
*/
|
*/
|
||||||
async handleLegacyV4Migration(projectDir, legacyV4) {
|
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
|
|
||||||
});
|
|
||||||
|
|
||||||
const inquirer = require('inquirer').default || require('inquirer');
|
const inquirer = require('inquirer').default || require('inquirer');
|
||||||
|
|
||||||
// Show warning for other offending paths FIRST
|
console.log('');
|
||||||
if (otherOffenders.length > 0) {
|
console.log(chalk.yellow.bold('⚠️ Legacy BMAD v4 detected'));
|
||||||
console.log(chalk.yellow('⚠️ Recommended cleanup:'));
|
console.log(chalk.yellow('─'.repeat(80)));
|
||||||
console.log(chalk.dim('It is recommended to remove the following items before proceeding:\n'));
|
console.log(chalk.yellow('Found .bmad-method folder from BMAD v4 installation.'));
|
||||||
for (const p of otherOffenders) console.log(chalk.dim(` - ${p}`));
|
console.log('');
|
||||||
|
|
||||||
console.log(chalk.cyan('\nCleanup commands you can copy/paste:'));
|
console.log(chalk.dim('Before continuing with installation, we recommend:'));
|
||||||
console.log(chalk.dim('macOS/Linux:'));
|
console.log(chalk.dim(' 1. Remove the .bmad-method folder, OR'));
|
||||||
for (const p of otherOffenders) console.log(chalk.dim(` rm -rf '${p}'`));
|
console.log(chalk.dim(' 2. Back it up by renaming it to another name (e.g., bmad-method-backup)'));
|
||||||
console.log(chalk.dim('Windows:'));
|
console.log('');
|
||||||
for (const p of otherOffenders) console.log(chalk.dim(` rmdir /S /Q "${p}"`));
|
|
||||||
|
|
||||||
const { cleanedUp } = await inquirer.prompt([
|
console.log(chalk.dim('If your v4 installation set up rules or commands, you should remove those as well.'));
|
||||||
{
|
console.log('');
|
||||||
type: 'confirm',
|
|
||||||
name: 'cleanedUp',
|
|
||||||
message: 'Have you completed the recommended cleanup? (You can proceed without it, but it is recommended)',
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (cleanedUp) {
|
|
||||||
console.log(chalk.green('✓ Cleanup acknowledged\n'));
|
|
||||||
} else {
|
|
||||||
console.log(chalk.yellow('⚠️ Proceeding without recommended cleanup\n'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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([
|
const { proceed } = await inquirer.prompt([
|
||||||
{
|
{
|
||||||
type: 'confirm',
|
type: 'list',
|
||||||
name: 'proceed',
|
name: 'proceed',
|
||||||
message: 'Proceed with backing up legacy v4 folders?',
|
message: 'What would you like to do?',
|
||||||
default: true,
|
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) {
|
if (proceed === 'exit') {
|
||||||
const backupDir = path.join(projectDir, 'v4-backup');
|
console.log('');
|
||||||
await fs.ensureDir(backupDir);
|
console.log(chalk.cyan('Please remove the .bmad-method folder and any v4 rules/commands,'));
|
||||||
|
console.log(chalk.cyan('then run the installer again.'));
|
||||||
for (const folder of bmadFolders) {
|
console.log('');
|
||||||
const folderName = path.basename(folder);
|
process.exit(0);
|
||||||
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('');
|
||||||
console.log(chalk.green(`✓ Moved ${folderName} to ${path.relative(projectDir, finalBackupPath)}`));
|
console.log(chalk.yellow('⚠️ Proceeding with installation despite legacy v4 folder'));
|
||||||
}
|
console.log('');
|
||||||
} else {
|
|
||||||
throw new Error('Installation cancelled by user');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue