pi-rewind

The rewind experience Pi deserves — 1 checkpoint per turn, /rewind command, safe restore, redo stack

1,727
Lines of Code
20
Tests Passing
5
Source Files
0
Dependencies
1 — Why pi-rewind?

Pi already had checkpoint extensions. This one combines the best of all of them.

checkpoint-pi brought safe restore and smart filtering. pi-rewind-hook added resume checkpoints and status UI. But neither had a dedicated /rewind command, per-tool granularity, or a redo stack — features that Claude Code, Cline, and OpenCode ship built-in.

We researched 11 coding agents (820K tokens of analysis), studied both existing Pi extensions, and merged the best ideas into one extension that closes every gap.

2 — Install
# From npm (recommended)
$ pi install npm:pi-rewind

# From GitHub
$ pi install github.com/arpagon/pi-rewind

# For development
$ git clone git@github.com:arpagon/pi-rewind.git
$ pi -e ./pi-rewind/src/index.ts
3 — Features
FeatureTriggerStatus
1 checkpoint per turnAfter all tools finish — like Cline. Label: "prompt" → toolsturn_endDone
Descriptive labelsUser prompt + tool list persisted in git refsAutomaticDone
/rewind commandCheckpoint browser → diff preview → restoreType /rewindDone
Esc+Esc shortcutQuick files-only rewindDouble EscapeDone
Restore modesFiles + conversation, files only, conversation onlyPicker menuDone
Redo stackMulti-level undo of rewinds"↩ Undo last rewind"Done
Safe restoreNever deletes node_modules, .venv, large filesAutomaticDone
Branch safetyBlocks cross-branch restore (avoids OpenCode bug)AutomaticDone
Smart filteringIgnores 13 dir patterns + files >10MiB + dirs >200 filesAutomaticDone
Resume checkpointSnapshot on session start for fallbacksession_startDone
Footer status◆ 5 checkpoints in footerAutomaticDone
Auto-pruningKeeps max 100 checkpoints per sessionturn_endDone
Fork/tree integrationRestore prompts on /fork and /tree navigationsession_before_fork/treeDone
Git refs storageSurvives restarts under refs/pi-checkpoints/AutomaticDone
Diff previewShow changes before confirming restore/rewind flowDone
"Summarize from here"Compact conversation from checkpoint forwardctx.compact()Planned
15 features14 done
4 — Architecture

Two-layer split: core is independently testable

core.ts has zero Pi dependency — pure git operations via spawn(). The extension layer (index.ts) wires Pi events to core functions. This means the 646-line core can be tested with just git and Node.js, no Pi runtime needed.

📦 Source files

  • src/core.ts — 654 LOCGit operations, filtering, safe restore
  • src/index.ts — 253 LOCPi event hooks, turn-end checkpointing
  • src/commands.ts — 339 LOC/rewind, Esc+Esc, fork/tree handlers
  • src/state.ts — 70 LOCShared mutable state
  • src/ui.ts — 33 LOCFooter status indicator

🧪 Testing

  • tests/core.test.ts — 327 LOC
  • 20 tests covering: utils, git helpers, CRUD, snapshot/restore, safe restore, branch safety, tool checkpoints, pruning
  • Runs on disposable temp repos — no fixtures
  • npx tsx tests/core.test.ts
Checkpoint data structure
Each checkpoint stores in a git commit message:

Identity: id, sessionId, trigger (turn/tool/resume/before-restore), turnIndex, toolName
Git state: headSha, indexTreeSha (staged), worktreeTreeSha (full snapshot)
Safety: preexistingUntrackedFiles[], skippedLargeFiles[], skippedLargeDirs[]

Restore is a 4-step process: reset --hard HEADread-tree -u worktree → safe clean (only new files) → read-tree index
5 — How It Works

Checkpoint lifecycle: when snapshots are created, stored, and restored.

