Your codebase has problems you cannot see — how a knowledge graph reveals what grep misses
Developers spend 70% of their time reading code, not writing it. And most of that time is spent looking for things. Not looking for a specific function — grep handles that in 2 seconds. The problem is looking for what you don’t even know exists: hidden dependencies, dead code that looks alive, coupled modules nobody noticed, entire workflows that nothing references.
We built a knowledge graph of our codebase and on the very first query, discovered 229 orphaned artifacts, 2 workflows completely disconnected from the system, and that changing a single agent impacts 31 other artifacts in a chain. All of this was invisible — for years.
What grep finds vs. what grep will never find
Grep is a presence tool. You search for “parseConfig” and it shows you the 5 files that mention that string. Perfect.
But grep can’t answer any of these questions:
- What happens if I change parseConfig? — Grep finds the 5 direct callers. But those 5 call 23 other modules that depend on it transitively. The real blast radius is 28, not 5.
- Which functions exist but nobody calls? — Grep finds presence. Orphans are defined by the absence of a reference. You’d have to check every function against every other — an explosive combination.
- How many modules depend on this one? — When you read
auth.js, you see its imports (what it consumes). But you don’t see who imports it (who depends on it). Fan-in is invisible at the individual file level. - Is there a circular dependency? — A→B→C→A. Each file shows one edge. The cycle only appears when you assemble the full graph.
A Microsoft Research study (Nagappan et al., ICSE 2006) showed that dependency graph-based metrics predict 73% of post-change defects. Developers who only checked direct callers (the grep approach) missed 60% of the breaks that happened in transitive dependents.
So: grep gives you a photo. The graph gives you the map.
The invisible layer nobody sees
Every codebase has 3 layers of understanding:
- Micro (readable) — Individual functions, classes, files. You open and read them.
- Macro (documented) — System architecture, diagrams, READMEs.
- Meso (invisible) — The actual connections between files. Who calls who, who depends on who, what breaks if I touch this.
The meso layer is distributed: each file holds a piece (its imports), but the full picture requires aggregating all of them. It’s cognitively impossible to keep this in your head with hundreds of files.
Fowler and Whitehead document a case in the book Building Evolutionary Architectures that illustrates this perfectly:
“We projected the dependency graph on the wall. The CTO said: ‘That’s not our architecture.’ We said: ‘That IS your architecture.’ The system they thought had 8 modules was, in practice, 2 mega-clusters with a bottleneck routing 67% of the traffic.”
They had been reading that code for 5 years and didn’t know this. Because it’s impossible to see without the graph.
What we did: two graphs, one codebase
We work with AIOS — an AI agent orchestration system for full stack development. It has agents (@dev, @qa, @architect), tasks (qa-gate, dev-develop-story), workflows (story-development-cycle, epic-orchestration), specialist squads, and minds (cognitive clones of thought leaders like Alex Hormozi, Paul Graham, Seth Godin).
The problem: every time Claude Code needed to understand the relationship between these artifacts, it re-read the files. 12 agents × 5,000 tokens each = 60,000 tokens burned just to answer “what does @dev do?”.
So we built two complementary knowledge graphs:
1. code-review-graph — For code
The code-review-graph (fork of the original project by tirth8205) uses Tree-sitter to parse source code into AST and build a SQLite graph with functions, classes, imports, and calls.
Average result: 8.2x fewer tokens per review operation. In a monorepo with 27,000 files, the graph filters down to ~15 relevant files.
In our fork, we contributed PR #95 which adds 10 CLI subcommands and CommonJS require() parsing.
2. aios-graph — For non-code artifacts
But code is only half the story. Our system has 206 tasks, 14 workflows, 13 agents, 93 squads and 115 minds — all defined in YAML and Markdown, outside the scope of code-review-graph.
So we built aios-graph: a local knowledge graph (Python + SQLite + PyYAML) that parses these artifacts and maps relationships like DEPENDS_ON_TASK, ASSIGNED_TO, MIND_IN_SQUAD, DELEGATES_TO.
No other tool on the market does this. Augment Code, codebase-memory-mcp, GitHub Stack Graphs — they all focus on code entities. Graphing agents, tasks and workflows is a new category.
aios-graph query agent dev # What does @dev do?
aios-graph impact agent:dev # Blast radius if you change @dev
aios-graph who-uses qa-gate # Who depends on qa-gate?
aios-graph mind-search --tier S # Minds ready for production
aios-graph dead # Artifacts nobody references200 tokens per query. Before it was 60,000.
What we found on day one
We ran aios-graph build and the queries revealed things no grep would ever show:
229 orphaned artifacts
48 tasks (23.5% of the total) existed in the repository but no agent declared them in its dependencies. These were orchestration, security, and build tasks that worked in practice — but the system didn’t know they existed.
This matters because if someone deleted one thinking it was “dead code”, it would break workflows that depended on it implicitly.
Follow me on Instagram @murilloimparavel — I show behind-the-scenes of how I use AI day-to-day, no filter.
Blast radius of 31 for @dev
The @dev agent (our main implementer) has 43 dependencies and 24 dependents. Changing its interface — one command, one input format — impacts 31 artifacts at 2 levels of depth: 8 workflows, 6 rules, 10 agents, 7 indirect workflows.
Without the graph, someone would edit @dev thinking it’s “just a file”. In reality, it’s the most coupled node in the entire system.
2 ghost workflows
epic-orchestration and development-cycle existed as complete, well-written YAML files with phases and agents defined. But the graph showed 0 edges — nothing referenced them, nothing connected them to the rest of the system. They were workflows someone created, committed, and forgot to wire up.
We discovered the parser didn’t understand their YAML format (they used phases as a dict instead of sequence as a list). We fixed the parser and suddenly: 12 new edges appeared. The workflows came back to life.
98.3% of minds without extracted frameworks
Of the 115 minds (cognitive clones of thought leaders), only 2 had operational frameworks extracted (Gary Vaynerchuk and Pedro Sobral). The other 113 were raw libraries — the agent had to re-read all the source material every time to derive principles.
It’s like having a cookbook library with 115 books but only 2 with the index ready. The rest, you have to read cover to cover to find what you need.
Invisible duplicates
squads/copy/ and squads/copy-squad/ had byte-for-byte identical agents. Without the graph, they’d look like 2 seemingly different squads (different names, different directories). The CONTAINS_AGENT edge comparison revealed they were the same thing.
The token economics — back-of-napkin math
According to the Codebase-Memory paper, a graph query returns results in ~200 tokens. Reading the same files manually costs 8,000-60,000 tokens.
Let’s do the math:
| Operation | Reading files | Graph query | Savings |
|---|---|---|---|
| “What does @dev do?” | 60,000 tokens (12 agents) | 200 tokens | 300x |
| “Who uses qa-gate?” | 15,000 tokens (search across tasks/agents/rules) | 200 tokens | 75x |
| “Blast radius of @dev” | 100,000+ tokens (manual BFS) | 200 tokens | 500x |
| “Tier S minds” | 50,000 tokens (read 115 minds) | 200 tokens | 250x |
With Claude Opus 4.6 at $9/1M input tokens, a heavy exploration session consuming 500K tokens costs $4.50. With graph queries, that same session costs $0.45. Over a month with 50 sessions, that’s $225 vs $22.50. $200 saved per month just by switching from grep to graph.
And that’s without counting the qualitative benefit: the model performs better with less context. Factory.ai showed that irrelevant context degrades performance — it’s not just more expensive, it makes the model seem worse.
Why CLI and not MCP
Let’s go a bit contrarian here. The entire ecosystem is pushing MCP (Model Context Protocol) as the way to connect AI agents to tools. We built aios-graph initially with an MCP server (FastMCP). Then we deleted it.
The data is clear:
| Dimension | CLI (--json) | MCP Server |
|---|---|---|
| Tokens per call | ~200 (stdout only) | ~6,500+ (schema overhead) |
| Cost per task | 1x | 32x (ScaleKit 2026) |
| Reliability | 100% | 72% (ScaleKit) |
| Processes running | 0 (spawn on demand) | 1 persistent |
| Config required | None | settings.json + env vars |
| Crash recovery | Each call is independent | Server crash = everything fails |
aios-graph makes stateless queries against SQLite. There’s no connection pooling, no streaming, no bidirectional communication — the only scenarios where MCP adds real value. For everything else, a CLI with --json gets the job done.
Nx deleted most of its MCP tools for the same reason. The Perplexity CTO dropped MCP internally. The trend is clear: CLI for development tools, MCP only for external integrations that need auth.
How to build your own
aios-graph was built in ~2 hours with this architecture:
aios_graph/
graph.py — SQLite store (WAL mode, parameterized queries)
parser.py — 7 parsers (agent, task, workflow, squad, mind, rule, framework)
tools.py — Core functions (stateless queries)
cli.py — Entry point with --json global flagDependencies: pyyaml. That’s it.
The pipeline is simple:
- Discover —
rglobacross artifact directories - Parse — Extract YAML blocks from markdown, load pure YAML
- Index — Upsert nodes + edges into SQLite with SHA-256 for change detection
- Query — BFS for impact, in-degree=0 for orphans, LIKE for search
Full build: 420 nodes, 993 edges, <15 seconds. Incremental update: <2 seconds.
The code is open source. The code-review-graph fork is on my GitHub with PR #95 that adds the CLI-first expansion. aios-graph is in mvp-system.
The future: every AI workspace should have a graph
Academic research is converging on this. Between 2024-2026, at least 7 papers were published on graphs + AI agents:
- CodeXGraph (NAACL 2025) — LLM agents write Cypher queries against code graphs
- GraphCodeAgent — +43.8% pass rate with GPT-4o using dual graph
- Codebase-Memory — 83% of quality with 10x fewer tokens
- Augment Code — +70% quality improvement with graph context
The pattern that’s emerging:
| Level | Tool | What it captures |
|---|---|---|
| L0: Nothing | Claude Code vanilla | Grep/Read per session |
| L1: Summary | Aider repo-map | Symbols per file, PageRank |
| L2: Embeddings | Cursor | Semantic similarity |
| L3: Structural graph | code-review-graph, aios-graph | Real relationships between entities |
| L4: Semantic + structural | Augment Context Engine | Graph + embeddings |
We’re at L3 with $0 in infra investment. Zero server, zero embedding model, zero API key. SQLite + Tree-sitter + PyYAML.
If this content made sense to you, share it with someone who needs to hear it. And if you want to talk knowledge graphs, AI agents, or how to orchestrate all of this, hit me up on Instagram.
FAQ
What is a codebase knowledge graph?
It’s a graph database (nodes + edges) that maps entities in your code (functions, classes, modules) and the relationships between them (who calls who, who imports who, who tests who). Unlike a search index, it allows relationship queries: “what breaks if I change X?” or “which functions nobody calls?”.
Do I need Neo4j or some graph database for this?
No. code-review-graph and aios-graph use plain SQLite. Two tables (nodes and edges) with indexes are enough for most cases. Neo4j makes sense if you need complex Cypher queries or scale to millions of nodes. For a typical codebase (<100K files), SQLite handles it.
How much dead code does my codebase probably have?
According to FlagShark, typical repositories have 10-30% dead code. Enterprise codebases with 5+ years reach 20-35%. Post-acquisition, it can hit 50%. One SaaS company discovered ~50% of its code was unused.
MCP or CLI to serve the graph to the AI agent?
CLI with --json. The benchmarks show that MCP costs 32x more tokens and has 72% reliability vs 100% for CLI. MCP makes sense for database connections (connection pooling), browser automation (persistent state), and OAuth delegation. For stateless queries against SQLite, CLI is strictly superior.
Does this work only for code or for other artifacts too?
It works for anything with structured relationships. aios-graph parses YAML and Markdown — agents, tasks, workflows, squads, minds. The same pattern works for Terraform modules, Kubernetes manifests, GitHub Actions workflows, or any declarative configuration that references other configurations.