feat: refactor Cursor IDE setup to do command generation and cleanup instead of rules#1283
Conversation
…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.
…r IDE setup - Reformatted the constructor method for consistency. - Updated the command path syntax in the Cursor IDE setup to use a more standard format.
- 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.
📝 WalkthroughWalkthroughMigrates Cursor IDE configuration from rules-based to commands-based directory structure. Updates file paths from Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related issues
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In @tools/cli/installers/lib/ide/cursor.js:
- Around line 23-37: In the cleanup function, filesystem removes can throw and
currently propagate; wrap each await fs.remove(bmadRulesDir) and await
fs.remove(bmadCommandsDir) (or the whole removal block) in try/catch, log
failures (e.g., console.error or chalk.red with the path and error.message) and
continue so a single failure doesn’t abort the rest of the cleanup; ensure you
still report successful removals with the existing console.log calls.
- Around line 61-75: The destructuring from collectAgentArtifacts currently
pulls counts: agentCounts which is never used; remove the unused agentCounts
from the destructuring (keep artifacts: agentArtifacts) or if you intend to
validate/log the count, use agentCounts (e.g., compare to or log alongside the
agentCount returned by writeAgentLaunchers). Update the line that calls
collectAgentArtifacts and any related logging/validation to either drop
agentCounts or consume it, referencing collectAgentArtifacts, agentArtifacts,
agentCounts, and writeAgentLaunchers in your changes.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
samples/sample-custom-modules/sample-unitary-module/agents/toolsmith/toolsmith-sidecar/knowledge/installers.mdsamples/sample-custom-modules/sample-unitary-module/agents/toolsmith/toolsmith-sidecar/knowledge/modules.mdtools/cli/installers/lib/ide/cursor.js
🧰 Additional context used
📓 Path-based instructions (2)
**/*
⚙️ CodeRabbit configuration file
**/*: Focus on inconsistencies, contradictions, edge cases and serious issues.
Avoid commenting on minor issues such as linting, formatting and style issues.
When providing code suggestions, use GitHub's suggestion format:<code changes>
Files:
samples/sample-custom-modules/sample-unitary-module/agents/toolsmith/toolsmith-sidecar/knowledge/modules.mdsamples/sample-custom-modules/sample-unitary-module/agents/toolsmith/toolsmith-sidecar/knowledge/installers.mdtools/cli/installers/lib/ide/cursor.js
**/*.js
⚙️ CodeRabbit configuration file
**/*.js: CLI tooling code. Check for: missing error handling on fs operations,
path.join vs string concatenation, proper cleanup in error paths.
Flag any process.exit() without error message.
Files:
tools/cli/installers/lib/ide/cursor.js
🧬 Code graph analysis (1)
tools/cli/installers/lib/ide/cursor.js (1)
tools/cli/installers/lib/ide/shared/task-tool-command-generator.js (2)
projectDir(16-58)item(63-82)
🔇 Additional comments (7)
tools/cli/installers/lib/ide/cursor.js (5)
6-6: LGTM!The import and property additions are consistent with the migration to a commands-based structure.
Also applies to: 16-16
77-92: Good filtering logic for workflow artifacts.The selective processing of only
workflow-commandartifacts while skippingworkflow-launcherREADMEs is appropriate for the Cursor command structure.However, note that the
writeFile()operation on Line 88 lacks error handling, consistent with the broader pattern in this method that should be addressed.
94-119: Comprehensive logging and return structure.The conditional logging for workflows and task/tool commands provides good visibility into what was generated, and the return object properly includes all relevant counts with defensive fallbacks.
138-148: @${agentPath} reference format is correct and supported in Cursor.Cursor IDE supports
@symbols for file path references in interactive contexts (Composer, Chat, Cmd-K). The@${agentPath}syntax on line 141 is embedded as instructional guidance within the slash command's markdown content, directing the user or AI to reference the agent file using Cursor's standard @ syntax. This pattern is consistently used across all IDE setup files in the codebase (windsurf, codex, opencode, claude-code, github-copilot, etc.), indicating it's an established and correct approach.
164-164: ThecreateIdeSlashCommands()function is exported from installer.js but is not currently called anywhere in the codebase. The API change to return/bmad/custom/agents/${agentName}is consistently implemented across all IDE handlers with proper error handling in the manager. However, since this function has no active callers, there are no downstream consumers to verify at this time. This may indicate the function is prepared for future integration or is unused code.samples/sample-custom-modules/sample-unitary-module/agents/toolsmith/toolsmith-sidecar/knowledge/modules.md (1)
131-131: LGTM!Documentation accurately reflects the migration from
.cursor/rules/bmad/with.mdcfiles to.cursor/commands/bmad/with.mdfiles, consistent with the implementation changes.samples/sample-custom-modules/sample-unitary-module/agents/toolsmith/toolsmith-sidecar/knowledge/installers.md (1)
64-64: LGTM!Documentation correctly updated to reflect both the path migration and the frontmatter format change from MDC to YAML, aligning with the implementation.
| async cleanup(projectDir) { | ||
| const fs = require('fs-extra'); | ||
| 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)) { | ||
| await fs.remove(bmadRulesDir); | ||
| 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}`)); | ||
| } | ||
| } |
There was a problem hiding this comment.
Add error handling for filesystem operations.
Per coding guidelines for CLI tooling, filesystem operations should include proper error handling. If either fs.remove() call fails, the error propagates without recovery, potentially leaving the cleanup in a partial state.
🛡️ Proposed fix with error handling
async cleanup(projectDir) {
const fs = require('fs-extra');
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)) {
- await fs.remove(bmadRulesDir);
- 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}`));
- }
+ try {
+ if (await fs.pathExists(bmadRulesDir)) {
+ await fs.remove(bmadRulesDir);
+ console.log(chalk.dim(` Removed old BMAD rules from ${this.name}`));
+ }
+ } catch (error) {
+ console.error(chalk.yellow(` Warning: Failed to remove BMAD rules: ${error.message}`));
+ }
+
+ try {
+ if (await fs.pathExists(bmadCommandsDir)) {
+ await fs.remove(bmadCommandsDir);
+ console.log(chalk.dim(` Removed old BMAD commands from ${this.name}`));
+ }
+ } catch (error) {
+ console.error(chalk.yellow(` Warning: Failed to remove BMAD commands: ${error.message}`));
+ }
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| async cleanup(projectDir) { | |
| const fs = require('fs-extra'); | |
| 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)) { | |
| await fs.remove(bmadRulesDir); | |
| 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}`)); | |
| } | |
| } | |
| async cleanup(projectDir) { | |
| const fs = require('fs-extra'); | |
| const bmadRulesDir = path.join(projectDir, this.configDir, this.rulesDir, 'bmad'); | |
| const bmadCommandsDir = path.join(projectDir, this.configDir, this.commandsDir, 'bmad'); | |
| try { | |
| if (await fs.pathExists(bmadRulesDir)) { | |
| await fs.remove(bmadRulesDir); | |
| console.log(chalk.dim(` Removed old BMAD rules from ${this.name}`)); | |
| } | |
| } catch (error) { | |
| console.error(chalk.yellow(` Warning: Failed to remove BMAD rules: ${error.message}`)); | |
| } | |
| try { | |
| if (await fs.pathExists(bmadCommandsDir)) { | |
| await fs.remove(bmadCommandsDir); | |
| console.log(chalk.dim(` Removed old BMAD commands from ${this.name}`)); | |
| } | |
| } catch (error) { | |
| console.error(chalk.yellow(` Warning: Failed to remove BMAD commands: ${error.message}`)); | |
| } | |
| } |
🤖 Prompt for AI Agents
In @tools/cli/installers/lib/ide/cursor.js around lines 23 - 37, In the cleanup
function, filesystem removes can throw and currently propagate; wrap each await
fs.remove(bmadRulesDir) and await fs.remove(bmadCommandsDir) (or the whole
removal block) in try/catch, log failures (e.g., console.error or chalk.red with
the path and error.message) and continue so a single failure doesn’t abort the
rest of the cleanup; ensure you still report successful removals with the
existing console.log calls.
| const { artifacts: agentArtifacts, counts: agentCounts } = await agentGen.collectAgentArtifacts(bmadDir, options.selectedModules || []); | ||
|
|
||
| // Create directories for each module | ||
| const modules = new Set(); | ||
| for (const item of [...agents, ...tasks, ...tools, ...workflows]) modules.add(item.module); | ||
|
|
||
| for (const module of modules) { | ||
| await this.ensureDir(path.join(bmadRulesDir, module)); | ||
| await this.ensureDir(path.join(bmadRulesDir, 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 | ||
| let agentCount = 0; | ||
| 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`); | ||
|
|
||
| await this.writeFile(targetPath, content); | ||
| agentCount++; | ||
| modules.add(artifact.module); | ||
| } | ||
|
|
||
| // 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++; | ||
| for (const module of modules) { | ||
| await this.ensureDir(path.join(bmadCommandsDir, module)); | ||
| await this.ensureDir(path.join(bmadCommandsDir, module, 'agents')); | ||
| } | ||
|
|
||
| // 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`); | ||
| // Write agent launcher files | ||
| const agentCount = await agentGen.writeAgentLaunchers(bmadCommandsDir, agentArtifacts); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's look at the full context of the file to understand the setup() method
fd cursor.js --type f | head -5Repository: bmad-code-org/BMAD-METHOD
Length of output: 107
🏁 Script executed:
# Get the file size to determine how to read it
wc -l tools/cli/installers/lib/ide/cursor.jsRepository: bmad-code-org/BMAD-METHOD
Length of output: 111
🏁 Script executed:
# Read the file to understand the full context
cat -n tools/cli/installers/lib/ide/cursor.jsRepository: bmad-code-org/BMAD-METHOD
Length of output: 7732
🏁 Script executed:
# Search for any usage of agentCounts variable in the entire codebase
rg 'agentCounts' --type=js -B 2 -A 2Repository: bmad-code-org/BMAD-METHOD
Length of output: 1734
🏁 Script executed:
# Check the collectAgentArtifacts method to understand what it returns
rg 'collectAgentArtifacts' --type=js -B 2 -A 5Repository: bmad-code-org/BMAD-METHOD
Length of output: 13347
🏁 Script executed:
# Look for writeAgentLaunchers to understand its signature and return value
rg 'writeAgentLaunchers' --type=js -B 2 -A 8Repository: bmad-code-org/BMAD-METHOD
Length of output: 3280
Remove unused agentCounts variable from destructuring.
Line 61 destructures counts: agentCounts from collectAgentArtifacts(), but this variable is never used anywhere in the method. The actual agent count comes from writeAgentLaunchers() on line 75. Either remove agentCounts from the destructuring or use it for validation/logging if it serves a purpose.
🤖 Prompt for AI Agents
In @tools/cli/installers/lib/ide/cursor.js around lines 61 - 75, The
destructuring from collectAgentArtifacts currently pulls counts: agentCounts
which is never used; remove the unused agentCounts from the destructuring (keep
artifacts: agentArtifacts) or if you intend to validate/log the count, use
agentCounts (e.g., compare to or log alongside the agentCount returned by
writeAgentLaunchers). Update the line that calls collectAgentArtifacts and any
related logging/validation to either drop agentCounts or consume it, referencing
collectAgentArtifacts, agentArtifacts, agentCounts, and writeAgentLaunchers in
your changes.
|
thank you @thecontstruct and also @wsmoak for the input and help here! |
|
I see it's already merged, just tried it out by installing from main and ✅ we have commands in Cursor now! |
What
Updated Cursor IDE setup to create slash commands in
.cursor/commands/bmad/instead of rules in.cursor/rules/bmad/. Commands now use plain Markdown files with YAML frontmatter (matching Claude Code format) instead of MDC format.Why
Cursor IDE supports slash commands as plain Markdown files in
.cursor/commands/directory that users explicitly invoke, rather than rules that auto-apply. This change aligns with Cursor's command model and matches Claude Code's implementation pattern for consistency across IDE handlers.How
CursorSetup.setup()to follow Claude Code's exact pattern: useAgentCommandGenerator.writeAgentLaunchers()directly, write workflow artifacts with their existing YAML frontmatter, and useTaskToolCommandGeneratorfor tasks/toolsprocessContent()override,wrapLauncherWithMDC(),readAndProcess()) since the generators already provide correct YAML frontmatter.cursor/rules/bmad/and.cursor/commands/bmad/directories to support migration from rules to commandsTesting
Verified that commands appear in Cursor's slash command palette (
/), file naming matches Claude Code exactly, frontmatter uses YAML format (not MDC), and cleanup removes both old rules and commands directories.