Node 20, installer improvements, agent improvements and Expansion Pack for game dev (#232)

* feat: add expansion pack installation system with game dev and infrastructure expansion packs

- Added expansion pack discovery and installation to BMAD installer
- Supports interactive and CLI installation of expansion packs
- Expansion pack files install to destination root (.bmad-core)
- Added game development expansion pack (.bmad-2d-phaser-game-dev)
  - Game designer, developer, and scrum master agents
  - Game-specific templates, tasks, workflows, and guidelines
  - Specialized for Phaser 3 + TypeScript development
- Added infrastructure devops expansion pack (.bmad-infrastructure-devops)
  - Platform engineering agent and infrastructure templates
- Expansion pack agents automatically integrate with IDE rules
- Added list:expansions command and --expansion-packs CLI option

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>

* alpha expansion packs and installer update to support installing expansion packs optionally

* node20

---------

Co-authored-by: Brian Madison <brianmadison@Brians-MacBook-Pro.local>
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Brian
2025-06-16 18:34:12 -05:00
committed by GitHub
parent 7df4f4cd0f
commit 595342cb10
126 changed files with 20695 additions and 29296 deletions

View File

@@ -7,10 +7,10 @@ class WebBuilder {
this.rootDir = options.rootDir || process.cwd();
this.outputDirs = options.outputDirs || [
path.join(this.rootDir, 'dist'),
path.join(this.rootDir, '.bmad-core', 'web-bundles')
path.join(this.rootDir, 'bmad-core', 'web-bundles')
];
this.resolver = new DependencyResolver(this.rootDir);
this.templatePath = path.join(this.rootDir, '.bmad-core', 'templates', 'web-agent-startup-instructions-template.md');
this.templatePath = path.join(this.rootDir, 'bmad-core', 'templates', 'web-agent-startup-instructions-template.md');
}
async cleanOutputDirs() {
@@ -138,6 +138,195 @@ class WebBuilder {
}
}
async buildAllExpansionPacks(options = {}) {
const expansionPacks = await this.listExpansionPacks();
for (const packName of expansionPacks) {
console.log(` Building expansion pack: ${packName}`);
await this.buildExpansionPack(packName, options);
}
console.log(`Built ${expansionPacks.length} expansion pack bundles`);
}
async buildExpansionPack(packName, options = {}) {
const packDir = path.join(this.rootDir, 'expansion-packs', packName);
const outputDirs = [
path.join(packDir, 'web-bundles'),
path.join(this.rootDir, 'dist', 'expansion-packs', packName)
];
// Clean output directories if requested
if (options.clean !== false) {
for (const outputDir of outputDirs) {
try {
await fs.rm(outputDir, { recursive: true, force: true });
} catch (error) {
// Directory might not exist, that's fine
}
}
}
// Build individual agents first
const agentsDir = path.join(packDir, 'agents');
try {
const agentFiles = await fs.readdir(agentsDir);
const agentMarkdownFiles = agentFiles.filter(f => f.endsWith('.md'));
if (agentMarkdownFiles.length > 0) {
console.log(` Building individual agents for ${packName}:`);
for (const agentFile of agentMarkdownFiles) {
const agentName = agentFile.replace('.md', '');
console.log(` - ${agentName}`);
// Build individual agent bundle
const bundle = await this.buildExpansionAgentBundle(packName, packDir, agentName);
// Write to all output directories
for (const outputDir of outputDirs) {
const agentsOutputDir = path.join(outputDir, 'agents');
await fs.mkdir(agentsOutputDir, { recursive: true });
const outputFile = path.join(agentsOutputDir, `${agentName}.txt`);
await fs.writeFile(outputFile, bundle, 'utf8');
}
}
}
} catch (error) {
console.debug(` No agents directory found for ${packName}`);
}
// Build team bundle
const agentTeamsDir = path.join(packDir, 'agent-teams');
try {
const teamFiles = await fs.readdir(agentTeamsDir);
const teamFile = teamFiles.find(f => f.startsWith('team-') && f.endsWith('.yml'));
if (teamFile) {
console.log(` Building team bundle for ${packName}`);
const teamConfigPath = path.join(agentTeamsDir, teamFile);
// Build expansion pack as a team bundle
const bundle = await this.buildExpansionTeamBundle(packName, packDir, teamConfigPath);
// Write to all output directories
for (const outputDir of outputDirs) {
const teamsOutputDir = path.join(outputDir, 'teams');
await fs.mkdir(teamsOutputDir, { recursive: true });
const outputFile = path.join(teamsOutputDir, teamFile.replace('.yml', '.txt'));
await fs.writeFile(outputFile, bundle, 'utf8');
console.log(` ✓ Created bundle: ${path.relative(this.rootDir, outputFile)}`);
}
} else {
console.warn(` ⚠ No team configuration found in ${packName}/agent-teams/`);
}
} catch (error) {
console.warn(` ⚠ No agent-teams directory found for ${packName}`);
}
}
async buildExpansionAgentBundle(packName, packDir, agentName) {
const template = await fs.readFile(this.templatePath, 'utf8');
const sections = [template];
// Add agent configuration
const agentPath = path.join(packDir, 'agents', `${agentName}.md`);
const agentContent = await fs.readFile(agentPath, 'utf8');
sections.push(this.formatSection(`agents#${agentName}`, agentContent));
// Resolve and add agent dependencies from expansion pack
const agentYaml = agentContent.match(/```yaml\n([\s\S]*?)\n```/);
if (agentYaml) {
try {
const yaml = require('js-yaml');
const agentConfig = yaml.load(agentYaml[1]);
if (agentConfig.dependencies) {
// Add resources from expansion pack
for (const [resourceType, resources] of Object.entries(agentConfig.dependencies)) {
if (Array.isArray(resources)) {
for (const resourceName of resources) {
const resourcePath = path.join(packDir, resourceType, `${resourceName}.md`);
try {
const resourceContent = await fs.readFile(resourcePath, 'utf8');
sections.push(this.formatSection(`${resourceType}#${resourceName}`, resourceContent));
} catch (error) {
// Resource might not exist in expansion pack, that's ok
}
}
}
}
}
} catch (error) {
console.debug(`Failed to parse agent YAML for ${agentName}:`, error.message);
}
}
return sections.join('\n');
}
async buildExpansionTeamBundle(packName, packDir, teamConfigPath) {
const template = await fs.readFile(this.templatePath, 'utf8');
const sections = [template];
// Add team configuration
const teamContent = await fs.readFile(teamConfigPath, 'utf8');
const teamFileName = path.basename(teamConfigPath, '.yml');
sections.push(this.formatSection(`agent-teams#${teamFileName}`, teamContent));
// Add bmad-orchestrator (required for all teams)
const orchestratorPath = path.join(this.rootDir, 'bmad-core', 'agents', 'bmad-orchestrator.md');
const orchestratorContent = await fs.readFile(orchestratorPath, 'utf8');
sections.push(this.formatSection('agents#bmad-orchestrator', orchestratorContent));
// Add expansion pack agents
const agentsDir = path.join(packDir, 'agents');
try {
const agentFiles = await fs.readdir(agentsDir);
for (const agentFile of agentFiles.filter(f => f.endsWith('.md'))) {
const agentPath = path.join(agentsDir, agentFile);
const agentContent = await fs.readFile(agentPath, 'utf8');
const agentName = agentFile.replace('.md', '');
sections.push(this.formatSection(`agents#${agentName}`, agentContent));
}
} catch (error) {
console.warn(` ⚠ No agents directory found in ${packName}`);
}
// Add expansion pack resources (templates, tasks, checklists)
const resourceDirs = ['templates', 'tasks', 'checklists', 'workflows', 'data'];
for (const resourceDir of resourceDirs) {
const resourcePath = path.join(packDir, resourceDir);
try {
const resourceFiles = await fs.readdir(resourcePath);
for (const resourceFile of resourceFiles.filter(f => f.endsWith('.md') || f.endsWith('.yml'))) {
const filePath = path.join(resourcePath, resourceFile);
const fileContent = await fs.readFile(filePath, 'utf8');
const fileName = resourceFile.replace(/\.(md|yml)$/, '');
sections.push(this.formatSection(`${resourceDir}#${fileName}`, fileContent));
}
} catch (error) {
// Directory might not exist, that's fine
}
}
return sections.join('\n');
}
async listExpansionPacks() {
const expansionPacksDir = path.join(this.rootDir, 'expansion-packs');
try {
const entries = await fs.readdir(expansionPacksDir, { withFileTypes: true });
return entries
.filter(entry => entry.isDirectory())
.map(entry => entry.name);
} catch (error) {
console.warn('No expansion-packs directory found');
return [];
}
}
listAgents() {
return this.resolver.listAgents();
}

View File

@@ -18,6 +18,8 @@ program
.description('Build web bundles for agents and teams')
.option('-a, --agents-only', 'Build only agent bundles')
.option('-t, --teams-only', 'Build only team bundles')
.option('-e, --expansions-only', 'Build only expansion pack bundles')
.option('--no-expansions', 'Skip building expansion packs')
.option('--no-clean', 'Skip cleaning output directories')
.action(async (options) => {
const builder = new WebBuilder({
@@ -30,14 +32,24 @@ program
await builder.cleanOutputDirs();
}
if (!options.teamsOnly) {
console.log('Building agent bundles...');
await builder.buildAgents();
}
if (options.expansionsOnly) {
console.log('Building expansion pack bundles...');
await builder.buildAllExpansionPacks({ clean: false });
} else {
if (!options.teamsOnly) {
console.log('Building agent bundles...');
await builder.buildAgents();
}
if (!options.agentsOnly) {
console.log('Building team bundles...');
await builder.buildTeams();
if (!options.agentsOnly) {
console.log('Building team bundles...');
await builder.buildTeams();
}
if (!options.noExpansions) {
console.log('Building expansion pack bundles...');
await builder.buildAllExpansionPacks({ clean: false });
}
}
// Generate IDE configuration folders
@@ -62,6 +74,32 @@ program
}
});
program
.command('build:expansions')
.description('Build web bundles for all expansion packs')
.option('--expansion <name>', 'Build specific expansion pack only')
.option('--no-clean', 'Skip cleaning output directories')
.action(async (options) => {
const builder = new WebBuilder({
rootDir: process.cwd()
});
try {
if (options.expansion) {
console.log(`Building expansion pack: ${options.expansion}`);
await builder.buildExpansionPack(options.expansion, { clean: options.clean });
} else {
console.log('Building all expansion packs...');
await builder.buildAllExpansionPacks({ clean: options.clean });
}
console.log('Expansion pack build completed successfully!');
} catch (error) {
console.error('Expansion pack build failed:', error.message);
process.exit(1);
}
});
program
.command('list:agents')
.description('List all available agents')
@@ -72,6 +110,16 @@ program
agents.forEach(agent => console.log(` - ${agent}`));
});
program
.command('list:expansions')
.description('List all available expansion packs')
.action(async () => {
const builder = new WebBuilder({ rootDir: process.cwd() });
const expansions = await builder.listExpansionPacks();
console.log('Available expansion packs:');
expansions.forEach(expansion => console.log(` - ${expansion}`));
});
program
.command('validate')
.description('Validate agent and team configurations')

