mirror of
https://github.com/bmadcode/BMAD-METHOD.git
synced 2025-12-29 16:14:59 +00:00
custom modules install after any non custom modules selected and after the core, manifest tracks custom modules separately to ensure always installed from the custom cache
This commit is contained in:
@@ -144,12 +144,18 @@ class CustomModuleCache {
|
||||
const sourceHash = await this.calculateHash(sourcePath);
|
||||
const cacheHash = await this.calculateHash(cacheDir);
|
||||
|
||||
// Update manifest - don't store originalPath for source control friendliness
|
||||
// Update manifest - don't store absolute paths for portability
|
||||
// Clean metadata to remove absolute paths
|
||||
const cleanMetadata = { ...metadata };
|
||||
if (cleanMetadata.sourcePath) {
|
||||
delete cleanMetadata.sourcePath;
|
||||
}
|
||||
|
||||
cacheManifest[moduleId] = {
|
||||
originalHash: sourceHash,
|
||||
cacheHash: cacheHash,
|
||||
cachedAt: new Date().toISOString(),
|
||||
...metadata,
|
||||
...cleanMetadata,
|
||||
};
|
||||
|
||||
await this.updateCacheManifest(cacheManifest);
|
||||
|
||||
@@ -417,12 +417,13 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
||||
|
||||
// Collect configurations for modules (skip if quick update already collected them)
|
||||
let moduleConfigs;
|
||||
let customModulePaths = new Map();
|
||||
|
||||
if (config._quickUpdate) {
|
||||
// Quick update already collected all configs, use them directly
|
||||
moduleConfigs = this.configCollector.collectedConfig;
|
||||
} else {
|
||||
// Build custom module paths map from customContent
|
||||
const customModulePaths = new Map();
|
||||
|
||||
// Handle selectedFiles (from existing install path or manual directory input)
|
||||
if (config.customContent && config.customContent.selected && config.customContent.selectedFiles) {
|
||||
@@ -435,6 +436,13 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
||||
}
|
||||
}
|
||||
|
||||
// Handle new custom content sources from UI
|
||||
if (config.customContent && config.customContent.sources) {
|
||||
for (const source of config.customContent.sources) {
|
||||
customModulePaths.set(source.id, source.path);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle cachedModules (from new install path where modules are cached)
|
||||
// Only include modules that were actually selected for installation
|
||||
if (config.customContent && config.customContent.cachedModules) {
|
||||
@@ -479,6 +487,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
||||
// Set bmad folder name on module manager and IDE manager for placeholder replacement
|
||||
this.moduleManager.setBmadFolderName(bmadFolderName);
|
||||
this.moduleManager.setCoreConfig(moduleConfigs.core || {});
|
||||
this.moduleManager.setCustomModulePaths(customModulePaths);
|
||||
this.ideManager.setBmadFolderName(bmadFolderName);
|
||||
|
||||
// Tool selection will be collected after we determine if it's a reinstall/update/new install
|
||||
@@ -733,6 +742,26 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
||||
spinner.text = 'Creating directory structure...';
|
||||
await this.createDirectoryStructure(bmadDir);
|
||||
|
||||
// Cache custom modules if any
|
||||
if (customModulePaths && customModulePaths.size > 0) {
|
||||
spinner.text = 'Caching custom modules...';
|
||||
const { CustomModuleCache } = require('./custom-module-cache');
|
||||
const customCache = new CustomModuleCache(bmadDir);
|
||||
|
||||
for (const [moduleId, sourcePath] of customModulePaths) {
|
||||
const cachedInfo = await customCache.cacheModule(moduleId, sourcePath, {
|
||||
sourcePath: sourcePath, // Store original path for updates
|
||||
});
|
||||
|
||||
// Update the customModulePaths to use the cached location
|
||||
customModulePaths.set(moduleId, cachedInfo.cachePath);
|
||||
}
|
||||
|
||||
// Update module manager with the cached paths
|
||||
this.moduleManager.setCustomModulePaths(customModulePaths);
|
||||
spinner.succeed('Custom modules cached');
|
||||
}
|
||||
|
||||
// Get project root
|
||||
const projectRoot = getProjectRoot();
|
||||
|
||||
@@ -790,6 +819,20 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
||||
|
||||
const modulesToInstall = allModules;
|
||||
|
||||
// For dependency resolution, we only need regular modules (not custom modules)
|
||||
// Custom modules are already installed in _bmad and don't need dependency resolution from source
|
||||
const regularModulesForResolution = allModules.filter((module) => {
|
||||
// Check if this is a custom module
|
||||
const isCustom =
|
||||
customModulePaths.has(module) ||
|
||||
(finalCustomContent && finalCustomContent.cachedModules && finalCustomContent.cachedModules.some((cm) => cm.id === module)) ||
|
||||
(finalCustomContent &&
|
||||
finalCustomContent.selected &&
|
||||
finalCustomContent.selectedFiles &&
|
||||
finalCustomContent.selectedFiles.some((f) => f.includes(module)));
|
||||
return !isCustom;
|
||||
});
|
||||
|
||||
// For dependency resolution, we need to pass the project root
|
||||
// Create a temporary module manager that knows about custom content locations
|
||||
const tempModuleManager = new ModuleManager({
|
||||
@@ -797,13 +840,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
||||
bmadDir: bmadDir, // Pass bmadDir so we can check cache
|
||||
});
|
||||
|
||||
// Make sure custom modules are discoverable
|
||||
if (config.customContent && config.customContent.selected && config.customContent.selectedFiles) {
|
||||
// The dependency resolver needs to know about these modules
|
||||
// We'll handle custom modules separately in the installation loop
|
||||
}
|
||||
|
||||
const resolution = await this.dependencyResolver.resolve(projectRoot, allModules, {
|
||||
const resolution = await this.dependencyResolver.resolve(projectRoot, regularModulesForResolution, {
|
||||
verbose: config.verbose,
|
||||
moduleManager: tempModuleManager,
|
||||
});
|
||||
@@ -974,7 +1011,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
||||
config._customModulesToTrack.push({
|
||||
id: customInfo.id,
|
||||
name: customInfo.name,
|
||||
sourcePath: sourcePath,
|
||||
sourcePath: useCache ? `_config/custom/${customInfo.id}` : sourcePath,
|
||||
installDate: new Date().toISOString(),
|
||||
});
|
||||
} else {
|
||||
@@ -1116,6 +1153,7 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
||||
const manifestStats = await manifestGen.generateManifests(bmadDir, allModulesForManifest, this.installedFiles, {
|
||||
ides: config.ides || [],
|
||||
preservedModules: modulesForCsvPreserve, // Scan these from installed bmad/ dir
|
||||
customModules: config._customModulesToTrack || [], // Custom modules to exclude from regular modules list
|
||||
});
|
||||
|
||||
// Add custom modules to manifest (now that it exists)
|
||||
@@ -2086,12 +2124,17 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
||||
|
||||
// Compile using the same compiler as initial installation
|
||||
const { compileAgent } = require('../../../lib/agent/compiler');
|
||||
const { xml } = await compileAgent(yamlContent, answers, agentName, path.relative(bmadDir, targetMdPath), {
|
||||
const result = await compileAgent(yamlContent, answers, agentName, path.relative(bmadDir, targetMdPath), {
|
||||
config: coreConfig,
|
||||
});
|
||||
|
||||
// Check if compilation succeeded
|
||||
if (!result || !result.xml) {
|
||||
throw new Error(`Failed to compile agent ${agentName}: No XML returned from compiler`);
|
||||
}
|
||||
|
||||
// Replace _bmad with actual folder name if needed
|
||||
const finalXml = xml.replaceAll('_bmad', path.basename(bmadDir));
|
||||
const finalXml = result.xml.replaceAll('_bmad', path.basename(bmadDir));
|
||||
|
||||
// Write the rebuilt .md file with POSIX-compliant final newline
|
||||
const content = finalXml.endsWith('\n') ? finalXml : finalXml + '\n';
|
||||
|
||||
@@ -34,13 +34,19 @@ class ManifestGenerator {
|
||||
|
||||
// Store modules list (all modules including preserved ones)
|
||||
const preservedModules = options.preservedModules || [];
|
||||
const customModules = options.customModules || [];
|
||||
|
||||
// Scan the bmad directory to find all actually installed modules
|
||||
const installedModules = await this.scanInstalledModules(bmadDir);
|
||||
|
||||
// Deduplicate modules list to prevent duplicates
|
||||
this.modules = [...new Set(['core', ...selectedModules, ...preservedModules, ...installedModules])];
|
||||
this.updatedModules = [...new Set(['core', ...selectedModules, ...installedModules])]; // All installed modules get rescanned
|
||||
// Filter out custom modules from the regular modules list
|
||||
const customModuleIds = new Set(customModules.map((cm) => cm.id));
|
||||
const regularModules = [...new Set(['core', ...selectedModules, ...preservedModules, ...installedModules])].filter(
|
||||
(module) => !customModuleIds.has(module),
|
||||
);
|
||||
|
||||
this.modules = regularModules;
|
||||
this.updatedModules = [...new Set(['core', ...selectedModules, ...installedModules])].filter((module) => !customModuleIds.has(module)); // Also exclude custom modules from rescanning
|
||||
|
||||
// For CSV manifests, we need to include ALL modules that are installed
|
||||
// preservedModules controls which modules stay as-is in the CSV (don't get rescanned)
|
||||
|
||||
@@ -29,6 +29,7 @@ class ModuleManager {
|
||||
this.xmlHandler = new XmlHandler();
|
||||
this.bmadFolderName = 'bmad'; // Default, can be overridden
|
||||
this.scanProjectForModules = options.scanProjectForModules !== false; // Default to true for backward compatibility
|
||||
this.customModulePaths = new Map(); // Initialize custom module paths
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -47,6 +48,14 @@ class ModuleManager {
|
||||
this.coreConfig = coreConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set custom module paths for priority lookup
|
||||
* @param {Map<string, string>} customModulePaths - Map of module ID to source path
|
||||
*/
|
||||
setCustomModulePaths(customModulePaths) {
|
||||
this.customModulePaths = customModulePaths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy a file and replace _bmad placeholder with actual folder name
|
||||
* @param {string} sourcePath - Source file path
|
||||
@@ -340,6 +349,11 @@ class ModuleManager {
|
||||
async findModuleSource(moduleName) {
|
||||
const projectRoot = getProjectRoot();
|
||||
|
||||
// First check custom module paths if they exist
|
||||
if (this.customModulePaths && this.customModulePaths.has(moduleName)) {
|
||||
return this.customModulePaths.get(moduleName);
|
||||
}
|
||||
|
||||
// First, check src/modules
|
||||
const srcModulePath = path.join(this.modulesSourcePath, moduleName);
|
||||
if (await fs.pathExists(srcModulePath)) {
|
||||
|
||||
Reference in New Issue
Block a user