English | 中文文档
Universal, configurable Codex-style apply_patch extension for Pi (pi-coding-agent).
Designed for Claude, Gemini, DeepSeek, GPT, and custom API relays supporting standard tool calls.
- Universal Model Support: Compatible with all models in
~/.pi/agent/models.json(Claude, Gemini, DeepSeek, GPT). - Compact-Patch Steering: prompt guidelines instruct the model to keep 2-3 lines of context per hunk, anchor with
@@, and split large refactorings — reducing the risk of gateway timeouts during long generations. - Grammar-Constrained Sampling: the official Codex Lark grammar is attached to the tool; providers with OpenAI grammar-tool support decode structurally valid patches, while all other providers are unaffected.
- Line-Numbered Colored Diff UI: The CLI shows completed diffs by default, just like Pi's native
edittool, using the samerenderDifffor line colors and word-level highlighting. Each file retains its path and+X -Ycounters; streaming progress stays compact. - WebUI Preview Contract: Completed results also include
details.preview.files[]with per-file numbered diffs (filePath,operation,movePath,diff) for pi-web and other web clients. This is separate from the CLI renderer. - Live Streaming Progress: Real-time counter preview while the model streams patch arguments, memoized per tool call to stay cheap on long patches.
- Preflight and Rollback: All files are matched and computed before any write. Commit failures or cancellation restore modified contents, permissions, and symlinks, and remove empty directories created by the patch.
- Stream-Truncation Detection: a patch cut off before
*** End Patchis reported distinctly (with guidance to split smaller) instead of failing with a confusing hunk error. - Per-Line Ending Preservation: untouched lines keep their exact CRLF/LF/CR terminators — including a missing final newline — including context inside a hunk and lines between hunks; changed lines adopt the file's preferred (first) ending, mirroring Codex's
SourceFilesemantics. - EOF Blank-Context Tolerance: a trailing blank context line standing in for the file's final newline is retried without it, so trailing additions land in the right place.
- No Silent Relocation: when quoted context cannot be located, the patch fails with a diagnostic instead of quietly inserting the change somewhere else.
- Self-Healing Diagnostics: hunk failures include the closest match with line numbers plus concrete advice, helping the model re-read and correct the patch.
- Interactive TUI Configuration: Manage active providers and models with
/apply-patch. - Native Edit Protection: Automatically hides and blocks native
edit/writetools when active, and restores them when switching away. - Shell Detour Guidance: Recognizes command-position
apply_patch, common aliases, and wrappers, while allowing searches, quoted text, and heredoc documentation containing the tool name. - Resilient Arguments:
input/patch/diff/contentargument keys — and raw strings — are normalized before validation, absorbing per-provider quirks. - Path Sandbox: Restricts patch operations to the current workspace by default (
allowAbsolutePaths: false), including symlink-escape checks and rejection of dangling symlinks. - Forgiving Markers: Auto-corrects a mis-prefixed
+*** End Patchso the marker is never written into your file. - Tolerant File Headers:
*** Update File:and friends are recognized despite indentation, missing/extra spaces, extra asterisks, backticked paths, or a stray diff prefix — so a later file's hunks are never absorbed into the previous file. - Drift-Corrected Line Hints:
@@ -L,N @@hints from later hunks are rebased by the net size change of earlier hunks in the same file, then verified by content, so multi-hunk patches do not drift. - Comment-Tolerant Fallback: When every strict pass fails, a final pass anchors on real code and tolerates paraphrased or truncated doc comments — but only when the alignment is unique, and it reports any
-line it could not find instead of skipping it. - Standard JSON Tool Calling: Usable with proxies and aggregators that support standard JSON tool calls, without requiring Lark grammar support.
pi install git:github.com/LyraAgent/pi-apply-patch-universalReload Pi after installation:
/reload
Inside your Pi session, run:
/apply-patch
Use the arrow keys and spacebar to toggle active providers and individual models automatically discovered from ~/.pi/agent/models.json.
Settings are persisted in ~/.pi/agent/pi-apply-patch.json. You can also create or edit it manually:
{
"providers": ["cliproxy", "openai"],
"models": ["anthropic/claude-3-7-sonnet"],
"disableNativeEdit": true,
"allowAbsolutePaths": false,
"addFileOnExisting": "overwrite",
"moveOnExisting": "error"
}| Field | Type | Default | Description |
|---|---|---|---|
providers |
string[] |
[] |
Provider-level matching. All models under the specified provider IDs (e.g., "cliproxy", "openai", "aio") will automatically activate apply_patch. |
models |
string[] |
[] |
Model-level granular matching. Enables apply_patch for specific models. Accepts full reference provider/model_id (e.g., "openai/gpt-4o"), bare model_id, or provider:model_id. Useful for enabling patch mode only on top-tier coding models while keeping others on standard tools. |
disableNativeEdit |
boolean |
true |
Tool exclusivity policy. When true, hides and blocks built-in edit and write tools whenever apply_patch is active, compelling the LLM to use token-efficient diff patches and avoiding accidental full-file rewrites. Switching to non-target models automatically restores native tools. Set to false to keep all tools available concurrently. |
allowAbsolutePaths |
boolean |
false |
Path traversal sandbox. When false (recommended), strictly restricts all patch operations within the current working directory (cwd) to prevent accidental edits outside your project root. Set to true only if you explicitly need cross-directory patch operations. |
addFileOnExisting |
"overwrite" | "error" |
"overwrite" |
Add File collision policy. "overwrite" lets *** Add File: replace a file that already exists (common when a previous run left a half-written file behind); the resulting diff shows the replaced lines and rollback restores the original content if a later action in the same patch fails. "error" restores the strict Codex behavior and fails with guidance to use *** Update File: or *** Delete File: instead. |
moveOnExisting |
"error" | "overwrite" |
"error" |
Move destination collision policy. "error" refuses a *** Move to: whose destination already exists. "overwrite" replaces the destination file, matching upstream Codex semantics; directories still fail rather than being recursively removed. |
- A model is active if it matches any entry in
modelsOR its provider is inproviders. - If both
providersandmodelsare empty[], the extension remains completely inactive (safe default).
*** Begin Patch
*** Add File: src/hello.py
+def greet(name: str) -> str:
+ return f"Hello, {name}!"
*** Update File: src/main.py
@@ def main():
- print("old")
+ print(greet("world"))
*** Delete File: obsolete.txt
*** End Patch
*** Begin Patch and *** End Patch must stand alone on their own lines. A stray diff prefix (+*** End Patch) is auto-corrected instead of being written into the target file, but only when no correctly formatted end marker is present — file bodies that legitimately contain the marker text are preserved.
Missing end markers are rejected before any write, including move-only and delete-only patches. Within file bodies, +++ and --- are ordinary addition/removal lines, not Git file headers.
A semantic anchor @@ <existing complete line> must exist; subsequent matching cannot fall back before it. Hunks are located by content, not by trusting line numbers. @@ -L,N +L,N @@ is treated as a hint: it is rebased by the net line change of earlier hunks in the same file, searched outward from there, and then falls back to a full-file scan. A hunk that only matches after ignoring comment differences is applied only when that alignment is unique in the permitted search region; comment lines the patch omitted are preserved, and a - line that cannot be found is reported rather than silently skipped.
Line endings are preserved per line: untouched lines keep their exact terminators (including a missing final newline), while changed and inserted lines use the file's preferred (first) ending, and a changed final line always receives a terminator — matching Codex's behavior.
All file changes are prepared before any write. Inputs are checked again before committing each operation; failures or cancellation restore modified contents, permissions, and symlinks, and clean up newly created empty directories. Rollback failures are reported explicitly. This is in-process recovery, not crash recovery or transaction isolation against concurrent external writes.
Shell detour detection is model guidance, not a full shell interpreter or a security boundary.
Built upon and enhanced from WufeiHalf/pi-apply_patch and matsuzaka-yuki/pi-apply-patch-plus.
Special thanks to @WufeiHalf for the configurable architecture and interactive TUI settings design.
MIT License © 2026 LyraAgent, WufeiHalf