Update WDS installer to use _bmad/wds/ standard path

Change default installation path from _wds/ to _bmad/wds/ to follow
BMAD module conventions. Add detection logic for existing installations
and migration prompt for legacy _wds/ projects.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Mårten Angner 2026-03-01 09:16:54 +01:00
parent 19e82cd3cb
commit 8ba623f048
5 changed files with 80 additions and 6 deletions

View File

@ -15,7 +15,7 @@ class BaseIdeSetup {
this.configDir = null; // Override in subclasses (e.g., '.windsurf/workflows/wds')
this.configFile = null; // Override in subclasses when detection is file-based
this.detectionPaths = []; // Additional paths that indicate the IDE is configured
this.wdsFolderName = '_wds'; // Default, can be overridden
this.wdsFolderName = '_bmad/wds'; // Default, can be overridden
}
/**

View File

@ -10,7 +10,7 @@ class IdeManager {
constructor() {
this.handlers = new Map();
this.loadHandlers();
this.wdsFolderName = '_wds'; // Default, can be overridden
this.wdsFolderName = '_bmad/wds'; // Default, can be overridden
}
/**

View File

@ -346,7 +346,7 @@ function rewritePaths(content, wdsFolder) {
// --- Main Compilation ---
function compileAgentFile(yamlPath, options = {}) {
const wdsFolder = options.wdsFolder || '_wds';
const wdsFolder = options.wdsFolder || '_bmad/wds';
const rawContent = fs.readFileSync(yamlPath, 'utf8');
// Rewrite paths before parsing

View File

@ -26,8 +26,28 @@ class Installer {
async install(config) {
const { projectDir, wdsFolder, root_folder } = config;
const wdsDir = path.join(projectDir, wdsFolder);
const detection = config._detection || { type: 'fresh' };
// Check if already installed
// Handle legacy _wds/ → _bmad/wds/ migration
if (detection.type === 'legacy' && wdsFolder !== '_wds') {
const legacyDir = path.join(projectDir, '_wds');
const legacyConfigPath = path.join(legacyDir, 'config.yaml');
// Save config from legacy location
if (await fs.pathExists(legacyConfigPath)) {
let savedConfig = await fs.readFile(legacyConfigPath, 'utf8');
// Update wds_folder in saved config to new path
savedConfig = savedConfig.replace(/wds_folder:.*/, `wds_folder: ${wdsFolder}`);
config._savedConfigYaml = savedConfig;
}
const migrateSpinner = ora(`Migrating _wds/ → ${wdsFolder}/...`).start();
await fs.ensureDir(path.dirname(wdsDir));
await fs.remove(legacyDir);
migrateSpinner.succeed(`Legacy _wds/ removed — installing fresh at ${wdsFolder}/`);
}
// Check if already installed at target path
if (await fs.pathExists(wdsDir)) {
console.log(chalk.yellow(`\n ${wdsFolder}/ already exists.`));
const { action } = await inquirer.prompt([
@ -68,6 +88,9 @@ class Installer {
}
}
// Ensure parent directory exists (for _bmad/wds/)
await fs.ensureDir(path.dirname(wdsDir));
console.log('');
// Step 1: Copy source files

View File

@ -6,6 +6,10 @@ const chalk = require('chalk');
const figlet = require('figlet');
const inquirer = require('inquirer').default || require('inquirer');
const path = require('node:path');
const fs = require('fs-extra');
const WDS_FOLDER = '_bmad/wds';
const LEGACY_WDS_FOLDER = '_wds';
class UI {
/**
@ -22,6 +26,26 @@ class UI {
console.log(chalk.dim(' Strategic design methodology for AI-powered workflows\n'));
}
/**
* Detect existing WDS installation and determine folder path
*/
async detectInstallation(projectDir) {
const hasBmadWds = await fs.pathExists(path.join(projectDir, WDS_FOLDER));
const hasLegacyWds = await fs.pathExists(path.join(projectDir, LEGACY_WDS_FOLDER));
const hasBmadDir = await fs.pathExists(path.join(projectDir, '_bmad'));
if (hasBmadWds) {
return { type: 'bmad', folder: WDS_FOLDER };
}
if (hasLegacyWds) {
return { type: 'legacy', folder: LEGACY_WDS_FOLDER };
}
if (hasBmadDir) {
return { type: 'bmad-ready', folder: WDS_FOLDER };
}
return { type: 'fresh', folder: WDS_FOLDER };
}
/**
* Run the full prompt flow and return config
*/
@ -30,9 +54,35 @@ class UI {
const projectDir = process.cwd();
const defaultProjectName = path.basename(projectDir);
const detection = await this.detectInstallation(projectDir);
console.log(chalk.white(` Target: ${chalk.cyan(projectDir)}`));
console.log(chalk.dim(` Agents and workflows will be installed in ${chalk.white('_wds/')}\n`));
// Handle legacy _wds/ detection
let wdsFolder = detection.folder;
if (detection.type === 'legacy') {
console.log(chalk.yellow(`\n Found legacy installation at ${chalk.white(LEGACY_WDS_FOLDER + '/')}`));
console.log(chalk.dim(` BMAD standard path is ${chalk.white(WDS_FOLDER + '/')}\n`));
const { migrationChoice } = await inquirer.prompt([
{
type: 'list',
name: 'migrationChoice',
message: 'How would you like to proceed?',
choices: [
{ name: `Migrate to ${WDS_FOLDER}/ (recommended)`, value: 'migrate' },
{ name: `Keep at ${LEGACY_WDS_FOLDER}/ (legacy)`, value: 'keep' },
],
},
]);
if (migrationChoice === 'keep') {
wdsFolder = LEGACY_WDS_FOLDER;
}
// 'migrate' keeps the default WDS_FOLDER — installer.js handles the actual move
} else {
console.log(chalk.dim(` Agents and workflows will be installed in ${chalk.white(wdsFolder + '/')}\n`));
}
// 5-question installer
const answers = await inquirer.prompt([
@ -102,7 +152,8 @@ class UI {
return {
projectDir,
...answers,
wdsFolder: '_wds',
wdsFolder,
_detection: detection,
cancelled: false,
};
}