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.
This commit is contained in:
Brian Madison 2026-04-09 09:01:28 -05:00
parent a7f469690a
commit 7302f350b5
1 changed files with 26 additions and 1 deletions

View File

@ -291,6 +291,8 @@ class CustomModuleManager {
// Search through all custom repo caches // Search through all custom repo caches
try { try {
const { PluginResolver } = require('./plugin-resolver');
const resolver = new PluginResolver();
const owners = await fs.readdir(cacheDir, { withFileTypes: true }); const owners = await fs.readdir(cacheDir, { withFileTypes: true });
for (const ownerEntry of owners) { for (const ownerEntry of owners) {
if (!ownerEntry.isDirectory()) continue; if (!ownerEntry.isDirectory()) continue;
@ -306,14 +308,37 @@ class CustomModuleManager {
try { try {
const data = JSON.parse(await fs.readFile(marketplacePath, 'utf8')); const data = JSON.parse(await fs.readFile(marketplacePath, 'utf8'));
for (const plugin of data.plugins || []) { for (const plugin of data.plugins || []) {
// Direct name match (legacy behavior)
if (plugin.name === moduleCode) { if (plugin.name === moduleCode) {
// Found the module - find its source
const sourcePath = plugin.source ? path.join(repoPath, plugin.source) : repoPath; const sourcePath = plugin.source ? path.join(repoPath, plugin.source) : repoPath;
const moduleYaml = path.join(sourcePath, 'module.yaml'); const moduleYaml = path.join(sourcePath, 'module.yaml');
if (await fs.pathExists(moduleYaml)) { if (await fs.pathExists(moduleYaml)) {
return sourcePath; 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 { } catch {
// Skip malformed marketplace.json // Skip malformed marketplace.json