feat: support skills in .agents directory and other provider config directories

This commit is contained in:
LaZzyMan 2026-03-09 10:14:47 +08:00
parent f3b56f5a31
commit ef772feea2
2 changed files with 51 additions and 22 deletions

View file

@ -504,17 +504,35 @@ Skill 3 content`);
});
});
describe('getSkillsBaseDir', () => {
it('should return project-level base dir', () => {
const baseDir = manager.getSkillsBaseDir('project');
describe('getSkillsBaseDirs', () => {
it('should return all project-level base dirs', () => {
const baseDirs = manager.getSkillsBaseDirs('project');
expect(baseDir).toBe(path.join('/test/project', '.qwen', 'skills'));
expect(baseDirs).toHaveLength(5);
expect(baseDirs).toContain(path.join('/test/project', '.qwen', 'skills'));
expect(baseDirs).toContain(
path.join('/test/project', '.agent', 'skills'),
);
expect(baseDirs).toContain(
path.join('/test/project', '.cursor', 'skills'),
);
expect(baseDirs).toContain(
path.join('/test/project', '.codex', 'skills'),
);
expect(baseDirs).toContain(
path.join('/test/project', '.claude', 'skills'),
);
});
it('should return user-level base dir', () => {
const baseDir = manager.getSkillsBaseDir('user');
it('should return all user-level base dirs', () => {
const baseDirs = manager.getSkillsBaseDirs('user');
expect(baseDir).toBe(path.join('/home/user', '.qwen', 'skills'));
expect(baseDirs).toHaveLength(5);
expect(baseDirs).toContain(path.join('/home/user', '.qwen', 'skills'));
expect(baseDirs).toContain(path.join('/home/user', '.agent', 'skills'));
expect(baseDirs).toContain(path.join('/home/user', '.cursor', 'skills'));
expect(baseDirs).toContain(path.join('/home/user', '.codex', 'skills'));
expect(baseDirs).toContain(path.join('/home/user', '.claude', 'skills'));
});
});

View file

@ -25,6 +25,13 @@ import { normalizeContent } from '../utils/textUtils.js';
const debugLogger = createDebugLogger('SKILL_MANAGER');
const QWEN_CONFIG_DIR = '.qwen';
const PROVIDER_CONFIG_DIRS = [
'.qwen',
'.agent',
'.cursor',
'.codex',
'.claude',
];
const SKILLS_CONFIG_DIR = 'skills';
const SKILL_MANIFEST_FILE = 'SKILL.md';
@ -412,19 +419,18 @@ export class SkillManager {
* Gets the base directory for skills at a specific level.
*
* @param level - Storage level
* @returns Absolute directory path
* @returns Absolute directory paths
*/
getSkillsBaseDir(level: SkillLevel): string {
const baseDir =
getSkillsBaseDirs(level: SkillLevel): string[] {
const baseDirs =
level === 'project'
? path.join(
this.config.getProjectRoot(),
QWEN_CONFIG_DIR,
SKILLS_CONFIG_DIR,
? PROVIDER_CONFIG_DIRS.map((v) =>
path.join(this.config.getProjectRoot(), v, SKILLS_CONFIG_DIR),
)
: path.join(os.homedir(), QWEN_CONFIG_DIR, SKILLS_CONFIG_DIR);
return baseDir;
: PROVIDER_CONFIG_DIRS.map((v) =>
path.join(os.homedir(), v, SKILLS_CONFIG_DIR),
);
return baseDirs;
}
/**
@ -461,9 +467,13 @@ export class SkillManager {
return skills;
}
const baseDir = this.getSkillsBaseDir(level);
debugLogger.debug(`Loading ${level} level skills from: ${baseDir}`);
const skills = await this.loadSkillsFromDir(baseDir, level);
const baseDirs = this.getSkillsBaseDirs(level);
const skills: SkillConfig[] = [];
for (let i = 0; i < baseDirs.length; i++) {
debugLogger.debug(`Loading ${level} level skills from: ${baseDirs[i]}`);
const skillsFromDir = await this.loadSkillsFromDir(baseDirs[i], level);
skills.push(...skillsFromDir);
}
debugLogger.debug(`Loaded ${skills.length} ${level} level skills`);
return skills;
}
@ -583,7 +593,8 @@ export class SkillManager {
private updateWatchersFromCache(): void {
const watchTargets = new Set<string>(
(['project', 'user'] as const)
.map((level) => this.getSkillsBaseDir(level))
.map((level) => this.getSkillsBaseDirs(level))
.reduce((acc, baseDirs) => acc.concat(baseDirs), [])
.filter((baseDir) => fsSync.existsSync(baseDir)),
);
@ -639,7 +650,7 @@ export class SkillManager {
}
private async ensureUserSkillsDir(): Promise<void> {
const baseDir = this.getSkillsBaseDir('user');
const baseDir = path.join(os.homedir(), QWEN_CONFIG_DIR, SKILLS_CONFIG_DIR);
try {
await fs.mkdir(baseDir, { recursive: true });
} catch (error) {