@pify/recall
extensionmaintainedFull-text search across your past pi sessions: a session_search tool over the local session logs, so the agent can recall what it did before
by — · v0.5.0 · published 18h ago
$ pi install npm:@pify/recallSignals
Download trend
No downloads in the last 12 weeks.
README
@pify/recall
Full-text search across your past pi sessions. A session_search tool over the local session logs, so the agent can recall what it did before — a decision, a fix, a command, a discussion — instead of re-deriving it.
Part of the Pify suite. Install with pify install recall or pi install npm:@pify/recall.
Why
Everything you and the agent worked through is already on disk: pi writes every session to a JSONL log. But the moment a session ends, that knowledge is unreachable — the next session starts blank, and the agent happily re-solves a problem it cracked last week. Recall makes those logs searchable so "we did this before" becomes a lookup instead of a redo.
The tool
session_search — full-text search over the user prompts and assistant messages in your past sessions.
| Parameter | Type | Notes |
|---|---|---|
query | string | Distinctive terms — an identifier, an error string, a feature name |
limit | number, optional | Max results (default 10, max 25) |
It returns the most relevant snippets with their session id and date, ranked by how many of your query terms each message contains (newest first on a tie). Query with the terms that make a session distinctive, not a whole sentence.
3 matches for "oauth token refresh", most relevant first:
[2026-09-10 · assistant · 2026-09-10]
…added refreshToken() to auth.ts…
/recall shows the index status; /recall <query> searches from the command line; /recall reindex rebuilds from scratch.
This session, not past ones
session_history — the same kind of search over the current session's complete branch: every user, assistant and tool-result entry, including everything compaction has already summarized away.
| Parameter | Type | Notes |
|---|---|---|
query | string | A command, an error string, a file name |
limit | number, optional | Max matching entries (default 8, max 25) |
context | number, optional | Characters of snippet around each match (default 220, max 1000) |
The index above deliberately skips the live session, and once @pify/compact (or pi itself) compacts, only the summary stays in context — but pi keeps the whole branch, append-only. The exact detail the summary dropped (what a command printed, the error string, the snippet you read) was still on disk and unreachable. This tool greps the branch, read fresh on every call, and labels each hit with its position and entry id. Split to remember: session_search is past sessions (indexed); session_history is this one (live, never indexed). (From can1357/oh-my-pi's history://current/full.)
Session names
A session with no display name is an opaque log id — in pi's session picker, and in this package's own results. So the first thing you type becomes the session's name, once, and only when nothing else has named it: an existing name (yours, or another extension's) always wins, and the attempt happens at most once per session. A bare slash command (/resume) is skipped — it says nothing about the work.
Results are then labelled with that name instead of the id:
[add a retry to the uploader · user · 3d ago]
...the matching snippet...
Names are read back out of pi's own session_info entries, so a session you named by hand is labelled correctly too. A session that never got one — everything recorded before this package was installed, or with auto-naming opted out — is labelled after the first thing typed in it (the same rule, applied when the log is read), and only a session with no usable first prompt falls back to its raw id. A fresh session is named from the prompt that starts it (pi has not appended that prompt to the session yet when the hook runs, which is why earlier versions never named anything new); a resumed session keeps its original first prompt as the name. Opt out with PIFY_RECALL_NO_AUTONAME=1. (Idea from trim21/pi-extensions.)
Auto-recall, if you turn it on
PIFY_RECALL_AUTORECALL=1 makes the first substantial prompt of a session (twenty-plus characters, three-plus distinct terms, not a slash command) run against the index unprompted, and hands the model the top three past-session snippets as a hidden note framed as prior context — verify before relying on it, session_search for more. Once per session, capped at a couple of thousand characters, and never in the system prompt, so nothing about the cache changes. Off by default because unprompted context is a cost you should choose.
Claude Code's sessions, if you turn it on
PIFY_RECALL_CLAUDE=1 also indexes Claude Code's own logs for the current repository (~/.claude/projects/<cwd-slug>/*.jsonl): user and assistant text only, tool calls and results skipped, labelled claude · <first prompt> in results so you can tell them apart. Current repository only, so the corpus stays bounded; the format is parsed tolerantly and anything unrecognised is skipped. Off by default; the index stays local and zero-dependency either way.
How it works
At session start (and lazily before each search) it syncs an inverted index of your session logs: only files whose size or mtime changed are re-read, so a steady corpus costs almost nothing. The live session is not read at all while it is live — it grows every turn, so it could never look unchanged, and one changed file used to mean rebuilding every posting and rewriting the whole index on every search; it is indexed once it is no longer the one being written, and it never appears in its own results either way. The index is one JSON file under <agentDir>/recall/, written atomically; a corrupt or wrong-version file is simply rebuilt. The corpus is bounded (newest sessions win the budget) so it never grows without limit. Words are words: a session that talks about constructor or __proto__ indexes and searches like any other (the postings map has no prototype to collide with).
Pure JavaScript, no native FTS engine. node:sqlite would be elegant, but it is absent under Bun and flag-gated on Node < 24 — a sqlite index would silently fail for a large share of users. A plain inverted index works on every runtime the suite supports, with zero runtime dependencies. Everything is local: no network, no LLM tokens.
Results are snippets from earlier sessions for orientation, not current truth — the tool's own guidance tells the agent to verify against the code as it is now before acting on them.
License
MIT © Pify maintainers