bmad-method 4.30.2 → 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.
- package/CHANGELOG.md +7 -0
- package/README.md +1 -1
- package/bmad-core/core-config.yaml +0 -1
- package/dist/agents/analyst.txt +1 -1
- package/dist/agents/bmad-master.txt +1 -1
- package/dist/agents/bmad-orchestrator.txt +1 -1
- package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-designer.txt +2409 -0
- package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-developer.txt +1480 -0
- package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-sm.txt +826 -0
- package/dist/expansion-packs/bmad-2d-unity-game-dev/teams/unity-2d-game-team.txt +10690 -0
- package/dist/teams/team-all.txt +1 -1
- package/dist/teams/team-fullstack.txt +1 -1
- package/dist/teams/team-ide-minimal.txt +1 -1
- package/dist/teams/team-no-ui.txt +1 -1
- package/docs/bmad-workflow-guide.md +1 -1
- package/expansion-packs/bmad-2d-phaser-game-dev/config.yaml +2 -2
- package/expansion-packs/bmad-2d-unity-game-dev/agent-teams/unity-2d-game-team.yaml +13 -0
- package/expansion-packs/bmad-2d-unity-game-dev/agents/game-designer.md +72 -0
- package/expansion-packs/bmad-2d-unity-game-dev/agents/game-developer.md +78 -0
- package/expansion-packs/{bmad-creator-tools/agents/bmad-the-creator.md → bmad-2d-unity-game-dev/agents/game-sm.md} +26 -28
- package/expansion-packs/bmad-2d-unity-game-dev/checklists/game-design-checklist.md +201 -0
- package/expansion-packs/bmad-2d-unity-game-dev/checklists/game-story-dod-checklist.md +160 -0
- package/expansion-packs/bmad-2d-unity-game-dev/config.yaml +6 -0
- package/expansion-packs/bmad-2d-unity-game-dev/data/bmad-kb.md +251 -0
- package/expansion-packs/bmad-2d-unity-game-dev/data/development-guidelines.md +590 -0
- package/expansion-packs/bmad-2d-unity-game-dev/tasks/advanced-elicitation.md +111 -0
- package/expansion-packs/bmad-2d-unity-game-dev/tasks/create-game-story.md +217 -0
- package/expansion-packs/bmad-2d-unity-game-dev/tasks/game-design-brainstorming.md +308 -0
- package/expansion-packs/bmad-2d-unity-game-dev/templates/game-architecture-tmpl.yaml +545 -0
- package/expansion-packs/bmad-2d-unity-game-dev/templates/game-brief-tmpl.yaml +356 -0
- package/expansion-packs/bmad-2d-unity-game-dev/templates/game-design-doc-tmpl.yaml +343 -0
- package/expansion-packs/bmad-2d-unity-game-dev/templates/game-story-tmpl.yaml +256 -0
- package/expansion-packs/bmad-2d-unity-game-dev/templates/level-design-doc-tmpl.yaml +484 -0
- package/expansion-packs/bmad-2d-unity-game-dev/workflows/game-dev-greenfield.yaml +183 -0
- package/expansion-packs/bmad-2d-unity-game-dev/workflows/game-prototype.yaml +175 -0
- package/expansion-packs/bmad-infrastructure-devops/config.yaml +2 -2
- package/package.json +4 -8
- package/tools/bump-all-versions.js +8 -9
- package/tools/bump-expansion-version.js +40 -35
- package/tools/installer/bin/bmad.js +8 -21
- package/tools/installer/lib/file-manager.js +76 -44
- package/tools/installer/lib/ide-base-setup.js +227 -0
- package/tools/installer/lib/ide-setup.js +8 -58
- package/tools/installer/lib/installer.js +99 -121
- package/tools/installer/lib/memory-profiler.js +224 -0
- package/tools/installer/lib/module-manager.js +110 -0
- package/tools/installer/lib/resource-locator.js +310 -0
- package/tools/installer/package.json +1 -1
- package/tools/semantic-release-sync-installer.js +20 -21
- package/dist/expansion-packs/bmad-creator-tools/agents/bmad-the-creator.txt +0 -2008
- package/expansion-packs/bmad-creator-tools/README.md +0 -8
- package/expansion-packs/bmad-creator-tools/config.yaml +0 -6
- package/expansion-packs/bmad-creator-tools/tasks/create-agent.md +0 -200
- package/expansion-packs/bmad-creator-tools/tasks/generate-expansion-pack.md +0 -1020
- package/expansion-packs/bmad-creator-tools/templates/agent-teams-tmpl.yaml +0 -178
- package/expansion-packs/bmad-creator-tools/templates/agent-tmpl.yaml +0 -154
- package/expansion-packs/bmad-creator-tools/templates/expansion-pack-plan-tmpl.yaml +0 -120
- 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;
|
|
@@ -5,27 +5,26 @@
|
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const path = require('path');
|
|
7
7
|
|
|
8
|
-
function prepare
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
if (!fs.existsSync(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
//
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
//
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
//
|
|
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
|
-
|
|
29
|
+
// Export the prepare function so semantic-release can use it
|
|
30
|
+
module.exports = { prepare };
|