Checkpoint Creation Flow
flowchart TD
    SS["session_start"]:::event --> RC["Create resume checkpoint"]:::checkpoint
    RC --> STATUS["Update footer: ◆ N checkpoints"]:::ui

    BA["before_agent_start"]:::event --> PROMPT["Capture user prompt"]:::step
    TC["tool_call"]:::event --> ARGS["Capture tool args (path, cmd)"]:::step

    TEE["tool_execution_end"]:::tool --> MUT{"write / edit / bash?"}:::decision
    MUT -->|"Yes"| ACC["Accumulate description"]:::step
    MUT -->|"No (read, grep...)"| SKIP["Skip"]:::skip

    TEND["turn_end"]:::event --> HAD{"Had mutations?"}:::decision
    HAD -->|"No"| SKIP2["Skip — no checkpoint needed"]:::skip
    HAD -->|"Yes"| CREATE["Create 1 checkpoint"]:::checkpoint
    CREATE --> DESC["Label: prompt + all tools"]:::step
    DESC --> STATUS
    CREATE --> PRUNE["Auto-prune if > 100"]:::prune

    classDef event fill:#1e1f32,stroke:#818cf8,stroke-width:2px,color:#e5e7eb
    classDef checkpoint fill:#0c3d2e,stroke:#34d399,stroke-width:2px,color:#e5e7eb
    classDef tool fill:#2e1f0c,stroke:#fbbf24,stroke-width:2px,color:#e5e7eb
    classDef decision fill:#1e1f32,stroke:#a78bfa,stroke-width:2px,color:#e5e7eb
    classDef step fill:#1e1f32,stroke:#60a5fa,stroke-width:2px,color:#e5e7eb
    classDef ui fill:#1e1f32,stroke:#22d3ee,stroke-width:2px,color:#e5e7eb
    classDef skip fill:#1e1f32,stroke:#4b5563,stroke-width:1px,color:#9ca3af
    classDef prune fill:#1e1f32,stroke:#f87171,stroke-width:2px,color:#e5e7eb
  
Pi event
Accumulate info
Checkpoint created
Tool execution
UI update
Cleanup
Restore Flow (/rewind or Esc+Esc)
flowchart TD
    TRIGGER["/rewind or Esc+Esc"]:::event --> LIST["List checkpoints (newest first)"]:::step
    LIST --> PICK["User picks checkpoint"]:::ui
    PICK --> DIFF["Show git diff --stat preview"]:::step
    DIFF --> CONFIRM{"Confirm?"}:::decision
    CONFIRM -->|"Cancel"| DONE["Done"]:::skip
    CONFIRM -->|"Yes"| MODE["Pick restore mode"]:::ui

    MODE --> M_ALL["Files + conversation"]:::restore
    MODE --> M_FILES["Files only"]:::restore
    MODE --> M_CONV["Conversation only"]:::restore

    M_ALL --> BEFORE["Create before-restore checkpoint (safety net)"]:::checkpoint
    M_FILES --> BEFORE
    BEFORE --> RESTORE["Restore worktree from snapshot"]:::restore

    RESTORE --> S1["1. git reset --hard HEAD"]:::git
    S1 --> S2["2. git read-tree -u worktree-tree"]:::git
    S2 --> S3["3. Safe clean (only NEW files)"]:::git
    S3 --> S4["4. git read-tree index-tree"]:::git
    S4 --> PUSH_REDO["Push to redo stack"]:::checkpoint

    M_CONV --> NAV["ctx.navigateTree(entryId)"]:::step
    M_ALL --> NAV

    PUSH_REDO --> NOTIFY["Notify: Rewound to checkpoint N"]:::ui

    classDef event fill:#1e1f32,stroke:#818cf8,stroke-width:2px,color:#e5e7eb
    classDef step fill:#1e1f32,stroke:#60a5fa,stroke-width:2px,color:#e5e7eb
    classDef ui fill:#1e1f32,stroke:#22d3ee,stroke-width:2px,color:#e5e7eb
    classDef decision fill:#1e1f32,stroke:#a78bfa,stroke-width:2px,color:#e5e7eb
    classDef restore fill:#2e1f0c,stroke:#fbbf24,stroke-width:2px,color:#e5e7eb
    classDef checkpoint fill:#0c3d2e,stroke:#34d399,stroke-width:2px,color:#e5e7eb
    classDef git fill:#14151f,stroke:#9ca3af,stroke-width:1px,color:#e5e7eb
    classDef skip fill:#1e1f32,stroke:#4b5563,stroke-width:1px,color:#9ca3af
  
