From cb1a7cccc8ccdbef437d9459eb931bed028747bd Mon Sep 17 00:00:00 2001 From: jheyworth <8269695+jheyworth@users.noreply.github.com> Date: Mon, 9 Feb 2026 13:39:18 +0000 Subject: [PATCH] feat: make agent capabilities data-driven via agent YAML metadata Replace the hardcoded getAgentCapabilities() map with a data-driven pipeline. Capabilities are now defined in each .agent.yaml source file, compiled into the XML output, extracted into agent-manifest.csv by the manifest generator, and read by the GitHub Copilot handler at install time. New agents automatically get their capabilities without code changes. Co-Authored-By: Claude Opus 4.6 --- src/bmm/agents/analyst.agent.yaml | 1 + src/bmm/agents/architect.agent.yaml | 1 + src/bmm/agents/dev.agent.yaml | 1 + src/bmm/agents/pm.agent.yaml | 1 + src/bmm/agents/qa.agent.yaml | 1 + src/bmm/agents/quick-flow-solo-dev.agent.yaml | 1 + src/bmm/agents/sm.agent.yaml | 1 + .../agents/tech-writer/tech-writer.agent.yaml | 1 + src/bmm/agents/ux-designer.agent.yaml | 1 + src/core/agents/bmad-master.agent.yaml | 1 + .../installers/lib/core/manifest-generator.js | 6 ++++- .../cli/installers/lib/ide/github-copilot.js | 26 ++----------------- tools/cli/lib/agent/compiler.js | 3 +++ tools/schema/agent.js | 1 + 14 files changed, 21 insertions(+), 25 deletions(-) diff --git a/src/bmm/agents/analyst.agent.yaml b/src/bmm/agents/analyst.agent.yaml index c340f69c1..28120d098 100644 --- a/src/bmm/agents/analyst.agent.yaml +++ b/src/bmm/agents/analyst.agent.yaml @@ -5,6 +5,7 @@ agent: title: Business Analyst icon: ๐Ÿ“Š module: bmm + capabilities: "market research, competitive analysis, requirements elicitation, domain expertise" hasSidecar: false persona: diff --git a/src/bmm/agents/architect.agent.yaml b/src/bmm/agents/architect.agent.yaml index 5ebfd90fb..d9fc48b9b 100644 --- a/src/bmm/agents/architect.agent.yaml +++ b/src/bmm/agents/architect.agent.yaml @@ -7,6 +7,7 @@ agent: title: Architect icon: ๐Ÿ—๏ธ module: bmm + capabilities: "distributed systems, cloud infrastructure, API design, scalable patterns" hasSidecar: false persona: diff --git a/src/bmm/agents/dev.agent.yaml b/src/bmm/agents/dev.agent.yaml index d88166eda..c707124d0 100644 --- a/src/bmm/agents/dev.agent.yaml +++ b/src/bmm/agents/dev.agent.yaml @@ -7,6 +7,7 @@ agent: title: Developer Agent icon: ๐Ÿ’ป module: bmm + capabilities: "story execution, test-driven development, code implementation" hasSidecar: false persona: diff --git a/src/bmm/agents/pm.agent.yaml b/src/bmm/agents/pm.agent.yaml index 9ce0bf32f..30377a682 100644 --- a/src/bmm/agents/pm.agent.yaml +++ b/src/bmm/agents/pm.agent.yaml @@ -5,6 +5,7 @@ agent: title: Product Manager icon: ๐Ÿ“‹ module: bmm + capabilities: "PRD creation, requirements discovery, stakeholder alignment, user interviews" hasSidecar: false persona: diff --git a/src/bmm/agents/qa.agent.yaml b/src/bmm/agents/qa.agent.yaml index 07ca4022f..9265f5a7b 100644 --- a/src/bmm/agents/qa.agent.yaml +++ b/src/bmm/agents/qa.agent.yaml @@ -5,6 +5,7 @@ agent: title: QA Engineer icon: ๐Ÿงช module: bmm + capabilities: "test automation, API testing, E2E testing, coverage analysis" hasSidecar: false persona: diff --git a/src/bmm/agents/quick-flow-solo-dev.agent.yaml b/src/bmm/agents/quick-flow-solo-dev.agent.yaml index 6d207a399..fff3052d4 100644 --- a/src/bmm/agents/quick-flow-solo-dev.agent.yaml +++ b/src/bmm/agents/quick-flow-solo-dev.agent.yaml @@ -7,6 +7,7 @@ agent: title: Quick Flow Solo Dev icon: ๐Ÿš€ module: bmm + capabilities: "rapid spec creation, lean implementation, minimum ceremony" hasSidecar: false persona: diff --git a/src/bmm/agents/sm.agent.yaml b/src/bmm/agents/sm.agent.yaml index 9e51f4259..d79f644e5 100644 --- a/src/bmm/agents/sm.agent.yaml +++ b/src/bmm/agents/sm.agent.yaml @@ -7,6 +7,7 @@ agent: title: Scrum Master icon: ๐Ÿƒ module: bmm + capabilities: "sprint planning, story preparation, agile ceremonies, backlog management" hasSidecar: false persona: diff --git a/src/bmm/agents/tech-writer/tech-writer.agent.yaml b/src/bmm/agents/tech-writer/tech-writer.agent.yaml index a742a6c9f..a129aca34 100644 --- a/src/bmm/agents/tech-writer/tech-writer.agent.yaml +++ b/src/bmm/agents/tech-writer/tech-writer.agent.yaml @@ -7,6 +7,7 @@ agent: title: Technical Writer icon: ๐Ÿ“š module: bmm + capabilities: "documentation, Mermaid diagrams, standards compliance, concept explanation" hasSidecar: true persona: diff --git a/src/bmm/agents/ux-designer.agent.yaml b/src/bmm/agents/ux-designer.agent.yaml index 639a8263f..cbff28576 100644 --- a/src/bmm/agents/ux-designer.agent.yaml +++ b/src/bmm/agents/ux-designer.agent.yaml @@ -7,6 +7,7 @@ agent: title: UX Designer icon: ๐ŸŽจ module: bmm + capabilities: "user research, interaction design, UI patterns, experience strategy" hasSidecar: false persona: diff --git a/src/core/agents/bmad-master.agent.yaml b/src/core/agents/bmad-master.agent.yaml index 66e9d37fc..a7dbc7105 100644 --- a/src/core/agents/bmad-master.agent.yaml +++ b/src/core/agents/bmad-master.agent.yaml @@ -7,6 +7,7 @@ agent: name: "BMad Master" title: "BMad Master Executor, Knowledge Custodian, and Workflow Orchestrator" icon: "๐Ÿง™" + capabilities: "runtime resource management, workflow orchestration, task execution, knowledge custodian" hasSidecar: false persona: diff --git a/tools/cli/installers/lib/core/manifest-generator.js b/tools/cli/installers/lib/core/manifest-generator.js index caea790eb..089a1817b 100644 --- a/tools/cli/installers/lib/core/manifest-generator.js +++ b/tools/cli/installers/lib/core/manifest-generator.js @@ -321,6 +321,7 @@ class ManifestGenerator { const nameMatch = content.match(/name="([^"]+)"/); const titleMatch = content.match(/title="([^"]+)"/); const iconMatch = content.match(/icon="([^"]+)"/); + const capabilitiesMatch = content.match(/capabilities="([^"]+)"/); // Extract persona fields const roleMatch = content.match(/([^<]+)<\/role>/); @@ -342,6 +343,7 @@ class ManifestGenerator { displayName: nameMatch ? nameMatch[1] : agentName, title: titleMatch ? titleMatch[1] : '', icon: iconMatch ? iconMatch[1] : '', + capabilities: capabilitiesMatch ? this.cleanForCSV(capabilitiesMatch[1]) : '', role: roleMatch ? this.cleanForCSV(roleMatch[1]) : '', identity: identityMatch ? this.cleanForCSV(identityMatch[1]) : '', communicationStyle: styleMatch ? this.cleanForCSV(styleMatch[1]) : '', @@ -784,7 +786,7 @@ class ManifestGenerator { } // Create CSV header with persona fields - let csvContent = 'name,displayName,title,icon,role,identity,communicationStyle,principles,module,path\n'; + let csvContent = 'name,displayName,title,icon,capabilities,role,identity,communicationStyle,principles,module,path\n'; // Combine existing and new agents, preferring new data for duplicates const allAgents = new Map(); @@ -802,6 +804,7 @@ class ManifestGenerator { displayName: agent.displayName, title: agent.title, icon: agent.icon, + capabilities: agent.capabilities, role: agent.role, identity: agent.identity, communicationStyle: agent.communicationStyle, @@ -818,6 +821,7 @@ class ManifestGenerator { escapeCsv(record.displayName), escapeCsv(record.title), escapeCsv(record.icon), + escapeCsv(record.capabilities), escapeCsv(record.role), escapeCsv(record.identity), escapeCsv(record.communicationStyle), diff --git a/tools/cli/installers/lib/ide/github-copilot.js b/tools/cli/installers/lib/ide/github-copilot.js index 77db771a1..635c94f8d 100644 --- a/tools/cli/installers/lib/ide/github-copilot.js +++ b/tools/cli/installers/lib/ide/github-copilot.js @@ -159,7 +159,7 @@ class GitHubCopilotSetup extends BaseIdeSetup { if (manifestEntry) { const persona = manifestEntry.displayName || artifact.name; const title = manifestEntry.title || this.formatTitle(artifact.name); - const capabilities = this.getAgentCapabilities(artifact.name); + const capabilities = manifestEntry.capabilities || 'agent capabilities'; description = `${persona} โ€” ${title}: ${capabilities}`; } else { description = `Activates the ${this.formatTitle(artifact.name)} agent persona.`; @@ -188,28 +188,6 @@ You must fully embody this agent's persona and follow all activation instruction `; } - /** - * Get capabilities string for an agent - * @param {string} agentName - Agent name - * @returns {string} Comma-separated capabilities - */ - getAgentCapabilities(agentName) { - const capabilitiesMap = { - 'bmad-master': 'runtime resource management, workflow orchestration, task execution, knowledge custodian', - analyst: 'market research, competitive analysis, requirements elicitation, domain expertise', - architect: 'distributed systems, cloud infrastructure, API design, scalable patterns', - dev: 'story execution, test-driven development, code implementation', - pm: 'PRD creation, requirements discovery, stakeholder alignment, user interviews', - qa: 'test automation, API testing, E2E testing, coverage analysis', - 'quick-flow-solo-dev': 'rapid spec creation, lean implementation, minimum ceremony', - sm: 'sprint planning, story preparation, agile ceremonies, backlog management', - 'tech-writer': 'documentation, Mermaid diagrams, standards compliance, concept explanation', - 'ux-designer': 'user research, interaction design, UI patterns, experience strategy', - }; - - return capabilitiesMap[agentName] || 'agent capabilities'; - } - /** * Generate .prompt.md files for workflows, tasks, tech-writer commands, and agent activators * @param {string} projectDir - Project directory @@ -449,7 +427,7 @@ tools: ${toolsStr} for (const agentName of agentOrder) { const meta = agentManifest.get(agentName); if (meta) { - const capabilities = this.getAgentCapabilities(agentName); + const capabilities = meta.capabilities || 'agent capabilities'; const cleanTitle = (meta.title || '').replaceAll('""', '"'); agentsTable += `| ${agentName} | ${meta.displayName} | ${cleanTitle} | ${capabilities} |\n`; } diff --git a/tools/cli/lib/agent/compiler.js b/tools/cli/lib/agent/compiler.js index a0dc4ae01..f9f71baab 100644 --- a/tools/cli/lib/agent/compiler.js +++ b/tools/cli/lib/agent/compiler.js @@ -279,6 +279,9 @@ async function compileToXml(agentYaml, agentName = '', targetPath = '') { `title="${meta.title || ''}"`, `icon="${meta.icon || '๐Ÿค–'}"`, ]; + if (meta.capabilities) { + agentAttrs.push(`capabilities="${escapeXml(meta.capabilities)}"`); + } xml += `\n`; diff --git a/tools/schema/agent.js b/tools/schema/agent.js index b6a36a985..93ced7c6e 100644 --- a/tools/schema/agent.js +++ b/tools/schema/agent.js @@ -228,6 +228,7 @@ function buildMetadataSchema(expectedModule) { title: createNonEmptyString('agent.metadata.title'), icon: createNonEmptyString('agent.metadata.icon'), module: createNonEmptyString('agent.metadata.module').optional(), + capabilities: createNonEmptyString('agent.metadata.capabilities').optional(), hasSidecar: z.boolean(), };