From 9390f98f0133d1592f084521545ecb50cbdae43e Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Sun, 22 Feb 2026 14:46:59 +0900 Subject: [PATCH] fix(hashline-edit): integrate continuation/merge helpers into expand logic and strengthen tool description - maybeExpandSingleLineMerge now uses stripTrailingContinuationTokens and stripMergeOperatorChars as fallback matching strategies - Add 'refs interpreted against last read' atomicity clause to tool description - Add 'output tool calls only; no prose' rule to tool description --- .../autocorrect-replacement-lines.ts | 16 +++++++++++++++- src/tools/hashline-edit/tool-description.ts | 3 ++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/tools/hashline-edit/autocorrect-replacement-lines.ts b/src/tools/hashline-edit/autocorrect-replacement-lines.ts index 66754531e..8239f4bc1 100644 --- a/src/tools/hashline-edit/autocorrect-replacement-lines.ts +++ b/src/tools/hashline-edit/autocorrect-replacement-lines.ts @@ -77,7 +77,21 @@ export function maybeExpandSingleLineMerge( let offset = 0 let orderedMatch = true for (const part of parts) { - const idx = merged.indexOf(part, offset) + let idx = merged.indexOf(part, offset) + if (idx === -1) { + const stripped = stripTrailingContinuationTokens(part) + if (stripped !== part) { + idx = merged.indexOf(stripped, offset) + } + } + if (idx === -1) { + const mergeStripped = stripMergeOperatorChars(merged.slice(offset)) + const partStripped = stripMergeOperatorChars(part) + const fuzzyIdx = mergeStripped.indexOf(partStripped) + if (fuzzyIdx !== -1) { + idx = offset + fuzzyIdx + } + } if (idx === -1) { orderedMatch = false break diff --git a/src/tools/hashline-edit/tool-description.ts b/src/tools/hashline-edit/tool-description.ts index edd29777f..d255ea8ce 100644 --- a/src/tools/hashline-edit/tool-description.ts +++ b/src/tools/hashline-edit/tool-description.ts @@ -11,7 +11,7 @@ VALIDATION: Payload shape: { "filePath": string, "edits": [...], "delete"?: boolean, "rename"?: string } Each edit must be one of: set_line, replace_lines, insert_after, insert_before, insert_between, replace, append, prepend text/new_text must contain plain replacement text only (no LINE#ID prefixes, no diff + markers) - CRITICAL: all operations validate against the same pre-edit file snapshot and apply bottom-up. + CRITICAL: all operations validate against the same pre-edit file snapshot and apply bottom-up. Refs/tags are interpreted against the last-read version of the file. LINE#ID FORMAT (CRITICAL): Each line reference must be in "LINE#ID" format where: @@ -48,6 +48,7 @@ RULES (CRITICAL): 5. Touch only requested code: avoid incidental edits. 6. Use exact current tokens: NEVER rewrite approximately. 7. For swaps/moves: prefer one range operation over multiple single-line operations. + 8. Output tool calls only; no prose or commentary between them. TAG CHOICE (ALWAYS): - Copy tags exactly from read output or >>> mismatch output.