Docs drift from code
READMEs and docstrings rot the moment behaviour changes. The "default timeout is 30s" line survives long after someone changed it to 10. You can't tell which lines still hold.
Local-first · Open source · MIT
RepoLens reads a codebase the way a careful contributor does: it parses the real
source, retrieves the relevant functions, and answers only from
what's on disk — every sentence carrying a file:line citation you can
open and check. No training-data guesses. No confident hallucinations.
$ pip install repolens-rag
०१ The problem
READMEs and docstrings rot the moment behaviour changes. The "default timeout is 30s" line survives long after someone changed it to 10. You can't tell which lines still hold.
A general LLM "remembers" an older version of the project from its training data and answers in a tone of total certainty — about code that no longer exists at this commit.
There's no tool that answers "how does X actually work, in this repo, at this commit" and hands you the evidence to check it yourself.
०२ Three pillars
Every answer sentence must cite a file:line span. A citation validator
re-opens the file and verifies the range exists and is relevant; a bad citation
forces a regeneration (up to two retries) rather than slipping through. If retrieval
finds nothing, RepoLens says "not found in this codebase" instead of
inventing one.
Source is parsed with tree-sitter into semantic units — functions, classes, structs — each carrying its signature and docstring. A symbol graph tracks caller/callee links so neighbours come along for context. Search is hybrid: dense embeddings + BM25, fused with RRF, then re-ranked by a cross-encoder.
A separate mode indexes documentation and code apart, extracts factual claims from the docs, and runs NLI entailment against the matching code: supported, contradicted, or not found. Out comes a Markdown report of stale docs — doc location beside code location — that fails CI on new findings.
०३ Under the hood
Semantic chunking into functions/classes/structs with signatures & docstrings, across Python, Go, JS, TS, Rust, C, C++, and Java.
Dense jina-embeddings-v2-base-code vectors and BM25 keyword matches, fused with Reciprocal Rank Fusion.
A ms-marco-MiniLM reranker re-scores the fused candidates so the most relevant code rises to the top.
A NetworkX caller/callee graph pulls in the functions around a match so answers see the call context, not an island.
Every answer gets an entailment score (0–1) from nli-deberta and a high/medium/low verdict shown in the UI.
Cited ranges are re-opened on disk and verified before the answer is allowed out — invalid citations trigger a regenerate.
Claim extraction + NLI produce a CI-friendly Markdown report; --ci exits non-zero on new stale-doc findings.
LanceDB vectors, BM25 index, symbol graph and SQLite metadata all live under ~/.repolens. Nothing leaves your machine but the LLM call.
Groq (free, default), OpenAI, Anthropic — or Ollama for fully offline inference with no rate limits.
A FastAPI server streams tokens, citations and grounding over SSE to a React "Observatory" UI; a typer CLI does the same headless.
Gitignore-style excludes (vendored code, virtualenvs, generated files) are applied before a single file is parsed.
One pip install repolens-rag ships the CLI, the API, and the prebuilt web UI as a single process on one port.
०४ How it works
Walk the repo (honouring .repolensignore) → parse with tree-sitter → chunk semantically → build the symbol graph → embed + BM25 → persist to ~/.repolens.
Hybrid dense + BM25 search → RRF fusion → cross-encoder rerank → graph expansion to pull in callers and callees of the top matches.
A grounded prompt asks the LLM to answer only from the retrieved chunks with citations → the validator re-checks every file:line → the NLI scorer rates the grounding.
Separately: extract claims from docs → retrieve matching code → NLI verdict (supported / contradicted / not found) → a Markdown report that can gate CI.
०५ Quickstart
$ pip install repolens-rag
$ export GROQ_API_KEY=gsk_…
Grab a free key at console.groq.com — or skip the cloud entirely with the Ollama backend.
$ repolens serve
Opens the Observatory UI at localhost:8000 — add a repo, ask away.
०६ Tech stack
०७ Who it's for
Onboard onto a large OSS project without spelunking for days — ask, get cited code, follow the trail.
Catch stale docs automatically in CI, and answer "where is this handled?" without re-reading the module.
Trace behaviour to exact lines at a specific commit — answers you can verify, not vibes.
Local-first by design: indexes and embeddings stay on disk; only the LLM call leaves (and Ollama keeps even that local).
०८ Questions
The embedder, reranker and grounding models all run locally. With the Ollama backend, generation is local too — RepoLens then makes zero network calls.
Three guards: answers must cite file:line spans; a validator re-opens each cited range; and if retrieval returns nothing relevant, it returns a structured "not found" instead of letting the model fill from memory.
Python, Go, JavaScript, TypeScript, Rust, C, C++ and Java — parsed via tree-sitter grammars. More can be added by wiring up the grammar.
Run repolens drift --ci; it exits non-zero when new stale-doc findings appear, so a pull request that lets docs drift from code fails the check.
No. Vectors, the BM25 index, the symbol graph and metadata all live under ~/.repolens. The only outbound traffic is the LLM API call — and only for the cloud backends.
Yes — the package is pure-Python and its native dependencies ship Windows wheels. Use Python 3.11 or 3.12 and have Git installed.
Index a repository in minutes and get answers you can actually open and verify.