pi-fast-resume 1.0.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/README.md ADDED
@@ -0,0 +1,219 @@
1
+ <div align="center">
2
+
3
+ # ⚡ pi-fast-resume
4
+
5
+ **Instant session picker for [pi](https://github.com/earendil-works/pi-coding-agent)**
6
+
7
+ _Reads 16KB per file instead of the full JSONL — first results in **6ms**._
8
+
9
+ [![pi extension](https://img.shields.io/badge/pi-extension-blueviolet)](https://github.com/earendil-works/pi-coding-agent)
10
+ [![license](https://img.shields.io/badge/license-MIT-blue)](./LICENSE)
11
+
12
+ </div>
13
+
14
+ ---
15
+
16
+ > **`/resume` takes 5.6 seconds** when you have 1,700+ sessions.
17
+ > pi-fast-resume's `/fast-resume` takes **6 milliseconds**.
18
+
19
+ Same picker UI and keybindings as `/resume`. The difference is pi-fast-resume never reads beyond the first 16KB of any session file. Headers, names, first messages — they all live in the first few lines. Everything after that is full message history the picker never shows. Search matches against the first message only (see [Known Limitations](#known-limitations)).
20
+
21
+ ```
22
+ ──────────────────────────────────────────────────────────
23
+
24
+ Resume Session (Current Folder) ◉ Current Folder | ○ All Name: All Sort: Threaded
25
+ Tab scope · re:<pattern> regex · "phrase" exact
26
+ Ctrl+S sort · Ctrl+N named · Ctrl+D delete · Ctrl+P path (off) · Ctrl+R rename
27
+
28
+ search: fix auth bug_
29
+
30
+ › Fix the auth bypass in middleware 34 2m
31
+ Add rate limiting to API 98 5h
32
+ Refactor user service 156 1d
33
+ Auth refactor 67 3d
34
+
35
+ ──────────────────────────────────────────────────────────
36
+ ```
37
+
38
+ ## Benchmarks
39
+
40
+ Tested with **1,771 sessions, 1.46 GB** of JSONL data on disk.
41
+
42
+ | Approach | First paint | Full load |
43
+ | --------------------------------- | ------------ | --------- |
44
+ | Built-in `/resume` | **5,600 ms** | 5,600 ms |
45
+ | `node:sqlite` indexed query | 52 ms | 52 ms |
46
+ | DuckDB persistent index | 49 ms | 49 ms |
47
+ | **pi-fast-resume (partial read)** | **6 ms** | ~580 ms |
48
+ | DuckDB NDJSON full scan | 2,560 ms | 2,560 ms |
49
+
50
+ <details>
51
+ <summary><strong>Full benchmark table</strong></summary>
52
+
53
+ | Approach | Time | Notes |
54
+ | ------------------------------------------- | --------- | ------------------------------------------ |
55
+ | `SessionManager.listAll()` (current) | ~5,600 ms | Full parse of every file |
56
+ | DuckDB `read_ndjson` full query | ~2,560 ms | Still reads all 1.46 GB, but multithreaded |
57
+ | Node.js partial read (16 KB/file) | ~730 ms | All 1,771 sessions |
58
+ | DuckDB persistent index (query all) | ~49 ms | After one-time build |
59
+ | `node:sqlite` persistent index (query) | ~52 ms | Zero external deps |
60
+ | **pi-fast-resume, first 30 sessions** | **~6 ms** | **Streaming display** |
61
+ | pi-fast-resume, stale-check for incremental | ~74 ms | Compare mtimes against last load |
62
+ | DuckDB CLI → JSON → Node parse | ~121 ms | Shell-out approach |
63
+ | `node:sqlite` FTS5 search | ~0 ms | Indexed full-text search |
64
+
65
+ </details>
66
+
67
+ ## Install
68
+
69
+ **With `pi install`** (recommended):
70
+
71
+ ```bash
72
+ pi install https://github.com/monotykamary/pi-fast-resume
73
+ ```
74
+
75
+ **Manual** — add to `~/.pi/agent/settings.json`:
76
+
77
+ ```json
78
+ {
79
+ "packages": ["git:github.com/monotykamary/pi-fast-resume"]
80
+ }
81
+ ```
82
+
83
+ **Local development** — add the extension path directly:
84
+
85
+ ```json
86
+ {
87
+ "extensions": ["./path/to/pi-fast-resume/fast-resume.ts"]
88
+ }
89
+ ```
90
+
91
+ Reload with `/reload` after any install method.
92
+
93
+ ## Usage
94
+
95
+ ### `/fast-resume` command
96
+
97
+ ```
98
+ /fast-resume Open picker (current project)
99
+ /fast-resume auth bug Open picker pre-filtered to "auth bug"
100
+ ```
101
+
102
+ ### Keyboard shortcut
103
+
104
+ | Shortcut | Action |
105
+ | -------------- | --------------------------- |
106
+ | `Ctrl+Shift+F` | Open the fast resume picker |
107
+
108
+ ### Picker controls
109
+
110
+ Identical to built-in `/resume`:
111
+
112
+ | Key | Action |
113
+ | ------------ | --------------------------------------------- |
114
+ | `↑` / `↓` | Navigate sessions |
115
+ | `Enter` | Switch to selected session |
116
+ | `Esc` | Cancel |
117
+ | `Tab` | Toggle scope — current project ↔ all sessions |
118
+ | `Ctrl+S` | Toggle sort — Threaded / Recent / Fuzzy |
119
+ | `Ctrl+N` | Toggle name filter — All / Named |
120
+ | `Ctrl+P` | Toggle session file path display |
121
+ | `Ctrl+D` | Delete selected session (with confirmation) |
122
+ | `Ctrl+R` | Rename selected session |
123
+ | typing | Filter sessions by text / regex / exact match |
124
+
125
+ ### Scope
126
+
127
+ The picker opens in **current project** scope, showing only sessions whose working directory matches your current `cwd`.
128
+
129
+ Press `Tab` to switch to **all sessions** — shows every session pi knows about, with the project path displayed for each entry.
130
+
131
+ ## How it works
132
+
133
+ ```
134
+ stat() all .jsonl files ──────► sort by mtime ──────► read 16KB of top 30
135
+ (~100ms) (recent first) (~6ms)
136
+
137
+
138
+ ┌─────────────────┐
139
+ │ Show picker │
140
+ │ immediately │
141
+ └────────┬────────┘
142
+
143
+ Background: load rest in batches of 50
144
+ (non-blocking via setImmediate)
145
+ ```
146
+
147
+ 1. **`stat()` all session files** — collect paths and mtimes (~100 ms for 1,700 files)
148
+ 2. **Sort by mtime descending** — most recent sessions first
149
+ 3. **Read first 16KB** of the top 30 files — extract header, name, first user message (~6 ms)
150
+ 4. **Show picker** — user can navigate, filter, and select immediately
151
+ 5. **Background load** — remaining sessions stream in batches of 50, non-blocking
152
+ 6. **Tab to switch scope** — filter to current project or show everything
153
+
154
+ No indexing. No database. No persistent state. Just reads the files on disk.
155
+
156
+ ## Why not index?
157
+
158
+ An indexed approach would be faster for subsequent queries, but at the cost of real complexity:
159
+
160
+ | | Partial read | Indexed (SQLite / DuckDB) |
161
+ | ------------------- | ------------------- | --------------------------------- |
162
+ | First open | 6 ms | 2–4 s (index build) |
163
+ | Subsequent opens | 6 ms (always fresh) | 50 ms + stale check |
164
+ | State to manage | None | Index file, staleness, corruption |
165
+ | Dependencies | None | `node:sqlite` or DuckDB binary |
166
+ | Freshness guarantee | Always | Requires staleness detection |
167
+
168
+ 6 ms is fast enough. The data is always fresh because it's read from disk every time. No staleness bugs, no index corruption, no extra files in `~/.pi/`.
169
+
170
+ ## Hijack mode
171
+
172
+ pi's built-in `/resume` is handled inside the interactive mode's `onSubmit` callback — it returns early before extension commands or input events are ever checked. Extensions **cannot intercept built-in commands** directly.
173
+
174
+ However, pi-fast-resume can **prototype-patch** `InteractiveMode.showSessionSelector` to intercept both the `/resume` command and the `app.session.resume` keybinding. Hijack mode is **on by default** — `/resume` opens the fast picker unless you opt out.
175
+
176
+ - `/resume` opens the **fast** picker instead of the built-in one
177
+ - `Ctrl+Shift+R` (or your mapped key) also opens the fast picker
178
+ - `/fast-resume` is not registered (no duplicate command)
179
+ - `pi -r` / `pi --resume` are **not** affected (they run before the interactive mode starts)
180
+
181
+ ### Disable
182
+
183
+ Create or edit `~/.pi/agent/extensions/pi-fast-resume.json`:
184
+
185
+ ```json
186
+ {
187
+ "hijackResume": false
188
+ }
189
+ ```
190
+
191
+ Then reload with `/reload`. To re-enable, set `hijackResume` to `true` (or delete the key) and reload.
192
+
193
+ ### How it works
194
+
195
+ On load, the extension patches `InteractiveMode.prototype.showSessionSelector` to open the fast picker via `ctx.ui.custom()`. On `session_shutdown` (reload, quit, session switch), the prototype is restored. The patch guards against API changes — if `showSessionSelector` doesn't exist or the runtime can't produce an `ExtensionCommandContext`, it falls back to the original.
196
+
197
+ ## Similar extensions
198
+
199
+ | Extension | Approach | Gap |
200
+ | ---------------------------------------------------------------- | ------------------------------ | ------------------------------------------------- |
201
+ | [pi-sessions](https://github.com/thurstonsand/pi-sessions) | Search, indexing, auto-titling | Session picker still uses `SessionManager.list()` |
202
+ | [pi-session-search](https://github.com/samfoy/pi-session-search) | FTS5 SQLite for search queries | Index for search, not for the picker |
203
+ | [pi-session-manager](https://github.com/Dwsy/pi-session-manager) | Full desktop app (Tauri) | External app, not integrated into pi |
204
+
205
+ None optimize the `/resume` picker itself — they either still fully parse every file or are standalone applications.
206
+
207
+ ## Known Limitations
208
+
209
+ The 16KB partial-read tradeoff that gives pi-fast-resume its speed comes with one functional gap vs. the built-in `/resume`:
210
+
211
+ | Area | Built-in `/resume` | pi-fast-resume | Impact |
212
+ | ---- | ------------------ | -------------- | ------ |
213
+ | **Search depth** | Matches against **all messages** in every session (`allMessagesText`) | Matches against **first message only** (`firstMessage`) + name + id + cwd | A query like `fix oauth` won't find a session where "fix oauth" appears in the 5th message but not the 1st. Name/id/cwd matches still work. |
214
+
215
+ All other features — tree view, regex/exact-phrase search, sort modes, scope toggle, delete, rename, path display — are identical to the built-in picker.
216
+
217
+ ## License
218
+
219
+ [MIT](./LICENSE)