@ahm3tj4f/pi-undo

Undo/redo for pi, but this time it works. Per-message snapshots, ported from OpenCode's exact undo/redo philosophy.

Packages

Package details

extension

Install @ahm3tj4f/pi-undo from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:@ahm3tj4f/pi-undo
Package
@ahm3tj4f/pi-undo
Version
0.4.0
Published
Aug 13, 2026
Downloads
636/mo · 207/wk
Author
ahm3tj4f
License
MIT
Types
extension
Size
73.4 KB
Dependencies
1 dependency · 1 peer
Pi manifest JSON
{
  "extensions": [
    "./src/index.ts"
  ]
}

Security note

Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.

README

pi-undo

npm version npm downloads license

Undo/redo for pi. But this time it works.

This is a port of OpenCode's exact undo/redo philosophy: snapshot the files per message with a shadow git repo, restore only what that message changed, and never lose your work in the process.

How it works

Each user message gets two git tree hashes: one before the turn, one after. The trees live in a shadow repo under ~/.pi/agent/pi-undo/snapshots/. The checkpoints are persisted in the session, so undo and redo work after a restart.

Why this works

  • Works without git. Non-git directories are fully supported.
  • Fast on big repos. Object reuse via git alternates, incremental adds, batched restores. No full git add twice per turn.
  • Garbage collection. Daily gc keeps the snapshot store bounded. Old snapshots get pruned, so storage doesn't grow forever.
  • Cancel and failures are safe. Cancel mid-undo rolls the files back. Restores are verified by tree hash and roll back on mismatch. Manual edits trigger a question first, so nothing gets clobbered.
  • Two snapshots per message. Each user message gets a before and an after tree hash. Undo restores only the files that message changed.
  • Gitignored files are undoable when the session edits them. The shadow repo snapshots gitignored files too, so a file the current pi session touched can always be undone, even if it is in .gitignore. A gitignored file with manual edits since the message triggers the manual-edits question. Confirming restores the file and loses the manual edits. Declining blocks the undo. Only pi-undo's own excludeDirectories are never snapshotted.
  • Undo restores only what this session wrote. Each message records which files its write and edit tools touched. Undo restores those files. Files that changed during the message for another reason are listed in the dialog and never restored; a warning names them. Files that another pi session touched get the session id in the warning. The records live in a small journal under the snapshot store, so they survive restarts. Changes made by bash commands cannot be attributed and are warned about the same way.

Configuration

pi-undo reads one config file: ~/.pi/agent/pi-undo.json. On the first run the file is created with the default values, and you edit it directly to change them. Add or remove patterns in excludeDirectories, or change maxFiles. The list in the file is the complete list: removing an entry really un-excludes that path.

{
  "excludeDirectories": ["node_modules", "dist", "Downloads", "tmp"],
  "maxFiles": 100000
}
Field What it does
excludeDirectories Full gitignore glob patterns, never snapshotted. Plain names match at any depth; globs like **/build-* or *.tmp work; a trailing slash means directories only
maxFiles Snapshot size cap (default 100000). Over this, snapshots are skipped for that message with a one-time warning instead of making pi slow

Commands

Command What it does
/undo Aborts the agent, shows a diff preview, restores the files to before the last message, and puts the prompt back.
/redo Re-applies the most recently undone message. Survives restarts.
/diff Shows what /undo would restore.

Install

pi install npm:@ahm3tj4f/pi-undo

# OR

pi install git:github.com/ahm3tj4f/pi-undo