Compare commits
6 Commits
user-guide
...
default-in
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed4db108df | ||
|
|
b00fd55fbf | ||
|
|
28f48af7f0 | ||
|
|
31c4744ed3 | ||
|
|
17e7d14cc2 | ||
|
|
7e2780cd3e |
@@ -40,6 +40,8 @@
|
|||||||
"release:minor": "gh workflow run \"Manual Release\" -f version_bump=minor",
|
"release:minor": "gh workflow run \"Manual Release\" -f version_bump=minor",
|
||||||
"release:patch": "gh workflow run \"Manual Release\" -f version_bump=patch",
|
"release:patch": "gh workflow run \"Manual Release\" -f version_bump=patch",
|
||||||
"release:watch": "gh run watch",
|
"release:watch": "gh run watch",
|
||||||
|
"status": "node tools/installer/bin/bmad.js status",
|
||||||
|
"status:debug": "BMAD_DEBUG=1 node tools/installer/bin/bmad.js status",
|
||||||
"validate": "node tools/cli.js validate",
|
"validate": "node tools/cli.js validate",
|
||||||
"version:all": "node tools/bump-all-versions.js",
|
"version:all": "node tools/bump-all-versions.js",
|
||||||
"version:all:major": "node tools/bump-all-versions.js major",
|
"version:all:major": "node tools/bump-all-versions.js major",
|
||||||
|
|||||||
@@ -26,9 +26,12 @@ if (isNpxExecution) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Honor the directory where the user invoked npx from
|
||||||
|
const originalCwd = process.env.INIT_CWD || process.env.PWD || process.cwd();
|
||||||
execSync(`node "${bmadScriptPath}" ${arguments_.join(' ')}`, {
|
execSync(`node "${bmadScriptPath}" ${arguments_.join(' ')}`, {
|
||||||
stdio: 'inherit',
|
stdio: 'inherit',
|
||||||
cwd: path.dirname(__dirname),
|
cwd: originalCwd,
|
||||||
|
env: { ...process.env, BMAD_ORIGINAL_CWD: originalCwd },
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
process.exit(error.status || 1);
|
process.exit(error.status || 1);
|
||||||
|
|||||||
@@ -208,6 +208,7 @@ async function promptInstallation() {
|
|||||||
console.log(chalk.bold.magenta('🚀 Universal AI Agent Framework for Any Domain'));
|
console.log(chalk.bold.magenta('🚀 Universal AI Agent Framework for Any Domain'));
|
||||||
console.log(chalk.bold.blue(`✨ Installer v${version}\n`));
|
console.log(chalk.bold.blue(`✨ Installer v${version}\n`));
|
||||||
|
|
||||||
|
const originalCwd = process.env.INIT_CWD || process.env.PWD || process.cwd();
|
||||||
const answers = {};
|
const answers = {};
|
||||||
|
|
||||||
// Ask for installation directory first
|
// Ask for installation directory first
|
||||||
@@ -216,7 +217,7 @@ async function promptInstallation() {
|
|||||||
type: 'input',
|
type: 'input',
|
||||||
name: 'directory',
|
name: 'directory',
|
||||||
message: 'Enter the full path to your project directory where BMad should be installed:',
|
message: 'Enter the full path to your project directory where BMad should be installed:',
|
||||||
default: path.resolve('.'),
|
default: originalCwd,
|
||||||
validate: (input) => {
|
validate: (input) => {
|
||||||
if (!input.trim()) {
|
if (!input.trim()) {
|
||||||
return 'Please enter a valid project path';
|
return 'Please enter a valid project path';
|
||||||
|
|||||||
@@ -520,15 +520,14 @@ class IdeSetup extends BaseIdeSetup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (await fileManager.pathExists(tasksDir)) {
|
if (await fileManager.pathExists(tasksDir)) {
|
||||||
const glob = require('glob');
|
const taskFiles = await resourceLocator.findFiles('*.md', { cwd: tasksDir });
|
||||||
const taskFiles = glob.sync('*.md', { cwd: tasksDir });
|
|
||||||
allTaskIds.push(...taskFiles.map((file) => path.basename(file, '.md')));
|
allTaskIds.push(...taskFiles.map((file) => path.basename(file, '.md')));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check common tasks
|
// Check common tasks
|
||||||
const commonTasksDir = path.join(installDir, 'common', 'tasks');
|
const commonTasksDir = path.join(installDir, 'common', 'tasks');
|
||||||
if (await fileManager.pathExists(commonTasksDir)) {
|
if (await fileManager.pathExists(commonTasksDir)) {
|
||||||
const commonTaskFiles = glob.sync('*.md', { cwd: commonTasksDir });
|
const commonTaskFiles = await resourceLocator.findFiles('*.md', { cwd: commonTasksDir });
|
||||||
allTaskIds.push(...commonTaskFiles.map((file) => path.basename(file, '.md')));
|
allTaskIds.push(...commonTaskFiles.map((file) => path.basename(file, '.md')));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ class Installer {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Store the original CWD where npx was executed
|
// Store the original CWD where npx was executed
|
||||||
const originalCwd = process.env.INIT_CWD || process.env.PWD || process.cwd();
|
const originalCwd =
|
||||||
|
process.env.BMAD_ORIGINAL_CWD || process.env.INIT_CWD || process.env.PWD || process.cwd();
|
||||||
|
|
||||||
// Resolve installation directory relative to where the user ran the command
|
// Resolve installation directory relative to where the user ran the command
|
||||||
let installDir = path.isAbsolute(config.directory)
|
let installDir = path.isAbsolute(config.directory)
|
||||||
@@ -1005,7 +1006,8 @@ class Installer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async showStatus() {
|
async showStatus() {
|
||||||
const installDir = await this.findInstallation();
|
// Try to locate an installation root. Include expansion-only installs.
|
||||||
|
const installDir = await this.findInstallation(true);
|
||||||
|
|
||||||
if (!installDir) {
|
if (!installDir) {
|
||||||
console.log(chalk.yellow('No BMad installation found in current directory tree'));
|
console.log(chalk.yellow('No BMad installation found in current directory tree'));
|
||||||
@@ -1013,35 +1015,61 @@ class Installer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const manifest = await fileManager.readManifest(installDir);
|
const manifest = await fileManager.readManifest(installDir);
|
||||||
|
const expansionPacks = await this.detectExpansionPacks(installDir);
|
||||||
|
const hasExpansionPacks = expansionPacks && Object.keys(expansionPacks).length > 0;
|
||||||
|
|
||||||
if (!manifest) {
|
if (!manifest && !hasExpansionPacks) {
|
||||||
console.log(chalk.red('Invalid installation - manifest not found'));
|
console.log(chalk.red('Invalid installation - manifest not found'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(chalk.bold('\nBMad Installation Status:\n'));
|
if (manifest) {
|
||||||
console.log(` Directory: ${installDir}`);
|
console.log(chalk.bold('\nBMad Installation Status:\n'));
|
||||||
console.log(` Version: ${manifest.version}`);
|
console.log(` Directory: ${installDir}`);
|
||||||
console.log(` Installed: ${new Date(manifest.installed_at).toLocaleDateString()}`);
|
console.log(` Version: ${manifest.version}`);
|
||||||
console.log(` Type: ${manifest.install_type}`);
|
console.log(` Installed: ${new Date(manifest.installed_at).toLocaleDateString()}`);
|
||||||
|
console.log(` Type: ${manifest.install_type}`);
|
||||||
|
|
||||||
if (manifest.agent) {
|
if (manifest.agent) {
|
||||||
console.log(` Agent: ${manifest.agent}`);
|
console.log(` Agent: ${manifest.agent}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (manifest.ides_setup && manifest.ides_setup.length > 0) {
|
||||||
|
console.log(` IDE Setup: ${manifest.ides_setup.join(', ')}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(` Total Files: ${manifest.files.length}`);
|
||||||
|
|
||||||
|
// Check for modifications in core
|
||||||
|
const modifiedFiles = await fileManager.checkModifiedFiles(installDir, manifest);
|
||||||
|
if (modifiedFiles.length > 0) {
|
||||||
|
console.log(chalk.yellow(` Modified Files: ${modifiedFiles.length}`));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are also expansion packs, list them
|
||||||
|
if (hasExpansionPacks) {
|
||||||
|
console.log('\nExpansion Packs:');
|
||||||
|
for (const [packId, info] of Object.entries(expansionPacks)) {
|
||||||
|
const version = info.manifest?.version || 'unknown';
|
||||||
|
console.log(` - ${packId} (${version})`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log('');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (manifest.ides_setup && manifest.ides_setup.length > 0) {
|
// Expansion-only installation (no .bmad-core manifest)
|
||||||
console.log(` IDE Setup: ${manifest.ides_setup.join(', ')}`);
|
if (hasExpansionPacks) {
|
||||||
|
console.log(chalk.bold('\nBMad Expansion-only Installation Status:\n'));
|
||||||
|
console.log(` Directory: ${installDir}`);
|
||||||
|
console.log(` Expansion Packs: ${Object.keys(expansionPacks).length}`);
|
||||||
|
for (const [packId, info] of Object.entries(expansionPacks)) {
|
||||||
|
const version = info.manifest?.version || 'unknown';
|
||||||
|
console.log(` • ${packId} (${version})`);
|
||||||
|
}
|
||||||
|
console.log('');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(` Total Files: ${manifest.files.length}`);
|
|
||||||
|
|
||||||
// Check for modifications
|
|
||||||
const modifiedFiles = await fileManager.checkModifiedFiles(installDir, manifest);
|
|
||||||
if (modifiedFiles.length > 0) {
|
|
||||||
console.log(chalk.yellow(` Modified Files: ${modifiedFiles.length}`));
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAvailableAgents() {
|
async getAvailableAgents() {
|
||||||
@@ -1792,32 +1820,22 @@ class Installer {
|
|||||||
|
|
||||||
for (const folder of dotFolders) {
|
for (const folder of dotFolders) {
|
||||||
const folderPath = path.join(installDir, folder);
|
const folderPath = path.join(installDir, folder);
|
||||||
const stats = await fileManager.pathExists(folderPath);
|
const exists = await fileManager.pathExists(folderPath);
|
||||||
|
if (!exists) continue;
|
||||||
|
|
||||||
if (stats) {
|
// Only consider folders with an explicit BMad expansion manifest
|
||||||
// Check if it has a manifest
|
const manifestPath = path.join(folderPath, 'install-manifest.yaml');
|
||||||
const manifestPath = path.join(folderPath, 'install-manifest.yaml');
|
if (!(await fileManager.pathExists(manifestPath))) continue;
|
||||||
if (await fileManager.pathExists(manifestPath)) {
|
|
||||||
const manifest = await fileManager.readExpansionPackManifest(installDir, folder.slice(1));
|
const manifest = await fileManager.readExpansionPackManifest(installDir, folder.slice(1));
|
||||||
if (manifest) {
|
// Validate manifest to avoid false positives from unrelated dot-folders
|
||||||
expansionPacks[folder.slice(1)] = {
|
if (!manifest || typeof manifest.expansion_pack_id !== 'string') continue;
|
||||||
path: folderPath,
|
|
||||||
manifest: manifest,
|
expansionPacks[folder.slice(1)] = {
|
||||||
hasManifest: true,
|
path: folderPath,
|
||||||
};
|
manifest,
|
||||||
}
|
hasManifest: true,
|
||||||
} else {
|
};
|
||||||
// Check if it has a config.yaml (expansion pack without manifest)
|
|
||||||
const configPath = path.join(folderPath, 'config.yaml');
|
|
||||||
if (await fileManager.pathExists(configPath)) {
|
|
||||||
expansionPacks[folder.slice(1)] = {
|
|
||||||
path: folderPath,
|
|
||||||
manifest: null,
|
|
||||||
hasManifest: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return expansionPacks;
|
return expansionPacks;
|
||||||
@@ -1943,27 +1961,80 @@ class Installer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async findInstallation() {
|
async findInstallation(includeExpansionOnly = false) {
|
||||||
// Look for .bmad-core in current directory or parent directories
|
// Start from where the user invoked the CLI (npx wrapper sets BMAD_ORIGINAL_CWD)
|
||||||
let currentDir = process.cwd();
|
const startDir =
|
||||||
|
process.env.BMAD_ORIGINAL_CWD || process.env.INIT_CWD || process.env.PWD || process.cwd();
|
||||||
|
|
||||||
while (currentDir !== path.dirname(currentDir)) {
|
const debug = (message) => {
|
||||||
const bmadDir = path.join(currentDir, '.bmad-core');
|
if (process.env.BMAD_DEBUG) console.log(chalk.dim(`[bmad:findInstallation] ${message}`));
|
||||||
const manifestPath = path.join(bmadDir, 'install-manifest.yaml');
|
};
|
||||||
|
|
||||||
if (await fileManager.pathExists(manifestPath)) {
|
let currentDir = path.resolve(startDir);
|
||||||
return currentDir; // Return parent directory, not .bmad-core itself
|
const visited = new Set();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (visited.has(currentDir)) break;
|
||||||
|
visited.add(currentDir);
|
||||||
|
debug(`checking: ${currentDir}`);
|
||||||
|
|
||||||
|
// Case: we are inside .bmad-core
|
||||||
|
if (path.basename(currentDir) === '.bmad-core') {
|
||||||
|
const parentDir = path.dirname(currentDir);
|
||||||
|
const coreManifest = path.join(parentDir, '.bmad-core', 'install-manifest.yaml');
|
||||||
|
if (await fileManager.pathExists(coreManifest)) {
|
||||||
|
debug(`found core manifest (from inside .bmad-core): ${coreManifest}`);
|
||||||
|
return parentDir;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
currentDir = path.dirname(currentDir);
|
// Case: we are inside an expansion pack dot-folder
|
||||||
}
|
if (includeExpansionOnly) {
|
||||||
|
const base = path.basename(currentDir);
|
||||||
// Also check if we're inside a .bmad-core directory
|
if (base.startsWith('.') && base !== '.git' && base !== '.bmad-core') {
|
||||||
if (path.basename(process.cwd()) === '.bmad-core') {
|
const packManifest = path.join(currentDir, 'install-manifest.yaml');
|
||||||
const manifestPath = path.join(process.cwd(), 'install-manifest.yaml');
|
if (await fileManager.pathExists(packManifest)) {
|
||||||
if (await fileManager.pathExists(manifestPath)) {
|
const parentDir = path.dirname(currentDir);
|
||||||
return path.dirname(process.cwd()); // Return parent directory
|
// Validate it's a BMad expansion manifest
|
||||||
|
try {
|
||||||
|
const manifest = await fileManager.readExpansionPackManifest(
|
||||||
|
parentDir,
|
||||||
|
base.slice(1),
|
||||||
|
);
|
||||||
|
if (manifest && typeof manifest.expansion_pack_id === 'string') {
|
||||||
|
debug(`found expansion pack folder at ${currentDir}; root is ${parentDir}`);
|
||||||
|
return parentDir;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// ignore invalid manifests
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Detection: full installation (.bmad-core with manifest)
|
||||||
|
const coreManifestPath = path.join(currentDir, '.bmad-core', 'install-manifest.yaml');
|
||||||
|
if (await fileManager.pathExists(coreManifestPath)) {
|
||||||
|
debug(`found core manifest at ${coreManifestPath}`);
|
||||||
|
return currentDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detection: expansion-only installation (any expansion dot-folder with manifest/config)
|
||||||
|
if (includeExpansionOnly) {
|
||||||
|
try {
|
||||||
|
const expansions = await this.detectExpansionPacks(currentDir);
|
||||||
|
if (expansions && Object.keys(expansions).length > 0) {
|
||||||
|
debug(`found expansion-only installation at ${currentDir}`);
|
||||||
|
return currentDir;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// ignore errors during detection and continue ascending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const parent = path.dirname(currentDir);
|
||||||
|
if (parent === currentDir) break; // reached filesystem root
|
||||||
|
currentDir = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
Reference in New Issue
Block a user