Compare commits

...

5 Commits

Author SHA1 Message Date
Sjoerd Bozon daf8a07ee8
Merge 015c74c46f into 677a00280b 2026-01-09 13:54:40 +01:00
Phil 677a00280b
feat: refactor Cursor IDE setup to do command generation and cleanup instead of rules (#1283)
* feat: refactor Cursor IDE setup to do  command generation and cleanup instead of rules

- Added support for command generation in the Cursor IDE setup, including the creation of a new commands directory.
- Implemented cleanup for old BMAD commands alongside existing rules.
- Integrated TaskToolCommandGenerator for generating task and tool commands.
- Updated logging to reflect the number of agents, tasks, tools, and workflow commands generated during setup.

* style: adjust constructor formatting and update command path in Cursor IDE setup

- Reformatted the constructor method for consistency.
- Updated the command path syntax in the Cursor IDE setup to use a more standard format.

* fix: update Cursor command paths in documentation

- Changed the command path for Cursor IDE setup from `.cursor/rules/bmad/` to `.cursor/commands/bmad/` in both installers.md and modules.md.
- Updated file extension references to use `.md` instead of `.mdc` for consistency.
2026-01-09 16:39:32 +08:00
Sjoerd Bozon 015c74c46f
Merge branch 'bmad-code-org:main' into feat/workflow-prompt-recommendations 2026-01-05 10:24:53 +01:00
Sjoerd Bozon 9317ef5a62 fix: address Copilot review feedback on PR #1205
- Move step 6 before WORKFLOW COMPLETE marker (fixes workflow structure)
- Change PRD shortcut from PR to PD (avoids conflict with parallel-research)
- Clarify instructions for reading/updating VS Code settings
- Update phase 4 comment to match actual handoff flow
2025-12-29 00:16:38 +01:00
Sjoerd Bozon d662aee4b2 feat: add VS Code workflow prompt recommendations
Add chat.promptFilesRecommendations support for GitHub Copilot to show
workflow shortcuts as new chat starters.

- Add workflow-prompts-config.js with all BMM, BMGD, and core prompts
- Add workflow-prompt-generator.js to create .github/prompts/*.prompt.md
- Update github-copilot.js to generate prompts and configure VS Code
- Add phase-based prompt toggling to implementation-readiness workflow
- Add phase-based prompt toggling to sprint-planning workflow

When implementation-readiness passes or sprint-planning completes, the
workflows update VS Code settings to prioritize the 'keep going' cycle
(create-story → dev-story → code-review) over setup phase prompts.
2025-12-28 23:44:40 +01:00
8 changed files with 412 additions and 313 deletions

View File

@ -61,7 +61,7 @@
| -------------- | -------------- | ------------------------- | ----------------------------- | | -------------- | -------------- | ------------------------- | ----------------------------- |
| claude-code | Claude Code | .claude/commands/ | .md with frontmatter | | claude-code | Claude Code | .claude/commands/ | .md with frontmatter |
| codex | Codex | (varies) | .md | | codex | Codex | (varies) | .md |
| cursor | Cursor | .cursor/rules/bmad/ | .mdc with MDC frontmatter | | cursor | Cursor | .cursor/commands/bmad/ | .md with YAML frontmatter |
| github-copilot | GitHub Copilot | .github/ | .md | | github-copilot | GitHub Copilot | .github/ | .md |
| opencode | OpenCode | .opencode/ | .md | | opencode | OpenCode | .opencode/ | .md |
| windsurf | Windsurf | .windsurf/workflows/bmad/ | .md with workflow frontmatter | | windsurf | Windsurf | .windsurf/workflows/bmad/ | .md with workflow frontmatter |

View File

@ -128,7 +128,7 @@ module.exports = { NewIdeSetup };
| IDE | Config Pattern | File Extension | | IDE | Config Pattern | File Extension |
| -------------- | ------------------------- | -------------- | | -------------- | ------------------------- | -------------- |
| Claude Code | .claude/commands/bmad/ | .md | | Claude Code | .claude/commands/bmad/ | .md |
| Cursor | .cursor/rules/bmad/ | .mdc | | Cursor | .cursor/commands/bmad/ | .md |
| Windsurf | .windsurf/workflows/bmad/ | .md | | Windsurf | .windsurf/workflows/bmad/ | .md |
| GitHub Copilot | .github/ | .md | | GitHub Copilot | .github/ | .md |

View File

@ -111,6 +111,29 @@ Report generated: {outputFile}
The assessment found [number] issues requiring attention. Review the detailed report for specific findings and recommendations." The assessment found [number] issues requiring attention. Review the detailed report for specific findings and recommendations."
### 6. Update IDE Prompt Recommendations
If the readiness status is **READY**, update `.vscode/settings.json` to prioritize the implementation cycle prompts.
Read the existing `chat.promptFilesRecommendations` object and modify these keys:
**Set to `true` (implementation cycle - "keep going" loop):**
- `bmd-create-story`
- `bmd-dev-story`
- `bmd-code-review`
- `bmd-retrospective`
- `bmd-correct-course`
**Set to `false` (setup phase - already completed):**
- `bmd-workflow-init`
- `bmd-brainstorm`
- `bmd-prd`
- `bmd-ux-design`
- `bmd-create-architecture`
- `bmd-epics-stories`
- `bmd-implementation-readiness`
- `bmd-sprint-planning`
## WORKFLOW COMPLETE ## WORKFLOW COMPLETE
The implementation readiness workflow is now complete. The report contains all findings and recommendations for the user to consider. The implementation readiness workflow is now complete. The report contains all findings and recommendations for the user to consider.

View File

@ -179,6 +179,38 @@ development_status:
</step> </step>
<step n="6" goal="Update IDE prompt recommendations for implementation phase">
<action>Read the existing `.vscode/settings.json` and update the `chat.promptFilesRecommendations` object.</action>
**Set to `true` (implementation cycle - "keep going" loop):**
- `bmd-create-story`
- `bmd-dev-story`
- `bmd-code-review`
- `bmd-retrospective`
- `bmd-correct-course`
**Set to `false` (setup phase - already completed):**
- `bmd-workflow-init`
- `bmd-brainstorm`
- `bmd-prd`
- `bmd-ux-design`
- `bmd-create-architecture`
- `bmd-epics-stories`
- `bmd-implementation-readiness`
- `bmd-sprint-planning`
<action>Inform {user_name}:</action>
**IDE Updated for Implementation Phase**
The "keep going" cycle prompts are now prioritized in VS Code:
- **@bmd-custom-bmm-sm → *create-story** (prepare a story)
- **@bmd-custom-bmm-dev → *dev-story** (implement it)
- **Same chat → *code-review** (review the code)
- **Repeat!**
</step>
</workflow> </workflow>
## Additional Documentation ## Additional Documentation

View File

@ -3,6 +3,7 @@ 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 { WorkflowCommandGenerator } = require('./shared/workflow-command-generator'); const { WorkflowCommandGenerator } = require('./shared/workflow-command-generator');
const { TaskToolCommandGenerator } = require('./shared/task-tool-command-generator');
/** /**
* Cursor IDE setup handler * Cursor IDE setup handler
@ -12,6 +13,7 @@ class CursorSetup extends BaseIdeSetup {
super('cursor', 'Cursor', true); // preferred IDE super('cursor', 'Cursor', true); // preferred IDE
this.configDir = '.cursor'; this.configDir = '.cursor';
this.rulesDir = 'rules'; this.rulesDir = 'rules';
this.commandsDir = 'commands';
} }
/** /**
@ -21,11 +23,17 @@ class CursorSetup extends BaseIdeSetup {
async cleanup(projectDir) { async cleanup(projectDir) {
const fs = require('fs-extra'); const fs = require('fs-extra');
const bmadRulesDir = path.join(projectDir, this.configDir, this.rulesDir, 'bmad'); const bmadRulesDir = path.join(projectDir, this.configDir, this.rulesDir, 'bmad');
const bmadCommandsDir = path.join(projectDir, this.configDir, this.commandsDir, 'bmad');
if (await fs.pathExists(bmadRulesDir)) { if (await fs.pathExists(bmadRulesDir)) {
await fs.remove(bmadRulesDir); await fs.remove(bmadRulesDir);
console.log(chalk.dim(` Removed old BMAD rules from ${this.name}`)); console.log(chalk.dim(` Removed old BMAD rules from ${this.name}`));
} }
if (await fs.pathExists(bmadCommandsDir)) {
await fs.remove(bmadCommandsDir);
console.log(chalk.dim(` Removed old BMAD commands from ${this.name}`));
}
} }
/** /**
@ -40,330 +48,76 @@ class CursorSetup extends BaseIdeSetup {
// Clean up old BMAD installation first // Clean up old BMAD installation first
await this.cleanup(projectDir); await this.cleanup(projectDir);
// Create .cursor/rules directory structure // Create .cursor/commands directory structure
const cursorDir = path.join(projectDir, this.configDir); const cursorDir = path.join(projectDir, this.configDir);
const rulesDir = path.join(cursorDir, this.rulesDir); const commandsDir = path.join(cursorDir, this.commandsDir);
const bmadRulesDir = path.join(rulesDir, 'bmad'); const bmadCommandsDir = path.join(commandsDir, 'bmad');
await this.ensureDir(bmadRulesDir); await this.ensureDir(bmadCommandsDir);
// Generate agent launchers first // Generate agent launchers using AgentCommandGenerator
// This creates small launcher files that reference the actual agents in _bmad/
const agentGen = new AgentCommandGenerator(this.bmadFolderName); const agentGen = new AgentCommandGenerator(this.bmadFolderName);
const { artifacts: agentArtifacts } = await agentGen.collectAgentArtifacts(bmadDir, options.selectedModules || []); const { artifacts: agentArtifacts, counts: agentCounts } = await agentGen.collectAgentArtifacts(bmadDir, options.selectedModules || []);
// Convert artifacts to agent format for index creation
const agents = agentArtifacts.map((a) => ({ module: a.module, name: a.name }));
// Get tasks, tools, and workflows (ALL workflows now generate commands)
const tasks = await this.getTasks(bmadDir, true);
const tools = await this.getTools(bmadDir, true);
// Get ALL workflows using the new workflow command generator
const workflowGenerator = new WorkflowCommandGenerator(this.bmadFolderName);
const { artifacts: workflowArtifacts, counts: workflowCounts } = await workflowGenerator.collectWorkflowArtifacts(bmadDir);
// Convert artifacts to workflow objects for directory creation
const workflows = workflowArtifacts
.filter((artifact) => artifact.type === 'workflow-command')
.map((artifact) => ({
module: artifact.module,
name: path.basename(artifact.relativePath, '.md'),
path: artifact.sourcePath,
}));
// Create directories for each module // Create directories for each module
const modules = new Set(); const modules = new Set();
for (const item of [...agents, ...tasks, ...tools, ...workflows]) modules.add(item.module); for (const artifact of agentArtifacts) {
modules.add(artifact.module);
}
for (const module of modules) { for (const module of modules) {
await this.ensureDir(path.join(bmadRulesDir, module)); await this.ensureDir(path.join(bmadCommandsDir, module));
await this.ensureDir(path.join(bmadRulesDir, module, 'agents')); await this.ensureDir(path.join(bmadCommandsDir, module, 'agents'));
await this.ensureDir(path.join(bmadRulesDir, module, 'tasks'));
await this.ensureDir(path.join(bmadRulesDir, module, 'tools'));
await this.ensureDir(path.join(bmadRulesDir, module, 'workflows'));
} }
// Process and write agent launchers with MDC format // Write agent launcher files
let agentCount = 0; const agentCount = await agentGen.writeAgentLaunchers(bmadCommandsDir, agentArtifacts);
for (const artifact of agentArtifacts) {
// Add MDC metadata header to launcher (but don't call processContent which adds activation headers)
const content = this.wrapLauncherWithMDC(artifact.content, {
module: artifact.module,
name: artifact.name,
});
const targetPath = path.join(bmadRulesDir, artifact.module, 'agents', `${artifact.name}.mdc`); // Generate workflow commands from manifest (if it exists)
const workflowGen = new WorkflowCommandGenerator(this.bmadFolderName);
const { artifacts: workflowArtifacts } = await workflowGen.collectWorkflowArtifacts(bmadDir);
await this.writeFile(targetPath, content); // Write only workflow-command artifacts, skip workflow-launcher READMEs
agentCount++; let workflowCommandCount = 0;
}
// Process and copy tasks
let taskCount = 0;
for (const task of tasks) {
const content = await this.readAndProcess(task.path, {
module: task.module,
name: task.name,
});
const targetPath = path.join(bmadRulesDir, task.module, 'tasks', `${task.name}.mdc`);
await this.writeFile(targetPath, content);
taskCount++;
}
// Process and copy tools
let toolCount = 0;
for (const tool of tools) {
const content = await this.readAndProcess(tool.path, {
module: tool.module,
name: tool.name,
});
const targetPath = path.join(bmadRulesDir, tool.module, 'tools', `${tool.name}.mdc`);
await this.writeFile(targetPath, content);
toolCount++;
}
// Process and copy workflow commands (generated, not raw workflows)
let workflowCount = 0;
for (const artifact of workflowArtifacts) { for (const artifact of workflowArtifacts) {
if (artifact.type === 'workflow-command') { if (artifact.type === 'workflow-command') {
// Add MDC metadata header to workflow command const moduleWorkflowsDir = path.join(bmadCommandsDir, artifact.module, 'workflows');
const content = this.wrapLauncherWithMDC(artifact.content, { await this.ensureDir(moduleWorkflowsDir);
module: artifact.module, const commandPath = path.join(moduleWorkflowsDir, path.basename(artifact.relativePath));
name: path.basename(artifact.relativePath, '.md'), await this.writeFile(commandPath, artifact.content);
}); workflowCommandCount++;
const targetPath = path.join(bmadRulesDir, artifact.module, 'workflows', `${path.basename(artifact.relativePath, '.md')}.mdc`);
await this.writeFile(targetPath, content);
workflowCount++;
} }
// Skip workflow-launcher READMEs as they would be treated as slash commands
} }
// Create BMAD index file (but NOT .cursorrules - user manages that) // Generate task and tool commands from manifests (if they exist)
await this.createBMADIndex(bmadRulesDir, agents, tasks, tools, workflows, modules); const taskToolGen = new TaskToolCommandGenerator();
const taskToolResult = await taskToolGen.generateTaskToolCommands(projectDir, bmadDir, bmadCommandsDir);
console.log(chalk.green(`${this.name} configured:`)); console.log(chalk.green(`${this.name} configured:`));
console.log(chalk.dim(` - ${agentCount} agents installed`)); console.log(chalk.dim(` - ${agentCount} agents installed`));
console.log(chalk.dim(` - ${taskCount} tasks installed`)); if (workflowCommandCount > 0) {
console.log(chalk.dim(` - ${toolCount} tools installed`)); console.log(chalk.dim(` - ${workflowCommandCount} workflow commands generated`));
console.log(chalk.dim(` - ${workflowCount} workflows installed`)); }
console.log(chalk.dim(` - Rules directory: ${path.relative(projectDir, bmadRulesDir)}`)); if (taskToolResult.generated > 0) {
console.log(
chalk.dim(
` - ${taskToolResult.generated} task/tool commands generated (${taskToolResult.tasks} tasks, ${taskToolResult.tools} tools)`,
),
);
}
console.log(chalk.dim(` - Commands directory: ${path.relative(projectDir, bmadCommandsDir)}`));
return { return {
success: true, success: true,
agents: agentCount, agents: agentCount,
tasks: taskCount, tasks: taskToolResult.tasks || 0,
tools: toolCount, tools: taskToolResult.tools || 0,
workflows: workflowCount, workflows: workflowCommandCount,
}; };
} }
/**
* Create BMAD index file for easy navigation
*/
async createBMADIndex(bmadRulesDir, agents, tasks, tools, workflows, modules) {
const indexPath = path.join(bmadRulesDir, 'index.mdc');
let content = `---
description: BMAD Method - Master Index
globs:
alwaysApply: true
---
# BMAD Method - Cursor Rules Index
This is the master index for all BMAD agents, tasks, tools, and workflows available in your project.
## Installation Complete!
BMAD rules have been installed to: \`.cursor/rules/bmad/\`
**Note:** BMAD does not modify your \`.cursorrules\` file. You manage that separately.
## How to Use
- Reference specific agents: @bmad/{module}/agents/{agent-name}
- Reference specific tasks: @bmad/{module}/tasks/{task-name}
- Reference specific tools: @bmad/{module}/tools/{tool-name}
- Reference specific workflows: @bmad/{module}/workflows/{workflow-name}
- Reference entire modules: @bmad/{module}
- Reference this index: @bmad/index
## Available Modules
`;
for (const module of modules) {
content += `### ${module.toUpperCase()}\n\n`;
// List agents for this module
const moduleAgents = agents.filter((a) => a.module === module);
if (moduleAgents.length > 0) {
content += `**Agents:**\n`;
for (const agent of moduleAgents) {
content += `- @bmad/${module}/agents/${agent.name} - ${agent.name}\n`;
}
content += '\n';
}
// List tasks for this module
const moduleTasks = tasks.filter((t) => t.module === module);
if (moduleTasks.length > 0) {
content += `**Tasks:**\n`;
for (const task of moduleTasks) {
content += `- @bmad/${module}/tasks/${task.name} - ${task.name}\n`;
}
content += '\n';
}
// List tools for this module
const moduleTools = tools.filter((t) => t.module === module);
if (moduleTools.length > 0) {
content += `**Tools:**\n`;
for (const tool of moduleTools) {
content += `- @bmad/${module}/tools/${tool.name} - ${tool.name}\n`;
}
content += '\n';
}
// List workflows for this module
const moduleWorkflows = workflows.filter((w) => w.module === module);
if (moduleWorkflows.length > 0) {
content += `**Workflows:**\n`;
for (const workflow of moduleWorkflows) {
content += `- @bmad/${module}/workflows/${workflow.name} - ${workflow.name}\n`;
}
content += '\n';
}
}
content += `
## Quick Reference
- All BMAD rules are Manual type - reference them explicitly when needed
- Agents provide persona-based assistance with specific expertise
- Tasks are reusable workflows for common operations
- Tools provide specialized functionality
- Workflows orchestrate multi-step processes
- Each agent includes an activation block for proper initialization
## Configuration
BMAD rules are configured as Manual rules (alwaysApply: false) to give you control
over when they're included in your context. Reference them explicitly when you need
specific agent expertise, task workflows, tools, or guided workflows.
`;
await this.writeFile(indexPath, content);
}
/**
* Read and process file content
*/
async readAndProcess(filePath, metadata) {
const fs = require('fs-extra');
const content = await fs.readFile(filePath, 'utf8');
return this.processContent(content, metadata);
}
/**
* Override processContent to add MDC metadata header for Cursor
* @param {string} content - File content
* @param {Object} metadata - File metadata
* @returns {string} Processed content with MDC header
*/
processContent(content, metadata = {}) {
// First apply base processing (includes activation injection for agents)
let processed = super.processContent(content, metadata);
// Strip any existing frontmatter from the processed content
// This prevents duplicate frontmatter blocks
const frontmatterRegex = /^---\s*\n[\s\S]*?\n---\s*\n/;
if (frontmatterRegex.test(processed)) {
processed = processed.replace(frontmatterRegex, '');
}
// Determine the type and description based on content
const isAgent = content.includes('<agent');
const isTask = content.includes('<task');
const isTool = content.includes('<tool');
const isWorkflow = content.includes('workflow:') || content.includes('name:');
let description = '';
let globs = '';
if (isAgent) {
// Extract agent title if available
const titleMatch = content.match(/title="([^"]+)"/);
const title = titleMatch ? titleMatch[1] : metadata.name;
description = `BMAD ${metadata.module.toUpperCase()} Agent: ${title}`;
globs = '';
} else if (isTask) {
// Extract task name if available
const nameMatch = content.match(/name="([^"]+)"/);
const taskName = nameMatch ? nameMatch[1] : metadata.name;
description = `BMAD ${metadata.module.toUpperCase()} Task: ${taskName}`;
globs = '';
} else if (isTool) {
// Extract tool name if available
const nameMatch = content.match(/name="([^"]+)"/);
const toolName = nameMatch ? nameMatch[1] : metadata.name;
description = `BMAD ${metadata.module.toUpperCase()} Tool: ${toolName}`;
globs = '';
} else if (isWorkflow) {
// Workflow
description = `BMAD ${metadata.module.toUpperCase()} Workflow: ${metadata.name}`;
globs = '';
} else {
description = `BMAD ${metadata.module.toUpperCase()}: ${metadata.name}`;
globs = '';
}
// Create MDC metadata header
const mdcHeader = `---
description: ${description}
globs: ${globs}
alwaysApply: false
---
`;
// Add the MDC header to the processed content
return mdcHeader + processed;
}
/**
* Wrap launcher content with MDC metadata (without base processing)
* Launchers are already complete and should not have activation headers injected
*/
wrapLauncherWithMDC(launcherContent, metadata = {}) {
// Strip the launcher's frontmatter - we'll replace it with MDC frontmatter
const frontmatterRegex = /^---\s*\n[\s\S]*?\n---\s*\n/;
const contentWithoutFrontmatter = launcherContent.replace(frontmatterRegex, '');
// Extract metadata from launcher frontmatter for MDC description
const nameMatch = launcherContent.match(/name:\s*"([^"]+)"/);
const name = nameMatch ? nameMatch[1] : metadata.name;
const description = `BMAD ${metadata.module.toUpperCase()} Agent: ${name}`;
// Create MDC metadata header
const mdcHeader = `---
description: ${description}
globs:
alwaysApply: false
---
`;
// Return MDC header + launcher content (without its original frontmatter)
return mdcHeader + contentWithoutFrontmatter;
}
/** /**
* Install a custom agent launcher for Cursor * Install a custom agent launcher for Cursor
* @param {string} projectDir - Project directory * @param {string} projectDir - Project directory
@ -373,7 +127,7 @@ alwaysApply: false
* @returns {Object|null} Info about created command * @returns {Object|null} Info about created command
*/ */
async installCustomAgentLauncher(projectDir, agentName, agentPath, metadata) { async installCustomAgentLauncher(projectDir, agentName, agentPath, metadata) {
const customAgentsDir = path.join(projectDir, this.configDir, this.rulesDir, 'bmad', 'custom', 'agents'); const customAgentsDir = path.join(projectDir, this.configDir, this.commandsDir, 'bmad', 'custom', 'agents');
if (!(await this.exists(path.join(projectDir, this.configDir)))) { if (!(await this.exists(path.join(projectDir, this.configDir)))) {
return null; // IDE not configured for this project return null; // IDE not configured for this project
@ -393,22 +147,21 @@ alwaysApply: false
</agent-activation> </agent-activation>
`; `;
// Cursor uses MDC format with metadata header // Cursor uses YAML frontmatter matching Claude Code format
const mdcContent = `--- const commandContent = `---
description: "${agentName} agent" name: '${agentName}'
globs: description: '${agentName} agent'
alwaysApply: false
--- ---
${launcherContent} ${launcherContent}
`; `;
const launcherPath = path.join(customAgentsDir, `${agentName}.mdc`); const launcherPath = path.join(customAgentsDir, `${agentName}.md`);
await this.writeFile(launcherPath, mdcContent); await this.writeFile(launcherPath, commandContent);
return { return {
path: launcherPath, path: launcherPath,
command: `@${agentName}`, command: `/bmad/custom/agents/${agentName}`,
}; };
} }
} }

