dejavu
Per-repo memory for coding agents. dejavu stores decisions, conventions, gotchas and open threads in .dejavu/ inside your repo. Claude Code, Cursor and Codex read and write the same memory through one MCP server, so a new session starts with what the last one learned.
Entries are markdown files you commit and review in PRs. A local SQLite index (built into Node, no native modules) makes search fast and can be rebuilt from the markdown at any time. Nothing calls a paid API.
Install
dejavu is not on npm. Install it from a clone (Node 22.13 or newer):
git clone https://github.com/aduthekaddu/dejavu.git
cd dejavu
npm ci
npm run build
npm link # puts `dejavu` on your PATH
Then, in any git repo:
cd ~/code/my-app
dejavu init
git add .dejavu && git commit -m "Add dejavu memory"
Connect your agent
Each tool starts dejavu mcp in the repo. The server finds the repo root from its working directory, or from DEJAVU_ROOT if set.
Claude Code
claude mcp add --scope project dejavu -- dejavu mcp
--scope project writes .mcp.json in the repo, so teammates get the server too. Drop it to keep the server to yourself.
Cursor
Create .cursor/mcp.json in the repo:
{
"mcpServers": {
"dejavu": {
"command": "dejavu",
"args": ["mcp"],
"env": { "DEJAVU_ROOT": "${workspaceFolder}" }
}
}
}
Codex
Add to ~/.codex/config.toml:
[mcp_servers.dejavu]
command = "dejavu"
args = ["mcp"]
Codex starts MCP servers in the directory you launch it from, so run codex from the repo.
Tell the agent to use it
Add this to AGENTS.md or CLAUDE.md, or run dejavu export --write AGENTS.md, which also inlines the current memory:
At the start of a task, call the dejavu `context` tool with your task and the files you expect to touch.
When you make a decision, find a convention, or hit a gotcha, save it with `remember`.
Before working on a directory in parallel with other agents, `claim` it. Check `read_cache` before summarizing a file.
MCP tools
| Tool | What it does |
|---|---|
context |
Returns a brief of the most relevant entries that fits budget tokens. Takes optional task and files. |
recall |
Full-text search. Every word must match, as a prefix. |
remember |
Saves an entry: kind, title, and optionally body, why, rejected, files, tags, pinned. |
claim / release |
Claims a path for ttlSec seconds (default 1800). Fails if another agent holds an overlapping path. |
read_cache |
With path only, returns another agent's summary of that file if its content is unchanged. With summary, stores yours. |
CLI
dejavu add decision "Store money in cents" --why "floats lose pennies" \
--rejected "decimal strings" --files src/billing.ts --tags billing --pin
dejavu add gotcha "CI runs in UTC" --tags ci
dejavu search billing
dejavu show # list all
dejavu show store-money-in-cents-1a2b3c
dejavu context --budget 800 --task "add refunds" --files src/billing.ts
dejavu capture --since main # propose entries from the diff, ask about each
echo "- decision: retry 3 times because the API flakes" | dejavu capture --since HEAD~3 --yes
dejavu claim src/auth --ttl 900 --agent claude-1
dejavu claim # list active claims
dejavu release src/auth --agent claude-1
dejavu prune # offer to delete stale entries
dejavu export --write CLAUDE.md # add or refresh the memory block
dejavu rebuild # rebuild index.db from the markdown
Run dejavu <command> --help for every flag.
How it works
Entries
There are four kinds:
decisionhas a title, the reason (why) and the alternatives you rejected.conventionis a rule the codebase follows.gotchais something that surprised you and will surprise the next agent.open-threadis unfinished work.
Every entry can link files and commits, carry tags, and be pinned. Each one is a file at .dejavu/entries/<id>.md:
---
id: store-money-in-cents-1a2b3c
kind: decision
title: Store money in cents
tags:
- billing
files:
- src/billing.ts
commits: []
pinned: true
createdAt: 2026-10-26T04:00:00.000Z
baseCommit: 9f2c1e0...
---
# Store money in cents
## Why
floats lose pennies
## Alternatives rejected
- decimal strings
You can edit these by hand. The next command notices the content changed and reindexes.
What is committed
.dejavu/.gitignore keeps index.db and events.jsonl out of git. The markdown is the shared record. The index and the event log are local to one machine.
Ranking and the token budget
context scores each entry:
- 2 points for each word of the task that appears in the entry (common words like "the" are ignored).
- For each touched file, 6 points when the entry links that exact file, 4 when it links a parent directory, 2 when it links a file in the same directory.
- Up to 1 point for recency, halving every 30 days.
Pinned entries always come first. Ties break on newer createdAt, then on id, so the same inputs give the same brief. Entries are added in that order until the next one would go over budget; an entry that is too big is skipped and smaller ones after it can still fit. Tokens are estimated as characters divided by 4.
Staleness
Each entry records baseCommit, the HEAD when it was written. dejavu runs git diff --numstat <baseCommit> on the linked files, which counts committed and uncommitted changes. When 20 or more lines changed, or a linked file was deleted, the entry is stale. context and recall show it with a warning, and dejavu prune offers to delete it.
Capture
dejavu capture --since <ref> reads git diff <ref> and proposes:
- an
open-threadfor each addedTODOorFIXMEcomment, - a
gotchafor each addedHACK,XXX,NOTEorWARNINGcomment, - a
decisionfor each new dependency in apackage.json, - a
conventionfor changed lint, format or tsconfig files.
It also reads a summary from stdin. Lines that start with decision:, convention:, gotcha: or todo: become entries, and a decision splits into title and why at the word "because". Agents can write this summary at the end of a session.
For smarter proposals, pass --proposer ./my-proposer.js. The module's default export gets { diff, summary } and returns proposals in the same shape. It can call any model you like. dejavu never calls one itself.
Parallel agents and events.jsonl
Claims and saved entries are appended to .dejavu/events.jsonl, one JSON object per line. Other tools can tail this file. Format version 1:
{"v":1,"type":"claim","at":"2026-10-26T04:00:00.000Z","agent":"claude-1","scope":"src/auth","ttlSec":1800,"note":"token refresh"}
{"v":1,"type":"release","at":"2026-10-26T04:10:00.000Z","agent":"claude-1","scope":"src/auth"}
{"v":1,"type":"remember","at":"2026-10-26T04:11:00.000Z","agent":"claude-1","id":"store-money-in-cents-1a2b3c","kind":"decision","title":"Store money in cents"}
atis an ISO 8601 UTC time.noteis optional.- A claim is active from
atuntilat + ttlSec, unless the same agent releases the same scope. - Scopes are repo-relative paths without a trailing slash.
src/authoverlapssrc/auth/login.tsbut notsrc/authz..covers the whole repo. - Claiming again renews the claim.
- Readers should skip lines they cannot parse and types they do not know.
The read cache lives in index.db, keyed by the SHA-256 of the file content. A summary is only returned while the file still has the exact content it was written for.
Development
npm ci
npm run typecheck && npm run lint && npm test && npm run build
Tests use Vitest and run against real temporary git repos. They cover ranking and budgets, staleness, the markdown and SQLite round trip, capture, claims, the read cache, the MCP tools over the SDK's in-memory transport, and the built CLI end to end, including a real stdio MCP session.
Limits
- Token counts are an estimate, not a real tokenizer.
- Search is keyword search (SQLite FTS5), not semantic search.
- Claims are advisory. An agent that never calls
claimis not stopped. - Capture heuristics are simple and will miss things. Review each proposal.
- Node's
node:sqlitemodule is still marked experimental in Node 22. The CLI hides the warning.
License
MIT