import { Command } from "commander"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { createCliRuntimeCapture } from "./test-runtime-capture.js"; const loadConfigMock = vi.fn(() => ({})); const resolveDefaultAgentIdMock = vi.fn(() => "main"); const resolveAgentWorkspaceDirMock = vi.fn(() => "/tmp/workspace"); const searchSkillsFromClawHubMock = vi.fn(); const installSkillFromClawHubMock = vi.fn(); const updateSkillsFromClawHubMock = vi.fn(); const readTrackedClawHubSkillSlugsMock = vi.fn(); const { defaultRuntime, runtimeLogs, runtimeErrors, resetRuntimeCapture } = createCliRuntimeCapture(); vi.mock("../runtime.js", () => ({ defaultRuntime, })); vi.mock("../config/config.js", () => ({ loadConfig: () => loadConfigMock(), })); vi.mock("../agents/agent-scope.js", () => ({ resolveDefaultAgentId: () => resolveDefaultAgentIdMock(), resolveAgentWorkspaceDir: () => resolveAgentWorkspaceDirMock(), })); vi.mock("../agents/skills-clawhub.js", () => ({ searchSkillsFromClawHub: (...args: unknown[]) => searchSkillsFromClawHubMock(...args), installSkillFromClawHub: (...args: unknown[]) => installSkillFromClawHubMock(...args), updateSkillsFromClawHub: (...args: unknown[]) => updateSkillsFromClawHubMock(...args), readTrackedClawHubSkillSlugs: (...args: unknown[]) => readTrackedClawHubSkillSlugsMock(...args), })); const { registerSkillsCli } = await import("./skills-cli.js"); describe("skills cli commands", () => { const createProgram = () => { const program = new Command(); program.exitOverride(); registerSkillsCli(program); return program; }; const runCommand = (argv: string[]) => createProgram().parseAsync(argv, { from: "user" }); beforeEach(() => { resetRuntimeCapture(); loadConfigMock.mockReset(); resolveDefaultAgentIdMock.mockReset(); resolveAgentWorkspaceDirMock.mockReset(); searchSkillsFromClawHubMock.mockReset(); installSkillFromClawHubMock.mockReset(); updateSkillsFromClawHubMock.mockReset(); readTrackedClawHubSkillSlugsMock.mockReset(); loadConfigMock.mockReturnValue({}); resolveDefaultAgentIdMock.mockReturnValue("main"); resolveAgentWorkspaceDirMock.mockReturnValue("/tmp/workspace"); searchSkillsFromClawHubMock.mockResolvedValue([]); installSkillFromClawHubMock.mockResolvedValue({ ok: false, error: "install disabled in test", }); updateSkillsFromClawHubMock.mockResolvedValue([]); readTrackedClawHubSkillSlugsMock.mockResolvedValue([]); }); it("searches ClawHub skills from the native CLI", async () => { searchSkillsFromClawHubMock.mockResolvedValue([ { slug: "calendar", displayName: "Calendar", summary: "CalDAV helpers", version: "1.2.3", }, ]); await runCommand(["skills", "search", "calendar"]); expect(searchSkillsFromClawHubMock).toHaveBeenCalledWith({ query: "calendar", limit: undefined, }); expect(runtimeLogs.some((line) => line.includes("calendar v1.2.3 Calendar"))).toBe(true); }); it("installs a skill from ClawHub into the active workspace", async () => { installSkillFromClawHubMock.mockResolvedValue({ ok: true, slug: "calendar", version: "1.2.3", targetDir: "/tmp/workspace/skills/calendar", }); await runCommand(["skills", "install", "calendar", "--version", "1.2.3"]); expect(installSkillFromClawHubMock).toHaveBeenCalledWith({ workspaceDir: "/tmp/workspace", slug: "calendar", version: "1.2.3", force: false, logger: expect.any(Object), }); expect( runtimeLogs.some((line) => line.includes("Installed calendar@1.2.3 -> /tmp/workspace/skills/calendar"), ), ).toBe(true); }); it("updates all tracked ClawHub skills", async () => { readTrackedClawHubSkillSlugsMock.mockResolvedValue(["calendar"]); updateSkillsFromClawHubMock.mockResolvedValue([ { ok: true, slug: "calendar", previousVersion: "1.2.2", version: "1.2.3", changed: true, targetDir: "/tmp/workspace/skills/calendar", }, ]); await runCommand(["skills", "update", "--all"]); expect(readTrackedClawHubSkillSlugsMock).toHaveBeenCalledWith("/tmp/workspace"); expect(updateSkillsFromClawHubMock).toHaveBeenCalledWith({ workspaceDir: "/tmp/workspace", slug: undefined, logger: expect.any(Object), }); expect(runtimeLogs.some((line) => line.includes("Updated calendar: 1.2.2 -> 1.2.3"))).toBe( true, ); expect(runtimeErrors).toEqual([]); }); });