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.
- package/.bmad-core/agent-teams/team-all.yml +16 -0
- package/.bmad-core/agent-teams/team-fullstack.yml +26 -0
- package/.bmad-core/agent-teams/team-no-ui.yml +15 -0
- package/.bmad-core/agents/analyst.md +65 -0
- package/.bmad-core/agents/architect.md +66 -0
- package/.bmad-core/agents/bmad-master.md +107 -0
- package/.bmad-core/agents/bmad-orchestrator.md +81 -0
- package/.bmad-core/agents/dev.md +69 -0
- package/.bmad-core/agents/pm.md +64 -0
- package/.bmad-core/agents/po.md +60 -0
- package/.bmad-core/agents/qa.md +52 -0
- package/.bmad-core/agents/sm.md +60 -0
- package/.bmad-core/agents/ux-expert.md +66 -0
- package/.bmad-core/checklists/architect-checklist.md +443 -0
- package/.bmad-core/checklists/change-checklist.md +182 -0
- package/.bmad-core/checklists/pm-checklist.md +375 -0
- package/.bmad-core/checklists/po-master-checklist.md +441 -0
- package/.bmad-core/checklists/story-dod-checklist.md +101 -0
- package/.bmad-core/checklists/story-draft-checklist.md +156 -0
- package/.bmad-core/data/bmad-kb.md +36 -0
- package/.bmad-core/data/technical-preferences.md +3 -0
- package/.bmad-core/schemas/agent-team-schema.yml +153 -0
- package/.bmad-core/tasks/advanced-elicitation.md +92 -0
- package/.bmad-core/tasks/brainstorming-techniques.md +238 -0
- package/.bmad-core/tasks/brownfield-create-epic.md +160 -0
- package/.bmad-core/tasks/brownfield-create-story.md +147 -0
- package/.bmad-core/tasks/core-dump.md +74 -0
- package/.bmad-core/tasks/correct-course.md +73 -0
- package/.bmad-core/tasks/create-agent.md +202 -0
- package/.bmad-core/tasks/create-deep-research-prompt.md +301 -0
- package/.bmad-core/tasks/create-doc.md +74 -0
- package/.bmad-core/tasks/create-expansion-pack.md +425 -0
- package/.bmad-core/tasks/create-next-story.md +206 -0
- package/.bmad-core/tasks/create-team.md +229 -0
- package/.bmad-core/tasks/doc-migration-task.md +198 -0
- package/.bmad-core/tasks/execute-checklist.md +97 -0
- package/.bmad-core/tasks/generate-ai-frontend-prompt.md +58 -0
- package/.bmad-core/tasks/index-docs.md +180 -0
- package/.bmad-core/tasks/shard-doc.md +173 -0
- package/.bmad-core/templates/agent-tmpl.md +58 -0
- package/.bmad-core/templates/architecture-tmpl.md +771 -0
- package/.bmad-core/templates/brownfield-architecture-tmpl.md +542 -0
- package/.bmad-core/templates/brownfield-prd-tmpl.md +240 -0
- package/.bmad-core/templates/competitor-analysis-tmpl.md +289 -0
- package/.bmad-core/templates/expansion-pack-plan-tmpl.md +91 -0
- package/.bmad-core/templates/front-end-architecture-tmpl.md +173 -0
- package/.bmad-core/templates/front-end-spec-tmpl.md +411 -0
- package/.bmad-core/templates/fullstack-architecture-tmpl.md +1034 -0
- package/.bmad-core/templates/market-research-tmpl.md +261 -0
- package/.bmad-core/templates/prd-tmpl.md +200 -0
- package/.bmad-core/templates/project-brief-tmpl.md +228 -0
- package/.bmad-core/templates/story-tmpl.md +61 -0
- package/.bmad-core/templates/web-agent-startup-instructions-template.md +39 -0
- package/.bmad-core/utils/agent-switcher.ide.md +112 -0
- package/.bmad-core/utils/template-format.md +26 -0
- package/.bmad-core/utils/workflow-management.md +224 -0
- package/.bmad-core/web-bundles/agents/analyst.txt +1679 -0
- package/.bmad-core/web-bundles/agents/architect.txt +3602 -0
- package/.bmad-core/web-bundles/agents/bmad-master.txt +9496 -0
- package/.bmad-core/web-bundles/agents/bmad-orchestrator.txt +1455 -0
- package/.bmad-core/web-bundles/agents/dev.txt +315 -0
- package/.bmad-core/web-bundles/agents/pm.txt +2196 -0
- package/.bmad-core/web-bundles/agents/po.txt +1489 -0
- package/.bmad-core/web-bundles/agents/qa.txt +129 -0
- package/.bmad-core/web-bundles/agents/sm.txt +663 -0
- package/.bmad-core/web-bundles/agents/ux-expert.txt +1099 -0
- package/.bmad-core/web-bundles/teams/team-all.txt +10315 -0
- package/.bmad-core/web-bundles/teams/team-fullstack.txt +9663 -0
- package/.bmad-core/web-bundles/teams/team-no-ui.txt +8504 -0
- package/.bmad-core/workflows/brownfield-fullstack.yml +116 -0
- package/.bmad-core/workflows/brownfield-service.yml +117 -0
- package/.bmad-core/workflows/brownfield-ui.yml +127 -0
- package/.bmad-core/workflows/greenfield-fullstack.yml +177 -0
- package/.bmad-core/workflows/greenfield-service.yml +143 -0
- package/.bmad-core/workflows/greenfield-ui.yml +172 -0
- package/.claude/commands/analyst.md +69 -0
- package/.claude/commands/architect.md +70 -0
- package/.claude/commands/bmad-master.md +111 -0
- package/.claude/commands/bmad-orchestrator.md +85 -0
- package/.claude/commands/dev.md +73 -0
- package/.claude/commands/pm.md +68 -0
- package/.claude/commands/po.md +64 -0
- package/.claude/commands/qa.md +56 -0
- package/.claude/commands/sm.md +64 -0
- package/.claude/commands/ux-expert.md +70 -0
- package/.cursor/rules/analyst.mdc +83 -0
- package/.cursor/rules/architect.mdc +84 -0
- package/.cursor/rules/bmad-master.mdc +125 -0
- package/.cursor/rules/bmad-orchestrator.mdc +99 -0
- package/.cursor/rules/dev.mdc +87 -0
- package/.cursor/rules/pm.mdc +82 -0
- package/.cursor/rules/po.mdc +78 -0
- package/.cursor/rules/qa.mdc +70 -0
- package/.cursor/rules/sm.mdc +78 -0
- package/.cursor/rules/ux-expert.mdc +84 -0
- package/.github/workflows/release.yml +59 -0
- package/.husky/pre-commit +2 -0
- package/.releaserc.json +17 -0
- package/.roo/.roomodes +95 -0
- package/.roo/README.md +38 -0
- package/.vscode/extensions.json +6 -0
- package/.vscode/settings.json +72 -0
- package/.windsurf/rules/analyst.md +77 -0
- package/.windsurf/rules/architect.md +78 -0
- package/.windsurf/rules/bmad-master.md +119 -0
- package/.windsurf/rules/bmad-orchestrator.md +93 -0
- package/.windsurf/rules/dev.md +81 -0
- package/.windsurf/rules/pm.md +76 -0
- package/.windsurf/rules/po.md +72 -0
- package/.windsurf/rules/qa.md +64 -0
- package/.windsurf/rules/sm.md +72 -0
- package/.windsurf/rules/ux-expert.md +78 -0
- package/CHANGELOG.md +22 -0
- package/CONTRIBUTING.md +46 -0
- package/LICENSE +21 -0
- package/README.md +283 -0
- package/docs/versioning-and-releases.md +85 -0
- package/docs/versions.md +49 -0
- package/expansion-packs/README.md +113 -0
- package/expansion-packs/infrastructure-devops/README.md +147 -0
- package/expansion-packs/infrastructure-devops/agents/infra-devops-platform.md +59 -0
- package/expansion-packs/infrastructure-devops/checklists/infrastructure-checklist.md +484 -0
- package/expansion-packs/infrastructure-devops/manifest.yml +38 -0
- package/expansion-packs/infrastructure-devops/tasks/review-infrastructure.md +160 -0
- package/expansion-packs/infrastructure-devops/tasks/validate-infrastructure.md +154 -0
- package/expansion-packs/infrastructure-devops/templates/infrastructure-architecture-tmpl.md +415 -0
- package/expansion-packs/infrastructure-devops/templates/infrastructure-platform-from-arch-tmpl.md +0 -0
- package/package.json +73 -0
- package/tools/bmad-npx-wrapper.js +41 -0
- package/tools/builders/web-builder.js +145 -0
- package/tools/cli.js +119 -0
- package/tools/installer/README.md +58 -0
- package/tools/installer/bin/bmad.js +179 -0
- package/tools/installer/config/install.config.yml +139 -0
- package/tools/installer/lib/config-loader.js +89 -0
- package/tools/installer/lib/file-manager.js +169 -0
- package/tools/installer/lib/ide-setup.js +419 -0
- package/tools/installer/lib/installer.js +534 -0
- package/tools/installer/package-lock.json +704 -0
- package/tools/installer/package.json +43 -0
- package/tools/installer/templates/claude-commands.md +7 -0
- package/tools/installer/templates/cursor-rules.md +22 -0
- package/tools/installer/templates/windsurf-rules.md +22 -0
- package/tools/lib/dependency-resolver.js +179 -0
- package/tools/upgraders/v3-to-v4-upgrader.js +766 -0
- package/tools/version-bump.js +72 -0
- 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();
|