This commit is contained in:
Rohit 2026-09-26 21:24:12 +05:30 • committed by GitHub
commit 2deb36a23a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 83 additions and 7 deletions

View file

@ -180,6 +180,80 @@ describe("ClaudeMemoryTool exact-file matching", () => {
})
})
describe("ClaudeMemoryTool insert line semantics", () => {
let tool: ClaudeMemoryTool
beforeEach(() => {
searchExecute.mockReset()
addMock.mockReset()
mockDocument(FILE_CONTENT)
tool = new ClaudeMemoryTool("test-api-key")
})
// The memory_20250818 spec: insert_text is inserted AFTER line insert_line,
// 0 inserts at the beginning of the file, and the valid range is [0, n_lines].
it("insert_line: 0 inserts at the beginning of the file", async () => {
const result = await tool.handleCommand({
command: "insert",
path: FILE_PATH,
insert_line: 0,
insert_text: "header",
})
expect(result.success).toBe(true)
const stored = addMock.mock.calls[0]?.[0]?.content as string
expect(stored).toBe("header\nline1\nline2\nline3\nline4\nline5")
})
it("inserts AFTER the given line, not before it", async () => {
const result = await tool.handleCommand({
command: "insert",
path: FILE_PATH,
insert_line: 2,
insert_text: "after2",
})
expect(result.success).toBe(true)
const stored = addMock.mock.calls[0]?.[0]?.content as string
// Regression guard: the old 1-based insert-BEFORE landed this one line early.
expect(stored).toBe("line1\nline2\nafter2\nline3\nline4\nline5")
})
it("insert_line: n_lines appends at the end of the file", async () => {
const result = await tool.handleCommand({
command: "insert",
path: FILE_PATH,
insert_line: 5,
insert_text: "tail",
})
expect(result.success).toBe(true)
const stored = addMock.mock.calls[0]?.[0]?.content as string
expect(stored).toBe("line1\nline2\nline3\nline4\nline5\ntail")
})
it("rejects insert_line outside [0, n_lines] without writing", async () => {
const below = await tool.handleCommand({
command: "insert",
path: FILE_PATH,
insert_line: -1,
insert_text: "x",
})
expect(below.success).toBe(false)
expect(below.error).toContain("[0, 5]")
const above = await tool.handleCommand({
command: "insert",
path: FILE_PATH,
insert_line: 6,
insert_text: "x",
})
expect(above.success).toBe(false)
expect(addMock).not.toHaveBeenCalled()
})
})
describe("ClaudeMemoryTool str_replace replacement literalness", () => {
let tool: ClaudeMemoryTool

View file

@ -490,16 +490,18 @@ export class ClaudeMemoryTool {
const originalContent = readResult.document.content
const lines = originalContent.split("\n")
// Validate line number
if (insertLine < 1 || insertLine > lines.length + 1) {
// Validate line number. Per the memory_20250818 spec the valid range
// is [0, n_lines]: text is inserted AFTER `insert_line`, and 0 means
// the beginning of the file.
if (insertLine < 0 || insertLine > lines.length) {
return {
success: false,
error: `Invalid line number: ${insertLine}. File has ${lines.length} lines.`,
error: `Invalid insert_line parameter: ${insertLine}. It should be within the range of lines of the file: [0, ${lines.length}]`,
}
}
// Insert the text (insertLine is 1-based)
lines.splice(insertLine - 1, 0, insertText)
// Insert the text after line `insertLine` (0-based insert-after)
lines.splice(insertLine, 0, insertText)
const newContent = lines.join("\n")
// Update the document
@ -517,7 +519,7 @@ export class ClaudeMemoryTool {
return {
success: true,
content: `Text inserted at line ${insertLine} in file: ${filePath}`,
content: `Text inserted after line ${insertLine} in file: ${filePath}`,
}
} catch (error) {
return {

View file

@ -309,7 +309,7 @@ describe("ClaudeMemoryTool", () => {
const result = await tool.handleCommand({
command: "insert",
path: FILE_PATH,
insert_line: 2,
insert_line: 1,
insert_text: "",
})