From e4fa6afffca77ee5e75bb1741bf9b0ec0b4a2002 Mon Sep 17 00:00:00 2001 From: can1357 Date: Wed, 4 Mar 2026 15:52:31 +0100 Subject: [PATCH] feat(prompts): next hashline prompt variant --- .../src/prompts/tools/hashline.md | 43 +++++++++---------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/packages/coding-agent/src/prompts/tools/hashline.md b/packages/coding-agent/src/prompts/tools/hashline.md index b4e3ebb7a..45557ecae 100644 --- a/packages/coding-agent/src/prompts/tools/hashline.md +++ b/packages/coding-agent/src/prompts/tools/hashline.md @@ -1,41 +1,40 @@ -Applies precise file edits using `LINE#ID` tags from `read` output. +Applies precise, surgical file edits by referencing `LINE#ID` tags from `read` output. Each tag uniquely identifies a line, so edits remain stable even when lines shift. -1. You **SHOULD** issue a `read` call before editing if you have no tagged context for a file. -2. You **MUST** pick the smallest operation per change site. -3. You **MUST** submit one `edit` call per file with all operations, think your changes through before submitting. +Follow these steps in order for every edit: +1. You **SHOULD** issue a `read` call before editing to get fresh `LINE#ID` tags. Editing without current tags causes mismatches because other edits or external changes may have shifted line numbers since your last read. +2. You **MUST** submit one `edit` call per file with all operations. Multiple calls to the same file require re-reading between each one (tags shift after each edit), so batching avoids wasted round-trips. Think your changes through before submitting. +3. You **MUST** pick the smallest operation per change site. Each operation should be one logical mutation — a single replace, insert, or delete. Combining unrelated changes into one operation makes errors harder to diagnose and recover from. **`path`** — the path to the file to edit. **`move`** — if set, move the file to the given path. **`delete`** — if true, delete the file. -**`edits.[n].pos`** — the anchor line. Meaning depends on `op`: +**`edits[n].pos`** — the anchor line. Meaning depends on `op`: - `replace`: start of range (or the single line to replace) - `prepend`: insert new lines **before** this line; omit for beginning of file - `append`: insert new lines **after** this line; omit for end of file -**`edits.[n].end`** — range replace only. The last line of the range (inclusive). Omit for single-line replace. -**`edits.[n].lines`** — the replacement content: +**`edits[n].end`** — range replace only. The last line of the range (inclusive). Omit for single-line replace. +**`edits[n].lines`** — the replacement content: - `["line1", "line2"]` — replace with these lines (array of strings) - `"line1"` — shorthand for `["line1"]` (single-line replace) - `[""]` — replace content with a blank line (line preserved, content cleared) - `null` or `[]` — **delete** the line(s) entirely -Tags should be referenced from the last `read` output. -Edits are applied bottom-up, so earlier tags stay valid even when later ops add or remove lines. +Tags are applied bottom-up: later edits (by position) are applied first, so earlier tags remain valid even when subsequent ops add or remove lines. Tags **MUST** be referenced from the most recent `read` output. -1. **Minimize scope:** You **MUST** use one logical mutation per operation. -2. **Prefer insertion over neighbor rewrites:** You **SHOULD** anchor on structural boundaries (`}`, `]`, `},`), not interior lines. -3. **Range end tag (inclusive):** `end` is inclusive and **MUST** point to the final line being replaced. - - If `lines` includes a closing boundary token (`}`, `]`, `)`, `);`, `},`), `end` **MUST** include the original boundary line. - - You **MUST NOT** set `end` to an interior line and then re-add the boundary token in `lines`; that duplicates the next surviving line. +1. **Anchor on unique, structural lines.** You **SHOULD** choose anchors like function signatures, class declarations, or distinct statements — lines that appear exactly once. Blank lines, `}`, and `return null;` repeat throughout a file; anchoring on them risks matching the wrong location. When inserting between blocks, anchor on the nearest unique declaration using `prepend` or `append`. +2. **Prefer insertion over neighbor rewrites.** You **SHOULD** use `prepend`/`append` anchored on a structural boundary (`}`, `]`, `},`) rather than replacing adjacent lines when adding code near existing code. This keeps the edit minimal and avoids accidentally rewriting lines that should stay. +3. **Include boundary lines in the replaced range.** `end` is inclusive and **MUST** point to the final line being replaced. If your replacement `lines` include a closing token (`}`, `]`, `)`, `);`, `},`), `end` **MUST** include the original closing line. Otherwise the original closer survives and you get a duplicate. -**Tag mismatch (`>>>`):** You **MUST** retry using fresh tags from the error snippet. If snippet lacks context, or if you repeatedly fail, you **MUST** re-read the file and issue less ambitious edits, i.e. single op. -**No-op (`identical`):** You **MUST NOT** resubmit. Re-read target lines and adjust the edit. +Edits can fail in two ways. Here is exactly what to do for each: +1. **Tag mismatch (`>>>`):** The file changed since your last read, so the tag no longer matches. You **MUST** retry using the fresh tags from the error snippet. If the snippet lacks enough context, or if you fail repeatedly, you **MUST** re-read the entire file and submit a simpler, single-op edit. +2. **No-op (`identical`):** Your replacement is identical to the existing content — nothing changed. You **MUST NOT** resubmit the same edit. Re-read the target lines to understand what is actually there, then adjust your edit. @@ -121,6 +120,7 @@ Range — add `end`: +This example demonstrates why `end` must include the original closing line when your replacement also contains that closer. ```ts {{hlinefull 70 "if (ok) {"}} {{hlinefull 71 " run();"}} @@ -159,7 +159,6 @@ Good — include original `}` in the replaced range when replacement keeps `}`: }] } ``` -Also apply the same rule to `);`, `],`, and `},` closers: if replacement includes the closer token, `end` must include the original closer line. @@ -191,7 +190,7 @@ Use a trailing `""` to preserve the blank line between top-level sibling declara -Blank lines and repeated patterns (`}`, `return null;`) appear many times — never anchor on them when a unique line exists nearby. +Blank lines and repeated patterns (`}`, `return null;`) appear many times. Always anchor on a unique line nearby instead. ```ts {{hlinefull 101 "}"}} {{hlinefull 102 ""}} @@ -233,8 +232,8 @@ Good — anchor on the unique declaration line: - Edit payload: `{ path, edits[] }`. Each entry: `op`, `lines`, optional `pos`/`end`. No extra keys. -- Every tag **MUST** be copied exactly from fresh tool result as `N#ID`. -- You **MUST** re-read after each edit call before issuing another on same file. -- Formatting is a batch operation. You **MUST NOT** use this tool to reformat, reindent, or adjust whitespace — run the project's formatter instead. If the only change is whitespace, it is formatting; do not touch it. -- `lines` entries **MUST** be literal file content with indentation copied exactly from the `read` output. If the file uses tabs, use `\t` in JSON (a real tab character) — you **MUST NOT** use `\\t` (two characters: backslash + t), which produces the literal string `\t` in the file. +- Every tag **MUST** be copied exactly from your most recent `read` output as `N#ID`. Stale or mistyped tags cause mismatches. +- You **MUST** re-read the file after each edit call before issuing another on the same file. Tags shift after every edit, so reusing old tags produces mismatches. +- You **MUST NOT** use this tool to reformat, reindent, or adjust whitespace — run the project's formatter instead. If the only difference is whitespace, it is formatting; leave it alone. +- `lines` entries **MUST** be literal file content with indentation copied exactly from the `read` output. If the file uses tabs, use `\t` in JSON (a real tab character). Using `\\t` (backslash + t) writes the literal two-character string `\t` into the file. \ No newline at end of file