fix: single agent install and team installation support

This commit is contained in:
Brian Madison
2025-06-16 21:26:32 -05:00
parent 449e42440a
commit 18a382baa4
7 changed files with 224 additions and 25 deletions

View File

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

View File

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