Fix github-copilot installer to use UnifiedInstaller for prompts

- Add .github/prompts directory alongside .github/agents
- Use UnifiedInstaller with TemplateType.COPILOT for prompts/workflows/tasks/tools
- Fix typo: bmd-custom- -> bmad- prefix for agents
- Update cleanup to handle both directories
- Format fixes for auggie.js and windsurf.js
This commit is contained in:
Brian Madison
2026-01-24 12:12:06 -06:00
parent 4cb5cc7dbc
commit 5aef6379b9
4 changed files with 134 additions and 27 deletions

View File

@@ -28,12 +28,17 @@ class AuggieSetup extends BaseIdeSetup {
const targetDir = path.join(projectDir, '.augment', 'commands');
// Install using UnifiedInstaller
const counts = await this.installer.install(projectDir, bmadDir, {
const counts = await this.installer.install(
projectDir,
bmadDir,
{
targetDir,
namingStyle: NamingStyle.FLAT_COLON,
templateType: TemplateType.AUGMENT,
includeNestedStructure: false,
}, options.selectedModules || []);
},
options.selectedModules || [],
);
console.log(chalk.green(`${this.name} configured:`));
console.log(chalk.dim(` - ${counts.agents} agents installed`));

View File

@@ -2,6 +2,7 @@ const path = require('node:path');
const { BaseIdeSetup } = require('./_base-ide');
const chalk = require('chalk');
const { AgentCommandGenerator } = require('./shared/agent-command-generator');
const { UnifiedInstaller, NamingStyle, TemplateType } = require('./shared/unified-installer');
const prompts = require('../../../lib/prompts');
/**
@@ -13,6 +14,7 @@ class GitHubCopilotSetup extends BaseIdeSetup {
super('github-copilot', 'GitHub Copilot', true); // preferred IDE
this.configDir = '.github';
this.agentsDir = 'agents';
this.promptsDir = 'prompts';
this.vscodeDir = '.vscode';
}
@@ -94,41 +96,63 @@ class GitHubCopilotSetup extends BaseIdeSetup {
const config = options.preCollectedConfig || {};
await this.configureVsCodeSettings(projectDir, { ...options, ...config });
// Create .github/agents directory
// Create .github/agents and .github/prompts directories
const githubDir = path.join(projectDir, this.configDir);
const agentsDir = path.join(githubDir, this.agentsDir);
const promptsDir = path.join(githubDir, this.promptsDir);
await this.ensureDir(agentsDir);
await this.ensureDir(promptsDir);
// Clean up any existing BMAD files before reinstalling
await this.cleanup(projectDir);
// Generate agent launchers
// 1. Generate agent launchers (custom .agent.md format - not using UnifiedInstaller)
const agentGen = new AgentCommandGenerator(this.bmadFolderName);
const { artifacts: agentArtifacts } = await agentGen.collectAgentArtifacts(bmadDir, options.selectedModules || []);
// Create agent files with bmd- prefix
// Create agent files with bmad- prefix
let agentCount = 0;
for (const artifact of agentArtifacts) {
const content = artifact.content;
const agentContent = await this.createAgentContent({ module: artifact.module, name: artifact.name }, content);
// Use bmd- prefix: bmd-custom-{module}-{name}.agent.md
const targetPath = path.join(agentsDir, `bmd-custom-${artifact.module}-${artifact.name}.agent.md`);
// Use bmad- prefix: bmad-{module}-{name}.agent.md
const targetPath = path.join(agentsDir, `bmad-${artifact.module}-${artifact.name}.agent.md`);
await this.writeFile(targetPath, agentContent);
agentCount++;
console.log(chalk.green(` ✓ Created agent: bmd-custom-${artifact.module}-${artifact.name}`));
console.log(chalk.green(` ✓ Created agent: bmad-${artifact.module}-${artifact.name}`));
}
// 2. Install prompts using UnifiedInstaller
const installer = new UnifiedInstaller(this.bmadFolderName);
const promptCounts = await installer.install(
projectDir,
bmadDir,
{
targetDir: promptsDir,
namingStyle: NamingStyle.FLAT_DASH,
templateType: TemplateType.COPILOT,
},
options.selectedModules || [],
);
console.log(chalk.green(`${this.name} configured:`));
console.log(chalk.dim(` - ${agentCount} agents created`));
console.log(
chalk.dim(
` - ${promptCounts.agents} prompts, ${promptCounts.workflows} workflows, ${promptCounts.tasks + promptCounts.tools} tasks/tools`,
),
);
console.log(chalk.dim(` - Agents directory: ${path.relative(projectDir, agentsDir)}`));
console.log(chalk.dim(` - Prompts directory: ${path.relative(projectDir, promptsDir)}`));
console.log(chalk.dim(` - VS Code settings configured`));
console.log(chalk.dim('\n Agents available in VS Code Chat view'));
console.log(chalk.dim('\n Agents and prompts available in VS Code Chat view'));
return {
success: true,
agents: agentCount,
prompts: promptCounts.total,
settings: true,
};
}
@@ -286,14 +310,15 @@ ${cleanContent}
}
}
// Clean up new agents directory
// Clean up agents directory
const agentsDir = path.join(projectDir, this.configDir, this.agentsDir);
if (await fs.pathExists(agentsDir)) {
const files = await fs.readdir(agentsDir);
let removed = 0;
for (const file of files) {
if (file.startsWith('bmd-') && file.endsWith('.agent.md')) {
// Remove old bmd-* files (typo fix) and current bmad-* files
if ((file.startsWith('bmd-') || file.startsWith('bmad-')) && file.endsWith('.agent.md')) {
await fs.remove(path.join(agentsDir, file));
removed++;
}
@@ -303,6 +328,24 @@ ${cleanContent}
console.log(chalk.dim(` Cleaned up ${removed} existing BMAD agents`));
}
}
// Clean up prompts directory
const promptsDir = path.join(projectDir, this.configDir, this.promptsDir);
if (await fs.pathExists(promptsDir)) {
const files = await fs.readdir(promptsDir);
let removed = 0;
for (const file of files) {
if (file.startsWith('bmad-') && file.endsWith('.md')) {
await fs.remove(path.join(promptsDir, file));
removed++;
}
}
if (removed > 0) {
console.log(chalk.dim(` Cleaned up ${removed} existing BMAD prompts`));
}
}
}
/**
@@ -370,12 +413,12 @@ tools: ${JSON.stringify(copilotTools)}
${launcherContent}
`;
const agentFilePath = path.join(agentsDir, `bmd-custom-${agentName}.agent.md`);
const agentFilePath = path.join(agentsDir, `bmad-${agentName}.agent.md`);
await this.writeFile(agentFilePath, agentContent);
return {
path: agentFilePath,
command: `bmd-custom-${agentName}`,
command: `bmad-${agentName}`,
};
}
}

View File

@@ -40,6 +40,7 @@ const TemplateType = {
WINDSURF: 'windsurf', // YAML with auto_execution_mode
AUGMENT: 'augment', // YAML frontmatter
GEMINI: 'gemini', // TOML frontmatter with description/prompt
COPILOT: 'copilot', // YAML with tools array for GitHub Copilot
};
/**
@@ -98,7 +99,15 @@ class UnifiedInstaller {
// 1. Install Agents
const agentGen = new AgentCommandGenerator(this.bmadFolderName);
const { artifacts: agentArtifacts } = await agentGen.collectAgentArtifacts(bmadDir, selectedModules);
counts.agents = await this.writeArtifacts(agentArtifacts, targetDir, namingStyle, templateType, fileExtension, customTemplateFn, 'agent');
counts.agents = await this.writeArtifacts(
agentArtifacts,
targetDir,
namingStyle,
templateType,
fileExtension,
customTemplateFn,
'agent',
);
// 2. Install Workflows (filter out README artifacts)
const workflowGen = new WorkflowCommandGenerator(this.bmadFolderName);
@@ -170,7 +179,9 @@ class UnifiedInstaller {
* @returns {Promise<number>} Number of artifacts written
*/
async writeArtifacts(artifacts, targetDir, namingStyle, templateType, fileExtension, customTemplateFn, artifactType) {
console.log(`[DEBUG] writeArtifacts: artifactType=${artifactType}, count=${artifacts.length}, targetDir=${targetDir}, fileExtension=${fileExtension}`);
console.log(
`[DEBUG] writeArtifacts: artifactType=${artifactType}, count=${artifacts.length}, targetDir=${targetDir}, fileExtension=${fileExtension}`,
);
let written = 0;
for (const artifact of artifacts) {
@@ -238,6 +249,11 @@ class UnifiedInstaller {
return this.addGeminiFrontmatter(artifact, content);
}
case TemplateType.COPILOT: {
// Add Copilot frontmatter with tools array
return this.addCopilotFrontmatter(artifact, content);
}
default: {
return content;
}
@@ -305,7 +321,7 @@ description: ${name}
}
// Escape any triple quotes in content
const escapedContent = contentWithoutFrontmatter.replace(/"""/g, '\\"\\"\\"');
const escapedContent = contentWithoutFrontmatter.replaceAll('"""', String.raw`\"\"\"`);
return `description = "${description}"
prompt = """
@@ -314,6 +330,44 @@ ${escapedContent}
`;
}
/**
* Add GitHub Copilot frontmatter with tools array
*/
addCopilotFrontmatter(artifact, content) {
// Remove existing frontmatter if present
const frontmatterRegex = /^---\s*\n[\s\S]*?\n---\s*\n/;
const contentWithoutFrontmatter = content.replace(frontmatterRegex, '');
// GitHub Copilot tools array (as specified)
const tools = [
'changes',
'edit',
'fetch',
'githubRepo',
'problems',
'runCommands',
'runTasks',
'runTests',
'search',
'runSubagent',
'testFailure',
'todos',
'usages',
];
const name = artifact.name || artifact.displayName || 'prompt';
const description = `Activates the ${name} ${artifact.type || 'workflow'}.`;
const frontmatter = `---
description: "${description}"
tools: ${JSON.stringify(tools)}
---
`;
return frontmatter + contentWithoutFrontmatter;
}
/**
* Get tasks from manifest CSV
*/

View File

@@ -39,12 +39,17 @@ class WindsurfSetup extends BaseIdeSetup {
await this.cleanup(projectDir);
// Use UnifiedInstaller with Windsurf-specific configuration
const counts = await this.unifiedInstaller.install(projectDir, bmadDir, {
const counts = await this.unifiedInstaller.install(
projectDir,
bmadDir,
{
targetDir: workflowsDir,
namingStyle: NamingStyle.FLAT_DASH,
templateType: TemplateType.WINDSURF,
customTemplateFn: this.windsurfTemplate.bind(this),
}, options.selectedModules || []);
},
options.selectedModules || [],
);
// Post-process tasks and tools to add Windsurf auto_execution_mode
// UnifiedInstaller handles agents/workflows correctly, but tasks/tools
@@ -129,7 +134,7 @@ ${contentWithoutFrontmatter}`;
const parts = entry.name.replace('bmad-', '').replace('.md', '').split('-');
if (parts.length < 2) continue;
const type = parts[parts.length - 2]; // second to last part should be 'task' or 'tool'
const type = parts.at(-2); // second to last part should be 'task' or 'tool'
if (type === 'task' || type === 'tool') {
// Check if auto_execution_mode is already present