View File

@ -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 { WorkflowPromptGenerator } = require('./shared/workflow-prompt-generator');
/** /**
* GitHub Copilot setup handler * GitHub Copilot setup handler
@ -12,6 +13,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,14 +96,12 @@ class GitHubCopilotSetup extends BaseIdeSetup {
async setup(projectDir, bmadDir, options = {}) { async setup(projectDir, bmadDir, options = {}) {
console.log(chalk.cyan(`Setting up ${this.name}...`)); console.log(chalk.cyan(`Setting up ${this.name}...`));
// Configure VS Code settings using pre-collected config if available
const config = options.preCollectedConfig || {};
await this.configureVsCodeSettings(projectDir, { ...options, ...config });
// Create .github/agents directory // Create .github/agents directory
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);
@ -117,22 +117,37 @@ class GitHubCopilotSetup extends BaseIdeSetup {
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 bmd- prefix: bmd-custom-{module}-{name}.agent.md
const targetPath = path.join(agentsDir, `bmd-custom-${artifact.module}-${artifact.name}.agent.md`); const agentFileName = `bmd-custom-${artifact.module}-${artifact.name}`;
const targetPath = path.join(agentsDir, `${agentFileName}.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: ${agentFileName}`));
} }
// Generate workflow prompts from config (shared logic)
// Each prompt includes nextSteps guidance for the agent to suggest next workflows
const promptGen = new WorkflowPromptGenerator();
const promptRecommendations = await promptGen.generatePromptFiles(promptsDir, options.selectedModules || []);
const promptCount = Object.keys(promptRecommendations).length;
// Configure VS Code settings using pre-collected config if available
const config = options.preCollectedConfig || {};
await this.configureVsCodeSettings(projectDir, { ...options, ...config, promptRecommendations });
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(` - ${promptCount} workflow prompts configured`));
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 available in VS Code Chat view'));
console.log(chalk.dim(' Workflow prompts show as new chat starters'));
return { return {
success: true, success: true,
agents: agentCount, agents: agentCount,
prompts: promptCount,
settings: true, settings: true,
}; };
} }
@ -199,6 +214,11 @@ class GitHubCopilotSetup extends BaseIdeSetup {
}; };
} }
// Add prompt file recommendations for new chat starters
if (options.promptRecommendations && Object.keys(options.promptRecommendations).length > 0) {
bmadSettings['chat.promptFilesRecommendations'] = options.promptRecommendations;
}
// Merge settings (existing take precedence) // Merge settings (existing take precedence)
const mergedSettings = { ...bmadSettings, ...existingSettings }; const mergedSettings = { ...bmadSettings, ...existingSettings };
@ -307,6 +327,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('bmd-') && file.endsWith('.prompt.md')) {
await fs.remove(path.join(promptsDir, file));
removed++;
}
}
if (removed > 0) {
console.log(chalk.dim(` Cleaned up ${removed} existing BMAD prompt files`));
}
}
} }
/** /**

View File

@ -0,0 +1,61 @@
const path = require('node:path');
const fs = require('fs-extra');
const { workflowPromptsConfig } = require('./workflow-prompts-config');
/**
* Generate workflow prompt recommendations for IDE new chat starters
* Uses static configuration from workflow-prompts-config.js which mirrors
* the workflows documented in quick-start.md
*
* The implementation-readiness and sprint-planning workflows update
* VS Code settings to toggle which prompts are shown based on project phase.
*/
class WorkflowPromptGenerator {
/**
* Get workflow prompts for selected modules
* @param {Array<string>} selectedModules - Modules to include (e.g., ['bmm', 'bmgd'])
* @returns {Array<Object>} Array of workflow prompt configurations
*/
getWorkflowPrompts(selectedModules = []) {
const allPrompts = [];
// Always include core prompts
if (workflowPromptsConfig.core) {
allPrompts.push(...workflowPromptsConfig.core);
}
// Add prompts for each selected module
for (const moduleName of selectedModules) {
if (workflowPromptsConfig[moduleName]) {
allPrompts.push(...workflowPromptsConfig[moduleName]);
}
}
return allPrompts;
}
/**
* Generate prompt files for an IDE
* @param {string} promptsDir - Directory to write prompt files
* @param {Array<string>} selectedModules - Modules to include
* @returns {Object} Map of prompt names to true for VS Code settings
*/
async generatePromptFiles(promptsDir, selectedModules = []) {
const prompts = this.getWorkflowPrompts(selectedModules);
const recommendations = {};
for (const prompt of prompts) {
const promptContent = ['---', `agent: ${prompt.agent}`, `description: "${prompt.description}"`, '---', '', prompt.prompt, ''].join(
'\n',
);
const promptFilePath = path.join(promptsDir, `bmd-${prompt.name}.prompt.md`);
await fs.writeFile(promptFilePath, promptContent);
recommendations[`bmd-${prompt.name}`] = true;
}
return recommendations;
}
}
module.exports = { WorkflowPromptGenerator };

