Skip to content

Commit 5e65cf2

Browse files
committed
improve: add context lines and line-shift warning to replace_lines response
1 parent 6b0615b commit 5e65cf2

2 files changed

Lines changed: 33 additions & 1 deletion

File tree

src/server.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,12 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
809809
- Delete lines 5-8: startLine=5, endLine=8, newContent=""
810810
- Insert after line 3: Use edit_block or write_file instead
811811
812+
WARNING - LINE NUMBER SHIFTING:
813+
After every replace_lines call where newContent has a different number of
814+
lines than the replaced range, ALL subsequent line numbers shift.
815+
ALWAYS re-read the file before making another replace_lines call on the
816+
same file. The response includes context lines to verify correctness.
817+
812818
IMPORTANT: Line numbers must match the read_file output exactly.
813819
If the file has been modified since the last read_file, re-read first.
814820

src/tools/edit.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -494,16 +494,42 @@ export async function handleReplaceLines(args: unknown): Promise<ServerResult> {
494494

495495
const removedCount = parsed.endLine - parsed.startLine + 1;
496496
const insertedCount = newLines.length;
497+
const lineDelta = insertedCount - removedCount;
498+
497499
capture('server_replace_lines', {
498500
fileExtension: path.extname(parsed.path).toLowerCase(),
499501
removedLines: removedCount,
500502
insertedLines: insertedCount,
501503
});
502504

505+
// Build response with context around the replacement
506+
const CONTEXT_LINES = 3;
507+
const newTotalLines = result.length;
508+
const insertStart = parsed.startLine; // 1-based start of new content
509+
const insertEnd = parsed.startLine + insertedCount - 1; // 1-based end of new content
510+
511+
const ctxStart = Math.max(0, insertStart - 1 - CONTEXT_LINES); // 0-based
512+
const ctxEnd = Math.min(newTotalLines, insertEnd + CONTEXT_LINES); // 0-based exclusive
513+
const contextSlice = result.slice(ctxStart, ctxEnd);
514+
const contextOutput = contextSlice.map((line, i) => {
515+
const lineNum = ctxStart + i + 1; // 1-based
516+
const marker = (lineNum >= insertStart && lineNum <= insertEnd) ? '+' : ' ';
517+
return `${marker} ${String(lineNum).padStart(4)} ${line}`;
518+
}).join('\n');
519+
520+
let msg = `Replaced lines ${parsed.startLine}-${parsed.endLine} (${removedCount} lines) with ${insertedCount} lines in ${parsed.path}`;
521+
522+
if (lineDelta !== 0) {
523+
msg += `\n\nWARNING: Line count changed by ${lineDelta > 0 ? '+' : ''}${lineDelta}. All line numbers after line ${insertEnd} have shifted. Re-read before further edits.`;
524+
}
525+
526+
msg += `\n\nContext (lines ${ctxStart + 1}-${ctxEnd}, + = new content):\n${contextOutput}`;
527+
503528
return {
504529
content: [{
505530
type: "text",
506-
text: `Replaced lines ${parsed.startLine}-${parsed.endLine} (${removedCount} lines) with ${insertedCount} lines in ${parsed.path}`
531+
text: msg
507532
}],
508533
};
509534
}
535+

0 commit comments

Comments
 (0)