cd /news/ai-agents/show-hn-rubi-a-knowledge-graph-that-… · home › topics › ai-agents › article
[ARTICLE · art-152507] src=github.com ↗ pub= topic=ai-agents verified=true sentiment=↑ positive

Show HN: Rubi – A knowledge graph that cuts how much Claude Code re-reads

A study of 284 real Claude Code sessions found that 71% of cost went to re-reading context and 97% came from sessions that grew past 150k tokens, according to the developer of Rubi, a knowledge graph and context manager for AI coding agents. Rubi, built as a standard MCP server that also runs in Codex, Gemini CLI, Cursor, GitHub Copilot and local models, compacts sessions early with saved handoffs and builds a code map plus a temporal knowledge graph searched via BM25, Personalized PageRank and Reciprocal Rank Fusion; an A/B benchmark of 90 sessions reported 37% fewer steps, 37% less context re-read and 17% less time, with billed cost down 6% in short cold-start sessions. Writing memory after every answer raised cost 15% in short sessions, so Rubi's default was changed to write less often.

read30 min views22 publishedOct 18, 2026
Show HN: Rubi – A knowledge graph that cuts how much Claude Code re-reads
Image: Michielbdejong (auto-discovered)

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:

  1. Keep sessions short without losing the thread. It compacts early, saves ahandoff before each compaction and restores it right after.
  2. 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 sendstermos : 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 bundledrubi-pesquisador subagent 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 /clear or /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.md into 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.

  1. Robertson, S., & Zaragoza, H. (2009). The Probabilistic Relevance Framework: BM25 and Beyond. Foundations and Trends in Information Retrieval.
  2. Brin, S., & Page, L. (1998). The Anatomy of a Large-Scale Hypertextual Web Search Engine. WWW7.
  3. Haveliwala, T. H. (2002). Topic-Sensitive PageRank. WWW 2002.
  4. 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.
  5. Cormack, G. V., Clarke, C. L. A., & Büttcher, S. (2009). Reciprocal Rank Fusion Outperforms Condorcet and Individual Rank Learning Methods. SIGIR 2009.
  6. Edge, D., et al. (2024). From Local to Global: A Graph RAG Approach to Query-Focused Summarization. Microsoft Research.
  7. Rasmussen, P., et al. (2025). Zep: A Temporal Knowledge Graph Architecture for Agent Memory.
  8. Packer, C., et al. (2023). MemGPT: Towards LLMs as Operating Systems.
  9. Gao, L., Ma, X., Lin, J., & Callan, J. (2022). Precise Zero-Shot Dense Retrieval without Relevance Labels (HyDE).
  10. Wang, L., Yang, N., & Wei, F. (2023). Query2doc: Query Expansion with Large Language Models. EMNLP 2023.
  11. Aider. Repository map (code graph ranked with PageRank). aider.chat/docs/repomap.html
  12. Anthropic. Prompt caching andClaude Code hooks documentation.
  13. 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>

── more in #ai-agents 4 stories · sorted by recency
── more on @rubi 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/show-hn-rubi-a-knowl…] indexed:0 read:30min 2026-10-18 · —