Installer: ask update/migrate first, skip config questions on update
Restructure the prompt flow so the update/fresh/migrate decision comes before config questions. On update, config.yaml is preserved so asking project name, output folder, IDEs etc. is unnecessary. Extract ides and root_folder from saved config for IDE setup and folder creation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
8ba623f048
commit
ce4453c950
|
|
@ -27,6 +27,7 @@ class Installer {
|
||||||
const { projectDir, wdsFolder, root_folder } = config;
|
const { projectDir, wdsFolder, root_folder } = config;
|
||||||
const wdsDir = path.join(projectDir, wdsFolder);
|
const wdsDir = path.join(projectDir, wdsFolder);
|
||||||
const detection = config._detection || { type: 'fresh' };
|
const detection = config._detection || { type: 'fresh' };
|
||||||
|
const action = config._action || 'fresh';
|
||||||
|
|
||||||
// Handle legacy _wds/ → _bmad/wds/ migration
|
// Handle legacy _wds/ → _bmad/wds/ migration
|
||||||
if (detection.type === 'legacy' && wdsFolder !== '_wds') {
|
if (detection.type === 'legacy' && wdsFolder !== '_wds') {
|
||||||
|
|
@ -47,45 +48,30 @@ class Installer {
|
||||||
migrateSpinner.succeed(`Legacy _wds/ removed — installing fresh at ${wdsFolder}/`);
|
migrateSpinner.succeed(`Legacy _wds/ removed — installing fresh at ${wdsFolder}/`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if already installed at target path
|
// Handle update vs fresh for existing target path
|
||||||
if (await fs.pathExists(wdsDir)) {
|
if (action === 'update' && await fs.pathExists(wdsDir)) {
|
||||||
console.log(chalk.yellow(`\n ${wdsFolder}/ already exists.`));
|
// Preserve config.yaml during update
|
||||||
const { action } = await inquirer.prompt([
|
const configPath = path.join(wdsDir, 'config.yaml');
|
||||||
{
|
if (!config._savedConfigYaml && await fs.pathExists(configPath)) {
|
||||||
type: 'list',
|
config._savedConfigYaml = await fs.readFile(configPath, 'utf8');
|
||||||
name: 'action',
|
|
||||||
message: 'What would you like to do?',
|
|
||||||
choices: [
|
|
||||||
{ name: 'Update - Replace WDS files, keep config.yaml', value: 'update' },
|
|
||||||
{ name: 'Fresh install - Remove everything and start over', value: 'fresh' },
|
|
||||||
{ name: 'Cancel', value: 'cancel' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (action === 'cancel') {
|
|
||||||
return { success: false };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action === 'fresh') {
|
const removeSpinner = ora('Updating WDS files...').start();
|
||||||
const removeSpinner = ora('Removing existing WDS installation...').start();
|
await fs.remove(wdsDir);
|
||||||
await fs.remove(wdsDir);
|
removeSpinner.succeed('Old files cleared');
|
||||||
removeSpinner.succeed('Old installation removed');
|
} else if (action === 'fresh' && await fs.pathExists(wdsDir)) {
|
||||||
} else if (action === 'update') {
|
const removeSpinner = ora('Removing existing WDS installation...').start();
|
||||||
// Preserve config.yaml during update
|
await fs.remove(wdsDir);
|
||||||
const configPath = path.join(wdsDir, 'config.yaml');
|
removeSpinner.succeed('Old installation removed');
|
||||||
let savedConfig = null;
|
}
|
||||||
if (await fs.pathExists(configPath)) {
|
|
||||||
savedConfig = await fs.readFile(configPath, 'utf8');
|
|
||||||
}
|
|
||||||
|
|
||||||
const removeSpinner = ora('Updating WDS files...').start();
|
// On update, extract ides and root_folder from saved config
|
||||||
await fs.remove(wdsDir);
|
if (action === 'update' && config._savedConfigYaml) {
|
||||||
removeSpinner.succeed('Old files cleared');
|
try {
|
||||||
|
const savedData = yaml.load(config._savedConfigYaml);
|
||||||
// Will be restored after copy
|
if (!config.ides && savedData.ides) config.ides = savedData.ides;
|
||||||
config._savedConfigYaml = savedConfig;
|
if (!config.root_folder && savedData.output_folder) config.root_folder = savedData.output_folder;
|
||||||
}
|
} catch { /* ignore parse errors, defaults will apply */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure parent directory exists (for _bmad/wds/)
|
// Ensure parent directory exists (for _bmad/wds/)
|
||||||
|
|
|
||||||
|
|
@ -58,33 +58,75 @@ class UI {
|
||||||
|
|
||||||
console.log(chalk.white(` Target: ${chalk.cyan(projectDir)}`));
|
console.log(chalk.white(` Target: ${chalk.cyan(projectDir)}`));
|
||||||
|
|
||||||
// Handle legacy _wds/ detection
|
|
||||||
let wdsFolder = detection.folder;
|
let wdsFolder = detection.folder;
|
||||||
|
let action = 'fresh';
|
||||||
|
|
||||||
|
// --- Existing installation: ask update/fresh/migrate FIRST ---
|
||||||
if (detection.type === 'legacy') {
|
if (detection.type === 'legacy') {
|
||||||
console.log(chalk.yellow(`\n Found legacy installation at ${chalk.white(LEGACY_WDS_FOLDER + '/')}`));
|
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`));
|
console.log(chalk.dim(` BMAD standard path is ${chalk.white(WDS_FOLDER + '/')}\n`));
|
||||||
|
|
||||||
const { migrationChoice } = await inquirer.prompt([
|
const { choice } = await inquirer.prompt([
|
||||||
{
|
{
|
||||||
type: 'list',
|
type: 'list',
|
||||||
name: 'migrationChoice',
|
name: 'choice',
|
||||||
message: 'How would you like to proceed?',
|
message: 'How would you like to proceed?',
|
||||||
choices: [
|
choices: [
|
||||||
{ name: `Migrate to ${WDS_FOLDER}/ (recommended)`, value: 'migrate' },
|
{ name: `Update & migrate to ${WDS_FOLDER}/ (recommended)`, value: 'migrate-update' },
|
||||||
{ name: `Keep at ${LEGACY_WDS_FOLDER}/ (legacy)`, value: 'keep' },
|
{ name: `Update at ${LEGACY_WDS_FOLDER}/ (keep legacy path)`, value: 'legacy-update' },
|
||||||
|
{ name: 'Fresh install (remove everything and start over)', value: 'fresh' },
|
||||||
|
{ name: 'Cancel', value: 'cancel' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (migrationChoice === 'keep') {
|
if (choice === 'cancel') return { cancelled: true };
|
||||||
|
|
||||||
|
if (choice === 'migrate-update') {
|
||||||
|
action = 'update';
|
||||||
|
// wdsFolder stays as WDS_FOLDER (_bmad/wds)
|
||||||
|
} else if (choice === 'legacy-update') {
|
||||||
|
action = 'update';
|
||||||
wdsFolder = LEGACY_WDS_FOLDER;
|
wdsFolder = LEGACY_WDS_FOLDER;
|
||||||
|
} else {
|
||||||
|
action = 'fresh';
|
||||||
}
|
}
|
||||||
// 'migrate' keeps the default WDS_FOLDER — installer.js handles the actual move
|
} else if (detection.type === 'bmad') {
|
||||||
|
console.log(chalk.dim(`\n Found existing installation at ${chalk.white(WDS_FOLDER + '/')}\n`));
|
||||||
|
|
||||||
|
const { choice } = await inquirer.prompt([
|
||||||
|
{
|
||||||
|
type: 'list',
|
||||||
|
name: 'choice',
|
||||||
|
message: 'What would you like to do?',
|
||||||
|
choices: [
|
||||||
|
{ name: 'Update - Replace WDS files, keep config.yaml', value: 'update' },
|
||||||
|
{ name: 'Fresh install - Remove everything and start over', value: 'fresh' },
|
||||||
|
{ name: 'Cancel', value: 'cancel' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (choice === 'cancel') return { cancelled: true };
|
||||||
|
action = choice;
|
||||||
} else {
|
} else {
|
||||||
console.log(chalk.dim(` Agents and workflows will be installed in ${chalk.white(wdsFolder + '/')}\n`));
|
console.log(chalk.dim(` Agents and workflows will be installed in ${chalk.white(wdsFolder + '/')}\n`));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5-question installer
|
// --- Update: skip config questions, config.yaml will be preserved ---
|
||||||
|
if (action === 'update') {
|
||||||
|
console.log(chalk.dim(' Existing config.yaml will be preserved.\n'));
|
||||||
|
|
||||||
|
return {
|
||||||
|
projectDir,
|
||||||
|
wdsFolder,
|
||||||
|
_detection: detection,
|
||||||
|
_action: action,
|
||||||
|
cancelled: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Fresh install: ask all config questions ---
|
||||||
const answers = await inquirer.prompt([
|
const answers = await inquirer.prompt([
|
||||||
{
|
{
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
|
@ -154,6 +196,7 @@ class UI {
|
||||||
...answers,
|
...answers,
|
||||||
wdsFolder,
|
wdsFolder,
|
||||||
_detection: detection,
|
_detection: detection,
|
||||||
|
_action: action,
|
||||||
cancelled: false,
|
cancelled: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue