feat(hashline-edit): add anchor insert modes and strict insert validation

This commit is contained in:
YeonGyu-Kim
2026-02-22 03:38:04 +09:00
parent f7c5c0be35
commit a39f183c31
13 changed files with 888 additions and 186 deletions
+88 -4
View File
@@ -11,12 +11,10 @@ function createMockContext(): ToolContext {
sessionID: "test",
messageID: "test",
agent: "test",
directory: "/tmp",
worktree: "/tmp",
abort: new AbortController().signal,
metadata: mock(() => {}),
ask: async () => {},
}
} as unknown as ToolContext
}
describe("createHashlineEditTool", () => {
@@ -103,7 +101,7 @@ describe("createHashlineEditTool", () => {
//#then
expect(result).toContain("Error")
expect(result).toContain("hash")
expect(result).toContain(">>>")
})
it("preserves literal backslash-n and supports string[] payload", async () => {
@@ -132,4 +130,90 @@ describe("createHashlineEditTool", () => {
//#then
expect(fs.readFileSync(filePath, "utf-8")).toBe("join(\\n)\na\nb\nline2")
})
it("supports insert_before and insert_between", async () => {
//#given
const filePath = path.join(tempDir, "test.txt")
fs.writeFileSync(filePath, "line1\nline2\nline3")
const line1 = computeLineHash(1, "line1")
const line2 = computeLineHash(2, "line2")
const line3 = computeLineHash(3, "line3")
//#when
await tool.execute(
{
filePath,
edits: [
{ type: "insert_before", line: `3#${line3}`, text: ["before3"] },
{ type: "insert_between", after_line: `1#${line1}`, before_line: `2#${line2}`, text: ["between"] },
],
},
createMockContext(),
)
//#then
expect(fs.readFileSync(filePath, "utf-8")).toBe("line1\nbetween\nline2\nbefore3\nline3")
})
it("returns error when insert text is empty array", async () => {
//#given
const filePath = path.join(tempDir, "test.txt")
fs.writeFileSync(filePath, "line1\nline2")
const line1 = computeLineHash(1, "line1")
//#when
const result = await tool.execute(
{
filePath,
edits: [{ type: "insert_after", line: `1#${line1}`, text: [] }],
},
createMockContext(),
)
//#then
expect(result).toContain("Error")
expect(result).toContain("non-empty")
})
it("supports file rename with edits", async () => {
//#given
const filePath = path.join(tempDir, "source.txt")
const renamedPath = path.join(tempDir, "renamed.txt")
fs.writeFileSync(filePath, "line1\nline2")
const line2 = computeLineHash(2, "line2")
//#when
await tool.execute(
{
filePath,
rename: renamedPath,
edits: [{ type: "set_line", line: `2#${line2}`, text: "line2-updated" }],
},
createMockContext(),
)
//#then
expect(fs.existsSync(filePath)).toBe(false)
expect(fs.readFileSync(renamedPath, "utf-8")).toBe("line1\nline2-updated")
})
it("supports file delete mode", async () => {
//#given
const filePath = path.join(tempDir, "delete-me.txt")
fs.writeFileSync(filePath, "line1")
//#when
const result = await tool.execute(
{
filePath,
delete: true,
edits: [],
},
createMockContext(),
)
//#then
expect(fs.existsSync(filePath)).toBe(false)
expect(result).toContain("Successfully deleted")
})
})