DEV Community

Cover image for My coding agent kept searching the same repo again and again, so I gave it a memory
Manoj Shetty
Manoj Shetty

Posted on AI-assisted

My coding agent kept searching the same repo again and again, so I gave it a memory

I use Claude Code every day. And one thing kept bugging me.

I gave it a small bug in a CLI library. Before it touched anything, it made 32 tool calls. grep, sed, cat, grep again. That one run used 1,052,132 input tokens. For a tiny fix.

So I went and counted my own sessions. 83 sessions, 32,713 tool calls. 31% of them were the agent just searching. The median was 24 searches before the first edit. And 44% of the time it opened a file, it had already read that file in the same session.

The next day, similar question, same repo. It did the same search again.

That's what I built Engram for, in the Graph Hacks hackathon (WeMakeDevs × FalkorDB).

What it does

Engram turns a repo into a graph in FalkorDB. Three kinds of things go in:

  • the code: files, functions, who calls whom, imports
  • the git history: commits, authors, and which files usually change together
  • the agents themselves: what they read, what they edited, what worked, and which file they're editing right now
(:File)-[:DEFINES]->(:Symbol)-[:CALLS]->(:Symbol)
(:File)-[:IMPORTS]->(:File)
(:Person)-[:AUTHORED]->(:Commit)-[:TOUCHED]->(:File)
(:File)-[:CO_CHANGES {count, weight}]->(:File)
(:Agent)-[:RAN]->(:Session)-[:READ|EDITED]->(:File)
(:Session)-[:LEARNED]->(:Lesson)-[:ABOUT]->(:File|:Symbol)
(:Session)-[:CLAIMS {since}]->(:File)
Enter fullscreen mode Exit fullscreen mode

Then, before the agent makes its first tool call, it gets a short map. Around 200 tokens. Just where to look.

This is the real map it gave Claude Code for one click bug ("click.Choice(["Apple", "Banana"], case_sensitive=False) rejects apple"):

[engram] Likely locations for this task (code graph, confidence high):
- src/click/types.py:406-424 Choice.normalize_choice
- src/click/exceptions.py:51-52 ClickException.format_message
- src/click/types.py:381-386 Choice.to_info_dict
- src/click/types.py:495-515 Choice.shell_complete
Tests: tests/test_types/test_Choice.py
Enter fullscreen mode Exit fullscreen mode

Without Engram, the agent did grep, grep, sed, then the edit. 4 tool calls, 112,298 tokens.
With Engram: read the file, edit, done. 2 tool calls, 75,733 tokens. Tests passed in both.

Things that went wrong on the way

Honestly this part taught me more than the parts that worked.

My first benchmark was fake. The agent fixed every bug super fast. Too fast. Then I read one of its answers and it said it "reverted the uncommitted edit". It had just run git diff and seen the bug I injected. So every run now gets a fresh copy with a single commit, the agent only sees a bug report like a user would write it, and a fix only counts if the whole test suite passes.

More context made it worse. My first version gave the agent 1,600 tokens of code and history up front. Tokens went up, not down. Whatever you inject gets re-read on every turn after that, so in a 6-turn session that's around 10k tokens. I cut it down to a map of 6 lines. Claude Code has to Read a file before editing it anyway, so pasting code in never saved a single call.

Bugs hide where names don't match. At first I matched words from the bug report to function names. That found the buggy function in the top 3 only 5 times out of 12. Bugs sit in big functions and private helpers. In networkx, the report says dijkstra_path_length but the bug is in _dijkstra_multisource, three calls down. That's not something grep finds. It's a graph walk:

MATCH p = (s:Symbol {id: $api})-[:CALLS*1..4]->(n:Symbol)
WHERE all(x IN nodes(p)[1..size(nodes(p)) - 1] WHERE size(x.callNames) <= 6)
RETURN n.qualname, min(length(p)) AS hops
Enter fullscreen mode Exit fullscreen mode

After that, on networkx the buggy function was in the top 10 for all 6 bugs. No LLM involved.

