bmad-method 4.30.1 → 4.30.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +1 -1
  3. package/bmad-core/core-config.yaml +0 -1
  4. package/bmad-core/data/bmad-kb.md +1 -1
  5. package/dist/agents/analyst.txt +1 -1
  6. package/dist/agents/bmad-master.txt +1 -1
  7. package/dist/agents/bmad-orchestrator.txt +1 -1
  8. package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-designer.txt +2409 -0
  9. package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-developer.txt +1480 -0
  10. package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-sm.txt +826 -0
  11. package/dist/expansion-packs/bmad-2d-unity-game-dev/teams/unity-2d-game-team.txt +10690 -0
  12. package/dist/teams/team-all.txt +1 -1
  13. package/dist/teams/team-fullstack.txt +1 -1
  14. package/dist/teams/team-ide-minimal.txt +1 -1
  15. package/dist/teams/team-no-ui.txt +1 -1
  16. package/docs/bmad-workflow-guide.md +2 -2
  17. package/expansion-packs/bmad-2d-phaser-game-dev/config.yaml +2 -2
  18. package/expansion-packs/bmad-2d-unity-game-dev/agent-teams/unity-2d-game-team.yaml +13 -0
  19. package/expansion-packs/bmad-2d-unity-game-dev/agents/game-designer.md +72 -0
  20. package/expansion-packs/bmad-2d-unity-game-dev/agents/game-developer.md +78 -0
  21. package/expansion-packs/{bmad-creator-tools/agents/bmad-the-creator.md → bmad-2d-unity-game-dev/agents/game-sm.md} +26 -28
  22. package/expansion-packs/bmad-2d-unity-game-dev/checklists/game-design-checklist.md +201 -0
  23. package/expansion-packs/bmad-2d-unity-game-dev/checklists/game-story-dod-checklist.md +160 -0
  24. package/expansion-packs/bmad-2d-unity-game-dev/config.yaml +6 -0
  25. package/expansion-packs/bmad-2d-unity-game-dev/data/bmad-kb.md +251 -0
  26. package/expansion-packs/bmad-2d-unity-game-dev/data/development-guidelines.md +590 -0
  27. package/expansion-packs/bmad-2d-unity-game-dev/tasks/advanced-elicitation.md +111 -0
  28. package/expansion-packs/bmad-2d-unity-game-dev/tasks/create-game-story.md +217 -0
  29. package/expansion-packs/bmad-2d-unity-game-dev/tasks/game-design-brainstorming.md +308 -0
  30. package/expansion-packs/bmad-2d-unity-game-dev/templates/game-architecture-tmpl.yaml +545 -0
  31. package/expansion-packs/bmad-2d-unity-game-dev/templates/game-brief-tmpl.yaml +356 -0
  32. package/expansion-packs/bmad-2d-unity-game-dev/templates/game-design-doc-tmpl.yaml +343 -0
  33. package/expansion-packs/bmad-2d-unity-game-dev/templates/game-story-tmpl.yaml +256 -0
  34. package/expansion-packs/bmad-2d-unity-game-dev/templates/level-design-doc-tmpl.yaml +484 -0
  35. package/expansion-packs/bmad-2d-unity-game-dev/workflows/game-dev-greenfield.yaml +183 -0
  36. package/expansion-packs/bmad-2d-unity-game-dev/workflows/game-prototype.yaml +175 -0
  37. package/expansion-packs/bmad-infrastructure-devops/config.yaml +2 -2
  38. package/package.json +4 -8
  39. package/tools/bump-all-versions.js +8 -9
  40. package/tools/bump-expansion-version.js +40 -35
  41. package/tools/installer/bin/bmad.js +8 -21
  42. package/tools/installer/lib/file-manager.js +76 -44
  43. package/tools/installer/lib/ide-base-setup.js +227 -0
  44. package/tools/installer/lib/ide-setup.js +14 -60
  45. package/tools/installer/lib/installer.js +99 -121
  46. package/tools/installer/lib/memory-profiler.js +224 -0
  47. package/tools/installer/lib/module-manager.js +110 -0
  48. package/tools/installer/lib/resource-locator.js +310 -0
  49. package/tools/installer/package.json +1 -1
  50. package/tools/semantic-release-sync-installer.js +20 -21
  51. package/dist/expansion-packs/bmad-creator-tools/agents/bmad-the-creator.txt +0 -2008
  52. package/expansion-packs/bmad-creator-tools/README.md +0 -8
  53. package/expansion-packs/bmad-creator-tools/config.yaml +0 -6
  54. package/expansion-packs/bmad-creator-tools/tasks/create-agent.md +0 -200
  55. package/expansion-packs/bmad-creator-tools/tasks/generate-expansion-pack.md +0 -1020
  56. package/expansion-packs/bmad-creator-tools/templates/agent-teams-tmpl.yaml +0 -178
  57. package/expansion-packs/bmad-creator-tools/templates/agent-tmpl.yaml +0 -154
  58. package/expansion-packs/bmad-creator-tools/templates/expansion-pack-plan-tmpl.yaml +0 -120
  59. package/tools/bump-core-version.js +0 -57
