bmad-method 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/.bmad-core/agent-teams/team-all.yml +16 -0
  2. package/.bmad-core/agent-teams/team-fullstack.yml +26 -0
  3. package/.bmad-core/agent-teams/team-no-ui.yml +15 -0
  4. package/.bmad-core/agents/analyst.md +65 -0
  5. package/.bmad-core/agents/architect.md +66 -0
  6. package/.bmad-core/agents/bmad-master.md +107 -0
  7. package/.bmad-core/agents/bmad-orchestrator.md +81 -0
  8. package/.bmad-core/agents/dev.md +69 -0
  9. package/.bmad-core/agents/pm.md +64 -0
  10. package/.bmad-core/agents/po.md +60 -0
  11. package/.bmad-core/agents/qa.md +52 -0
  12. package/.bmad-core/agents/sm.md +60 -0
  13. package/.bmad-core/agents/ux-expert.md +66 -0
  14. package/.bmad-core/checklists/architect-checklist.md +443 -0
  15. package/.bmad-core/checklists/change-checklist.md +182 -0
  16. package/.bmad-core/checklists/pm-checklist.md +375 -0
  17. package/.bmad-core/checklists/po-master-checklist.md +441 -0
  18. package/.bmad-core/checklists/story-dod-checklist.md +101 -0
  19. package/.bmad-core/checklists/story-draft-checklist.md +156 -0
  20. package/.bmad-core/data/bmad-kb.md +36 -0
  21. package/.bmad-core/data/technical-preferences.md +3 -0
  22. package/.bmad-core/schemas/agent-team-schema.yml +153 -0
  23. package/.bmad-core/tasks/advanced-elicitation.md +92 -0
  24. package/.bmad-core/tasks/brainstorming-techniques.md +238 -0
  25. package/.bmad-core/tasks/brownfield-create-epic.md +160 -0
  26. package/.bmad-core/tasks/brownfield-create-story.md +147 -0
  27. package/.bmad-core/tasks/core-dump.md +74 -0
  28. package/.bmad-core/tasks/correct-course.md +73 -0
  29. package/.bmad-core/tasks/create-agent.md +202 -0
  30. package/.bmad-core/tasks/create-deep-research-prompt.md +301 -0
  31. package/.bmad-core/tasks/create-doc.md +74 -0
  32. package/.bmad-core/tasks/create-expansion-pack.md +425 -0
  33. package/.bmad-core/tasks/create-next-story.md +206 -0
  34. package/.bmad-core/tasks/create-team.md +229 -0
  35. package/.bmad-core/tasks/doc-migration-task.md +198 -0
  36. package/.bmad-core/tasks/execute-checklist.md +97 -0
  37. package/.bmad-core/tasks/generate-ai-frontend-prompt.md +58 -0
  38. package/.bmad-core/tasks/index-docs.md +180 -0
  39. package/.bmad-core/tasks/shard-doc.md +173 -0
  40. package/.bmad-core/templates/agent-tmpl.md +58 -0
  41. package/.bmad-core/templates/architecture-tmpl.md +771 -0
  42. package/.bmad-core/templates/brownfield-architecture-tmpl.md +542 -0
  43. package/.bmad-core/templates/brownfield-prd-tmpl.md +240 -0
  44. package/.bmad-core/templates/competitor-analysis-tmpl.md +289 -0
  45. package/.bmad-core/templates/expansion-pack-plan-tmpl.md +91 -0
  46. package/.bmad-core/templates/front-end-architecture-tmpl.md +173 -0
  47. package/.bmad-core/templates/front-end-spec-tmpl.md +411 -0
  48. package/.bmad-core/templates/fullstack-architecture-tmpl.md +1034 -0
  49. package/.bmad-core/templates/market-research-tmpl.md +261 -0
  50. package/.bmad-core/templates/prd-tmpl.md +200 -0
  51. package/.bmad-core/templates/project-brief-tmpl.md +228 -0
  52. package/.bmad-core/templates/story-tmpl.md +61 -0
  53. package/.bmad-core/templates/web-agent-startup-instructions-template.md +39 -0
  54. package/.bmad-core/utils/agent-switcher.ide.md +112 -0
  55. package/.bmad-core/utils/template-format.md +26 -0
  56. package/.bmad-core/utils/workflow-management.md +224 -0
  57. package/.bmad-core/web-bundles/agents/analyst.txt +1679 -0
  58. package/.bmad-core/web-bundles/agents/architect.txt +3602 -0
  59. package/.bmad-core/web-bundles/agents/bmad-master.txt +9496 -0
  60. package/.bmad-core/web-bundles/agents/bmad-orchestrator.txt +1455 -0
  61. package/.bmad-core/web-bundles/agents/dev.txt +315 -0
  62. package/.bmad-core/web-bundles/agents/pm.txt +2196 -0
  63. package/.bmad-core/web-bundles/agents/po.txt +1489 -0
  64. package/.bmad-core/web-bundles/agents/qa.txt +129 -0
  65. package/.bmad-core/web-bundles/agents/sm.txt +663 -0
  66. package/.bmad-core/web-bundles/agents/ux-expert.txt +1099 -0
  67. package/.bmad-core/web-bundles/teams/team-all.txt +10315 -0
  68. package/.bmad-core/web-bundles/teams/team-fullstack.txt +9663 -0
  69. package/.bmad-core/web-bundles/teams/team-no-ui.txt +8504 -0
  70. package/.bmad-core/workflows/brownfield-fullstack.yml +116 -0
  71. package/.bmad-core/workflows/brownfield-service.yml +117 -0
  72. package/.bmad-core/workflows/brownfield-ui.yml +127 -0
  73. package/.bmad-core/workflows/greenfield-fullstack.yml +177 -0
  74. package/.bmad-core/workflows/greenfield-service.yml +143 -0
  75. package/.bmad-core/workflows/greenfield-ui.yml +172 -0
  76. package/.claude/commands/analyst.md +69 -0
  77. package/.claude/commands/architect.md +70 -0
  78. package/.claude/commands/bmad-master.md +111 -0
  79. package/.claude/commands/bmad-orchestrator.md +85 -0
  80. package/.claude/commands/dev.md +73 -0
  81. package/.claude/commands/pm.md +68 -0
  82. package/.claude/commands/po.md +64 -0
  83. package/.claude/commands/qa.md +56 -0
  84. package/.claude/commands/sm.md +64 -0
  85. package/.claude/commands/ux-expert.md +70 -0
  86. package/.cursor/rules/analyst.mdc +83 -0
  87. package/.cursor/rules/architect.mdc +84 -0
  88. package/.cursor/rules/bmad-master.mdc +125 -0
  89. package/.cursor/rules/bmad-orchestrator.mdc +99 -0
  90. package/.cursor/rules/dev.mdc +87 -0
  91. package/.cursor/rules/pm.mdc +82 -0
  92. package/.cursor/rules/po.mdc +78 -0
  93. package/.cursor/rules/qa.mdc +70 -0
  94. package/.cursor/rules/sm.mdc +78 -0
  95. package/.cursor/rules/ux-expert.mdc +84 -0
  96. package/.github/workflows/release.yml +59 -0
  97. package/.husky/pre-commit +2 -0
  98. package/.releaserc.json +17 -0
  99. package/.roo/.roomodes +95 -0
  100. package/.roo/README.md +38 -0
  101. package/.vscode/extensions.json +6 -0
  102. package/.vscode/settings.json +72 -0
  103. package/.windsurf/rules/analyst.md +77 -0
  104. package/.windsurf/rules/architect.md +78 -0
  105. package/.windsurf/rules/bmad-master.md +119 -0
  106. package/.windsurf/rules/bmad-orchestrator.md +93 -0
  107. package/.windsurf/rules/dev.md +81 -0
  108. package/.windsurf/rules/pm.md +76 -0
  109. package/.windsurf/rules/po.md +72 -0
  110. package/.windsurf/rules/qa.md +64 -0
  111. package/.windsurf/rules/sm.md +72 -0
  112. package/.windsurf/rules/ux-expert.md +78 -0
  113. package/CHANGELOG.md +22 -0
  114. package/CONTRIBUTING.md +46 -0
  115. package/LICENSE +21 -0
  116. package/README.md +283 -0
  117. package/docs/versioning-and-releases.md +85 -0
  118. package/docs/versions.md +49 -0
  119. package/expansion-packs/README.md +113 -0
  120. package/expansion-packs/infrastructure-devops/README.md +147 -0
  121. package/expansion-packs/infrastructure-devops/agents/infra-devops-platform.md +59 -0
  122. package/expansion-packs/infrastructure-devops/checklists/infrastructure-checklist.md +484 -0
  123. package/expansion-packs/infrastructure-devops/manifest.yml +38 -0
  124. package/expansion-packs/infrastructure-devops/tasks/review-infrastructure.md +160 -0
  125. package/expansion-packs/infrastructure-devops/tasks/validate-infrastructure.md +154 -0
  126. package/expansion-packs/infrastructure-devops/templates/infrastructure-architecture-tmpl.md +415 -0
  127. package/expansion-packs/infrastructure-devops/templates/infrastructure-platform-from-arch-tmpl.md +0 -0
  128. package/package.json +73 -0
  129. package/tools/bmad-npx-wrapper.js +41 -0
  130. package/tools/builders/web-builder.js +145 -0
  131. package/tools/cli.js +119 -0
  132. package/tools/installer/README.md +58 -0
  133. package/tools/installer/bin/bmad.js +179 -0
  134. package/tools/installer/config/install.config.yml +139 -0
  135. package/tools/installer/lib/config-loader.js +89 -0
  136. package/tools/installer/lib/file-manager.js +169 -0
  137. package/tools/installer/lib/ide-setup.js +419 -0
  138. package/tools/installer/lib/installer.js +534 -0
  139. package/tools/installer/package-lock.json +704 -0
  140. package/tools/installer/package.json +43 -0
  141. package/tools/installer/templates/claude-commands.md +7 -0
  142. package/tools/installer/templates/cursor-rules.md +22 -0
  143. package/tools/installer/templates/windsurf-rules.md +22 -0
  144. package/tools/lib/dependency-resolver.js +179 -0
  145. package/tools/upgraders/v3-to-v4-upgrader.js +766 -0
  146. package/tools/version-bump.js +72 -0
  147. package/tools/yaml-format.js +211 -0
