zlf can turn a repository into queryable code symbols, lexical documents, and graph relationships via Tree-sitter. The default code-index path emphasizes BM25 plus structural graph traversal and does not require vector embedding.
Java, C, C++, Python, Rust, JavaScript, TypeScript, Kotlin, Go, Swift — ten languages with pinned Tree-sitter grammars, versioned adapters, and golden parse fixtures.
The zlf-codeindex crate provides four layers:
code_search, code_callers,
code_callees, code_path, code_cycle), and a shared CodeQuery AST
for Prolog and JSON/HTTP.use zlf_codeindex::{CodeGraph, CodeRetrieval, CodeQuery, IndexLifecycle, FileRef};
// 1. Open (or create) the lifecycle and graph.
let mut life = IndexLifecycle::open("my-index/index")?;
let graph = CodeGraph::open("my-index")?;
// 2. Discover files.
let inventory = zlf_codeindex::discover("src/".as_ref(), &Default::default());
// 3. Ingest one file.
let f = FileRef {
rel_path: "src/main.py".into(),
language: Some(zlf_codeindex::Language::Python),
size_bytes: 0,
};
life.import_file(&f, source, &[])?;
// 4. Sync into the graph.
graph.sync("my-repo", &life)?;
// 5. Query.
let retrieval = CodeRetrieval::new(graph.storage())?;
let hits = retrieval.search("parse", 10, &Default::default(), 1000);
let callers = retrieval.callers("code:sym:...", Default::default());
Once a repository is ingested, code symbols are ordinary graph facts queryable through the standard Prolog runtime:
% Label shortcut: every code_symbol is a fact.
? code_symbol(X).
% Property shortcut.
? prop_simple_name(X, "parse").
% Edge shortcut: call edges.
? code_calls(A, B).
% Bounded traversal (with explicit depth and status).
? code_search("Dispatcher", 10, S).
? code_callers(S, Caller, 8, Status).
? code_callees(S, Callee, 8, Status).
? code_path(From, To, Path, Rank, Status).
? code_cycle(Symbol, Cycle, Status).
Status is ok or exhausted(budget) — budget exhaustion is explicit.code_search returns symbol hits only; raw-source search is a ripgrep
concern.The same CodeQuery AST is used by both Prolog and JSON. execute_json
parses a JSON request and returns a uniform result shape:
{
"op": "search",
"query": "parse",
"top_k": 10
}
{
"op": "search",
"answers": [
{ "symbol_id": "code:sym:...", "score": 3.21 }
],
"exhausted": []
}
| Variant | Input | Output |
|---|---|---|
search |
query string, top_k, filters | ranked symbol hits |
callers |
symbol id, budgets | reachable callers (deduplicated) |
callees |
symbol id, budgets | reachable callees (deduplicated) |
path |
from, to, top_n, budgets | top-N shortest simple paths |
cycle |
symbol id, budgets | simple cycles containing the symbol |
expand |
symbol ids, edge kinds, budgets | adjacent symbols |
All requests carry CodeBudgets (depth, visited, edges, paths, timeout) and
an exhausted vector reporting which budget was hit.
# Initialize (full build from a scan root).
zlf code init --scan-root ./my-repo --db ./zlf-db --repo my-repo
# Full rebuild (re-import every file).
zlf code index --scan-root ./my-repo --db ./zlf-db --repo my-repo
# Incremental sync (only changed/deleted files).
zlf code sync --scan-root ./my-repo --db ./zlf-db --repo my-repo
# Query.
zlf code search "parse" --top-k 10 --kind function --lang python
zlf code callers <symbol-id> --depth 8
zlf code callees <symbol-id> --depth 8
zlf code path <from> <to> --top-n 8 --depth 32
zlf code cycle <symbol-id> --depth 8
zlf code expand sym1,sym2 --kinds calls,imports --depth 8
All query subcommands print a uniform JSON result with op, answers, and
exhausted fields.
The same operations are available through the HTTP server (zlf serve):
| Endpoint | Operation |
|---|---|
POST /api {"command":"code_init", ...} |
full build |
POST /api {"command":"code_index", ...} |
full rebuild |
POST /api {"command":"code_sync", ...} |
incremental sync |
POST /api {"command":"code_query", "query":{...}} |
any CodeQuery |
The code_query endpoint accepts the same CodeQuery JSON shape used by
Prolog and the Rust API. All endpoints accept an optional path field
(defaults to the server’s configured db_path).
{"command": "code_query", "query": {"op": "search", "query": "parse", "top_k": 10}}
{"command": "code_query", "query": {"op": "callers", "symbol": "code:sym:...", "budgets": {"depth": 8}}}
The code_symbol analyzer stores the normalized complete identifier plus
identifier-boundary subtokens: ServiceDispatcher → service + dispatcher.
Matching is boundary-subtoken only (no ngram, suffix, or fuzzy).
Run the smoke tier (1 000 files):
ZLF_BENCH_TIER=smoke cargo test -p zlf-codeindex --test bench_runner --ignored --nocapture
Tiers: smoke (1 000 files) / mid (10 000) / target (100 000).
The runner prints a machine-readable JSON report and asserts: