From d19cca79d2d607d8a099b57df93aee4f202d1cf7 Mon Sep 17 00:00:00 2001 From: Q00 <31264094+Q00@users.noreply.github.com> Date: Thu, 8 Jan 2026 16:42:22 +0900 Subject: [PATCH 1/2] fix: resolve ERR_REQUIRE_ESM by using dynamic import for inquirer (#1278) Inquirer v9+ is ESM-only, causing ERR_REQUIRE_ESM when loaded via require() in CommonJS. Convert all require('inquirer') calls to dynamic import('inquirer') across 8 CLI files. Fixes #1197 --- tools/cli/commands/install.js | 2 +- .../installers/lib/core/config-collector.js | 12 +++++++++- tools/cli/installers/lib/core/installer.js | 7 +++--- tools/cli/installers/lib/ide/antigravity.js | 6 ++--- tools/cli/installers/lib/ide/claude-code.js | 6 ++--- tools/cli/installers/lib/ide/codex.js | 2 +- .../cli/installers/lib/ide/github-copilot.js | 2 +- tools/cli/lib/ui.js | 23 ++++++++++++++++++- 8 files changed, 45 insertions(+), 15 deletions(-) diff --git a/tools/cli/commands/install.js b/tools/cli/commands/install.js index 1ae5d4c0..f71d5679 100644 --- a/tools/cli/commands/install.js +++ b/tools/cli/commands/install.js @@ -1,6 +1,5 @@ const chalk = require('chalk'); const path = require('node:path'); -const inquirer = require('inquirer').default || require('inquirer'); const { Installer } = require('../installers/lib/core/installer'); const { UI } = require('../lib/ui'); @@ -72,6 +71,7 @@ module.exports = { console.log(chalk.dim(' • ElevenLabs AI (150+ premium voices)')); console.log(chalk.dim(' • Piper TTS (50+ free voices)\n')); + const { default: inquirer } = await import('inquirer'); await inquirer.prompt([ { type: 'input', diff --git a/tools/cli/installers/lib/core/config-collector.js b/tools/cli/installers/lib/core/config-collector.js index fad0f108..fb48b68d 100644 --- a/tools/cli/installers/lib/core/config-collector.js +++ b/tools/cli/installers/lib/core/config-collector.js @@ -2,10 +2,18 @@ const path = require('node:path'); const fs = require('fs-extra'); const yaml = require('yaml'); const chalk = require('chalk'); -const inquirer = require('inquirer').default || require('inquirer'); const { getProjectRoot, getModulePath } = require('../../../lib/project-root'); const { CLIUtils } = require('../../../lib/cli-utils'); +// Lazy-load inquirer (ESM module) to avoid ERR_REQUIRE_ESM +let _inquirer = null; +async function getInquirer() { + if (!_inquirer) { + _inquirer = (await import('inquirer')).default; + } + return _inquirer; +} + class ConfigCollector { constructor() { this.collectedConfig = {}; @@ -175,6 +183,7 @@ class ConfigCollector { * @returns {boolean} True if new fields were prompted, false if all fields existed */ async collectModuleConfigQuick(moduleName, projectDir, silentMode = true) { + const inquirer = await getInquirer(); this.currentProjectDir = projectDir; // Load existing config if not already loaded @@ -493,6 +502,7 @@ class ConfigCollector { * @param {boolean} skipCompletion - Skip showing completion message (for early core collection) */ async collectModuleConfig(moduleName, projectDir, skipLoadExisting = false, skipCompletion = false) { + const inquirer = await getInquirer(); this.currentProjectDir = projectDir; // Load existing config if needed and not already loaded if (!skipLoadExisting && !this.existingConfig) { diff --git a/tools/cli/installers/lib/core/installer.js b/tools/cli/installers/lib/core/installer.js index 816dbbbc..8b7e05fd 100644 --- a/tools/cli/installers/lib/core/installer.js +++ b/tools/cli/installers/lib/core/installer.js @@ -2,7 +2,6 @@ const path = require('node:path'); const fs = require('fs-extra'); const chalk = require('chalk'); const ora = require('ora'); -const inquirer = require('inquirer').default || require('inquirer'); const { Detector } = require('./detector'); const { Manifest } = require('./manifest'); const { ModuleManager } = require('../modules/manager'); @@ -2140,7 +2139,7 @@ class Installer { * Private: Prompt for update action */ async promptUpdateAction() { - const inquirer = require('inquirer').default || require('inquirer'); + const { default: inquirer } = await import('inquirer'); return await inquirer.prompt([ { type: 'list', @@ -2157,7 +2156,7 @@ class Installer { * @param {Object} _legacyV4 - Legacy V4 detection result (unused in simplified version) */ async handleLegacyV4Migration(_projectDir, _legacyV4) { - const inquirer = require('inquirer').default || require('inquirer'); + const { default: inquirer } = await import('inquirer'); console.log(''); console.log(chalk.yellow.bold('⚠️ Legacy BMAD v4 detected')); @@ -2438,7 +2437,7 @@ class Installer { console.log(chalk.yellow(`\n⚠️ Found ${customModulesWithMissingSources.length} custom module(s) with missing sources:`)); - const inquirer = require('inquirer').default || require('inquirer'); + const { default: inquirer } = await import('inquirer'); let keptCount = 0; let updatedCount = 0; let removedCount = 0; diff --git a/tools/cli/installers/lib/ide/antigravity.js b/tools/cli/installers/lib/ide/antigravity.js index a7fed6ea..c896d62d 100644 --- a/tools/cli/installers/lib/ide/antigravity.js +++ b/tools/cli/installers/lib/ide/antigravity.js @@ -58,7 +58,7 @@ class AntigravitySetup extends BaseIdeSetup { if (config.subagentChoices.install !== 'none') { // Ask for installation location - const inquirer = require('inquirer').default || require('inquirer'); + const { default: inquirer } = await import('inquirer'); const locationAnswer = await inquirer.prompt([ { type: 'list', @@ -297,7 +297,7 @@ class AntigravitySetup extends BaseIdeSetup { choices = await this.promptSubagentInstallation(config.subagents); if (choices.install !== 'none') { - const inquirer = require('inquirer').default || require('inquirer'); + const { default: inquirer } = await import('inquirer'); const locationAnswer = await inquirer.prompt([ { type: 'list', @@ -334,7 +334,7 @@ class AntigravitySetup extends BaseIdeSetup { * Prompt user for subagent installation preferences */ async promptSubagentInstallation(subagentConfig) { - const inquirer = require('inquirer').default || require('inquirer'); + const { default: inquirer } = await import('inquirer'); // First ask if they want to install subagents const { install } = await inquirer.prompt([ diff --git a/tools/cli/installers/lib/ide/claude-code.js b/tools/cli/installers/lib/ide/claude-code.js index 35e70b0b..f2a33221 100644 --- a/tools/cli/installers/lib/ide/claude-code.js +++ b/tools/cli/installers/lib/ide/claude-code.js @@ -57,7 +57,7 @@ class ClaudeCodeSetup extends BaseIdeSetup { if (config.subagentChoices.install !== 'none') { // Ask for installation location - const inquirer = require('inquirer').default || require('inquirer'); + const { default: inquirer } = await import('inquirer'); const locationAnswer = await inquirer.prompt([ { type: 'list', @@ -305,7 +305,7 @@ class ClaudeCodeSetup extends BaseIdeSetup { choices = await this.promptSubagentInstallation(config.subagents); if (choices.install !== 'none') { - const inquirer = require('inquirer').default || require('inquirer'); + const { default: inquirer } = await import('inquirer'); const locationAnswer = await inquirer.prompt([ { type: 'list', @@ -342,7 +342,7 @@ class ClaudeCodeSetup extends BaseIdeSetup { * Prompt user for subagent installation preferences */ async promptSubagentInstallation(subagentConfig) { - const inquirer = require('inquirer').default || require('inquirer'); + const { default: inquirer } = await import('inquirer'); // First ask if they want to install subagents const { install } = await inquirer.prompt([ diff --git a/tools/cli/installers/lib/ide/codex.js b/tools/cli/installers/lib/ide/codex.js index 9967057a..3ce9d910 100644 --- a/tools/cli/installers/lib/ide/codex.js +++ b/tools/cli/installers/lib/ide/codex.js @@ -21,7 +21,7 @@ class CodexSetup extends BaseIdeSetup { * @returns {Object} Collected configuration */ async collectConfiguration(options = {}) { - const inquirer = require('inquirer').default || require('inquirer'); + const { default: inquirer } = await import('inquirer'); let confirmed = false; let installLocation = 'global'; diff --git a/tools/cli/installers/lib/ide/github-copilot.js b/tools/cli/installers/lib/ide/github-copilot.js index 36d3eecb..b9dd5f98 100644 --- a/tools/cli/installers/lib/ide/github-copilot.js +++ b/tools/cli/installers/lib/ide/github-copilot.js @@ -1,7 +1,6 @@ const path = require('node:path'); const { BaseIdeSetup } = require('./_base-ide'); const chalk = require('chalk'); -const inquirer = require('inquirer').default || require('inquirer'); const { AgentCommandGenerator } = require('./shared/agent-command-generator'); /** @@ -22,6 +21,7 @@ class GitHubCopilotSetup extends BaseIdeSetup { * @returns {Object} Collected configuration */ async collectConfiguration(options = {}) { + const { default: inquirer } = await import('inquirer'); const config = {}; console.log('\n' + chalk.blue(' 🔧 VS Code Settings Configuration')); diff --git a/tools/cli/lib/ui.js b/tools/cli/lib/ui.js index ab055643..85f31ce1 100644 --- a/tools/cli/lib/ui.js +++ b/tools/cli/lib/ui.js @@ -1,11 +1,19 @@ const chalk = require('chalk'); -const inquirer = require('inquirer').default || require('inquirer'); const path = require('node:path'); const os = require('node:os'); const fs = require('fs-extra'); const { CLIUtils } = require('./cli-utils'); const { CustomHandler } = require('../installers/lib/custom/handler'); +// Lazy-load inquirer (ESM module) to avoid ERR_REQUIRE_ESM +let _inquirer = null; +async function getInquirer() { + if (!_inquirer) { + _inquirer = (await import('inquirer')).default; + } + return _inquirer; +} + /** * UI utilities for the installer */ @@ -15,6 +23,7 @@ class UI { * @returns {Object} Installation configuration */ async promptInstall() { + const inquirer = await getInquirer(); CLIUtils.displayLogo(); // Display version-specific start message from install-messages.yaml @@ -450,6 +459,7 @@ class UI { * @returns {Object} Tool configuration */ async promptToolSelection(projectDir, selectedModules) { + const inquirer = await getInquirer(); // Check for existing configured IDEs - use findBmadDir to detect custom folder names const { Detector } = require('../installers/lib/core/detector'); const { Installer } = require('../installers/lib/core/installer'); @@ -582,6 +592,7 @@ class UI { * @returns {Object} Update configuration */ async promptUpdate() { + const inquirer = await getInquirer(); const answers = await inquirer.prompt([ { type: 'confirm', @@ -606,6 +617,7 @@ class UI { * @returns {Array} Selected modules */ async promptModules(modules) { + const inquirer = await getInquirer(); const choices = modules.map((mod) => ({ name: `${mod.name} - ${mod.description}`, value: mod.id, @@ -637,6 +649,7 @@ class UI { * @returns {boolean} User confirmation */ async confirm(message, defaultValue = false) { + const inquirer = await getInquirer(); const { confirmed } = await inquirer.prompt([ { type: 'confirm', @@ -743,6 +756,7 @@ class UI { * @returns {Array} Module choices for inquirer */ async getModuleChoices(installedModuleIds, customContentConfig = null) { + const inquirer = await getInquirer(); const moduleChoices = []; const isNewInstallation = installedModuleIds.size === 0; @@ -823,6 +837,7 @@ class UI { * @returns {Array} Selected module IDs */ async selectModules(moduleChoices, defaultSelections = []) { + const inquirer = await getInquirer(); const moduleAnswer = await inquirer.prompt([ { type: 'checkbox', @@ -843,6 +858,7 @@ class UI { * @returns {Object} Directory answer from inquirer */ async promptForDirectory() { + const inquirer = await getInquirer(); return await inquirer.prompt([ { type: 'input', @@ -899,6 +915,7 @@ class UI { * @returns {boolean} Whether user confirmed */ async confirmDirectory(directory) { + const inquirer = await getInquirer(); const dirExists = await fs.pathExists(directory); if (dirExists) { @@ -1085,6 +1102,7 @@ class UI { * - GitHub Issue: paulpreibisch/AgentVibes#36 */ async promptAgentVibes(projectDir) { + const inquirer = await getInquirer(); CLIUtils.displaySection('🎤 Voice Features', 'Enable TTS for multi-agent conversations'); // Check if AgentVibes is already installed @@ -1235,6 +1253,7 @@ class UI { * @returns {Object} Custom content configuration */ async promptCustomContentSource() { + const inquirer = await getInquirer(); const customContentConfig = { hasCustomContent: true, sources: [] }; // Keep asking for more sources until user is done @@ -1372,6 +1391,7 @@ class UI { * @returns {Object} Result with selected custom modules and custom content config */ async handleCustomModulesInModifyFlow(directory, selectedModules) { + const inquirer = await getInquirer(); // Get existing installation to find custom modules const { existingInstall } = await this.getExistingInstallation(directory); @@ -1566,6 +1586,7 @@ class UI { * @returns {Promise} True if user wants to proceed, false if they cancel */ async showOldAlphaVersionWarning(installedVersion, currentVersion, bmadFolderName) { + const inquirer = await getInquirer(); const versionInfo = this.checkAlphaVersionAge(installedVersion, currentVersion); // Also warn if version is unknown or can't be parsed (legacy/unsupported) From 677a00280b42651719296227bcefc526e7fc5a56 Mon Sep 17 00:00:00 2001 From: Phil Date: Fri, 9 Jan 2026 03:39:32 -0500 Subject: [PATCH 2/2] 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. --- .../toolsmith-sidecar/knowledge/installers.md | 2 +- .../toolsmith-sidecar/knowledge/modules.md | 2 +- tools/cli/installers/lib/ide/cursor.js | 363 +++--------------- 3 files changed, 60 insertions(+), 307 deletions(-) diff --git a/samples/sample-custom-modules/sample-unitary-module/agents/toolsmith/toolsmith-sidecar/knowledge/installers.md b/samples/sample-custom-modules/sample-unitary-module/agents/toolsmith/toolsmith-sidecar/knowledge/installers.md index b6f8be22..d3bb907f 100644 --- a/samples/sample-custom-modules/sample-unitary-module/agents/toolsmith/toolsmith-sidecar/knowledge/installers.md +++ b/samples/sample-custom-modules/sample-unitary-module/agents/toolsmith/toolsmith-sidecar/knowledge/installers.md @@ -61,7 +61,7 @@ | -------------- | -------------- | ------------------------- | ----------------------------- | | claude-code | Claude Code | .claude/commands/ | .md with frontmatter | | 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 | | opencode | OpenCode | .opencode/ | .md | | windsurf | Windsurf | .windsurf/workflows/bmad/ | .md with workflow frontmatter | diff --git a/samples/sample-custom-modules/sample-unitary-module/agents/toolsmith/toolsmith-sidecar/knowledge/modules.md b/samples/sample-custom-modules/sample-unitary-module/agents/toolsmith/toolsmith-sidecar/knowledge/modules.md index 663fcc60..fa03b247 100644 --- a/samples/sample-custom-modules/sample-unitary-module/agents/toolsmith/toolsmith-sidecar/knowledge/modules.md +++ b/samples/sample-custom-modules/sample-unitary-module/agents/toolsmith/toolsmith-sidecar/knowledge/modules.md @@ -128,7 +128,7 @@ module.exports = { NewIdeSetup }; | IDE | Config Pattern | File Extension | | -------------- | ------------------------- | -------------- | | Claude Code | .claude/commands/bmad/ | .md | -| Cursor | .cursor/rules/bmad/ | .mdc | +| Cursor | .cursor/commands/bmad/ | .md | | Windsurf | .windsurf/workflows/bmad/ | .md | | GitHub Copilot | .github/ | .md | diff --git a/tools/cli/installers/lib/ide/cursor.js b/tools/cli/installers/lib/ide/cursor.js index 183bbced..61f374a4 100644 --- a/tools/cli/installers/lib/ide/cursor.js +++ b/tools/cli/installers/lib/ide/cursor.js @@ -3,6 +3,7 @@ const { BaseIdeSetup } = require('./_base-ide'); const chalk = require('chalk'); const { AgentCommandGenerator } = require('./shared/agent-command-generator'); const { WorkflowCommandGenerator } = require('./shared/workflow-command-generator'); +const { TaskToolCommandGenerator } = require('./shared/task-tool-command-generator'); /** * Cursor IDE setup handler @@ -12,6 +13,7 @@ class CursorSetup extends BaseIdeSetup { super('cursor', 'Cursor', true); // preferred IDE this.configDir = '.cursor'; this.rulesDir = 'rules'; + this.commandsDir = 'commands'; } /** @@ -21,11 +23,17 @@ class CursorSetup extends BaseIdeSetup { 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}`)); + } } /** @@ -40,330 +48,76 @@ class CursorSetup extends BaseIdeSetup { // Clean up old BMAD installation first await this.cleanup(projectDir); - // Create .cursor/rules directory structure + // Create .cursor/commands directory structure const cursorDir = path.join(projectDir, this.configDir); - const rulesDir = path.join(cursorDir, this.rulesDir); - const bmadRulesDir = path.join(rulesDir, 'bmad'); + const commandsDir = path.join(cursorDir, this.commandsDir); + 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 { artifacts: agentArtifacts } = 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, - })); + 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 artifact of agentArtifacts) { + modules.add(artifact.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')); + await this.ensureDir(path.join(bmadCommandsDir, module)); + await this.ensureDir(path.join(bmadCommandsDir, module, 'agents')); } - // 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, - }); + // Write agent launcher files + const agentCount = await agentGen.writeAgentLaunchers(bmadCommandsDir, agentArtifacts); - 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); - agentCount++; - } - - // 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; + // Write only workflow-command artifacts, skip workflow-launcher READMEs + let workflowCommandCount = 0; for (const artifact of workflowArtifacts) { if (artifact.type === 'workflow-command') { - // Add MDC metadata header to workflow command - const content = this.wrapLauncherWithMDC(artifact.content, { - module: artifact.module, - name: path.basename(artifact.relativePath, '.md'), - }); - - const targetPath = path.join(bmadRulesDir, artifact.module, 'workflows', `${path.basename(artifact.relativePath, '.md')}.mdc`); - - await this.writeFile(targetPath, content); - workflowCount++; + const moduleWorkflowsDir = path.join(bmadCommandsDir, artifact.module, 'workflows'); + await this.ensureDir(moduleWorkflowsDir); + const commandPath = path.join(moduleWorkflowsDir, path.basename(artifact.relativePath)); + await this.writeFile(commandPath, artifact.content); + workflowCommandCount++; } + // Skip workflow-launcher READMEs as they would be treated as slash commands } - // Create BMAD index file (but NOT .cursorrules - user manages that) - await this.createBMADIndex(bmadRulesDir, agents, tasks, tools, workflows, modules); + // Generate task and tool commands from manifests (if they exist) + const taskToolGen = new TaskToolCommandGenerator(); + const taskToolResult = await taskToolGen.generateTaskToolCommands(projectDir, bmadDir, bmadCommandsDir); console.log(chalk.green(`✓ ${this.name} configured:`)); console.log(chalk.dim(` - ${agentCount} agents installed`)); - console.log(chalk.dim(` - ${taskCount} tasks installed`)); - console.log(chalk.dim(` - ${toolCount} tools installed`)); - console.log(chalk.dim(` - ${workflowCount} workflows installed`)); - console.log(chalk.dim(` - Rules directory: ${path.relative(projectDir, bmadRulesDir)}`)); + if (workflowCommandCount > 0) { + console.log(chalk.dim(` - ${workflowCommandCount} workflow commands generated`)); + } + 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 { success: true, agents: agentCount, - tasks: taskCount, - tools: toolCount, - workflows: workflowCount, + tasks: taskToolResult.tasks || 0, + tools: taskToolResult.tools || 0, + 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(' `; - // Cursor uses MDC format with metadata header - const mdcContent = `--- -description: "${agentName} agent" -globs: -alwaysApply: false + // Cursor uses YAML frontmatter matching Claude Code format + const commandContent = `--- +name: '${agentName}' +description: '${agentName} agent' --- ${launcherContent} `; - const launcherPath = path.join(customAgentsDir, `${agentName}.mdc`); - await this.writeFile(launcherPath, mdcContent); + const launcherPath = path.join(customAgentsDir, `${agentName}.md`); + await this.writeFile(launcherPath, commandContent); return { path: launcherPath, - command: `@${agentName}`, + command: `/bmad/custom/agents/${agentName}`, }; } }