From 847d9941999f424be47efb3321f0b447e926ec72 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Sat, 7 Feb 2026 13:12:44 +0900 Subject: [PATCH] fix: allow Prometheus to overwrite .sisyphus/*.md plan files Add exception in write-existing-file-guard for .sisyphus/*.md files so Prometheus can rewrite plan files without being blocked by the guard. The prometheus-md-only hook (which runs later) still validates that only Prometheus can write to these paths, preserving security. Fixes #1576 --- .../write-existing-file-guard/index.test.ts | 82 ++++++++++++++++++- src/hooks/write-existing-file-guard/index.ts | 9 ++ 2 files changed, 90 insertions(+), 1 deletion(-) diff --git a/src/hooks/write-existing-file-guard/index.test.ts b/src/hooks/write-existing-file-guard/index.test.ts index 3fd2ddd47..f9654b64f 100644 --- a/src/hooks/write-existing-file-guard/index.test.ts +++ b/src/hooks/write-existing-file-guard/index.test.ts @@ -198,9 +198,89 @@ describe("createWriteExistingFileGuardHook", () => { //#when const result = differentHook["tool.execute.before"]?.(input as any, output as any) + //#then + await expect(result).rejects.toThrow("File already exists. Use edit tool instead.") + }) + + describe(".sisyphus/*.md exception", () => { + test("allows write to existing .sisyphus/plans/plan.md", async () => { + //#given + const sisyphusDir = path.join(tempDir, ".sisyphus", "plans") + fs.mkdirSync(sisyphusDir, { recursive: true }) + const planFile = path.join(sisyphusDir, "plan.md") + fs.writeFileSync(planFile, "# Existing Plan") + const input = { tool: "Write", sessionID: "ses_1", callID: "call_1" } + const output = { args: { filePath: planFile, content: "# Updated Plan" } } + + //#when + const result = hook["tool.execute.before"]?.(input as any, output as any) + + //#then + await expect(result).resolves.toBeUndefined() + }) + + test("allows write to existing .sisyphus/notes.md", async () => { + //#given + const sisyphusDir = path.join(tempDir, ".sisyphus") + fs.mkdirSync(sisyphusDir, { recursive: true }) + const notesFile = path.join(sisyphusDir, "notes.md") + fs.writeFileSync(notesFile, "# Notes") + const input = { tool: "Write", sessionID: "ses_1", callID: "call_1" } + const output = { args: { filePath: notesFile, content: "# Updated Notes" } } + + //#when + const result = hook["tool.execute.before"]?.(input as any, output as any) + + //#then + await expect(result).resolves.toBeUndefined() + }) + + test("allows write to existing .sisyphus/*.md using relative path", async () => { + //#given + const sisyphusDir = path.join(tempDir, ".sisyphus") + fs.mkdirSync(sisyphusDir, { recursive: true }) + const planFile = path.join(sisyphusDir, "plan.md") + fs.writeFileSync(planFile, "# Plan") + const input = { tool: "Write", sessionID: "ses_1", callID: "call_1" } + const output = { args: { filePath: ".sisyphus/plan.md", content: "# Updated" } } + + //#when + const result = hook["tool.execute.before"]?.(input as any, output as any) + + //#then + await expect(result).resolves.toBeUndefined() + }) + + test("blocks write to existing .sisyphus/file.txt (non-markdown)", async () => { + //#given + const sisyphusDir = path.join(tempDir, ".sisyphus") + fs.mkdirSync(sisyphusDir, { recursive: true }) + const textFile = path.join(sisyphusDir, "file.txt") + fs.writeFileSync(textFile, "content") + const input = { tool: "Write", sessionID: "ses_1", callID: "call_1" } + const output = { args: { filePath: textFile, content: "new content" } } + + //#when + const result = hook["tool.execute.before"]?.(input as any, output as any) + + //#then + await expect(result).rejects.toThrow("File already exists. Use edit tool instead.") + }) + + test("blocks write to existing regular file (not in .sisyphus)", async () => { + //#given + const regularFile = path.join(tempDir, "regular.md") + fs.writeFileSync(regularFile, "# Regular") + const input = { tool: "Write", sessionID: "ses_1", callID: "call_1" } + const output = { args: { filePath: regularFile, content: "# Updated" } } + + //#when + const result = hook["tool.execute.before"]?.(input as any, output as any) + //#then await expect(result).rejects.toThrow("File already exists. Use edit tool instead.") }) }) - }) + }) +}) }) diff --git a/src/hooks/write-existing-file-guard/index.ts b/src/hooks/write-existing-file-guard/index.ts index 806cf1f45..f3a522409 100644 --- a/src/hooks/write-existing-file-guard/index.ts +++ b/src/hooks/write-existing-file-guard/index.ts @@ -20,6 +20,15 @@ export function createWriteExistingFileGuardHook(ctx: PluginInput): Hooks { const resolvedPath = isAbsolute(filePath) ? filePath : resolve(ctx.directory, filePath) if (existsSync(resolvedPath)) { + const isSisyphusMarkdown = resolvedPath.replace(/\\/g, "/").includes("/.sisyphus/") && filePath.endsWith(".md") + if (isSisyphusMarkdown) { + log("[write-existing-file-guard] Allowing .sisyphus/*.md overwrite", { + sessionID: input.sessionID, + filePath, + }) + return + } + log("[write-existing-file-guard] Blocking write to existing file", { sessionID: input.sessionID, filePath,