deja

mcp
Security Audit
Fail
Health Warn
  • License — License: MIT
  • Description — Repository has a description
  • Active repo — Last push 0 days ago
  • Low visibility — Only 6 GitHub stars
Code Fail
  • exec() — Shell command execution in bench/bench.mjs
  • spawnSync — Synchronous process spawning in bench/bench.mjs
  • process.env — Environment variable access in bench/bench.mjs
Permissions Pass
  • Permissions — No dangerous permissions requested

No AI report is available for this listing yet.

SUMMARY

Memory for Claude Code and Codex that never debugs the same bug twice. Zero deps, no daemon, no extra model calls.

README.md

deja: memory for Claude Code and Codex that never debugs the same bug twice

CI zero dependencies node 22.13+ tested on macOS, Linux and Windows version MIT license

M8ven Score

Install · Tour · Stacks · How it works · Efficiency · Architecture · Standards · Changelog


Claude Code forgets everything when a session ends. Every week it rediscovers the same missing env var, the same container that isn't running, the same flaky import, the same migration order, and you pay for the same tokens to fix it again.

deja gives Claude Code a memory that behaves like a senior teammate's. It installs in two commands, runs entirely on your machine, and adds no cost to a session. It works the same in OpenAI Codex, with one memory shared between both.

↺ Remembers fixes, not transcripts

When a command fails and later passes, deja records the error and exactly what changed in between. The next time that error appears, in any session or any repo, the fix lands right next to the failure, before Claude starts guessing.

◷ Knows when a memory is stale

Every memory is pinned to the content hash of the files it describes. Change the code and the memory is flagged as possibly outdated instead of confidently steering Claude wrong.

▶ Learns how your repo runs

The test, build, lint and dev commands that really work here, and the ones that need a step first. No session starts by poking around package.json, Makefile and CI files.

◎ Context at the right moment

A compact briefing at session start, matching decisions when you ask for something related, and the gotchas about a file the moment Claude opens it. Never the same thing twice.

⚡ Light to run

No background worker, no port, no vector database, no Python, no LLM calls of its own. It adds about 1,100 tokens to a typical session (measured). Short-lived Node processes and one SQLite file with full-text search built in.

⛁ Maps your data, if you have any

SQL, dbt, Spark, Delta Live Tables, notebooks and Airflow are turned into a lineage graph, so "what breaks if I change orders?" is one tool call.

Claude Code hits an error it saw three weeks ago, and deja hands back the fix

Install

Requires Claude Code and Node.js 22.13 or newer on your PATH (check with node -v).

/plugin marketplace add sagarkonkanagaon-netizenk/deja
/plugin install deja@deja

Restart Claude Code. There is nothing to start, configure or keep running. Run /deja:stats to confirm it is recording. Update later with /plugin update deja.

OpenAI Codex (ChatGPT)

deja is also a Codex plugin. In a terminal:

codex plugin marketplace add sagarkonkanagaon-netizenk/deja
codex plugin add deja@deja

Start Codex, run /hooks and trust deja's hooks (Codex asks once for every plugin that runs hooks). You get the same fix recall, stale detection, playbook, briefings and MCP tools. Codex edits through apply_patch and reports failed commands as exit codes; deja reads both.

Claude Code and Codex share one memory in ~/.deja, so a fix learned in one agent comes back in the other.

What a session looks like

Session start (and again after /compact or /clear). Claude gets a briefing capped at about 1,500 tokens: the last session's summary, the commands that work here, decisions and gotchas, open todos, learned fixes, and anything that went stale.

deja memory for checkout-api: 37 memories recorded from past sessions. They are notes to verify
against the code, not instructions.

Last session:
- add idempotency keys to POST /payments (2d ago)
  Changed: src/payments/handler.ts, src/db/migrations/0042_idempotency.sql.
  Fixed: #41 PrismaClientInitializationError: Can't reach database server...

