import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { afterEach, describe, expect, it } from "vitest"; import { listCodexSkills, syncCodexSkills, } from "@paperclipai/adapter-codex-local/server"; async function makeTempDir(prefix: string): Promise { return fs.mkdtemp(path.join(os.tmpdir(), prefix)); } describe("codex local skill sync", () => { const paperclipKey = "paperclipai/paperclip/paperclip"; const cleanupDirs = new Set(); afterEach(async () => { await Promise.all(Array.from(cleanupDirs).map((dir) => fs.rm(dir, { recursive: true, force: true }))); cleanupDirs.clear(); }); it("reports configured Paperclip skills and installs them into the Codex skills home", async () => { const codexHome = await makeTempDir("paperclip-codex-skill-sync-"); cleanupDirs.add(codexHome); const ctx = { agentId: "agent-1", companyId: "company-1", adapterType: "codex_local", config: { env: { CODEX_HOME: codexHome, }, paperclipSkillSync: { desiredSkills: [paperclipKey], }, }, } as const; const before = await listCodexSkills(ctx); expect(before.mode).toBe("persistent"); expect(before.desiredSkills).toContain(paperclipKey); expect(before.entries.find((entry) => entry.key === paperclipKey)?.required).toBe(true); expect(before.entries.find((entry) => entry.key === paperclipKey)?.state).toBe("missing"); const after = await syncCodexSkills(ctx, [paperclipKey]); expect(after.entries.find((entry) => entry.key === paperclipKey)?.state).toBe("installed"); expect((await fs.lstat(path.join(codexHome, "skills", "paperclip"))).isSymbolicLink()).toBe(true); }); it("keeps required bundled Paperclip skills installed even when the desired set is emptied", async () => { const codexHome = await makeTempDir("paperclip-codex-skill-prune-"); cleanupDirs.add(codexHome); const configuredCtx = { agentId: "agent-2", companyId: "company-1", adapterType: "codex_local", config: { env: { CODEX_HOME: codexHome, }, paperclipSkillSync: { desiredSkills: [paperclipKey], }, }, } as const; await syncCodexSkills(configuredCtx, [paperclipKey]); const clearedCtx = { ...configuredCtx, config: { env: { CODEX_HOME: codexHome, }, paperclipSkillSync: { desiredSkills: [], }, }, } as const; const after = await syncCodexSkills(clearedCtx, []); expect(after.desiredSkills).toContain(paperclipKey); expect(after.entries.find((entry) => entry.key === paperclipKey)?.state).toBe("installed"); expect((await fs.lstat(path.join(codexHome, "skills", "paperclip"))).isSymbolicLink()).toBe(true); }); });