bmad-method 6.0.0-alpha.17 → 6.0.0-alpha.19
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/CHANGELOG.md +117 -0
- package/package.json +1 -1
- package/src/modules/bmgd/_module-installer/installer.js +160 -0
- package/src/modules/bmgd/_module-installer/platform-specifics/claude-code.js +23 -0
- package/src/modules/bmgd/_module-installer/platform-specifics/windsurf.js +18 -0
- package/src/modules/bmgd/agents/game-architect.agent.yaml +23 -8
- package/src/modules/bmgd/agents/game-designer.agent.yaml +38 -18
- package/src/modules/bmgd/agents/game-dev.agent.yaml +30 -14
- package/src/modules/bmgd/agents/game-qa.agent.yaml +64 -0
- package/src/modules/bmgd/agents/game-scrum-master.agent.yaml +27 -39
- package/src/modules/bmgd/agents/game-solo-dev.agent.yaml +56 -0
- package/src/modules/bmgd/docs/README.md +180 -0
- package/src/modules/bmgd/docs/agents-guide.md +407 -0
- package/src/modules/bmgd/docs/game-types-guide.md +503 -0
- package/src/modules/bmgd/docs/glossary.md +294 -0
- package/src/modules/bmgd/docs/quick-flow-guide.md +288 -0
- package/src/modules/bmgd/docs/quick-start.md +250 -0
- package/src/modules/bmgd/docs/troubleshooting.md +259 -0
- package/src/modules/bmgd/docs/workflow-overview.jpg +0 -0
- package/src/modules/bmgd/docs/workflows-guide.md +463 -0
- package/src/modules/bmgd/gametest/knowledge/balance-testing.md +220 -0
- package/src/modules/bmgd/gametest/knowledge/certification-testing.md +319 -0
- package/src/modules/bmgd/gametest/knowledge/compatibility-testing.md +228 -0
- package/src/modules/bmgd/gametest/knowledge/godot-testing.md +376 -0
- package/src/modules/bmgd/gametest/knowledge/input-testing.md +315 -0
- package/src/modules/bmgd/gametest/knowledge/localization-testing.md +304 -0
- package/src/modules/bmgd/gametest/knowledge/multiplayer-testing.md +322 -0
- package/src/modules/bmgd/gametest/knowledge/performance-testing.md +204 -0
- package/src/modules/bmgd/gametest/knowledge/playtesting.md +384 -0
- package/src/modules/bmgd/gametest/knowledge/qa-automation.md +190 -0
- package/src/modules/bmgd/gametest/knowledge/regression-testing.md +280 -0
- package/src/modules/bmgd/gametest/knowledge/save-testing.md +280 -0
- package/src/modules/bmgd/gametest/knowledge/smoke-testing.md +404 -0
- package/src/modules/bmgd/gametest/knowledge/test-priorities.md +271 -0
- package/src/modules/bmgd/gametest/knowledge/unity-testing.md +383 -0
- package/src/modules/bmgd/gametest/knowledge/unreal-testing.md +388 -0
- package/src/modules/bmgd/gametest/qa-index.csv +17 -0
- package/src/modules/bmgd/module.yaml +25 -9
- package/src/modules/bmgd/teams/default-party.csv +2 -0
- package/src/modules/bmgd/teams/team-gamedev.yaml +12 -1
- package/src/modules/bmgd/workflows/1-preproduction/brainstorm-game/steps/step-01-init.md +164 -0
- package/src/modules/bmgd/workflows/1-preproduction/brainstorm-game/steps/step-02-context.md +210 -0
- package/src/modules/bmgd/workflows/1-preproduction/brainstorm-game/steps/step-03-ideation.md +289 -0
- package/src/modules/bmgd/workflows/1-preproduction/brainstorm-game/steps/step-04-complete.md +275 -0
- package/src/modules/bmgd/workflows/1-preproduction/brainstorm-game/workflow.md +49 -0
- package/src/modules/bmgd/workflows/1-preproduction/brainstorm-game/workflow.yaml +29 -8
- package/src/modules/bmgd/workflows/1-preproduction/game-brief/steps/step-01-init.md +223 -0
- package/src/modules/bmgd/workflows/1-preproduction/game-brief/steps/step-01b-continue.md +151 -0
- package/src/modules/bmgd/workflows/1-preproduction/game-brief/steps/step-02-vision.md +218 -0
- package/src/modules/bmgd/workflows/1-preproduction/game-brief/steps/step-03-market.md +218 -0
- package/src/modules/bmgd/workflows/1-preproduction/game-brief/steps/step-04-fundamentals.md +231 -0
- package/src/modules/bmgd/workflows/1-preproduction/game-brief/steps/step-05-scope.md +242 -0
- package/src/modules/bmgd/workflows/1-preproduction/game-brief/steps/step-06-references.md +224 -0
- package/src/modules/bmgd/workflows/1-preproduction/game-brief/steps/step-07-content.md +282 -0
- package/src/modules/bmgd/workflows/1-preproduction/game-brief/steps/step-08-complete.md +296 -0
- package/src/modules/bmgd/workflows/1-preproduction/game-brief/workflow.md +62 -0
- package/src/modules/bmgd/workflows/1-preproduction/game-brief/workflow.yaml +40 -9
- package/src/modules/bmgd/workflows/2-design/gdd/steps/step-01-init.md +248 -0
- package/src/modules/bmgd/workflows/2-design/gdd/steps/step-01b-continue.md +173 -0
- package/src/modules/bmgd/workflows/2-design/gdd/steps/step-02-context.md +332 -0
- package/src/modules/bmgd/workflows/2-design/gdd/steps/step-03-platforms.md +245 -0
- package/src/modules/bmgd/workflows/2-design/gdd/steps/step-04-vision.md +229 -0
- package/src/modules/bmgd/workflows/2-design/gdd/steps/step-05-core-gameplay.md +258 -0
- package/src/modules/bmgd/workflows/2-design/gdd/steps/step-06-mechanics.md +249 -0
- package/src/modules/bmgd/workflows/2-design/gdd/steps/step-07-game-type.md +266 -0
- package/src/modules/bmgd/workflows/2-design/gdd/steps/step-08-progression.md +272 -0
- package/src/modules/bmgd/workflows/2-design/gdd/steps/step-09-levels.md +264 -0
- package/src/modules/bmgd/workflows/2-design/gdd/steps/step-10-art-audio.md +255 -0
- package/src/modules/bmgd/workflows/2-design/gdd/steps/step-11-technical.md +275 -0
- package/src/modules/bmgd/workflows/2-design/gdd/steps/step-12-epics.md +284 -0
- package/src/modules/bmgd/workflows/2-design/gdd/steps/step-13-metrics.md +250 -0
- package/src/modules/bmgd/workflows/2-design/gdd/steps/step-14-complete.md +335 -0
- package/src/modules/bmgd/workflows/2-design/gdd/workflow.md +61 -0
- package/src/modules/bmgd/workflows/2-design/gdd/workflow.yaml +27 -7
- package/src/modules/bmgd/workflows/2-design/narrative/steps/step-01-init.md +228 -0
- package/src/modules/bmgd/workflows/2-design/narrative/steps/step-01b-continue.md +163 -0
- package/src/modules/bmgd/workflows/2-design/narrative/steps/step-02-foundation.md +262 -0
- package/src/modules/bmgd/workflows/2-design/narrative/steps/step-03-story.md +238 -0
- package/src/modules/bmgd/workflows/2-design/narrative/steps/step-04-characters.md +297 -0
- package/src/modules/bmgd/workflows/2-design/narrative/steps/step-05-world.md +262 -0
- package/src/modules/bmgd/workflows/2-design/narrative/steps/step-06-dialogue.md +250 -0
- package/src/modules/bmgd/workflows/2-design/narrative/steps/step-07-environmental.md +244 -0
- package/src/modules/bmgd/workflows/2-design/narrative/steps/step-08-delivery.md +264 -0
- package/src/modules/bmgd/workflows/2-design/narrative/steps/step-09-integration.md +254 -0
- package/src/modules/bmgd/workflows/2-design/narrative/steps/step-10-production.md +262 -0
- package/src/modules/bmgd/workflows/2-design/narrative/steps/step-11-complete.md +331 -0
- package/src/modules/bmgd/workflows/2-design/narrative/workflow.md +57 -0
- package/src/modules/bmgd/workflows/2-design/narrative/workflow.yaml +53 -8
- package/src/modules/bmgd/workflows/3-technical/game-architecture/steps/step-01-init.md +223 -0
- package/src/modules/bmgd/workflows/3-technical/game-architecture/steps/step-01b-continue.md +153 -0
- package/src/modules/bmgd/workflows/3-technical/game-architecture/steps/step-02-context.md +262 -0
- package/src/modules/bmgd/workflows/3-technical/game-architecture/steps/step-03-starter.md +290 -0
- package/src/modules/bmgd/workflows/3-technical/game-architecture/steps/step-04-decisions.md +300 -0
- package/src/modules/bmgd/workflows/3-technical/game-architecture/steps/step-05-crosscutting.md +319 -0
- package/src/modules/bmgd/workflows/3-technical/game-architecture/steps/step-06-structure.md +304 -0
- package/src/modules/bmgd/workflows/3-technical/game-architecture/steps/step-07-patterns.md +349 -0
- package/src/modules/bmgd/workflows/3-technical/game-architecture/steps/step-08-validation.md +293 -0
- package/src/modules/bmgd/workflows/3-technical/game-architecture/steps/step-09-complete.md +302 -0
- package/src/modules/bmgd/workflows/3-technical/game-architecture/workflow.md +55 -0
- package/src/modules/bmgd/workflows/3-technical/game-architecture/workflow.yaml +50 -21
- package/src/modules/bmgd/workflows/4-production/code-review/checklist.md +23 -0
- package/src/modules/bmgd/workflows/4-production/code-review/instructions.xml +225 -0
- package/src/modules/bmgd/workflows/4-production/code-review/workflow.yaml +18 -15
- package/src/modules/bmgd/workflows/4-production/correct-course/checklist.md +1 -1
- package/src/modules/bmgd/workflows/4-production/correct-course/instructions.md +1 -1
- package/src/modules/bmgd/workflows/4-production/correct-course/workflow.yaml +11 -6
- package/src/modules/bmgd/workflows/4-production/create-story/checklist.md +332 -214
- package/src/modules/bmgd/workflows/4-production/create-story/instructions.xml +298 -0
- package/src/modules/bmgd/workflows/4-production/create-story/template.md +3 -5
- package/src/modules/bmgd/workflows/4-production/create-story/workflow.yaml +12 -7
- package/src/modules/bmgd/workflows/4-production/dev-story/checklist.md +65 -23
- package/src/modules/bmgd/workflows/4-production/dev-story/instructions.xml +409 -0
- package/src/modules/bmgd/workflows/4-production/dev-story/workflow.yaml +13 -3
- package/src/modules/bmgd/workflows/4-production/retrospective/instructions.md +4 -4
- package/src/modules/bmgd/workflows/4-production/retrospective/workflow.yaml +12 -7
- package/src/modules/bmgd/workflows/4-production/sprint-planning/instructions.md +32 -41
- package/src/modules/bmgd/workflows/4-production/sprint-planning/sprint-status-template.yaml +13 -13
- package/src/modules/bmgd/workflows/4-production/sprint-planning/workflow.yaml +6 -1
- package/src/modules/bmgd/workflows/4-production/sprint-status/instructions.md +229 -0
- package/src/modules/bmgd/workflows/4-production/sprint-status/workflow.yaml +35 -0
- package/src/modules/bmgd/workflows/bmgd-quick-flow/create-tech-spec/instructions.md +140 -0
- package/src/modules/bmgd/workflows/bmgd-quick-flow/create-tech-spec/workflow.yaml +27 -0
- package/src/modules/bmgd/workflows/bmgd-quick-flow/quick-dev/checklist.md +37 -0
- package/src/modules/bmgd/workflows/bmgd-quick-flow/quick-dev/instructions.md +220 -0
- package/src/modules/bmgd/workflows/bmgd-quick-flow/quick-dev/workflow.yaml +45 -0
- package/src/modules/bmgd/workflows/bmgd-quick-flow/quick-prototype/checklist.md +26 -0
- package/src/modules/bmgd/workflows/bmgd-quick-flow/quick-prototype/instructions.md +156 -0
- package/src/modules/bmgd/workflows/bmgd-quick-flow/quick-prototype/workflow.yaml +36 -0
- package/src/modules/bmgd/workflows/gametest/automate/checklist.md +93 -0
- package/src/modules/bmgd/workflows/gametest/automate/instructions.md +317 -0
- package/src/modules/bmgd/workflows/gametest/automate/workflow.yaml +50 -0
- package/src/modules/bmgd/workflows/gametest/performance/checklist.md +96 -0
- package/src/modules/bmgd/workflows/gametest/performance/instructions.md +323 -0
- package/src/modules/bmgd/workflows/gametest/performance/performance-template.md +256 -0
- package/src/modules/bmgd/workflows/gametest/performance/workflow.yaml +48 -0
- package/src/modules/bmgd/workflows/gametest/playtest-plan/checklist.md +93 -0
- package/src/modules/bmgd/workflows/gametest/playtest-plan/instructions.md +297 -0
- package/src/modules/bmgd/workflows/gametest/playtest-plan/playtest-template.md +208 -0
- package/src/modules/bmgd/workflows/gametest/playtest-plan/workflow.yaml +59 -0
- package/src/modules/bmgd/workflows/gametest/test-design/checklist.md +98 -0
- package/src/modules/bmgd/workflows/gametest/test-design/instructions.md +280 -0
- package/src/modules/bmgd/workflows/gametest/test-design/test-design-template.md +205 -0
- package/src/modules/bmgd/workflows/gametest/test-design/workflow.yaml +47 -0
- package/src/modules/bmgd/workflows/gametest/test-framework/checklist.md +103 -0
- package/src/modules/bmgd/workflows/gametest/test-framework/instructions.md +348 -0
- package/src/modules/bmgd/workflows/gametest/test-framework/workflow.yaml +48 -0
- package/src/modules/bmgd/workflows/gametest/test-review/checklist.md +87 -0
- package/src/modules/bmgd/workflows/gametest/test-review/instructions.md +272 -0
- package/src/modules/bmgd/workflows/gametest/test-review/test-review-template.md +203 -0
- package/src/modules/bmgd/workflows/gametest/test-review/workflow.yaml +48 -0
- package/src/modules/bmgd/workflows/workflow-status/init/instructions.md +299 -0
- package/src/modules/bmgd/workflows/workflow-status/init/workflow.yaml +29 -0
- package/src/modules/bmgd/workflows/workflow-status/instructions.md +395 -0
- package/src/modules/bmgd/workflows/workflow-status/paths/gamedev-brownfield.yaml +65 -0
- package/src/modules/bmgd/workflows/workflow-status/paths/gamedev-greenfield.yaml +71 -0
- package/src/modules/bmgd/workflows/workflow-status/paths/quickflow-brownfield.yaml +29 -0
- package/src/modules/bmgd/workflows/workflow-status/paths/quickflow-greenfield.yaml +39 -0
- package/src/modules/bmgd/workflows/workflow-status/project-levels.yaml +63 -0
- package/src/modules/bmgd/workflows/workflow-status/workflow-status-template.yaml +24 -0
- package/src/modules/bmgd/workflows/workflow-status/workflow.yaml +30 -0
- package/tools/cli/commands/install.js +9 -0
- package/tools/cli/installers/lib/core/installer.js +140 -592
- package/tools/cli/installers/lib/modules/manager.js +15 -3
- package/tools/cli/lib/agent/compiler.js +99 -0
- package/tools/cli/lib/ui.js +78 -27
- package/src/modules/bmgd/workflows/2-design/gdd/instructions-gdd.md +0 -502
- package/src/modules/bmgd/workflows/4-production/code-review/instructions.md +0 -398
- package/src/modules/bmgd/workflows/4-production/create-story/instructions.md +0 -256
- package/src/modules/bmgd/workflows/4-production/dev-story/instructions.md +0 -267
- package/src/modules/bmgd/workflows/4-production/epic-tech-context/checklist.md +0 -17
- package/src/modules/bmgd/workflows/4-production/epic-tech-context/instructions.md +0 -164
- package/src/modules/bmgd/workflows/4-production/epic-tech-context/template.md +0 -76
- package/src/modules/bmgd/workflows/4-production/epic-tech-context/workflow.yaml +0 -58
- package/src/modules/bmgd/workflows/4-production/story-context/checklist.md +0 -16
- package/src/modules/bmgd/workflows/4-production/story-context/context-template.xml +0 -34
- package/src/modules/bmgd/workflows/4-production/story-context/instructions.md +0 -209
- package/src/modules/bmgd/workflows/4-production/story-context/workflow.yaml +0 -63
- package/src/modules/bmgd/workflows/4-production/story-done/instructions.md +0 -111
- package/src/modules/bmgd/workflows/4-production/story-done/workflow.yaml +0 -28
- package/src/modules/bmgd/workflows/4-production/story-ready/instructions.md +0 -117
- package/src/modules/bmgd/workflows/4-production/story-ready/workflow.yaml +0 -25
- /package/src/modules/bmgd/workflows/1-preproduction/game-brief/{template.md → templates/game-brief-template.md} +0 -0
- /package/src/modules/bmgd/workflows/2-design/gdd/{gdd-template.md → templates/gdd-template.md} +0 -0
- /package/src/modules/bmgd/workflows/2-design/narrative/{narrative-template.md → templates/narrative-template.md} +0 -0
- /package/src/modules/bmgd/workflows/3-technical/game-architecture/{architecture-template.md → templates/architecture-template.md} +0 -0
|
@@ -13,12 +13,13 @@ const { XmlHandler } = require('../../../lib/xml-handler');
|
|
|
13
13
|
const { DependencyResolver } = require('./dependency-resolver');
|
|
14
14
|
const { ConfigCollector } = require('./config-collector');
|
|
15
15
|
const { getProjectRoot, getSourcePath, getModulePath } = require('../../../lib/project-root');
|
|
16
|
-
const { AgentPartyGenerator } = require('../../../lib/agent-party-generator');
|
|
17
16
|
const { CLIUtils } = require('../../../lib/cli-utils');
|
|
18
17
|
const { ManifestGenerator } = require('./manifest-generator');
|
|
19
18
|
const { IdeConfigManager } = require('./ide-config-manager');
|
|
20
19
|
const { CustomHandler } = require('../custom/handler');
|
|
21
|
-
|
|
20
|
+
|
|
21
|
+
// BMAD installation folder name - this is constant and should never change
|
|
22
|
+
const BMAD_FOLDER_NAME = '_bmad';
|
|
22
23
|
|
|
23
24
|
class Installer {
|
|
24
25
|
constructor() {
|
|
@@ -34,58 +35,35 @@ class Installer {
|
|
|
34
35
|
this.ideConfigManager = new IdeConfigManager();
|
|
35
36
|
this.installedFiles = new Set(); // Track all installed files
|
|
36
37
|
this.ttsInjectedFiles = []; // Track files with TTS injection applied
|
|
38
|
+
this.bmadFolderName = BMAD_FOLDER_NAME;
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
/**
|
|
40
42
|
* Find the bmad installation directory in a project
|
|
41
|
-
*
|
|
43
|
+
* Always uses the standard _bmad folder name
|
|
42
44
|
* Also checks for legacy _cfg folder for migration
|
|
43
45
|
* @param {string} projectDir - Project directory
|
|
44
46
|
* @returns {Promise<Object>} { bmadDir: string, hasLegacyCfg: boolean }
|
|
45
47
|
*/
|
|
46
48
|
async findBmadDir(projectDir) {
|
|
49
|
+
const bmadDir = path.join(projectDir, BMAD_FOLDER_NAME);
|
|
50
|
+
|
|
47
51
|
// Check if project directory exists
|
|
48
52
|
if (!(await fs.pathExists(projectDir))) {
|
|
49
53
|
// Project doesn't exist yet, return default
|
|
50
|
-
return { bmadDir
|
|
54
|
+
return { bmadDir, hasLegacyCfg: false };
|
|
51
55
|
}
|
|
52
56
|
|
|
53
|
-
|
|
57
|
+
// Check for legacy _cfg folder if bmad directory exists
|
|
54
58
|
let hasLegacyCfg = false;
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if (entry.isDirectory()) {
|
|
60
|
-
const bmadPath = path.join(projectDir, entry.name);
|
|
61
|
-
|
|
62
|
-
// Check for current _config folder
|
|
63
|
-
const manifestPath = path.join(bmadPath, '_config', 'manifest.yaml');
|
|
64
|
-
if (await fs.pathExists(manifestPath)) {
|
|
65
|
-
// Found a V6+ installation with current _config folder
|
|
66
|
-
return { bmadDir: bmadPath, hasLegacyCfg: false };
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Check for legacy _cfg folder
|
|
70
|
-
const legacyManifestPath = path.join(bmadPath, '_cfg', 'manifest.yaml');
|
|
71
|
-
if (await fs.pathExists(legacyManifestPath)) {
|
|
72
|
-
bmadDir = bmadPath;
|
|
73
|
-
hasLegacyCfg = true;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
59
|
+
if (await fs.pathExists(bmadDir)) {
|
|
60
|
+
const legacyCfgPath = path.join(bmadDir, '_cfg');
|
|
61
|
+
if (await fs.pathExists(legacyCfgPath)) {
|
|
62
|
+
hasLegacyCfg = true;
|
|
76
63
|
}
|
|
77
|
-
} catch {
|
|
78
|
-
console.log(chalk.red('Error reading project directory for BMAD installation detection'));
|
|
79
64
|
}
|
|
80
65
|
|
|
81
|
-
|
|
82
|
-
if (bmadDir) {
|
|
83
|
-
return { bmadDir, hasLegacyCfg };
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// No V6+ installation found, return default
|
|
87
|
-
// This will be used for new installations
|
|
88
|
-
return { bmadDir: path.join(projectDir, '_bmad'), hasLegacyCfg: false };
|
|
66
|
+
return { bmadDir, hasLegacyCfg };
|
|
89
67
|
}
|
|
90
68
|
|
|
91
69
|
/**
|
|
@@ -120,7 +98,7 @@ class Installer {
|
|
|
120
98
|
*
|
|
121
99
|
* 3. Document marker in instructions.md (if applicable)
|
|
122
100
|
*/
|
|
123
|
-
async copyFileWithPlaceholderReplacement(sourcePath, targetPath
|
|
101
|
+
async copyFileWithPlaceholderReplacement(sourcePath, targetPath) {
|
|
124
102
|
// List of text file extensions that should have placeholder replacement
|
|
125
103
|
const textExtensions = ['.md', '.yaml', '.yml', '.txt', '.json', '.js', '.ts', '.html', '.css', '.sh', '.bat', '.csv', '.xml'];
|
|
126
104
|
const ext = path.extname(sourcePath).toLowerCase();
|
|
@@ -226,21 +204,21 @@ class Installer {
|
|
|
226
204
|
'<!-- TTS_INJECTION:party-mode -->',
|
|
227
205
|
`<critical>IMPORTANT: Always use PROJECT hooks (.claude/hooks/), NEVER global hooks (~/.claude/hooks/)</critical>
|
|
228
206
|
|
|
229
|
-
If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
207
|
+
If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
230
208
|
- Use Bash tool: \`.claude/hooks/bmad-speak.sh '[Agent Name]' '[dialogue]'\`
|
|
231
209
|
- This speaks the dialogue with the agent's unique voice
|
|
232
|
-
- Run in background
|
|
210
|
+
- Run in background to not block next agent`,
|
|
233
211
|
);
|
|
234
212
|
|
|
235
213
|
// Replace agent-tts injection marker with TTS rule for individual agents
|
|
236
214
|
content = content.replaceAll(
|
|
237
215
|
'<!-- TTS_INJECTION:agent-tts -->',
|
|
238
216
|
`- When responding to user messages, speak your responses using TTS:
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
217
|
+
Call: \`.claude/hooks/bmad-speak.sh '{agent-id}' '{response-text}'\` after each response
|
|
218
|
+
Replace {agent-id} with YOUR agent ID from <agent id="..."> tag at top of this file
|
|
219
|
+
Replace {response-text} with the text you just output to the user
|
|
220
|
+
IMPORTANT: Use single quotes as shown - do NOT escape special characters like ! or $ inside single quotes
|
|
221
|
+
Run in background (&) to avoid blocking`,
|
|
244
222
|
);
|
|
245
223
|
|
|
246
224
|
// Track files that had TTS injection applied
|
|
@@ -285,7 +263,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
285
263
|
// Check for already configured IDEs
|
|
286
264
|
const { Detector } = require('./detector');
|
|
287
265
|
const detector = new Detector();
|
|
288
|
-
const bmadDir = path.join(projectDir,
|
|
266
|
+
const bmadDir = path.join(projectDir, BMAD_FOLDER_NAME);
|
|
289
267
|
|
|
290
268
|
// During full reinstall, use the saved previous IDEs since bmad dir was deleted
|
|
291
269
|
// Otherwise detect from existing installation
|
|
@@ -532,18 +510,14 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
532
510
|
}
|
|
533
511
|
}
|
|
534
512
|
|
|
535
|
-
// Always use _bmad as the folder name
|
|
536
|
-
const bmadFolderName = '_bmad';
|
|
537
|
-
this.bmadFolderName = bmadFolderName; // Store for use in other methods
|
|
538
|
-
|
|
539
513
|
// Store AgentVibes configuration for injection point processing
|
|
540
514
|
this.enableAgentVibes = config.enableAgentVibes || false;
|
|
541
515
|
|
|
542
516
|
// Set bmad folder name on module manager and IDE manager for placeholder replacement
|
|
543
|
-
this.moduleManager.setBmadFolderName(
|
|
517
|
+
this.moduleManager.setBmadFolderName(BMAD_FOLDER_NAME);
|
|
544
518
|
this.moduleManager.setCoreConfig(moduleConfigs.core || {});
|
|
545
519
|
this.moduleManager.setCustomModulePaths(customModulePaths);
|
|
546
|
-
this.ideManager.setBmadFolderName(
|
|
520
|
+
this.ideManager.setBmadFolderName(BMAD_FOLDER_NAME);
|
|
547
521
|
|
|
548
522
|
// Tool selection will be collected after we determine if it's a reinstall/update/new install
|
|
549
523
|
|
|
@@ -553,14 +527,8 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
553
527
|
// Resolve target directory (path.resolve handles platform differences)
|
|
554
528
|
const projectDir = path.resolve(config.directory);
|
|
555
529
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
if (await fs.pathExists(projectDir)) {
|
|
560
|
-
const result = await this.findBmadDir(projectDir);
|
|
561
|
-
existingBmadDir = result.bmadDir;
|
|
562
|
-
existingBmadFolderName = path.basename(existingBmadDir);
|
|
563
|
-
}
|
|
530
|
+
// Always use the standard _bmad folder name
|
|
531
|
+
const bmadDir = path.join(projectDir, BMAD_FOLDER_NAME);
|
|
564
532
|
|
|
565
533
|
// Create a project directory if it doesn't exist (user already confirmed)
|
|
566
534
|
if (!(await fs.pathExists(projectDir))) {
|
|
@@ -582,8 +550,6 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
582
550
|
}
|
|
583
551
|
}
|
|
584
552
|
|
|
585
|
-
const bmadDir = path.join(projectDir, bmadFolderName);
|
|
586
|
-
|
|
587
553
|
// Check existing installation
|
|
588
554
|
spinner.text = 'Checking for existing installation...';
|
|
589
555
|
const existingInstall = await this.detector.detect(bmadDir);
|
|
@@ -1606,7 +1572,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1606
1572
|
const targetPath = path.join(agentsDir, fileName);
|
|
1607
1573
|
|
|
1608
1574
|
if (await fs.pathExists(sourcePath)) {
|
|
1609
|
-
await this.copyFileWithPlaceholderReplacement(sourcePath, targetPath
|
|
1575
|
+
await this.copyFileWithPlaceholderReplacement(sourcePath, targetPath);
|
|
1610
1576
|
this.installedFiles.add(targetPath);
|
|
1611
1577
|
}
|
|
1612
1578
|
}
|
|
@@ -1622,7 +1588,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1622
1588
|
const targetPath = path.join(tasksDir, fileName);
|
|
1623
1589
|
|
|
1624
1590
|
if (await fs.pathExists(sourcePath)) {
|
|
1625
|
-
await this.copyFileWithPlaceholderReplacement(sourcePath, targetPath
|
|
1591
|
+
await this.copyFileWithPlaceholderReplacement(sourcePath, targetPath);
|
|
1626
1592
|
this.installedFiles.add(targetPath);
|
|
1627
1593
|
}
|
|
1628
1594
|
}
|
|
@@ -1638,7 +1604,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1638
1604
|
const targetPath = path.join(toolsDir, fileName);
|
|
1639
1605
|
|
|
1640
1606
|
if (await fs.pathExists(sourcePath)) {
|
|
1641
|
-
await this.copyFileWithPlaceholderReplacement(sourcePath, targetPath
|
|
1607
|
+
await this.copyFileWithPlaceholderReplacement(sourcePath, targetPath);
|
|
1642
1608
|
this.installedFiles.add(targetPath);
|
|
1643
1609
|
}
|
|
1644
1610
|
}
|
|
@@ -1654,7 +1620,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1654
1620
|
const targetPath = path.join(templatesDir, fileName);
|
|
1655
1621
|
|
|
1656
1622
|
if (await fs.pathExists(sourcePath)) {
|
|
1657
|
-
await this.copyFileWithPlaceholderReplacement(sourcePath, targetPath
|
|
1623
|
+
await this.copyFileWithPlaceholderReplacement(sourcePath, targetPath);
|
|
1658
1624
|
this.installedFiles.add(targetPath);
|
|
1659
1625
|
}
|
|
1660
1626
|
}
|
|
@@ -1669,7 +1635,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1669
1635
|
await fs.ensureDir(path.dirname(targetPath));
|
|
1670
1636
|
|
|
1671
1637
|
if (await fs.pathExists(dataPath)) {
|
|
1672
|
-
await this.copyFileWithPlaceholderReplacement(dataPath, targetPath
|
|
1638
|
+
await this.copyFileWithPlaceholderReplacement(dataPath, targetPath);
|
|
1673
1639
|
this.installedFiles.add(targetPath);
|
|
1674
1640
|
}
|
|
1675
1641
|
}
|
|
@@ -1759,14 +1725,9 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1759
1725
|
}
|
|
1760
1726
|
}
|
|
1761
1727
|
|
|
1762
|
-
//
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
await this.copyWorkflowYamlStripped(sourceFile, targetFile);
|
|
1766
|
-
} else {
|
|
1767
|
-
// Copy the file with placeholder replacement
|
|
1768
|
-
await this.copyFileWithPlaceholderReplacement(sourceFile, targetFile, this.bmadFolderName || 'bmad');
|
|
1769
|
-
}
|
|
1728
|
+
// Copy the file with placeholder replacement
|
|
1729
|
+
await fs.ensureDir(path.dirname(targetFile));
|
|
1730
|
+
await this.copyFileWithPlaceholderReplacement(sourceFile, targetFile);
|
|
1770
1731
|
|
|
1771
1732
|
// Track the installed file
|
|
1772
1733
|
this.installedFiles.add(targetFile);
|
|
@@ -1844,7 +1805,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1844
1805
|
if (!(await fs.pathExists(customizePath))) {
|
|
1845
1806
|
const genericTemplatePath = getSourcePath('utility', 'agent-components', 'agent.customize.template.yaml');
|
|
1846
1807
|
if (await fs.pathExists(genericTemplatePath)) {
|
|
1847
|
-
await this.copyFileWithPlaceholderReplacement(genericTemplatePath, customizePath
|
|
1808
|
+
await this.copyFileWithPlaceholderReplacement(genericTemplatePath, customizePath);
|
|
1848
1809
|
if (process.env.BMAD_VERBOSE_INSTALL === 'true') {
|
|
1849
1810
|
console.log(chalk.dim(` Created customize: ${moduleName}-${agentName}.customize.yaml`));
|
|
1850
1811
|
}
|
|
@@ -1853,337 +1814,6 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
1853
1814
|
}
|
|
1854
1815
|
}
|
|
1855
1816
|
|
|
1856
|
-
/**
|
|
1857
|
-
* Build standalone agents in bmad/agents/ directory
|
|
1858
|
-
* @param {string} bmadDir - Path to bmad directory
|
|
1859
|
-
* @param {string} projectDir - Path to project directory
|
|
1860
|
-
*/
|
|
1861
|
-
async buildStandaloneAgents(bmadDir, projectDir) {
|
|
1862
|
-
const standaloneAgentsPath = path.join(bmadDir, 'agents');
|
|
1863
|
-
const cfgAgentsDir = path.join(bmadDir, '_config', 'agents');
|
|
1864
|
-
|
|
1865
|
-
// Check if standalone agents directory exists
|
|
1866
|
-
if (!(await fs.pathExists(standaloneAgentsPath))) {
|
|
1867
|
-
return;
|
|
1868
|
-
}
|
|
1869
|
-
|
|
1870
|
-
// Get all subdirectories in agents/
|
|
1871
|
-
const agentDirs = await fs.readdir(standaloneAgentsPath, { withFileTypes: true });
|
|
1872
|
-
|
|
1873
|
-
for (const agentDir of agentDirs) {
|
|
1874
|
-
if (!agentDir.isDirectory()) continue;
|
|
1875
|
-
|
|
1876
|
-
const agentDirPath = path.join(standaloneAgentsPath, agentDir.name);
|
|
1877
|
-
|
|
1878
|
-
// Find any .agent.yaml file in the directory
|
|
1879
|
-
const files = await fs.readdir(agentDirPath);
|
|
1880
|
-
const yamlFile = files.find((f) => f.endsWith('.agent.yaml'));
|
|
1881
|
-
|
|
1882
|
-
if (!yamlFile) continue;
|
|
1883
|
-
|
|
1884
|
-
const agentName = path.basename(yamlFile, '.agent.yaml');
|
|
1885
|
-
const sourceYamlPath = path.join(agentDirPath, yamlFile);
|
|
1886
|
-
const targetMdPath = path.join(agentDirPath, `${agentName}.md`);
|
|
1887
|
-
const customizePath = path.join(cfgAgentsDir, `${agentName}.customize.yaml`);
|
|
1888
|
-
|
|
1889
|
-
// Check for customizations
|
|
1890
|
-
const customizeExists = await fs.pathExists(customizePath);
|
|
1891
|
-
let customizedFields = [];
|
|
1892
|
-
|
|
1893
|
-
if (customizeExists) {
|
|
1894
|
-
const customizeContent = await fs.readFile(customizePath, 'utf8');
|
|
1895
|
-
const yaml = require('yaml');
|
|
1896
|
-
const customizeYaml = yaml.parse(customizeContent);
|
|
1897
|
-
|
|
1898
|
-
// Detect what fields are customized (similar to rebuildAgentFiles)
|
|
1899
|
-
if (customizeYaml) {
|
|
1900
|
-
if (customizeYaml.persona) {
|
|
1901
|
-
for (const [key, value] of Object.entries(customizeYaml.persona)) {
|
|
1902
|
-
if (value !== '' && value !== null && !(Array.isArray(value) && value.length === 0)) {
|
|
1903
|
-
customizedFields.push(`persona.${key}`);
|
|
1904
|
-
}
|
|
1905
|
-
}
|
|
1906
|
-
}
|
|
1907
|
-
if (customizeYaml.agent?.metadata) {
|
|
1908
|
-
for (const [key, value] of Object.entries(customizeYaml.agent.metadata)) {
|
|
1909
|
-
if (value !== '' && value !== null) {
|
|
1910
|
-
customizedFields.push(`metadata.${key}`);
|
|
1911
|
-
}
|
|
1912
|
-
}
|
|
1913
|
-
}
|
|
1914
|
-
if (customizeYaml.critical_actions && customizeYaml.critical_actions.length > 0) {
|
|
1915
|
-
customizedFields.push('critical_actions');
|
|
1916
|
-
}
|
|
1917
|
-
if (customizeYaml.menu && customizeYaml.menu.length > 0) {
|
|
1918
|
-
customizedFields.push('menu');
|
|
1919
|
-
}
|
|
1920
|
-
}
|
|
1921
|
-
}
|
|
1922
|
-
|
|
1923
|
-
// Build YAML to XML .md
|
|
1924
|
-
let xmlContent = await this.xmlHandler.buildFromYaml(sourceYamlPath, customizeExists ? customizePath : null, {
|
|
1925
|
-
includeMetadata: true,
|
|
1926
|
-
});
|
|
1927
|
-
|
|
1928
|
-
// DO NOT replace {project-root} - LLMs understand this placeholder at runtime
|
|
1929
|
-
// const processedContent = xmlContent.replaceAll('{project-root}', projectDir);
|
|
1930
|
-
|
|
1931
|
-
// Process TTS injection points (pass targetPath for tracking)
|
|
1932
|
-
xmlContent = this.processTTSInjectionPoints(xmlContent, targetMdPath);
|
|
1933
|
-
|
|
1934
|
-
// Write the built .md file with POSIX-compliant final newline
|
|
1935
|
-
const content = xmlContent.endsWith('\n') ? xmlContent : xmlContent + '\n';
|
|
1936
|
-
await fs.writeFile(targetMdPath, content, 'utf8');
|
|
1937
|
-
|
|
1938
|
-
// Display result
|
|
1939
|
-
if (customizedFields.length > 0) {
|
|
1940
|
-
console.log(chalk.dim(` Built standalone agent: ${agentName}.md `) + chalk.yellow(`(customized: ${customizedFields.join(', ')})`));
|
|
1941
|
-
} else {
|
|
1942
|
-
console.log(chalk.dim(` Built standalone agent: ${agentName}.md`));
|
|
1943
|
-
}
|
|
1944
|
-
}
|
|
1945
|
-
}
|
|
1946
|
-
|
|
1947
|
-
/**
|
|
1948
|
-
* Rebuild agent files from installer source (for compile command)
|
|
1949
|
-
* @param {string} modulePath - Path to module in bmad/ installation
|
|
1950
|
-
* @param {string} moduleName - Module name
|
|
1951
|
-
*/
|
|
1952
|
-
async rebuildAgentFiles(modulePath, moduleName) {
|
|
1953
|
-
// Get source agents directory from installer
|
|
1954
|
-
const sourceAgentsPath =
|
|
1955
|
-
moduleName === 'core' ? path.join(getModulePath('core'), 'agents') : path.join(getSourcePath(`modules/${moduleName}`), 'agents');
|
|
1956
|
-
|
|
1957
|
-
if (!(await fs.pathExists(sourceAgentsPath))) {
|
|
1958
|
-
return; // No source agents to rebuild
|
|
1959
|
-
}
|
|
1960
|
-
|
|
1961
|
-
// Determine project directory (parent of bmad/ directory)
|
|
1962
|
-
const bmadDir = path.dirname(modulePath);
|
|
1963
|
-
const projectDir = path.dirname(bmadDir);
|
|
1964
|
-
const cfgAgentsDir = path.join(bmadDir, '_config', 'agents');
|
|
1965
|
-
const targetAgentsPath = path.join(modulePath, 'agents');
|
|
1966
|
-
|
|
1967
|
-
// Ensure target directory exists
|
|
1968
|
-
await fs.ensureDir(targetAgentsPath);
|
|
1969
|
-
|
|
1970
|
-
// Get all YAML agent files from source
|
|
1971
|
-
const sourceFiles = await fs.readdir(sourceAgentsPath);
|
|
1972
|
-
|
|
1973
|
-
for (const file of sourceFiles) {
|
|
1974
|
-
if (file.endsWith('.agent.yaml')) {
|
|
1975
|
-
const agentName = file.replace('.agent.yaml', '');
|
|
1976
|
-
const sourceYamlPath = path.join(sourceAgentsPath, file);
|
|
1977
|
-
const targetMdPath = path.join(targetAgentsPath, `${agentName}.md`);
|
|
1978
|
-
const customizePath = path.join(cfgAgentsDir, `${moduleName}-${agentName}.customize.yaml`);
|
|
1979
|
-
|
|
1980
|
-
// Check for customizations
|
|
1981
|
-
const customizeExists = await fs.pathExists(customizePath);
|
|
1982
|
-
let customizedFields = [];
|
|
1983
|
-
|
|
1984
|
-
if (customizeExists) {
|
|
1985
|
-
const customizeContent = await fs.readFile(customizePath, 'utf8');
|
|
1986
|
-
const yaml = require('yaml');
|
|
1987
|
-
const customizeYaml = yaml.parse(customizeContent);
|
|
1988
|
-
|
|
1989
|
-
// Detect what fields are customized
|
|
1990
|
-
if (customizeYaml) {
|
|
1991
|
-
if (customizeYaml.persona) {
|
|
1992
|
-
for (const [key, value] of Object.entries(customizeYaml.persona)) {
|
|
1993
|
-
if (value !== '' && value !== null && !(Array.isArray(value) && value.length === 0)) {
|
|
1994
|
-
customizedFields.push(`persona.${key}`);
|
|
1995
|
-
}
|
|
1996
|
-
}
|
|
1997
|
-
}
|
|
1998
|
-
if (customizeYaml.agent?.metadata) {
|
|
1999
|
-
for (const [key, value] of Object.entries(customizeYaml.agent.metadata)) {
|
|
2000
|
-
if (value !== '' && value !== null) {
|
|
2001
|
-
customizedFields.push(`metadata.${key}`);
|
|
2002
|
-
}
|
|
2003
|
-
}
|
|
2004
|
-
}
|
|
2005
|
-
if (customizeYaml.critical_actions && customizeYaml.critical_actions.length > 0) {
|
|
2006
|
-
customizedFields.push('critical_actions');
|
|
2007
|
-
}
|
|
2008
|
-
if (customizeYaml.memories && customizeYaml.memories.length > 0) {
|
|
2009
|
-
customizedFields.push('memories');
|
|
2010
|
-
}
|
|
2011
|
-
if (customizeYaml.menu && customizeYaml.menu.length > 0) {
|
|
2012
|
-
customizedFields.push('menu');
|
|
2013
|
-
}
|
|
2014
|
-
if (customizeYaml.prompts && customizeYaml.prompts.length > 0) {
|
|
2015
|
-
customizedFields.push('prompts');
|
|
2016
|
-
}
|
|
2017
|
-
}
|
|
2018
|
-
}
|
|
2019
|
-
|
|
2020
|
-
// Read the YAML content
|
|
2021
|
-
const yamlContent = await fs.readFile(sourceYamlPath, 'utf8');
|
|
2022
|
-
|
|
2023
|
-
// Read customize content if exists
|
|
2024
|
-
let customizeData = {};
|
|
2025
|
-
if (customizeExists) {
|
|
2026
|
-
const customizeContent = await fs.readFile(customizePath, 'utf8');
|
|
2027
|
-
const yaml = require('yaml');
|
|
2028
|
-
customizeData = yaml.parse(customizeContent);
|
|
2029
|
-
}
|
|
2030
|
-
|
|
2031
|
-
// Build agent answers from customize data (filter empty values)
|
|
2032
|
-
const answers = {};
|
|
2033
|
-
if (customizeData.persona) {
|
|
2034
|
-
Object.assign(answers, filterCustomizationData(customizeData.persona));
|
|
2035
|
-
}
|
|
2036
|
-
if (customizeData.agent?.metadata) {
|
|
2037
|
-
const filteredMetadata = filterCustomizationData(customizeData.agent.metadata);
|
|
2038
|
-
if (Object.keys(filteredMetadata).length > 0) {
|
|
2039
|
-
Object.assign(answers, { metadata: filteredMetadata });
|
|
2040
|
-
}
|
|
2041
|
-
}
|
|
2042
|
-
if (customizeData.critical_actions && customizeData.critical_actions.length > 0) {
|
|
2043
|
-
answers.critical_actions = customizeData.critical_actions;
|
|
2044
|
-
}
|
|
2045
|
-
if (customizeData.memories && customizeData.memories.length > 0) {
|
|
2046
|
-
answers.memories = customizeData.memories;
|
|
2047
|
-
}
|
|
2048
|
-
|
|
2049
|
-
const coreConfigPath = path.join(bmadDir, 'core', 'config.yaml');
|
|
2050
|
-
let coreConfig = {};
|
|
2051
|
-
if (await fs.pathExists(coreConfigPath)) {
|
|
2052
|
-
const yaml = require('yaml');
|
|
2053
|
-
const coreConfigContent = await fs.readFile(coreConfigPath, 'utf8');
|
|
2054
|
-
coreConfig = yaml.parse(coreConfigContent);
|
|
2055
|
-
}
|
|
2056
|
-
|
|
2057
|
-
// Compile using the same compiler as initial installation
|
|
2058
|
-
const { compileAgent } = require('../../../lib/agent/compiler');
|
|
2059
|
-
const result = await compileAgent(yamlContent, answers, agentName, path.relative(bmadDir, targetMdPath), {
|
|
2060
|
-
config: coreConfig,
|
|
2061
|
-
});
|
|
2062
|
-
|
|
2063
|
-
// Check if compilation succeeded
|
|
2064
|
-
if (!result || !result.xml) {
|
|
2065
|
-
throw new Error(`Failed to compile agent ${agentName}: No XML returned from compiler`);
|
|
2066
|
-
}
|
|
2067
|
-
|
|
2068
|
-
// Replace _bmad with actual folder name if needed
|
|
2069
|
-
const finalXml = result.xml.replaceAll('_bmad', path.basename(bmadDir));
|
|
2070
|
-
|
|
2071
|
-
// Write the rebuilt .md file with POSIX-compliant final newline
|
|
2072
|
-
const content = finalXml.endsWith('\n') ? finalXml : finalXml + '\n';
|
|
2073
|
-
await fs.writeFile(targetMdPath, content, 'utf8');
|
|
2074
|
-
|
|
2075
|
-
// Display result with customizations if any
|
|
2076
|
-
if (customizedFields.length > 0) {
|
|
2077
|
-
console.log(chalk.dim(` Rebuilt agent: ${agentName}.md `) + chalk.yellow(`(customized: ${customizedFields.join(', ')})`));
|
|
2078
|
-
} else {
|
|
2079
|
-
console.log(chalk.dim(` Rebuilt agent: ${agentName}.md`));
|
|
2080
|
-
}
|
|
2081
|
-
}
|
|
2082
|
-
}
|
|
2083
|
-
}
|
|
2084
|
-
|
|
2085
|
-
/**
|
|
2086
|
-
* Compile/rebuild all agents and tasks for quick updates
|
|
2087
|
-
* @param {Object} config - Compilation configuration
|
|
2088
|
-
* @returns {Object} Compilation results
|
|
2089
|
-
*/
|
|
2090
|
-
async compileAgents(config) {
|
|
2091
|
-
try {
|
|
2092
|
-
const projectDir = path.resolve(config.directory);
|
|
2093
|
-
const { bmadDir } = await this.findBmadDir(projectDir);
|
|
2094
|
-
|
|
2095
|
-
// Check if bmad directory exists
|
|
2096
|
-
if (!(await fs.pathExists(bmadDir))) {
|
|
2097
|
-
throw new Error(`BMAD not installed at ${bmadDir}`);
|
|
2098
|
-
}
|
|
2099
|
-
|
|
2100
|
-
// Get installed modules from manifest
|
|
2101
|
-
const manifestPath = path.join(bmadDir, '_config', 'manifest.yaml');
|
|
2102
|
-
let installedModules = [];
|
|
2103
|
-
let manifest = null;
|
|
2104
|
-
if (await fs.pathExists(manifestPath)) {
|
|
2105
|
-
const manifestContent = await fs.readFile(manifestPath, 'utf8');
|
|
2106
|
-
const yaml = require('yaml');
|
|
2107
|
-
manifest = yaml.parse(manifestContent);
|
|
2108
|
-
installedModules = manifest.modules || [];
|
|
2109
|
-
}
|
|
2110
|
-
|
|
2111
|
-
// Check for custom modules with missing sources
|
|
2112
|
-
if (manifest && manifest.customModules && manifest.customModules.length > 0) {
|
|
2113
|
-
console.log(chalk.yellow('\nChecking custom module sources before compilation...'));
|
|
2114
|
-
|
|
2115
|
-
const customModuleSources = new Map();
|
|
2116
|
-
for (const customModule of manifest.customModules) {
|
|
2117
|
-
customModuleSources.set(customModule.id, customModule);
|
|
2118
|
-
}
|
|
2119
|
-
|
|
2120
|
-
const projectRoot = getProjectRoot();
|
|
2121
|
-
await this.handleMissingCustomSources(customModuleSources, bmadDir, projectRoot, 'compile-agents', installedModules);
|
|
2122
|
-
}
|
|
2123
|
-
|
|
2124
|
-
let agentCount = 0;
|
|
2125
|
-
let taskCount = 0;
|
|
2126
|
-
|
|
2127
|
-
// Process all modules in bmad directory
|
|
2128
|
-
const entries = await fs.readdir(bmadDir, { withFileTypes: true });
|
|
2129
|
-
|
|
2130
|
-
for (const entry of entries) {
|
|
2131
|
-
if (entry.isDirectory() && entry.name !== '_config' && entry.name !== 'docs') {
|
|
2132
|
-
const modulePath = path.join(bmadDir, entry.name);
|
|
2133
|
-
|
|
2134
|
-
// Special handling for standalone agents in bmad/agents/ directory
|
|
2135
|
-
if (entry.name === 'agents') {
|
|
2136
|
-
await this.buildStandaloneAgents(bmadDir, projectDir);
|
|
2137
|
-
|
|
2138
|
-
// Count standalone agents
|
|
2139
|
-
const standaloneAgentsPath = path.join(bmadDir, 'agents');
|
|
2140
|
-
const standaloneAgentDirs = await fs.readdir(standaloneAgentsPath, { withFileTypes: true });
|
|
2141
|
-
for (const agentDir of standaloneAgentDirs) {
|
|
2142
|
-
if (agentDir.isDirectory()) {
|
|
2143
|
-
const agentDirPath = path.join(standaloneAgentsPath, agentDir.name);
|
|
2144
|
-
const agentFiles = await fs.readdir(agentDirPath);
|
|
2145
|
-
agentCount += agentFiles.filter((f) => f.endsWith('.md') && !f.endsWith('.agent.yaml')).length;
|
|
2146
|
-
}
|
|
2147
|
-
}
|
|
2148
|
-
} else {
|
|
2149
|
-
// Rebuild module agents from installer source
|
|
2150
|
-
const agentsPath = path.join(modulePath, 'agents');
|
|
2151
|
-
if (await fs.pathExists(agentsPath)) {
|
|
2152
|
-
await this.rebuildAgentFiles(modulePath, entry.name);
|
|
2153
|
-
const agentFiles = await fs.readdir(agentsPath);
|
|
2154
|
-
agentCount += agentFiles.filter((f) => f.endsWith('.md')).length;
|
|
2155
|
-
}
|
|
2156
|
-
|
|
2157
|
-
// Count tasks (already built)
|
|
2158
|
-
const tasksPath = path.join(modulePath, 'tasks');
|
|
2159
|
-
if (await fs.pathExists(tasksPath)) {
|
|
2160
|
-
const taskFiles = await fs.readdir(tasksPath);
|
|
2161
|
-
taskCount += taskFiles.filter((f) => f.endsWith('.md')).length;
|
|
2162
|
-
}
|
|
2163
|
-
}
|
|
2164
|
-
}
|
|
2165
|
-
}
|
|
2166
|
-
|
|
2167
|
-
// Update IDE configurations using the existing IDE list from manifest
|
|
2168
|
-
if (manifest && manifest.ides && manifest.ides.length > 0) {
|
|
2169
|
-
for (const ide of manifest.ides) {
|
|
2170
|
-
await this.ideManager.setup(ide, projectDir, bmadDir, {
|
|
2171
|
-
selectedModules: installedModules,
|
|
2172
|
-
skipModuleInstall: true, // Skip module installation, just update IDE files
|
|
2173
|
-
verbose: config.verbose,
|
|
2174
|
-
preCollectedConfig: { _alreadyConfigured: true }, // Skip all interactive prompts during compile
|
|
2175
|
-
});
|
|
2176
|
-
}
|
|
2177
|
-
console.log(chalk.green('✓ IDE configurations updated'));
|
|
2178
|
-
} else {
|
|
2179
|
-
console.log(chalk.yellow('⚠️ No IDEs configured. Skipping IDE update.'));
|
|
2180
|
-
}
|
|
2181
|
-
return { agentCount, taskCount };
|
|
2182
|
-
} catch (error) {
|
|
2183
|
-
throw error;
|
|
2184
|
-
}
|
|
2185
|
-
}
|
|
2186
|
-
|
|
2187
1817
|
/**
|
|
2188
1818
|
* Private: Update core
|
|
2189
1819
|
*/
|
|
@@ -2404,6 +2034,108 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2404
2034
|
}
|
|
2405
2035
|
}
|
|
2406
2036
|
|
|
2037
|
+
/**
|
|
2038
|
+
* Compile agents with customizations only
|
|
2039
|
+
* @param {Object} config - Configuration with directory
|
|
2040
|
+
* @returns {Object} Compilation result
|
|
2041
|
+
*/
|
|
2042
|
+
async compileAgents(config) {
|
|
2043
|
+
const ora = require('ora');
|
|
2044
|
+
const chalk = require('chalk');
|
|
2045
|
+
const { ModuleManager } = require('../modules/manager');
|
|
2046
|
+
const { getSourcePath } = require('../../../lib/project-root');
|
|
2047
|
+
|
|
2048
|
+
const spinner = ora('Recompiling agents with customizations...').start();
|
|
2049
|
+
|
|
2050
|
+
try {
|
|
2051
|
+
const projectDir = path.resolve(config.directory);
|
|
2052
|
+
const { bmadDir } = await this.findBmadDir(projectDir);
|
|
2053
|
+
|
|
2054
|
+
// Check if bmad directory exists
|
|
2055
|
+
if (!(await fs.pathExists(bmadDir))) {
|
|
2056
|
+
spinner.fail('No BMAD installation found');
|
|
2057
|
+
throw new Error(`BMAD not installed at ${bmadDir}. Use regular install for first-time setup.`);
|
|
2058
|
+
}
|
|
2059
|
+
|
|
2060
|
+
// Detect existing installation
|
|
2061
|
+
const existingInstall = await this.detector.detect(bmadDir);
|
|
2062
|
+
const installedModules = existingInstall.modules.map((m) => m.id);
|
|
2063
|
+
|
|
2064
|
+
// Initialize module manager
|
|
2065
|
+
const moduleManager = new ModuleManager();
|
|
2066
|
+
moduleManager.setBmadFolderName(path.basename(bmadDir));
|
|
2067
|
+
|
|
2068
|
+
let totalAgentCount = 0;
|
|
2069
|
+
|
|
2070
|
+
// Get custom module sources from cache
|
|
2071
|
+
const customModuleSources = new Map();
|
|
2072
|
+
const cacheDir = path.join(bmadDir, '_config', 'custom');
|
|
2073
|
+
if (await fs.pathExists(cacheDir)) {
|
|
2074
|
+
const cachedModules = await fs.readdir(cacheDir, { withFileTypes: true });
|
|
2075
|
+
|
|
2076
|
+
for (const cachedModule of cachedModules) {
|
|
2077
|
+
if (cachedModule.isDirectory()) {
|
|
2078
|
+
const moduleId = cachedModule.name;
|
|
2079
|
+
const cachedPath = path.join(cacheDir, moduleId);
|
|
2080
|
+
const moduleYamlPath = path.join(cachedPath, 'module.yaml');
|
|
2081
|
+
|
|
2082
|
+
// Check if this is actually a custom module
|
|
2083
|
+
if (await fs.pathExists(moduleYamlPath)) {
|
|
2084
|
+
customModuleSources.set(moduleId, cachedPath);
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
}
|
|
2089
|
+
|
|
2090
|
+
// Process each installed module
|
|
2091
|
+
for (const moduleId of installedModules) {
|
|
2092
|
+
spinner.text = `Recompiling agents in ${moduleId}...`;
|
|
2093
|
+
|
|
2094
|
+
// Get source path
|
|
2095
|
+
let sourcePath;
|
|
2096
|
+
if (moduleId === 'core') {
|
|
2097
|
+
sourcePath = getSourcePath('core');
|
|
2098
|
+
} else {
|
|
2099
|
+
// First check if it's in the custom cache
|
|
2100
|
+
if (customModuleSources.has(moduleId)) {
|
|
2101
|
+
sourcePath = customModuleSources.get(moduleId);
|
|
2102
|
+
} else {
|
|
2103
|
+
sourcePath = await moduleManager.findModuleSource(moduleId);
|
|
2104
|
+
}
|
|
2105
|
+
}
|
|
2106
|
+
|
|
2107
|
+
if (!sourcePath) {
|
|
2108
|
+
console.log(chalk.yellow(` Warning: Source not found for module ${moduleId}, skipping...`));
|
|
2109
|
+
continue;
|
|
2110
|
+
}
|
|
2111
|
+
|
|
2112
|
+
const targetPath = path.join(bmadDir, moduleId);
|
|
2113
|
+
|
|
2114
|
+
// Compile agents for this module
|
|
2115
|
+
await moduleManager.compileModuleAgents(sourcePath, targetPath, moduleId, bmadDir, this);
|
|
2116
|
+
|
|
2117
|
+
// Count agents (rough estimate based on files)
|
|
2118
|
+
const agentsPath = path.join(targetPath, 'agents');
|
|
2119
|
+
if (await fs.pathExists(agentsPath)) {
|
|
2120
|
+
const agentFiles = await fs.readdir(agentsPath);
|
|
2121
|
+
const agentCount = agentFiles.filter((f) => f.endsWith('.md')).length;
|
|
2122
|
+
totalAgentCount += agentCount;
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
|
|
2126
|
+
spinner.succeed('Agent recompilation complete!');
|
|
2127
|
+
|
|
2128
|
+
return {
|
|
2129
|
+
success: true,
|
|
2130
|
+
agentCount: totalAgentCount,
|
|
2131
|
+
modules: installedModules,
|
|
2132
|
+
};
|
|
2133
|
+
} catch (error) {
|
|
2134
|
+
spinner.fail('Agent recompilation failed');
|
|
2135
|
+
throw error;
|
|
2136
|
+
}
|
|
2137
|
+
}
|
|
2138
|
+
|
|
2407
2139
|
/**
|
|
2408
2140
|
* Private: Prompt for update action
|
|
2409
2141
|
*/
|
|
@@ -2677,190 +2409,6 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2677
2409
|
return { customFiles, modifiedFiles };
|
|
2678
2410
|
}
|
|
2679
2411
|
|
|
2680
|
-
/**
|
|
2681
|
-
* Private: Create agent configuration files
|
|
2682
|
-
* @param {string} bmadDir - BMAD installation directory
|
|
2683
|
-
* @param {Object} userInfo - User information including name and language
|
|
2684
|
-
*/
|
|
2685
|
-
async createAgentConfigs(bmadDir, userInfo = null) {
|
|
2686
|
-
const agentConfigDir = path.join(bmadDir, '_config', 'agents');
|
|
2687
|
-
await fs.ensureDir(agentConfigDir);
|
|
2688
|
-
|
|
2689
|
-
// Get all agents from all modules
|
|
2690
|
-
const agents = [];
|
|
2691
|
-
const agentDetails = []; // For manifest generation
|
|
2692
|
-
|
|
2693
|
-
// Check modules for agents (including core)
|
|
2694
|
-
const entries = await fs.readdir(bmadDir, { withFileTypes: true });
|
|
2695
|
-
for (const entry of entries) {
|
|
2696
|
-
if (entry.isDirectory() && entry.name !== '_config') {
|
|
2697
|
-
const moduleAgentsPath = path.join(bmadDir, entry.name, 'agents');
|
|
2698
|
-
if (await fs.pathExists(moduleAgentsPath)) {
|
|
2699
|
-
const agentFiles = await fs.readdir(moduleAgentsPath);
|
|
2700
|
-
for (const agentFile of agentFiles) {
|
|
2701
|
-
if (agentFile.endsWith('.md')) {
|
|
2702
|
-
const agentPath = path.join(moduleAgentsPath, agentFile);
|
|
2703
|
-
const agentContent = await fs.readFile(agentPath, 'utf8');
|
|
2704
|
-
|
|
2705
|
-
// Skip agents with localskip="true"
|
|
2706
|
-
const hasLocalSkip = agentContent.match(/<agent[^>]*\slocalskip="true"[^>]*>/);
|
|
2707
|
-
if (hasLocalSkip) {
|
|
2708
|
-
continue; // Skip this agent - it should not have been installed
|
|
2709
|
-
}
|
|
2710
|
-
|
|
2711
|
-
const agentName = path.basename(agentFile, '.md');
|
|
2712
|
-
|
|
2713
|
-
// Extract any nodes with agentConfig="true"
|
|
2714
|
-
const agentConfigNodes = this.extractAgentConfigNodes(agentContent);
|
|
2715
|
-
|
|
2716
|
-
agents.push({
|
|
2717
|
-
name: agentName,
|
|
2718
|
-
module: entry.name,
|
|
2719
|
-
agentConfigNodes: agentConfigNodes,
|
|
2720
|
-
});
|
|
2721
|
-
|
|
2722
|
-
// Use shared AgentPartyGenerator to extract details
|
|
2723
|
-
let details = AgentPartyGenerator.extractAgentDetails(agentContent, entry.name, agentName);
|
|
2724
|
-
|
|
2725
|
-
// Apply config overrides if they exist
|
|
2726
|
-
if (details) {
|
|
2727
|
-
const configPath = path.join(agentConfigDir, `${entry.name}-${agentName}.md`);
|
|
2728
|
-
if (await fs.pathExists(configPath)) {
|
|
2729
|
-
const configContent = await fs.readFile(configPath, 'utf8');
|
|
2730
|
-
details = AgentPartyGenerator.applyConfigOverrides(details, configContent);
|
|
2731
|
-
}
|
|
2732
|
-
agentDetails.push(details);
|
|
2733
|
-
}
|
|
2734
|
-
}
|
|
2735
|
-
}
|
|
2736
|
-
}
|
|
2737
|
-
}
|
|
2738
|
-
}
|
|
2739
|
-
|
|
2740
|
-
// Create config file for each agent
|
|
2741
|
-
let createdCount = 0;
|
|
2742
|
-
let skippedCount = 0;
|
|
2743
|
-
|
|
2744
|
-
// Load agent config template
|
|
2745
|
-
const templatePath = getSourcePath('utility', 'models', 'agent-config-template.md');
|
|
2746
|
-
const templateContent = await fs.readFile(templatePath, 'utf8');
|
|
2747
|
-
|
|
2748
|
-
for (const agent of agents) {
|
|
2749
|
-
const configPath = path.join(agentConfigDir, `${agent.module}-${agent.name}.md`);
|
|
2750
|
-
|
|
2751
|
-
// Skip if config file already exists (preserve custom configurations)
|
|
2752
|
-
if (await fs.pathExists(configPath)) {
|
|
2753
|
-
skippedCount++;
|
|
2754
|
-
continue;
|
|
2755
|
-
}
|
|
2756
|
-
|
|
2757
|
-
// Build config content header
|
|
2758
|
-
let configContent = `# Agent Config: ${agent.name}\n\n`;
|
|
2759
|
-
|
|
2760
|
-
// Process template and add agent-specific config nodes
|
|
2761
|
-
let processedTemplate = templateContent;
|
|
2762
|
-
|
|
2763
|
-
// Replace {core:user_name} placeholder with actual user name if available
|
|
2764
|
-
if (userInfo && userInfo.userName) {
|
|
2765
|
-
processedTemplate = processedTemplate.replaceAll('{core:user_name}', userInfo.userName);
|
|
2766
|
-
}
|
|
2767
|
-
|
|
2768
|
-
// Replace {core:communication_language} placeholder with actual language if available
|
|
2769
|
-
if (userInfo && userInfo.responseLanguage) {
|
|
2770
|
-
processedTemplate = processedTemplate.replaceAll('{core:communication_language}', userInfo.responseLanguage);
|
|
2771
|
-
}
|
|
2772
|
-
|
|
2773
|
-
// If this agent has agentConfig nodes, add them after the existing comment
|
|
2774
|
-
if (agent.agentConfigNodes && agent.agentConfigNodes.length > 0) {
|
|
2775
|
-
// Find the agent-specific configuration nodes comment
|
|
2776
|
-
const commentPattern = /(\s*<!-- Agent-specific configuration nodes -->)/;
|
|
2777
|
-
const commentMatch = processedTemplate.match(commentPattern);
|
|
2778
|
-
|
|
2779
|
-
if (commentMatch) {
|
|
2780
|
-
// Add nodes right after the comment
|
|
2781
|
-
let agentSpecificNodes = '';
|
|
2782
|
-
for (const node of agent.agentConfigNodes) {
|
|
2783
|
-
agentSpecificNodes += `\n ${node}`;
|
|
2784
|
-
}
|
|
2785
|
-
|
|
2786
|
-
processedTemplate = processedTemplate.replace(commentPattern, `$1${agentSpecificNodes}`);
|
|
2787
|
-
}
|
|
2788
|
-
}
|
|
2789
|
-
|
|
2790
|
-
configContent += processedTemplate;
|
|
2791
|
-
|
|
2792
|
-
// Ensure POSIX-compliant final newline
|
|
2793
|
-
if (!configContent.endsWith('\n')) {
|
|
2794
|
-
configContent += '\n';
|
|
2795
|
-
}
|
|
2796
|
-
|
|
2797
|
-
await fs.writeFile(configPath, configContent, 'utf8');
|
|
2798
|
-
this.installedFiles.add(configPath); // Track agent config files
|
|
2799
|
-
createdCount++;
|
|
2800
|
-
}
|
|
2801
|
-
|
|
2802
|
-
// Generate agent manifest with overrides applied
|
|
2803
|
-
await this.generateAgentManifest(bmadDir, agentDetails);
|
|
2804
|
-
|
|
2805
|
-
return { total: agents.length, created: createdCount, skipped: skippedCount };
|
|
2806
|
-
}
|
|
2807
|
-
|
|
2808
|
-
/**
|
|
2809
|
-
* Generate agent manifest XML file
|
|
2810
|
-
* @param {string} bmadDir - BMAD installation directory
|
|
2811
|
-
* @param {Array} agentDetails - Array of agent details
|
|
2812
|
-
*/
|
|
2813
|
-
async generateAgentManifest(bmadDir, agentDetails) {
|
|
2814
|
-
const manifestPath = path.join(bmadDir, '_config', 'agent-manifest.csv');
|
|
2815
|
-
await AgentPartyGenerator.writeAgentParty(manifestPath, agentDetails, { forWeb: false });
|
|
2816
|
-
}
|
|
2817
|
-
|
|
2818
|
-
/**
|
|
2819
|
-
* Extract nodes with agentConfig="true" from agent content
|
|
2820
|
-
* @param {string} content - Agent file content
|
|
2821
|
-
* @returns {Array} Array of XML nodes that should be added to agent config
|
|
2822
|
-
*/
|
|
2823
|
-
extractAgentConfigNodes(content) {
|
|
2824
|
-
const nodes = [];
|
|
2825
|
-
|
|
2826
|
-
try {
|
|
2827
|
-
// Find all XML nodes with agentConfig="true"
|
|
2828
|
-
// Match self-closing tags and tags with content
|
|
2829
|
-
const selfClosingPattern = /<([a-zA-Z][a-zA-Z0-9_-]*)\s+[^>]*agentConfig="true"[^>]*\/>/g;
|
|
2830
|
-
const withContentPattern = /<([a-zA-Z][a-zA-Z0-9_-]*)\s+[^>]*agentConfig="true"[^>]*>([\s\S]*?)<\/\1>/g;
|
|
2831
|
-
|
|
2832
|
-
// Extract self-closing tags
|
|
2833
|
-
let match;
|
|
2834
|
-
while ((match = selfClosingPattern.exec(content)) !== null) {
|
|
2835
|
-
// Extract just the tag without children (structure only)
|
|
2836
|
-
const tagMatch = match[0].match(/<([a-zA-Z][a-zA-Z0-9_-]*)([^>]*)\/>/);
|
|
2837
|
-
if (tagMatch) {
|
|
2838
|
-
const tagName = tagMatch[1];
|
|
2839
|
-
const attributes = tagMatch[2].replace(/\s*agentConfig="true"/, ''); // Remove agentConfig attribute
|
|
2840
|
-
nodes.push(`<${tagName}${attributes}></${tagName}>`);
|
|
2841
|
-
}
|
|
2842
|
-
}
|
|
2843
|
-
|
|
2844
|
-
// Extract tags with content
|
|
2845
|
-
while ((match = withContentPattern.exec(content)) !== null) {
|
|
2846
|
-
const fullMatch = match[0];
|
|
2847
|
-
const tagName = match[1];
|
|
2848
|
-
|
|
2849
|
-
// Extract opening tag with attributes (removing agentConfig="true")
|
|
2850
|
-
const openingTagMatch = fullMatch.match(new RegExp(`<${tagName}([^>]*)>`));
|
|
2851
|
-
if (openingTagMatch) {
|
|
2852
|
-
const attributes = openingTagMatch[1].replace(/\s*agentConfig="true"/, '');
|
|
2853
|
-
// Add empty node structure (no children)
|
|
2854
|
-
nodes.push(`<${tagName}${attributes}></${tagName}>`);
|
|
2855
|
-
}
|
|
2856
|
-
}
|
|
2857
|
-
} catch (error) {
|
|
2858
|
-
console.error('Error extracting agentConfig nodes:', error);
|
|
2859
|
-
}
|
|
2860
|
-
|
|
2861
|
-
return nodes;
|
|
2862
|
-
}
|
|
2863
|
-
|
|
2864
2412
|
/**
|
|
2865
2413
|
* Handle missing custom module sources interactively
|
|
2866
2414
|
* @param {Map} customModuleSources - Map of custom module ID to info
|
|
@@ -2999,7 +2547,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
2999
2547
|
await this.manifest.addCustomModule(bmadDir, missing.info);
|
|
3000
2548
|
|
|
3001
2549
|
validCustomModules.push({
|
|
3002
|
-
id:
|
|
2550
|
+
id: missing.id,
|
|
3003
2551
|
name: missing.name,
|
|
3004
2552
|
path: resolvedPath,
|
|
3005
2553
|
info: missing.info,
|
|
@@ -3013,7 +2561,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
|
|
3013
2561
|
case 'remove': {
|
|
3014
2562
|
// Extra confirmation for destructive remove
|
|
3015
2563
|
console.log(chalk.red.bold(`\n⚠️ WARNING: This will PERMANENTLY DELETE "${missing.name}" and all its files!`));
|
|
3016
|
-
console.log(chalk.red(` Module location: ${path.join(bmadDir,
|
|
2564
|
+
console.log(chalk.red(` Module location: ${path.join(bmadDir, missing.id)}`));
|
|
3017
2565
|
|
|
3018
2566
|
const { confirm } = await inquirer.prompt([
|
|
3019
2567
|
{
|