- Integrated the @radix-ui/react-alert-dialog package for improved alert dialog functionality. - Updated SkillImportService to include a new inspectSourceDir method for enhanced file inspection and warning generation during skill imports. - Refactored existing methods to streamline file reading and directory walking processes, improving overall performance and error handling. - Added new SkillPlanService to manage skill upsert plans, enhancing the skills mutation workflow. - Updated UI components to support new features and improve user experience in the skills management interface.
107 lines
3.8 KiB
TypeScript
107 lines
3.8 KiB
TypeScript
import * as fs from 'node:fs/promises';
|
|
import * as os from 'node:os';
|
|
import * as path from 'node:path';
|
|
|
|
import { afterEach, describe, expect, it } from 'vitest';
|
|
|
|
import { SkillPlanService } from '@main/services/extensions/skills/SkillPlanService';
|
|
import { SkillScaffoldService } from '@main/services/extensions/skills/SkillScaffoldService';
|
|
import { SkillsCatalogService } from '@main/services/extensions/skills/SkillsCatalogService';
|
|
import { SkillsMutationService } from '@main/services/extensions/skills/SkillsMutationService';
|
|
|
|
import type { ResolvedSkillRoot } from '@main/services/extensions/skills/SkillRootsResolver';
|
|
|
|
function createResolver(rootPath: string) {
|
|
return {
|
|
resolve(projectPath?: string): ResolvedSkillRoot[] {
|
|
return [
|
|
{
|
|
scope: 'project',
|
|
rootKind: 'claude',
|
|
projectRoot: projectPath ?? rootPath,
|
|
rootPath,
|
|
},
|
|
];
|
|
},
|
|
};
|
|
}
|
|
|
|
describe('SkillsMutationService', () => {
|
|
const createdDirs: string[] = [];
|
|
|
|
afterEach(async () => {
|
|
await Promise.all(createdDirs.splice(0).map((dir) => fs.rm(dir, { recursive: true, force: true })));
|
|
});
|
|
|
|
it('applies the reviewed plan and deletes obsolete managed files', async () => {
|
|
const projectRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'skill-mutation-'));
|
|
createdDirs.push(projectRoot);
|
|
|
|
const skillsRoot = path.join(projectRoot, '.claude', 'skills');
|
|
const skillDir = path.join(skillsRoot, 'demo');
|
|
await fs.mkdir(path.join(skillDir, 'scripts'), { recursive: true });
|
|
await fs.writeFile(path.join(skillDir, 'SKILL.md'), '# old', 'utf8');
|
|
await fs.writeFile(path.join(skillDir, 'scripts', 'README.md'), 'old script', 'utf8');
|
|
|
|
const resolver = createResolver(skillsRoot);
|
|
const mutationService = new SkillsMutationService(
|
|
resolver as any,
|
|
new SkillsCatalogService(resolver as any),
|
|
new SkillScaffoldService(resolver as any),
|
|
undefined,
|
|
new SkillPlanService()
|
|
);
|
|
|
|
const request = {
|
|
scope: 'project' as const,
|
|
rootKind: 'claude' as const,
|
|
projectPath: projectRoot,
|
|
folderName: 'demo',
|
|
existingSkillId: skillDir,
|
|
files: [{ relativePath: 'SKILL.md', content: '# updated' }],
|
|
};
|
|
|
|
const preview = await mutationService.previewUpsert(request);
|
|
await mutationService.applyUpsert({ ...request, reviewPlanId: preview.planId });
|
|
|
|
await expect(fs.readFile(path.join(skillDir, 'SKILL.md'), 'utf8')).resolves.toBe('# updated');
|
|
await expect(fs.stat(path.join(skillDir, 'scripts', 'README.md'))).rejects.toMatchObject({
|
|
code: 'ENOENT',
|
|
});
|
|
});
|
|
|
|
it('rejects apply when the reviewed plan is stale', async () => {
|
|
const projectRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'skill-mutation-'));
|
|
createdDirs.push(projectRoot);
|
|
|
|
const skillsRoot = path.join(projectRoot, '.claude', 'skills');
|
|
const skillDir = path.join(skillsRoot, 'demo');
|
|
await fs.mkdir(skillDir, { recursive: true });
|
|
await fs.writeFile(path.join(skillDir, 'SKILL.md'), '# old', 'utf8');
|
|
|
|
const resolver = createResolver(skillsRoot);
|
|
const mutationService = new SkillsMutationService(
|
|
resolver as any,
|
|
new SkillsCatalogService(resolver as any),
|
|
new SkillScaffoldService(resolver as any),
|
|
undefined,
|
|
new SkillPlanService()
|
|
);
|
|
|
|
const request = {
|
|
scope: 'project' as const,
|
|
rootKind: 'claude' as const,
|
|
projectPath: projectRoot,
|
|
folderName: 'demo',
|
|
existingSkillId: skillDir,
|
|
files: [{ relativePath: 'SKILL.md', content: '# updated' }],
|
|
};
|
|
|
|
const preview = await mutationService.previewUpsert(request);
|
|
await fs.writeFile(path.join(skillDir, 'SKILL.md'), '# changed after review', 'utf8');
|
|
|
|
await expect(
|
|
mutationService.applyUpsert({ ...request, reviewPlanId: preview.planId })
|
|
).rejects.toThrow('changed after review');
|
|
});
|
|
});
|