From 7302f350b566f8d17393eb688edd7a591aa636ad Mon Sep 17 00:00:00 2001 From: Brian Madison Date: Thu, 9 Apr 2026 09:01:28 -0500 Subject: [PATCH] fix(installer): resolve custom modules from disk cache on quick update When the resolution cache is empty (fresh CLI process, e.g. quick update), findModuleSourceByCode only matched plugin.name against the module code. This failed for modules like "sam" and "dw" where the code comes from module.yaml inside a setup/standalone skill, not from the plugin name in marketplace.json. Now runs the PluginResolver on cached repos when the direct name match fails, finding the correct module source and re-populating the cache for the install pipeline. --- .../modules/custom-module-manager.js | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/tools/installer/modules/custom-module-manager.js b/tools/installer/modules/custom-module-manager.js index 84b250931..a23505bf1 100644 --- a/tools/installer/modules/custom-module-manager.js +++ b/tools/installer/modules/custom-module-manager.js @@ -291,6 +291,8 @@ class CustomModuleManager { // Search through all custom repo caches try { + const { PluginResolver } = require('./plugin-resolver'); + const resolver = new PluginResolver(); const owners = await fs.readdir(cacheDir, { withFileTypes: true }); for (const ownerEntry of owners) { if (!ownerEntry.isDirectory()) continue; @@ -306,14 +308,37 @@ class CustomModuleManager { try { const data = JSON.parse(await fs.readFile(marketplacePath, 'utf8')); for (const plugin of data.plugins || []) { + // Direct name match (legacy behavior) if (plugin.name === moduleCode) { - // Found the module - find its source const sourcePath = plugin.source ? path.join(repoPath, plugin.source) : repoPath; const moduleYaml = path.join(sourcePath, 'module.yaml'); if (await fs.pathExists(moduleYaml)) { return sourcePath; } } + + // Resolve plugin to check if any module.yaml code matches + if (plugin.skills && plugin.skills.length > 0) { + try { + const resolved = await resolver.resolve(repoPath, plugin); + for (const mod of resolved) { + if (mod.code === moduleCode) { + // Derive repo URL from cache path for manifest tracking + const repoUrl = `https://github.com/${ownerEntry.name}/${repoEntry.name}`; + mod.repoUrl = repoUrl; + CustomModuleManager._resolutionCache.set(mod.code, mod); + if (mod.moduleYamlPath) { + return path.dirname(mod.moduleYamlPath); + } + if (mod.skillPaths && mod.skillPaths.length > 0) { + return path.dirname(mod.skillPaths[0]); + } + } + } + } catch { + // Skip unresolvable plugins + } + } } } catch { // Skip malformed marketplace.json