Patch Tool
Reference for the patch default tool: apply_patch with V4A context-anchored diffs for multi-file atomic changes.
Patch Tool
Script: powers/patch.py
Dependencies: pydantic
Apply context-anchored diffs across multiple files in a single atomic operation. Inspired by the V4A diff format, the patch tool uses context lines to locate changes rather than fragile line numbers or exact string matching.
apply_patch
Apply a multi-file patch using context-anchored diffs. All operations are validated before any changes are written (atomic), so either the entire patch succeeds or nothing changes.
Input
| Field | Type | Default | Description |
|---|---|---|---|
patch | str | required | Multi-file patch in V4A-style format (see format below) |
dry_run | bool | false | Validate the patch without applying changes |
working_dir | str | "." | Base directory for resolving relative file paths |
Output
| Field | Type | Description |
|---|---|---|
ok | bool | true if all operations succeeded |
results | list | Array of per-file result objects |
files_modified | int | Number of files updated |
files_added | int | Number of new files created |
files_deleted | int | Number of files removed |
error | str | Error message on failure |
Each result object:
| Field | Type | Description |
|---|---|---|
file | str | File path |
operation | str | "add", "update", or "delete" |
ok | bool | Whether this operation succeeded |
error | str | Error message if failed |
Patch Format
The patch format supports three operations:
Add File
Create a new file with the given content. Each content line starts with +:
*** Add File: path/to/new_file.py
+#!/usr/bin/env python3
+"""New module."""
+
+def hello():
+ print("hello")Update File
Modify an existing file using context-anchored hunks. The @@ line identifies where in the file to apply changes. Lines prefixed with - are removed, lines prefixed with + are added:
*** Update File: path/to/existing.py
@@ def main():
- pass
+ print("hello")
+ return 0Multiple hunks can be applied to the same file:
*** Update File: src/config.py
@@ DEBUG = False
-DEBUG = False
+DEBUG = True
@@ DEFAULT_PORT = 8080
-DEFAULT_PORT = 8080
+DEFAULT_PORT = 3000Delete File
Remove an existing file:
*** Delete File: path/to/unwanted.pyMulti-File Example
A single patch can contain multiple file operations:
{
"patch": "*** Add File: src/utils/helpers.py\n+\"\"\"Helper utilities.\"\"\"\n+\n+def clamp(value, min_val, max_val):\n+ return max(min_val, min(value, max_val))\n\n*** Update File: src/main.py\n@@ import os\n-import os\n+import os\n+from utils.helpers import clamp\n\n*** Delete File: src/old_helpers.py",
"dry_run": false
}Examples
// Add a new file
{"patch": "*** Add File: hello.py\n+print('hello')\n"}
// Update an existing file
{"patch": "*** Update File: main.py\n@@ def main():\n- pass\n+ print('hello')\n"}
// Delete a file
{"patch": "*** Delete File: old_module.py"}
// Dry run (validate without applying)
{
"patch": "*** Update File: config.py\n@@ PORT = 8080\n-PORT = 8080\n+PORT = 3000\n",
"dry_run": true
}How Context Matching Works
The @@ line specifies a context anchor -- a line that exists in the file. The patch tool:
- Searches the file for a line matching the context (exact match, or stripped whitespace match)
- Verifies that the
-(remove) lines appear immediately after the context line - Replaces the remove lines with the
+(add) lines
If the context line is not found, or the remove lines don't match what's in the file, the entire patch fails (atomic behavior).
Atomicity
The patch tool operates in two phases:
- Validation phase: All operations are parsed and validated. For updates, the context anchors are located and remove lines are verified. No files are modified.
- Application phase: Only if all operations pass validation, changes are written to disk.
If any single operation fails validation, nothing is changed. This prevents partial updates that could leave the codebase in an inconsistent state.
Tips
- Use
dry_run: trueto validate a patch before applying it - Context lines should be unique enough to identify the correct location in the file
- The
-lines in an Update hunk must exactly match the file content (whitespace-tolerant) - The patch tool is ideal for making coordinated changes across multiple files
- Combine with
read_fileto inspect files before generating patches - When a hunk fails, the error message includes the context line that was not found, making it easy to diagnose