BMAD-METHOD/tools/cli/installers/lib/modules/external-manager.js

134 lines
3.9 KiB
JavaScript

const fs = require('fs-extra');
const path = require('node:path');
const yaml = require('yaml');
/**
* Manages external official modules defined in external-official-modules.yaml
* These are modules hosted in external repositories that can be installed
*
* @class ExternalModuleManager
*/
class ExternalModuleManager {
constructor() {
this.externalModulesConfigPath = path.join(__dirname, '../../../external-official-modules.yaml');
this.cachedModules = null;
}
/**
* Load and parse the external-official-modules.yaml file
* @returns {Object} Parsed YAML content with modules object
*/
async loadExternalModulesConfig() {
if (this.cachedModules) {
return this.cachedModules;
}
try {
const content = await fs.readFile(this.externalModulesConfigPath, 'utf8');
const config = yaml.parse(content);
this.cachedModules = config;
return config;
} catch (error) {
console.warn(`Failed to load external modules config: ${error.message}`);
return { modules: {} };
}
}
/**
* Get list of available external modules
* @returns {Array<Object>} Array of module info objects
*/
async listAvailable() {
const config = await this.loadExternalModulesConfig();
const modules = [];
for (const [key, moduleConfig] of Object.entries(config.modules || {})) {
modules.push({
key,
url: moduleConfig.url,
moduleDefinition: moduleConfig['module-definition'],
code: moduleConfig.code,
name: moduleConfig.name,
header: moduleConfig.header,
subheader: moduleConfig.subheader,
description: moduleConfig.description || '',
defaultSelected: moduleConfig.defaultSelected === true,
type: moduleConfig.type || 'community', // bmad-org or community
isExternal: true,
});
}
return modules;
}
/**
* Get module info by code
* @param {string} code - The module code (e.g., 'cis')
* @returns {Object|null} Module info or null if not found
*/
async getModuleByCode(code) {
const modules = await this.listAvailable();
return modules.find((m) => m.code === code) || null;
}
/**
* Get module info by key
* @param {string} key - The module key (e.g., 'bmad-creative-intelligence-suite')
* @returns {Object|null} Module info or null if not found
*/
async getModuleByKey(key) {
const config = await this.loadExternalModulesConfig();
const moduleConfig = config.modules?.[key];
if (!moduleConfig) {
return null;
}
return {
key,
url: moduleConfig.url,
moduleDefinition: moduleConfig['module-definition'],
code: moduleConfig.code,
name: moduleConfig.name,
header: moduleConfig.header,
subheader: moduleConfig.subheader,
description: moduleConfig.description || '',
defaultSelected: moduleConfig.defaultSelected === true,
type: moduleConfig.type || 'community', // bmad-org or community
isExternal: true,
};
}
/**
* Check if a module code exists in external modules
* @param {string} code - The module code to check
* @returns {boolean} True if the module exists
*/
async hasModule(code) {
const module = await this.getModuleByCode(code);
return module !== null;
}
/**
* Get the URL for a module by code
* @param {string} code - The module code
* @returns {string|null} The URL or null if not found
*/
async getModuleUrl(code) {
const module = await this.getModuleByCode(code);
return module ? module.url : null;
}
/**
* Get the module definition path for a module by code
* @param {string} code - The module code
* @returns {string|null} The module definition path or null if not found
*/
async getModuleDefinition(code) {
const module = await this.getModuleByCode(code);
return module ? module.moduleDefinition : null;
}
}
module.exports = { ExternalModuleManager };