Merge pull request #1593 from code-yeongyu/fix/prometheus-plan-overwrite
fix: allow Prometheus to overwrite .sisyphus/*.md plan files
This commit is contained in:
commit
6e2f3b1f50
@ -198,9 +198,108 @@ describe("createWriteExistingFileGuardHook", () => {
|
|||||||
//#when
|
//#when
|
||||||
const result = differentHook["tool.execute.before"]?.(input as any, output as any)
|
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 when .sisyphus is in parent path but not under ctx.directory", async () => {
|
||||||
|
//#given
|
||||||
|
const fakeSisyphusParent = path.join(os.tmpdir(), ".sisyphus", "evil-project")
|
||||||
|
fs.mkdirSync(fakeSisyphusParent, { recursive: true })
|
||||||
|
const evilFile = path.join(fakeSisyphusParent, "plan.md")
|
||||||
|
fs.writeFileSync(evilFile, "# Evil Plan")
|
||||||
|
const input = { tool: "Write", sessionID: "ses_1", callID: "call_1" }
|
||||||
|
const output = { args: { filePath: evilFile, content: "# Hacked" } }
|
||||||
|
|
||||||
|
//#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.")
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
fs.rmSync(path.join(os.tmpdir(), ".sisyphus"), { recursive: true, force: true })
|
||||||
|
})
|
||||||
|
|
||||||
|
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
|
//#then
|
||||||
await expect(result).rejects.toThrow("File already exists. Use edit tool instead.")
|
await expect(result).rejects.toThrow("File already exists. Use edit tool instead.")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import type { Hooks, PluginInput } from "@opencode-ai/plugin"
|
import type { Hooks, PluginInput } from "@opencode-ai/plugin"
|
||||||
import { existsSync } from "fs"
|
import { existsSync } from "fs"
|
||||||
import { resolve, isAbsolute } from "path"
|
import { resolve, isAbsolute, join, normalize, sep } from "path"
|
||||||
import { log } from "../../shared"
|
import { log } from "../../shared"
|
||||||
|
|
||||||
export function createWriteExistingFileGuardHook(ctx: PluginInput): Hooks {
|
export function createWriteExistingFileGuardHook(ctx: PluginInput): Hooks {
|
||||||
@ -17,9 +17,19 @@ export function createWriteExistingFileGuardHook(ctx: PluginInput): Hooks {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const resolvedPath = isAbsolute(filePath) ? filePath : resolve(ctx.directory, filePath)
|
const resolvedPath = normalize(isAbsolute(filePath) ? filePath : resolve(ctx.directory, filePath))
|
||||||
|
|
||||||
if (existsSync(resolvedPath)) {
|
if (existsSync(resolvedPath)) {
|
||||||
|
const sisyphusRoot = join(ctx.directory, ".sisyphus") + sep
|
||||||
|
const isSisyphusMarkdown = resolvedPath.startsWith(sisyphusRoot) && resolvedPath.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", {
|
log("[write-existing-file-guard] Blocking write to existing file", {
|
||||||
sessionID: input.sessionID,
|
sessionID: input.sessionID,
|
||||||
filePath,
|
filePath,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user