fix: single agent install and team installation support
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
bundle:
|
||||
name: Team All
|
||||
icon: 👥
|
||||
description: This is a full organization of agents and includes every possible agent. This will produce the larges bundle but give the most options for discussion in a single session
|
||||
description: Includes every core system agent.
|
||||
agents:
|
||||
- bmad-orchestrator
|
||||
- '*'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
bundle:
|
||||
name: Team Fullstack
|
||||
icon: 🚀
|
||||
description: Comprehensive full-stack development team capable of handling both greenfield application development and brownfield enhancement projects. This team combines strategic planning, user experience design, and holistic system architecture to deliver complete solutions from concept to deployment. Specializes in full-stack applications, SaaS platforms, enterprise apps, feature additions, refactoring, and system modernization.
|
||||
description: Team capable of full stack, front end only, or service development.
|
||||
agents:
|
||||
- bmad-orchestrator
|
||||
- analyst
|
||||
|
||||
10
bmad-core/agent-teams/team-ide-minimal.yml
Normal file
10
bmad-core/agent-teams/team-ide-minimal.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
bundle:
|
||||
name: Team IDE Minimal
|
||||
icon: ⚡
|
||||
description: Only the bare minimum for the IDE PO SM dev qa cycle.
|
||||
agents:
|
||||
- po
|
||||
- sm
|
||||
- dev
|
||||
- qa
|
||||
workflows: null
|
||||
@@ -1,7 +1,7 @@
|
||||
bundle:
|
||||
name: Team No UI
|
||||
icon: 🔧
|
||||
description: This is a team that is responsible for planning the project without any UI/UX design. This is for projects that do not require UI/UX design.
|
||||
description: Team with no UX or UI Planning.
|
||||
agents:
|
||||
- bmad-orchestrator
|
||||
- analyst
|
||||
|
||||
@@ -46,21 +46,29 @@ program
|
||||
.description('Install BMAD Method agents and tools')
|
||||
.option('-f, --full', 'Install complete .bmad-core folder')
|
||||
.option('-a, --agent <agent>', 'Install specific agent with dependencies')
|
||||
.option('-t, --team <team>', 'Install specific team with required agents and dependencies')
|
||||
.option('-x, --expansion-only', 'Install only expansion packs (no bmad-core)')
|
||||
.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, other)')
|
||||
.option('-e, --expansion-packs <packs...>', 'Install specific expansion packs (can specify multiple)')
|
||||
.action(async (options) => {
|
||||
try {
|
||||
await initializeModules();
|
||||
if (!options.full && !options.agent) {
|
||||
if (!options.full && !options.agent && !options.team && !options.expansionOnly) {
|
||||
// Interactive mode
|
||||
const answers = await promptInstallation();
|
||||
await installer.install(answers);
|
||||
} else {
|
||||
// Direct mode
|
||||
let installType = 'full';
|
||||
if (options.agent) installType = 'single-agent';
|
||||
else if (options.team) installType = 'team';
|
||||
else if (options.expansionOnly) installType = 'expansion-only';
|
||||
|
||||
const config = {
|
||||
installType: options.full ? 'full' : 'single-agent',
|
||||
installType,
|
||||
agent: options.agent,
|
||||
team: options.team,
|
||||
directory: options.directory || '.bmad-core',
|
||||
ides: (options.ide || []).filter(ide => ide !== 'other'),
|
||||
expansionPacks: options.expansionPacks || []
|
||||
@@ -161,9 +169,17 @@ async function promptInstallation() {
|
||||
name: 'Complete installation (recommended) - All agents and tools',
|
||||
value: 'full'
|
||||
},
|
||||
{
|
||||
name: 'Team installation - Install a specific team with required agents',
|
||||
value: 'team'
|
||||
},
|
||||
{
|
||||
name: 'Single agent - Choose one agent to install',
|
||||
value: 'single-agent'
|
||||
},
|
||||
{
|
||||
name: 'Expansion packs only - Install expansion packs without bmad-core',
|
||||
value: 'expansion-only'
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -187,25 +203,62 @@ async function promptInstallation() {
|
||||
answers.agent = agent;
|
||||
}
|
||||
|
||||
// Ask for expansion pack selection (only for full installation)
|
||||
if (installType === 'full') {
|
||||
// If team installation, ask which team
|
||||
if (installType === 'team') {
|
||||
const teams = await installer.getAvailableTeams();
|
||||
const { team } = await inquirer.prompt([
|
||||
{
|
||||
type: 'list',
|
||||
name: 'team',
|
||||
message: 'Select a team to install:',
|
||||
choices: teams.map(t => ({
|
||||
name: `${t.icon || '📋'} ${t.name}: ${t.description}`,
|
||||
value: t.id
|
||||
}))
|
||||
}
|
||||
]);
|
||||
answers.team = team;
|
||||
}
|
||||
|
||||
// Ask for expansion pack selection
|
||||
if (installType === 'full' || installType === 'team' || installType === 'expansion-only') {
|
||||
try {
|
||||
const availableExpansionPacks = await installer.getAvailableExpansionPacks();
|
||||
|
||||
if (availableExpansionPacks.length > 0) {
|
||||
let choices;
|
||||
let message;
|
||||
|
||||
if (installType === 'expansion-only') {
|
||||
message = 'Select expansion packs to install (required):'
|
||||
choices = availableExpansionPacks.map(pack => ({
|
||||
name: `${pack.name} - ${pack.description}`,
|
||||
value: pack.id
|
||||
}));
|
||||
} else {
|
||||
message = 'Select expansion packs to install (optional):';
|
||||
choices = [
|
||||
{ name: 'Skip expansion packs', value: 'none', checked: true },
|
||||
new inquirer.Separator(' --- Expansion Packs ---'),
|
||||
...availableExpansionPacks.map(pack => ({
|
||||
name: `${pack.name} - ${pack.description}`,
|
||||
value: pack.id
|
||||
}))
|
||||
];
|
||||
}
|
||||
|
||||
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
|
||||
}))
|
||||
]
|
||||
message,
|
||||
choices,
|
||||
validate: installType === 'expansion-only' ? (answer) => {
|
||||
if (answer.length < 1) {
|
||||
return 'You must select at least one expansion pack for expansion-only installation.';
|
||||
}
|
||||
return true;
|
||||
} : undefined
|
||||
}
|
||||
]);
|
||||
|
||||
|
||||
@@ -78,10 +78,7 @@ class ConfigLoader {
|
||||
// Convert to flat list of file paths
|
||||
const depPaths = [];
|
||||
|
||||
// Add core files
|
||||
const config = await this.load();
|
||||
const coreFiles = config['agent-dependencies']?.['core-files'] || [];
|
||||
depPaths.push(...coreFiles);
|
||||
// Core files and utilities are included automatically by DependencyResolver
|
||||
|
||||
// Add agent file itself is already handled by installer
|
||||
|
||||
@@ -121,6 +118,82 @@ class ConfigLoader {
|
||||
getAgentPath(agentId) {
|
||||
return path.join(this.getBmadCorePath(), 'agents', `${agentId}.md`);
|
||||
}
|
||||
|
||||
async getAvailableTeams() {
|
||||
const teamsDir = path.join(this.getBmadCorePath(), 'agent-teams');
|
||||
|
||||
try {
|
||||
const entries = await fs.readdir(teamsDir, { withFileTypes: true });
|
||||
const teams = [];
|
||||
|
||||
for (const entry of entries) {
|
||||
if (entry.isFile() && entry.name.endsWith('.yml')) {
|
||||
const teamPath = path.join(teamsDir, entry.name);
|
||||
|
||||
try {
|
||||
const teamContent = await fs.readFile(teamPath, 'utf8');
|
||||
const teamConfig = yaml.load(teamContent);
|
||||
|
||||
if (teamConfig.bundle) {
|
||||
teams.push({
|
||||
id: path.basename(entry.name, '.yml'),
|
||||
name: teamConfig.bundle.name || entry.name,
|
||||
description: teamConfig.bundle.description || 'Team configuration',
|
||||
icon: teamConfig.bundle.icon || '📋'
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`Warning: Could not load team config ${entry.name}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return teams;
|
||||
} catch (error) {
|
||||
console.warn(`Warning: Could not scan teams directory: ${error.message}`);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
getTeamPath(teamId) {
|
||||
return path.join(this.getBmadCorePath(), 'agent-teams', `${teamId}.yml`);
|
||||
}
|
||||
|
||||
async getTeamDependencies(teamId) {
|
||||
// Use DependencyResolver to dynamically parse team dependencies
|
||||
const DependencyResolver = require('../../lib/dependency-resolver');
|
||||
const resolver = new DependencyResolver(path.join(__dirname, '..', '..', '..'));
|
||||
|
||||
try {
|
||||
const teamDeps = await resolver.resolveTeamDependencies(teamId);
|
||||
|
||||
// Convert to flat list of file paths
|
||||
const depPaths = [];
|
||||
|
||||
// Add team config file
|
||||
depPaths.push(`.bmad-core/agent-teams/${teamId}.yml`);
|
||||
|
||||
// Add all agents
|
||||
for (const agent of teamDeps.agents) {
|
||||
const filePath = `.bmad-core/agents/${agent.id}.md`;
|
||||
if (!depPaths.includes(filePath)) {
|
||||
depPaths.push(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
// Add all resolved resources
|
||||
for (const resource of teamDeps.resources) {
|
||||
const filePath = `.bmad-core/${resource.type}/${resource.id}.${resource.type === 'workflows' ? 'yml' : 'md'}`;
|
||||
if (!depPaths.includes(filePath)) {
|
||||
depPaths.push(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
return depPaths;
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to resolve team dependencies for ${teamId}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new ConfigLoader();
|
||||
@@ -220,11 +220,12 @@ class Installer {
|
||||
const agentPath = configLoader.getAgentPath(config.agent);
|
||||
const destAgentPath = path.join(
|
||||
installDir,
|
||||
".bmad-core",
|
||||
"agents",
|
||||
`${config.agent}.md`
|
||||
);
|
||||
await fileManager.copyFile(agentPath, destAgentPath);
|
||||
files.push(`agents/${config.agent}.md`);
|
||||
files.push(`.bmad-core/agents/${config.agent}.md`);
|
||||
|
||||
// Copy dependencies
|
||||
const dependencies = await configLoader.getAgentDependencies(
|
||||
@@ -240,9 +241,9 @@ class Installer {
|
||||
const copiedFiles = await fileManager.copyGlobPattern(
|
||||
dep.replace(".bmad-core/", ""),
|
||||
sourceBase,
|
||||
installDir
|
||||
path.join(installDir, ".bmad-core")
|
||||
);
|
||||
files.push(...copiedFiles);
|
||||
files.push(...copiedFiles.map(f => `.bmad-core/${f}`));
|
||||
} else {
|
||||
// Handle single files
|
||||
const sourcePath = path.join(
|
||||
@@ -251,14 +252,72 @@ class Installer {
|
||||
);
|
||||
const destPath = path.join(
|
||||
installDir,
|
||||
dep.replace(".bmad-core/", "")
|
||||
dep
|
||||
);
|
||||
|
||||
if (await fileManager.copyFile(sourcePath, destPath)) {
|
||||
files.push(dep.replace(".bmad-core/", ""));
|
||||
files.push(dep);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (config.installType === "team") {
|
||||
// Team installation
|
||||
spinner.text = `Installing ${config.team} team...`;
|
||||
|
||||
// Get team dependencies
|
||||
const teamDependencies = await configLoader.getTeamDependencies(config.team);
|
||||
const sourceBase = configLoader.getBmadCorePath();
|
||||
|
||||
// Install all team dependencies
|
||||
for (const dep of teamDependencies) {
|
||||
spinner.text = `Copying team dependency: ${dep}`;
|
||||
|
||||
if (dep.includes("*")) {
|
||||
// Handle glob patterns
|
||||
const copiedFiles = await fileManager.copyGlobPattern(
|
||||
dep.replace(".bmad-core/", ""),
|
||||
sourceBase,
|
||||
path.join(installDir, ".bmad-core")
|
||||
);
|
||||
files.push(...copiedFiles.map(f => `.bmad-core/${f}`));
|
||||
} else {
|
||||
// Handle single files
|
||||
const sourcePath = path.join(sourceBase, dep.replace(".bmad-core/", ""));
|
||||
const destPath = path.join(installDir, dep);
|
||||
|
||||
if (await fileManager.copyFile(sourcePath, destPath)) {
|
||||
files.push(dep);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (config.installType === "expansion-only") {
|
||||
// Expansion-only installation - create minimal .bmad-core structure
|
||||
spinner.text = "Creating minimal .bmad-core structure for expansion packs...";
|
||||
|
||||
const bmadCoreDestDir = path.join(installDir, ".bmad-core");
|
||||
await fileManager.ensureDirectory(bmadCoreDestDir);
|
||||
|
||||
// Create basic directory structure
|
||||
const dirs = ['agents', 'agent-teams', 'templates', 'tasks', 'checklists', 'workflows', 'data', 'utils', 'schemas'];
|
||||
for (const dir of dirs) {
|
||||
await fileManager.ensureDirectory(path.join(bmadCoreDestDir, dir));
|
||||
}
|
||||
|
||||
// Copy minimal required files (schemas, utils, etc.)
|
||||
const sourceBase = configLoader.getBmadCorePath();
|
||||
const essentialFiles = [
|
||||
'schemas/**/*',
|
||||
'utils/**/*'
|
||||
];
|
||||
|
||||
for (const pattern of essentialFiles) {
|
||||
const copiedFiles = await fileManager.copyGlobPattern(
|
||||
pattern,
|
||||
sourceBase,
|
||||
bmadCoreDestDir
|
||||
);
|
||||
files.push(...copiedFiles.map(f => `.bmad-core/${f}`));
|
||||
}
|
||||
}
|
||||
|
||||
// Install expansion packs if requested
|
||||
@@ -664,6 +723,10 @@ class Installer {
|
||||
return configLoader.getAvailableExpansionPacks();
|
||||
}
|
||||
|
||||
async getAvailableTeams() {
|
||||
return configLoader.getAvailableTeams();
|
||||
}
|
||||
|
||||
async installExpansionPacks(installDir, selectedPacks, spinner) {
|
||||
if (!selectedPacks || selectedPacks.length === 0) {
|
||||
return [];
|
||||
|
||||
Reference in New Issue
Block a user