fix: BMAD install creates .bmad-core/.bmad-core/ directory structure + updates (#223)

* chore: fix installation directory handling to use .bmad-core as default path

- Remove redundant ./ prefix from default directory
- Update all default paths from ./.bmad-core to .bmad-core
- Add logic to handle direct .bmad-core path selection
- Treat parent as project root when .bmad-core specified
- Simplify directory state detection for existing files
- Remove unknown_existing state type from installer logic

* chore: refactor installer to use modern JS patterns and improve code clarity

## CHANGES

- Replace require with node:path import
- Add block scoping to switch cases
- Remove unused options parameter from update
- Use optional chaining for ideConfig check
- Replace forEach with for...of loops
- Use template literals for string concatenation
- Add early return to avoid else block
- Update spell check dictionary entries

* chore: update dependencies to latest major versions

## CHANGES

- Update @kayvan/markdown-tree-parser to v1.5.0
- Update chalk to v5.4.1 for ESM support
- Update commander to v14.0.0 with Node 20 requirement
- Update fs-extra to v11.3.0
- Update glob to v11.0.3 with new API
- Update inquirer to v12.6.3 with modular design
- Update ora to v8.2.0 with improved features
This commit is contained in:
Kayvan Sylvan
2025-06-15 10:50:40 -07:00
committed by GitHub
parent 9a10a153fb
commit 28b313c01d
7 changed files with 1401 additions and 806 deletions

View File

@@ -27,6 +27,7 @@
"Luxon", "Luxon",
"MERN", "MERN",
"mgmt", "mgmt",
"nodir",
"Nuxt", "Nuxt",
"overcommitting", "overcommitting",
"pasteable", "pasteable",
@@ -58,6 +59,8 @@
"Turborepo", "Turborepo",
"Underserved", "Underserved",
"unredacted", "unredacted",
"upgrader",
"upgraders",
"VARCHAR", "VARCHAR",
"venv", "venv",
"vercel", "vercel",

1265
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -23,14 +23,14 @@
"prepare": "husky" "prepare": "husky"
}, },
"dependencies": { "dependencies": {
"@kayvan/markdown-tree-parser": "^1.4.2", "@kayvan/markdown-tree-parser": "^1.5.0",
"chalk": "^4.1.2", "chalk": "^5.4.1",
"commander": "^9.4.1", "commander": "^14.0.0",
"fs-extra": "^11.1.0", "fs-extra": "^11.3.0",
"glob": "^8.0.3", "glob": "^11.0.3",
"inquirer": "^8.2.5", "inquirer": "^12.6.3",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"ora": "^5.4.1" "ora": "^8.2.0"
}, },
"keywords": [ "keywords": [
"agile", "agile",

View File

@@ -37,7 +37,7 @@ program
.description('Install BMAD Method agents and tools') .description('Install BMAD Method agents and tools')
.option('-f, --full', 'Install complete .bmad-core folder') .option('-f, --full', 'Install complete .bmad-core folder')
.option('-a, --agent <agent>', 'Install specific agent with dependencies') .option('-a, --agent <agent>', 'Install specific agent with dependencies')
.option('-d, --directory <path>', 'Installation directory (default: ./bmad-core)') .option('-d, --directory <path>', 'Installation directory (default: .bmad-core)')
.option('-i, --ide <ide>', 'Configure for specific IDE (cursor, claude-code, windsurf, roo)') .option('-i, --ide <ide>', 'Configure for specific IDE (cursor, claude-code, windsurf, roo)')
.action(async (options) => { .action(async (options) => {
try { try {
@@ -50,7 +50,7 @@ program
const config = { const config = {
installType: options.full ? 'full' : 'single-agent', installType: options.full ? 'full' : 'single-agent',
agent: options.agent, agent: options.agent,
directory: options.directory || './.bmad-core', directory: options.directory || '.bmad-core',
ide: options.ide ide: options.ide
}; };
await installer.install(config); await installer.install(config);
@@ -66,9 +66,9 @@ program
.description('Update existing BMAD installation') .description('Update existing BMAD installation')
.option('--force', 'Force update, overwriting modified files') .option('--force', 'Force update, overwriting modified files')
.option('--dry-run', 'Show what would be updated without making changes') .option('--dry-run', 'Show what would be updated without making changes')
.action(async (options) => { .action(async () => {
try { try {
await installer.update(options); await installer.update();
} catch (error) { } catch (error) {
console.error(chalk.red('Update failed:'), error.message); console.error(chalk.red('Update failed:'), error.message);
process.exit(1); process.exit(1);
@@ -110,7 +110,7 @@ async function promptInstallation() {
type: 'input', type: 'input',
name: 'directory', name: 'directory',
message: 'Where would you like to install BMAD?', message: 'Where would you like to install BMAD?',
default: './.bmad-core' default: '.bmad-core'
} }
]); ]);
answers.directory = directory; answers.directory = directory;

View File

@@ -1,4 +1,4 @@
const path = require("path"); const path = require("node:path");
const chalk = require("chalk"); const chalk = require("chalk");
const ora = require("ora"); const ora = require("ora");
const inquirer = require("inquirer"); const inquirer = require("inquirer");
@@ -12,7 +12,11 @@ class Installer {
try { try {
// Resolve installation directory // Resolve installation directory
const installDir = path.resolve(config.directory); let installDir = path.resolve(config.directory);
if (path.basename(installDir) === '.bmad-core') {
// If user points directly to .bmad-core, treat its parent as the project root
installDir = path.dirname(installDir);
}
// Detect current state // Detect current state
const state = await this.detectInstallationState(installDir); const state = await this.detectInstallationState(installDir);
@@ -103,9 +107,9 @@ class Installer {
}); });
if (files.length > 0) { if (files.length > 0) {
state.type = "unknown_existing"; // Directory has other files, but no BMAD installation.
// Treat as clean install but record that it isn't empty.
state.hasOtherFiles = true; state.hasOtherFiles = true;
return state;
} }
return state; // clean install return state; // clean install
@@ -253,11 +257,12 @@ class Installer {
]); ]);
switch (action) { switch (action) {
case "upgrade": case "upgrade": {
console.log(chalk.cyan("\n📦 Starting v3 to v4 upgrade process...")); console.log(chalk.cyan("\n📦 Starting v3 to v4 upgrade process..."));
const V3ToV4Upgrader = require("../../upgraders/v3-to-v4-upgrader"); const V3ToV4Upgrader = require("../../upgraders/v3-to-v4-upgrader");
const upgrader = new V3ToV4Upgrader(); const upgrader = new V3ToV4Upgrader();
return await upgrader.upgrade({ projectPath: installDir }); return await upgrader.upgrade({ projectPath: installDir });
}
case "alongside": case "alongside":
return await this.performFreshInstall(config, installDir, spinner); return await this.performFreshInstall(config, installDir, spinner);
case "cancel": case "cancel":
@@ -295,7 +300,7 @@ class Installer {
switch (action) { switch (action) {
case "force": case "force":
return await this.performFreshInstall(config, installDir, spinner); return await this.performFreshInstall(config, installDir, spinner);
case "different": case "different": {
const { newDir } = await inquirer.prompt([ const { newDir } = await inquirer.prompt([
{ {
type: "input", type: "input",
@@ -306,6 +311,7 @@ class Installer {
]); ]);
config.directory = newDir; config.directory = newDir;
return await this.install(config); return await this.install(config);
}
case "cancel": case "cancel":
console.log("Installation cancelled."); console.log("Installation cancelled.");
return; return;
@@ -326,7 +332,9 @@ class Installer {
if (modifiedFiles.length > 0) { if (modifiedFiles.length > 0) {
spinner.warn("Found modified files"); spinner.warn("Found modified files");
console.log(chalk.yellow("\nThe following files have been modified:")); console.log(chalk.yellow("\nThe following files have been modified:"));
modifiedFiles.forEach((file) => console.log(` - ${file}`)); for (const file of modifiedFiles) {
console.log(` - ${file}`);
}
const { action } = await inquirer.prompt([ const { action } = await inquirer.prompt([
{ {
@@ -391,9 +399,9 @@ class Installer {
if (config.ide) { if (config.ide) {
const ideConfig = configLoader.getIdeConfiguration(config.ide); const ideConfig = configLoader.getIdeConfiguration(config.ide);
if (ideConfig && ideConfig.instructions) { if (ideConfig?.instructions) {
console.log( console.log(
chalk.bold("To use BMAD agents in " + ideConfig.name + ":") chalk.bold(`To use BMAD agents in ${ideConfig.name}:`)
); );
console.log(ideConfig.instructions); console.log(ideConfig.instructions);
} }
@@ -418,7 +426,7 @@ class Installer {
} }
// Legacy method for backward compatibility // Legacy method for backward compatibility
async update(options) { async update() {
console.log(chalk.yellow('The "update" command is deprecated.')); console.log(chalk.yellow('The "update" command is deprecated.'));
console.log( console.log(
'Please use "install" instead - it will detect and offer to update existing installations.' 'Please use "install" instead - it will detect and offer to update existing installations.'
@@ -432,9 +440,8 @@ class Installer {
ide: null, ide: null,
}; };
return await this.install(config); return await this.install(config);
} else {
console.log(chalk.red("No BMAD installation found."));
} }
console.log(chalk.red("No BMAD installation found."));
} }
async listAgents() { async listAgents() {
@@ -442,9 +449,9 @@ class Installer {
console.log(chalk.bold("\nAvailable BMAD Agents:\n")); console.log(chalk.bold("\nAvailable BMAD Agents:\n"));
agents.forEach((agent) => { for (const agent of agents) {
console.log(chalk.cyan(` ${agent.id.padEnd(20)}`), agent.description); console.log(chalk.cyan(` ${agent.id.padEnd(20)}`), agent.description);
}); }
console.log( console.log(
chalk.dim("\nInstall with: npx bmad-method install --agent=<id>\n") chalk.dim("\nInstall with: npx bmad-method install --agent=<id>\n")

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "bmad-method", "name": "bmad-method",
"version": "4.0.0", "version": "4.0.1",
"description": "BMAD Method installer - AI-powered Agile development framework", "description": "BMAD Method installer - AI-powered Agile development framework",
"main": "lib/installer.js", "main": "lib/installer.js",
"bin": { "bin": {
@@ -22,12 +22,12 @@
"author": "BMAD Team", "author": "BMAD Team",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"chalk": "^4.1.2", "chalk": "^5.4.1",
"commander": "^9.4.1", "commander": "^14.0.0",
"fs-extra": "^11.1.0", "fs-extra": "^11.3.0",
"inquirer": "^8.2.5", "inquirer": "^12.6.3",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"ora": "^5.4.1" "ora": "^8.2.0"
}, },
"engines": { "engines": {
"node": ">=14.0.0" "node": ">=14.0.0"