mirror of
https://github.com/bmad-code-org/BMAD-METHOD.git
synced 2026-01-30 04:32:02 +00:00
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:
@@ -28,12 +28,17 @@ class AuggieSetup extends BaseIdeSetup {
|
|||||||
const targetDir = path.join(projectDir, '.augment', 'commands');
|
const targetDir = path.join(projectDir, '.augment', 'commands');
|
||||||
|
|
||||||
// Install using UnifiedInstaller
|
// Install using UnifiedInstaller
|
||||||
const counts = await this.installer.install(projectDir, bmadDir, {
|
const counts = await this.installer.install(
|
||||||
targetDir,
|
projectDir,
|
||||||
namingStyle: NamingStyle.FLAT_COLON,
|
bmadDir,
|
||||||
templateType: TemplateType.AUGMENT,
|
{
|
||||||
includeNestedStructure: false,
|
targetDir,
|
||||||
}, options.selectedModules || []);
|
namingStyle: NamingStyle.FLAT_COLON,
|
||||||
|
templateType: TemplateType.AUGMENT,
|
||||||
|
includeNestedStructure: false,
|
||||||
|
},
|
||||||
|
options.selectedModules || [],
|
||||||
|
);
|
||||||
|
|
||||||
console.log(chalk.green(`✓ ${this.name} configured:`));
|
console.log(chalk.green(`✓ ${this.name} configured:`));
|
||||||
console.log(chalk.dim(` - ${counts.agents} agents installed`));
|
console.log(chalk.dim(` - ${counts.agents} agents installed`));
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ const path = require('node:path');
|
|||||||
const { BaseIdeSetup } = require('./_base-ide');
|
const { BaseIdeSetup } = require('./_base-ide');
|
||||||
const chalk = require('chalk');
|
const chalk = require('chalk');
|
||||||
const { AgentCommandGenerator } = require('./shared/agent-command-generator');
|
const { AgentCommandGenerator } = require('./shared/agent-command-generator');
|
||||||
|
const { UnifiedInstaller, NamingStyle, TemplateType } = require('./shared/unified-installer');
|
||||||
const prompts = require('../../../lib/prompts');
|
const prompts = require('../../../lib/prompts');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -13,6 +14,7 @@ class GitHubCopilotSetup extends BaseIdeSetup {
|
|||||||
super('github-copilot', 'GitHub Copilot', true); // preferred IDE
|
super('github-copilot', 'GitHub Copilot', true); // preferred IDE
|
||||||
this.configDir = '.github';
|
this.configDir = '.github';
|
||||||
this.agentsDir = 'agents';
|
this.agentsDir = 'agents';
|
||||||
|
this.promptsDir = 'prompts';
|
||||||
this.vscodeDir = '.vscode';
|
this.vscodeDir = '.vscode';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,41 +96,63 @@ class GitHubCopilotSetup extends BaseIdeSetup {
|
|||||||
const config = options.preCollectedConfig || {};
|
const config = options.preCollectedConfig || {};
|
||||||
await this.configureVsCodeSettings(projectDir, { ...options, ...config });
|
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 githubDir = path.join(projectDir, this.configDir);
|
||||||
const agentsDir = path.join(githubDir, this.agentsDir);
|
const agentsDir = path.join(githubDir, this.agentsDir);
|
||||||
|
const promptsDir = path.join(githubDir, this.promptsDir);
|
||||||
await this.ensureDir(agentsDir);
|
await this.ensureDir(agentsDir);
|
||||||
|
await this.ensureDir(promptsDir);
|
||||||
|
|
||||||
// Clean up any existing BMAD files before reinstalling
|
// Clean up any existing BMAD files before reinstalling
|
||||||
await this.cleanup(projectDir);
|
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 agentGen = new AgentCommandGenerator(this.bmadFolderName);
|
||||||
const { artifacts: agentArtifacts } = await agentGen.collectAgentArtifacts(bmadDir, options.selectedModules || []);
|
const { artifacts: agentArtifacts } = await agentGen.collectAgentArtifacts(bmadDir, options.selectedModules || []);
|
||||||
|
|
||||||
// Create agent files with bmd- prefix
|
// Create agent files with bmad- prefix
|
||||||
let agentCount = 0;
|
let agentCount = 0;
|
||||||
for (const artifact of agentArtifacts) {
|
for (const artifact of agentArtifacts) {
|
||||||
const content = artifact.content;
|
const content = artifact.content;
|
||||||
const agentContent = await this.createAgentContent({ module: artifact.module, name: artifact.name }, content);
|
const agentContent = await this.createAgentContent({ module: artifact.module, name: artifact.name }, content);
|
||||||
|
|
||||||
// Use bmd- prefix: bmd-custom-{module}-{name}.agent.md
|
// Use bmad- prefix: bmad-{module}-{name}.agent.md
|
||||||
const targetPath = path.join(agentsDir, `bmd-custom-${artifact.module}-${artifact.name}.agent.md`);
|
const targetPath = path.join(agentsDir, `bmad-${artifact.module}-${artifact.name}.agent.md`);
|
||||||
await this.writeFile(targetPath, agentContent);
|
await this.writeFile(targetPath, agentContent);
|
||||||
agentCount++;
|
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.green(`✓ ${this.name} configured:`));
|
||||||
console.log(chalk.dim(` - ${agentCount} agents created`));
|
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(` - 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(` - 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 {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
agents: agentCount,
|
agents: agentCount,
|
||||||
|
prompts: promptCounts.total,
|
||||||
settings: true,
|
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);
|
const agentsDir = path.join(projectDir, this.configDir, this.agentsDir);
|
||||||
if (await fs.pathExists(agentsDir)) {
|
if (await fs.pathExists(agentsDir)) {
|
||||||
const files = await fs.readdir(agentsDir);
|
const files = await fs.readdir(agentsDir);
|
||||||
let removed = 0;
|
let removed = 0;
|
||||||
|
|
||||||
for (const file of files) {
|
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));
|
await fs.remove(path.join(agentsDir, file));
|
||||||
removed++;
|
removed++;
|
||||||
}
|
}
|
||||||
@@ -303,6 +328,24 @@ ${cleanContent}
|
|||||||
console.log(chalk.dim(` Cleaned up ${removed} existing BMAD agents`));
|
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}
|
${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);
|
await this.writeFile(agentFilePath, agentContent);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
path: agentFilePath,
|
path: agentFilePath,
|
||||||
command: `bmd-custom-${agentName}`,
|
command: `bmad-${agentName}`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ const TemplateType = {
|
|||||||
WINDSURF: 'windsurf', // YAML with auto_execution_mode
|
WINDSURF: 'windsurf', // YAML with auto_execution_mode
|
||||||
AUGMENT: 'augment', // YAML frontmatter
|
AUGMENT: 'augment', // YAML frontmatter
|
||||||
GEMINI: 'gemini', // TOML frontmatter with description/prompt
|
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
|
// 1. Install Agents
|
||||||
const agentGen = new AgentCommandGenerator(this.bmadFolderName);
|
const agentGen = new AgentCommandGenerator(this.bmadFolderName);
|
||||||
const { artifacts: agentArtifacts } = await agentGen.collectAgentArtifacts(bmadDir, selectedModules);
|
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)
|
// 2. Install Workflows (filter out README artifacts)
|
||||||
const workflowGen = new WorkflowCommandGenerator(this.bmadFolderName);
|
const workflowGen = new WorkflowCommandGenerator(this.bmadFolderName);
|
||||||
@@ -170,7 +179,9 @@ class UnifiedInstaller {
|
|||||||
* @returns {Promise<number>} Number of artifacts written
|
* @returns {Promise<number>} Number of artifacts written
|
||||||
*/
|
*/
|
||||||
async writeArtifacts(artifacts, targetDir, namingStyle, templateType, fileExtension, customTemplateFn, artifactType) {
|
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;
|
let written = 0;
|
||||||
|
|
||||||
for (const artifact of artifacts) {
|
for (const artifact of artifacts) {
|
||||||
@@ -238,6 +249,11 @@ class UnifiedInstaller {
|
|||||||
return this.addGeminiFrontmatter(artifact, content);
|
return this.addGeminiFrontmatter(artifact, content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case TemplateType.COPILOT: {
|
||||||
|
// Add Copilot frontmatter with tools array
|
||||||
|
return this.addCopilotFrontmatter(artifact, content);
|
||||||
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
@@ -305,7 +321,7 @@ description: ${name}
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Escape any triple quotes in content
|
// Escape any triple quotes in content
|
||||||
const escapedContent = contentWithoutFrontmatter.replace(/"""/g, '\\"\\"\\"');
|
const escapedContent = contentWithoutFrontmatter.replaceAll('"""', String.raw`\"\"\"`);
|
||||||
|
|
||||||
return `description = "${description}"
|
return `description = "${description}"
|
||||||
prompt = """
|
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
|
* Get tasks from manifest CSV
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -39,12 +39,17 @@ class WindsurfSetup extends BaseIdeSetup {
|
|||||||
await this.cleanup(projectDir);
|
await this.cleanup(projectDir);
|
||||||
|
|
||||||
// Use UnifiedInstaller with Windsurf-specific configuration
|
// Use UnifiedInstaller with Windsurf-specific configuration
|
||||||
const counts = await this.unifiedInstaller.install(projectDir, bmadDir, {
|
const counts = await this.unifiedInstaller.install(
|
||||||
targetDir: workflowsDir,
|
projectDir,
|
||||||
namingStyle: NamingStyle.FLAT_DASH,
|
bmadDir,
|
||||||
templateType: TemplateType.WINDSURF,
|
{
|
||||||
customTemplateFn: this.windsurfTemplate.bind(this),
|
targetDir: workflowsDir,
|
||||||
}, options.selectedModules || []);
|
namingStyle: NamingStyle.FLAT_DASH,
|
||||||
|
templateType: TemplateType.WINDSURF,
|
||||||
|
customTemplateFn: this.windsurfTemplate.bind(this),
|
||||||
|
},
|
||||||
|
options.selectedModules || [],
|
||||||
|
);
|
||||||
|
|
||||||
// Post-process tasks and tools to add Windsurf auto_execution_mode
|
// Post-process tasks and tools to add Windsurf auto_execution_mode
|
||||||
// UnifiedInstaller handles agents/workflows correctly, but tasks/tools
|
// UnifiedInstaller handles agents/workflows correctly, but tasks/tools
|
||||||
@@ -129,7 +134,7 @@ ${contentWithoutFrontmatter}`;
|
|||||||
const parts = entry.name.replace('bmad-', '').replace('.md', '').split('-');
|
const parts = entry.name.replace('bmad-', '').replace('.md', '').split('-');
|
||||||
if (parts.length < 2) continue;
|
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') {
|
if (type === 'task' || type === 'tool') {
|
||||||
// Check if auto_execution_mode is already present
|
// Check if auto_execution_mode is already present
|
||||||
|
|||||||
Reference in New Issue
Block a user