# Open-source memory for coding agents, synced over SSH

> Source: <https://github.com/vshulcz/deja-vu/>
> Published: 2026-07-15 16:15:03+00:00

**Your agents already solved this. deja finds it.**

Claude Code, Codex and opencode write every conversation to local files — gigabytes of debugged problems and design decisions you can't search. deja is a zero-dependency binary that turns those histories into a memory layer:

Search |
`deja "connection pool exhausted"` — 7–9 ms over gigabytes, retroactive: months of logs from before you installed it |
Agent recall |
MCP `recall` tool — the agent answers "we fixed this three weeks ago" instead of re-debugging, across harnesses |
Auto-recall |
`install --auto` adds a SessionStart hook: relevant memory lands in context before you ask |
Redaction |
API keys, JWTs, private keys are stripped at index time — the cache is safe to keep |
Stats |
`deja stats` — your agent work, wrapped: harnesses, top projects, activity sparkline |
Share |
`deja share <id>` — hand a colleague a sanitized digest of a session, secrets already scrubbed |
Sync |
`deja sync export/import` — move memory between machines, append-only, idempotent |

One binary. No models to download, no services to run, nothing leaves your machine. (opencode indexing shells out to the `sqlite3`

CLI, preinstalled on macOS and most Linux distros.)

```
curl -fsSL https://raw.githubusercontent.com/vshulcz/deja-vu/main/install.sh | sh
```

or:

```
go install github.com/vshulcz/deja-vu/cmd/deja@latest   # Go
npx @vshulcz/deja-vu "query"                            # npm, no install
brew install vshulcz/tap/deja-vu                        # Homebrew
```

Wire it into the agents you use (edits config, keeps a `.bak`

):

```
deja install --all          # MCP recall for claude-code, codex, opencode
deja install claude-code --auto   # + SessionStart auto-recall hook
```

That's it. Next session, ask your agent:

have we dealt with jwt refresh rotation before? check your memory

— or with `--auto`

, don't ask: the agent starts each session already knowing what you solved in that project.

``` bash
$ deja "jwt refresh token"
[claude] api        · Jul 8 · 8f31c0a9 — 2 matches
  login started failing after refresh token rotation; jwt kid mismatch in tests
  fixed by reloading jwks cache after rotateKey and adding a clock-skew test
[codex]  web        · Jul 1 · b77d91e2 — 1 match
  refresh token cookie needed SameSite=Lax in local callback flow
```

| Command | What it does |
|---|---|
`deja <query>` |
Search all histories. Multi-word = AND, substrings match (`code` finds `opencode` ). `--re` , `--harness` , `--project` , `--since 30d` , `--role` , `--json` . |
`deja ctx <query>` |
Compact markdown digest of the best match — pipe it into a prompt. |
`deja share <id>` |
Sanitized session digest for a colleague: secrets redacted, tool noise stripped. |
`deja stats` |
Totals, per-harness split, top projects, monthly sparkline. `--json` too. |
`deja sync export <dir> [--full]` / `import <dir>` / `ssh <host> [--pull]` |
Move memory between machines — via a shared folder or one ssh command. Watermarked, append-only, idempotent. |
`deja show <id>` / `deja last [n]` |
Read one session / list recent ones. |
`deja sources` |
Discovered stores, sizes, message and redaction counts. |
`deja mcp` |
The stdio MCP server (what `deja install` wires in). |
`deja warmup` |
Build/refresh the index without searching — handy in cron or shell startup. |

Context piping without MCP:

```
claude "Prior context: $(deja ctx 'database migration')"
```

Point both machines at one shared folder (Syncthing, iCloud, a git repo — anything that moves files):

```
deja sync export ~/Sync/deja   # machine A: appends new batches since last export
deja sync import ~/Sync/deja   # machine B: picks up what it hasn't seen
```

Or skip the shared folder when the other machine is a ssh hop away:

``` python
deja sync ssh mini          # push new records to mini and import them there
deja sync ssh mini --pull   # fetch mini's new records into this machine
```

`ssh`

mode uses your system ssh/scp and the `deja`

binary on the remote (looked up on PATH, falling back to `~/.local/bin/deja`

).

Batches are plain JSONL, redacted on the way out. Import is idempotent, so keep the folder as an append-only log and run both commands from cron if you like. Records never echo back to their origin. `--full`

re-exports everything regardless of watermarks — useful when adding a machine after old batches are gone. Synced sessions show up under `imported:<project>`

in search, `recall`

, and session-start auto-recall.

`deja install --all`

wires up MCP recall and the session-start hook. To make
the agent reach for memory on its own, add this to your `CLAUDE.md`

/
`AGENTS.md`

:

```
Before debugging or re-implementing something, run `deja "<query>"` (or the
MCP recall tool) — past agent sessions across Claude Code, Codex and opencode
are indexed locally. Cite what you reuse.
```

| Tool | Arguments | Returns |
|---|---|---|
`recall` |
`query` , `harness?` , `limit?` |
Dense matching snippets, ≤4KB — cheap on context. |
`recall_context` |
`query` |
Markdown digest of the best-matching session. |

With `--auto`

, a SessionStart hook also feeds the current project's recent memory in automatically — read-only, capped at 2KB, and it never delays or breaks agent startup.

Credentials are redacted at index time: AWS keys, generic `api_key=`

/`token=`

assignments, bearer tokens and raw JWTs, PEM private key blocks, provider tokens (`ghp_`

, `sk-`

, `npm_`

, `xox.`

, `AIza`

), and `scheme://user:pass@host`

URLs. The value is replaced with `[redacted:<kind>]`

; surrounding text stays searchable. `deja sources`

shows per-store counts. Opt out with `DEJA_NO_REDACT=1`

(unsafe). `deja share`

and `deja sync export`

re-apply redaction on the way out.

| Harness | Store | Status |
|---|---|---|
| Claude Code | `~/.claude/projects/**/*.jsonl` |
✅ |
| Codex CLI | `~/.codex/sessions/**` + `history.jsonl` |
✅ |
| opencode | `~/.local/share/opencode/opencode.db` |
✅ |
| aider, Gemini CLI |
|

Custom locations via `DEJA_CLAUDE_ROOT`

, `DEJA_CODEX_ROOT`

, `DEJA_OPENCODE_DB`

, `DEJA_INDEX_DIR`

.

Measured on a real corpus — 1,250+ sessions, ~3.3GB across three harnesses:

| Warm search | 7–9 ms typical, ~40 ms worst-case |
| Cold index (once) | ~10 s |
| Index size | ~2.4% of corpus |

The index is incremental: when a session file grows, only that file is re-read.

Local inverted index in `~/.cache/deja`

: parse JSONL/SQLite stores → redact credentials → `records.bin`

+ token buckets → `manifest.json`

tracks per-file state so repeat runs only ingest what changed. The MCP server, stats, share and sync all read the same index. Details: [docs/ARCHITECTURE.md](/vshulcz/deja-vu/blob/main/docs/ARCHITECTURE.md).

**Privacy:** no network path exists in the indexing or search code. Local files in, local cache out.

**Does anything leave my machine?** No. There is no network code in the tool. `sync`

writes files to a directory you choose; moving them is up to you.

**How is this different from cass?**
[cass](https://github.com/Dicklesworthstone/coding_agent_session_search) is the kitchen-sink take on session search: 22 providers, Rust, optional semantic embeddings, a TUI. deja is the opposite bet — one small Go binary, pure lexical, three harnesses, zero setup — plus the memory-layer pieces around it: auto-recall, redaction, share, sync.

**And from MemPalace / Mem0 / Letta?**
Those are memory *platforms*: embeddings, vector stores, capture hooks or APIs that record going forward. deja has no capture step at all — it indexes what your agents already wrote to disk, including months of history from before you installed it. They can coexist.

**What about secrets already in my logs?** They stay in the original harness files (that's your agent's data), but they don't enter deja's index, digests, shares or sync exports.

**What about Windows?** Builds exist, CI runs the suite on Windows; macOS/Linux are the battle-tested paths. Field reports welcome: [#9](https://github.com/vshulcz/deja-vu/issues/9).

**Can I exclude a project?** Not yet — planned as `--exclude`

([#8](https://github.com/vshulcz/deja-vu/issues/8)). Today you can point `DEJA_*_ROOT`

at a filtered copy.

**How do I wipe everything?**

```
deja uninstall --all
rm -rf ~/.cache/deja
```

`make build test lint`

— see [CONTRIBUTING.md](/vshulcz/deja-vu/blob/main/CONTRIBUTING.md). Adding a harness is one parser file: [docs/ARCHITECTURE.md](/vshulcz/deja-vu/blob/main/docs/ARCHITECTURE.md#adding-a-harness). Good first issues are labeled.

MIT © [Vladislav Shulcz](https://github.com/vshulcz)
