AST-safe edit primitives
The plain Edit tool is string-replace: it works most of the time but has a long tail of whitespace / brace / trailing-comma failures. The diff looks right, the compile fails, the agent burns turns figuring out which comma got eaten.
The three AST-safe edit tools side-step that class of failure by parsing the file, locating the target by name, and splicing at byte-offsets derived from the AST. If the replacement can’t possibly land cleanly, the tool refuses up front — nothing gets written.
All three ship in v1 for Go only. The underlying internal/ast package uses a pluggable Language registry, so Python / TypeScript / Rust adapters can land later without touching tool handlers.
Common contract
Section titled “Common contract”Every AST-safe edit tool:
- Resolves
file_paththrough path containment. - Requires the file to have been Read in the current session (same gate as Edit).
- Parses the file before attempting any edit; returns
parse error: …if the source isn’t valid Go. - Re-parses the replacement in context; returns
replacement did not parse: …if it doesn’t. - Writes atomically via the shared
atomicWritehelper. - Returns a structured result:
<tool>: modified <path> at line <N>plus a unified-diff-style dump of the touched region only (not the whole file).
Errors are returned as tool-level error results; none of these panic.
Naming functions and methods
Section titled “Naming functions and methods”function_name accepts two shapes:
"Foo"— top-level function."(*T).Foo"or"(T).Foo"— method on typeT. Pointer vs value receiver is ignored for lookup:(*T).Foomatches methods declared with eitherfunc (t *T) Fooorfunc (t T) Foo.
Ambiguous names — two functions with the same name in the same file — are rejected with an error listing the matches:
ambiguous: Greet matches 2 declarations at line 12, line 27edit_function_body
Section titled “edit_function_body”Replace the body of a function or method. The signature and any leading doc comment are left untouched.
| Param | Type | Required | Notes |
|---|---|---|---|
file_path | string | yes | Absolute or workspace-relative path to a .go file. |
function_name | string | yes | "Foo" or "(*T).Foo". |
new_body | string | yes | The bytes that replace the content inside the braces. The tool writes the braces itself. |
Example. Replace Greet’s body:
{ "file_path": "greet.go", "function_name": "Greet", "new_body": "\n\treturn \"hi, \" + name + \"!\"\n"}Prefer edit_function_body over Edit any time you’re rewriting a whole function body. You can’t accidentally eat a trailing comma, leave an unbalanced brace, or split a multi-line receiver clause.
insert_after_method
Section titled “insert_after_method”Insert a new method as a peer immediately after the named anchor method.
| Param | Type | Required | Notes |
|---|---|---|---|
file_path | string | yes | Path to a .go file. |
receiver_type | string | yes | The type the anchor method is on. "T" or "*T" — both are treated as “methods on this type”. |
method_name | string | yes | The anchor method the new method should follow. |
new_method | string | yes | The complete new method declaration, including func, receiver, signature, and body. |
The tool preserves indentation by detecting the anchor method’s leading whitespace and re-applying it to every non-blank line of new_method. If the anchor isn’t found, the error lists the methods that do exist on the receiver so you can recover without re-reading the file:
method Stop not found on *Server in server.go (available: (*Server).Run, (*Server).Close)change_function_signature
Section titled “change_function_signature”Rewrite the declaration line of a function or method; leave the body verbatim.
| Param | Type | Required | Notes |
|---|---|---|---|
file_path | string | yes | Path to a .go file. |
function_name | string | yes | "Foo" or "(*T).Foo". |
new_signature | string | yes | The complete replacement declaration up to (but not including) the opening {. |
Example. Add a context.Context parameter:
{ "file_path": "server.go", "function_name": "(*Server).Run", "new_signature": "func (s *Server) Run(ctx context.Context, x int) error"}new_signature is validated by appending an empty body ({}) and parsing the synthetic declaration — a typo in parameters, a missing func keyword, or a malformed return list is caught before the file is touched.
When to prefer which tool
Section titled “When to prefer which tool”| Change | Tool |
|---|---|
| Rewrite the entire body of a function you identified by name | edit_function_body |
| Add a new method next to an existing one | insert_after_method |
Add / remove / rename a parameter, change return type, add context.Context | change_function_signature |
| Tweak one line inside a function | Edit (string replace) |
| Whole-file replacement | Write |
The AST tools require parsing the file, which is linear in file size but measured in milliseconds for any plausible sandbox file. For single-token edits the overhead is not worth the parse.
Language support
Section titled “Language support”v1 supports Go only. Language detection is by file extension: .go files route to the Go adapter; any other extension is rejected with no AST support for <path> (known extensions: .go).
See Language support model for the registry pattern and how future language adapters slot in.
Limitations
Section titled “Limitations”- Go only in v1. Python / TypeScript / Rust will ship as follow-on issues.
- File-scoped lookup.
edit_function_bodysearches the target file only; cross-file refactors are out of scope. - Ambiguous names fail. Two functions with the same name in the same file is a compile error in Go anyway, but two methods on different receivers with the same name do legitimately coexist; disambiguate with the
(*Receiver).Methodform. - Doc comments are preserved, not rewritten. If you want to rewrite the doc comment,
Editthe comment separately or supply a new doc-comment-inclusive method toinsert_after_method. - No formatting. The tool writes exactly the bytes you give it. Run run_lint or your project’s formatter afterward if you want the file gofmt’d.
Related
Section titled “Related”- Edit — string-replace fallback for one-line tweaks.
- Language support model — how new languages land.
- Path containment — the workspace boundary every filesystem tool enforces.