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 <noreply@anthropic.com>
This commit is contained in:
parent
eb0e1fb1a5
commit
cb1a7cccc8
|
|
@ -5,6 +5,7 @@ agent:
|
|||
title: Business Analyst
|
||||
icon: 📊
|
||||
module: bmm
|
||||
capabilities: "market research, competitive analysis, requirements elicitation, domain expertise"
|
||||
hasSidecar: false
|
||||
|
||||
persona:
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ agent:
|
|||
title: Architect
|
||||
icon: 🏗️
|
||||
module: bmm
|
||||
capabilities: "distributed systems, cloud infrastructure, API design, scalable patterns"
|
||||
hasSidecar: false
|
||||
|
||||
persona:
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ agent:
|
|||
title: Developer Agent
|
||||
icon: 💻
|
||||
module: bmm
|
||||
capabilities: "story execution, test-driven development, code implementation"
|
||||
hasSidecar: false
|
||||
|
||||
persona:
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ agent:
|
|||
title: Product Manager
|
||||
icon: 📋
|
||||
module: bmm
|
||||
capabilities: "PRD creation, requirements discovery, stakeholder alignment, user interviews"
|
||||
hasSidecar: false
|
||||
|
||||
persona:
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ agent:
|
|||
title: QA Engineer
|
||||
icon: 🧪
|
||||
module: bmm
|
||||
capabilities: "test automation, API testing, E2E testing, coverage analysis"
|
||||
hasSidecar: false
|
||||
|
||||
persona:
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ agent:
|
|||
title: Quick Flow Solo Dev
|
||||
icon: 🚀
|
||||
module: bmm
|
||||
capabilities: "rapid spec creation, lean implementation, minimum ceremony"
|
||||
hasSidecar: false
|
||||
|
||||
persona:
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ agent:
|
|||
title: Scrum Master
|
||||
icon: 🏃
|
||||
module: bmm
|
||||
capabilities: "sprint planning, story preparation, agile ceremonies, backlog management"
|
||||
hasSidecar: false
|
||||
|
||||
persona:
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ agent:
|
|||
title: Technical Writer
|
||||
icon: 📚
|
||||
module: bmm
|
||||
capabilities: "documentation, Mermaid diagrams, standards compliance, concept explanation"
|
||||
hasSidecar: true
|
||||
|
||||
persona:
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ agent:
|
|||
title: UX Designer
|
||||
icon: 🎨
|
||||
module: bmm
|
||||
capabilities: "user research, interaction design, UI patterns, experience strategy"
|
||||
hasSidecar: false
|
||||
|
||||
persona:
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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>([^<]+)<\/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),
|
||||
|
|
|
|||
|
|
@ -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`;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 += `<agent ${agentAttrs.join(' ')}>\n`;
|
||||
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue