recall
Search every past coding-agent session on your machine, from any checkout, in milliseconds.
You solved this before. Two months ago, in a branch you've since deleted, in a session you can't find. Your agent doesn't remember it, and neither do you. Only that it happened.
recall finds it. One query reaches every session your coding agent has ever recorded on this
machine, across every checkout, clone and worktree of the repo you're standing in. No index, no
per-project setup, no daemon. Ask from a shell, or let your agent ask over MCP.
$ cd ~/src/payments # the main checkout
$ recall find idempotency
2 sessions · 5 hits for "idempotency"
6e2b8d15-4c70-4a93-b8e1-05d7c2914f63 2026-06-03 github.com/acme/payments hotfix/dedupe 4 hits in 3 turns of 6 turns
human Duplicate charges again. Two merchants hit the same «idempotenc»y key and one of them saw the other's receipt. How do we scope…
assistant Scope the «idempotenc»y key per merchant, never globally. The key is a string the… ×2
assistant …a row it cannot interpret. The existing primary key is the bare «idempotenc»y_key, so the migration is: add merchant_id to the table,…
3b8a2c94-6d17-4e05-8f2b-c091d67a4e38 2026-07-21 github.com/acme/payments main 1 hit of 4 turns
assistant …charge may well have succeeded — so a retry there needs the «idempotenc»y key to be safe, and without one it must not be retried at all.
── 3 sessions · 3 searched · conversation only — tool output NOT searched (--results)
── live to 2026-05-12 · archived before that · refreshed just now
── scanned 3.0 KB · 14 turns · 6 ms
── 1.0 KB · ~264 tokens
That answer came out of a different checkout (payments-hotfix, a branch that no longer
exists locally), found from payments without naming it. Every block in this README is real
output you can reproduce: ./scripts/demo.sh builds a fixed corpus and runs them.
Why it exists
Claude Code keys sessions by checkout path (~/.claude/projects/-home-you-src-payments/…).
One logical repository sprawls across clones, worktrees and relocations, so a search scoped to
the directory you happen to be in silently misses the answer. That is the failure this was built
for: a conclusion hunted for in one checkout that turned out to live in another checkout of the
same repo.
recall resolves a repo by its git remote, not its path, so every checkout is one corpus.
Install
curl -fsSL https://raw.githubusercontent.com/mayberuk/recall/main/install.sh | sh
Downloads the binary for your platform, verifies it against the release's published sha256, and
installs to ~/.local/bin. It registers recall with nothing: that is a separate, deliberate step.
go install github.com/mayberuk/recall/cmd/recall@latest # needs Go 1.25+
# from a checkout
git clone https://github.com/mayberuk/recall.git && cd recall
./scripts/install-from-source.sh # builds, verifies, installs to ~/.local/bin
Pin a version with RECALL_VERSION=v1.1.0, or change the destination with RECALL_INSTALL_DIR.
Pre-built binaries for Linux, macOS and Windows on amd64 and arm64 are attached to
every release.
recall is a single static binary (CGO_ENABLED=0) with two dependencies and no configuration.
Nothing to initialize: a directory you never set up is exactly the one you'll want to search.
Let your agent ask
recall speaks the Model Context Protocol over stdio. One
command registers it and installs the skill that makes an agent actually reach for it:
recall mcp install claude-code # or codex · gemini · copilot · cursor · opencode · windsurf
recall mcp config <client> prints the same entry and writes nothing, if you'd rather paste it
yourself. Five read-only tools (recall_find, recall_turns, recall_show, recall_when,
recall_guide), each running the CLI verb it's named after, so a tool call and a command line can
never disagree about what the corpus holds. Full client-by-client detail: docs/mcp.md.
Paste this to any agent with shell access:
Install
recall, a CLI that searches my past coding-agent sessions, by runningcurl -fsSL https://raw.githubusercontent.com/mayberuk/recall/main/install.sh | sh. Then register it with yourself by runningrecall mcp install <your client>. Runrecall mcp config --helpto see the supported client names and pick the one that matches you. Finally runrecall guideand tell me in one line what it can answer.
What it answers
Searching in the repo you're in but the answer is elsewhere? It says so, and hands you the query:
$ recall turns "connection pool" --limit 1
no turns carry "connection pool" in github.com/acme/payments
found elsewhere: 5 hits in 1 other repo
github.com/acme/infra 5 hits · 1 session
run: recall find 'connection pool' --all
Place a topic in time with recall when:
$ recall when "rate limit"
"rate limit" first 2026-05-12 · last 2026-07-21 · 8 hits in 2 sessions
2026-05 6 hits · 1 session
2026-07 2 hits · 1 session
More worked examples, with output: docs/examples.md.
Interactive, if you have fzf
source shell/recall.zsh
recall-fzf idempotency # live finder; prints the chosen session id
recall show "$(recall-fzf --ids idempotency | head -1)"
Type and the list re-searches; ctrl-o opens the whole session in your pager, enter prints
its id and exits, so it composes with everything above. recall owns matching and ranking
(fzf's own are turned off), which is why the ordering you see is still concentration ranking.
With no terminal the same function prints ranked records to stdout instead, so it works in a
pipeline: recall-fzf --ids <query> gives one session id per line.
Fast enough that an index would be a liability
Of everything Claude Code writes to disk, the actual conversation is about 3%: 47.8 MB of a 1.52 GB session store. The rest is tool output stored twice, thinking-block signatures and attachments. Searching that 3% is quick enough that there is nothing left for an index to win.
On a real session store
143 sessions · 192,000 turns · 1.52 GB · AMD Ryzen 7 5700X3D.
On a corpus you can reproduce
Every figure below comes from a corpus generated from a fixed seed rather than from anyone's
session store, so make bench on your machine measures the same bytes. Small is 5.1 MB of
transcript across 17 files (2,972 turns); medium is 51.4 MB across 56 files (29,564 turns).
The whole binary, invoked end to end (process start, freshness check, search, render, exit):
Most of those milliseconds are process start and getting the corpus into memory. The search itself is the small part:
Finding a term across 51 MB of transcript costs 261 allocations. That is what having no index buys: no B-tree to walk, no posting lists to merge, nothing to hold in sync with the corpus.
The thresholds it has to clear
make bench-gate fails the build on a breach. The limits sit where the architecture would come
into question (a search you can feel, an update you would notice), not next to the current
measurement, so a breach means linear scanning stopped being viable rather than that a benchmark
wobbled.
Allocation counts are gated separately, against a committed baseline, because they are the same
number on any machine where a nanosecond is not. make bench-compare fails on a 2% regression.
Full tables (32 micro benchmarks, 48 end-to-end scenarios, and the corpus they ran against) are
in bench/RESULTS.md, regenerated by make bench.
So there is no index: nothing to rebuild, nothing to go stale, no corruption class to guard against. A SQLite FTS5 prototype was built and worked; it lost on exactly those grounds.
Staying current
recall update # verify the sha256, replace this binary
recall update --check # report what is available, install nothing
recall makes no network request of any kind except from recall update and from recall doctor, which refreshes a cached version number once a day. A search reads that cache and never
asks anybody, so there is still no daemon, no background process, and no cost to an answer.
When a newer release is cached, commands print one line about it on stderr, only to a terminal,
at most once a day. It never touches stdout, so it cannot land in a piped answer or an agent's
context. RECALL_NO_UPDATE_CHECK set to any non-empty value turns off both the check and the
notice. Full detail: Staying current.
It tells you what it didn't search
Every searching command ends in a ── footer naming what it left out: tier, scope, exclusions,
and what the search cost. If the footer doesn't mention a narrowing, that narrowing didn't
happen. Treat it as the coverage contract, not decoration.
── 3 sessions · 3 searched · conversation only — tool output NOT searched (--results)
── live to 2026-05-12 · archived before that · refreshed just now
── scanned 3.0 KB · 14 turns · 6 ms
── 1.0 KB · ~264 tokens
That last line is the one agents care about: every answer states its own size and rough token
cost, and --budget 2000 shapes an answer down to fit. Recovering a conclusion should not cost
more context than re-deriving it.
Agents it reads
--provider auto (the default) detects who is asking; a name pins one; all searches every
registered agent at once. Naming an agent this build cannot read is refused outright rather than
answered from a corpus that isn't theirs.
Codex archives to its own tree, so adding it never rebuilds an existing Claude Code archive.
Documentation
License
MIT. See LICENSE.