Compare commits

...

6 Commits

Author SHA1 Message Date
manjaroblack
ed4db108df feat: enhance status command to support expansion-only installations and add debug mode 2025-08-19 11:49:07 -05:00
manjaroblack
b00fd55fbf chore: revert version from 4.39.2 to 4.39.1 in package manifests 2025-08-18 20:40:10 -05:00
manjaroblack
28f48af7f0 chore: remove unnecessary whitespace and comments from workflow files 2025-08-18 20:38:25 -05:00
manjaroblack
31c4744ed3 fix: remove unnecessary divider lines in creative writing workflow files 2025-08-18 20:35:30 -05:00
manjaroblack
17e7d14cc2 fix: honor original working directory when running npx installer and searching for task files
(cherry picked from commit 6a5a597fe39bc75379f54bedc1616bfab283dfa1)
2025-08-18 19:34:09 -05:00
github-actions[bot]
7e2780cd3e release: bump to v4.39.2 2025-08-17 16:08:37 +00:00
5 changed files with 144 additions and 68 deletions

View File

@@ -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",

View File

@@ -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);

View File

@@ -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';

View File

@@ -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')));
} }

View File

@@ -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;