Universal Repository Agent Graph: structure-aware, token-efficient RAG for projects
Project description
urag — Universal Repository Agent Graph
Structure-aware, token-efficient RAG for software projects. Indexes symbols, signatures, relationships and docs into a single portable SQLite database, then answers queries with small, cited evidence packets instead of whole files.
Key design (per 2026 RAG research):
- Retrieval keys ≠ evidence payloads — compact L0/L1 records (name, signature, summary, concepts) are embedded and lexically indexed; exact source spans (L2) are loaded lazily on demand.
- Hybrid retrieval — FTS5 lexical (finds names/identifiers) + sqlite-vec dense (finds concepts), fused with Reciprocal Rank Fusion.
- Structure-aware units — tree-sitter extracts functions, methods, classes, interfaces, imports with line/byte spans, docstrings and relationships.
- Incremental + continuous — mtime-based re-indexing;
watchdaemon keeps the index fresh automatically. - Model-agnostic embeddings — local ONNX (offline, no API key) by default,
or any OpenAI-compatible
/embeddingsendpoint.
Install
urag is a Python CLI that installs anywhere uv or Python runs (Windows,
macOS, Linux). Requires only the urag command plus git (for git-aware
provenance); the embedding model (~25 MB) downloads on first use.
One-liner from the internet (needs uv; auto-installs uv if missing):
# macOS / Linux
curl -LsSf https://raw.githubusercontent.com/Abas-Tim/urag/main/bootstrap/install.sh | sh
# Windows (PowerShell)
irm https://raw.githubusercontent.com/Abas-Tim/urag/main/bootstrap/install.ps1 | iex
From PyPI (once published) — the standard install:
uv tool install urag-cli # global `urag` on PATH (uv)
pipx install urag-cli # same, via pipx
# or in a project venv: uv add urag-cli
urag --version
Zero-install for agent harnesses (MCP) — uvx downloads and caches on
first use, no setup on new machines:
{ "mcpServers": { "urag": { "command": ["uvx", "--from", "urag-cli", "urag", "mcp", "--root", "/path/to/project"] } } }
uvx supports any install source, so until urag is on PyPI you can point
it at the repo or a wheel: uvx --from git+https://github.com/Abas-Tim/urag urag mcp --root .
From source (developers):
git clone https://github.com/Abas-Tim/urag
cd urag && uv sync && uv run urag --help
Usage
urag init # set up .urag/ config + .gitignore, initial index
urag watch # continuously re-index on file changes (Ctrl+C to stop)
urag search "how does auth work" # hybrid search
urag search "TokenValidator" --mode lexical --top-k 5
urag search "RRF" --mode lexical --json --evidence
urag get 190 # fetch exact source span for a unit
urag status # index stats
urag doctor # health check
Project config lives in .urag/urag.toml (auto-created):
[embedding]
provider = "local" # local | http | none
model = "BAAI/bge-small-en-v1.5"
dimension = 384
# http_url = "http://localhost:11434/v1"
# http_api_key = ""
# http_model = "nomic-embed-text"
[index]
languages = ["python", "typescript", "javascript", "go", "rust", "java", "c", "cpp", "markdown"]
exclude = [".urag", ".git", "node_modules", "dist", "build", ".venv"]
Architecture
sources ──► tree-sitter extractors ──► units (L0/L1 records)
│
┌──────────────────┼──────────────────┐
▼ ▼ ▼
FTS5 (lexical) vec0 (dense) files/units tables
└──────────────┬──────────────────┘
▼
RRF fusion → ranked evidence packets
▼
L2 source spans loaded on demand (`get`)
| Layer | Contents | Size |
|---|---|---|
| L0 | qualname, signature, summary, concepts (retrieval key, embedded) | ~100–300 tokens |
| L1 | relationships, file/line/byte pointers | ~50 tokens |
| L2 | exact source span, loaded on demand | full unit |
Notes
- Per-project single-file index:
.urag/index.db(SQLite + WAL). Gitignored. - Embeddings default to
BAAI/bge-small-en-v1.5(384 dims, ~25 MB, cached in%LOCALAPPDATA%\uragor~/.cache/urag). tree-sitteris pinned<0.26— 0.26.0 has an unfixed use-after-free segfault during repo-scale traversals (py-tree-sitter issue #472).
Git-aware invalidation
Every indexed file records the commit it was indexed at. Search results and
evidence packets carry commit (8-hex) and stale flags:
stale=truemeans the file changed since it was indexed (uncommitted edit, branch switch, pull) — treat the evidence as outdated and re-index.- Re-indexing uses
git status/git diffto find changed files (O(changed) instead of stat-ing the whole repo) and works even when file mtimes lie. - Non-git projects fall back to mtime-based checks automatically.
urag search "parse_token" --mode lexical --json # results include commit/stale
urag get 190 # span + commit + stale flag
Adaptive query classification
Queries are auto-classified into budget tiers before retrieval — context is a per-query decision, not a fixed constant:
| Class | top_k | budget | behavior |
|---|---|---|---|
symbol |
3 | 800 tok | exact names/identifiers: lexical-only |
local |
5 | 2,000 tok | single-module implementation questions |
debugging |
8 | 4,000 tok | errors, crashes, cross-module flow |
impact |
10 | 6,000 tok | callers, dependencies, "what breaks if X" |
Evidence spans returned with --evidence are trimmed to the class budget
(head + truncation note; the full span stays available via urag get).
urag classify "what calls this method" # -> impact (top_k=10, 6000 tok)
urag search "why does it crash" --top-k 2
Evaluating retrieval quality (urag eval)
Compares urag against the non-urag context baselines on the same questions:
# auto-generate provable-gold questions (definitions + callers), compare default systems
urag eval --root . --autogen 10 --top-k 5
# hand-written questions.jsonl: {"query": "...", "gold_file": "src/a.py"}
urag eval --root . --questions questions.jsonl
# add the LLM answer-quality tier (OpenAI-compatible endpoint)
urag eval --root . --autogen 10 --judge-url https://api.openai.com/v1 --judge-model gpt-4o-mini --judge-key $KEY
# machine-readable report
urag eval --root . --autogen 10 --json --report eval.json
Systems compared per question: urag-hybrid, urag-lexical, rg (grep
baseline), chunk (structure-free fixed-size chunk embeddings), and optional
oracle (whole gold files). Metrics: recall@k, MRR, tokens/retrieval,
p50/p95 latency. Definition and call questions get provable gold from the
index itself (the unit's file / the actual call_edges), so evaluation works
without hand labeling; doc/conceptual questions need a hand-written
--questions file.
Call graph (impact analysis)
Every function body is scanned for call sites (python call, ts/js/go/rust/c/cpp
call_expression, java method_invocation, c# invocation_expression), stored
as call_edges(caller_unit, callee, callee_full, line). Impact questions
("what calls X", "what breaks if X changes") are answered from the graph
instead of text similarity:
urag search "what calls index_all" # -> mode=calls, real callers + line numbers
urag callers fit_evidence # dedicated command
{"query": "what calls index_all", "mode": "calls", "results": [
{"qualname": "init", "file": "src/urag/cli.py", "lines": [57, 82], "calls": "indexer.index_all", "call_line": 81}
]}
Matching normalizes the last segment (self.validate, os.path.exists,
TokenValidator::validate all match validate); constructor calls are
included (who instantiates X is an impact question). Alias resolution is
approximate by design — dynamic/reflective calls are invisible to static
analysis.
MCP server (agent harnesses)
urag mcp --root /path/to/project # stdio MCP server
# root also auto-discovers from cwd; set URAG_ROOT env var to override
Tools: search, fetch_unit, index_now, status, init_project.
Results are compact evidence packets (signature + summary + file:line + ranks);
exact source spans are fetched only for the units you need.
opencode
// opencode.json
{
"mcp": {
"urag": {
"type": "local",
"command": ["urag", "mcp", "--root", "/path/to/project"],
"enabled": true
}
}
}
Claude Code
// .mcp.json
{
"mcpServers": {
"urag": {
"command": "urag",
"args": ["mcp", "--root", "/path/to/project"]
}
}
}
Cursor / generic
{
"mcpServers": {
"urag": {
"command": "urag",
"args": ["mcp", "--root", "/path/to/project"],
"type": "stdio"
}
}
}
Skill files (harness guidance)
skills/urag/SKILL.md teaches agents the token-efficient workflow (search
small → fetch evidence only for what you use). Install per harness:
# opencode (global)
cp -r skills/urag ~/.config/opencode/skills/urag
# Claude Code
cp -r skills/urag ~/.claude/skills/urag
Roadmap
- MCP server (
urag mcp) for Claude Code / opencode / Cursor - Skill files (
skills/urag/SKILL.md) for agent harnesses - Git-aware invalidation (per-commit provenance, stale-evidence flags)
- More languages (go, rust, java, c, c++, c#)
- Adaptive query-classifier → per-query token budgets
- Call-graph pass — impact queries answered from real call edges
- Multi-hop traversal (callers-of-callers) on demand
- Import-alias resolution for fully-qualified callee chains
- More languages (kotlin, swift, ruby, php, zig)
- Train the query router from production traces
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file urag_cli-0.1.1.tar.gz.
File metadata
- Download URL: urag_cli-0.1.1.tar.gz
- Upload date:
- Size: 46.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.12.1 {"installer":{"name":"uv","version":"0.12.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
09db07f9cb44013dd68e0ba6b8f4c19a736289df1ee19f34239f6853144b6aa0
|
|
| MD5 |
fec1ff72bcb17f64484b297f3daf1c74
|
|
| BLAKE2b-256 |
756c66df83379c16078d2075ee178be1e79bdf9e580298c86fea02a142378a00
|
File details
Details for the file urag_cli-0.1.1-py3-none-any.whl.
File metadata
- Download URL: urag_cli-0.1.1-py3-none-any.whl
- Upload date:
- Size: 52.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.12.1 {"installer":{"name":"uv","version":"0.12.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
af416770cd93c32eb196605accdcda4691cb7b55fc5089f0c456e5cb10f38dd4
|
|
| MD5 |
d7f4b1c0043db2447b108aaa6d8e3a55
|
|
| BLAKE2b-256 |
6466db1f35c802877296f03366986dec8bbe695c5314cfbc4a4329cdc528349f
|