diff --git a/packages/adapters/codex-local/src/server/codex-home.ts b/packages/adapters/codex-local/src/server/codex-home.ts index de037d6a..08c851e7 100644 --- a/packages/adapters/codex-local/src/server/codex-home.ts +++ b/packages/adapters/codex-local/src/server/codex-home.ts @@ -94,7 +94,7 @@ export async function prepareWorktreeCodexHome( } await onLog( - "stderr", + "stdout", `[paperclip] Using worktree-isolated Codex home "${targetHome}" (seeded from "${sourceHome}").\n`, ); return targetHome; diff --git a/packages/adapters/codex-local/src/server/execute.ts b/packages/adapters/codex-local/src/server/execute.ts index b0c72ad5..54863010 100644 --- a/packages/adapters/codex-local/src/server/execute.ts +++ b/packages/adapters/codex-local/src/server/execute.ts @@ -116,7 +116,7 @@ export async function ensureCodexSkillsInjected( ); for (const skillName of removedSkills) { await onLog( - "stderr", + "stdout", `[paperclip] Removed maintainer-only Codex skill "${skillName}" from ${skillsHome}\n`, ); } @@ -143,7 +143,7 @@ export async function ensureCodexSkillsInjected( await fs.symlink(entry.source, target); } await onLog( - "stderr", + "stdout", `[paperclip] Repaired Codex skill "${entry.name}" into ${skillsHome}\n`, ); continue; @@ -154,7 +154,7 @@ export async function ensureCodexSkillsInjected( if (result === "skipped") continue; await onLog( - "stderr", + "stdout", `[paperclip] ${result === "repaired" ? "Repaired" : "Injected"} Codex skill "${entry.name}" into ${skillsHome}\n`, ); } catch (err) { @@ -364,7 +364,7 @@ export async function execute(ctx: AdapterExecutionContext): Promise { it("uses a worktree-isolated CODEX_HOME while preserving shared auth and config", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "paperclip-codex-execute-")); @@ -62,6 +67,7 @@ describe("codex execute", () => { process.env.CODEX_HOME = sharedCodexHome; try { + const logs: LogEntry[] = []; const result = await execute({ runId: "run-1", agent: { @@ -87,7 +93,9 @@ describe("codex execute", () => { }, context: {}, authToken: "run-jwt-token", - onLog: async () => {}, + onLog: async (stream, chunk) => { + logs.push({ stream, chunk }); + }, }); expect(result.exitCode).toBe(0); @@ -116,6 +124,18 @@ describe("codex execute", () => { expect((await fs.lstat(isolatedConfig)).isFile()).toBe(true); expect(await fs.readFile(isolatedConfig, "utf8")).toBe('model = "codex-mini-latest"\n'); expect((await fs.lstat(isolatedSkill)).isSymbolicLink()).toBe(true); + expect(logs).toContainEqual( + expect.objectContaining({ + stream: "stdout", + chunk: expect.stringContaining("Using worktree-isolated Codex home"), + }), + ); + expect(logs).toContainEqual( + expect.objectContaining({ + stream: "stdout", + chunk: expect.stringContaining('Injected Codex skill "paperclip"'), + }), + ); } finally { if (previousHome === undefined) delete process.env.HOME; else process.env.HOME = previousHome; diff --git a/server/src/__tests__/codex-local-skill-injection.test.ts b/server/src/__tests__/codex-local-skill-injection.test.ts index bbbaec63..22b714e1 100644 --- a/server/src/__tests__/codex-local-skill-injection.test.ts +++ b/server/src/__tests__/codex-local-skill-injection.test.ts @@ -50,10 +50,10 @@ describe("codex local adapter skill injection", () => { await createPaperclipRepoSkill(oldRepo, "paperclip"); await fs.symlink(path.join(oldRepo, "skills", "paperclip"), path.join(skillsHome, "paperclip")); - const logs: string[] = []; + const logs: Array<{ stream: "stdout" | "stderr"; chunk: string }> = []; await ensureCodexSkillsInjected( - async (_stream, chunk) => { - logs.push(chunk); + async (stream, chunk) => { + logs.push({ stream, chunk }); }, { skillsHome, @@ -64,7 +64,12 @@ describe("codex local adapter skill injection", () => { expect(await fs.realpath(path.join(skillsHome, "paperclip"))).toBe( await fs.realpath(path.join(currentRepo, "skills", "paperclip")), ); - expect(logs.some((line) => line.includes('Repaired Codex skill "paperclip"'))).toBe(true); + expect(logs).toContainEqual( + expect.objectContaining({ + stream: "stdout", + chunk: expect.stringContaining('Repaired Codex skill "paperclip"'), + }), + ); }); it("preserves a custom Codex skill symlink outside Paperclip repo checkouts", async () => {