mirror of
https://github.com/bmadcode/BMAD-METHOD.git
synced 2025-12-29 16:14:59 +00:00
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:
@@ -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();
|
||||
}
|
||||
|
||||
62
tools/cli.js
62
tools/cli.js
@@ -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')
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
6
tools/installer/package-lock.json
generated
6
tools/installer/package-lock.json
generated
@@ -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": {
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
"ora": "^8.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
"node": ">=20.0.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user