pi-patcher
v1.1.0
Published
Update-resilient self-healing patches for pi installs.
Downloads
308
Maintainers
Readme
pi-patcher
Self-healing patches for pi.
Want to modify pi but your changes require a patch? Use pi-patcher to keep source patches applied across every pi update. When patching fails due to updates to target files, pi-patcher uses pi to self-heal: the AI rewrites the patch file for the new code, and pi-patcher proves the rewrite by applying it mechanically.
The idea
Every time pi updates, your local patches break. The old story is: re-derive the patch, re-apply, repeat forever.
pi-patcher flips that. Each patch is a tiny oldText → newText spec plus a plain-English intent.md. On every pi update:
- If the spec still applies cleanly, it's re-applied. Done.
- If it drifted, pi-patcher hands the
PATCH.mdand the new target file to pi, and pi rewrites the patch's edit blocks for the current code. - pi-patcher reverts any scratch edits, then proves the rewritten spec by applying it mechanically. Next update, it just works.
Pi patches pi. The patch maintainer is the agent.
Install
npm install -g pi-patcher
pi-patcher initpi-patcher init applies the bundled bootstrap-hook patch, wiring pi-patcher reconcile into the end of pi update so your patches are re-applied automatically on every future update. Every edit is reversible via pi-patcher remove or pi-patcher uninstall, and any failed apply or heal is rolled back automatically, leaving the target file untouched.
You write patches in ~/.pi/patches/; pi-patcher's own bundled patches live separately under ~/.pi/pi-patcher/internal-patches/ and never touch your dir.
Commands
pi-patcher init # one-time setup: install bundled patches + wire into `pi update`
pi-patcher reconcile # apply pending patches; heal drifted ones
pi-patcher reconcile --redesign # also autonomously redesign patches that can't be re-anchored
pi-patcher reconcile --prompt # ...or print a prompt to drive the redesign yourself
pi-patcher list # status + most recent heal session per patch
pi-patcher heal <id> # force-heal one patch (accepts --redesign / --prompt)
pi-patcher remove <id> # revert a user patch's edits and delete its folder
pi-patcher uninstall # revert every patch and `npm uninstall -g pi-patcher`Running pi-patcher with no arguments prints this list.
init is what you run once after npm install. Re-running it after upgrades is safe.
reconcile is the workhorse for steady-state. It runs automatically after every pi update (via the hook init installs), and you can also run it by hand after authoring or editing a patch.
remove reverts a user patch, then deletes its folder. For bundled patches managed by pi-patcher, use uninstall.
Writing a patch
A patch is just a folder in ~/.pi/patches/ with a PATCH.md file:
~/.pi/patches/<id>/
PATCH.mdPATCH.md is freeform markdown plus frontmatter and fenced edit blocks. The prose is fed to the AI when healing; mechanical apply only reads the frontmatter and edit fences:
---
id: my-patch
summary: What this patch does
version: 0.1.0
lastUpdated: 2026-06-25
---
# My patch
Whatever prose helps explain the patch.
```patch file=dist/example.js
<<<<<<< SEARCH
old text
=======
new text
>>>>>>> REPLACE
```Diff-style hunks are also supported:
```diff file=dist/example.js
@@ optional hint @@
context line
+added line
```Each derived oldText must appear exactly once in the target file. newText must be non-empty, but deletion-only patches aren't supported in this version. Replace the line with a comment instead.
A patch can contain multiple fenced edits, across one or more files. Mechanical apply and revert iterate over every entry; heal runs one AI session per drifted patch, and the agent rewrites PATCH.md itself. Legacy intent.md + spec.json patch folders are still read for backwards compatibility.
JavaScript and JSON targets are syntax-checked after every edit. Any file type (markdown, plain text, etc.) is supported.
How healing works
One contract underlies everything: the AI is the only writer of patch specs. pi-patcher never writes a PATCH.md — it snapshots, restores, applies, and verifies.
Drift is resolved in tiers, smallest change first:
- Re-anchor. When the literal
oldText/newTextno longer matches, pi-patcher hands thePATCH.mdto pi, which makes the smallest spec edit that restores the intent. - Retarget. If the code the patch targets has moved to another file in the same package, pi finds it there and points the edit's
file=at the new location. Re-anchor and retarget both happen automatically duringreconcile. - Redesign. Only when restoring the intent would require genuine rework does pi abort. That patch is then routed rather than failed (see below).
Under the hood, for tiers 1–2:
- the patch spec and the package's files are snapshotted first
pi -p --model ${PI_PATCHER_HEAL_MODEL:-openai-codex/gpt-5.5:low} --session-id <id>is invoked withprompts/heal.mdon stdin- pi rewrites the fenced edits in
PATCH.mditself; it may trial the change in the source, but those edits are scratch - pi-patcher then restores the sources from the snapshot and proves the rewritten spec by applying it mechanically (unique anchors + syntax checks, JS / JSON only)
- if the spec doesn't verify, the same session is resumed once with the exact failure; a second miss rolls everything back — spec and sources alike
- pi uses your normal session storage, so heal sessions remain discoverable anywhere
pi --session <id>can find them - pi-patcher shows a small progress spinner while the headless heal runs, but suppresses raw nested pi output by default
Each heal prints the short replay command, e.g. pi --session 019efc67-5d7d-75f1-b395-62e7ccc0eda0.
When a patch needs a redesign
If pi aborts because the intent no longer maps to a small edit, the patch is marked needs redesign and routed:
- Interactively (a TTY, e.g. you running
pi updateorpi-patcher reconcileby hand) you get a 3-option picker: redesign automatically, copy a prompt to your clipboard to drive the fix yourself inpi, or skip. - Non-interactively (CI, unattended updates) pi-patcher just reports it and points you at the resolution commands, so it never blocks.
--redesignforces the autonomous path for every aborted patch: pi re-authors the patch spec against the current target, and pi-patcher proves the redesign by applying it mechanically (rolled back if it doesn't apply).--promptprints a ready-to-paste prompt instead, so you can run the redesign in an interactivepisession and re-runreconcileafterwards.
Both flags act only on patches that aborted; healthy and re-anchorable patches are unaffected.
Uninstalling
pi-patcher uninstallReverts every patch (yours and the bundled ones), deletes their folders, forgets state, then runs npm uninstall -g pi-patcher.
To temporarily disable pi-patcher without uninstalling, rename any patch directory to _<id>. The loader skips underscore-prefixed entries.
Model configuration
PI_PATCHER_HEAL_MODEL overrides the default heal model (openai-codex/gpt-5.5:low).
Development
bun test
bun run typecheck
bun run buildLicense
MIT
