From 623ab1c3ea0c3c03fb68bb6a65b9218d99810dad Mon Sep 17 00:00:00 2001 From: Devin Foley Date: Wed, 25 Mar 2026 16:04:53 -0700 Subject: [PATCH] Fix skill injection to use effective CODEX_HOME, not shared home The previous commit incorrectly used resolveSharedCodexHomeDir() (~/.codex) but Codex runs with CODEX_HOME set to a per-company managed home under ~/.paperclip/instances/. Skills injected into ~/.codex/skills/ would not be discoverable by Codex. Now uses effectiveCodexHome directly. Co-Authored-By: Paperclip --- packages/adapters/codex-local/src/server/execute.ts | 8 ++++---- packages/adapters/codex-local/src/server/skills.ts | 2 +- server/src/__tests__/codex-local-execute.test.ts | 4 ++-- server/src/__tests__/codex-local-skill-sync.test.ts | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/adapters/codex-local/src/server/execute.ts b/packages/adapters/codex-local/src/server/execute.ts index a35caf4b..7d23dbbf 100644 --- a/packages/adapters/codex-local/src/server/execute.ts +++ b/packages/adapters/codex-local/src/server/execute.ts @@ -21,7 +21,7 @@ import { runChildProcess, } from "@paperclipai/adapter-utils/server-utils"; import { parseCodexJsonl, isCodexUnknownSessionError } from "./parse.js"; -import { pathExists, prepareManagedCodexHome, resolveManagedCodexHomeDir, resolveSharedCodexHomeDir } from "./codex-home.js"; +import { pathExists, prepareManagedCodexHome, resolveManagedCodexHomeDir } from "./codex-home.js"; import { resolveCodexDesiredSkillNames } from "./skills.js"; const __moduleDir = path.dirname(fileURLToPath(import.meta.url)); @@ -135,8 +135,8 @@ async function pruneBrokenUnavailablePaperclipSkillSymlinks( } } -function resolveCodexSkillsHome(): string { - return path.join(resolveSharedCodexHomeDir(), "skills"); +function resolveCodexSkillsDir(codexHome: string): string { + return path.join(codexHome, "skills"); } type EnsureCodexSkillsInjectedOptions = { @@ -273,7 +273,7 @@ export async function execute(ctx: AdapterExecutionContext): Promise { "company-1", "codex-home", ); - const homeSkill = path.join(sharedCodexHome, "skills", "paperclip"); + const homeSkill = path.join(isolatedCodexHome, "skills", "paperclip"); await fs.mkdir(workspace, { recursive: true }); await fs.mkdir(sharedCodexHome, { recursive: true }); await fs.writeFile(path.join(sharedCodexHome, "auth.json"), '{"token":"shared"}\n', "utf8"); @@ -371,7 +371,7 @@ describe("codex execute", () => { const capture = JSON.parse(await fs.readFile(capturePath, "utf8")) as CapturePayload; expect(capture.codexHome).toBe(explicitCodexHome); - expect((await fs.lstat(path.join(sharedCodexHome, "skills", "paperclip"))).isSymbolicLink()).toBe(true); + expect((await fs.lstat(path.join(explicitCodexHome, "skills", "paperclip"))).isSymbolicLink()).toBe(true); await expect(fs.lstat(path.join(paperclipHome, "instances", "worktree-1", "codex-home"))).rejects.toThrow(); } finally { if (previousHome === undefined) delete process.env.HOME; diff --git a/server/src/__tests__/codex-local-skill-sync.test.ts b/server/src/__tests__/codex-local-skill-sync.test.ts index 49c239c9..55568bef 100644 --- a/server/src/__tests__/codex-local-skill-sync.test.ts +++ b/server/src/__tests__/codex-local-skill-sync.test.ts @@ -43,7 +43,7 @@ describe("codex local skill sync", () => { 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("configured"); - expect(before.entries.find((entry) => entry.key === paperclipKey)?.detail).toContain("~/.codex/skills/"); + expect(before.entries.find((entry) => entry.key === paperclipKey)?.detail).toContain("$CODEX_HOME/skills/"); }); it("does not persist Paperclip skills into CODEX_HOME during sync", async () => {