npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

pi-fast-resume

v1.4.6

Published

Fast session picker for pi — reads headers + first messages from 16KB partial reads instead of full-file parsing

Readme

⚡ pi-fast-resume

Instant session picker for pi

Reads just enough of each file to show the title row — first results in 6ms.

pi extension license


/resume takes 5.6 seconds when you have 1,700+ sessions. pi-fast-resume's /fast-resume takes 6 milliseconds.

Same picker UI and keybindings as /resume. The difference is pi-fast-resume never parses the full JSONL. For each file it streams complete lines forward just until the first user message (the title), then reads a bounded tail near EOF to recover the latest session name (which pi appends on /rename). Everything between 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 |

The all-dirs stat() pass (~100 ms at scale) now runs after first paint, so first paint no longer depends on total session count — only on the current project's session dir.

| 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 streaming partial read (this extension) | ~370 ms | All ~2,550 sessions; stops at first user message + bounded tail | | 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 npm:pi-fast-resume

Or install from GitHub:

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() current dir's .jsonl ──► sort by mtime ──► stream top 30 forward
      (~cheap, one dir)          (recent first)        (~6ms)
                                                            │
                                                            ▼
                                                    ┌─────────────────┐
                                                    │  Show picker    │
                                                    │  immediately    │
                                                    └────────┬────────┘
                                                             │
                          Background (after first paint):
                          1. stream the rest of the current dir in batches of 50 (rows appear as they load)
                          2. stat() ALL session dirs (~100ms at scale) — deferred off the critical path
                          3. forward-load all-scope headers in batches of 50 (non-blocking via setImmediate)
                          4. resolve rename names in the background (skip header-only files; bound each tail read)
  1. stat() the current project's session dir — collect paths and mtimes (cheap, one directory)
  2. Sort by mtime descending — most recent sessions first
  3. Stream the top 30 forward line by line until the first user message (~6 ms for the first screen)
  4. Show picker — user can navigate, filter, and select immediately
  5. Background load — after first paint, stream the remaining current-scope sessions in batches of 50, then stat every session dir and forward-load the all-scope headers, non-blocking
  6. 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.

  • /resume opens the fast picker instead of the built-in one
  • Ctrl+Shift+R (or your mapped key) also opens the fast picker
  • /fast-resume is not registered (no duplicate command)
  • pi -r / pi --resume are 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 partial-read tradeoff that gives pi-fast-resume its speed comes with functional gaps vs. the built-in /resume. The forward stream reads exactly as many bytes as the first user message needs (no fixed window), so oversized first messages — <skill> injections, long pastes, base64 images — are parsed correctly. A rename is recovered only if it lives within a bounded tail near EOF (32 KB by default); a rename buried under more continued activity than that falls back to firstMessage.

| 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.

License

MIT