xtrc is a local, editor-agnostic AI code navigation system.
It indexes your repository with tree-sitter + embeddings, stores vectors in local Qdrant, and returns file+line jump targets from natural-language queries.
- Local daemon with FastAPI APIs:
/index,/query,/status - Tree-sitter parsing for Python, JavaScript, TypeScript, TSX
- Semantic chunking with token-aware chunk size (200-800 tokens)
- Chunk metadata includes path, line range, symbol name, description
- Local embeddings via
BAAI/bge-base-en-v1.5 - Embedding cache keyed by content hash
- Local Qdrant vector search (embedded mode)
- Incremental indexing (changed files only) with full rebuild option
- Honors
.gitignorepatterns during indexing - Hybrid ranking:
0.50 vector + 0.18 keyword + 0.12 symbol + 0.12 intent + 0.08 structural - Optional Gemini reranking for low-confidence results (top 10 candidates)
- Optional Gemini-based query rewriting with response caching
- Optional Gemini-based chunk summaries generated once during indexing (cached)
- Intent-aware route encoding (
POST -> create,PUT/PATCH -> update,DELETE -> delete) - Intent + structural query boosting for API/route searches
- Semantic embedding input built from enriched metadata and summaries (no raw-code body embedding)
- Optional pre-embedding query rewriting to precise technical intent
- Local cross-encoder reranking on top candidates before final LLM rerank
- CLI, Neovim, and VS Code clients using the same HTTP protocol
Clients (CLI, Neovim, VS Code)
|
| HTTP+JSON
v
+-----------------------------+
| FastAPI daemon (xtrc) |
| endpoints: /index /query |
| /status |
+-----------------------------+
|
v
+-----------------------------+
| Core services |
| - Repo walker + ignore |
| - Tree-sitter parser |
| - Chunk builder |
| - Embedding service + cache |
| - Qdrant vector store |
| - Hybrid query scorer |
| - Optional Gemini reranker |
| - Optional chunk summarizer |
| - Query rewriter |
| - Local cross-encoder rank |
+-----------------------------+
|
v
+-----------------------------+
| Local persistent state |
| - .xtrc/metadata.db |
| - .xtrc/qdrant/ |
| - .xtrc embeddings cache |
+-----------------------------+
POST /indexwalks repo recursively and filters ignored paths (.git,node_modules,dist,build, and.gitignorematches).- File content hash is compared with stored hash to detect changes.
- Changed files are parsed by tree-sitter into symbols and blocks.
- Chunk builder creates semantic chunks in token budget.
- Embeddings are generated or loaded from hash-based cache.
- Vectors are upserted into Qdrant with metadata payload.
- During indexing, route chunks are enriched with explicit intent metadata:
Intent: create resourceHTTP method: POSTResource: post
- Optional index-time summary is generated once per chunk (cached by chunk content hash + model).
- Embedding input is semantic text only:
File,Symbol,Type,Intent,Summary, HTTP metadata.
- Query may be rewritten to technical intent before embedding.
POST /queryembeds rewritten query, runs ANN search, and computes hybrid scores with structural heuristics.- Top candidates are locally reranked by cross-encoder before optional Gemini reranking.
- Response includes ranked results with explanations (
matched intents,matched keywords,why) plus finalselection.
xtrc/
api/
core/
indexer/
llm/
query/
ranking/
cli.py
client.py
config.py
schemas.py
server.py
plugins/
nvim/xtrc.lua
scripts/
install.sh
run_demo.sh
bench_query.py
reindex_with_semantics.sh
demo_semantic_ranking.sh
examples/
demo_app/
tests/
pyproject.toml
requirements.txt
requirements-dev.txt
README.md
scripts/install.sh
source .venv/bin/activateManual install alternative:
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
pip install -e .Set these env vars before running xtrc serve:
export USE_GEMINI=true
export GEMINI_API_KEY="<your_api_key>"
export GEMINI_MODEL=gemini-2.5-flash # or another supported Gemini model
export GEMINI_THRESHOLD=0.85
export GEMINI_TIMEOUT_SECONDS=2
export GEMINI_ENABLE_REWRITE=false
export GEMINI_SUMMARIZE_ON_INDEX=true
export GEMINI_SUMMARY_MODEL=gemini-2.5-flash
export GEMINI_SUMMARY_MAX_CHARS=320
export LLM_PROVIDER=gemini # gemini or openai
export LLM_TIMEOUT_SECONDS=2
export QUERY_REWRITE_ENABLED=false # set true to enable LLM query rewrite (higher latency)
export QUERY_REWRITE_MODEL=gemini-2.5-flash
export LOCAL_RERANKER_ENABLED=false # set true to enable local cross-encoder rerank (higher latency)
export LOCAL_RERANKER_MODEL=cross-encoder/ms-marco-MiniLM-L-6-v2
export LOCAL_RERANKER_TOP_K=10
export HEURISTIC_ROUTE_BOOST=1.3
export HEURISTIC_NOISE_PENALTY=0.7
export HEURISTIC_INTENT_BOOST=1.2
# If using OpenAI provider:
# export OPENAI_API_KEY="<your_openai_key>"Behavior:
- Best vector similarity
>= GEMINI_THRESHOLD: Gemini is skipped. - Best vector similarity
< GEMINI_THRESHOLD: top 10 candidates are sent to Gemini. - If Gemini fails or times out,
xtrcgracefully falls back to top vector result. - If
GEMINI_SUMMARIZE_ON_INDEX=true, chunk summaries are generated once and cached by chunk content key. - Query rewrite and index-time summaries use a hard LLM timeout (
LLM_TIMEOUT_SECONDS, default2s). - Local cross-encoder reranking runs on top candidates before Gemini fallback when
LOCAL_RERANKER_ENABLED=true. - By default,
QUERY_REWRITE_ENABLED=falseandLOCAL_RERANKER_ENABLED=falsefor lower/querylatency.
- Start daemon:
xtrc serve- Index repository:
xtrc index .- Query for jump target:
xtrc query "get user score" --repo . --top-k 8- Check status:
xtrc status .xtrc serve [--host 127.0.0.1] [--port 8765]xtrc index <repo_path> [--rebuild] [--json]xtrc query "<natural language>" [--repo <path>] [--top-k N] [--json]xtrc status <repo_path> [--json]
Request:
{
"repo_path": ".",
"rebuild": false
}Response:
{
"status": "ok",
"repo_path": "/absolute/path/to/repo",
"files_scanned": 120,
"files_indexed": 8,
"files_deleted": 1,
"chunks_indexed": 233,
"duration_ms": 842
}Request:
{
"repo_path": ".",
"query": "get user score",
"top_k": 8
}Response:
{
"status": "ok",
"repo_path": "/absolute/path/to/repo",
"query": "get user score",
"results": [
{
"file_path": "src/user.py",
"start_line": 42,
"end_line": 88,
"symbol": "get_user_score",
"description": "Function get_user_score in src/user.py...",
"score": 0.83,
"vector_score": 0.78,
"keyword_score": 1.0,
"symbol_score": 0.5,
"intent_score": 1.0,
"structural_score": 0.75,
"matched_intents": ["create_resource"],
"matched_keywords": ["create", "post", "endpoint"],
"explanation": "semantic=0.780; keyword=1.000; symbol=0.500; intent=1.000; structural=0.750; heuristics=intent match: create_resource, route handler boost"
}
],
"duration_ms": 12,
"selection": {
"file": "src/user.py",
"line": 42,
"reason": "Chunk contains the user scoring path and return value."
},
"selection_source": "gemini",
"used_gemini": true,
"gemini_model": "gemini-2.5-flash",
"gemini_latency_ms": 241,
"rewritten_query": "Find where user score is computed and returned."
}Response:
{
"status": "ok",
"repo_path": "/absolute/path/to/repo",
"indexed_files": 120,
"indexed_chunks": 2330,
"model": "BAAI/bge-base-en-v1.5",
"healthy": true,
"last_indexed_at": "2026-02-20T22:08:17.439000+00:00"
}{
"status": "error",
"error": {
"code": "INVALID_REPO",
"message": "Repository path does not exist or is not a directory",
"details": {}
}
}File: plugins/nvim/xtrc.lua
Minimal setup in Neovim config:
local xtrc = dofile("/absolute/path/to/repo/plugins/nvim/xtrc.lua")
xtrc.setup({
server_url = "http://127.0.0.1:8765",
top_k = 8,
})Usage:
:xtrc get user scoreBehavior:
- One result: opens file directly at line
- Multiple results: shows
vim.ui.selectpicker
- Query path is optimized with cached query embeddings and ANN search.
- Gemini calls are gated by a similarity threshold to avoid unnecessary LLM latency.
- Gemini rerank + rewrite responses are cached with LRU strategy.
- Index-time chunk summaries are cached in SQLite and reused across subsequent indexes.
- Intent + structural boosts reduce route query ambiguity (
create post,update post,delete post). - Local cross-encoder reranking improves ordering among top semantic candidates.
- For BGE models, query embeddings use model-recommended retrieval instruction prefixing.
- Embedding cache uses content hash keys in local SQLite.
- Incremental index avoids re-embedding unchanged files.
- Qdrant collection is per repository for isolated search space.
Actual latency depends on hardware and model warm-up.
Benchmark helper:
scripts/bench_query.py "get user score" --repo . --runs 100Run complete demo flow:
scripts/run_demo.shDemo indexes examples/demo_app and runs query get user score.
Gemini rerank demo:
export USE_GEMINI=true
export GEMINI_API_KEY="<your_api_key>"
export GEMINI_MODEL=gemini-2.5-flash
xtrc serve
xtrc index examples/demo_app
xtrc query "where is score calculation used" --repo examples/demo_app --jsonCheck selection, selection_source, and used_gemini in the JSON response.
Semantic reindex migration:
scripts/reindex_with_semantics.sh .Semantic ranking demo:
scripts/demo_semantic_ranking.sh examples/demo_appRun unit tests:
source .venv/bin/activate
.venv/bin/pytestQuick quality checks:
python -m compileall xtrc
ruff check .
mypy xtrc- Server unreachable:
- Start daemon with
xtrc serve. - Verify host/port match client settings.
- Start daemon with
- First query is slow:
- Embedding model warm-up can take seconds.
- Subsequent queries use in-memory and SQLite cache.
- Indexing is slower after enabling summaries:
GEMINI_SUMMARIZE_ON_INDEX=trueadds one LLM summary pass per new/changed chunk.- Summaries are cached and reused on future indexes.
- No results:
- Ensure index completed successfully.
- Use
xtrc status .to verify chunk count. - Rebuild index with
xtrc index . --rebuild.
- Gemini not used when expected:
- Confirm
USE_GEMINI=true. - Confirm
GEMINI_API_KEYis set in the server process environment. - Lower
GEMINI_THRESHOLDif vector confidence is usually high.
- Confirm
- Gemini failures/timeouts:
- Default timeout is 2 seconds (
GEMINI_TIMEOUT_SECONDS). - On timeout/failure, response falls back to vector top match.
- Default timeout is 2 seconds (
- Crash after changing embedding model:
- Existing vector collections may use a different dimension.
xtrcnow auto-resets incompatible collections, butxtrc index . --rebuildis still recommended immediately after model changes.- If query returns
INDEX_DIMENSION_MISMATCH, runxtrc index <repo> --rebuild.
- VS Code command missing:
- Compile extension:
npm run compile. - Relaunch extension host.
- Compile extension:
- Neovim plugin fails:
- Ensure
curlis installed. - Confirm plugin points to running
server_url.
- Ensure
MIT