View File

@@ -47,7 +47,8 @@ program
.option('-f, --full', 'Install complete .bmad-core folder')
.option('-a, --agent <agent>', 'Install specific agent with dependencies')
.option('-d, --directory <path>', 'Installation directory (default: .bmad-core)')
.option('-i, --ide <ide...>', 'Configure for specific IDE(s) - can specify multiple (cursor, claude-code, windsurf, roo)')
.option('-i, --ide <ide...>', 'Configure for specific IDE(s) - can specify multiple (cursor, claude-code, windsurf, roo, other)')
.option('-e, --expansion-packs <packs...>', 'Install specific expansion packs (can specify multiple)')
.action(async (options) => {
try {
await initializeModules();
@@ -61,7 +62,8 @@ program
installType: options.full ? 'full' : 'single-agent',
agent: options.agent,
directory: options.directory || '.bmad-core',
ides: options.ide || []
ides: (options.ide || []).filter(ide => ide !== 'other'),
expansionPacks: options.expansionPacks || []
};
await installer.install(config);
}
@@ -100,6 +102,19 @@ program
}
});
program
.command('list:expansions')
.description('List available expansion packs')
.action(async () => {
try {
await installer.listExpansionPacks();
} catch (error) {
if (!chalk) await initializeModules();
console.error(chalk.red('Error:'), error.message);
process.exit(1);
}
});
program
.command('status')
.description('Show installation status')
@@ -167,6 +182,41 @@ async function promptInstallation() {
answers.agent = agent;
}
// Ask for expansion pack selection (only for full installation)
if (installType === 'full') {
try {
const availableExpansionPacks = await installer.getAvailableExpansionPacks();
if (availableExpansionPacks.length > 0) {
const { expansionPacks } = await inquirer.prompt([
{
type: 'checkbox',
name: 'expansionPacks',
message: 'Select expansion packs to install (optional):',
choices: [
{ name: 'BMAD Core only (no expansion packs)', value: 'none', checked: true },
new inquirer.Separator(' --- Expansion Packs ---'),
...availableExpansionPacks.map(pack => ({
name: `${pack.name} - ${pack.description}`,
value: pack.id
}))
]
}
]);
// Filter out 'none' selection and only include actual expansion packs
answers.expansionPacks = expansionPacks.filter(pack => pack !== 'none');
} else {
answers.expansionPacks = [];
}
} catch (error) {
console.warn(chalk.yellow('Warning: Could not load expansion packs. Continuing without them.'));
answers.expansionPacks = [];
}
} else {
answers.expansionPacks = [];
}
// Ask for IDE configuration
const { ides } = await inquirer.prompt([
{
@@ -177,17 +227,20 @@ async function promptInstallation() {
{ name: 'Cursor', value: 'cursor' },
{ name: 'Claude Code', value: 'claude-code' },
{ name: 'Windsurf', value: 'windsurf' },
{ name: 'Roo Code', value: 'roo' }
{ name: 'Roo Code', value: 'roo' },
{ name: 'Other (skip IDE setup)', value: 'other' }
],
validate: (answer) => {
if (answer.length < 1) {
return 'You must choose at least one IDE, or press Ctrl+C to skip IDE setup.';
return 'You must choose at least one IDE option.';
}
return true;
}
}
]);
answers.ides = ides;
// Filter out 'other' from the list and only include actual IDEs
answers.ides = ides.filter(ide => ide !== 'other');
return answers;
}