View File

@ -0,0 +1,192 @@
/**
* Workflow prompt configuration for IDE new chat starters
*
* This configuration defines the workflow prompts that appear as suggestions
* when starting a new chat in VS Code (via chat.promptFilesRecommendations).
*
* The implementation-readiness and sprint-planning workflows update the
* VS Code settings to toggle which prompts are shown based on project phase.
*
* Reference: docs/modules/bmm-bmad-method/quick-start.md
*/
const workflowPromptsConfig = {
// BMad Method Module (bmm) - Standard development workflow
bmm: [
// ═══════════════════════════════════════════════════════════════════════
// Phase 1 - Analysis (Optional)
// ═══════════════════════════════════════════════════════════════════════
{
name: 'workflow-init',
agent: 'bmd-custom-bmm-analyst',
shortcut: 'WI',
description: '[WI] Initialize workflow and choose planning track',
prompt: '*workflow-init',
},
{
name: 'brainstorm',
agent: 'bmd-custom-bmm-analyst',
shortcut: 'BP',
description: '[BP] Brainstorm project ideas and concepts',
prompt: '*brainstorm-project',
},
{
name: 'workflow-status',
agent: 'bmd-custom-bmm-pm',
shortcut: 'WS',
description: '[WS] Check current workflow status and next steps',
prompt: '*workflow-status',
},
// ═══════════════════════════════════════════════════════════════════════
// Phase 2 - Planning (Required)
// ═══════════════════════════════════════════════════════════════════════
{
name: 'prd',
agent: 'bmd-custom-bmm-pm',
shortcut: 'PD',
description: '[PD] Create Product Requirements Document (PRD)',
prompt: '*prd',
},
{
name: 'ux-design',
agent: 'bmd-custom-bmm-ux-designer',
shortcut: 'UD',
description: '[UD] Create UX Design specification',
prompt: '*ux-design',
},
// ═══════════════════════════════════════════════════════════════════════
// Phase 3 - Solutioning
// ═══════════════════════════════════════════════════════════════════════
{
name: 'create-architecture',
agent: 'bmd-custom-bmm-architect',
shortcut: 'CA',
description: '[CA] Create system architecture document',
prompt: '*create-architecture',
},
{
name: 'epics-stories',
agent: 'bmd-custom-bmm-pm',
shortcut: 'ES',
description: '[ES] Create Epics and User Stories from PRD',
prompt: '*epics-stories',
},
{
name: 'implementation-readiness',
agent: 'bmd-custom-bmm-architect',
shortcut: 'IR',
description: '[IR] Check implementation readiness across all docs',
prompt: '*implementation-readiness',
},
{
name: 'sprint-planning',
agent: 'bmd-custom-bmm-sm',
shortcut: 'SP',
description: '[SP] Initialize sprint planning from epics',
prompt: '*sprint-planning',
},
// ═══════════════════════════════════════════════════════════════════════
// Phase 4 - Implementation: The "Keep Going" Cycle
// SM → create-story → DEV → dev-story → code-review → (create-story | retrospective)
// ═══════════════════════════════════════════════════════════════════════
{
name: 'create-story',
agent: 'bmd-custom-bmm-sm',
shortcut: 'CS',
description: '[CS] Create developer-ready story from epic',
prompt: '*create-story',
},
{
name: 'dev-story',
agent: 'bmd-custom-bmm-dev',
shortcut: 'DS',
description: '[DS] Implement the current story',
prompt: '*dev-story',
},
{
name: 'code-review',
agent: 'bmd-custom-bmm-dev',
shortcut: 'CR',
description: '[CR] Perform code review on implementation',
prompt: '*code-review',
},
{
name: 'retrospective',
agent: 'bmd-custom-bmm-sm',
shortcut: 'ER',
description: '[ER] Run epic retrospective after completion',
prompt: '*epic-retrospective',
},
{
name: 'correct-course',
agent: 'bmd-custom-bmm-sm',
shortcut: 'CC',
description: '[CC] Course correction when things go off track',
prompt: '*correct-course',
},
],
// BMad Game Development Module (bmgd)
bmgd: [
// Implementation cycle
{
name: 'game-implement',
agent: 'bmd-custom-bmgd-game-dev',
shortcut: 'GI',
description: '[GI] Implement game feature',
prompt: '*game-implement',
},
{
name: 'game-qa',
agent: 'bmd-custom-bmgd-game-qa',
shortcut: 'GQ',
description: '[GQ] Test and QA game feature',
prompt: '*game-qa',
},
// Planning & Design
{
name: 'game-design',
agent: 'bmd-custom-bmgd-game-designer',
shortcut: 'GD',
description: '[GD] Design game mechanics and systems',
prompt: '*game-design',
},
{
name: 'game-architecture',
agent: 'bmd-custom-bmgd-game-architect',
shortcut: 'GA',
description: '[GA] Create game technical architecture',
prompt: '*game-architecture',
},
{
name: 'game-sprint',
agent: 'bmd-custom-bmgd-game-scrum-master',
shortcut: 'GS',
description: '[GS] Plan game development sprint',
prompt: '*game-sprint',
},
],
// Core agents (always available)
core: [
{
name: 'list-tasks',
agent: 'bmd-custom-core-bmad-master',
shortcut: 'LT',
description: '[LT] List available tasks',
prompt: '*list-tasks',
},
{
name: 'list-workflows',
agent: 'bmd-custom-core-bmad-master',
shortcut: 'LW',
description: '[LW] List available workflows',
prompt: '*list-workflows',
},
],
};
module.exports = { workflowPromptsConfig };