mirror of
https://github.com/iflytek/skillhub.git
synced 2026-09-16 23:41:19 +00:00
- Interactive agent selection for install/uninstall/list - Namespace search selection for unspecified namespace - --skill-version option to skip interactive selection - --from option for local/github source installation - Remove deprecated add command (use install --from instead) - Path-based grouping for uninstall results
112 lines
3.7 KiB
TypeScript
112 lines
3.7 KiB
TypeScript
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
|
import { existsSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
|
|
import { join } from "node:path";
|
|
import { tmpdir } from "node:os";
|
|
|
|
const mockSuccess = vi.fn();
|
|
const mockError = vi.fn();
|
|
const mockInfo = vi.fn();
|
|
|
|
vi.mock("../src/utils/logger.js", () => ({
|
|
success: mockSuccess,
|
|
error: mockError,
|
|
info: mockInfo,
|
|
dim: vi.fn(),
|
|
}));
|
|
|
|
describe("uninstall command", () => {
|
|
let tempDir: string;
|
|
|
|
beforeEach(() => {
|
|
tempDir = join(tmpdir(), "uninstall-test-" + Date.now());
|
|
mkdirSync(tempDir, { recursive: true });
|
|
mockSuccess.mockClear();
|
|
mockError.mockClear();
|
|
mockInfo.mockClear();
|
|
});
|
|
|
|
afterEach(() => {
|
|
try { rmSync(tempDir, { recursive: true, force: true }); } catch {}
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
describe("removeDir utility", () => {
|
|
it("should handle file removal", () => {
|
|
const testFile = join(tempDir, "test-file.txt");
|
|
writeFileSync(testFile, "content");
|
|
expect(existsSync(testFile)).toBe(true);
|
|
});
|
|
|
|
it("should handle directory removal recursively", () => {
|
|
const testSubDir = join(tempDir, "subdir", "nested");
|
|
mkdirSync(testSubDir, { recursive: true });
|
|
writeFileSync(join(testSubDir, "file.txt"), "content");
|
|
expect(existsSync(testSubDir)).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("--all flag", () => {
|
|
it("should discover all installed skills", async () => {
|
|
const skill1 = join(tempDir, ".claude", "skills", "skill-one");
|
|
const skill2 = join(tempDir, ".claude", "skills", "skill-two");
|
|
mkdirSync(skill1, { recursive: true });
|
|
mkdirSync(skill2, { recursive: true });
|
|
writeFileSync(join(skill1, "SKILL.md"), "# Skill One\n");
|
|
writeFileSync(join(skill2, "SKILL.md"), "# Skill Two\n");
|
|
|
|
expect(existsSync(skill1)).toBe(true);
|
|
expect(existsSync(skill2)).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("--agent filter", () => {
|
|
it("should filter by specific agent", () => {
|
|
const claudeDir = join(tempDir, ".claude", "skills", "shared-skill");
|
|
const cursorDir = join(tempDir, ".agents", "skills", "shared-skill");
|
|
mkdirSync(claudeDir, { recursive: true });
|
|
mkdirSync(cursorDir, { recursive: true });
|
|
writeFileSync(join(claudeDir, "SKILL.md"), "# Shared Skill\n");
|
|
writeFileSync(join(cursorDir, "SKILL.md"), "# Shared Skill\n");
|
|
|
|
expect(existsSync(claudeDir)).toBe(true);
|
|
expect(existsSync(cursorDir)).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("--global flag", () => {
|
|
it("should target global scope only", () => {
|
|
const globalSkill = join(tempDir, ".claude", "skills", "global-skill");
|
|
mkdirSync(globalSkill, { recursive: true });
|
|
writeFileSync(join(globalSkill, "SKILL.md"), "# Global Skill\n");
|
|
|
|
expect(existsSync(globalSkill)).toBe(true);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("source parser", () => {
|
|
describe("parseSource", () => {
|
|
it("should identify git source: owner/repo", () => {
|
|
const source = "vercel-labs/agent-skills";
|
|
const pattern = /^[\w-]+\/[\w-]+/;
|
|
expect(pattern.test(source)).toBe(true);
|
|
});
|
|
|
|
it("should identify git source: GitHub URL", () => {
|
|
const source = "https://github.com/vercel-labs/agent-skills";
|
|
expect(source.startsWith("https://github.com/")).toBe(true);
|
|
});
|
|
|
|
it("should identify registry source: slug", () => {
|
|
const source = "my-skill";
|
|
const isGit = /^[\w-]+\/[\w-]+/.test(source) || source.startsWith("https://github.com/");
|
|
expect(isGit).toBe(false);
|
|
});
|
|
|
|
it("should identify registry source: namespace--slug", () => {
|
|
const source = "global--my-skill";
|
|
const parts = source.split("--");
|
|
expect(parts.length >= 2).toBe(true);
|
|
});
|
|
});
|
|
});
|