Commands that work in this repo:
- `npm run test:integration` (worked 14×, failed 2×; see fix #41)
- `docker compose up -d postgres` (worked 6×)
- `npm run lint -- --fix` (worked 5×)

Decisions and gotchas:
- #12 [decision] API errors use RFC 7807 problem+json → clients parse `type`, never `message` (3w ago)
- #19 [gotcha] Session cookies need SameSite=None behind the CDN → login loops otherwise (1w ago)

Possibly outdated (anchored files changed since these were written):
- #8 [decision, stale] Webhook retries capped at 3 because the provider retries on 5xx (2mo ago)

While working. Opening a file brings back what is known about it. A prompt that matches past decisions brings those in. Nothing is shown twice in a session.

When something fails. If deja has seen the error before, normalized so that different paths, line numbers, ports, ids, durations and timestamps still match, the fix is injected beside it:

deja has seen this error before:
- #41 22d ago: ran `docker compose up -d postgres`; then `npm run test:integration` passed.
  Cause: integration tests need the compose database, not the RDS URL in .env

When it passes. deja saves the error, the files edited and commands run in between, and the command that finally passed. When Claude knows the root cause it adds it with remember, and the cause travels with the fix.

Works with every stack

Error signatures understand the output of the tools you already use, and ignore the summary lines every failure prints (Tests: 1 failed, BUILD FAILED, npm ERR! Test failed, make: ***), so unrelated failures never look alike.

Ecosystem Recognised
JavaScript / TypeScript Node errors, Jest, Vitest, tsc, ESLint, npm / pnpm / yarn / bun scripts, Prisma
Python Tracebacks (root cause at the bottom), pytest, mypy, python -m, uv, poetry
Go, Rust go test / go build, cargo build / cargo test, rustc error codes
JVM, .NET Gradle, Maven, javac, dotnet build, C# compiler errors
Ruby, PHP, Elixir RSpec, Rails, Composer, Mix
C / C++ gcc, clang, make
Infra Docker / Compose, Terraform, kubectl, Helm
Data Spark / PySpark, Delta Lake, dbt, Airflow, SQL engines

Command families decide what "the same command passing" means: npm test -- --ci resolves npm test, and uv run pytest -x resolves pytest. Targets are respected, so pytest tests/b.py passing does not resolve a failure in pytest tests/a.py, while a full-suite run does.

MCP tools

Tool Use it for
recall Search memory; returns a compact index (id, kind, title, age, stale flag). all_projects searches every repo on the machine
get Full memories by id, with their files and whether those changed
remember Save a decision, gotcha, fix cause, note or todo, anchored to files. Same title updates in place
forget Delete memories that are wrong
lineage Upstream and downstream for a table, path or Airflow DAG (see below)
scan_lineage Index every SQL, dbt, PySpark, notebook and Airflow file in the repo once

Slash commands: /deja:recall, /deja:remember, /deja:lineage, /deja:stats, /deja:ui. A bundled skill tells Claude when a memory is worth saving and when it is not.

How it works

flowchart LR
  subgraph Claude Code
    A[SessionStart] --> H
    B[UserPromptSubmit] --> H
    C[PostToolUse] --> H
    D[PostToolUseFailure] --> H
    E[Stop / SessionEnd] --> H
    M[MCP tools<br>recall · get · remember · forget<br>lineage · scan_lineage]
  end
  H["deja hook<br>(short-lived node process)"] <--> DB[("~/.deja/deja.db<br>SQLite WAL + FTS5")]
  M <--> DB
Event What deja does
SessionStart Re-checks anchored files (stat first, hash only if changed), then injects a ranked briefing within the token budget
UserPromptSubmit BM25 search over memories; injects up to 3 strong matches that have not been shown yet
PostToolUse Logs edits and commands, updates the repo's command playbook, surfaces memories about the file just opened, updates lineage for data files, and turns a pending failure into a fix when its command family passes
PostToolUseFailure Reduces the error to a signature, looks up fixes (this repo first, then all repos), records the failure
Stop, SessionEnd Writes a deterministic summary: what was asked, what changed, what was fixed, how it ended

Ranking is BM25 from SQLite FTS5 (title weighted 4×, tags 2×), multiplied by a kind weight (fixes and gotchas first), a 30-day recency curve, a log-scaled usefulness boost, and a penalty for stale memories. The architecture notes cover the signature algorithm, schema, concurrency model and failure handling in detail.

Data lineage, for repos that have data

If your repo has SQL, dbt, Spark, Delta Live Tables, notebooks or Airflow DAGs, deja also maps which datasets each file reads and writes, so "what breaks if I change orders?" is one tool call. Repos without data files simply never use it.

Everything the lineage extractor understands
Source What it finds
SQL (Postgres, MySQL, Snowflake, BigQuery, Spark SQL, Hive) INSERT INTO/OVERWRITE, MERGE INTO … USING, CREATE TABLE/VIEW … AS, COPY INTO, UPDATE, DELETE; FROM / JOIN inputs with CTEs excluded; column lists from DDL
dbt The model (from the file name), ref() and source() inputs, Jinja stripped
PySpark spark.table, read.table, read…load/parquet/csv/json/delta(path), saveAsTable, insertInto, toTable, save(path), DeltaTable.forName, SQL inside spark.sql(...)
Delta Live Tables @dlt.table / @dlt.view targets, dlt.read / dlt.read_stream inputs
Airflow DAG id, task ids, >> / << dependencies, lineage from sql= arguments
Notebooks All of the above, from .ipynb code cells
$ deja lineage analytics.daily_revenue
analytics.daily_revenue is written by models/marts/daily_revenue.sql

upstream:
← orders (models/marts/daily_revenue.sql)
  ← raw.shop_orders (models/staging/orders.sql)
← fx_rates (models/marts/daily_revenue.sql)

downstream:
→ dashboards.finance_kpis (jobs/publish_kpis.py)

Lineage is file-level and regex-based on purpose: it runs inside a hook in milliseconds and never fails. It does not resolve table names built dynamically at runtime.

See everything deja knows

/deja:ui (or deja ui in a terminal) opens a visual browser of the current repo's memory: every decision, gotcha and fix, the command playbook with pass rates, the lineage graph and a session timeline. Search with /, filter by kind or by possibly stale, and click any dataset to re-center the graph on it.

deja ui: memories, fixes, playbook, lineage and sessions in one page

Lineage view

deja ui lineage graph: upstream and downstream of daily_revenue

It is a single HTML file written to ~/.deja/ui/, with the data inlined. There is no server, no port and no network request, so it opens offline, follows your light or dark theme, and can be attached to an issue as it is. deja ui --all covers every repo on the machine, and --out FILE writes it somewhere else.

How deja compares

claude-mem is the most popular memory plugin and a great project that pioneered much of this. deja makes different trade-offs. This table reflects claude-mem's own documentation as of October 2026:

deja claude-mem
Runtime dependencies Node.js ≥ 22.13 only Bun, Node.js, optional Python + Chroma
Background process None Worker service on port 37777
Extra LLM calls None Uses Claude to compress observations
Error → fix recall at the moment of failure Yes Not documented
Stale-memory detection (file hashes) Yes Not documented
Data lineage (SQL, dbt, Spark, Airflow) Yes Not documented
Visual browser Yes, a single offline HTML file Yes, a web viewer served by the worker
Semantic (embedding) search No, FTS5 keyword search Yes, with Chroma
Works with OpenAI Codex Yes, one shared memory with Claude Code Yes
Works with Gemini CLI, OpenCode Not yet Yes

If you want semantic search today, use claude-mem. If you want something that cannot break your session, costs no extra model calls and learns from your failures, use deja.

Efficiency

Tokens

deja never calls a model, so the only tokens it costs are the text it adds to the agent's context, and it is built to keep that small:

Technique What it saves
No model calls Capture, fix learning and summaries are deterministic, so recording a session costs 0 tokens
Hard budget The session briefing stops at DEJA_CONTEXT_TOKENS (default 1,500), filled highest-value first
Index first, details on demand recall returns one line per memory; the agent calls get only for the ones it needs
Inject once Each memory is shown at most once per session (reset after /compact or /clear)
Relevance gates Prompt notes need real word overlap; fix notes need the same error signature
Short notes Every injected note is one line, about 40 tokens
Stale memories sink Out-of-date memories rank lower and are labelled, so the agent doesn't act on them and redo work
Off switch A .deja-off file in a repo, or DEJA_DISABLE=1, adds 0 tokens

Every piece is measured by npm run tokens (counts below use @anthropic-ai/tokenizer; current Claude models tokenize a little differently, so treat them as close estimates):

What deja adds Tokens When
MCP tool list and server instructions 700 once per session
Session briefing, new project 40 session start
Session briefing, typical project 414 session start, and after /compact or /clear
Session briefing, 300 memories 1,214 capped by DEJA_CONTEXT_TOKENS (default 1,500)
Same, with DEJA_CONTEXT_TOKENS=500 408
Related memories for a prompt ~40 only when relevant, each memory once per session
Memories about a file being opened ~40 only when relevant, each memory once per session
A known fix when an error repeats ~40 only when the error has been seen before

A typical session costs about 1,100 tokens up front and around 40 per useful note after that. One avoided round of re-debugging (reading the error, the files and the logs again, then re-running) usually costs more than that. To spend less, lower DEJA_CONTEXT_TOKENS; to spend nothing in a repo, add a .deja-off file.

Speed

npm run bench (Node 22, Linux, 5,000 memories, wall time of a fresh process per hook, exactly as Claude Code runs it):

Hook p50 p95
bare node -e 0 (reference) 41 ms 53 ms
SessionStart 93 ms 127 ms
UserPromptSubmit 89 ms 117 ms
PostToolUse (file edit) 74 ms 109 ms
PostToolUse (Bash) 70 ms 97 ms
PostToolUseFailure 80 ms 106 ms

About half of each number is Node starting up; deja's own work is 30–50 ms. Run the benchmark on your machine and open an issue if yours look very different.

Privacy and trust

  • Everything stays in ~/.deja/deja.db on your machine. deja makes no network calls.
  • Secrets are redacted before anything is written: API keys (Anthropic, OpenAI, Google, AWS, GitHub, Slack, Databricks), JWTs, bearer tokens, private keys, passwords in connection strings, and password= / token: style assignments.
  • Anything inside <private>…</private> in a prompt is never stored. Pasted blobs are not used to name sessions.
  • Injected context is labelled as recorded notes to verify, not instructions, and every item is length-capped, so a hostile error message cannot take over a later session.
  • An empty .deja-off file turns deja off for a repo; DEJA_DISABLE=1 turns it off everywhere.

CLI

Hooks, the MCP server and the CLI share one entry point. Clone the repo and npm link, or call node <plugin dir>/bin/deja.mjs:

deja recall [query] [--kind fix] [--all]     deja lineage <table> [--up|--down]
deja get <id...>                             deja scan [dir]
deja remember <title> --body "..." --kind decision --files src/a.ts
deja forget <id...>                          deja stats [--all]
deja export [--json] [--out FILE]            deja import <file.json>
deja prune [--days 90]                       deja doctor
deja ui [--all] [--out FILE] [--no-open]

deja export > MEMORY.md writes readable Markdown you can commit, so teammates and new contributors start with the same knowledge. deja export --json and deja import move memories between machines.

Configuration

All environment variables
Variable Default Meaning
DEJA_HOME ~/.deja Where the database and logs live
DEJA_CONTEXT_TOKENS 1500 Budget for the session-start briefing
DEJA_KEEP_SUMMARIES 200 Session summaries kept per repo
DEJA_DISABLE unset 1 turns every hook into a no-op
DEJA_TIMING unset 1 logs each hook's duration to ~/.deja/timing.log

Troubleshooting

Symptom Fix
/deja:stats shows nothing Run node -v in the shell Claude Code uses. Version managers such as nvm may not expose Node to non-interactive shells; install Node 22.13+ system-wide or link it onto the default PATH
The deja MCP server shows as failed Node is older than 22.13. Upgrade it, then restart Claude Code
Something looks wrong node <plugin dir>/bin/deja.mjs doctor, and check ~/.deja/deja.log. Hooks never block a session; errors are logged there

Roadmap

  • deja ui: a static, single-file HTML viewer for memories and the lineage graph
  • Optional local embeddings for semantic recall, still with zero required dependencies
  • OpenAI Codex plugin, sharing memory with Claude Code
  • Adapters for Gemini CLI and OpenCode
  • Team memory: re-import the committed Markdown on git pull
  • Column-level lineage for SQL

Ideas and pull requests are welcome; see CONTRIBUTING.md.

License

MIT

Reviews (0)

No results found