@@ -0,0 +1,224 @@
1
+ /**
2
+ * Memory Profiler - Track memory usage during installation
3
+ * Helps identify memory leaks and optimize resource usage
4
+ */
5
+
6
+ const v8 = require('v8');
7
+
8
+ class MemoryProfiler {
9
+ constructor() {
10
+ this.checkpoints = [];
11
+ this.startTime = Date.now();
12
+ this.peakMemory = 0;
13
+ }
14
+
15
+ /**
16
+ * Create a memory checkpoint
17
+ * @param {string} label - Label for this checkpoint
18
+ */
19
+ checkpoint(label) {
20
+ const memUsage = process.memoryUsage();
21
+ const heapStats = v8.getHeapStatistics();
22
+
23
+ const checkpoint = {
24
+ label,
25
+ timestamp: Date.now() - this.startTime,
26
+ memory: {
27
+ rss: this.formatBytes(memUsage.rss),
28
+ heapTotal: this.formatBytes(memUsage.heapTotal),
29
+ heapUsed: this.formatBytes(memUsage.heapUsed),
30
+ external: this.formatBytes(memUsage.external),
31
+ arrayBuffers: this.formatBytes(memUsage.arrayBuffers || 0)
32
+ },
33
+ heap: {
34
+ totalHeapSize: this.formatBytes(heapStats.total_heap_size),
35
+ usedHeapSize: this.formatBytes(heapStats.used_heap_size),
36
+ heapSizeLimit: this.formatBytes(heapStats.heap_size_limit),
37
+ mallocedMemory: this.formatBytes(heapStats.malloced_memory),
38
+ externalMemory: this.formatBytes(heapStats.external_memory)
39
+ },
40
+ raw: {
41
+ heapUsed: memUsage.heapUsed
42
+ }
43
+ };
44
+
45
+ // Track peak memory
46
+ if (memUsage.heapUsed > this.peakMemory) {
47
+ this.peakMemory = memUsage.heapUsed;
48
+ }
49
+
50
+ this.checkpoints.push(checkpoint);
51
+ return checkpoint;
52
+ }
53
+
54
+ /**
55
+ * Force garbage collection (requires --expose-gc flag)
56
+ */
57
+ forceGC() {
58
+ if (global.gc) {
59
+ global.gc();
60
+ return true;
61
+ }
62
+ return false;
63
+ }
64
+
65
+ /**
66
+ * Get memory usage summary
67
+ */
68
+ getSummary() {
69
+ const currentMemory = process.memoryUsage();
70
+
71
+ return {
72
+ currentUsage: {
73
+ rss: this.formatBytes(currentMemory.rss),
74
+ heapTotal: this.formatBytes(currentMemory.heapTotal),
75
+ heapUsed: this.formatBytes(currentMemory.heapUsed)
76
+ },
77
+ peakMemory: this.formatBytes(this.peakMemory),
78
+ totalCheckpoints: this.checkpoints.length,
79
+ runTime: `${((Date.now() - this.startTime) / 1000).toFixed(2)}s`
80
+ };
81
+ }
82
+
83
+ /**
84
+ * Get detailed report of memory usage
85
+ */
86
+ getDetailedReport() {
87
+ const summary = this.getSummary();
88
+ const memoryGrowth = this.calculateMemoryGrowth();
89
+
90
+ return {
91
+ summary,
92
+ memoryGrowth,
93
+ checkpoints: this.checkpoints,
94
+ recommendations: this.getRecommendations(memoryGrowth)
95
+ };
96
+ }
97
+
98
+ /**
99
+ * Calculate memory growth between checkpoints
100
+ */
101
+ calculateMemoryGrowth() {
102
+ if (this.checkpoints.length < 2) return [];
103
+
104
+ const growth = [];
105
+ for (let i = 1; i < this.checkpoints.length; i++) {
106
+ const prev = this.checkpoints[i - 1];
107
+ const curr = this.checkpoints[i];
108
+
109
+ const heapDiff = curr.raw.heapUsed - prev.raw.heapUsed;
110
+
111
+ growth.push({
112
+ from: prev.label,
113
+ to: curr.label,
114
+ heapGrowth: this.formatBytes(Math.abs(heapDiff)),
115
+ isIncrease: heapDiff > 0,
116
+ timeDiff: `${((curr.timestamp - prev.timestamp) / 1000).toFixed(2)}s`
117
+ });
118
+ }
119
+
120
+ return growth;
121
+ }
122
+
123
+ /**
124
+ * Get recommendations based on memory usage
125
+ */
126
+ getRecommendations(memoryGrowth) {
127
+ const recommendations = [];
128
+
129
+ // Check for large memory growth
130
+ const largeGrowths = memoryGrowth.filter(g => {
131
+ const bytes = this.parseBytes(g.heapGrowth);
132
+ return bytes > 50 * 1024 * 1024; // 50MB
133
+ });
134
+
135
+ if (largeGrowths.length > 0) {
136
+ recommendations.push({
137
+ type: 'warning',
138
+ message: `Large memory growth detected in ${largeGrowths.length} operations`,
139
+ details: largeGrowths.map(g => `${g.from} → ${g.to}: ${g.heapGrowth}`)
140
+ });
141
+ }
142
+
143
+ // Check peak memory
144
+ if (this.peakMemory > 500 * 1024 * 1024) { // 500MB
145
+ recommendations.push({
146
+ type: 'warning',
147
+ message: `High peak memory usage: ${this.formatBytes(this.peakMemory)}`,
148
+ suggestion: 'Consider processing files in smaller batches'
149
+ });
150
+ }
151
+
152
+ // Check for potential memory leaks
153
+ const continuousGrowth = this.checkContinuousGrowth();
154
+ if (continuousGrowth) {
155
+ recommendations.push({
156
+ type: 'error',
157
+ message: 'Potential memory leak detected',
158
+ details: 'Memory usage continuously increases without significant decreases'
159
+ });
160
+ }
161
+
162
+ return recommendations;
163
+ }
164
+
165
+ /**
166
+ * Check for continuous memory growth (potential leak)
167
+ */
168
+ checkContinuousGrowth() {
169
+ if (this.checkpoints.length < 5) return false;
170
+
171
+ let increasingCount = 0;
172
+ for (let i = 1; i < this.checkpoints.length; i++) {
173
+ if (this.checkpoints[i].raw.heapUsed > this.checkpoints[i - 1].raw.heapUsed) {
174
+ increasingCount++;
175
+ }
176
+ }
177
+
178
+ // If memory increases in more than 80% of checkpoints, might be a leak
179
+ return increasingCount / (this.checkpoints.length - 1) > 0.8;
180
+ }
181
+
182
+ /**
183
+ * Format bytes to human-readable string
184
+ */
185
+ formatBytes(bytes) {
186
+ if (bytes === 0) return '0 B';
187
+
188
+ const k = 1024;
189
+ const sizes = ['B', 'KB', 'MB', 'GB'];
190
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
191
+
192
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
193
+ }
194
+
195
+ /**
196
+ * Parse human-readable bytes back to number
197
+ */
198
+ parseBytes(str) {
199
+ const match = str.match(/^([\d.]+)\s*([KMGT]?B?)$/i);
200
+ if (!match) return 0;
201
+
202
+ const value = parseFloat(match[1]);
203
+ const unit = match[2].toUpperCase();
204
+
205
+ const multipliers = {
206
+ 'B': 1,
207
+ 'KB': 1024,
208
+ 'MB': 1024 * 1024,
209
+ 'GB': 1024 * 1024 * 1024
210
+ };
211
+
212
+ return value * (multipliers[unit] || 1);
213
+ }
214
+
215
+ /**
216
+ * Clear checkpoints to free memory
217
+ */
218
+ clear() {
219
+ this.checkpoints = [];
220
+ }
221
+ }
222
+
223
+ // Export singleton instance
224
+ module.exports = new MemoryProfiler();
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Module Manager - Centralized dynamic import management
3
+ * Handles loading and caching of ES modules to reduce memory overhead
4
+ */
5
+
6
+ class ModuleManager {
7
+ constructor() {
8
+ this._cache = new Map();
9
+ this._loadingPromises = new Map();
10
+ }
11
+
12
+ /**
13
+ * Initialize all commonly used ES modules at once
14
+ * @returns {Promise<Object>} Object containing all loaded modules
15
+ */
16
+ async initializeCommonModules() {
17
+ const modules = await Promise.all([
18
+ this.getModule('chalk'),
19
+ this.getModule('ora'),
20
+ this.getModule('inquirer')
21
+ ]);
22
+
23
+ return {
24
+ chalk: modules[0],
25
+ ora: modules[1],
26
+ inquirer: modules[2]
27
+ };
28
+ }
29
+
30
+ /**
31
+ * Get a module by name, with caching
32
+ * @param {string} moduleName - Name of the module to load
33
+ * @returns {Promise<any>} The loaded module
34
+ */
35
+ async getModule(moduleName) {
36
+ // Return from cache if available
37
+ if (this._cache.has(moduleName)) {
38
+ return this._cache.get(moduleName);
39
+ }
40
+
41
+ // If already loading, return the existing promise
42
+ if (this._loadingPromises.has(moduleName)) {
43
+ return this._loadingPromises.get(moduleName);
44
+ }
45
+
46
+ // Start loading the module
47
+ const loadPromise = this._loadModule(moduleName);
48
+ this._loadingPromises.set(moduleName, loadPromise);
49
+
50
+ try {
51
+ const module = await loadPromise;
52
+ this._cache.set(moduleName, module);
53
+ this._loadingPromises.delete(moduleName);
54
+ return module;
55
+ } catch (error) {
56
+ this._loadingPromises.delete(moduleName);
57
+ throw error;
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Internal method to load a specific module
63
+ * @private
64
+ */
65
+ async _loadModule(moduleName) {
66
+ switch (moduleName) {
67
+ case 'chalk':
68
+ return (await import('chalk')).default;
69
+ case 'ora':
70
+ return (await import('ora')).default;
71
+ case 'inquirer':
72
+ return (await import('inquirer')).default;
73
+ case 'glob':
74
+ return (await import('glob')).glob;
75
+ case 'globSync':
76
+ return (await import('glob')).globSync;
77
+ default:
78
+ throw new Error(`Unknown module: ${moduleName}`);
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Clear the module cache to free memory
84
+ */
85
+ clearCache() {
86
+ this._cache.clear();
87
+ this._loadingPromises.clear();
88
+ }
89
+
90
+ /**
91
+ * Get multiple modules at once
92
+ * @param {string[]} moduleNames - Array of module names
93
+ * @returns {Promise<Object>} Object with module names as keys
94
+ */
95
+ async getModules(moduleNames) {
96
+ const modules = await Promise.all(
97
+ moduleNames.map(name => this.getModule(name))
98
+ );
99
+
100
+ return moduleNames.reduce((acc, name, index) => {
101
+ acc[name] = modules[index];
102
+ return acc;
103
+ }, {});
104
+ }
105
+ }
106
+
107
+ // Singleton instance
108
+ const moduleManager = new ModuleManager();
109
+
110
+ module.exports = moduleManager;
@@ -0,0 +1,310 @@
1
+ /**
2
+ * Resource Locator - Centralized file path resolution and caching
3
+ * Reduces duplicate file system operations and memory usage
4
+ */
5
+
6
+ const path = require('node:path');
7
+ const fs = require('fs-extra');
8
+ const moduleManager = require('./module-manager');
9
+
10
+ class ResourceLocator {
11
+ constructor() {
12
+ this._pathCache = new Map();
13
+ this._globCache = new Map();
14
+ this._bmadCorePath = null;
15
+ this._expansionPacksPath = null;
16
+ }
17
+
18
+ /**
19
+ * Get the base path for bmad-core
20
+ */
21
+ getBmadCorePath() {
22
+ if (!this._bmadCorePath) {
23
+ this._bmadCorePath = path.join(__dirname, '../../../bmad-core');
24
+ }
25
+ return this._bmadCorePath;
26
+ }
27
+
28
+ /**
29
+ * Get the base path for expansion packs
30
+ */
31
+ getExpansionPacksPath() {
32
+ if (!this._expansionPacksPath) {
33
+ this._expansionPacksPath = path.join(__dirname, '../../../expansion-packs');
34
+ }
35
+ return this._expansionPacksPath;
36
+ }
37
+
38
+ /**
39
+ * Find all files matching a pattern, with caching
40
+ * @param {string} pattern - Glob pattern
41
+ * @param {Object} options - Glob options
42
+ * @returns {Promise<string[]>} Array of matched file paths
43
+ */
44
+ async findFiles(pattern, options = {}) {
45
+ const cacheKey = `${pattern}:${JSON.stringify(options)}`;
46
+
47
+ if (this._globCache.has(cacheKey)) {
48
+ return this._globCache.get(cacheKey);
49
+ }
50
+
51
+ const { glob } = await moduleManager.getModules(['glob']);
52
+ const files = await glob(pattern, options);
53
+
54
+ // Cache for 5 minutes
55
+ this._globCache.set(cacheKey, files);
56
+ setTimeout(() => this._globCache.delete(cacheKey), 5 * 60 * 1000);
57
+
58
+ return files;
59
+ }
60
+
61
+ /**
62
+ * Get agent path with caching
63
+ * @param {string} agentId - Agent identifier
64
+ * @returns {Promise<string|null>} Path to agent file or null if not found
65
+ */
66
+ async getAgentPath(agentId) {
67
+ const cacheKey = `agent:${agentId}`;
68
+
69
+ if (this._pathCache.has(cacheKey)) {
70
+ return this._pathCache.get(cacheKey);
71
+ }
72
+
73
+ // Check in bmad-core
74
+ let agentPath = path.join(this.getBmadCorePath(), 'agents', `${agentId}.md`);
75
+ if (await fs.pathExists(agentPath)) {
76
+ this._pathCache.set(cacheKey, agentPath);
77
+ return agentPath;
78
+ }
79
+
80
+ // Check in expansion packs
81
+ const expansionPacks = await this.getExpansionPacks();
82
+ for (const pack of expansionPacks) {
83
+ agentPath = path.join(pack.path, 'agents', `${agentId}.md`);
84
+ if (await fs.pathExists(agentPath)) {
85
+ this._pathCache.set(cacheKey, agentPath);
86
+ return agentPath;
87
+ }
88
+ }
89
+
90
+ return null;
91
+ }
92
+
93
+ /**
94
+ * Get available agents with metadata
95
+ * @returns {Promise<Array>} Array of agent objects
96
+ */
97
+ async getAvailableAgents() {
98
+ const cacheKey = 'all-agents';
99
+
100
+ if (this._pathCache.has(cacheKey)) {
101
+ return this._pathCache.get(cacheKey);
102
+ }
103
+
104
+ const agents = [];
105
+ const yaml = require('js-yaml');
106
+ const { extractYamlFromAgent } = require('../../lib/yaml-utils');
107
+
108
+ // Get agents from bmad-core
109
+ const coreAgents = await this.findFiles('agents/*.md', {
110
+ cwd: this.getBmadCorePath()
111
+ });
112
+
113
+ for (const agentFile of coreAgents) {
114
+ const content = await fs.readFile(
115
+ path.join(this.getBmadCorePath(), agentFile),
116
+ 'utf8'
117
+ );
118
+ const yamlContent = extractYamlFromAgent(content);
119
+ if (yamlContent) {
120
+ try {
121
+ const metadata = yaml.load(yamlContent);
122
+ agents.push({
123
+ id: path.basename(agentFile, '.md'),
124
+ name: metadata.agent_name || path.basename(agentFile, '.md'),
125
+ description: metadata.description || 'No description available',
126
+ source: 'core'
127
+ });
128
+ } catch (e) {
129
+ // Skip invalid agents
130
+ }
131
+ }
132
+ }
133
+
134
+ // Cache for 10 minutes
135
+ this._pathCache.set(cacheKey, agents);
136
+ setTimeout(() => this._pathCache.delete(cacheKey), 10 * 60 * 1000);
137
+
138
+ return agents;
139
+ }
140
+
141
+ /**
142
+ * Get available expansion packs
143
+ * @returns {Promise<Array>} Array of expansion pack objects
144
+ */
145
+ async getExpansionPacks() {
146
+ const cacheKey = 'expansion-packs';
147
+
148
+ if (this._pathCache.has(cacheKey)) {
149
+ return this._pathCache.get(cacheKey);
150
+ }
151
+
152
+ const packs = [];
153
+ const expansionPacksPath = this.getExpansionPacksPath();
154
+
155
+ if (await fs.pathExists(expansionPacksPath)) {
156
+ const entries = await fs.readdir(expansionPacksPath, { withFileTypes: true });
157
+
158
+ for (const entry of entries) {
159
+ if (entry.isDirectory()) {
160
+ const configPath = path.join(expansionPacksPath, entry.name, 'config.yaml');
161
+ if (await fs.pathExists(configPath)) {
162
+ try {
163
+ const yaml = require('js-yaml');
164
+ const config = yaml.load(await fs.readFile(configPath, 'utf8'));
165
+ packs.push({
166
+ id: entry.name,
167
+ name: config.name || entry.name,
168
+ version: config.version || '1.0.0',
169
+ description: config.description || 'No description available',
170
+ shortTitle: config['short-title'] || config.description || 'No description available',
171
+ author: config.author || 'Unknown',
172
+ path: path.join(expansionPacksPath, entry.name)
173
+ });
174
+ } catch (e) {
175
+ // Skip invalid packs
176
+ }
177
+ }
178
+ }
179
+ }
180
+ }
181
+
182
+ // Cache for 10 minutes
183
+ this._pathCache.set(cacheKey, packs);
184
+ setTimeout(() => this._pathCache.delete(cacheKey), 10 * 60 * 1000);
185
+
186
+ return packs;
187
+ }
188
+
189
+ /**
190
+ * Get team configuration
191
+ * @param {string} teamId - Team identifier
192
+ * @returns {Promise<Object|null>} Team configuration or null
193
+ */
194
+ async getTeamConfig(teamId) {
195
+ const cacheKey = `team:${teamId}`;
196
+
197
+ if (this._pathCache.has(cacheKey)) {
198
+ return this._pathCache.get(cacheKey);
199
+ }
200
+
201
+ const teamPath = path.join(this.getBmadCorePath(), 'agent-teams', `${teamId}.yaml`);
202
+
203
+ if (await fs.pathExists(teamPath)) {
204
+ try {
205
+ const yaml = require('js-yaml');
206
+ const content = await fs.readFile(teamPath, 'utf8');
207
+ const config = yaml.load(content);
208
+ this._pathCache.set(cacheKey, config);
209
+ return config;
210
+ } catch (e) {
211
+ return null;
212
+ }
213
+ }
214
+
215
+ return null;
216
+ }
217
+
218
+ /**
219
+ * Get resource dependencies for an agent
220
+ * @param {string} agentId - Agent identifier
221
+ * @returns {Promise<Object>} Dependencies object
222
+ */
223
+ async getAgentDependencies(agentId) {
224
+ const cacheKey = `deps:${agentId}`;
225
+
226
+ if (this._pathCache.has(cacheKey)) {
227
+ return this._pathCache.get(cacheKey);
228
+ }
229
+
230
+ const agentPath = await this.getAgentPath(agentId);
231
+ if (!agentPath) {
232
+ return { all: [], byType: {} };
233
+ }
234
+
235
+ const content = await fs.readFile(agentPath, 'utf8');
236
+ const { extractYamlFromAgent } = require('../../lib/yaml-utils');
237
+ const yamlContent = extractYamlFromAgent(content);
238
+
239
+ if (!yamlContent) {
240
+ return { all: [], byType: {} };
241
+ }
242
+
243
+ try {
244
+ const yaml = require('js-yaml');
245
+ const metadata = yaml.load(yamlContent);
246
+ const dependencies = metadata.dependencies || {};
247
+
248
+ // Flatten dependencies
249
+ const allDeps = [];
250
+ const byType = {};
251
+
252
+ for (const [type, deps] of Object.entries(dependencies)) {
253
+ if (Array.isArray(deps)) {
254
+ byType[type] = deps;
255
+ for (const dep of deps) {
256
+ allDeps.push(`.bmad-core/${type}/${dep}`);
257
+ }
258
+ }
259
+ }
260
+
261
+ const result = { all: allDeps, byType };
262
+ this._pathCache.set(cacheKey, result);
263
+ return result;
264
+ } catch (e) {
265
+ return { all: [], byType: {} };
266
+ }
267
+ }
268
+
269
+ /**
270
+ * Clear all caches to free memory
271
+ */
272
+ clearCache() {
273
+ this._pathCache.clear();
274
+ this._globCache.clear();
275
+ }
276
+
277
+ /**
278
+ * Get IDE configuration
279
+ * @param {string} ideId - IDE identifier
280
+ * @returns {Promise<Object|null>} IDE configuration or null
281
+ */
282
+ async getIdeConfig(ideId) {
283
+ const cacheKey = `ide:${ideId}`;
284
+
285
+ if (this._pathCache.has(cacheKey)) {
286
+ return this._pathCache.get(cacheKey);
287
+ }
288
+
289
+ const idePath = path.join(this.getBmadCorePath(), 'ide-rules', `${ideId}.yaml`);
290
+
291
+ if (await fs.pathExists(idePath)) {
292
+ try {
293
+ const yaml = require('js-yaml');
294
+ const content = await fs.readFile(idePath, 'utf8');
295
+ const config = yaml.load(content);
296
+ this._pathCache.set(cacheKey, config);
297
+ return config;
298
+ } catch (e) {
299
+ return null;
300
+ }
301
+ }
302
+
303
+ return null;
304
+ }
305
+ }
306
+
307
+ // Singleton instance
308
+ const resourceLocator = new ResourceLocator();
309
+
310
+ module.exports = resourceLocator;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bmad-method",
3
- "version": "4.30.1",
3
+ "version": "4.30.3",
4
4
  "description": "BMad Method installer - AI-powered Agile development framework",
5
5
  "main": "lib/installer.js",
6
6
  "bin": {
@@ -5,27 +5,26 @@
5
5
  const fs = require('fs');
6
6
  const path = require('path');
7
7
 
8
- function prepare(pluginConfig, context) {
9
- const { nextRelease, logger } = context;
10
-
11
- // Path to installer package.json
12
- const installerPackagePath = path.join(process.cwd(), 'tools', 'installer', 'package.json');
13
-
14
- if (!fs.existsSync(installerPackagePath)) {
15
- logger.log('Installer package.json not found, skipping sync');
16
- return;
17
- }
18
-
19
- // Read installer package.json
20
- const installerPackage = JSON.parse(fs.readFileSync(installerPackagePath, 'utf8'));
21
-
22
- // Update version
23
- installerPackage.version = nextRelease.version;
24
-
25
- // Write back
26
- fs.writeFileSync(installerPackagePath, JSON.stringify(installerPackage, null, 2) + '\n');
27
-
8
+ // This function runs during the "prepare" step of semantic-release
9
+ function prepare(_, { nextRelease, logger }) {
10
+ // Define the path to the installer package.json file
11
+ const file = path.join(process.cwd(), 'tools/installer/package.json');
12
+
13
+ // If the file does not exist, skip syncing and log a message
14
+ if (!fs.existsSync(file)) return logger.log('Installer package.json not found, skipping');
15
+
16
+ // Read and parse the package.json file
17
+ const pkg = JSON.parse(fs.readFileSync(file, 'utf8'));
18
+
19
+ // Update the version field with the next release version
20
+ pkg.version = nextRelease.version;
21
+
22
+ // Write the updated JSON back to the file
23
+ fs.writeFileSync(file, JSON.stringify(pkg, null, 2) + '\n');
24
+
25
+ // Log success message
28
26
  logger.log(`Synced installer package.json to version ${nextRelease.version}`);
29
27
  }
30
28
 
31
- module.exports = { prepare };
29
+ // Export the prepare function so semantic-release can use it
30
+ module.exports = { prepare };