View File

@@ -3,55 +3,55 @@ installation-options:
name: Complete BMAD Core
description: Copy the entire .bmad-core folder with all agents, templates, and tools
action: copy-folder
source: .bmad-core
source: bmad-core
single-agent:
name: Single Agent
description: Select and install a single agent with its dependencies
action: copy-agent
agent-dependencies:
core-files:
- .bmad-core/utils/template-format.md
- bmad-core/utils/template-format.md
dev:
- .bmad-core/templates/story-tmpl.md
- .bmad-core/checklists/story-dod-checklist.md
- bmad-core/templates/story-tmpl.md
- bmad-core/checklists/story-dod-checklist.md
pm:
- .bmad-core/templates/prd-tmpl.md
- .bmad-core/templates/brownfield-prd-tmpl.md
- .bmad-core/checklists/pm-checklist.md
- .bmad-core/checklists/change-checklist.md
- .bmad-core/tasks/advanced-elicitation.md
- .bmad-core/tasks/create-doc.md
- .bmad-core/tasks/correct-course.md
- .bmad-core/tasks/create-deep-research-prompt.md
- .bmad-core/tasks/brownfield-create-epic.md
- .bmad-core/tasks/brownfield-create-story.md
- .bmad-core/tasks/execute-checklist.md
- .bmad-core/tasks/shard-doc.md
- bmad-core/templates/prd-tmpl.md
- bmad-core/templates/brownfield-prd-tmpl.md
- bmad-core/checklists/pm-checklist.md
- bmad-core/checklists/change-checklist.md
- bmad-core/tasks/advanced-elicitation.md
- bmad-core/tasks/create-doc.md
- bmad-core/tasks/correct-course.md
- bmad-core/tasks/create-deep-research-prompt.md
- bmad-core/tasks/brownfield-create-epic.md
- bmad-core/tasks/brownfield-create-story.md
- bmad-core/tasks/execute-checklist.md
- bmad-core/tasks/shard-doc.md
architect:
- .bmad-core/templates/architecture-tmpl.md
- .bmad-core/checklists/architect-checklist.md
- bmad-core/templates/architecture-tmpl.md
- bmad-core/checklists/architect-checklist.md
sm:
- .bmad-core/templates/story-tmpl.md
- .bmad-core/checklists/story-draft-checklist.md
- .bmad-core/workflows/*.yml
- bmad-core/templates/story-tmpl.md
- bmad-core/checklists/story-draft-checklist.md
- bmad-core/workflows/*.yml
po:
- .bmad-core/checklists/po-master-checklist.md
- .bmad-core/templates/acceptance-criteria-tmpl.md
- bmad-core/checklists/po-master-checklist.md
- bmad-core/templates/acceptance-criteria-tmpl.md
analyst:
- .bmad-core/templates/prd-tmpl.md
- .bmad-core/tasks/advanced-elicitation.md
- bmad-core/templates/prd-tmpl.md
- bmad-core/tasks/advanced-elicitation.md
qa:
- .bmad-core/checklists/story-dod-checklist.md
- .bmad-core/templates/test-plan-tmpl.md
- bmad-core/checklists/story-dod-checklist.md
- bmad-core/templates/test-plan-tmpl.md
ux-expert:
- .bmad-core/templates/ux-tmpl.md
- bmad-core/templates/ux-tmpl.md
bmad-master:
- .bmad-core/templates/*.md
- .bmad-core/tasks/*.md
- .bmad-core/schemas/*.yml
- bmad-core/templates/*.md
- bmad-core/tasks/*.md
- bmad-core/schemas/*.yml
bmad-orchestrator:
- .bmad-core/agent-teams/*.yml
- .bmad-core/workflows/*.yml
- bmad-core/agent-teams/*.yml
- bmad-core/workflows/*.yml
ide-configurations:
cursor:
name: Cursor
@@ -99,41 +99,41 @@ ide-configurations:
available-agents:
- id: analyst
name: Business Analyst
file: .bmad-core/agents/analyst.md
file: bmad-core/agents/analyst.md
description: Requirements gathering and analysis
- id: pm
name: Product Manager
file: .bmad-core/agents/pm.md
file: bmad-core/agents/pm.md
description: Product strategy and roadmap planning
- id: architect
name: Solution Architect
file: .bmad-core/agents/architect.md
file: bmad-core/agents/architect.md
description: Technical design and architecture
- id: po
name: Product Owner
file: .bmad-core/agents/po.md
file: bmad-core/agents/po.md
description: Backlog management and prioritization
- id: sm
name: Scrum Master
file: .bmad-core/agents/sm.md
file: bmad-core/agents/sm.md
description: Agile process and story creation
- id: dev
name: Developer
file: .bmad-core/agents/dev.md
file: bmad-core/agents/dev.md
description: Code implementation and testing
- id: qa
name: QA Engineer
file: .bmad-core/agents/qa.md
file: bmad-core/agents/qa.md
description: Quality assurance and testing
- id: ux-expert
name: UX Expert
file: .bmad-core/agents/ux-expert.md
file: bmad-core/agents/ux-expert.md
description: User experience design
- id: bmad-master
name: BMAD Master
file: .bmad-core/agents/bmad-master.md
file: bmad-core/agents/bmad-master.md
description: BMAD framework expert and guide
- id: bmad-orchestrator
name: BMAD Orchestrator
file: .bmad-core/agents/bmad-orchestrator.md
file: bmad-core/agents/bmad-orchestrator.md
description: Multi-agent workflow coordinator

View File

@@ -30,6 +30,43 @@ class ConfigLoader {
return config['available-agents'] || [];
}
async getAvailableExpansionPacks() {
const expansionPacksDir = path.join(this.getBmadCorePath(), '..', 'expansion-packs');
try {
const entries = await fs.readdir(expansionPacksDir, { withFileTypes: true });
const expansionPacks = [];
for (const entry of entries) {
if (entry.isDirectory()) {
const manifestPath = path.join(expansionPacksDir, entry.name, 'manifest.yml');
try {
const manifestContent = await fs.readFile(manifestPath, 'utf8');
const manifest = yaml.load(manifestContent);
expansionPacks.push({
id: entry.name,
name: manifest.name || entry.name,
description: manifest.description || 'No description available',
version: manifest.version || '1.0.0',
author: manifest.author || 'Unknown',
manifestPath: manifestPath,
dependencies: manifest.dependencies || []
});
} catch (error) {
console.warn(`Failed to read manifest for expansion pack ${entry.name}: ${error.message}`);
}
}
}
return expansionPacks;
} catch (error) {
console.warn(`Failed to read expansion packs directory: ${error.message}`);
return [];
}
}
async getAgentDependencies(agentId) {
// Use DependencyResolver to dynamically parse agent dependencies
const DependencyResolver = require('../../lib/dependency-resolver');
@@ -77,8 +114,8 @@ class ConfigLoader {
}
getBmadCorePath() {
// Get the path to .bmad-core relative to the installer (now under tools)
return path.join(__dirname, '..', '..', '..', '.bmad-core');
// Get the path to bmad-core relative to the installer (now under tools)
return path.join(__dirname, '..', '..', '..', 'bmad-core');
}
getAgentPath(agentId) {

View File

@@ -261,6 +261,10 @@ class Installer {
}
}
// Install expansion packs if requested
const expansionFiles = await this.installExpansionPacks(installDir, config.expansionPacks, spinner);
files.push(...expansionFiles);
// Set up IDE integration if requested
const ides = config.ides || (config.ide ? [config.ide] : []);
if (ides.length > 0) {
@@ -505,6 +509,11 @@ class Installer {
console.log(chalk.bold("\n🎯 Installation Summary:"));
console.log(chalk.green("✓ .bmad-core framework installed with all agents and workflows"));
if (config.expansionPacks && config.expansionPacks.length > 0) {
const packNames = config.expansionPacks.join(", ");
console.log(chalk.green(`✓ Expansion packs installed: ${packNames}`));
}
if (ides.length > 0) {
const ideNames = ides.map(ide => {
const ideConfig = configLoader.getIdeConfiguration(ide);
@@ -569,6 +578,33 @@ class Installer {
);
}
async listExpansionPacks() {
// Initialize ES modules
await initializeModules();
const expansionPacks = await this.getAvailableExpansionPacks();
console.log(chalk.bold("\nAvailable BMAD Expansion Packs:\n"));
if (expansionPacks.length === 0) {
console.log(chalk.yellow("No expansion packs found."));
return;
}
for (const pack of expansionPacks) {
console.log(chalk.cyan(` ${pack.id.padEnd(20)}`),
`${pack.name} v${pack.version}`);
console.log(chalk.dim(` ${' '.repeat(22)}${pack.description}`));
if (pack.author && pack.author !== 'Unknown') {
console.log(chalk.dim(` ${' '.repeat(22)}by ${pack.author}`));
}
console.log();
}
console.log(
chalk.dim("Install with: npx bmad-method install --full --expansion-packs <id>\n")
);
}
async showStatus() {
// Initialize ES modules
await initializeModules();
@@ -624,6 +660,96 @@ class Installer {
return configLoader.getAvailableAgents();
}
async getAvailableExpansionPacks() {
return configLoader.getAvailableExpansionPacks();
}
async installExpansionPacks(installDir, selectedPacks, spinner) {
if (!selectedPacks || selectedPacks.length === 0) {
return [];
}
const installedFiles = [];
const glob = require('glob');
for (const packId of selectedPacks) {
spinner.text = `Installing expansion pack: ${packId}...`;
try {
const expansionPacks = await this.getAvailableExpansionPacks();
const pack = expansionPacks.find(p => p.id === packId);
if (!pack) {
console.warn(`Expansion pack ${packId} not found, skipping...`);
continue;
}
const expansionPackDir = path.dirname(pack.manifestPath);
// Define the folders to copy from expansion packs to .bmad-core
const foldersToSync = [
'agents',
'agent-teams',
'templates',
'tasks',
'checklists',
'workflows',
'data',
'utils',
'schemas'
];
// Copy each folder if it exists
for (const folder of foldersToSync) {
const sourceFolder = path.join(expansionPackDir, folder);
// Check if folder exists in expansion pack
if (await fileManager.pathExists(sourceFolder)) {
// Get all files in this folder
const files = glob.sync('**/*', {
cwd: sourceFolder,
nodir: true
});
// Copy each file to the destination
for (const file of files) {
const sourcePath = path.join(sourceFolder, file);
const destPath = path.join(installDir, '.bmad-core', folder, file);
if (await fileManager.copyFile(sourcePath, destPath)) {
installedFiles.push(path.join('.bmad-core', folder, file));
}
}
}
}
// Also copy web-bundles if they exist (to a different location)
const webBundlesSource = path.join(expansionPackDir, 'web-bundles');
if (await fileManager.pathExists(webBundlesSource)) {
const files = glob.sync('**/*', {
cwd: webBundlesSource,
nodir: true
});
for (const file of files) {
const sourcePath = path.join(webBundlesSource, file);
const destPath = path.join(installDir, '.bmad-core', 'web-bundles', 'expansion-packs', packId, file);
if (await fileManager.copyFile(sourcePath, destPath)) {
installedFiles.push(path.join('.bmad-core', 'web-bundles', 'expansion-packs', packId, file));
}
}
}
console.log(chalk.green(`✓ Installed expansion pack: ${pack.name}`));
} catch (error) {
console.error(chalk.red(`Failed to install expansion pack ${packId}: ${error.message}`));
}
}
return installedFiles;
}
async findInstallation() {
// Look for .bmad-core in current directory or parent directories
let currentDir = process.cwd();

View File

@@ -1,12 +1,12 @@
{
"name": "bmad-method",
"version": "4.0.1",
"version": "4.3.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "bmad-method",
"version": "4.0.1",
"version": "4.3.0",
"license": "MIT",
"dependencies": {
"chalk": "^5.4.1",
@@ -21,7 +21,7 @@
"bmad-method": "bin/bmad.js"
},
"engines": {
"node": ">=14.0.0"
"node": ">=20.0.0"
}
},
"node_modules/@inquirer/checkbox": {

View File

@@ -30,7 +30,7 @@
"ora": "^8.2.0"
},
"engines": {
"node": ">=14.0.0"
"node": ">=20.0.0"
},
"repository": {
"type": "git",

View File

@@ -5,7 +5,7 @@ const yaml = require('js-yaml');
class DependencyResolver {
constructor(rootDir) {
this.rootDir = rootDir;
this.bmadCore = path.join(rootDir, '.bmad-core');
this.bmadCore = path.join(rootDir, 'bmad-core');
this.cache = new Map();
}