Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 26 additions & 9 deletions src/core/skills.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,26 +98,43 @@ export async function getAllSkillsFromPlugins(
const pluginName = resolved.pluginName ?? getPluginName(pluginPath);
const skillsDir = join(pluginPath, 'skills');

if (!existsSync(skillsDir)) continue;

const entries = await readdir(skillsDir, { withFileTypes: true });
const skillDirs = entries.filter((e) => e.isDirectory());

// Only apply enabledSkills to plugins that actually have entries in the set
const hasEnabledEntries = enabledSkills &&
[...enabledSkills].some((s) => s.startsWith(`${pluginName}:`));

for (const entry of skillDirs) {
const skillKey = `${pluginName}:${entry.name}`;
let skillEntries: { name: string; skillPath: string }[];

if (existsSync(skillsDir)) {
// Standard layout: plugin/skills/<skill-name>/
const entries = await readdir(skillsDir, { withFileTypes: true });
skillEntries = entries
.filter((e) => e.isDirectory())
.map((e) => ({ name: e.name, skillPath: join(skillsDir, e.name) }));
} else {
// Flat layout: plugin/<skill-name>/SKILL.md
const entries = await readdir(pluginPath, { withFileTypes: true });
const flatSkills: { name: string; skillPath: string }[] = [];
for (const entry of entries) {
if (!entry.isDirectory()) continue;
const skillMdPath = join(pluginPath, entry.name, 'SKILL.md');
if (existsSync(skillMdPath)) {
flatSkills.push({ name: entry.name, skillPath: join(pluginPath, entry.name) });
}
}
skillEntries = flatSkills;
}

for (const { name, skillPath } of skillEntries) {
const skillKey = `${pluginName}:${name}`;
const isDisabled = hasEnabledEntries
? !enabledSkills?.has(skillKey)
: disabledSkills.has(skillKey);

skills.push({
name: entry.name,
name,
pluginName,
pluginSource,
path: join(skillsDir, entry.name),
path: skillPath,
disabled: isDisabled,
});
}
Expand Down
39 changes: 27 additions & 12 deletions src/core/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -367,29 +367,44 @@ export async function collectPluginSkills(
): Promise<CollectedSkill[]> {
const skillsDir = join(pluginPath, 'skills');

if (!existsSync(skillsDir)) {
return [];
}

const entries = await readdir(skillsDir, { withFileTypes: true });
const skillDirs = entries.filter((e) => e.isDirectory());

// Filter skills: enabledSkills (allowlist) takes priority over disabledSkills (blocklist)
// Only apply enabledSkills to plugins that actually have entries in the set
const hasEnabledEntries = enabledSkills && pluginName &&
[...enabledSkills].some((s) => s.startsWith(`${pluginName}:`));

let candidateDirs: { name: string; path: string }[];

if (existsSync(skillsDir)) {
// Standard layout: plugin/skills/<skill-name>/
const entries = await readdir(skillsDir, { withFileTypes: true });
candidateDirs = entries
.filter((e) => e.isDirectory())
.map((e) => ({ name: e.name, path: join(skillsDir, e.name) }));
} else {
// Flat layout: plugin/<skill-name>/SKILL.md
const entries = await readdir(pluginPath, { withFileTypes: true });
const flatDirs: { name: string; path: string }[] = [];
for (const entry of entries) {
if (!entry.isDirectory()) continue;
const skillMdPath = join(pluginPath, entry.name, 'SKILL.md');
if (existsSync(skillMdPath)) {
flatDirs.push({ name: entry.name, path: join(pluginPath, entry.name) });
}
}
candidateDirs = flatDirs;
}

const filteredDirs = pluginName
? hasEnabledEntries
? skillDirs.filter((e) => enabledSkills?.has(`${pluginName}:${e.name}`))
? candidateDirs.filter((e) => enabledSkills?.has(`${pluginName}:${e.name}`))
: disabledSkills
? skillDirs.filter((e) => !disabledSkills.has(`${pluginName}:${e.name}`))
: skillDirs
: skillDirs;
? candidateDirs.filter((e) => !disabledSkills.has(`${pluginName}:${e.name}`))
: candidateDirs
: candidateDirs;

return filteredDirs.map((entry) => ({
folderName: entry.name,
skillPath: join(skillsDir, entry.name),
skillPath: entry.path,
pluginPath,
pluginSource,
}));
Expand Down