llm-tools.el

Opinionated tools for use by a (local) LLM
Log | Files | Refs | LICENSE

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:
Mllm-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)