Semantic code search and static call-graph analysis for your repos — one manifest, one build command, artifacts you can query from a CLI, a Python API, or an MCP server.
- Semantic search: AST-aware chunking (function/class boundaries), contextual retrieval (each chunk embedded with a sentence situating it in its file), hybrid vector + BM25 retrieval with RRF fusion, optional query rewriting, and an authority/recency rerank fed by your git history.
- Call graph: a static PHP/Python/TypeScript/JavaScript call graph with
confidence-scored edges — impact analysis (
who calls this?), hotspots (PageRank- fan-in), shortest call path between two symbols, dead-symbol detection. 100 % offline, no API key needed.
- Pipeline:
repolens buildruns sidecar → code index → docs index → graph, incrementally (sha256 + strategy version). Re-running on an unchanged workspace makes zero API calls — point a cron/systemd timer/CI schedule at it and forget it. - Pluggable: embeddings/chat providers are protocols (Mistral built in, ~20 lines to bring your own), forge URL mapping and governance-status hooks are manifest configuration, prompts are templated (en/fr) with domain-vocabulary injection.
pip install "repolens[full]" # or: uv add "repolens[full]"
cd ~/workspace # the directory containing your git clones
repolens init # writes repolens.yaml, detects your repos
export MISTRAL_API_KEY=… # only needed for the semantic index
repolens build --dry-run # estimate volume & cost first (no API call)
repolens build # sidecar + index + graph → ./.repolens/
repolens search "where is user authentication handled?"
repolens graph impact --graph .repolens/graph.json.gz UserService --direction callersTypical cost: embeddings run at ~$0.15 per million tokens — a full first index of a mid-size repo is cents, and subsequent builds only re-embed changed files.
No API key? The call graph and all four graph queries (impact, hotspots,
path, dead) work without any provider: repolens build --skip code --skip docs.
from repolens import build_all, search_code
report = build_all("repolens.yaml") # same pipeline as the CLI
hits = search_code("retry logic for failed uploads", k=5, repos=["backend"])
for h in hits:
print(h.location, h.score, h.source_url)
from repolens.graph import load_graph, code_impact
g = load_graph(".repolens/graph.json.gz")
print(code_impact(g, "fetch_user", direction="callers", depth=2))Everything lives in one YAML manifest (repolens init scaffolds it; every knob is
also overridable with REPOLENS_* environment variables — see
docs/manifest.md):
base_dir: . # where the repos live, relative to this file
repos: [backend, frontend, infra]
include_ext: [.py, .ts, .php]
output: {dir: ./.repolens}
prompts: # optional: teach the query rewriter your vocabulary
domain_hints: >-
e-commerce platform; PIM = product information management
forges: # optional: self-hosted forges for source permalinks
- {host: git.example.org, kind: gitlab}
provider: # optional: bring your own embeddings/chat backend
embeddings: repolens.providers:MistralProvider| docs/quickstart.md | the long version, with cost estimation |
| docs/manifest.md | full manifest & environment reference |
| docs/providers.md | write your own embeddings/chat provider |
| docs/extensions.md | status hooks & forge rules |
| docs/pipelines.md | cron / systemd / GitHub Actions recipes |
| docs/mcp.md | expose search & graph as MCP tools for agents |
| docs/telemetry.md | OpenTelemetry spans and counters |
| docs/internals.md | how the index and the graph work |
| Extra | Pulls | Enables |
|---|---|---|
| (core) | pyyaml, pydantic | manifest, walk/chunk, graph queries |
[ast] |
tree-sitter | AST chunking, graph build |
[store] |
lancedb | vector store (search needs it) |
[mistral] |
mistralai | default embeddings/chat provider |
[otel] |
opentelemetry | build telemetry |
[search] |
mistral+store | query an existing index |
[full] |
everything | build + query + observe |
Python ≥ 3.10. Licensed under Apache-2.0.
Extracted from the Infoclimat data-platform tooling, where it indexes ~6 000 files across 17 repos and powers the MCP tools of a Discord data-governance bot. The Infoclimat-specific configuration (manifest, French prompts, governance status hook) lives in that project — nothing domain-specific remains in this library.