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.
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.
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.
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.
A patch is just a folder in ~/.pi/patches/ with a PATCH.md file:
~/.pi/patches/<id>/
PATCH.md
PATCH.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. The format is defined by the PATCH.md spec (AVGVSTVS96/patch_md-spec) — see it for the frontmatter fields, edit-block syntax, and anchoring rules.
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.
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.
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.
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.
PI_PATCHER_HEAL_MODEL overrides the default heal model (openai-codex/gpt-5.5:low).
bun test
bun run typecheck
bun run buildMIT