A knowledge graph and context manager for AI coding agents.
Claude, Codex, Gemini, Cursor, Copilot and local models.
Shorter sessions, organized projects, fewer tokens.
Check your usage · Live demo · Walkthrough · Results · Install & test · The theory
AI coding assistants are billed for every token they read, and in a long conversation they re-read the whole history on every message. Analyzing 284 real Claude Code sessions, we found that 71% of the cost was spent re-reading context and 97% came from sessions that grew past 150k tokens. Claude was not doing more work. It was carrying more weight.
Rubi attacks the problem from two sides:
- Keep sessions short without losing the thread. It compacts early, saves ahandoff before each compaction and restores it right after.
- Make the project easy to find your way around, so less reading is needed. It builds amap of the code (imports and centrality via PageRank) and atemporal knowledge graph of decisions, bugs, gotchas and concepts. The graph is written by Claude itself, at the moment it already holds that knowledge in context, and searched with a hybrid of BM25, Personalized PageRank and Reciprocal Rank Fusion, within a token budget. Relevant knowledge isrecalled automatically on every request, before Claude starts exploring.
Everything is plain markdown inside the project (.rubi/), opens as an Obsidian graph, and runs locally with zero dependencies. It was built for Claude Code and, because its core is a standard MCP server, it also runs in Codex, Gemini CLI, Cursor, GitHub Copilot, Windsurf, Cline, OpenCode, Continue, Goose and Zed, with hosted or local models (see the table).
Results. Two studies, reported as measured, including what did not work:
| Study | Result |
|---|---|
| Early compaction, replayed on 284 real sessions | ≈37% lower total cost (simulation on real context curves) |
| A/B benchmark, 90 sessions, reading a mature graph | −37% steps, −37% context re-read, −17% time ; billed cost**−6%** in short cold-start sessions |
| Same, projected onto the real cost mix (71% cache reads) | ≈26% lower cost (projection, not yet measured) |
| A/B benchmark, writing memory after every answer | +15% cost in short sessions, so the default was changed to write less often |
A language model does not "remember" a conversation. Every time you send a message, the entire conversation so far is sent again: your requests, Claude's answers, and every file it read and every command it ran. That bundle is the context.
In plain words. Imagine a consultant who, before answering each new question, re-reads the whole meeting transcript from the start. Message 10 is cheap. Message 500 means re-reading 499 pages first.
So the cost of a session is not proportional to the number of messages but to the sum of the context sizes. If the context grows by a roughly constant amount per message, that sum grows with the square of the session length.
Anthropic's prompt caching stores the beginning of the context so that re-reading it costs about 10% of the normal input price. It is a real discount, and in our data it worked: 99% cache hit rate. But 10% of a million tokens, on every message, for hundreds of messages, is still most of the bill. When the cache expires (for example after a ), the whole context is written to it again at a higher price.
Rubi ships a token X-ray that reads Claude Code's local transcripts. On 30 days of real usage:
| Finding (284 sessions, 30 days) | |
|---|---|
| Share of cost that was re-reading context (cache reads) | 71% |
| Share of cost from sessions that grew past 150k tokens | 97% |
| Sessions that reached ~1 million tokens | 68 |
| Context loaded before the first message (median) | 48k |
| Tool whose outputs weighed most in the context | Bash (15%), then Read (4%) |
| Times the cache expired mid-session and was rewritten | 197 |
Two conclusions drove the design:
- Sessions are too long. Claude Code only compacts near the model's limit, and with a 1M-token model that is very late.
- Claude spends a lot of effort finding its way around. A large share of the context is file reads and searches that rediscover how the project works, every new session.
Rubi combines well-known techniques from information retrieval, graph theory and recent research on memory for language models. Each one below gets a plain-words explanation and a pointer to the original work.
A graph is just things (nodes) connected by relationships (edges). In Rubi the nodes are files, modules, decisions, bugs, gotchas and concepts, and the edges are "file A imports file B", "this decision is about login.ts", "this bug is related to Checkout", "this decision replaces that one".
In plain words. A folder of notes is like a pile of index cards: you only find a card if you guess the word written on it. A graph is like a city map: from where you are, you can see what's next door. Ask about "finishing a purchase" and a plain search finds nothing, because no note says "purchase". With Rubi, Claude adds the related word checkout to the search (section 2.9), the graph finds the concept Checkout, sees that it is linked to a gotcha called "Coupon applied twice" and to the file cupom.ts, and brings those along. You get the trap you were about to fall into, even though you never typed its name.
That is the difference: search finds what matches your words; a graph also finds what is connected to them. In code, connections are everything: who imports whom, which decision affects which file, which bug came back where.
When you search, something has to decide which results come first. BM25 (Robertson & Zaragoza, 2009) is the classic answer used by search engines: a word counts more if it is rare in the collection and frequent in the document, with diminishing returns so repetition doesn't win. Rubi uses it for the textual part of the search, with accent folding, camelCase splitting (PaymentError → payment, error) and light Portuguese/English stemming.
PageRank (Brin & Page, 1998), the algorithm behind early Google, says a page is important if important pages link to it. Applied to code: a file is central if many files import it, especially central ones. Rubi computes PageRank over the import graph to tell Claude, in one line, which files are the backbone of the project. This is the same idea as Aider's repo map.
Personalized PageRank (Haveliwala, 2002) is PageRank that "starts" from a few chosen nodes and spreads outward, so you get what is important relative to your question. HippoRAG (Gutiérrez et al., 2024) showed this works remarkably well as long-term memory for language models, inspired by how the hippocampus links memories. Rubi seeds it with the nodes that matched your words and lets relevance flow through the graph, discounting "hub" files that everything touches.
Text, exact file and function names, and graph proximity each produce their own ranked list. Reciprocal Rank Fusion (Cormack et al., 2009) merges them with a simple rule: each list gives every item 1 / (60 + position), and the scores are added. It is robust, needs no tuning, and is what ai-memory also uses.
Microsoft's GraphRAG (Edge et al., 2024) showed the value of summarizing a knowledge graph at several levels. Rubi keeps three:
| Level | File | Size | When it's read |
|---|---|---|---|
| 0 | mapa.md |
~500 tokens | at the start of every session |
| 1 | modulos/<module>.md |
a page per module | on demand, with rubi_mapa(module) |
| 2 | conhecimento/*.md |
one file per node | found by search |
In plain words. Instead of handing someone the whole encyclopedia, you hand them the table of contents and let them open the chapter they need.
Decisions change. Zep / Graphiti (Rasmussen et al., 2025) proposed a temporal knowledge graph for agent memory, where facts are invalidated instead of deleted. Rubi does the same: when a new decision replaces an old one, the old node is marked superseded and points to its successor. Claude never follows a stale rule, yet it can still see why things changed. Each node also records the files and the git commit it came from. If a file changed after the note was written, search flags it with ⚠ so Claude double-checks.
MemGPT (Packer et al., 2023) framed LLM memory like an operating system: a small core always in context, a working set, and archival storage. Rubi maps that onto core (the level-0 map), working (the current handoff) and archival (the graph, queried on demand).
Most memory systems call another model to extract facts and compute embeddings. Rubi doesn't need one, because Claude is already running:
- Extraction happens when knowledge is already paid for. At handoff time, or at the end of an answer that required exploring the code, Claude has everything in context. Writing down 3 facts costs a few hundred output tokens. Asking another model later would mean re-reading everything.
- Semantic search is done by Claude, not by vectors. When Claude queries Rubi it sends
termos: synonyms, related concepts and likely function names it imagines. That is thequery expansion idea behind HyDE (Gao et al., 2022) and Query2doc (Wang et al., 2023). The LLM supplies the meaning; BM25 and the graph supply the speed. - Heavy research runs in a separate window. The bundled
rubi-pesquisadorsubagent explores in its own context and returns only the conclusion, so the main session stays light.
| Work | Who does it | Token cost |
|---|---|---|
| Map the code (files, imports, symbols, PageRank) | Rubi, deterministic | none |
| Link notes to files and git history | Rubi, deterministic | none |
| Extract decisions, bugs, gotchas, concepts | Claude, at handoff or at the end of an exploration | a few hundred output tokens |
| Recall what matters for each new request | Rubi hook (BM25 + graph over your prompt) | none to compute; ≤700 tokens injected, only when something matches |
| Understand the question (synonyms, related terms) | Claude, when calling the search | dozens of tokens |
| Deep exploration of the code | rubi-pesquisador subagent |
isolated from the main context |
your-project/
├── .rubi/ ← commit it: your team shares the memory
│ ├── mapa.md level 0 · injected at the start of every session
│ ├── modulos/src-checkout.md level 1 · one per module
│ ├── conhecimento/ level 2 · one markdown file per graph node
│ │ ├── decisao-usar-jwt-em-vez-de-sessao.md
│ │ ├── armadilha-cupom-aplicado-duas-vezes.md
│ │ └── conceito-checkout.md
│ ├── handoffs/2026-10-17-1530.md
│ ├── sessoes/… automatic pre-compaction records (git-ignored)
│ └── .indice.json derived index, rebuildable (git-ignored)
└── CLAUDE.md can now be short: it points to the map
A node is ordinary markdown with its type and edges in the frontmatter:
---
id: "decisao-usar-jwt-em-vez-de-sessao"
tipo: "decisao"
status: "ativo"
arquivos: ["src/auth/login.ts"]
liga: [{"rel":"sobre","alvo":"conceito-autenticacao"}]
substitui: "decisao-sessao-em-cookie"
commit: "a1b2c3d"
---
Stateless, so it runs on edge runtimes without a session store.
## Ligações
- sobre [[conceito-autenticacao]]
- substitui [[decisao-sessao-em-cookie]]
Personal preferences that apply to every project live in a separate personal vault (~/rubi-vault/_pessoal). Search covers both.
sequenceDiagram
participant You
participant Claude as Claude Code
participant Rubi as Rubi hooks + MCP
participant Mem as .rubi/ (markdown graph)
Note over Rubi,Mem: session start: index the code (incremental), inject the ~500-token map
You->>Claude: "fix the coupon bug in checkout"
Rubi-->>Claude: recall: gotcha "Coupon applied twice" (from the graph, before any exploration)
Claude->>Rubi: rubi_contexto("coupon bug", termos: [discount, cart, Checkout])
Rubi->>Mem: BM25 + entities + Personalized PageRank → RRF
Rubi-->>Claude: gotcha "Coupon applied twice", cupom.ts, Checkout.tsx (≤1.5k tokens)
Claude->>Claude: goes straight to the right file
Note over Claude,Rubi: end of an answer that explored the code
Rubi-->>Claude: Stop hook: "register what you learned"
Claude->>Mem: rubi_registrar(facts, links, files)
Note over Claude,Rubi: context reaches 80% of 150k
Rubi-->>Claude: "save a handoff now"
Claude->>Mem: rubi_salvar_handoff
Claude->>Rubi: PreCompact → session record saved
Claude->>Rubi: SessionStart(compact) → handoff injected back
Claude->>You: continues, same thread, ~60k context
| Hook / tool | What it does |
|---|---|
SessionStart |
Indexes the code (incremental, in the background after the first run) and injects the level-0 map. After a compaction or /clear , injects the handoff. |
UserPromptSubmit |
Recall. Searches the graph with your request and injects the relevant knowledge (≤700 tokens, only when something actually matches). Also runs the meter. |
PostToolUse |
Meter. Reads the real context size and, at 80% of the window, asks Claude to register facts, write missing summaries and save a handoff. |
Stop |
Harvest. If the answer involved heavy exploration (≥10 reads/searches) and nothing was saved, asks Claude to register 1–5 durable facts before finishing. |
PreCompact |
Saves an objective record of what is about to be compacted (requests, files changed, commands) and links it to the files. |
PreToolUse |
Read guard. Blocks reading a huge file in full once and points to Grep oroffset/limit . |
rubi_contexto |
Hybrid search within a token budget. |
rubi_registrar |
Writes graph nodes: merges duplicates, resolves links, supersedes old decisions. |
rubi_mapa ,rubi_escrever_resumo |
Zoom levels and curated summaries. |
[armadilha] Cupom aplicado duas vezes — conhecimento/armadilha-cupom-aplicado-duas-vezes.md
`src/desconto/cupom.ts` · ⚠ src/desconto/cupom.ts mudou depois desta nota
O desconto rodava antes do total ser recalculado.
[conceito] Checkout — conhecimento/conceito-checkout.md
Fluxo de finalizar compra.
[arquivo] src/desconto/cupom.ts — módulo src/desconto, usado por 2 · ligado a "Cupom aplicado duas vezes"
aplicarCupom
(3 itens, ~180 tokens. Abra um item com rubi_ler; zoom no módulo com rubi_mapa.)
Every item says why it is there ("ligado a…" = linked to, "citado" = named in the question), so Claude can trust or discard it quickly.
We ran two studies: a retrospective analysis of real usage, and a controlled A/B benchmark.
Method. The X-ray reads every Claude Code transcript from the last 30 days (284 sessions) and replays each session's actual context curve under a policy that compacts whenever the context passes a threshold and resumes from the fixed context plus an 8k-token handoff. The cost of each compaction (rewriting the new cache prefix) is charged. Costs are in input-token equivalents using Anthropic's price ratios (cache read 0.1×, cache write 1.25–2×, output 5×).
| Compaction threshold | Window switches | Estimated saving on total cost |
|---|---|---|
| 100k | 1,827 | ~29% |
| 150k | 695 | ~37% |
| 250k | 336 | ~31% |
150k was the sweet spot: lower thresholds pay for too many compactions, higher ones carry too much context. This is why Rubi's default is 150k. The interactive demo lets you replay a single session with your own numbers.
Method. Two public repositories were cloned fresh for every run: Flask (Python) and Hono (TypeScript). For each one we wrote three chained questions about the same area of the code, like three days of work on one feature (see bench/tarefas.json). Each question ran in a new session, so nothing carried over except what was stored on disk.
We separated the two things a memory system does, reading and writing, into four arms:
| Arm | What runs | Question it answers |
|---|---|---|
| A | Claude Code, no plugin | baseline |
| B | Rubi complete, harvesting after every answer that explored ≥4 files | what does writing the memory cost? |
| C | Rubi, map + automatic recall, no harvest, empty graph | what does reading cost or save with no knowledge yet? |
| D | Same as C, but starting with a graph of 5–6 nodes written by Claude in arm B | what does a mature project memory save? |
- Model
claude-sonnet-5, read-only tools,3 repetitions per arm , fresh clone per repetition,90 sessions in total (round 1: A vs B; round 2: A vs C vs D, re-running A so both rounds have their own baseline). - Metrics: the cost Claude Code itself reports (
total_cost_usd), turns (API calls), tokens by type, and wall-clock time. - A pilot round was discarded : a quoting bug in the benchmark script meant the plugin never loaded in arm B. The script now refuses to record a Rubi arm if the plugin did not run.
Reproduce with node bench/rodar.mjs --reps 3 --bracos A,C,D. Raw CSVs, summaries and every answer are in bench/resultados/ and on Hugging Face. The seed graph for arm D is in bench/sementes/.
| A no Rubi | C map + recall | D mature graph | |
|---|---|---|---|
| Turns (3 sessions, mean) | 34.0 | 26.7 (−21%) | 21.3 (−37%) |
| Cache reads (tokens) | 919k | 771k (−16%) | 579k (−37%) |
| Output tokens | 8.5k | 6.8k (−20%) | 6.3k (−26%) |
| Cache writes (tokens) | 131k | 141k (+7%) | 141k (+8%) |
| Wall-clock time | 161 s | 146 s (−9%) | 133 s (−17%) |
| Billed cost | $0.793 | $0.785 (−1%) | $0.744 (−6%) |
| Billed cost by slice | A | C | D |
|---|---|---|---|
| Flask | $0.444 | $0.422 (−5%) | $0.383 (−14%) |
| Hono | $0.349 | $0.362 (+4%) | $0.361 (+3%) |
| Session 1 | $0.310 | $0.278 (−10%) | $0.278 (−11%) |
| Session 2 | $0.205 | $0.255 (+24%) | $0.237 (+16%) |
| Session 3 | $0.278 | $0.252 (−9%) | $0.229 (−18%) |
| A no Rubi | B harvest after every exploring answer | |
|---|---|---|
| Billed cost (3 sessions, mean) | $0.800 | $0.922 (+15%) |
| Turns | 33.0 | 32.3 |
| Nodes in the graph at the end | — | 2 to 5 |
Reading a project memory makes Claude much more direct. With a mature graph, Claude needed 37% fewer steps, re-read 37% less context and finished 17% faster. It went straight to the files the memory pointed at instead of searching around. Even with an empty graph, the map and the recall alone cut 21% of the steps.
In this benchmark that only became a 6% lower bill, and here is why. Every question ran in a brand-new, cold session: a single question, then the session ends. In that setting the most expensive part is writing the cache from scratch (cache writes cost 2× the input price), and that part is the same with or without Rubi. The savings land on cache reads and output, which are a small share of a 3-to-5-turn session.
Real work looks the other way around. In the 284 real sessions of Study 1, cache reads were 71% of the cost, writes ~20% and output ~10%. Applying arm D's measured changes to that real mix (reads −37%, writes +8%, output −26%) gives:
0.71 × 0.63 + 0.20 × 1.08 + 0.10 × 0.74 = 0.737 → ≈ 26% lower cost
That is a projection, not a measurement. Measuring it properly needs long, multi-task sessions on a real project; that is the next benchmark.
Answer quality. A spot check found the answers equivalent, with one telling difference. On the Flask teardown question, the baseline answer cited RequestContext.pop, a class that no longer exists in current Flask (it was merged into AppContext), most likely recalled from training data. The answer with a mature graph got it right, because the graph held the fact "AppContext and RequestContext were unified", written by Claude after reading the actual code. A memory of this codebase beats the model's memory of old versions of it. That is one observation, not a measured rate.
Writing on every answer does not pay in short sessions. Arm B's harvest (an extra tool load plus an extra call that re-reads the context) cost 15% more, and three short sessions are not enough to earn it back. Following this result, Rubi's default changed: the harvest now only triggers after heavy exploration (≥10 reads/searches with nothing saved), and the main extraction happens at the meter (80% of the window), when the context is already large and paid for.
Where the money is today. Of everything measured, the largest lever is still keeping sessions short (Study 1: about 37% of total cost, simulated on real usage). The knowledge graph's measured contribution is fewer steps and less re-reading, which compounds in long sessions and on codebases the model has never seen. Flask and Hono are famous projects the model partly knows from training, the least favorable case for a memory.
Monday, install (2 minutes).
/plugin marketplace add YURIBEDRAN/rubi
/plugin install rubi@rubi
Then node scripts/instalar.mjs from a clone of this repo turns on early compaction (150k) and creates your personal vault. Details in Install.
Monday, first session. You open Claude Code in your project. Rubi indexes the code in under a second for most projects and injects the map:
[rubi] Mapa do projeto (nível 0)
## Módulos
- src/checkout (8) — centrais: Checkout.tsx, useCart.ts, api.ts
- src/desconto (4) — centrais: cupom.ts, regras.ts
## Arquivos centrais
`src/lib/api.ts` · `src/types.ts` · `src/checkout/Checkout.tsx` …
Claude already knows where things live before you ask anything.
Monday, you ask: "why is the coupon applied twice?" Claude calls rubi_contexto with related terms (discount, cart, aplicarCupom), goes straight to cupom.ts, finds the bug and fixes it. When it finishes, the harvest hook asks it to save what it learned:
rubi_registrar → criado: conhecimento/bug-cupom-aplicado-duas-vezes.md
criado: conhecimento/conceito-checkout.md
Tuesday, new session, related task: "add a second coupon type." Before Claude reads anything, Rubi's recall hook searches the graph with your request and injects the bug note, the concept Checkout and the files involved. Claude doesn't rediscover the checkout flow. It reads two files instead of ten.
Wednesday, a long refactor. The context grows. At 120k the meter tells Claude to register the decisions it made and save a handoff. At 150k Claude Code compacts on its own. Rubi saves a record of the session and, right after compaction, injects the handoff back. You keep talking; Claude keeps going from ~60k instead of dragging 500k along.
Thursday, a decision changes. "Let's use JWT instead of session cookies." Claude registers the new decision with substitui: "Sessão em cookie". The old one is marked superseded, never deleted, and no future session will follow it.
Friday, look at it. Open .rubi/ in Obsidian and switch to graph view: modules, decisions, bugs and concepts, connected. Run /rubi:raio-x to see where the week's tokens went, and /rubi:organizar if your CLAUDE.md has grown fat. It moves the details into the graph and leaves only the rules that matter in every message.
- Small benchmark. Two repositories, three chained tasks, 3 repetitions per arm (90 sessions). LLM runs vary between executions (standard deviation up to $0.05 per 3-session run on Flask); treat the numbers as indicative. The benchmark is in the repo so it can be rerun and extended.
- Cold, short sessions. Each benchmark question is a one-shot session, which favors cache writes over cache reads and hides most of the reading savings. The 26% figure for real usage is a projection.
- The seed graph for arm D came from arm B's own runs on the same questions, so it is relevant by construction. A real project memory is built from many different tasks, so it is broader and noisier.
- Famous repositories. Flask and Hono are well known, so the model already knows parts of them from training, which favors the baseline. Private, less-known codebases should benefit more from a map and a graph. We have not measured that yet.
- The graph is only as good as what is registered. Claude decides what to store. The harvest hook and the meter make it happen at the right moments, but extraction quality depends on the model.
- Overhead on tiny tasks. The map (~500 tokens) and the tool definitions are a fixed cost. On a one-shot question that needs no exploration, Rubi can cost slightly more than nothing.
- Code map is regex-based , not a full parser (no tree-sitter, to keep zero dependencies). Imports are resolved for JS/TS (relative and
@/aliases), Python, Go, Rust, Java/Kotlin, PHP, Ruby, C/C++, CSS and Dart. Dynamic imports and exotic aliases may be missed. - No vector embeddings. Meaning comes from Claude's own query expansion. For very large knowledge bases (tens of thousands of nodes), an embedding index could be added later; the JSON index can also be swapped for SQLite FTS5.
- Hooks cannot run
/clearor/compact, nor edit Claude Code's compaction summary. Rubi works around it: Claude Code compacts at the threshold you set, and Rubi handles what happens before and after. - Platforms. The benchmark was run on Claude Code only. Claude Code and Codex were tested end to end; the other agents are configured from their official docs and not yet tested end to end. Agents without context-injecting hooks rely on the model following instructions. The Claude website and mobile app are not supported yet.
You need Node 18+ (node --version to check; get it at nodejs.org).
1. Install the plugin. Open Claude Code in any project and type:
/plugin marketplace add YURIBEDRAN/rubi
/plugin install rubi@rubi
Restart Claude Code (exit and run claude again) so the hooks load.
2. Turn on early compaction (recommended). In a terminal:
git clone https://github.com/YURIBEDRAN/rubi
cd rubi
node scripts/instalar.mjs
It asks two things: where to keep your personal notes (default ~/rubi-vault) and when to compact (default 150000 tokens). It writes CLAUDE_CODE_AUTO_COMPACT_WINDOW into ~/.claude/settings.json, keeping a backup in settings.json.bak-rubi. Undo any time with node scripts/instalar.mjs --desfazer.
3. Check that it works. Open Claude Code inside a project that uses git.
| Try this | You should see |
|---|---|
| Start a session | A .rubi/ folder appears in the project, withmapa.md listing your modules and central files |
| Ask: "what do you know about this project's structure?" | Claude answers using the map (modules, central files) without exploring first |
| Ask: "use Rubi to register a decision: we use Postgres because the client already runs it" | criado: conhecimento/decisao-...md , and the file appears in.rubi/conhecimento/ |
| Exit, start a new session and ask*"why did we choose Postgres?"* | Claude answers from memory: the recall hook injected the decision |
Type /rubi:raio-x |
A breakdown of where your tokens went in the last 30 days |
Note
.rubi/ is meant to be committed so your team shares the memory. Its own .gitignore already leaves out the derived index and the automatic session records. If you'd rather keep it private, add .rubi/ to your project's .gitignore.
To try Rubi without installing it: claude --plugin-dir path/to/rubi.
1. Download rubi.mcpb from Releases.
2. Double-click the file, or drag it into Claude Desktop under Settings → Extensions, and click Install.
3. Optionally choose a vault folder. Pick an empty folder used only by Rubi (for example Documents/Rubi) or an Obsidian vault. Leave it blank to use ~/rubi-vault. Don't point it at a busy folder like Downloads.
4. Make sure the extension is turned on in Settings → Extensions.
5. Check that it works.
| Try this | You should see |
|---|---|
| "Use Rubi to register a preference: I like practical examples" | Claude confirms it was registered, and a file appears in <vault>/_pessoal/conhecimento/ |
| In a new conversation:"look up my preferences in Rubi" | Claude finds "I like practical examples" |
If Claude says it can't find a tool called Rubi, the extension is turned off: switch it on in Settings → Extensions and start a new conversation.
In the Desktop app, Rubi provides the memory: the knowledge graph, hybrid search and handoffs. The code map and the automatic hooks (recall, meter, harvest, early compaction) need Claude Code. Claude Desktop also has its own built-in memory, so mention Rubi when you want something stored there.
Rubi's core is a standard MCP server, so it works in any coding agent that speaks MCP, with the vendor's models or with local models. One command connects it:
git clone https://github.com/YURIBEDRAN/rubi
cd your-project
node /path/to/rubi/scripts/conectar.mjs codex # or: gemini, cursor, vscode, windsurf, cline, opencode, continue, goose, zed
For each agent it registers the MCP server in that agent's config (keeping a .bak-rubi backup), writes the instructions into the file the agent reads on its own (AGENTS.md, GEMINI.md, .cursor/rules/rubi.mdc...) and, where the agent supports hooks that feed context to the model, turns on the map at session start and the automatic recall on every request. Formats that are risky to edit automatically (YAML, JSON with comments) are printed for you to paste. Use --so-mostrar to only print, never write. --listar shows all agents.
| Agent | Memory tools (MCP) | Map + recall automatically | Instructions file | Local models | Tested end to end |
|---|---|---|---|---|---|
| Claude Code | yes (plugin) | yes, plus meter, harvest, early compaction, read guard | CLAUDE.md |
yes (Ollama, LM Studio) | yes |
| Claude Desktop | yes ( rubi.mcpb ) |
no hooks in the app | – | – | yes |
| OpenAI Codex | yes | yes (hooks) | AGENTS.md |
yes ( codex --oss , Ollama, LM Studio) |
yes (MCP and hooks) |
| Gemini CLI | yes | yes (hooks) | GEMINI.md |
– | configured from docs |
| Cursor | yes | via instructions | .cursor/rules/rubi.mdc |
– | configured from docs |
| GitHub Copilot (VS Code) | yes, per project | via instructions | AGENTS.md |
yes (bring your own key) | configured from docs |
| Windsurf | yes | via instructions | AGENTS.md |
– | configured from docs |
| Cline | yes | via instructions | AGENTS.md |
yes | configured from docs |
| OpenCode | yes | via instructions | AGENTS.md |
yes | configured from docs |
| Continue | yes, per project (agent mode) | via instructions | .continue/rules/rubi.md |
yes | snippet to paste |
| Goose | yes | via instructions | AGENTS.md |
yes | snippet to paste |
| Zed | yes | via instructions | AGENTS.md |
yes | snippet to paste |
"Via instructions" means the agent is told, through its instructions file and the MCP server's own instructions, to open the map at the start of a task, search the memory before exploring and register what it learns. It works, but it relies on the model following instructions, while hooks do it automatically.
Note
Codex asks you to approve new hooks once: open Codex and type /hooks to review and trust Rubi's two hooks.
Local models need to support tool calling. In our tests a 4B model (qwen3:4b on Ollama) called Rubi's tools and stored knowledge correctly, but often ignored the injected context. Use 8B+ models for real work.
How the server finds your project in each agent: RUBI_PROJECT_DIR if set, then the workspace folders the agent reports through MCP roots, then the folder it was launched in.
Support for the website and the mobile app is in development. The pieces already in this repo (a claude.ai skill in app/claude-ai/ and an HTTP mode of the MCP server for a self-hosted connector) are experimental and not yet validated on claude.ai, so we don't recommend them yet.
MCP tools #
| Tool | Purpose |
|---|---|
rubi_contexto |
Hybrid search (text + names + graph) within a token budget. Send termos with related words. |
rubi_registrar |
Store facts as graph nodes: decisao ,bug ,armadilha ,conceito ,como-fazer ,preferencia ,tarefa ,nota ; witharquivos ,liga andsubstitui . |
rubi_mapa |
Level 0 (no args) or level 1 ( modulo ). |
rubi_escrever_resumo |
Curated summary for mapa ormodulo:<name> , preserved across re-indexing. |
rubi_ler |
Open a memory item ( pessoal: prefix for the personal vault). |
rubi_salvar_handoff /rubi_ultimo_handoff |
Where we left off. |
rubi_raio_x /rubi_auditar |
Token diagnostics (Claude Code only). |
rubi_projetos |
List stored projects (app mode only). |
Skills, agent and commands #
/rubi:memoria <topic>: search the project memory and learn what is worth storing./rubi:handoff: register facts and save a handoff now./rubi:organizar: slim down a largeCLAUDE.mdinto the graph and the map./rubi:raio-x: where your tokens go, and what to change first.- Agent
rubi-pesquisador: researches the codebase in its own context window and returns only the conclusion. - CLI:
node scripts/indexar.mjs,node scripts/raio-x.mjs --dias 30,node scripts/auditar.mjs <dir>.
Configuration (~/.rubi/config.json) #
~/.rubi/config.json)
| Key | Default | Meaning |
|---|---|---|
vault |
~/rubi-vault |
Personal vault (or RUBI_VAULT ) |
janela |
150000 |
Compaction threshold. CLAUDE_CODE_AUTO_COMPACT_WINDOW wins when set |
avisoPct |
0.8 |
When the meter asks for a handoff |
colheita /colheitaMin |
true /10 |
Harvest after N reads/searches with nothing registered |
guarda.maxLinhas /maxBytes |
800 /80000 |
When a full-file read is blocked |
Development #
npm test # 12 tests: code map, graph, search, map, hooks, MCP stdio + HTTP
node bench/rodar.mjs # A/B benchmark
npm run empacotar # dist/rubi.mcpb + dist/rubi.zip
python docs/assets/build_svgs.py
| File | Role |
|---|---|
scripts/lib/codigo.mjs |
Code map: imports, symbols, modules, PageRank |
scripts/lib/grafo.mjs |
Temporal knowledge graph: register, merge, supersede |
scripts/lib/busca.mjs |
BM25 + entities + Personalized PageRank, fused with RRF |
scripts/lib/mapa.mjs |
Level 0/1 maps and the derived index |
scripts/hook.mjs |
All hooks: start, recall, meter, harvest, pre-compact, guard |
scripts/mcp-server.mjs |
MCP server (stdio and HTTP), zero dependencies |
Why some names are in Portuguese. Rubi was born in Brazil, and tool names, folders and the messages it sends to Claude are in Portuguese. Claude understands them either way. The name comes from Rubi nger, and from the idea of keeping only what's precious.
- Robertson, S., & Zaragoza, H. (2009). The Probabilistic Relevance Framework: BM25 and Beyond. Foundations and Trends in Information Retrieval.
- Brin, S., & Page, L. (1998). The Anatomy of a Large-Scale Hypertextual Web Search Engine. WWW7.
- Haveliwala, T. H. (2002). Topic-Sensitive PageRank. WWW 2002.
- Gutiérrez, B. J., Shu, Y., Gu, Y., Yasunaga, M., & Su, Y. (2024). HippoRAG: Neurobiologically Inspired Long-Term Memory for Large Language Models. NeurIPS 2024.
- Cormack, G. V., Clarke, C. L. A., & Büttcher, S. (2009). Reciprocal Rank Fusion Outperforms Condorcet and Individual Rank Learning Methods. SIGIR 2009.
- Edge, D., et al. (2024). From Local to Global: A Graph RAG Approach to Query-Focused Summarization. Microsoft Research.
- Rasmussen, P., et al. (2025). Zep: A Temporal Knowledge Graph Architecture for Agent Memory.
- Packer, C., et al. (2023). MemGPT: Towards LLMs as Operating Systems.
- Gao, L., Ma, X., Lin, J., & Callan, J. (2022). Precise Zero-Shot Dense Retrieval without Relevance Labels (HyDE).
- Wang, L., Yang, N., & Wei, F. (2023). Query2doc: Query Expansion with Large Language Models. EMNLP 2023.
- Aider. Repository map (code graph ranked with PageRank). aider.chat/docs/repomap.html
- Anthropic. Prompt caching andClaude Code hooks documentation.
- Akita, F. ai-memory : long-term memory for AI coding agents. github.com/akitaonrails/ai-memory
<sub>MIT · built with Claude Code · yuribedran.github.io/rubi</sub>