@@ -0,0 +1,89 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+ const yaml = require('js-yaml');
4
+
5
+ class ConfigLoader {
6
+ constructor() {
7
+ this.configPath = path.join(__dirname, '..', 'config', 'install.config.yml');
8
+ this.config = null;
9
+ }
10
+
11
+ async load() {
12
+ if (this.config) return this.config;
13
+
14
+ try {
15
+ const configContent = await fs.readFile(this.configPath, 'utf8');
16
+ this.config = yaml.load(configContent);
17
+ return this.config;
18
+ } catch (error) {
19
+ throw new Error(`Failed to load configuration: ${error.message}`);
20
+ }
21
+ }
22
+
23
+ async getInstallationOptions() {
24
+ const config = await this.load();
25
+ return config['installation-options'] || {};
26
+ }
27
+
28
+ async getAvailableAgents() {
29
+ const config = await this.load();
30
+ return config['available-agents'] || [];
31
+ }
32
+
33
+ async getAgentDependencies(agentId) {
34
+ // Use DependencyResolver to dynamically parse agent dependencies
35
+ const DependencyResolver = require('../../lib/dependency-resolver');
36
+ const resolver = new DependencyResolver(path.join(__dirname, '..', '..', '..'));
37
+
38
+ try {
39
+ const agentDeps = await resolver.resolveAgentDependencies(agentId);
40
+
41
+ // Convert to flat list of file paths
42
+ const depPaths = [];
43
+
44
+ // Add core files
45
+ const config = await this.load();
46
+ const coreFiles = config['agent-dependencies']?.['core-files'] || [];
47
+ depPaths.push(...coreFiles);
48
+
49
+ // Add agent file itself is already handled by installer
50
+
51
+ // Add all resolved resources
52
+ for (const resource of agentDeps.resources) {
53
+ const filePath = `.bmad-core/${resource.type}/${resource.id}.md`;
54
+ if (!depPaths.includes(filePath)) {
55
+ depPaths.push(filePath);
56
+ }
57
+ }
58
+
59
+ return depPaths;
60
+ } catch (error) {
61
+ console.warn(`Failed to dynamically resolve dependencies for ${agentId}: ${error.message}`);
62
+
63
+ // Fall back to static config
64
+ const config = await this.load();
65
+ const dependencies = config['agent-dependencies'] || {};
66
+ const coreFiles = dependencies['core-files'] || [];
67
+ const agentDeps = dependencies[agentId] || [];
68
+
69
+ return [...coreFiles, ...agentDeps];
70
+ }
71
+ }
72
+
73
+ async getIdeConfiguration(ide) {
74
+ const config = await this.load();
75
+ const ideConfigs = config['ide-configurations'] || {};
76
+ return ideConfigs[ide] || null;
77
+ }
78
+
79
+ getBmadCorePath() {
80
+ // Get the path to .bmad-core relative to the installer (now under tools)
81
+ return path.join(__dirname, '..', '..', '..', '.bmad-core');
82
+ }
83
+
84
+ getAgentPath(agentId) {
85
+ return path.join(this.getBmadCorePath(), 'agents', `${agentId}.md`);
86
+ }
87
+ }
88
+
89
+ module.exports = new ConfigLoader();
@@ -0,0 +1,169 @@
1
+ const fs = require("fs-extra");
2
+ const path = require("path");
3
+ const crypto = require("crypto");
4
+ const glob = require("glob");
5
+ const chalk = require("chalk");
6
+
7
+ class FileManager {
8
+ constructor() {
9
+ this.manifestDir = ".bmad-core";
10
+ this.manifestFile = "install-manifest.yml";
11
+ }
12
+
13
+ async copyFile(source, destination) {
14
+ try {
15
+ await fs.ensureDir(path.dirname(destination));
16
+ await fs.copy(source, destination);
17
+ return true;
18
+ } catch (error) {
19
+ console.error(chalk.red(`Failed to copy ${source}:`), error.message);
20
+ return false;
21
+ }
22
+ }
23
+
24
+ async copyDirectory(source, destination) {
25
+ try {
26
+ await fs.ensureDir(destination);
27
+ await fs.copy(source, destination);
28
+ return true;
29
+ } catch (error) {
30
+ console.error(
31
+ chalk.red(`Failed to copy directory ${source}:`),
32
+ error.message
33
+ );
34
+ return false;
35
+ }
36
+ }
37
+
38
+ async copyGlobPattern(pattern, sourceDir, destDir) {
39
+ const files = glob.sync(pattern, { cwd: sourceDir });
40
+ const copied = [];
41
+
42
+ for (const file of files) {
43
+ const sourcePath = path.join(sourceDir, file);
44
+ const destPath = path.join(destDir, file);
45
+
46
+ if (await this.copyFile(sourcePath, destPath)) {
47
+ copied.push(file);
48
+ }
49
+ }
50
+
51
+ return copied;
52
+ }
53
+
54
+ async calculateFileHash(filePath) {
55
+ try {
56
+ const content = await fs.readFile(filePath);
57
+ return crypto
58
+ .createHash("sha256")
59
+ .update(content)
60
+ .digest("hex")
61
+ .slice(0, 16);
62
+ } catch (error) {
63
+ return null;
64
+ }
65
+ }
66
+
67
+ async createManifest(installDir, config, files) {
68
+ const manifestPath = path.join(
69
+ installDir,
70
+ this.manifestDir,
71
+ this.manifestFile
72
+ );
73
+
74
+ const manifest = {
75
+ version: require("../package.json").version,
76
+ installed_at: new Date().toISOString(),
77
+ install_type: config.installType,
78
+ agent: config.agent || null,
79
+ ide_setup: config.ide || null,
80
+ files: [],
81
+ };
82
+
83
+ // Add file information
84
+ for (const file of files) {
85
+ const filePath = path.join(installDir, file);
86
+ const hash = await this.calculateFileHash(filePath);
87
+
88
+ manifest.files.push({
89
+ path: file,
90
+ hash: hash,
91
+ modified: false,
92
+ });
93
+ }
94
+
95
+ // Write manifest
96
+ await fs.ensureDir(path.dirname(manifestPath));
97
+ await fs.writeFile(manifestPath, JSON.stringify(manifest, null, 2));
98
+
99
+ return manifest;
100
+ }
101
+
102
+ async readManifest(installDir) {
103
+ const manifestPath = path.join(
104
+ installDir,
105
+ this.manifestDir,
106
+ this.manifestFile
107
+ );
108
+
109
+ try {
110
+ const content = await fs.readFile(manifestPath, "utf8");
111
+ return JSON.parse(content);
112
+ } catch (error) {
113
+ return null;
114
+ }
115
+ }
116
+
117
+ async checkModifiedFiles(installDir, manifest) {
118
+ const modified = [];
119
+
120
+ for (const file of manifest.files) {
121
+ const filePath = path.join(installDir, file.path);
122
+ const currentHash = await this.calculateFileHash(filePath);
123
+
124
+ if (currentHash && currentHash !== file.hash) {
125
+ modified.push(file.path);
126
+ }
127
+ }
128
+
129
+ return modified;
130
+ }
131
+
132
+ async backupFile(filePath) {
133
+ const backupPath = filePath + ".bak";
134
+ let counter = 1;
135
+ let finalBackupPath = backupPath;
136
+
137
+ // Find a unique backup filename
138
+ while (await fs.pathExists(finalBackupPath)) {
139
+ finalBackupPath = `${filePath}.bak${counter}`;
140
+ counter++;
141
+ }
142
+
143
+ await fs.copy(filePath, finalBackupPath);
144
+ return finalBackupPath;
145
+ }
146
+
147
+ async ensureDirectory(dirPath) {
148
+ await fs.ensureDir(dirPath);
149
+ }
150
+
151
+ async pathExists(filePath) {
152
+ return fs.pathExists(filePath);
153
+ }
154
+
155
+ async readFile(filePath) {
156
+ return fs.readFile(filePath, "utf8");
157
+ }
158
+
159
+ async writeFile(filePath, content) {
160
+ await fs.ensureDir(path.dirname(filePath));
161
+ await fs.writeFile(filePath, content);
162
+ }
163
+
164
+ async removeDirectory(dirPath) {
165
+ await fs.remove(dirPath);
166
+ }
167
+ }
168
+
169
+ module.exports = new FileManager();
@@ -0,0 +1,419 @@
1
+ const path = require("path");
2
+ const fileManager = require("./file-manager");
3
+ const configLoader = require("./config-loader");
4
+ const chalk = require("chalk");
5
+
6
+ class IdeSetup {
7
+ async setup(ide, installDir, selectedAgent = null) {
8
+ const ideConfig = await configLoader.getIdeConfiguration(ide);
9
+
10
+ if (!ideConfig) {
11
+ console.log(chalk.yellow(`\nNo configuration available for ${ide}`));
12
+ return false;
13
+ }
14
+
15
+ switch (ide) {
16
+ case "cursor":
17
+ return this.setupCursor(installDir, selectedAgent);
18
+ case "claude-code":
19
+ return this.setupClaudeCode(installDir, selectedAgent);
20
+ case "windsurf":
21
+ return this.setupWindsurf(installDir, selectedAgent);
22
+ case "roo":
23
+ return this.setupRoo(installDir, selectedAgent);
24
+ default:
25
+ console.log(chalk.yellow(`\nIDE ${ide} not yet supported`));
26
+ return false;
27
+ }
28
+ }
29
+
30
+ async setupCursor(installDir, selectedAgent) {
31
+ const cursorRulesDir = path.join(installDir, ".cursor", "rules");
32
+ const agents = selectedAgent
33
+ ? [selectedAgent]
34
+ : await this.getAllAgentIds(installDir);
35
+
36
+ await fileManager.ensureDirectory(cursorRulesDir);
37
+
38
+ for (const agentId of agents) {
39
+ // Check if .bmad-core is a subdirectory (full install) or if agents are in root (single agent install)
40
+ let agentPath = path.join(
41
+ installDir,
42
+ ".bmad-core",
43
+ "agents",
44
+ `${agentId}.md`
45
+ );
46
+ if (!(await fileManager.pathExists(agentPath))) {
47
+ agentPath = path.join(installDir, "agents", `${agentId}.md`);
48
+ }
49
+
50
+ if (await fileManager.pathExists(agentPath)) {
51
+ const agentContent = await fileManager.readFile(agentPath);
52
+ const mdcPath = path.join(cursorRulesDir, `${agentId}.mdc`);
53
+
54
+ // Create MDC content with proper format
55
+ let mdcContent = "---\n";
56
+ mdcContent += "description: \n";
57
+ mdcContent += "globs: []\n";
58
+ mdcContent += "alwaysApply: false\n";
59
+ mdcContent += "---\n\n";
60
+ mdcContent += `# ${agentId.toUpperCase()} Agent Rule\n\n`;
61
+ mdcContent += `This rule is triggered when the user types \`@${agentId}\` and activates the ${this.getAgentTitle(
62
+ agentId
63
+ )} agent persona.\n\n`;
64
+ mdcContent += "## Agent Activation\n\n";
65
+ mdcContent +=
66
+ "CRITICAL: Read the full YML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode:\n\n";
67
+ mdcContent += "```yml\n";
68
+ // Extract just the YAML content from the agent file
69
+ const yamlMatch = agentContent.match(/```ya?ml\n([\s\S]*?)```/);
70
+ if (yamlMatch) {
71
+ mdcContent += yamlMatch[1].trim();
72
+ } else {
73
+ // If no YAML found, include the whole content minus the header
74
+ mdcContent += agentContent.replace(/^#.*$/m, "").trim();
75
+ }
76
+ mdcContent += "\n```\n\n";
77
+ mdcContent += "## File Reference\n\n";
78
+ mdcContent += `The complete agent definition is available in [.bmad-core/agents/${agentId}.md](mdc:.bmad-core/agents/${agentId}.md).\n\n`;
79
+ mdcContent += "## Usage\n\n";
80
+ mdcContent += `When the user types \`@${agentId}\`, activate this ${this.getAgentTitle(
81
+ agentId
82
+ )} persona and follow all instructions defined in the YML configuration above.\n`;
83
+
84
+ await fileManager.writeFile(mdcPath, mdcContent);
85
+ console.log(chalk.green(`āœ“ Created rule: ${agentId}.mdc`));
86
+ }
87
+ }
88
+
89
+ console.log(chalk.green(`\nāœ“ Created Cursor rules in ${cursorRulesDir}`));
90
+
91
+ return true;
92
+ }
93
+
94
+ async setupClaudeCode(installDir, selectedAgent) {
95
+ const commandsDir = path.join(installDir, ".claude", "commands");
96
+ const agents = selectedAgent
97
+ ? [selectedAgent]
98
+ : await this.getAllAgentIds(installDir);
99
+
100
+ await fileManager.ensureDirectory(commandsDir);
101
+
102
+ for (const agentId of agents) {
103
+ // Check if .bmad-core is a subdirectory (full install) or if agents are in root (single agent install)
104
+ let agentPath = path.join(
105
+ installDir,
106
+ ".bmad-core",
107
+ "agents",
108
+ `${agentId}.md`
109
+ );
110
+ if (!(await fileManager.pathExists(agentPath))) {
111
+ agentPath = path.join(installDir, "agents", `${agentId}.md`);
112
+ }
113
+ const commandPath = path.join(commandsDir, `${agentId}.md`);
114
+
115
+ if (await fileManager.pathExists(agentPath)) {
116
+ // Create command file with agent content
117
+ const agentContent = await fileManager.readFile(agentPath);
118
+
119
+ // Add command header
120
+ let commandContent = `# /${agentId} Command\n\n`;
121
+ commandContent += `When this command is used, adopt the following agent persona:\n\n`;
122
+ commandContent += agentContent;
123
+
124
+ await fileManager.writeFile(commandPath, commandContent);
125
+ console.log(chalk.green(`āœ“ Created command: /${agentId}`));
126
+ }
127
+ }
128
+
129
+ console.log(
130
+ chalk.green(`\nāœ“ Created Claude Code commands in ${commandsDir}`)
131
+ );
132
+
133
+ return true;
134
+ }
135
+
136
+ async setupWindsurf(installDir, selectedAgent) {
137
+ const windsurfRulesDir = path.join(installDir, ".windsurf", "rules");
138
+ const agents = selectedAgent
139
+ ? [selectedAgent]
140
+ : await this.getAllAgentIds(installDir);
141
+
142
+ await fileManager.ensureDirectory(windsurfRulesDir);
143
+
144
+ for (const agentId of agents) {
145
+ // Check if .bmad-core is a subdirectory (full install) or if agents are in root (single agent install)
146
+ let agentPath = path.join(
147
+ installDir,
148
+ ".bmad-core",
149
+ "agents",
150
+ `${agentId}.md`
151
+ );
152
+ if (!(await fileManager.pathExists(agentPath))) {
153
+ agentPath = path.join(installDir, "agents", `${agentId}.md`);
154
+ }
155
+
156
+ if (await fileManager.pathExists(agentPath)) {
157
+ const agentContent = await fileManager.readFile(agentPath);
158
+ const mdPath = path.join(windsurfRulesDir, `${agentId}.md`);
159
+
160
+ // Create MD content (similar to Cursor but without frontmatter)
161
+ let mdContent = `# ${agentId.toUpperCase()} Agent Rule\n\n`;
162
+ mdContent += `This rule is triggered when the user types \`@${agentId}\` and activates the ${this.getAgentTitle(
163
+ agentId
164
+ )} agent persona.\n\n`;
165
+ mdContent += "## Agent Activation\n\n";
166
+ mdContent +=
167
+ "CRITICAL: Read the full YML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode:\n\n";
168
+ mdContent += "```yml\n";
169
+ // Extract just the YAML content from the agent file
170
+ const yamlMatch = agentContent.match(/```ya?ml\n([\s\S]*?)```/);
171
+ if (yamlMatch) {
172
+ mdContent += yamlMatch[1].trim();
173
+ } else {
174
+ // If no YAML found, include the whole content minus the header
175
+ mdContent += agentContent.replace(/^#.*$/m, "").trim();
176
+ }
177
+ mdContent += "\n```\n\n";
178
+ mdContent += "## File Reference\n\n";
179
+ mdContent += `The complete agent definition is available in [.bmad-core/agents/${agentId}.md](.bmad-core/agents/${agentId}.md).\n\n`;
180
+ mdContent += "## Usage\n\n";
181
+ mdContent += `When the user types \`@${agentId}\`, activate this ${this.getAgentTitle(
182
+ agentId
183
+ )} persona and follow all instructions defined in the YML configuration above.\n`;
184
+
185
+ await fileManager.writeFile(mdPath, mdContent);
186
+ console.log(chalk.green(`āœ“ Created rule: ${agentId}.md`));
187
+ }
188
+ }
189
+
190
+ console.log(
191
+ chalk.green(`\nāœ“ Created Windsurf rules in ${windsurfRulesDir}`)
192
+ );
193
+
194
+ return true;
195
+ }
196
+
197
+ async getAllAgentIds(installDir) {
198
+ // Check if .bmad-core is a subdirectory (full install) or if agents are in root (single agent install)
199
+ let agentsDir = path.join(installDir, ".bmad-core", "agents");
200
+ if (!(await fileManager.pathExists(agentsDir))) {
201
+ agentsDir = path.join(installDir, "agents");
202
+ }
203
+
204
+ const glob = require("glob");
205
+ const agentFiles = glob.sync("*.md", { cwd: agentsDir });
206
+ return agentFiles.map((file) => path.basename(file, ".md"));
207
+ }
208
+
209
+ getAgentTitle(agentId) {
210
+ const agentTitles = {
211
+ analyst: "Business Analyst",
212
+ architect: "Solution Architect",
213
+ "bmad-master": "BMAD Master",
214
+ "bmad-orchestrator": "BMAD Orchestrator",
215
+ dev: "Developer",
216
+ pm: "Product Manager",
217
+ po: "Product Owner",
218
+ qa: "QA Specialist",
219
+ sm: "Scrum Master",
220
+ "ux-expert": "UX Expert",
221
+ };
222
+ return agentTitles[agentId] || agentId;
223
+ }
224
+
225
+ async setupRoo(installDir, selectedAgent) {
226
+ const agents = selectedAgent
227
+ ? [selectedAgent]
228
+ : await this.getAllAgentIds(installDir);
229
+
230
+ // Create .roo directory first
231
+ const rooDir = path.join(installDir, ".roo");
232
+ await fileManager.ensureDirectory(rooDir);
233
+
234
+ // Check for existing .roomodes file inside .roo directory
235
+ const roomodesPath = path.join(rooDir, ".roomodes");
236
+ let existingModes = [];
237
+ let existingContent = "";
238
+
239
+ if (await fileManager.pathExists(roomodesPath)) {
240
+ existingContent = await fileManager.readFile(roomodesPath);
241
+ // Parse existing modes to avoid duplicates
242
+ const modeMatches = existingContent.matchAll(/- slug: ([\w-]+)/g);
243
+ for (const match of modeMatches) {
244
+ existingModes.push(match[1]);
245
+ }
246
+ console.log(
247
+ chalk.yellow(
248
+ `Found existing .roomodes file with ${existingModes.length} modes`
249
+ )
250
+ );
251
+ }
252
+
253
+ // Create new modes content
254
+ let newModesContent = "";
255
+
256
+ // Define file permissions for each agent type
257
+ const agentPermissions = {
258
+ 'analyst': {
259
+ fileRegex: '\\.(md|txt)$',
260
+ description: 'Documentation and text files'
261
+ },
262
+ 'pm': {
263
+ fileRegex: '\\.(md|txt)$',
264
+ description: 'Product documentation'
265
+ },
266
+ 'architect': {
267
+ fileRegex: '\\.(md|txt|yml|yaml|json)$',
268
+ description: 'Architecture docs and configs'
269
+ },
270
+ 'dev': null, // Full edit access
271
+ 'qa': {
272
+ fileRegex: '\\.(test|spec)\\.(js|ts|jsx|tsx)$|\\.md$',
273
+ description: 'Test files and documentation'
274
+ },
275
+ 'ux-expert': {
276
+ fileRegex: '\\.(md|css|scss|html|jsx|tsx)$',
277
+ description: 'Design-related files'
278
+ },
279
+ 'po': {
280
+ fileRegex: '\\.(md|txt)$',
281
+ description: 'Story and requirement docs'
282
+ },
283
+ 'sm': {
284
+ fileRegex: '\\.(md|txt)$',
285
+ description: 'Process and planning docs'
286
+ },
287
+ 'bmad-orchestrator': null, // Full edit access
288
+ 'bmad-master': null // Full edit access
289
+ };
290
+
291
+ for (const agentId of agents) {
292
+ // Skip if already exists
293
+ if (existingModes.includes(`bmad-${agentId}`)) {
294
+ console.log(
295
+ chalk.dim(`Skipping ${agentId} - already exists in .roomodes`)
296
+ );
297
+ continue;
298
+ }
299
+
300
+ // Read agent file to extract all information
301
+ let agentPath = path.join(
302
+ installDir,
303
+ ".bmad-core",
304
+ "agents",
305
+ `${agentId}.md`
306
+ );
307
+ if (!(await fileManager.pathExists(agentPath))) {
308
+ agentPath = path.join(installDir, "agents", `${agentId}.md`);
309
+ }
310
+
311
+ if (await fileManager.pathExists(agentPath)) {
312
+ const agentContent = await fileManager.readFile(agentPath);
313
+
314
+ // Extract YAML content
315
+ const yamlMatch = agentContent.match(/```ya?ml\n([\s\S]*?)```/);
316
+ if (yamlMatch) {
317
+ const yaml = yamlMatch[1];
318
+
319
+ // Extract agent info from YAML
320
+ const titleMatch = yaml.match(/title:\s*(.+)/);
321
+ const iconMatch = yaml.match(/icon:\s*(.+)/);
322
+ const whenToUseMatch = yaml.match(/whenToUse:\s*"(.+)"/);
323
+ const roleDefinitionMatch = yaml.match(/roleDefinition:\s*"(.+)"/);
324
+
325
+ const title = titleMatch ? titleMatch[1].trim() : this.getAgentTitle(agentId);
326
+ const icon = iconMatch ? iconMatch[1].trim() : "šŸ¤–";
327
+ const whenToUse = whenToUseMatch
328
+ ? whenToUseMatch[1].trim()
329
+ : `Use for ${title} tasks`;
330
+ const roleDefinition = roleDefinitionMatch
331
+ ? roleDefinitionMatch[1].trim()
332
+ : `You are a ${title} specializing in ${title.toLowerCase()} tasks and responsibilities.`;
333
+
334
+ // Build mode entry with proper formatting (matching exact indentation)
335
+ newModesContent += ` - slug: bmad-${agentId}\n`;
336
+ newModesContent += ` name: '${icon} ${title}'\n`;
337
+ newModesContent += ` roleDefinition: ${roleDefinition}\n`;
338
+ newModesContent += ` whenToUse: ${whenToUse}\n`;
339
+ newModesContent += ` customInstructions: CRITICAL Read the full YML from .bmad-core/agents/${agentId}.md start activation to alter your state of being follow startup section instructions stay in this being until told to exit this mode\n`;
340
+ newModesContent += ` groups:\n`;
341
+ newModesContent += ` - read\n`;
342
+
343
+ // Add permissions based on agent type
344
+ const permissions = agentPermissions[agentId];
345
+ if (permissions) {
346
+ newModesContent += ` - - edit\n`;
347
+ newModesContent += ` - fileRegex: ${permissions.fileRegex}\n`;
348
+ newModesContent += ` description: ${permissions.description}\n`;
349
+ } else {
350
+ newModesContent += ` - edit\n`;
351
+ }
352
+
353
+ console.log(
354
+ chalk.green(`āœ“ Added mode: bmad-${agentId} (${icon} ${title})`)
355
+ );
356
+ }
357
+ }
358
+ }
359
+
360
+ // Build final roomodes content
361
+ let roomodesContent = "";
362
+ if (existingContent) {
363
+ // If there's existing content, append new modes to it
364
+ roomodesContent = existingContent.trim() + "\n" + newModesContent;
365
+ } else {
366
+ // Create new .roomodes file with proper YAML structure
367
+ roomodesContent = "customModes:\n" + newModesContent;
368
+ }
369
+
370
+ // Write .roomodes file
371
+ await fileManager.writeFile(roomodesPath, roomodesContent);
372
+ console.log(chalk.green("āœ“ Created .roo/.roomodes file"));
373
+
374
+ // Create README in .roo directory
375
+ const rooReadme = `# Roo Code Custom Modes for BMAD-METHOD
376
+
377
+ This directory contains custom mode configurations for Roo Code to enable BMAD agent personalities.
378
+
379
+ ## Setup
380
+
381
+ The \`.roomodes\` file defines all BMAD agents as custom modes using the proper \`customModes:\` structure. Modes are automatically available in Roo Code when you open this project.
382
+
383
+ ## Available Modes
384
+
385
+ ${agents.map((id) => `- **bmad-${id}** - ${this.getAgentTitle(id)}`).join("\n")}
386
+
387
+ ## Usage
388
+
389
+ In Roo Code:
390
+ 1. Open the mode selector (usually in the status bar)
391
+ 2. Select any BMAD agent mode
392
+ 3. The AI will adopt that agent's personality and expertise
393
+
394
+ ## File Permissions
395
+
396
+ Each agent has specific file access permissions:
397
+ - **Analysts, PM, PO, SM**: Limited to documentation files (.md, .txt)
398
+ - **Architect**: Architecture docs and configs (.md, .txt, .yml, .yaml, .json)
399
+ - **QA**: Test files and documentation
400
+ - **UX Expert**: Design-related files (.md, .css, .scss, .html, .jsx, .tsx)
401
+ - **Developer, Orchestrator, Master**: Full edit access to all files
402
+ `;
403
+
404
+ const readmePath = path.join(rooDir, "README.md");
405
+ await fileManager.writeFile(readmePath, rooReadme);
406
+ console.log(chalk.green("āœ“ Created .roo/README.md"));
407
+
408
+ console.log(chalk.green(`\nāœ“ Roo Code setup complete!`));
409
+ console.log(
410
+ chalk.dim(
411
+ "Custom modes will be available when you open this project in Roo Code"
412
+ )
413
+ );
414
+
415
+ return true;
416
+ }
417
+ }
418
+
419
+ module.exports = new IdeSetup();