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.
This commit is contained in:
parent
d19cca79d2
commit
677a00280b
|
|
@ -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 |
|
||||||
|
|
|
||||||
|
|
@ -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 |
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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}`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue