From b6c2919b5f63382276fa372fc87e5398e67cc765 Mon Sep 17 00:00:00 2001 From: anish Date: Tue, 19 May 2026 05:48:40 +0000 Subject: [PATCH] fix: preserve symlinks in .config/ during restore to prevent ENOENT on dangling links The `restoreConfigFromBase` function crashes with ENOENT when `.config/` contains symlinks pointing to files that don't exist yet (e.g., symlinks into dependency directories before installation). Signed-off-by: anish --- src/github/operations/restore-config.ts | 2 +- test/restore-config.test.ts | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/github/operations/restore-config.ts b/src/github/operations/restore-config.ts index b847cf328..e5f628e35 100644 --- a/src/github/operations/restore-config.ts +++ b/src/github/operations/restore-config.ts @@ -86,7 +86,7 @@ export function restoreConfigFromBase(baseBranch: string): void { rmSync(".claude-pr", { recursive: true, force: true }); for (const p of SENSITIVE_PATHS) { if (existsSync(p)) { - cpSync(p, `.claude-pr/${p}`, { recursive: true, dereference: true }); + cpSync(p, `.claude-pr/${p}`, { recursive: true, dereference: false }); } } if (existsSync(".claude-pr")) { diff --git a/test/restore-config.test.ts b/test/restore-config.test.ts index 80439ead9..e376ff4d9 100644 --- a/test/restore-config.test.ts +++ b/test/restore-config.test.ts @@ -134,6 +134,27 @@ describe("restoreConfigFromBase", () => { expect(countClaudePrExcludeEntries()).toBe(1); }); + test("handles dangling symlinks in .claude/ without crashing", () => { + const skillsDir = join(repoDir, ".claude/skills"); + mkdirSync(skillsDir, { recursive: true }); + + // Create a symlink pointing to a non-existent file (simulating a dependency + // directory that hasn't been installed yet) + execFileSync("ln", ["-s", "../../vendor/package/.claude/skills/check", join(skillsDir, "check")], { + cwd: repoDir, + stdio: "pipe", + }); + + git(["add", ".claude/skills/check"]); + git(["commit", "-m", "add dangling symlink"]); + + // This should not throw ENOENT + expect(() => restoreConfigFromBase("main")).not.toThrow(); + + // Verify the symlink was preserved in .claude-pr/ + expect(existsRepoFile(".claude-pr/.claude/skills/check")).toBe(true); + }); + function git(args: string[]): string { return execFileSync("git", args, { cwd: repoDir,