fix: improve code in the installer to be more memory efficient
This commit is contained in:
227
tools/installer/lib/ide-base-setup.js
Normal file
227
tools/installer/lib/ide-base-setup.js
Normal file
@@ -0,0 +1,227 @@
|
||||
/**
|
||||
* Base IDE Setup - Common functionality for all IDE setups
|
||||
* Reduces duplication and provides shared methods
|
||||
*/
|
||||
|
||||
const path = require("path");
|
||||
const fs = require("fs-extra");
|
||||
const yaml = require("js-yaml");
|
||||
const chalk = require("chalk");
|
||||
const fileManager = require("./file-manager");
|
||||
const resourceLocator = require("./resource-locator");
|
||||
const { extractYamlFromAgent } = require("../../lib/yaml-utils");
|
||||
|
||||
class BaseIdeSetup {
|
||||
constructor() {
|
||||
this._agentCache = new Map();
|
||||
this._pathCache = new Map();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all agent IDs with caching
|
||||
*/
|
||||
async getAllAgentIds(installDir) {
|
||||
const cacheKey = `all-agents:${installDir}`;
|
||||
if (this._agentCache.has(cacheKey)) {
|
||||
return this._agentCache.get(cacheKey);
|
||||
}
|
||||
|
||||
const allAgents = new Set();
|
||||
|
||||
// Get core agents
|
||||
const coreAgents = await this.getCoreAgentIds(installDir);
|
||||
coreAgents.forEach(id => allAgents.add(id));
|
||||
|
||||
// Get expansion pack agents
|
||||
const expansionPacks = await this.getInstalledExpansionPacks(installDir);
|
||||
for (const pack of expansionPacks) {
|
||||
const packAgents = await this.getExpansionPackAgents(pack.path);
|
||||
packAgents.forEach(id => allAgents.add(id));
|
||||
}
|
||||
|
||||
const result = Array.from(allAgents);
|
||||
this._agentCache.set(cacheKey, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get core agent IDs
|
||||
*/
|
||||
async getCoreAgentIds(installDir) {
|
||||
const coreAgents = [];
|
||||
const corePaths = [
|
||||
path.join(installDir, ".bmad-core", "agents"),
|
||||
path.join(installDir, "bmad-core", "agents")
|
||||
];
|
||||
|
||||
for (const agentsDir of corePaths) {
|
||||
if (await fileManager.pathExists(agentsDir)) {
|
||||
const files = await resourceLocator.findFiles("*.md", { cwd: agentsDir });
|
||||
coreAgents.push(...files.map(file => path.basename(file, ".md")));
|
||||
break; // Use first found
|
||||
}
|
||||
}
|
||||
|
||||
return coreAgents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find agent path with caching
|
||||
*/
|
||||
async findAgentPath(agentId, installDir) {
|
||||
const cacheKey = `agent-path:${agentId}:${installDir}`;
|
||||
if (this._pathCache.has(cacheKey)) {
|
||||
return this._pathCache.get(cacheKey);
|
||||
}
|
||||
|
||||
// Use resource locator for efficient path finding
|
||||
let agentPath = await resourceLocator.getAgentPath(agentId);
|
||||
|
||||
if (!agentPath) {
|
||||
// Check installation-specific paths
|
||||
const possiblePaths = [
|
||||
path.join(installDir, ".bmad-core", "agents", `${agentId}.md`),
|
||||
path.join(installDir, "bmad-core", "agents", `${agentId}.md`),
|
||||
path.join(installDir, "common", "agents", `${agentId}.md`)
|
||||
];
|
||||
|
||||
for (const testPath of possiblePaths) {
|
||||
if (await fileManager.pathExists(testPath)) {
|
||||
agentPath = testPath;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (agentPath) {
|
||||
this._pathCache.set(cacheKey, agentPath);
|
||||
}
|
||||
return agentPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get agent title from metadata
|
||||
*/
|
||||
async getAgentTitle(agentId, installDir) {
|
||||
const agentPath = await this.findAgentPath(agentId, installDir);
|
||||
if (!agentPath) return agentId;
|
||||
|
||||
try {
|
||||
const content = await fileManager.readFile(agentPath);
|
||||
const yamlContent = extractYamlFromAgent(content);
|
||||
if (yamlContent) {
|
||||
const metadata = yaml.load(yamlContent);
|
||||
return metadata.agent_name || agentId;
|
||||
}
|
||||
} catch (error) {
|
||||
// Fallback to agent ID
|
||||
}
|
||||
return agentId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get installed expansion packs
|
||||
*/
|
||||
async getInstalledExpansionPacks(installDir) {
|
||||
const cacheKey = `expansion-packs:${installDir}`;
|
||||
if (this._pathCache.has(cacheKey)) {
|
||||
return this._pathCache.get(cacheKey);
|
||||
}
|
||||
|
||||
const expansionPacks = [];
|
||||
|
||||
// Check for dot-prefixed expansion packs
|
||||
const dotExpansions = await resourceLocator.findFiles(".bmad-*", { cwd: installDir });
|
||||
|
||||
for (const dotExpansion of dotExpansions) {
|
||||
if (dotExpansion !== ".bmad-core") {
|
||||
const packPath = path.join(installDir, dotExpansion);
|
||||
const packName = dotExpansion.substring(1); // remove the dot
|
||||
expansionPacks.push({
|
||||
name: packName,
|
||||
path: packPath
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Check other dot folders that have config.yaml
|
||||
const allDotFolders = await resourceLocator.findFiles(".*", { cwd: installDir });
|
||||
for (const folder of allDotFolders) {
|
||||
if (!folder.startsWith(".bmad-") && folder !== ".bmad-core") {
|
||||
const packPath = path.join(installDir, folder);
|
||||
const configPath = path.join(packPath, "config.yaml");
|
||||
if (await fileManager.pathExists(configPath)) {
|
||||
expansionPacks.push({
|
||||
name: folder.substring(1), // remove the dot
|
||||
path: packPath
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._pathCache.set(cacheKey, expansionPacks);
|
||||
return expansionPacks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get expansion pack agents
|
||||
*/
|
||||
async getExpansionPackAgents(packPath) {
|
||||
const agentsDir = path.join(packPath, "agents");
|
||||
if (!(await fileManager.pathExists(agentsDir))) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const agentFiles = await resourceLocator.findFiles("*.md", { cwd: agentsDir });
|
||||
return agentFiles.map(file => path.basename(file, ".md"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create agent rule content (shared logic)
|
||||
*/
|
||||
async createAgentRuleContent(agentId, agentPath, installDir, format = 'mdc') {
|
||||
const agentContent = await fileManager.readFile(agentPath);
|
||||
const agentTitle = await this.getAgentTitle(agentId, installDir);
|
||||
const yamlContent = extractYamlFromAgent(agentContent);
|
||||
|
||||
let content = "";
|
||||
|
||||
if (format === 'mdc') {
|
||||
// MDC format for Cursor
|
||||
content = "---\n";
|
||||
content += "description: \n";
|
||||
content += "globs: []\n";
|
||||
content += "alwaysApply: false\n";
|
||||
content += "---\n\n";
|
||||
content += `# ${agentId.toUpperCase()} Agent Rule\n\n`;
|
||||
content += `This rule is triggered when the user types \`@${agentId}\` and activates the ${agentTitle} agent persona.\n\n`;
|
||||
content += "## Agent Activation\n\n";
|
||||
content += "CRITICAL: Read the full YAML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode:\n\n";
|
||||
content += "```yaml\n";
|
||||
content += yamlContent || agentContent.replace(/^#.*$/m, "").trim();
|
||||
content += "\n```\n\n";
|
||||
content += "## File Reference\n\n";
|
||||
const relativePath = path.relative(installDir, agentPath).replace(/\\/g, '/');
|
||||
content += `The complete agent definition is available in [${relativePath}](mdc:${relativePath}).\n\n`;
|
||||
content += "## Usage\n\n";
|
||||
content += `When the user types \`@${agentId}\`, activate this ${agentTitle} persona and follow all instructions defined in the YAML configuration above.\n`;
|
||||
} else if (format === 'claude') {
|
||||
// Claude Code format
|
||||
content = `# /${agentId} Command\n\n`;
|
||||
content += `When this command is used, adopt the following agent persona:\n\n`;
|
||||
content += agentContent;
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all caches
|
||||
*/
|
||||
clearCache() {
|
||||
this._agentCache.clear();
|
||||
this._pathCache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BaseIdeSetup;
|
||||
Reference in New Issue
Block a user