UNPKG

@cloudkinetix/bmad-enhanced

Version:

Cloud-Kinetix enhanced fork of BMAD-METHOD - Breakthrough Method of Agile AI-driven Development with robust versioning and unified validation.

343 lines (281 loc) 10.9 kB
const fs = require("fs-extra"); const path = require("path"); const yaml = require("js-yaml"); class ConfigLoader { constructor() { this.configPath = path.join(__dirname, "..", "config", "install.config.yaml"); this.ckDefaultsPath = path.join(__dirname, "..", "config", "ck-defaults.yaml"); this.config = null; this.ckDefaults = null; } async load() { if (this.config) return this.config; try { const configContent = await fs.readFile(this.configPath, "utf8"); this.config = yaml.load(configContent); return this.config; } catch (error) { throw new Error(`Failed to load configuration: ${error.message}`); } } async getInstallationOptions() { const config = await this.load(); return config["installation-options"] || {}; } async getAvailableAgents() { const config = await this.load(); return config["available-agents"] || []; } async getAvailableExpansionPacks() { const expansionPacksDir = this.getExpansionPacksPath(); try { const entries = await fs.readdir(expansionPacksDir, { withFileTypes: true }); const expansionPacks = []; for (const entry of entries) { if (entry.isDirectory()) { const manifestPath = path.join(expansionPacksDir, entry.name, "manifest.yaml"); try { const manifestContent = await fs.readFile(manifestPath, "utf8"); const manifest = yaml.load(manifestContent); expansionPacks.push({ id: entry.name, name: manifest.name || entry.name, description: manifest.description || "No description available", version: manifest.version || "1.0.0", author: manifest.author || "Unknown", manifestPath: manifestPath, dependencies: manifest.dependencies || [], }); } catch (error) { // Skip expansion packs without manifests (they might be incomplete) if (process.env.VERBOSE) { console.warn( `Skipping expansion pack ${entry.name}: ${error.message}` ); } } } } return expansionPacks; } catch (error) { console.warn(`Failed to read expansion packs directory: ${error.message}`); return []; } } async getAgentDependencies(agentId) { // Use DependencyResolver to dynamically parse agent dependencies const DependencyResolver = require("../lib/dependency-resolver"); const resolver = new DependencyResolver(this.getBmadCorePath()); try { const agentDeps = await resolver.resolveAgentDependencies(agentId); // Convert to flat list of file paths const depPaths = []; // Core files and utilities are included automatically by DependencyResolver // Add agent file itself is already handled by installer // Add all resolved resources for (const resource of agentDeps.resources) { const filePath = `.bmad-core/${resource.type}/${resource.id}.md`; if (!depPaths.includes(filePath)) { depPaths.push(filePath); } } return depPaths; } catch (error) { console.warn(`Failed to dynamically resolve dependencies for ${agentId}: ${error.message}`); // Fall back to static config const config = await this.load(); const dependencies = config["agent-dependencies"] || {}; const coreFiles = dependencies["core-files"] || []; const agentDeps = dependencies[agentId] || []; return [...coreFiles, ...agentDeps]; } } async getIdeConfiguration(ide) { const config = await this.load(); const ideConfigs = config["ide-configurations"] || {}; return ideConfigs[ide] || null; } async getAvailableIDEs() { const config = await this.load(); const ideConfigs = config["ide-configurations"] || {}; return Object.keys(ideConfigs); } getBmadCorePath() { // Get the path to bmad-core relative to the installer // Now that lib is in ck-layer/lib, we need to go up one more level const devPath = path.join(__dirname, "..", "..", "..", "bmad-core"); const npmPath = path.join(__dirname, "..", "..", "bmad-core"); // Check if we're in development environment const fs = require("fs"); if (fs.existsSync(devPath)) { return devPath; } return npmPath; } getExpansionPacksPath() { // Get the path to expansion-packs relative to the installer // Now that lib is in ck-layer/lib, we need to go up one more level const devPath = path.join(__dirname, "..", "..", "..", "expansion-packs"); const npmPath = path.join(__dirname, "..", "..", "expansion-packs"); // Check if we're in development environment const fs = require("fs"); if (fs.existsSync(devPath)) { return devPath; } return npmPath; } getDistPath() { // Get the path to dist directory relative to the installer // Now that lib is in ck-layer/lib, we need to go up appropriate levels return path.join(__dirname, "..", "..", "..", "dist"); } getAgentPath(agentId) { return path.join(this.getBmadCorePath(), "agents", `${agentId}.md`); } async getAvailableTeams() { const teamsDir = path.join(this.getBmadCorePath(), "agent-teams"); try { const entries = await fs.readdir(teamsDir, { withFileTypes: true }); const teams = []; for (const entry of entries) { if (entry.isFile() && entry.name.startsWith("team-") && entry.name.endsWith(".yaml")) { const teamPath = path.join(teamsDir, entry.name); try { const teamContent = await fs.readFile(teamPath, "utf8"); const teamConfig = yaml.load(teamContent); if (teamConfig.bundle) { teams.push({ id: path.basename(entry.name, ".yaml").replace("team-", ""), name: teamConfig.bundle.name || entry.name, description: teamConfig.bundle.description || "Team configuration", icon: teamConfig.bundle.icon || "📋", }); } } catch (error) { console.warn(`Warning: Could not load team config ${entry.name}: ${error.message}`); } } } return teams; } catch (error) { console.warn(`Warning: Could not scan teams directory: ${error.message}`); return []; } } getTeamPath(teamId) { return path.join(this.getBmadCorePath(), "agent-teams", `team-${teamId}.yaml`); } async getTeamDependencies(teamId) { // Use DependencyResolver to dynamically parse team dependencies const DependencyResolver = require("../lib/dependency-resolver"); const resolver = new DependencyResolver(this.getBmadCorePath()); try { const teamDeps = await resolver.resolveTeamDependencies(teamId); // Convert to flat list of file paths const depPaths = []; // Add team config file - resolver already handles the team- prefix const teamFileName = teamId.startsWith("team-") ? teamId : `team-${teamId}`; depPaths.push(path.join(".bmad-core", "agent-teams", `${teamFileName}.yaml`)); // Add all agents for (const agent of teamDeps.agents) { const filePath = path.join(".bmad-core", "agents", `${agent.id}.md`); if (!depPaths.includes(filePath)) { depPaths.push(filePath); } } // Add all resolved resources for (const resource of teamDeps.resources) { const filePath = path.join( ".bmad-core", resource.type, `${resource.id}.${resource.type === "workflows" ? "yml" : "md"}` ); if (!depPaths.includes(filePath)) { depPaths.push(filePath); } } return depPaths; } catch (error) { throw new Error(`Failed to resolve team dependencies for ${teamId}: ${error.message}`); } } async loadCKDefaults() { if (this.ckDefaults) return this.ckDefaults; try { const defaultsContent = await fs.readFile(this.ckDefaultsPath, "utf8"); this.ckDefaults = yaml.load(defaultsContent); return this.ckDefaults; } catch (error) { // If defaults file doesn't exist, return sensible defaults console.warn(`Could not load CK defaults, using built-in defaults: ${error.message}`); this.ckDefaults = { "ides": [], // Empty = install all "expansion-packs": [], // Empty = install all "excluded-ides": [] // None excluded by default }; return this.ckDefaults; } } async getDefaultIDEs() { const defaults = await this.loadCKDefaults(); // New format with default-settings if (defaults["default-settings"]) { return defaults["default-settings"]["ides"] || []; } // Old format for backward compatibility return defaults["ides"] || []; } async getDefaultExpansionPacks() { const defaults = await this.loadCKDefaults(); // New format with default-settings if (defaults["default-settings"]) { return defaults["default-settings"]["expansion-packs"] || []; } // Old format for backward compatibility return defaults["expansion-packs"] || []; } async getExcludedIDEs() { const defaults = await this.loadCKDefaults(); // New format with default-settings if (defaults["default-settings"]) { return defaults["default-settings"]["excluded-ides"] || []; } // Old format for backward compatibility return defaults["excluded-ides"] || []; } async getSmartFeatureDetection() { // Smart feature detection based on expansion pack keywords return { 'jira': ['jira-sync'], 'gitlab': ['gitlab-integration'], 'cicd': ['cicd-automation'], 'ai-agent': ['ai-agent-tools'], 'parallel': ['parallel-development'] }; } async getInstallationProfile(profileName) { const defaults = await this.loadCKDefaults(); // Check if profiles section exists if (defaults.profiles && defaults.profiles[profileName]) { const profile = defaults.profiles[profileName]; // Normalize field names to match expected format return { name: profile.name, description: profile.description, ides: profile.ides, expansionPacks: profile["expansion-packs"] || profile.expansionPacks || [], excludedIdes: profile["excluded-ides"] || profile.excludedIdes || [] }; } return null; } async getAvailableProfiles() { const defaults = await this.loadCKDefaults(); return defaults.profiles ? Object.keys(defaults.profiles) : []; } async getDefaultProfileName() { const defaults = await this.loadCKDefaults(); return defaults.metadata?.["default-profile"] || null; } } module.exports = new ConfigLoader();