walk → chunk (AST) → contextualize → embed → LanceDB (+ BM25 FTS)
↑ manifest ↑ chat provider ↑ embeddings provider
- Walk (
walk.py, pure stdlib): discovers files per the manifest — extension allow-list, directory/glob excludes, per-repo excludes, size cap, binary sniff. - Chunk (
chunk_ast.py): tree-sitter isolates top-level definitions (functions, classes, methods…) per language (PHP/Python/TS/JS); comments and decorators are absorbed into their definition; inter-definition "glue" is grouped. Unsupported languages fall back to a character window with overlap (chunk.py). Thedocscorpus chunks per entry instead: YAML top-level keys, Markdown sections. - Contextualize (
context.py, "contextual retrieval"): each chunk gets a one-sentence description of its role in its file, prefixed to the text before embedding. One chat call per file (not per chunk) keeps the cost tiny; a deterministic structural fallback ([repo X | file Y | in <symbol> | lines a-b/n]) covers gaps, failures, and--dry-run. Modestructgives you the free deterministic variant;offdisables prefixes. - Embed (
embed.py+ a provider): batched under both an input-count and a character budget, throttled globally, retried on 429 with backoff. - Store (
store.py, LanceDB): rows carry the raw chunk (text), the contextualized text (embedded + BM25-indexed), line numbers, sha256, the strategy version (embed_ver), and the sidecar metadata (source forge, last commit date, status, permalink).
Incrementality: a file is re-embedded only when its sha256 or the strategy version changed; files gone from disk are removed. The FTS index is rebuilt after any change (deletions included) to stay consistent and mono-fragment.
question → rewrite (chat) → embed → vector + BM25 → RRF → [LLM rerank] → meta rerank → top-k
- Rewrite: a chat call expands acronyms (yours, via
prompts.domain_hints) and adds plausible symbol names. Failure = fall back to the original question. - Hybrid retrieval: vector similarity and BM25 full-text run together, fused by Reciprocal Rank Fusion; oversampled (k×3) when a rerank follows.
- Meta rerank: a bounded nudge on top of relevance — dead code sinks hard, doubtful/stale (>3 y) code drops a few places, active/recent (<1 y) code rises a little. Relevance stays dominant; the nudge only breaks near-ties.
Pure AST, no API (graph.py). Definitions and call sites are extracted per file,
then calls are resolved through a confidence cascade:
| Resolution | Confidence |
|---|---|
$this->m() / self:: / parent:: via class hierarchy |
0.95 |
Class::m() / new Class() via use/import/namespace |
0.90 |
| free function, exact import match | 0.95 |
| free function, same namespace | 0.90 |
| unique name in the project | 0.75 |
| ambiguous name | 0.35 |
Edges ≥ 0.70 are never pruned; low-confidence name-based edges are capped by
fan-out. Each node carries fan-in and PageRank (that's hotspots). Queries bucket
results into certain / probable / uncertain tiers so a consumer (or an agent) can
qualify its claims. The artifact is versioned gzipped JSON (graph-v2);
load_graph rebuilds the reverse adjacency at load time.
out_dir/
├── lancedb/ # tables code_chunks, docs_chunks
├── graph.json.gz # {version, nodes, out, by_fqn}
└── meta.json # {repo_source, repo_ref, web_base, last_commit, status}
Artifacts are the interface between the build side and the query side: build where
your clones and git history live, ship the directory (atomically), query anywhere —
including hosts that never see the source code. Schema/versioning constants are
frozen and test-guarded; an index built by the ancestor of this library
(Infoclimat's code_index) reads unchanged.