pi-rewind-hook 1.7.2 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -14
- package/README.md +127 -109
- package/banner.png +0 -0
- package/index.test.ts +602 -0
- package/index.ts +1205 -449
- package/install.js +37 -47
- package/package.json +8 -4
- package/test-support/pi-coding-agent-shim.ts +6 -0
- package/tsconfig.test.json +11 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,18 +4,30 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- Added `pi-package` keyword for npm discoverability (pi v0.50.0 package system)
|
|
9
|
-
|
|
10
|
-
## [1.7.1] - 2026-01-22
|
|
7
|
+
## [1.8.0] - 2026-04-03
|
|
11
8
|
|
|
12
9
|
### Changed
|
|
13
|
-
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
- Replaced the old git-ref checkpoint ledger with session-native `rewind-turn` and `rewind-op` records
|
|
11
|
+
- Rewind points are now aligned to visible session nodes: the triggering user node and each assistant step captured at `turn_end`
|
|
12
|
+
- Snapshot commits are now kept reachable through a single `refs/pi-rewind/store` keepalive ref instead of one ref per checkpoint
|
|
13
|
+
- Removed the fixed 100-checkpoint per-session pruning model; exact mode now has no cap by default
|
|
14
|
+
- `/fork` and `/tree` now persist resulting exact file state through `rewind-op.current`, including keep-current-files flows
|
|
15
|
+
- Undo snapshots now persist with the resulting session state instead of depending on old before-restore refs
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- Lineage-aware exact snapshot lookup through `parentSession`
|
|
19
|
+
- Exact assistant-node rewind for v2 captures
|
|
20
|
+
- Compaction and branch-summary snapshot aliasing without creating fresh snapshots when files did not change
|
|
21
|
+
- Optional retention over unique snapshot commits via `rewind.retention.maxSnapshots`, `rewind.retention.maxAgeDays`, and `rewind.retention.pinLabeledEntries`
|
|
22
|
+
- Retention discovery mode setting via `rewind.retention.scanMode` (`ancestor-only` default, optional `repo-sessions`)
|
|
23
|
+
- Startup retention sweep budget setting via `rewind.retention.startupBudgetMs`
|
|
16
24
|
|
|
17
25
|
### Fixed
|
|
18
|
-
-
|
|
26
|
+
- Restore now deletes files absent from the target snapshot before worktree-only restore, producing exact restore for tracked and untracked non-ignored files in the snapshot domain without staging the repo index
|
|
27
|
+
- Rewind bookkeeping for reconstruction, capture, migration, and retention no longer depends on UI availability
|
|
28
|
+
- `/tree` restore options now match actual exact rewind availability: user, assistant, compaction, and branch-summary nodes only
|
|
29
|
+
- Critical rewind handlers now fail closed with user-facing errors instead of bubbling git/storage exceptions through `/fork` and `/tree`
|
|
30
|
+
- Turn capture/finalization now warns and recovers when snapshot writes fail, avoiding stale collector state
|
|
19
31
|
|
|
20
32
|
## [1.7.0] - 2026-01-15
|
|
21
33
|
|
|
@@ -37,12 +49,12 @@ All notable changes to this project will be documented in this file.
|
|
|
37
49
|
- Old-format checkpoints are not pruned (to avoid cross-session interference)
|
|
38
50
|
- New checkpoints are created in the new session-scoped format
|
|
39
51
|
|
|
40
|
-
## [1.6.0] -
|
|
52
|
+
## [1.6.0] - 2026-01-13
|
|
41
53
|
|
|
42
54
|
### Added
|
|
43
55
|
- `rewind.silentCheckpoints` setting to hide checkpoint status and notifications
|
|
44
56
|
|
|
45
|
-
## [1.5.0] -
|
|
57
|
+
## [1.5.0] - 2026-01-10
|
|
46
58
|
|
|
47
59
|
### Changed
|
|
48
60
|
- Replaced noisy stderr logging with clean TUI output
|
|
@@ -88,7 +100,7 @@ All notable changes to this project will be documented in this file.
|
|
|
88
100
|
- Install script now migrates old hooks config and cleans up old directory
|
|
89
101
|
- Renamed "Hook" to "Extension" throughout codebase and docs
|
|
90
102
|
|
|
91
|
-
## [1.2.0] -
|
|
103
|
+
## [1.2.0] - 2026-01-03
|
|
92
104
|
|
|
93
105
|
### Added
|
|
94
106
|
- Tree navigation support (`session_before_tree`) - restore files when navigating session tree
|
|
@@ -102,13 +114,13 @@ All notable changes to this project will be documented in this file.
|
|
|
102
114
|
- Removed `agent_end` handler that was clearing checkpoints after each turn
|
|
103
115
|
- "Undo last file rewind" now cancels branch instead of creating unwanted branch
|
|
104
116
|
|
|
105
|
-
## [1.1.1] -
|
|
117
|
+
## [1.1.1] - 2025-12-27
|
|
106
118
|
|
|
107
119
|
### Fixed
|
|
108
120
|
- Use `before_branch` event instead of `branch` for proper hook timing (thanks @badlogic)
|
|
109
121
|
- Cancel branch when user dismisses restore options menu
|
|
110
122
|
|
|
111
|
-
## [1.1.0] -
|
|
123
|
+
## [1.1.0] - 2025-12-27
|
|
112
124
|
|
|
113
125
|
### Added
|
|
114
126
|
- "Undo last file rewind" option - restore files to state before last rewind
|
|
@@ -122,7 +134,7 @@ All notable changes to this project will be documented in this file.
|
|
|
122
134
|
### Fixed
|
|
123
135
|
- Code-only restore options now properly skip conversation restore
|
|
124
136
|
|
|
125
|
-
## [1.0.0] -
|
|
137
|
+
## [1.0.0] - 2025-12-19
|
|
126
138
|
|
|
127
139
|
### Added
|
|
128
140
|
- Initial release
|
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Rewind Extension
|
|
2
2
|
|
|
3
|
-
A Pi agent extension that
|
|
3
|
+
A Pi agent extension that records exact file-state rewind points, allowing restoration of file states during `/tree` navigation and across resumed and forked sessions
|
|
4
|
+
|
|
5
|
+
Rewind metadata lives in the session itself as hidden entries, so rewind history survives across forks, resumes, tree navigation, and compaction. Snapshot commits are kept reachable through a single git ref rather than one ref per checkpoint, and rewind points can be resolved across session lineage via `parentSession` links. Retention is optional and configurable; without it, exact history is kept indefinitely.
|
|
4
6
|
|
|
5
7
|
## Screenshots
|
|
6
8
|
|
|
@@ -10,9 +12,9 @@ A Pi agent extension that enables rewinding file changes during coding sessions.
|
|
|
10
12
|
|
|
11
13
|
## Requirements
|
|
12
14
|
|
|
13
|
-
- Pi agent v0.35.0+
|
|
15
|
+
- Pi agent v0.35.0+
|
|
14
16
|
- Node.js (for installation)
|
|
15
|
-
- Git repository
|
|
17
|
+
- Git repository
|
|
16
18
|
|
|
17
19
|
## Installation
|
|
18
20
|
|
|
@@ -24,173 +26,191 @@ This will:
|
|
|
24
26
|
1. Create `~/.pi/agent/extensions/rewind/`
|
|
25
27
|
2. Download the extension files
|
|
26
28
|
3. Add the extension to your `~/.pi/agent/settings.json`
|
|
27
|
-
4. Migrate any existing hooks config to extensions (if upgrading from
|
|
29
|
+
4. Migrate any existing hooks config to extensions (if upgrading from an older version)
|
|
28
30
|
5. Clean up old `hooks/rewind` directory (if present)
|
|
29
31
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
Using curl:
|
|
33
|
-
|
|
34
|
-
```bash
|
|
35
|
-
curl -fsSL https://raw.githubusercontent.com/nicobailon/pi-rewind-hook/main/install.js | node
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
Or clone the repo and configure manually:
|
|
39
|
-
|
|
40
|
-
```bash
|
|
41
|
-
git clone https://github.com/nicobailon/pi-rewind-hook ~/.pi/agent/extensions/rewind
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
Then add to `~/.pi/agent/settings.json`:
|
|
45
|
-
|
|
46
|
-
```json
|
|
47
|
-
{
|
|
48
|
-
"extensions": ["~/.pi/agent/extensions/rewind/index.ts"]
|
|
49
|
-
}
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
### Platform Notes
|
|
53
|
-
|
|
54
|
-
**Windows:** The `npx` command works in PowerShell, Command Prompt, and WSL. If you prefer curl on Windows without WSL:
|
|
55
|
-
|
|
56
|
-
```powershell
|
|
57
|
-
Invoke-WebRequest -Uri "https://raw.githubusercontent.com/nicobailon/pi-rewind-hook/main/install.js" -OutFile install.js; node install.js; Remove-Item install.js
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
### Upgrading from v1.2.0
|
|
61
|
-
|
|
62
|
-
If you're upgrading from pi-rewind-hook v1.2.0 (which used the hooks system), simply run `npx pi-rewind-hook` again. The installer will:
|
|
63
|
-
- Move the extension from `hooks/rewind` to `extensions/rewind`
|
|
64
|
-
- Migrate your settings.json from `hooks` to `extensions`
|
|
65
|
-
- Clean up the old hooks directory
|
|
66
|
-
|
|
67
|
-
**Note:** v1.3.0+ requires pi v0.35.0 or later. If you're on an older version of pi, stay on pi-rewind-hook v1.2.0.
|
|
32
|
+
Or clone the repo into `~/.pi/agent/extensions/rewind/` — Pi discovers extensions in that directory automatically.
|
|
68
33
|
|
|
69
34
|
## Configuration
|
|
70
35
|
|
|
71
|
-
|
|
36
|
+
Optionally add settings to `~/.pi/agent/settings.json`. For example:
|
|
72
37
|
|
|
73
38
|
```json
|
|
74
39
|
{
|
|
75
|
-
"extensions": ["~/.pi/agent/extensions/rewind/index.ts"],
|
|
76
40
|
"rewind": {
|
|
77
|
-
"silentCheckpoints": true
|
|
41
|
+
"silentCheckpoints": true,
|
|
42
|
+
"retention": {
|
|
43
|
+
"maxSnapshots": 2000,
|
|
44
|
+
"maxAgeDays": 30,
|
|
45
|
+
"pinLabeledEntries": true
|
|
46
|
+
}
|
|
78
47
|
}
|
|
79
48
|
}
|
|
80
49
|
```
|
|
81
50
|
|
|
82
51
|
### Settings
|
|
83
52
|
|
|
84
|
-
-
|
|
53
|
+
- `rewind.silentCheckpoints` — hides footer/status and checkpoint notifications
|
|
54
|
+
- `rewind.retention.maxSnapshots` — optional cap on unpinned unique snapshot commits kept reachable
|
|
55
|
+
- `rewind.retention.maxAgeDays` — optional age limit for unpinned snapshot commits
|
|
56
|
+
- `rewind.retention.pinLabeledEntries` — when true, snapshot commits bound to **labeled nodes** in the Pi session tree are exempt from `maxSnapshots` and `maxAgeDays` pruning (useful for bookmarking important rewind points you want to prevent getting garbage-collected)
|
|
57
|
+
- `rewind.retention.scanMode` — retention discovery mode: `ancestor-only` (default, follow current session lineage only) or `repo-sessions` (scan discovered session roots)
|
|
58
|
+
- `rewind.retention.startupBudgetMs` — optional startup time budget for retention sweeps; if exceeded, startup sweep is skipped and deferred
|
|
85
59
|
|
|
86
|
-
|
|
60
|
+
If `rewind.retention` is omitted, Rewind keeps exact history with no automatic expiration. This can grow git object storage without bound over time.
|
|
87
61
|
|
|
88
|
-
|
|
62
|
+
## Usage
|
|
89
63
|
|
|
90
|
-
|
|
64
|
+
### Rewinding via `/fork`
|
|
91
65
|
|
|
92
|
-
1.
|
|
93
|
-
2.
|
|
66
|
+
1. Type `/fork` in Pi
|
|
67
|
+
2. Select a message to branch from
|
|
68
|
+
3. Choose a restore option
|
|
94
69
|
|
|
95
|
-
|
|
70
|
+
**Options:**
|
|
96
71
|
|
|
97
|
-
|
|
72
|
+
| Option | Files | Conversation |
|
|
73
|
+
|--------|-------|-------------|
|
|
74
|
+
| **Conversation only (keep current files)** | Unchanged | Reset to that point |
|
|
75
|
+
| **Restore all (files + conversation)** | Restored | Reset to that point |
|
|
76
|
+
| **Code only (restore files, keep conversation)** | Restored | Unchanged |
|
|
77
|
+
| **Undo last file rewind** | Restored to before last rewind | Unchanged |
|
|
98
78
|
|
|
99
|
-
|
|
79
|
+
`Restore all`, `Code only`, and `Undo last file rewind` are only shown when an exact rewind point or undo snapshot is available for the selected message.
|
|
100
80
|
|
|
101
|
-
|
|
102
|
-
2. Select a message to branch from
|
|
103
|
-
3. Choose a restore option
|
|
81
|
+
When you restore files during `/fork`, the undo snapshot is persisted in the **new child session**, because that is where you continue working.
|
|
104
82
|
|
|
105
|
-
|
|
83
|
+
### Rewinding via `/tree`
|
|
106
84
|
|
|
107
85
|
1. Press `Tab` to open the session tree
|
|
108
86
|
2. Navigate to a different node
|
|
109
87
|
3. Choose a restore option
|
|
110
88
|
|
|
111
|
-
**
|
|
89
|
+
**Options:**
|
|
112
90
|
|
|
113
91
|
| Option | Files | Conversation |
|
|
114
|
-
|
|
115
|
-
| **
|
|
116
|
-
| **
|
|
117
|
-
| **
|
|
118
|
-
| **Undo last file rewind** | Restored to before last rewind | Unchanged |
|
|
92
|
+
|--------|-------|-------------|
|
|
93
|
+
| **Keep current files** | Unchanged | Navigated to that point |
|
|
94
|
+
| **Restore files to that point** | Restored | Navigated to that point |
|
|
95
|
+
| **Undo last file rewind** | Restored to before last rewind | Navigated to that point |
|
|
119
96
|
|
|
120
|
-
|
|
97
|
+
If you navigate with `Keep current files`, Rewind still persists the resulting session position's exact current file baseline via `rewind-op.current`.
|
|
121
98
|
|
|
122
|
-
|
|
123
|
-
|--------|-------|--------------|
|
|
124
|
-
| **Restore to session start (files + conversation)** | Restored to session start | Reset to that point |
|
|
125
|
-
| **Conversation only (keep current files)** | Unchanged | Reset to that point |
|
|
126
|
-
| **Restore to session start (files only, keep conversation)** | Restored to session start | Unchanged |
|
|
127
|
-
| **Undo last file rewind** | Restored to before last rewind | Unchanged |
|
|
128
|
-
|
|
129
|
-
### Resumed Sessions
|
|
130
|
-
|
|
131
|
-
When you resume a session (`pi --resume`), the extension creates a resume checkpoint. If you branch to a message from before the current session, you can restore files to the state when you resumed (not per-message granularity, but a safety net).
|
|
99
|
+
### Examples
|
|
132
100
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
### Undo a bad refactor
|
|
101
|
+
**Undo a bad refactor:**
|
|
136
102
|
|
|
137
103
|
```
|
|
138
104
|
You: refactor the auth module to use JWT
|
|
139
105
|
Agent: [makes changes you don't like]
|
|
140
106
|
|
|
141
|
-
You: /
|
|
107
|
+
You: /fork
|
|
142
108
|
→ Select "refactor the auth module to use JWT"
|
|
143
109
|
→ Select "Code only (restore files, keep conversation)"
|
|
144
110
|
|
|
145
111
|
Result: Files restored, conversation intact. Try a different approach.
|
|
146
112
|
```
|
|
147
113
|
|
|
148
|
-
|
|
114
|
+
**Start fresh from an earlier point:**
|
|
149
115
|
|
|
150
116
|
```
|
|
151
|
-
You: /
|
|
117
|
+
You: /fork
|
|
152
118
|
→ Select an earlier message
|
|
153
119
|
→ Select "Restore all (files + conversation)"
|
|
154
120
|
|
|
155
121
|
Result: Both files and conversation reset to that point.
|
|
156
122
|
```
|
|
157
123
|
|
|
158
|
-
|
|
124
|
+
## How Rewind works
|
|
159
125
|
|
|
160
|
-
|
|
161
|
-
pi --resume # resume old session
|
|
162
|
-
```
|
|
126
|
+
Rewind stores its ledger in hidden session `custom` entries:
|
|
163
127
|
|
|
164
|
-
|
|
165
|
-
|
|
128
|
+
- `rewind-turn` — one per prompt, containing the triggering user-node snapshot plus any assistant-node snapshots produced during that prompt
|
|
129
|
+
- `rewind-op` — sparse records for `/fork`, `/tree`, compaction aliases, summary aliases, and undo state
|
|
166
130
|
|
|
167
|
-
|
|
168
|
-
→ Select any old message
|
|
169
|
-
→ Select "Restore to session start (files only, keep conversation)"
|
|
131
|
+
Snapshots are only considered at visible boundaries:
|
|
170
132
|
|
|
171
|
-
|
|
172
|
-
|
|
133
|
+
- the user pre-prompt boundary
|
|
134
|
+
- each assistant `turn_end`
|
|
135
|
+
- selected stateful session operations
|
|
173
136
|
|
|
174
|
-
|
|
137
|
+
Rewind does not create per-tool snapshots.
|
|
175
138
|
|
|
176
|
-
|
|
139
|
+
### Canonical exact rewind points
|
|
177
140
|
|
|
178
|
-
|
|
179
|
-
|
|
141
|
+
Exact file restore is available for:
|
|
142
|
+
|
|
143
|
+
- user nodes
|
|
144
|
+
- assistant nodes
|
|
145
|
+
- compaction nodes aliased to the current exact state
|
|
146
|
+
- branch-summary nodes aliased during `/tree`
|
|
147
|
+
|
|
148
|
+
Exact file restore is not offered for toolResult nodes.
|
|
149
|
+
|
|
150
|
+
### Git storage model
|
|
151
|
+
|
|
152
|
+
Snapshot commits are kept reachable through a single repo-local ref:
|
|
153
|
+
|
|
154
|
+
```text
|
|
155
|
+
refs/pi-rewind/store
|
|
180
156
|
```
|
|
181
157
|
|
|
182
|
-
|
|
158
|
+
That ref is only for git object reachability; the session ledger is authoritative.
|
|
159
|
+
|
|
160
|
+
### Deduplication
|
|
161
|
+
|
|
162
|
+
Before creating a snapshot commit, Rewind captures the worktree tree SHA. If it matches the latest exact snapshot tree, Rewind reuses that existing snapshot commit instead of creating a new one.
|
|
163
|
+
|
|
164
|
+
### Restore exactness and scope
|
|
165
|
+
|
|
166
|
+
Rewind restores the exact file state for the snapshot domain it owns:
|
|
167
|
+
|
|
168
|
+
- tracked files
|
|
169
|
+
- untracked, non-ignored files
|
|
170
|
+
- without staging the real git index during restore
|
|
171
|
+
|
|
172
|
+
Before restoring target contents, it deletes paths present in the current snapshot but absent from the target snapshot, then restores the target snapshot into the worktree only.
|
|
173
|
+
|
|
174
|
+
Out of scope:
|
|
175
|
+
|
|
176
|
+
- ignored files
|
|
177
|
+
- empty directories
|
|
178
|
+
- exact rewind points for `toolResult` nodes
|
|
179
|
+
- exact rewind points for `bashExecution` nodes
|
|
180
|
+
|
|
181
|
+
## Lineage and resume behavior
|
|
182
|
+
|
|
183
|
+
Rewind resolves exact rewind points across session lineage by following `parentSession` links and reading rewind metadata from ancestor session files.
|
|
184
|
+
|
|
185
|
+
Rewind v2 uses only session-native `rewind-turn`/`rewind-op` metadata and does not read legacy checkpoint refs.
|
|
186
|
+
|
|
187
|
+
## Retention
|
|
188
|
+
|
|
189
|
+
Retention only affects git reachability. Session JSONL metadata is append-only and is never compacted by Rewind.
|
|
190
|
+
|
|
191
|
+
When retention is enabled, Rewind keeps snapshot commits alive if they are referenced by:
|
|
192
|
+
|
|
193
|
+
- a `rewind-turn` or `rewind-op` binding in a discovered same-repo session
|
|
194
|
+
- the latest `rewind-op.current` for a discovered same-repo session
|
|
195
|
+
- the latest `rewind-op.undo` for a discovered same-repo session
|
|
196
|
+
- a labeled bound entry when `pinLabeledEntries` is enabled
|
|
197
|
+
|
|
198
|
+
If a snapshot commit has been pruned, Rewind validates commit existence before offering restore.
|
|
199
|
+
|
|
200
|
+
If retention discovery yields an empty live set, Rewind preserves the existing `refs/pi-rewind/store` ref rather than deleting it.
|
|
201
|
+
|
|
202
|
+
## Viewing storage
|
|
183
203
|
|
|
184
|
-
|
|
204
|
+
Show the store ref head:
|
|
185
205
|
|
|
186
206
|
```bash
|
|
187
|
-
git
|
|
207
|
+
git rev-parse refs/pi-rewind/store
|
|
188
208
|
```
|
|
189
209
|
|
|
190
|
-
|
|
210
|
+
Show the hidden rewind ledger in a session file:
|
|
191
211
|
|
|
192
212
|
```bash
|
|
193
|
-
|
|
213
|
+
grep '"customType":"rewind-' ~/.pi/agent/sessions/**/*.jsonl
|
|
194
214
|
```
|
|
195
215
|
|
|
196
216
|
## Uninstalling
|
|
@@ -199,19 +219,17 @@ git for-each-ref --format='%(refname)' refs/pi-checkpoints/ | xargs -n1 git upda
|
|
|
199
219
|
```bash
|
|
200
220
|
rm -rf ~/.pi/agent/extensions/rewind
|
|
201
221
|
```
|
|
202
|
-
On Windows (PowerShell): `Remove-Item -Recurse -Force ~/.pi/agent/extensions/rewind`
|
|
203
222
|
|
|
204
|
-
2. Remove
|
|
223
|
+
2. Remove any `rewind` key from `~/.pi/agent/settings.json` if you added one
|
|
205
224
|
|
|
206
|
-
3. Optionally, clean up git
|
|
225
|
+
3. Optionally, clean up the git reachability ref in each repo where you used the extension:
|
|
207
226
|
```bash
|
|
208
|
-
git
|
|
227
|
+
git update-ref -d refs/pi-rewind/store
|
|
209
228
|
```
|
|
210
229
|
|
|
211
230
|
## Limitations
|
|
212
231
|
|
|
213
232
|
- Only works in git repositories
|
|
214
|
-
-
|
|
215
|
-
-
|
|
216
|
-
-
|
|
217
|
-
- Each session has its own 100-checkpoint limit (pruning doesn't affect other sessions)
|
|
233
|
+
- Session metadata grows append-only; retention only trims git reachability
|
|
234
|
+
- Discovery for retention is best-effort across discovered Pi session roots and explicit `parentSession` ancestors
|
|
235
|
+
- Ignored files and empty directories are outside the snapshot model
|
package/banner.png
ADDED
|
Binary file
|