Skip to content

Commit 2a8a438

Browse files
Brian MadisonBrian Madison
authored andcommitted
v4 detection cleanup
1 parent d4a94df commit 2a8a438

File tree

3 files changed

+52
-173
lines changed

3 files changed

+52
-173
lines changed

src/modules/bmm/workflows/document-project/checklist.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Document Project Workflow - Validation Checklist
22

3-
## Scan Level and Resumability (v1.2.0)
3+
## Scan Level and Resumability
44

55
- [ ] Scan level selection offered (quick/deep/exhaustive) for initial_scan and full_rescan modes
66
- [ ] Deep-dive mode automatically uses exhaustive scan (no choice given)
@@ -223,7 +223,7 @@
223223

224224
All items in the following sections must be checked:
225225

226-
- ✓ Scan Level and Resumability (v1.2.0)
226+
- ✓ Scan Level and Resumability
227227
- ✓ Write-as-you-go Architecture
228228
- ✓ Batching Strategy (if deep/exhaustive scan)
229229
- ✓ Project Detection and Classification

tools/cli/installers/lib/core/detector.js

Lines changed: 5 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -203,107 +203,17 @@ class Detector {
203203
}
204204

205205
/**
206-
* Detect legacy BMAD v4 footprints (case-sensitive path checks)
207-
* V4 used _bmad-method as default folder name
208-
* V6+ uses configurable folder names and ALWAYS has _config/manifest.yaml with installation.version
206+
* Detect legacy BMAD v4 .bmad-method folder
209207
* @param {string} projectDir - Project directory to check
210208
* @returns {{ hasLegacyV4: boolean, offenders: string[] }}
211209
*/
212210
async detectLegacyV4(projectDir) {
213-
// Helper: check if a directory is a V6+ installation
214-
const isV6Installation = async (dirPath) => {
215-
const manifestPath = path.join(dirPath, '_config', 'manifest.yaml');
216-
if (!(await fs.pathExists(manifestPath))) {
217-
return false;
218-
}
219-
try {
220-
const yaml = require('yaml');
221-
const manifestContent = await fs.readFile(manifestPath, 'utf8');
222-
const manifest = yaml.parse(manifestContent);
223-
// V6+ manifest has installation.version
224-
return manifest && manifest.installation && manifest.installation.version;
225-
} catch {
226-
return false;
227-
}
228-
};
229-
230211
const offenders = [];
231212

232-
// Strategy:
233-
// 1. First scan for ANY V6+ installation (_config/manifest.yaml)
234-
// 2. If V6+ found → don't flag anything (user is already on V6+)
235-
// 3. If NO V6+ found → flag folders with "bmad" in name as potential V4 legacy
236-
237-
let hasV6Installation = false;
238-
const potentialV4Folders = [];
239-
240-
try {
241-
const entries = await fs.readdir(projectDir, { withFileTypes: true });
242-
243-
for (const entry of entries) {
244-
if (entry.isDirectory()) {
245-
const name = entry.name;
246-
const fullPath = path.join(projectDir, entry.name);
247-
248-
// Check if directory is empty (skip empty leftover folders)
249-
const dirContents = await fs.readdir(fullPath);
250-
if (dirContents.length === 0) {
251-
continue; // Skip empty folders
252-
}
253-
254-
// Check if it's a V6+ installation by looking for _config/manifest.yaml
255-
// This works for ANY folder name (not just bmad-prefixed)
256-
const isV6 = await isV6Installation(fullPath);
257-
258-
if (isV6) {
259-
// Found a V6+ installation - user is already on V6+
260-
hasV6Installation = true;
261-
// Don't break - continue scanning to be thorough
262-
} else {
263-
// Not V6+, check if this is the exact V4 folder name "bmad-method"
264-
if (name === 'bmad-method') {
265-
// This is the V4 default folder - flag it as legacy
266-
potentialV4Folders.push(fullPath);
267-
}
268-
}
269-
}
270-
}
271-
} catch {
272-
// Ignore errors reading directory
273-
}
274-
275-
// Only flag V4 folders if NO V6+ installation was found
276-
if (!hasV6Installation && potentialV4Folders.length > 0) {
277-
offenders.push(...potentialV4Folders);
278-
}
279-
280-
// Check inside various IDE command folders for legacy bmad folders
281-
// V4 used folders like 'bmad-method' or custom names in IDE commands
282-
// V6+ uses 'bmad' in IDE commands (hardcoded in IDE handlers)
283-
// Legacy V4 IDE command folders won't have a corresponding V6+ installation
284-
const ideConfigFolders = ['.opencode', '.claude', '.crush', '.continue', '.cursor', '.windsurf', '.cline', '.roo-cline'];
285-
286-
for (const ideFolder of ideConfigFolders) {
287-
const commandsDirName = ideFolder === '.opencode' ? 'command' : 'commands';
288-
const commandsPath = path.join(projectDir, ideFolder, commandsDirName);
289-
if (await fs.pathExists(commandsPath)) {
290-
try {
291-
const commandEntries = await fs.readdir(commandsPath, { withFileTypes: true });
292-
for (const entry of commandEntries) {
293-
if (entry.isDirectory()) {
294-
const name = entry.name;
295-
// V4 used 'bmad-method' or similar in IDE commands folders
296-
// V6+ uses 'bmad' (hardcoded)
297-
// So anything that's NOT 'bmad' but starts with bmad/Bmad is likely V4
298-
if ((name.startsWith('bmad') || name.startsWith('Bmad') || name === 'BMad') && name !== 'bmad') {
299-
offenders.push(path.join(commandsPath, entry.name));
300-
}
301-
}
302-
}
303-
} catch {
304-
// Ignore errors reading commands directory
305-
}
306-
}
213+
// Check for .bmad-method folder
214+
const bmadMethodPath = path.join(projectDir, '.bmad-method');
215+
if (await fs.pathExists(bmadMethodPath)) {
216+
offenders.push(bmadMethodPath);
307217
}
308218

309219
return { hasLegacyV4: offenders.length > 0, offenders };

tools/cli/installers/lib/core/installer.js

Lines changed: 45 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -2152,90 +2152,59 @@ class Installer {
21522152
}
21532153

21542154
/**
2155-
* Handle legacy BMAD v4 migration with automatic backup
2156-
* @param {string} projectDir - Project directory
2157-
* @param {Object} legacyV4 - Legacy V4 detection result with offenders array
2155+
* Handle legacy BMAD v4 detection with simple warning
2156+
* @param {string} _projectDir - Project directory (unused in simplified version)
2157+
* @param {Object} _legacyV4 - Legacy V4 detection result (unused in simplified version)
21582158
*/
2159-
async handleLegacyV4Migration(projectDir, legacyV4) {
2160-
console.log(chalk.yellow.bold('\n⚠️ Legacy BMAD v4 detected'));
2161-
console.log(chalk.dim('The installer found legacy artefacts in your project.\n'));
2162-
2163-
// Separate _bmad* folders (auto-backup) from other offending paths (manual cleanup)
2164-
const bmadFolders = legacyV4.offenders.filter((p) => {
2165-
const name = path.basename(p);
2166-
return name.startsWith('_bmad'); // Only dot-prefixed folders get auto-backed up
2167-
});
2168-
const otherOffenders = legacyV4.offenders.filter((p) => {
2169-
const name = path.basename(p);
2170-
return !name.startsWith('_bmad'); // Everything else is manual cleanup
2171-
});
2172-
2159+
async handleLegacyV4Migration(_projectDir, _legacyV4) {
21732160
const inquirer = require('inquirer').default || require('inquirer');
21742161

2175-
// Show warning for other offending paths FIRST
2176-
if (otherOffenders.length > 0) {
2177-
console.log(chalk.yellow('⚠️ Recommended cleanup:'));
2178-
console.log(chalk.dim('It is recommended to remove the following items before proceeding:\n'));
2179-
for (const p of otherOffenders) console.log(chalk.dim(` - ${p}`));
2180-
2181-
console.log(chalk.cyan('\nCleanup commands you can copy/paste:'));
2182-
console.log(chalk.dim('macOS/Linux:'));
2183-
for (const p of otherOffenders) console.log(chalk.dim(` rm -rf '${p}'`));
2184-
console.log(chalk.dim('Windows:'));
2185-
for (const p of otherOffenders) console.log(chalk.dim(` rmdir /S /Q "${p}"`));
2186-
2187-
const { cleanedUp } = await inquirer.prompt([
2188-
{
2189-
type: 'confirm',
2190-
name: 'cleanedUp',
2191-
message: 'Have you completed the recommended cleanup? (You can proceed without it, but it is recommended)',
2192-
default: false,
2193-
},
2194-
]);
2195-
2196-
if (cleanedUp) {
2197-
console.log(chalk.green('✓ Cleanup acknowledged\n'));
2198-
} else {
2199-
console.log(chalk.yellow('⚠️ Proceeding without recommended cleanup\n'));
2200-
}
2201-
}
2202-
2203-
// Handle _bmad* folders with automatic backup
2204-
if (bmadFolders.length > 0) {
2205-
console.log(chalk.cyan('The following legacy folders will be moved to v4-backup:'));
2206-
for (const p of bmadFolders) console.log(chalk.dim(` - ${p}`));
2207-
2208-
const { proceed } = await inquirer.prompt([
2209-
{
2210-
type: 'confirm',
2211-
name: 'proceed',
2212-
message: 'Proceed with backing up legacy v4 folders?',
2213-
default: true,
2214-
},
2215-
]);
2162+
console.log('');
2163+
console.log(chalk.yellow.bold('⚠️ Legacy BMAD v4 detected'));
2164+
console.log(chalk.yellow('─'.repeat(80)));
2165+
console.log(chalk.yellow('Found .bmad-method folder from BMAD v4 installation.'));
2166+
console.log('');
22162167

2217-
if (proceed) {
2218-
const backupDir = path.join(projectDir, 'v4-backup');
2219-
await fs.ensureDir(backupDir);
2168+
console.log(chalk.dim('Before continuing with installation, we recommend:'));
2169+
console.log(chalk.dim(' 1. Remove the .bmad-method folder, OR'));
2170+
console.log(chalk.dim(' 2. Back it up by renaming it to another name (e.g., bmad-method-backup)'));
2171+
console.log('');
22202172

2221-
for (const folder of bmadFolders) {
2222-
const folderName = path.basename(folder);
2223-
const backupPath = path.join(backupDir, folderName);
2173+
console.log(chalk.dim('If your v4 installation set up rules or commands, you should remove those as well.'));
2174+
console.log('');
22242175

2225-
// If backup already exists, add timestamp
2226-
let finalBackupPath = backupPath;
2227-
if (await fs.pathExists(backupPath)) {
2228-
const timestamp = new Date().toISOString().replaceAll(/[:.]/g, '-').split('T')[0];
2229-
finalBackupPath = path.join(backupDir, `${folderName}-${timestamp}`);
2230-
}
2176+
const { proceed } = await inquirer.prompt([
2177+
{
2178+
type: 'list',
2179+
name: 'proceed',
2180+
message: 'What would you like to do?',
2181+
choices: [
2182+
{
2183+
name: 'Exit and clean up manually (recommended)',
2184+
value: 'exit',
2185+
short: 'Exit installation',
2186+
},
2187+
{
2188+
name: 'Continue with installation anyway',
2189+
value: 'continue',
2190+
short: 'Continue',
2191+
},
2192+
],
2193+
default: 'exit',
2194+
},
2195+
]);
22312196

2232-
await fs.move(folder, finalBackupPath, { overwrite: false });
2233-
console.log(chalk.green(`✓ Moved ${folderName} to ${path.relative(projectDir, finalBackupPath)}`));
2234-
}
2235-
} else {
2236-
throw new Error('Installation cancelled by user');
2237-
}
2197+
if (proceed === 'exit') {
2198+
console.log('');
2199+
console.log(chalk.cyan('Please remove the .bmad-method folder and any v4 rules/commands,'));
2200+
console.log(chalk.cyan('then run the installer again.'));
2201+
console.log('');
2202+
process.exit(0);
22382203
}
2204+
2205+
console.log('');
2206+
console.log(chalk.yellow('⚠️ Proceeding with installation despite legacy v4 folder'));
2207+
console.log('');
22392208
}
22402209

22412210
/**

0 commit comments

Comments
 (0)