NervaPack
NervaPack is a privacy-first, offline knowledge graph for your codebase. It solves two fundamental problems with standard Vector RAG:
- Token waste — chunk-based RAG retrieves blobs of text that may only tangentially relate to your query, bloating your context window.
- Privacy risk — sending code to cloud embedding APIs leaks your proprietary logic.
NervaPack runs 100% on your machine. It uses tree-sitter to parse your codebase into a deterministic Abstract Syntax Tree graph, then uses a local Ollama model to draw hard semantic edges between your documentation and your code. Queries traverse this graph with a K-Hop BFS, returning a hyper-targeted, token-efficient context window — no cloud required.
Verified Performance
91.2% Average Token Reduction — independently verified on real-world codebases.
| Test Type | Tokens (Naive) | Tokens (NervaPack) | Reduction |
|---|---|---|---|
| Simple Query | 10,926 | 101 | 99.1% |
| Medium Query | 13,092 | 164 | 98.7% |
| Complex Query | 3,290 | 1,102 | 66.5% |
| Average | 52,037 | 2,459 | 91.2% |
Cost Savings: $181–$724 per developer per year (GPT-4o to Claude Sonnet)
📊 View Full Benchmarks · 🧪 Messy Code Performance
Code quality impact: 90–99% reduction on clean code, 50–75% on legacy/messy code. Even poorly structured codebases benefit significantly.
Why NervaPack vs. standard Vector RAG
| Standard Vector RAG | NervaPack | |
|---|---|---|
| Parsing | Arbitrary text chunks | Deterministic AST nodes (class, function, import) |
| Retrieval | Nearest-neighbour blob | K-Hop BFS on a structural graph |
| Doc ↔ Code links | None | Hard EXPLAINS edges drawn by local LLM |
| Privacy | Cloud embeddings | 100% local (ChromaDB + ONNX + optional Ollama) |
| Incremental sync | Re-index everything | Surgical per-file update via GitPython diff |
| Token savings | No measurement | Built-in dashboard shows exact reduction per query |
| Graph visibility | Black box | Interactive HTML visualization of every node and edge |
| Duplicate-safe | Repeated ingest = duplicate data | upsert — re-ingest is idempotent |
| Agent memory | None | 17-tool MCP server for cross-session memory |
Prerequisites
- Python 3.10+
- Git — your project must be a git repository (
git initif not)
(Optional for semantic code-doc binding) — an LLM provider. Structural graph indexing and basic queries work out-of-the-box with zero configuration and no cloud connection.
| Provider | Setup | Cost | Privacy |
|---|---|---|---|
| Ollama (default) | brew install ollama && ollama pull llama3 | Free | 100% local |
| Claude API | pip install "nervapack[claude]" + ANTHROPIC_API_KEY | ~$0.25/1k calls | Cloud |
| OpenAI API | pip install "nervapack[openai]" + OPENAI_API_KEY | ~$0.15/1k calls | Cloud |
| MCP (Claude Code) | Zero config | Included in subscription | Cloud |
Installation
# Recommended
pip install nervapack
# With optional features
pip install "nervapack[mcp]" # MCP server for Claude Code / Cursor
pip install "nervapack[memory]" # agent memory MCP server
pip install "nervapack[metrics]" # exact token counts (tiktoken)
pip install "nervapack[dashboard]" # web dashboard (streamlit + plotly)
pip install "nervapack[claude]" # Claude API support
pip install "nervapack[openai]" # OpenAI API support
pip install "nervapack[all]" # everything
On first run, ChromaDB downloads an ONNX embedding model (~30 MB) to
~/.cache/chroma/. This is a one-time download.
Quick Start
cd your-project/
# 1. Build the knowledge graph (runs locally, no LLM required for basic use)
nervapack ingest .
# 2. Query for context — get focused results + token savings dashboard
nervapack query "How does authentication work?"
# 3. Add semantic doc-to-code edges (requires an LLM)
nervapack enrich .
# 4. Visualize the full graph
nervapack visualize --enhanced --communities
# 5. After changing files, sync incrementally (fast — only changed files)
nervapack sync .
# 6. If you ingested wrong data or need a fresh start
nervapack clean --all
nervapack ingest .
# 7. Check system health
nervapack doctor
Command Reference
nervapack ingest [PATH] — Build the graph
Scans PATH (default: .) and builds the full knowledge graph.
What happens:
- Walks the directory tree with tree-sitter, skipping
dist/,build/,node_modules/,venv/,site/,.tox/, and dozens of other build directories automatically. - Parses source files into exact AST nodes: classes, functions, imports.
- Chunks all
.mdfiles by header hierarchy. - Embeds every entity into a local ChromaDB vector store (ONNX by default, Ollama optional).
- Optionally binds doc chunks to code nodes via an LLM, adding
EXPLAINSedges. - Saves the graph once to
.nervapack/graph.graphml.
Re-ingesting is safe — upsert is used throughout, so running ingest twice does not duplicate data.
nervapack ingest . # auto-detect LLM
nervapack ingest . --llm ollama # force Ollama
nervapack ingest . --llm claude # use Claude API
nervapack ingest . --llm openai --model gpt-4o-mini
nervapack ingest . --embeddings ollama # use Ollama for embeddings too
Supported languages (bundled): Python, JavaScript, JSX, TypeScript, TSX
Additional languages:
pip install "nervapack[go]" # Go
pip install "nervapack[rust]" # Rust
pip install "nervapack[java]" # Java
pip install "nervapack[c]" # C / C headers
pip install "nervapack[cpp]" # C++
pip install "nervapack[ruby]" # Ruby
pip install "nervapack[csharp]" # C#
pip install "nervapack[all-languages]" # all of the above
Exclude directories — create .nervapackignore in your project root (gitignore syntax):
dist/
build/
site/
generated/
*.egg-info/
nervapack query PROMPT — Query the graph
Retrieves focused context for a natural-language prompt and prints a token savings dashboard.
What happens:
- Intent detection — "what breaks if I change X" routes to impact analysis (reverse BFS); exact symbol names bypass vector search.
- ChromaDB returns the top-3 most semantically similar nodes.
- Those nodes seed a K-Hop BFS through the NetworkX graph (default: 1 hop, both directions).
- Adjacent nodes — including any markdown docs via
EXPLAINSedges and memory notes viaTOUCHESedges — are collected. - A focused Markdown context block is printed, ready to paste into an LLM prompt.
- Token efficiency panel shows savings vs. naive "dump the whole file" RAG.
nervapack query "How does authentication work?"
nervapack query "What calls VectorStore?"
nervapack query "what breaks if I change GraphBuilder" # impact analysis
Example output:
Query: "How does authentication work?"
Query Router: Intent: semantic, Direction: both
Vector Search: Found 3 seed nodes
Retrieved Context:
──────────────────────────────────────────────────────────
# NervaPack Context Retrieval
## File: `src/auth/middleware.py`
### CLASS: AuthMiddleware (L15-L42)
...
──────────────────────────────────────────────────────────
╭────────────── NervaPack Token Efficiency ──────────────╮
│ Strategy Tokens Reduction │
│ Naive RAG (3 files) 12,840 100% (base) │
│ NervaPack 1,180 9.2% │
│ ─────────────────────────────────────────────────────────│
│ Tokens saved: 11,660 Reduction: 90.8% │
│ Cost saved (GPT-4o $2.50/1M): $0.0292 per query │
╰───────────────────────────────────────────────────────────╯
nervapack sync [PATH] — Incremental update
Updates only the files that changed since the last ingest. Uses GitPython to diff the working tree.
nervapack sync .
A full ingest on a large project can take minutes. sync turns that into a 2–5 second surgical update per file. Re-parses changed files, batch-upserts new vectors, and saves the graph once at the end.
nervapack clean [OPTIONS] — Remove ingested data
Wipe graph data and start fresh. Use this when you have duplicate vectors, ingested the wrong directory, or need to reduce disk usage.
nervapack clean --vectors # wipe ChromaDB only (chroma_db/)
nervapack clean --graph # delete graph.graphml only
nervapack clean --history # clear query + graph history logs
nervapack clean --all # everything above (keeps memory.db)
nervapack clean --all --yes # skip confirmation (CI / scripts)
Never deleted by clean: memory.db — your agent memory is always safe.
Typical workflow after a bad ingest:
nervapack clean --all
nervapack ingest .
nervapack enrich [PATH] — Add semantic edges
Runs LLM doc-to-code binding on an already-ingested graph. Use this if you:
- Ran
ingestwithout an LLM and want to addEXPLAINSedges now. - Added new documentation and want to bind it without a full re-ingest.
nervapack enrich . # auto-detect LLM
nervapack enrich . --llm ollama --model llama3
nervapack enrich . --llm claude # shows cost estimate before proceeding
nervapack status [--detailed] — Graph health
nervapack status # node/edge counts + unsynced files
nervapack status --detailed # full analytics: health score, language distribution, coverage
Health score (0–100) factors in documentation coverage, node connectivity, graph density, and edge diversity. A structural-only graph typically scores 30–40; after enrich it rises to 70–90.
nervapack visualize [OPTIONS] — Interactive HTML graph
nervapack visualize # basic visualization
nervapack visualize --enhanced # + real-time search + path finder
nervapack visualize --enhanced --communities # + community detection (Louvain)
nervapack visualize --output ~/my-graph.html # custom output path
nervapack visualize --no-browser # generate without opening
Produces a standalone HTML file with no external dependencies — drag, zoom, search, find shortest paths between nodes.
nervapack explore TARGET [--hops N] — Focused subgraph
Extract and visualize the N-hop neighbourhood of a specific file, class, or function.
nervapack explore GraphBuilder --hops 2
nervapack explore src/auth/middleware.py --hops 1
nervapack explore "function:parse"
nervapack dependencies [FILE] — Import dependency analysis
Analyze file-level import chains, detect circular dependencies, and visualize the dependency graph.
nervapack dependencies # full project analysis
nervapack dependencies src/graph/builder.py # single file
nervapack dependencies --no-cycles # skip cycle detection
nervapack hotspots [OPTIONS] — Change frequency analysis
Show which files change most often in git history — prime targets for documentation and review.
nervapack hotspots # top 20 by commit count
nervapack hotspots --since "6 months ago" # recent history only
nervapack hotspots --ext .py --churn # Python files, sort by lines changed
nervapack history [OPTIONS] — Query history
nervapack history # last 10 queries with token savings
nervapack history --limit 50 # last 50
nervapack history --stats # aggregate: total savings, cost avoided, top topics
nervapack history --clear # delete history
nervapack serve [--port N] — Web dashboard
nervapack serve # http://localhost:8501
nervapack serve --port 8080 # custom port
Requires nervapack[dashboard]. Shows graph overview, language distribution, analytics, query history trends, and an interactive graph explorer.
nervapack doctor — Environment check
nervapack doctor
Verifies Python version, tree-sitter grammars, embedding backend, Ollama connectivity, and MCP config. Run this after installation or when troubleshooting.
Storage Layout
Everything lives in .nervapack/ inside your project root:
.nervapack/
├── graph.graphml # NetworkX DiGraph (AST + EXPLAINS edges)
├── chroma_db/ # ChromaDB vector store (ONNX embeddings)
├── memory.db # Agent memory (SQLite + FTS5, bi-temporal)
├── query_history.jsonl # Per-query token savings log
└── graph_history.jsonl # Ingest/sync event log
Add .nervapack/ to .gitignore to keep it out of version control.
Disk usage guide:
chroma_db/— typically 10–100 MB depending on project size. Runnervapack clean --vectors && nervapack ingest .if it grows unexpectedly.graph.graphml— typically 0.5–5 MB.memory.db— grows with agent usage; rarely exceeds a few MB.
MCP Integration (Claude Code, Cursor, Windsurf)
NervaPack ships two MCP servers in the same package. Drop this .mcp.json in your project root:
{
"mcpServers": {
"nervapack": {
"command": "nervapack-mcp",
"description": "NervaPack knowledge graph (v0.6.1) — query, graph_status, explore, impact"
},
"nervapack-memory": {
"command": "nervapack-memory-mcp",
"description": "NervaPack agent memory (v0.6.1) — store, recall, and reason over facts across sessions"
}
}
}
Knowledge Graph MCP tools (nervapack-mcp)
| Tool | What it does |
|---|---|
query | Vector search → K-Hop BFS → focused Markdown context + token savings |
graph_status | Node/edge counts, language breakdown, unsynced file warnings |
explore | Browse all indexed classes, functions, imports, markdown docs |
impact | Reverse dependency analysis — find what depends on a given entity |
Agent Memory MCP tools (nervapack-memory-mcp) — 17 tools
| Tool | Purpose |
|---|---|
memory_start_session | Open a named session |
memory_store | Persist a fact, decision, outcome, procedure, preference, or action |
memory_recall | FTS5 search → graph expansion → scored, budget-capped recall |
memory_about | Entity dossier: all facts/decisions linked to one entity |
memory_why | Explain a decision: rationale, rejected alternatives, outcomes |
memory_timeline | Chronological trace including superseded versions |
memory_end_session | Close session with an outcome summary |
memory_forget | Tombstone or hard-purge nodes |
memory_verify | Confirm (confidence +0.1) or refute (close + confidence ×0.5) |
memory_stats | Node counts, DB size, top entities, all namespaces |
memory_list_sessions | List all sessions with node counts |
memory_clear_session | Delete a session and all its nodes |
memory_for_code | Memories that TOUCH a source file or specific line |
memory_to_code | Code locations a memory node TOUCHES |
memory_import | Bulk-seed memory from a JSON array |
memory_switch_namespace | Switch the active namespace |
memory_verify_staleness | Flag memories whose source file changed since stored |
CLAUDE.md template
Add to your CLAUDE.md to wire NervaPack into every Claude Code session:
## Always use NervaPack MCP tools
At the start of every session:
1. Call `memory_start_session("<task name>")` to open a named session.
2. Call `memory_recall("project context", budget_tokens=400)` to load prior decisions.
3. Call `query("<topic>")` before answering any question about how the code works.
During the session — call `memory_store` for:
- Any decision made (kind="decision" with rationale and alternatives_rejected)
- Any fact discovered about system behaviour (kind="fact")
- Any procedure or convention established (kind="procedure")
At session end — call `memory_end_session("<one-paragraph summary>")`.
Architecture
nervapack ingest .
│
├─ ASTParser (tree-sitter) 16 extensions, 10 languages
│ └─ ParsedEntity[]: class, function, import
│
├─ GraphBuilder (NetworkX DiGraph)
│ ├─ Nodes: file, class, function, import, markdown
│ ├─ Edges: DEFINES (AST, confidence=1.0)
│ ├─ Edges: REFERENCES (heuristic name overlap, confidence=0.7)
│ └─ Edges: EXPLAINS (LLM or keyword binding, confidence=0.5–0.9)
│
├─ VectorStore (ChromaDB)
│ ├─ Embedding: ONNX (default) or Ollama
│ └─ upsert — re-ingest is idempotent
│
└─ GraphHistory — snapshot recorded after each ingest/sync
nervapack query "..."
│
├─ Intent detection (impact / exact / semantic)
├─ VectorStore.search() → seed node IDs
├─ GraphRetriever.retrieve_context()
│ └─ deque BFS, direction: forward / reverse / both
├─ MemoryStore.get_touches_for_file() → memory context injection
└─ TokenMeter → savings vs. naive RAG
nervapack clean --all
└─ Deletes chroma_db/, graph.graphml, history logs
Never touches memory.db
Key source modules:
| Module | Responsibility |
|---|---|
nervapack.parser.ast_parser | tree-sitter parsing → ParsedEntity; shared singleton parser instance |
nervapack.parser.md_chunker | Markdown → header-delimited chunks; prunes build dirs from os.walk |
nervapack.graph.builder | NetworkX DiGraph; O(1) file-index for sync; compiled regex for REFERENCES |
nervapack.graph.vector_store | ChromaDB upsert (idempotent); pluggable embedding function |
nervapack.graph.retrieval | K-Hop BFS with deque (O(n) not O(n²)) |
nervapack.graph.token_meter | tiktoken singleton; token savings panel |
nervapack.graph.query_history | Tail-read JSONL — O(limit) not O(total) |
nervapack.graph.analytics | Bulk graph.degree() — single call not per-node loop |
nervapack.llm.base | bind_docs_to_ast with keyword pre-filter (top-12 candidates) |
nervapack.llm.providers.ollama | ollama.list() cached 60 s |
nervapack.memory.store | SQLite + FTS5; bi-temporal; batch_neighbors for O(1) hop expansion |
nervapack.memory.recall | Batched hop expansion; audit trail on recall |
nervapack.mcp_server | FastMCP — query, graph_status, explore, impact |
nervapack.memory.mcp_server | FastMCP — 17 memory tools |
nervapack.memory — Agent Memory
Stop re-pasting project context into every new chat.
# Store a decision
memory_store(
"Chose JWT over session cookies for auth_service — stateless horizontal scaling",
kind="decision",
entities=["auth_service"],
confidence=0.9,
rationale="Stateless tokens enable horizontal scaling without shared session store.",
alternatives_rejected=["server-side sessions", "PASETO"],
)
# Recall in any future session
memory_recall("project context", budget_tokens=400)
## Memory recall: "project context" (6 items · 171/400 tokens)
### Decisions
- [d_0019f2] 2026-06-05 · conf 0.90 — Chose JWT for auth_service
### Facts
- [f_0019f3] 2026-06-05 · conf 1.00 — auth_service issues 15-min access tokens
### Procedures
- [p_0019f5] 2026-06-12 · conf 1.00 — Deploy: GitHub Actions → staging → prod manual
Token efficiency:
| Approach | Tokens per session | After 20 sessions |
|---|---|---|
| Manual paste (architecture doc) | ~2,400 | ~48,000 |
memory_recall | ~171 | ~3,420 |
| Savings | 93% fewer tokens |
CLI:
nervapack-memory init # create schema
nervapack-memory stats # counts + top entities
nervapack-memory search "JWT auth" # FTS search
nervapack-memory timeline "auth service" # chronological trace
nervapack-memory audit d_0019f2abc # access audit trail
nervapack-memory rebind old/path.py new/path.py # update file links after rename
nervapack-memory export --out dump.json # JSON dump
Data model: 8 node kinds, 7 edge kinds, bi-temporal (valid_from/valid_until), never hard-deletes by default.
Privacy
NervaPack is 100% offline by default:
- Embeddings use ChromaDB's built-in ONNX model (runs on CPU, no cloud).
- LLM calls (when using Ollama) go to
localhost:11434only. - All graph and vector data lives in
.nervapack/inside your project. - No telemetry, no analytics, no network calls at runtime.
Only if you explicitly pass --llm claude or --llm openai does any code leave your machine.
Contributing
- Fork the repo and create a branch.
- Run tests:
python3 -m pytest tests/memory/ -q - Run docs check:
python3 -m mkdocs build --strict - Open a pull request against
master.
Bug reports and feature requests: issue tracker.
License
MIT — see LICENSE.