pi-fast-resume
Fast session picker for pi — reads headers + first messages from 16KB partial reads instead of full-file parsing
Package details
Install pi-fast-resume from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:pi-fast-resume- Package
pi-fast-resume- Version
1.1.0- Published
- Jun 11, 2026
- Downloads
- not available
- Author
- monotykamary
- License
- MIT
- Types
- extension
- Size
- 72 KB
- Dependencies
- 0 dependencies · 0 peers
Pi manifest JSON
{
"extensions": [
"./fast-resume.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
⚡ pi-fast-resume
Instant session picker for pi
Reads 16KB per file instead of the full JSONL — first results in 6ms.
/resumetakes 5.6 seconds when you have 1,700+ sessions. pi-fast-resume's/fast-resumetakes 6 milliseconds.
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).
──────────────────────────────────────────────────────────
Resume Session (Current Folder) ◉ Current Folder | ○ All Name: All Sort: Threaded
Tab scope · re:<pattern> regex · "phrase" exact
Ctrl+S sort · Ctrl+N named · Ctrl+D delete · Ctrl+P path (off) · Ctrl+R rename
search: fix auth bug_
› Fix the auth bypass in middleware 34 2m
Add rate limiting to API 98 5h
Refactor user service 156 1d
Auth refactor 67 3d
──────────────────────────────────────────────────────────
Benchmarks
Tested with 1,771 sessions, 1.46 GB of JSONL data on disk.
| Approach | First paint | Full load |
|---|---|---|
Built-in /resume |
5,600 ms | 5,600 ms |
node:sqlite indexed query |
52 ms | 52 ms |
| DuckDB persistent index | 49 ms | 49 ms |
| pi-fast-resume (partial read) | 6 ms | ~580 ms |
| DuckDB NDJSON full scan | 2,560 ms | 2,560 ms |
| Approach | Time | Notes |
|---|---|---|
SessionManager.listAll() (current) |
~5,600 ms | Full parse of every file |
DuckDB read_ndjson full query |
~2,560 ms | Still reads all 1.46 GB, but multithreaded |
| Node.js partial read (16 KB/file) | ~730 ms | All 1,771 sessions |
| DuckDB persistent index (query all) | ~49 ms | After one-time build |
node:sqlite persistent index (query) |
~52 ms | Zero external deps |
| pi-fast-resume, first 30 sessions | ~6 ms | Streaming display |
| pi-fast-resume, stale-check for incremental | ~74 ms | Compare mtimes against last load |
| DuckDB CLI → JSON → Node parse | ~121 ms | Shell-out approach |
node:sqlite FTS5 search |
~0 ms | Indexed full-text search |
Install
With pi install (recommended):
pi install https://github.com/monotykamary/pi-fast-resume
With npm:
npm install pi-fast-resume
Manual — add to ~/.pi/agent/settings.json:
{
"packages": ["git:github.com/monotykamary/pi-fast-resume"]
}
Local development — add the extension path directly:
{
"extensions": ["./path/to/pi-fast-resume/fast-resume.ts"]
}
Reload with /reload after any install method.
Usage
/fast-resume command
/fast-resume Open picker (current project)
/fast-resume auth bug Open picker pre-filtered to "auth bug"
Keyboard shortcut
There are two ways to bind a key to the fast resume picker:
Option 1: Rebind app.session.resume (hijack mode only)
In hijack mode (default), the fast picker replaces /resume — bind app.session.resume in ~/.pi/agent/keybindings.json:
{
"app.session.resume": "alt+u"
}
Option 2: Standalone shortcut (works regardless of hijack mode)
Set the shortcut key in ~/.pi/agent/extensions/pi-fast-resume.json:
{
"shortcut": "ctrl+shift+f"
}
This registers an independent shortcut that opens the fast picker without overriding the built-in /resume. Works in both hijack and non-hijack modes.
See keybindings.md for the key format and all available actions.
Picker controls
Identical to built-in /resume:
| Key | Action |
|---|---|
↑ / ↓ |
Navigate sessions |
Enter |
Switch to selected session |
Esc |
Cancel |
Tab |
Toggle scope — current project ↔ all sessions |
Ctrl+S |
Toggle sort — Threaded / Recent / Fuzzy |
Ctrl+N |
Toggle name filter — All / Named |
Ctrl+P |
Toggle session file path display |
Ctrl+D |
Delete selected session (with confirmation) |
Ctrl+R |
Rename selected session |
| typing | Filter sessions by text / regex / exact match |
Scope
The picker opens in current project scope, showing only sessions whose working directory matches your current cwd.
Press Tab to switch to all sessions — shows every session pi knows about, with the project path displayed for each entry.
How it works
stat() all .jsonl files ──────► sort by mtime ──────► read 16KB of top 30
(~100ms) (recent first) (~6ms)
│
▼
┌─────────────────┐
│ Show picker │
│ immediately │
└────────┬────────┘
│
Background: load rest in batches of 50
(non-blocking via setImmediate)
stat()all session files — collect paths and mtimes (~100 ms for 1,700 files)- Sort by mtime descending — most recent sessions first
- Read first 16KB of the top 30 files — extract header, name, first user message (~6 ms)
- Show picker — user can navigate, filter, and select immediately
- Background load — remaining sessions stream in batches of 50, non-blocking
- Tab to switch scope — filter to current project or show everything
No indexing. No database. No persistent state. Just reads the files on disk.
Why not index?
An indexed approach would be faster for subsequent queries, but at the cost of real complexity:
| Partial read | Indexed (SQLite / DuckDB) | |
|---|---|---|
| First open | 6 ms | 2–4 s (index build) |
| Subsequent opens | 6 ms (always fresh) | 50 ms + stale check |
| State to manage | None | Index file, staleness, corruption |
| Dependencies | None | node:sqlite or DuckDB binary |
| Freshness guarantee | Always | Requires staleness detection |
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/.
Hijack mode
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.
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.
/resumeopens the fast picker instead of the built-in oneCtrl+Shift+R(or your mapped key) also opens the fast picker/fast-resumeis not registered (no duplicate command)pi -r/pi --resumeare not affected (they run before the interactive mode starts)
Config options
All options go in ~/.pi/agent/extensions/pi-fast-resume.json:
| Key | Type | Default | Description |
|---|---|---|---|
hijackResume |
boolean |
true |
When true, /resume and app.session.resume open the fast picker. Set false to use the built-in picker instead and keep /fast-resume as a separate command. |
shortcut |
string |
(none) | Register a standalone keybinding to open the fast picker. Works regardless of hijackResume. Example: "ctrl+shift+f" or "alt+u". |
Example:
{
"hijackResume": false,
"shortcut": "alt+u"
}
Reload with /reload after changing config.
How it works
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.
Similar extensions
| Extension | Approach | Gap |
|---|---|---|
| pi-sessions | Search, indexing, auto-titling | Session picker still uses SessionManager.list() |
| pi-session-search | FTS5 SQLite for search queries | Index for search, not for the picker |
| pi-session-manager | Full desktop app (Tauri) | External app, not integrated into pi |
None optimize the /resume picker itself — they either still fully parse every file or are standalone applications.
Known Limitations
The 16KB partial-read tradeoff that gives pi-fast-resume its speed comes with one functional gap vs. the built-in /resume:
| Area | Built-in /resume |
pi-fast-resume | Impact |
|---|---|---|---|
| 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. |
All other features — tree view, regex/exact-phrase search, sort modes, scope toggle, delete, rename, path display — are identical to the built-in picker.