import path from "node:path"; import { fileURLToPath } from "node:url"; import type { AdapterSkillContext, AdapterSkillEntry, AdapterSkillSnapshot, } from "@paperclipai/adapter-utils"; import { readPaperclipRuntimeSkillEntries, resolvePaperclipDesiredSkillNames, } from "@paperclipai/adapter-utils/server-utils"; const __moduleDir = path.dirname(fileURLToPath(import.meta.url)); async function buildCodexSkillSnapshot( config: Record, ): Promise { const availableEntries = await readPaperclipRuntimeSkillEntries(config, __moduleDir); const availableByKey = new Map(availableEntries.map((entry) => [entry.key, entry])); const desiredSkills = resolvePaperclipDesiredSkillNames(config, availableEntries); const desiredSet = new Set(desiredSkills); const entries: AdapterSkillEntry[] = availableEntries.map((entry) => ({ key: entry.key, runtimeName: entry.runtimeName, desired: desiredSet.has(entry.key), managed: true, state: desiredSet.has(entry.key) ? "configured" : "available", origin: entry.required ? "paperclip_required" : "company_managed", originLabel: entry.required ? "Required by Paperclip" : "Managed by Paperclip", readOnly: false, sourcePath: entry.source, targetPath: null, detail: desiredSet.has(entry.key) ? "Will be linked into the effective CODEX_HOME/skills/ directory on the next run." : null, required: Boolean(entry.required), requiredReason: entry.requiredReason ?? null, })); const warnings: string[] = []; for (const desiredSkill of desiredSkills) { if (availableByKey.has(desiredSkill)) continue; warnings.push(`Desired skill "${desiredSkill}" is not available from the Paperclip skills directory.`); entries.push({ key: desiredSkill, runtimeName: null, desired: true, managed: true, state: "missing", origin: "external_unknown", originLabel: "External or unavailable", readOnly: false, sourcePath: null, targetPath: null, detail: "Paperclip cannot find this skill in the local runtime skills directory.", }); } entries.sort((left, right) => left.key.localeCompare(right.key)); return { adapterType: "codex_local", supported: true, mode: "ephemeral", desiredSkills, entries, warnings, }; } export async function listCodexSkills(ctx: AdapterSkillContext): Promise { return buildCodexSkillSnapshot(ctx.config); } export async function syncCodexSkills( ctx: AdapterSkillContext, _desiredSkills: string[], ): Promise { return buildCodexSkillSnapshot(ctx.config); } export function resolveCodexDesiredSkillNames( config: Record, availableEntries: Array<{ key: string; required?: boolean }>, ) { return resolvePaperclipDesiredSkillNames(config, availableEntries); }