feat: remove hardcoding from installer for agents, improve expansion pack installation to its own locations, common files moved to common folder
This commit is contained in:
@@ -8,50 +8,6 @@ installation-options:
|
||||
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
|
||||
dev:
|
||||
- common/templates/story-tmpl.md
|
||||
- common/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
|
||||
architect:
|
||||
- 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
|
||||
po:
|
||||
- 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
|
||||
qa:
|
||||
- bmad-core/checklists/story-dod-checklist.md
|
||||
- bmad-core/templates/test-plan-tmpl.md
|
||||
ux-expert:
|
||||
- bmad-core/templates/ux-tmpl.md
|
||||
bmad-master:
|
||||
- bmad-core/templates/*.md
|
||||
- bmad-core/tasks/*.md
|
||||
- bmad-core/schemas/*.yml
|
||||
bmad-orchestrator:
|
||||
- bmad-core/agent-teams/*.yml
|
||||
- bmad-core/workflows/*.yml
|
||||
ide-configurations:
|
||||
cursor:
|
||||
name: Cursor
|
||||
@@ -111,44 +67,3 @@ ide-configurations:
|
||||
# 2. It also configures .gemini/settings.json to load all agent files.
|
||||
# 3. Simply mention the agent in your prompt (e.g., "As @dev, ...").
|
||||
# 4. The Gemini CLI will automatically have the context for that agent.
|
||||
available-agents:
|
||||
- id: analyst
|
||||
name: Business Analyst
|
||||
file: bmad-core/agents/analyst.md
|
||||
description: Requirements gathering and analysis
|
||||
- id: pm
|
||||
name: Product Manager
|
||||
file: bmad-core/agents/pm.md
|
||||
description: Product strategy and roadmap planning
|
||||
- id: architect
|
||||
name: Solution Architect
|
||||
file: bmad-core/agents/architect.md
|
||||
description: Technical design and architecture
|
||||
- id: po
|
||||
name: Product Owner
|
||||
file: bmad-core/agents/po.md
|
||||
description: Backlog management and prioritization
|
||||
- id: sm
|
||||
name: Scrum Master
|
||||
file: bmad-core/agents/sm.md
|
||||
description: Agile process and story creation
|
||||
- id: dev
|
||||
name: Developer
|
||||
file: bmad-core/agents/dev.md
|
||||
description: Code implementation and testing
|
||||
- id: qa
|
||||
name: QA Engineer
|
||||
file: bmad-core/agents/qa.md
|
||||
description: Quality assurance and testing
|
||||
- id: ux-expert
|
||||
name: UX Expert
|
||||
file: bmad-core/agents/ux-expert.md
|
||||
description: User experience design
|
||||
- id: bmad-master
|
||||
name: BMAD Master
|
||||
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
|
||||
description: Multi-agent workflow coordinator
|
||||
|
||||
@@ -26,8 +26,47 @@ class ConfigLoader {
|
||||
}
|
||||
|
||||
async getAvailableAgents() {
|
||||
const config = await this.load();
|
||||
return config['available-agents'] || [];
|
||||
const agentsDir = path.join(this.getBmadCorePath(), 'agents');
|
||||
|
||||
try {
|
||||
const entries = await fs.readdir(agentsDir, { withFileTypes: true });
|
||||
const agents = [];
|
||||
|
||||
for (const entry of entries) {
|
||||
if (entry.isFile() && entry.name.endsWith('.md')) {
|
||||
const agentPath = path.join(agentsDir, entry.name);
|
||||
const agentId = path.basename(entry.name, '.md');
|
||||
|
||||
try {
|
||||
const agentContent = await fs.readFile(agentPath, 'utf8');
|
||||
|
||||
// Extract YAML block from agent file
|
||||
const yamlMatch = agentContent.match(/```yml\n([\s\S]*?)\n```/);
|
||||
if (yamlMatch) {
|
||||
const yamlContent = yaml.load(yamlMatch[1]);
|
||||
const agentConfig = yamlContent.agent || {};
|
||||
|
||||
agents.push({
|
||||
id: agentId,
|
||||
name: agentConfig.title || agentConfig.name || agentId,
|
||||
file: `bmad-core/agents/${entry.name}`,
|
||||
description: agentConfig.whenToUse || 'No description available'
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`Failed to read agent ${entry.name}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort agents by name for consistent display
|
||||
agents.sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
return agents;
|
||||
} catch (error) {
|
||||
console.warn(`Failed to read agents directory: ${error.message}`);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async getAvailableExpansionPacks() {
|
||||
@@ -72,36 +111,24 @@ class ConfigLoader {
|
||||
const DependencyResolver = require('../../lib/dependency-resolver');
|
||||
const resolver = new DependencyResolver(path.join(__dirname, '..', '..', '..'));
|
||||
|
||||
try {
|
||||
const agentDeps = await resolver.resolveAgentDependencies(agentId);
|
||||
|
||||
// Convert to flat list of file paths
|
||||
const depPaths = [];
|
||||
|
||||
// Core files and utilities are included automatically by DependencyResolver
|
||||
|
||||
// Add agent file itself is already handled by installer
|
||||
|
||||
// Add all resolved resources
|
||||
for (const resource of agentDeps.resources) {
|
||||
const filePath = `.bmad-core/${resource.type}/${resource.id}.md`;
|
||||
if (!depPaths.includes(filePath)) {
|
||||
depPaths.push(filePath);
|
||||
}
|
||||
const agentDeps = await resolver.resolveAgentDependencies(agentId);
|
||||
|
||||
// Convert to flat list of file paths
|
||||
const depPaths = [];
|
||||
|
||||
// Core files and utilities are included automatically by DependencyResolver
|
||||
|
||||
// Add agent file itself is already handled by installer
|
||||
|
||||
// Add all resolved resources
|
||||
for (const resource of agentDeps.resources) {
|
||||
const filePath = `.bmad-core/${resource.type}/${resource.id}.md`;
|
||||
if (!depPaths.includes(filePath)) {
|
||||
depPaths.push(filePath);
|
||||
}
|
||||
|
||||
return depPaths;
|
||||
} catch (error) {
|
||||
console.warn(`Failed to dynamically resolve dependencies for ${agentId}: ${error.message}`);
|
||||
|
||||
// Fall back to static config
|
||||
const config = await this.load();
|
||||
const dependencies = config['agent-dependencies'] || {};
|
||||
const coreFiles = dependencies['core-files'] || [];
|
||||
const agentDeps = dependencies[agentId] || [];
|
||||
|
||||
return [...coreFiles, ...agentDeps];
|
||||
}
|
||||
|
||||
return depPaths;
|
||||
}
|
||||
|
||||
async getIdeConfiguration(ide) {
|
||||
|
||||
@@ -65,8 +65,9 @@ class IdeSetup {
|
||||
mdcContent += "alwaysApply: false\n";
|
||||
mdcContent += "---\n\n";
|
||||
mdcContent += `# ${agentId.toUpperCase()} Agent Rule\n\n`;
|
||||
mdcContent += `This rule is triggered when the user types \`@${agentId}\` and activates the ${this.getAgentTitle(
|
||||
agentId
|
||||
mdcContent += `This rule is triggered when the user types \`@${agentId}\` and activates the ${await this.getAgentTitle(
|
||||
agentId,
|
||||
installDir
|
||||
)} agent persona.\n\n`;
|
||||
mdcContent += "## Agent Activation\n\n";
|
||||
mdcContent +=
|
||||
@@ -84,8 +85,9 @@ class IdeSetup {
|
||||
mdcContent += "## File Reference\n\n";
|
||||
mdcContent += `The complete agent definition is available in [.bmad-core/agents/${agentId}.md](mdc:.bmad-core/agents/${agentId}.md).\n\n`;
|
||||
mdcContent += "## Usage\n\n";
|
||||
mdcContent += `When the user types \`@${agentId}\`, activate this ${this.getAgentTitle(
|
||||
agentId
|
||||
mdcContent += `When the user types \`@${agentId}\`, activate this ${await this.getAgentTitle(
|
||||
agentId,
|
||||
installDir
|
||||
)} persona and follow all instructions defined in the YML configuration above.\n`;
|
||||
|
||||
await fileManager.writeFile(mdcPath, mdcContent);
|
||||
@@ -150,8 +152,9 @@ class IdeSetup {
|
||||
|
||||
// Create MD content (similar to Cursor but without frontmatter)
|
||||
let mdContent = `# ${agentId.toUpperCase()} Agent Rule\n\n`;
|
||||
mdContent += `This rule is triggered when the user types \`@${agentId}\` and activates the ${this.getAgentTitle(
|
||||
agentId
|
||||
mdContent += `This rule is triggered when the user types \`@${agentId}\` and activates the ${await this.getAgentTitle(
|
||||
agentId,
|
||||
installDir
|
||||
)} agent persona.\n\n`;
|
||||
mdContent += "## Agent Activation\n\n";
|
||||
mdContent +=
|
||||
@@ -169,8 +172,9 @@ class IdeSetup {
|
||||
mdContent += "## File Reference\n\n";
|
||||
mdContent += `The complete agent definition is available in [.bmad-core/agents/${agentId}.md](.bmad-core/agents/${agentId}.md).\n\n`;
|
||||
mdContent += "## Usage\n\n";
|
||||
mdContent += `When the user types \`@${agentId}\`, activate this ${this.getAgentTitle(
|
||||
agentId
|
||||
mdContent += `When the user types \`@${agentId}\`, activate this ${await this.getAgentTitle(
|
||||
agentId,
|
||||
installDir
|
||||
)} persona and follow all instructions defined in the YML configuration above.\n`;
|
||||
|
||||
await fileManager.writeFile(mdPath, mdContent);
|
||||
@@ -195,20 +199,34 @@ class IdeSetup {
|
||||
return agentFiles.map((file) => path.basename(file, ".md"));
|
||||
}
|
||||
|
||||
getAgentTitle(agentId) {
|
||||
const agentTitles = {
|
||||
analyst: "Business Analyst",
|
||||
architect: "Solution Architect",
|
||||
"bmad-master": "BMAD Master",
|
||||
"bmad-orchestrator": "BMAD Orchestrator",
|
||||
dev: "Developer",
|
||||
pm: "Product Manager",
|
||||
po: "Product Owner",
|
||||
qa: "QA Specialist",
|
||||
sm: "Scrum Master",
|
||||
"ux-expert": "UX Expert",
|
||||
};
|
||||
return agentTitles[agentId] || agentId;
|
||||
async getAgentTitle(agentId, installDir) {
|
||||
// Try to read the actual agent file to get the title
|
||||
let agentPath = path.join(installDir, ".bmad-core", "agents", `${agentId}.md`);
|
||||
if (!(await fileManager.pathExists(agentPath))) {
|
||||
agentPath = path.join(installDir, "agents", `${agentId}.md`);
|
||||
}
|
||||
|
||||
if (await fileManager.pathExists(agentPath)) {
|
||||
try {
|
||||
const agentContent = await fileManager.readFile(agentPath);
|
||||
const yamlMatch = agentContent.match(/```ya?ml\n([\s\S]*?)```/);
|
||||
|
||||
if (yamlMatch) {
|
||||
const yaml = yamlMatch[1];
|
||||
const titleMatch = yaml.match(/title:\s*(.+)/);
|
||||
if (titleMatch) {
|
||||
return titleMatch[1].trim();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`Failed to read agent title for ${agentId}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to formatted agent ID
|
||||
return agentId.split('-').map(word =>
|
||||
word.charAt(0).toUpperCase() + word.slice(1)
|
||||
).join(' ');
|
||||
}
|
||||
|
||||
async setupRoo(installDir, selectedAgent) {
|
||||
@@ -294,7 +312,7 @@ class IdeSetup {
|
||||
const whenToUseMatch = yaml.match(/whenToUse:\s*"(.+)"/);
|
||||
const roleDefinitionMatch = yaml.match(/roleDefinition:\s*"(.+)"/);
|
||||
|
||||
const title = titleMatch ? titleMatch[1].trim() : this.getAgentTitle(agentId);
|
||||
const title = titleMatch ? titleMatch[1].trim() : await this.getAgentTitle(agentId, installDir);
|
||||
const icon = iconMatch ? iconMatch[1].trim() : "🤖";
|
||||
const whenToUse = whenToUseMatch ? whenToUseMatch[1].trim() : `Use for ${title} tasks`;
|
||||
const roleDefinition = roleDefinitionMatch
|
||||
@@ -381,8 +399,8 @@ class IdeSetup {
|
||||
const mdPath = path.join(clineRulesDir, `${prefix}-${agentId}.md`);
|
||||
|
||||
// Create MD content for Cline (focused on project standards and role)
|
||||
let mdContent = `# ${this.getAgentTitle(agentId)} Agent\n\n`;
|
||||
mdContent += `This rule defines the ${this.getAgentTitle(agentId)} persona and project standards.\n\n`;
|
||||
let mdContent = `# ${await this.getAgentTitle(agentId, installDir)} Agent\n\n`;
|
||||
mdContent += `This rule defines the ${await this.getAgentTitle(agentId, installDir)} persona and project standards.\n\n`;
|
||||
mdContent += "## Role Definition\n\n";
|
||||
mdContent +=
|
||||
"When the user types `@" + agentId + "`, adopt this persona and follow these guidelines:\n\n";
|
||||
@@ -402,7 +420,7 @@ class IdeSetup {
|
||||
mdContent += `- Update relevant project files when making changes\n`;
|
||||
mdContent += `- Reference the complete agent definition in [.bmad-core/agents/${agentId}.md](.bmad-core/agents/${agentId}.md)\n\n`;
|
||||
mdContent += "## Usage\n\n";
|
||||
mdContent += `Type \`@${agentId}\` to activate this ${this.getAgentTitle(agentId)} persona.\n`;
|
||||
mdContent += `Type \`@${agentId}\` to activate this ${await this.getAgentTitle(agentId, installDir)} persona.\n`;
|
||||
|
||||
await fileManager.writeFile(mdPath, mdContent);
|
||||
console.log(chalk.green(`✓ Created rule: ${prefix}-${agentId}.md`));
|
||||
|
||||
Reference in New Issue
Block a user