Merge pull request #2891 from Ttungx/fix/skill-visibility-windows-paths
fix(ui): normalize Windows paths in skill visibility filtering
This commit is contained in:
@@ -6,6 +6,9 @@ const skill = (name: string, path: string) => ({ name, path });
|
||||
const AGENTS = (name: string) => skill(name, `/repo/.agents/skills/${name}/SKILL.md`);
|
||||
const CLAUDE = (name: string) => skill(name, `/repo/.claude/skills/${name}/SKILL.md`);
|
||||
const OPENCODE = (name: string) => skill(name, `/home/u/.config/opencode/skill/${name}/SKILL.md`);
|
||||
const WIN_AGENTS = (name: string) => skill(name, String.raw`C:\Users\u\.agents\skills\${name}\SKILL.md`);
|
||||
const WIN_CLAUDE = (name: string) => skill(name, String.raw`C:\Users\u\.claude\skills\${name}\SKILL.md`);
|
||||
const WIN_OPENCODE = (name: string) => skill(name, String.raw`C:\Users\u\.config\opencode\skill\${name}\SKILL.md`);
|
||||
|
||||
const ENABLED = { claudeDisabled: false, allDisabled: false };
|
||||
|
||||
@@ -20,6 +23,13 @@ describe('resolveSkillRoot', () => {
|
||||
test('does not match a directory that merely contains the name', () => {
|
||||
expect(resolveSkillRoot('/repo/my.claude.backup/skills/a/SKILL.md')).toBe('opencode');
|
||||
});
|
||||
|
||||
test('classifies Windows backslash paths', () => {
|
||||
expect(resolveSkillRoot(WIN_CLAUDE('a').path)).toBe('claude');
|
||||
expect(resolveSkillRoot(WIN_AGENTS('a').path)).toBe('agents');
|
||||
expect(resolveSkillRoot(WIN_OPENCODE('a').path)).toBe('opencode');
|
||||
expect(resolveSkillRoot(String.raw`C:\repo\my.claude.backup\skills\a\SKILL.md`)).toBe('opencode');
|
||||
});
|
||||
});
|
||||
|
||||
describe('filterSkillsByRuntimeFlags', () => {
|
||||
@@ -72,4 +82,22 @@ describe('filterSkillsByRuntimeFlags', () => {
|
||||
const result = filterSkillsByRuntimeFlags([CLAUDE('only-claude'), AGENTS('other')], ENABLED);
|
||||
expect(result.map((s) => s.name).sort()).toEqual(['only-claude', 'other']);
|
||||
});
|
||||
|
||||
test('drops Windows .agents and .claude skills when external skills are disabled', () => {
|
||||
const skills = [WIN_AGENTS('a'), WIN_CLAUDE('b'), WIN_OPENCODE('c')];
|
||||
const result = filterSkillsByRuntimeFlags(skills, { claudeDisabled: false, allDisabled: true });
|
||||
expect(result.map((s) => s.name)).toEqual(['c']);
|
||||
});
|
||||
|
||||
test('drops only Windows .claude skills when claude skills are disabled', () => {
|
||||
const skills = [WIN_AGENTS('a'), WIN_CLAUDE('b'), WIN_OPENCODE('c')];
|
||||
const result = filterSkillsByRuntimeFlags(skills, { claudeDisabled: true, allDisabled: false });
|
||||
expect(result.map((s) => s.name).sort()).toEqual(['a', 'c']);
|
||||
});
|
||||
|
||||
test('prefers the .agents copy for a duplicated name on Windows', () => {
|
||||
const result = filterSkillsByRuntimeFlags([WIN_CLAUDE('dup'), WIN_AGENTS('dup')], ENABLED);
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].path).toContain('.agents');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -35,8 +35,12 @@ const AGENTS_ROOT = /(^|\/)\.agents\//;
|
||||
type SkillRoot = 'claude' | 'agents' | 'opencode';
|
||||
|
||||
export const resolveSkillRoot = (skillPath: string): SkillRoot => {
|
||||
if (CLAUDE_ROOT.test(skillPath)) return 'claude';
|
||||
if (AGENTS_ROOT.test(skillPath)) return 'agents';
|
||||
// Server discovery joins paths with the platform separator, so Windows
|
||||
// skill paths arrive with backslashes. Normalize before matching the
|
||||
// root regexes, which are expressed with forward slashes.
|
||||
const normalized = skillPath.replace(/\\/g, '/');
|
||||
if (CLAUDE_ROOT.test(normalized)) return 'claude';
|
||||
if (AGENTS_ROOT.test(normalized)) return 'agents';
|
||||
return 'opencode';
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user