commit 62336cfba249c9bdd781d0789cc041f02f2aef0b
parent b4464b16c87f3b4a39584aa5faf66f2f9fee8be2
Author: Vineet Kumar <git@vineetk.net>
Date: Mon, 25 May 2026 23:46:24 -0400
final tool, edit_file, added
this took way too long.
Diffstat:
| M | llm-tools.el | | | 144 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- |
1 file changed, 143 insertions(+), 1 deletion(-)
diff --git a/llm-tools.el b/llm-tools.el
@@ -91,7 +91,149 @@ final file."))
:confirm t
:include t)
-;; TODO edit_file (hashline + patch apply)
+;; edit_file (hl-edit)
+(gptel-make-tool
+ :name "edit_file"
+ :function #'llm-tools--hl-edit
+ :description "\
+Edit an existing file surgically using the hashline patch
+format. Call `read_file` first to obtain current content and valid
+line anchors. Use `write_file` instead for creating new files or
+completely replacing content. Returns a success message if the patch
+applied, or an error string describing what went wrong (structural
+issues or stale anchors)."
+ :args '((:name "patch"
+ :type string
+ :description "\
+A hashline patch string containing one or more file sections. Each
+section begins with `@@ PATH` on its own line, followed by operations
+that reference lines by their anchor (line number + 2-character hash,
+e.g. `5ff`). Copy anchors verbatim from `read_file` output; never
+fabricate or modify them.
+
+CRITICAL: This is NOT a unified diff. Do NOT use `---`, `+++`, `@@ -1,3
++1,4 @@`, `-old`, `+new`, or any unified diff syntax. The header is
+`@@ PATH`; operations use `<`, `+`, `-`, `=`. This format is purely
+textual -- the tool has NO awareness of language, indentation,
+brackets, fences, or table widths. You must emit valid syntax in
+replacements and insertions.
+
+Operations:
+ + ANCHOR Insert lines AFTER the anchored line (or `EOF` to append)
+ < ANCHOR Insert lines BEFORE the anchored line (or `BOF` to prepend)
+ - A..B Delete the inclusive line range A through B
+ = A..B Replace the inclusive range A through B with payload lines
+
+Payload lines follow the operation line and must start with `~`
+immediately followed by content -- no space after `~`. Every character
+after `~` is verbatim file content. Op lines carry no content:
+
+WRONG: + 5pg some code
+RIGHT:
++ 5pg
+~some code
+
+One operation can have many payload lines. To insert N consecutive
+lines, write ONE op followed by N `~` lines:
+
+WRONG (one op per line, with fabricated anchors):
+ + 5pg
+ ~first new line
+ + 6xx <- FABRICATED
+ ~second new line
+
+RIGHT (one op, many payload lines):
+ + 5pg
+ ~first new line
+ ~second new line
+
+Rules:
+- Payload is ONLY what is NEW relative to your range. Do not repeat
+ existing lines or duplicate nearby content.
+- Anchors reference the file as last read via `read_file`. Do not shift
+ line numbers for prior operations in the same patch.
+- Prefer narrow operations (`+`, `-`) over wide replacements (`=`).
+ Two narrow ops beat one wide `=`.
+- When editing a multiline construct, widen to the whole construct
+ rather than bisecting it.
+- `= A..B` deletes the range; payload is what is written. Before using
+ `=`, mentally delete A..B. If that splits an unclosed brace or
+ orphans a closer, you are bisecting a construct -- widen the range.
+- Every payload line must start with `~`. Raw content without `~` is
+ invalid.
+- Comments (lines starting with `#`) and blank lines are ignored.
+
+Examples:
+
+Replace a single line:
+@@ greeting.el
+= 2in..2in
+~(defconst greeting-title \"Mrs\")
+
+Replace a multiline block (widen to self-contained boundary):
+@@ greeting.el
+= 4ei..7be
+~ (concat
+~ greeting-title
+~ (or (string-trim name) \"guest\"))
+~ )
+
+Insert after:
+@@ greeting.el
++ 4ei
+~ greeting-title
+
+Insert before:
+@@ greeting.el
+< 5ff
+~ greeting-title
+
+Append to end of file:
+@@ greeting.el
++ EOF
+~(provide 'greeting)
+
+Prepend to beginning of file:
+@@ greeting.el
+< BOF
+~;; greeting.el -- utilities
+
+Delete a single line:
+@@ greeting.el
+- 5ff..5ff
+
+Blank a line (replace with empty):
+@@ greeting.el
+= 5ff..5ff
+
+Multiple operations in one section:
+@@ greeting.el
++ 1vx
+~(defconst DEBUG nil)
+- 5ff..5ff
+
+Multiple files in one patch:
+@@ a.el
++ 1vx
+~(require 'b)
+@@ b.el
+= 2in..2in
+~(defconst VALUE 42)
+
+Insert multiple lines with one op:
+@@ greeting.el
++ 1vx
+~(defconst GREETING \"Hello\")
+~(defconst FAREWELL \"Goodbye\")
+
+Replace one line with multiple lines:
+@@ greeting.el
+= 1vx..1vx
+~(defconst TITLE \"Dr\")
+~(defconst SUBTITLE \"Assistant\")"))
+ :async nil
+ :confirm t
+ :include t)
;; bash (make-process, taken from gptel-agent)
(defun llm-tools--execute-bash (callback command)