74
* not as directory entries).
75
*/
77
>
workingDirectory: URI | undefined,
78
>
userHome: URI,
79
>
fileService: IFileService,
80
>
): Promise<readonly (IParsedAgent | IParsedSkill)[]> {
81
>
// Project scope first so it wins precedence over user scope.
82
>
const scopes = workingDirectory ? [workingDirectory, userHome] : [userHome];
83
>
const agents = new Map<string, IParsedAgent>();
84
>
const skills = new Map<string, IParsedSkill>();
85
>
86
>
for (const scope of scopes) {
87
>
const { agents: agentsDir, skills: skillsDir, commands: commandsDir } = scopeRoots(scope);
88
>
const [agentRes, skillRes, commandRes] = await Promise.all([
89
>
readAgentComponents([agentsDir], fileService),
90
>
// pluginRoot = the skills dir itself, so the readSkills fallback
91
>
// targets `<skillsDir>/SKILL.md` (a legit single-skill dir), never
92
>
// an unrelated `<userHome>/SKILL.md`.
93
>
readSkills(skillsDir, [skillsDir], fileService),
94
>
readAgentComponents([commandsDir], fileService),
95
>
]);
96
>
collectByName(agents, agentRes.map(toParsedAgent));
97
>
// Skills before commands so a same-named skill wins (spec section 3).
98
>
// Drop `@skills-dir` plugin dirs first — they surface as plugins (PB-8).
99
>
const standaloneSkills = await excludeNativePluginSkills(skillRes, fileService);
100
>
collectByName(skills, standaloneSkills.map(toParsedSkill));
101
>
collectByName(skills, commandRes.map(toParsedSkill));
102
>
}
103
>
104
>
return [...agents.values(), ...skills.values()];
105
>
}