fix(coding-agent): resource collision precedence, user/project skills override package skills
Sort resolved resources by precedence rank (project settings > project auto > user settings > user auto > package) so first-wins collision logic produces correct results. Also reorder CLI paths to prepend instead of append. fixes #2781
This commit is contained in:
@@ -118,6 +118,24 @@ interface ResourceAccumulator {
|
||||
themes: Map<string, { metadata: PathMetadata; enabled: boolean }>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute a numeric precedence rank for a resource based on its metadata.
|
||||
* Lower rank = higher precedence. Used to sort resolved resources so that
|
||||
* name-collision resolution ("first wins") produces the correct outcome.
|
||||
*
|
||||
* Precedence (highest to lowest):
|
||||
* 0 project + settings entry (source: "local", scope: "project")
|
||||
* 1 project + auto-discovered (source: "auto", scope: "project")
|
||||
* 2 user + settings entry (source: "local", scope: "user")
|
||||
* 3 user + auto-discovered (source: "auto", scope: "user")
|
||||
* 4 package resource (origin: "package")
|
||||
*/
|
||||
function resourcePrecedenceRank(m: PathMetadata): number {
|
||||
if (m.origin === "package") return 4;
|
||||
const scopeBase = m.scope === "project" ? 0 : 2;
|
||||
return scopeBase + (m.source === "local" ? 0 : 1);
|
||||
}
|
||||
|
||||
interface PackageFilter {
|
||||
extensions?: string[];
|
||||
skills?: string[];
|
||||
@@ -2146,11 +2164,13 @@ export class DefaultPackageManager implements PackageManager {
|
||||
|
||||
private toResolvedPaths(accumulator: ResourceAccumulator): ResolvedPaths {
|
||||
const toResolved = (entries: Map<string, { metadata: PathMetadata; enabled: boolean }>): ResolvedResource[] => {
|
||||
return Array.from(entries.entries()).map(([path, { metadata, enabled }]) => ({
|
||||
const resolved = Array.from(entries.entries()).map(([path, { metadata, enabled }]) => ({
|
||||
path,
|
||||
enabled,
|
||||
metadata,
|
||||
}));
|
||||
resolved.sort((a, b) => resourcePrecedenceRank(a.metadata) - resourcePrecedenceRank(b.metadata));
|
||||
return resolved;
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@@ -413,7 +413,7 @@ export class DefaultResourceLoader implements ResourceLoader {
|
||||
|
||||
const skillPaths = this.noSkills
|
||||
? this.mergePaths(cliEnabledSkills, this.additionalSkillPaths)
|
||||
: this.mergePaths([...enabledSkills, ...cliEnabledSkills], this.additionalSkillPaths);
|
||||
: this.mergePaths([...cliEnabledSkills, ...enabledSkills], this.additionalSkillPaths);
|
||||
|
||||
this.lastSkillPaths = skillPaths;
|
||||
this.updateSkillsFromPaths(skillPaths, metadataByPath);
|
||||
@@ -425,7 +425,7 @@ export class DefaultResourceLoader implements ResourceLoader {
|
||||
|
||||
const promptPaths = this.noPromptTemplates
|
||||
? this.mergePaths(cliEnabledPrompts, this.additionalPromptTemplatePaths)
|
||||
: this.mergePaths([...enabledPrompts, ...cliEnabledPrompts], this.additionalPromptTemplatePaths);
|
||||
: this.mergePaths([...cliEnabledPrompts, ...enabledPrompts], this.additionalPromptTemplatePaths);
|
||||
|
||||
this.lastPromptPaths = promptPaths;
|
||||
this.updatePromptsFromPaths(promptPaths, metadataByPath);
|
||||
@@ -437,7 +437,7 @@ export class DefaultResourceLoader implements ResourceLoader {
|
||||
|
||||
const themePaths = this.noThemes
|
||||
? this.mergePaths(cliEnabledThemes, this.additionalThemePaths)
|
||||
: this.mergePaths([...enabledThemes, ...cliEnabledThemes], this.additionalThemePaths);
|
||||
: this.mergePaths([...cliEnabledThemes, ...enabledThemes], this.additionalThemePaths);
|
||||
|
||||
this.lastThemePaths = themePaths;
|
||||
this.updateThemesFromPaths(themePaths, metadataByPath);
|
||||
|
||||
Reference in New Issue
Block a user