fix: use raw agent names and resolve duplicate command filenames
Use raw agent names (e.g., 'dev', 'pm') for the name field in .agent.md frontmatter and all agent references in prompt frontmatter, instead of persona display names. This provides clean @mention and /command names. Resolve Create Story / Validate Story filename collision where both entries shared command 'bmad-bmm-create-story'. When multiple entries share a command, derive unique filenames from the entry name slug. Also pass workflow options (Create Mode, Validate Mode) to the prompt body so each prompt invokes the correct workflow mode. Fixes #1794 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
78ab55c7e3
commit
2a9d984a2c
|
|
@ -145,16 +145,15 @@ class GitHubCopilotSetup extends BaseIdeSetup {
|
||||||
createAgentContent(artifact, manifestEntry) {
|
createAgentContent(artifact, manifestEntry) {
|
||||||
// Build enriched description from manifest metadata
|
// Build enriched description from manifest metadata
|
||||||
let description;
|
let description;
|
||||||
let name;
|
// Use the raw agent name (e.g., "dev", "pm") for clean @mention selection
|
||||||
|
const name = artifact.name;
|
||||||
if (manifestEntry) {
|
if (manifestEntry) {
|
||||||
const persona = manifestEntry.displayName || artifact.name;
|
const persona = manifestEntry.displayName || artifact.name;
|
||||||
const title = manifestEntry.title || this.formatTitle(artifact.name);
|
const title = manifestEntry.title || this.formatTitle(artifact.name);
|
||||||
const capabilities = manifestEntry.capabilities || 'agent capabilities';
|
const capabilities = manifestEntry.capabilities || 'agent capabilities';
|
||||||
description = `${persona} — ${title}: ${capabilities}`;
|
description = `${persona} — ${title}: ${capabilities}`;
|
||||||
name = manifestEntry.displayName || this.formatTitle(artifact.name);
|
|
||||||
} else {
|
} else {
|
||||||
description = `Activates the ${this.formatTitle(artifact.name)} agent persona.`;
|
description = `Activates the ${this.formatTitle(artifact.name)} agent persona.`;
|
||||||
name = this.formatTitle(artifact.name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const safeName = this.escapeYamlSingleQuote(name);
|
const safeName = this.escapeYamlSingleQuote(name);
|
||||||
|
|
@ -197,14 +196,30 @@ You must fully embody this agent's persona and follow all activation instruction
|
||||||
const helpEntries = await this.loadBmadHelp(bmadDir);
|
const helpEntries = await this.loadBmadHelp(bmadDir);
|
||||||
|
|
||||||
if (helpEntries) {
|
if (helpEntries) {
|
||||||
|
// Detect duplicate commands to derive unique filenames when multiple entries share one
|
||||||
|
const commandCounts = new Map();
|
||||||
|
for (const entry of helpEntries) {
|
||||||
|
if (!entry.command || !entry['workflow-file']) continue;
|
||||||
|
commandCounts.set(entry.command, (commandCounts.get(entry.command) || 0) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
for (const entry of helpEntries) {
|
for (const entry of helpEntries) {
|
||||||
const command = entry.command;
|
const command = entry.command;
|
||||||
if (!command) continue; // Skip entries without a command (tech-writer commands have no command column)
|
if (!command) continue; // Skip entries without a command (tech-writer commands have no command column)
|
||||||
|
|
||||||
const workflowFile = entry['workflow-file'];
|
const workflowFile = entry['workflow-file'];
|
||||||
if (!workflowFile) continue; // Skip entries with no workflow file path
|
if (!workflowFile) continue; // Skip entries with no workflow file path
|
||||||
const promptFileName = `${command}.prompt.md`;
|
|
||||||
const promptContent = this.createWorkflowPromptContent(entry, workflowFile, agentManifest);
|
// When multiple entries share the same command, derive a unique filename from the entry name
|
||||||
|
let promptFileName;
|
||||||
|
if (commandCounts.get(command) > 1) {
|
||||||
|
const slug = entry.name.toLowerCase().replaceAll(/[^a-z0-9]+/g, '-');
|
||||||
|
promptFileName = `bmad-bmm-${slug}.prompt.md`;
|
||||||
|
} else {
|
||||||
|
promptFileName = `${command}.prompt.md`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const promptContent = this.createWorkflowPromptContent(entry, workflowFile);
|
||||||
const promptPath = path.join(promptsDir, promptFileName);
|
const promptPath = path.join(promptsDir, promptFileName);
|
||||||
await this.writeFile(promptPath, promptContent);
|
await this.writeFile(promptPath, promptContent);
|
||||||
promptCount++;
|
promptCount++;
|
||||||
|
|
@ -241,10 +256,9 @@ You must fully embody this agent's persona and follow all activation instruction
|
||||||
* Determines the pattern (A, B, or A for .xml tasks) based on file extension
|
* Determines the pattern (A, B, or A for .xml tasks) based on file extension
|
||||||
* @param {Object} entry - bmad-help.csv row
|
* @param {Object} entry - bmad-help.csv row
|
||||||
* @param {string} workflowFile - Workflow file path
|
* @param {string} workflowFile - Workflow file path
|
||||||
* @param {Map} agentManifest - Agent manifest data for display name lookup
|
|
||||||
* @returns {string} Prompt file content
|
* @returns {string} Prompt file content
|
||||||
*/
|
*/
|
||||||
createWorkflowPromptContent(entry, workflowFile, agentManifest) {
|
createWorkflowPromptContent(entry, workflowFile) {
|
||||||
const description = this.escapeYamlSingleQuote(this.createPromptDescription(entry.name));
|
const description = this.escapeYamlSingleQuote(this.createPromptDescription(entry.name));
|
||||||
const promptName = this.escapeYamlSingleQuote(entry.name || description);
|
const promptName = this.escapeYamlSingleQuote(entry.name || description);
|
||||||
// bmm/config.yaml is safe to hardcode here: these prompts are only generated when
|
// bmm/config.yaml is safe to hardcode here: these prompts are only generated when
|
||||||
|
|
@ -267,21 +281,23 @@ You must fully embody this agent's persona and follow all activation instruction
|
||||||
2. Load and follow the workflow at {project-root}/${workflowFile}`;
|
2. Load and follow the workflow at {project-root}/${workflowFile}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the agent line: use agent displayName from manifest if available
|
// Build the agent line: use raw agent name to match agent .agent.md name field
|
||||||
const agentName = (entry['agent-name'] || '').trim();
|
const agentName = (entry['agent-name'] || '').trim();
|
||||||
let agentLine = '';
|
let agentLine = '';
|
||||||
if (agentName) {
|
if (agentName) {
|
||||||
const agentMeta = agentManifest.get(agentName);
|
agentLine = `\nagent: '${this.escapeYamlSingleQuote(agentName)}'`;
|
||||||
const agentDisplayName = (agentMeta && agentMeta.displayName) || this.formatTitle(agentName);
|
|
||||||
agentLine = `\nagent: '${this.escapeYamlSingleQuote(agentDisplayName)}'`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Include options (e.g., "Create Mode", "Validate Mode") when present
|
||||||
|
const options = (entry.options || '').trim();
|
||||||
|
const optionsInstruction = options ? `\n4. Use option: ${options}` : '';
|
||||||
|
|
||||||
return `---
|
return `---
|
||||||
name: '${promptName}'
|
name: '${promptName}'
|
||||||
description: '${description}'${agentLine}
|
description: '${description}'${agentLine}
|
||||||
---
|
---
|
||||||
|
|
||||||
${body}
|
${body}${optionsInstruction}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -352,9 +368,8 @@ ${body}
|
||||||
const safeName = this.escapeYamlSingleQuote(entry.name);
|
const safeName = this.escapeYamlSingleQuote(entry.name);
|
||||||
const safeDescription = this.escapeYamlSingleQuote(cmd.description);
|
const safeDescription = this.escapeYamlSingleQuote(cmd.description);
|
||||||
|
|
||||||
// Use agent display name from merged CSV if available, otherwise format the raw name
|
// Use raw agent name to match agent .agent.md name field
|
||||||
const agentDisplayName = (entry['agent-display-name'] || '').trim() || this.formatTitle(entry['agent-name']);
|
const agentLine = `\nagent: '${this.escapeYamlSingleQuote(entry['agent-name'])}'`;
|
||||||
const agentLine = `\nagent: '${this.escapeYamlSingleQuote(agentDisplayName)}'`;
|
|
||||||
|
|
||||||
const content = `---
|
const content = `---
|
||||||
name: '${safeName}'
|
name: '${safeName}'
|
||||||
|
|
@ -377,15 +392,14 @@ description: '${safeDescription}'${agentLine}
|
||||||
*/
|
*/
|
||||||
createAgentActivatorPromptContent(artifact, manifestEntry) {
|
createAgentActivatorPromptContent(artifact, manifestEntry) {
|
||||||
let description;
|
let description;
|
||||||
let name;
|
|
||||||
if (manifestEntry) {
|
if (manifestEntry) {
|
||||||
description = manifestEntry.title || this.formatTitle(artifact.name);
|
description = manifestEntry.title || this.formatTitle(artifact.name);
|
||||||
name = manifestEntry.displayName || this.formatTitle(artifact.name);
|
|
||||||
} else {
|
} else {
|
||||||
description = this.formatTitle(artifact.name);
|
description = this.formatTitle(artifact.name);
|
||||||
name = this.formatTitle(artifact.name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use the raw agent name (e.g., "dev") to match agent .agent.md name field
|
||||||
|
const name = artifact.name;
|
||||||
const safeName = this.escapeYamlSingleQuote(name);
|
const safeName = this.escapeYamlSingleQuote(name);
|
||||||
const safeDescription = this.escapeYamlSingleQuote(description);
|
const safeDescription = this.escapeYamlSingleQuote(description);
|
||||||
const agentPath = artifact.agentPath || artifact.relativePath;
|
const agentPath = artifact.agentPath || artifact.relativePath;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue