Compare commits

...

3 Commits

Author SHA1 Message Date
Jérôme Revillard d4125b869d
Merge d6cc60c2db into 724867d48d 2026-05-13 06:50:15 +00:00
Jerome Revillard d6cc60c2db fix: resolve default branch explicitly when updating shallow-cloned custom modules
With shallow clones (--depth 1), `origin/HEAD` becomes stale after the
initial clone. The update path used `git reset --hard origin/HEAD` which
never picked up new commits pushed to the default branch.

Resolve the default branch name via `git symbolic-ref refs/remotes/origin/HEAD`,
then fetch and reset against `origin/<branch>` explicitly. Falls back to
`main` if origin/HEAD is not set.
2026-05-13 08:45:44 +02:00
Brian 724867d48d
fix(installer): descriptive error when module definition missing after clone (#2377)
* fix(installer): throw descriptive error when module definition missing after clone

When a stable tag predates a module restructure (e.g. baut v1.14.0 had
payload/source dirs, but the registry pointed to skills/module.yaml which
only exists on main), findExternalModuleSource silently returned the
configured but non-existent path. This caused a confusing ENOENT inside
getFileList/copyModuleWithFiltering rather than a clear error.

Now throws with the version that was cloned and a --next hint when the
install channel was stable, so users know exactly how to recover.

Closes #2372

* style: fix prettier formatting in external-manager.js

* style: apply prettier formatting
2026-05-12 23:44:11 -05:00
2 changed files with 35 additions and 3 deletions

View File

@ -424,7 +424,27 @@ class CustomModuleManager {
stdio: ['ignore', 'pipe', 'pipe'],
});
} else {
execSync('git reset --hard origin/HEAD', {
// Resolve the default branch (origin/HEAD) and fetch it explicitly.
// With shallow clones, `origin/HEAD` is stale and `git reset --hard
// origin/HEAD` never picks up new commits on the default branch.
let defaultBranch = 'main';
try {
defaultBranch = execSync('git symbolic-ref refs/remotes/origin/HEAD --short', {
cwd: repoCacheDir,
stdio: 'pipe',
})
.toString()
.trim()
.replace('origin/', '');
} catch {
// Fallback if origin/HEAD is not set
}
execSync(`git fetch --depth 1 origin ${quoteCustomRef(defaultBranch)}`, {
cwd: repoCacheDir,
stdio: ['ignore', 'pipe', 'pipe'],
env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
});
execSync(`git reset --hard origin/${quoteCustomRef(defaultBranch)}`, {
cwd: repoCacheDir,
stdio: ['ignore', 'pipe', 'pipe'],
});

View File

@ -524,8 +524,20 @@ class ExternalModuleManager {
return path.dirname(rootCandidate);
}
// Nothing found: return configured path (preserves old behavior for error messaging)
return path.dirname(configuredPath);
// Nothing found: the cloned ref does not contain a recognizable module structure.
// This happens when a stable tag predates a module restructure (e.g. the repo
// moved files from payload/ to skills/ after the tag was cut). Returning a
// non-existent path silently causes a confusing ENOENT deep inside copyModuleWithFiltering;
// throw a descriptive error here instead so the user knows what happened and how to recover.
const resolution = ExternalModuleManager._resolutions.get(moduleCode);
const versionHint = resolution?.version ? `version ${resolution.version}` : 'the cloned version';
const channelHint =
resolution?.channel === 'stable' ? ` Try reinstalling with \`--next=${moduleCode}\` to use the latest main branch instead.` : '';
throw new Error(
`Module '${moduleCode}' was downloaded but its module definition was not found. ` +
`Expected '${moduleDefinitionPath}' to exist in ${versionHint}, but it is missing. ` +
`The repository may have been restructured after this release was tagged.${channelHint}`,
);
}
cachedModules = null;
}