Trigger
User interaction
Restore operation
Safety checkpoint
Git command
Safe Restore Detail
flowchart LR
    UNTRACKED["Current untracked files"]:::step --> CHECK{"Was it there at checkpoint time?"}:::decision
    CHECK -->|"Yes (pre-existing)"| KEEP["KEEP - never delete"]:::safe
    CHECK -->|"No (new file)"| CHECK2{"In ignored dir? Large file?"}:::decision
    CHECK2 -->|"Yes"| KEEP2["KEEP - protected"]:::safe
    CHECK2 -->|"No"| DELETE["DELETE - git clean"]:::danger

    classDef step fill:#1e1f32,stroke:#60a5fa,stroke-width:2px,color:#e5e7eb
    classDef decision fill:#1e1f32,stroke:#a78bfa,stroke-width:2px,color:#e5e7eb
    classDef safe fill:#0c3d2e,stroke:#34d399,stroke-width:2px,color:#e5e7eb
    classDef danger fill:#2e0c0c,stroke:#f87171,stroke-width:2px,color:#e5e7eb
  
What gets protected during safe restore?
Pre-existing untracked files: Tracked at checkpoint creation time. If a file was untracked when the checkpoint was made, it will never be deleted on restore.

Ignored directories (13 patterns): node_modules, .venv, venv, env, .env, dist, build, .pytest_cache, .mypy_cache, .cache, .tox, __pycache__

Large files: Files exceeding 10 MiB are skipped during snapshot and never deleted on restore.

Large directories: Directories with 200+ files are skipped entirely — prevents accidentally snapshotting or deleting generated content.
6 — vs Other Coding Agents

Based on research across 11 agents (820K tokens analyzed via Codex). Ranked by rewind completeness.

#AgentRewind featurePer-toolRedoFiles + Conv
1ClineCheckpoints per tool call, Compare/Restore UIYesNoYes
2Gemini CLI/rewind + Esc+Esc + /restoreNoNoYes
3Claude Code/rewind + Esc+Esc, "Summarize from here"NoNoYes
4OpenCode/undo + /redo, step-level patchesYesYesYes
pi-rewind/rewind + Esc+Esc, 1 per turn, redo stack, diff preview, safe restoreYesYesYes
9Aider/undo — last aider commit onlyNoNoNo
10Codex CLIEsc+Esc thread rollback (conversation only)NoNoConv only

What pi-rewind combines from the best

  • From Cline: 1 checkpoint per model response (not per tool — less noise)
  • From Claude Code / Gemini: /rewind command + Esc+Esc shortcut
  • From OpenCode: Redo stack for multi-level undo
  • From Claude Code: Diff preview before restore (planned: "Summarize from here")
  • Unique to pi-rewind: Descriptive labels ("user prompt" → write → file.ts, edit → other.ts)
  • Unique to pi-rewind: Safe restore that never deletes node_modules, .venv, or large files
7 — vs Existing Pi Extensions
Capabilitycheckpoint-pipi-rewind-hookpi-rewind
Dedicated /rewind commandNoNoYes
Esc+Esc shortcutNoNoYes
1 checkpoint per turn (not per tool)NoNoYes
Diff preview before restoreNoNoYes
Redo stack (multi-level undo)NoNoYes
Safe restore (preserve untracked)YesNoYes
Branch safety (cross-branch protection)YesNoN/A (shadow git)
Smart filtering (13 dirs, large files)YesNoYes
HEAD + index + worktree captureYesNoYes
Resume checkpoint (session start)NoYesYes
Footer status indicatorNoYesYes
Auto-pruningNoYesYes
Undo last rewindYesYesYes
Unit tests906 LOCNone327 LOC
Two-layer architecture (testable core)YesNoYes
No shell injection (spawn() only)YesPartialYes
15 capabilities8515
8 — Lineage & Credits

Built on the shoulders of two great Pi extensions

pi-rewind is not a clone — it's a clean-room rewrite that combines the best architectural decisions from both projects with new features inspired by the top coding agents.

Base architecture: checkpoint-pi by prateekmedia — two-layer design, safe restore, smart filtering, unit tests
UX inspiration: pi-rewind-hook by nicobailon — resume checkpoint, footer status, notifications, auto-pruning
Feature targets: Cline (per-tool), Claude Code (/rewind + Esc+Esc), OpenCode (/undo + /redo), Gemini CLI (/rewind + /restore)