pi-fast-tree
Fast session tree navigator for pi — slim in-memory projection instead of walking full message payloads
Package details
Install pi-fast-tree from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:pi-fast-tree- Package
pi-fast-tree- Version
0.1.2- Published
- Sep 21, 2026
- Downloads
- 307/mo · 155/wk
- Author
- monotykamary
- License
- MIT
- Types
- extension
- Size
- 76 KB
- Dependencies
- 0 dependencies · 0 peers
Pi manifest JSON
{
"image": "https://raw.githubusercontent.com/monotykamary/pi-fast-tree/main/media/cover.svg",
"extensions": [
"./fast-tree.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
⚡ pi-fast-tree
Instant session-tree navigator for Pi
Same /tree. Same keys. A slim projection so fat sessions don't freeze the picker.
Built-in /tree freezes on fat sessions because the picker keeps every SessionEntry — tool arguments, images, whole assistant bodies — on the nodes it flattens, filters, and searches. pi-fast-tree hijacks /tree and paints from a slim projection: ids, topology, a 200-character preview, and a precomputed search haystack.
Selecting a node still calls Pi's navigateTree. Branch summarization, leaf movement, and the transcript rebuild are unchanged.
Why this is not pi-fast-resume
| pi-fast-resume | pi-fast-tree | |
|---|---|---|
| Command | /resume |
/tree |
| Data | thousands of JSONL files | one already-loaded session |
| Win | skip bytes on disk | skip work on in-memory payloads |
| First paint | stream top-N file headers | project the id/parentId skeleton once |
Partial JSONL reads do not help /tree. The current session is already parsed. The freeze is CPU: getTree() copies every entry, flattenTree() stashes every toolCall including arguments, and extractContent does extractFullContent(content).slice(0, 200) on every node, every search keystroke.
Install
pi install npm:pi-fast-tree
From GitHub:
pi install git:github.com/monotykamary/pi-fast-tree
From a local checkout:
bun install
pi install /absolute/path/to/pi-fast-tree
Then /reload.
Hijack mode is on by default — /tree, double-escape, and app.session.tree open the fast navigator.
Usage
/tree Open fast navigator (hijack mode)
/fast-tree Open fast navigator (only when hijackTree is false)
Picker controls
Identical to built-in /tree:
| Key | Action |
|---|---|
| ↑ / ↓ | Navigate visible entries |
| ← / → | Page up/down |
| Ctrl+← / Ctrl+→ or Alt+← / Alt+→ | Fold/unfold or jump between branch segments |
| Shift+L | Set or clear a label |
| Shift+T | Toggle label timestamps |
| Ctrl+X | Copy the selected message |
| Enter | Jump to that point |
| Escape | Cancel (clears search first) |
| Ctrl+O | Cycle filter mode |
| typing | Filter by precomputed preview + label + role |
Filter modes: default, no-tools, user-only, labeled-only, and all. Default comes from treeFilterMode in settings.
Config
All options go in ~/.pi/agent/extensions/pi-fast-tree.json:
| Key | Type | Default | Description |
|---|---|---|---|
hijackTree |
boolean |
true |
When true, /tree and app.session.tree open the fast navigator. Set false to keep built-in /tree and register /fast-tree as a separate command. |
shortcut |
string |
(none) | Standalone keybinding for the fast navigator. Works regardless of hijackTree. Example: "ctrl+shift+t". |
{
"hijackTree": false,
"shortcut": "ctrl+shift+t"
}
Reload with /reload after changing config.
How hijack works
On load, the extension patches InteractiveMode.prototype.showTreeSelector. Built-in /tree returns early inside interactive onSubmit before extension commands run, so this is the same approach as pi-fast-resume. On session_shutdown the prototype is restored. If showTreeSelector is missing, it falls back to the original.
Known limitations
| Area | Built-in /tree |
pi-fast-tree | Impact |
|---|---|---|---|
| Search depth | Matches against extracted message text (still capped at 200 after a full concat) | Matches the capped preview + label + role computed once | A token that only appears after the first 200 characters of a message will not match. |
| Unknown tool args | JSON.stringify(args).slice(0, 40) |
Same 40-char snippet, but only from shallow scalars (nested objects become ...) |
Custom tool rows still show [name: {…}]. Megabyte nested payloads are not stringified. |
| Abort-during-summary | Re-opens /tree on summarization abort |
Cancelled navigateTree notifies and returns |
Escape-during-summary uses pi's navigation result; the picker does not auto-reopen on abort. |
Copy (Ctrl+X) reads the full selected entry via sessionManager.getEntry, so it is not capped.
Development
bun install
bun run check # typecheck + vitest + knip
License
MIT.