Memory made it worse too, at first. I stored "the test command that worked" after each task. But I stored the exact command ("pytest tests/test_types.py"), not the general one. So the next task ran the wrong tests, they passed, and the agent got confused. Now it stores the general command and the graph picks the right tests for each edit.

The graph doubled itself. On a bigger repo I saw the same function listed twice in a map. Two indexers had run at the same time and both inserted everything. I reproduced it: 19,508 symbols instead of 9,754. The fix ended up being one Cypher write, because FalkorDB runs writes on a graph one at a time, so only one process can win:

MERGE (l:IndexLock {k: 1})
WITH l, (l.owner IS NULL OR l.at < $stale) AS free
SET l.owner = CASE WHEN free THEN $me ELSE l.owner END,
    l.at    = CASE WHEN free THEN $now ELSE l.at END
RETURN l.owner = $me AS got
Enter fullscreen mode Exit fullscreen mode

The numbers

I ran Claude Code (claude-haiku-5-5) on 12 real bugs I injected into pallets/click, 3 times each, in 4 setups. 144 runs.

Setup Fixed Input tokens Tool calls Time
Claude Code 36/36 137.6k 5.1 16.4 s
+ codebase-memory-mcp (47k★) 36/36 126.9k 4.7 13.4 s
+ Engram 35/36 103.5k (−25%) 3.6 (−29%) 12.7 s (−23%)

On networkx (128k lines, 6 bugs): −24% tokens, −40% tool calls, −36% time.

What actually goes away is the searching. Search calls per bug went from 3.4 to 1.7.

And yes, it missed one fix out of 36. That was my fault. The map showed a 5-line wrapper function and only mentioned the real function as "the next call", so the agent fixed the wrapper. I changed it so the real function gets its own line. I didn't re-run the whole benchmark after that, so I'm keeping 35/36.

Then I tried it on a messy real project

Injected bugs are one thing. I wanted to see it on code I never touched, with bugs real people reported.

yt-dlp: 1,128 Python files, 248k lines, years of community patches. I indexed it as of 30 April (took 4 seconds) and took every fix after that which closes a GitHub issue. Engram only gets the issue the user wrote, and I check it against what the maintainers actually changed.

  • 30 real issues: the right file was in the top 3 for 22 of them. No LLM calls at all.
  • I ran Claude Code live on 4 of those issues, with and without Engram. Both found the right function all 4 times. With Engram it used 31% fewer tokens and 33% fewer tool calls.

One small thing I learned here: in a GitHub issue the title matters most. A long description full of words like "download" and "video" was drowning out the one useful word in the title. Now title words count double.

Two agents, one file

If two agents try to edit the same file, the second one gets stopped. This is the actual message Engram's hook sent back when a second agent session tried to edit a yt-dlp file the first one had claimed:

Engram: yt_dlp/extractor/bandcamp.py is being edited by claude-code-agentC right now.
Work on something else or wait; do not edit this file.
Enter fullscreen mode Exit fullscreen mode

Same trick as the index lock, one Cypher write.

Why FalkorDB

I needed the graph to answer before every single prompt, without the agent waiting. Multi-hop walks and PageRank come back in milliseconds (my queries take 1 to 40 ms). One query after each edit joins git history, what this session edited, and the call graph, in about 11 ms. Locks between agents are just writes. One graph per repo, many repos in one instance. And FalkorDBLite, their embedded build, means people don't need Docker at all. It uses 43 to 60 MB of RAM with two repos loaded.

Try it

curl -fsSL https://raw.githubusercontent.com/ManojRakshu/engram/main/install.sh | sh
Enter fullscreen mode Exit fullscreen mode

No Docker, no API keys, and Engram itself never calls an LLM. It connects to Claude Code, Cursor, Codex, Gemini CLI, VS Code and any MCP client it finds.

Code, the benchmark harness and all the raw results are here: github.com/ManojRakshu/engram. There's also a 30-second version of the video.

I'm curious: what does your agent waste the most calls on? Searching, re-reading files, running the wrong tests? Tell me, that's what I want to measure next.

I used Claude Code as a coding assistant while building this.

Top comments (0)