Local context retrieval engine for coding agents. Indexes codebases, resolves dependencies, and delivers the smallest useful context packet — so agents read fewer files and waste fewer tokens.
Agents burn tokens exploring codebases: reading files, grepping, guessing structure. Kungfu replaces that with one call:
$ kungfu ask-context "add C++ language support" --budget tiny
Task: add C++ language support
Intent: lookup
Budget: tiny
Items: 3
0.50 [crates/kungfu-parse/src/lib.rs] extract_symbols
0.45 [crates/kungfu-config/src/lib.rs] LanguagesConfig
0.45 [crates/kungfu-types/src/file.rs] Language
275 tokens instead of reading files manually (~7,500+ tokens per file).
Recommended — single binary, no dependencies:
# macOS / Linux
curl -fsSL https://raw.githubusercontent.com/denyzhirkov/kungfu/master/install.sh | sh
# Windows (PowerShell)
irm https://raw.githubusercontent.com/denyzhirkov/kungfu/master/install.ps1 | iexSupports macOS (ARM64, x86_64), Linux (ARM64, x86_64), and Windows (x86_64).
Build from source:
git clone https://github.com/denyzhirkov/kungfu.git
cd kungfu
cargo build --release
cp target/release/kungfu ~/.local/bin/kungfu init # initialize in project root
kungfu index # build the index
kungfu doctor # verify everything works# Smart context — the main command
kungfu ask-context "find where JWT refresh is implemented" --budget small
kungfu ask-context "impact of changing Budget enum" --budget medium
kungfu ask-context "fix crash in python parser" --budget tiny
# Context from git changes
kungfu diff-context --budget smallkungfu find-symbol AuthService # search symbols by name
kungfu get-symbol refreshToken # exact symbol lookup
kungfu search "refresh token" # text search across files
kungfu search "auth logic" --semantic # vector search when embeddings are built (see below)# Parse a stack trace from stdin, get context for the involved code
cargo run 2>&1 | kungfu debug-trace --budget small
# Build context for a specific commit or PR
kungfu commit-context a0bab6d --budget small
kungfu pr-context 42 --budget small # requires `gh` CLIkungfu memory add "Use zod for all new validation" --kind decision --tag validation --pin
kungfu memory add "Legacy webhook uses old auth — do not refactor" --kind warning --tag auth
kungfu memory list # show all active entries
kungfu memory search "validation" # search memory
kungfu memory pin mem_0001 # pin for higher context priority
kungfu memory archive mem_0002 # archive without deletingPinned entries and warnings are automatically injected into ask-context output, so agents don't restart from zero every session. When two active entries cover the same topic with different content, ask-context surfaces a memory_conflicts block instead of silently picking one.
kungfu repo-outline # compact repo map
kungfu file-outline src/auth/service.ts # symbols in a filekungfu onboard # project summary: architecture, patterns, key symbols
kungfu affected AuthService --depth 3 # blast radius: who depends on this symbol?
kungfu affected --staged --depth 3 # blast radius of the current staged diff
kungfu smart-test # minimal test set based on git diff
kungfu test-subjects test_login # reverse: production code exercised by a test
kungfu review # code review context: risks, missing co-changes
kungfu coupling --top 10 # module coupling: fan-in, fan-out, co-change
kungfu hotspots --churn # largest/most changed codekungfu export --format jsonl > graph.jsonl # dump files/symbols/relations/memory for external toolskungfu index --full # full rebuild
kungfu index --changed # reindex only git-changed files
kungfu doctor # validate installation and index
kungfu watch # auto re-index on file changes
kungfu clean # wipe index and cache
kungfu config # show current configuration
kungfu embeddings status # vector-search readiness (see below)semantic_search (MCP) / kungfu search --semantic (CLI) has two modes that share the same call site. The right one runs automatically based on what's available — the agent doesn't need to choose.
With a default build and no setup, --semantic runs query expansion: the query is split into keywords, each keyword is widened with a small synonym table (auth → verify, token, session, ...), and we search symbol names for the expansion. Fast, zero install, decent for queries that share vocabulary with the codebase.
With a real embedding model installed, the same call runs vector cosine top-K over 384-dim sentence embeddings. Finds concepts even when the query and the symbol share no words ("graceful degradation when index is missing" → ensure_fresh_index).
Three steps to enable:
cargo build --release --features semantic # 1. compile with candle + tokenizers (+~80MB binary, ~1min cold)
kungfu embeddings install # 2. download BAAI/bge-small-en-v1.5 (~130MB, one-time)
kungfu embeddings build # 3. compute one 384-dim vector per indexed symbolAfter every kungfu index rerun kungfu embeddings build to keep vectors in sync — it skips symbols whose text hash hasn't changed.
| You see in the output | What ran | What to do |
|---|---|---|
Engine: vector (CLI) or "engine": "vector" (JSON) |
Real embeddings | Use the results |
Engine: keyword |
Query expansion fallback | Use the results; for richer concept matching, finish the vector setup |
kungfu embeddings status (CLI) and the embeddings_status MCP tool both return the same struct with a one-line hint telling you the next step if anything is missing. Agents connected via MCP can call embeddings_status once on connect and embeddings_build after every index to manage the lifecycle themselves.
Query "rank symbols by relevance" against this repo (822 symbols):
| Mode | Top hit | Score | Notes |
|---|---|---|---|
| keyword | build_context_packet |
0.40 | matches "rank" in symbol bodies |
| vector | stem_match_ranking_to_rank |
0.70 | no shared words with the query — concept-level match |
Same call, different signal density. Keyword is honest about what it can do; vector lifts the ceiling for everything you can't predict in advance.
ask_context runs a multi-strategy search; when an embedding store + a real engine are present, it adds Strategy A2 — cosine top-K against the same vector store, capped at 5 hits, only when the query has at least 3 non-stop-word terms and each hit clears a cosine threshold of 0.55. This augments rather than replaces the existing strategies: vector hits land alongside symbol-name and content matches with a moderate base score, so they show up when lexical strategies miss the concept (e.g. "graceful degradation when index is missing" → ensure_fresh_index) and stay out of the way when lexical already nails it. Tunable via KUNGFU_W_VECTOR and KUNGFU_W_VECTOR_MIN.
All commands support --json for machine output and --budget tiny|small|medium|full.
The highest-value command. Given a task description, it:
- Detects intent — lookup, debug, understand, or impact
- Runs multiple search strategies — symbol search, text search, related files, import chains
- Applies contextual bonuses — changed files (+0.3), test/config proximity (+0.15), language weighting, tunable weights
- Returns a ranked packet with signatures and code snippets
| Intent | Triggers | Extra strategies |
|---|---|---|
lookup |
find, where, show, search | symbol + text search |
debug |
bug, fix, error, crash | + related files, error symbol boost |
understand |
how, explain, what, why | + sibling symbols from same file |
impact |
impact, refactor, rename, delete | + import chain, sibling symbols |
| Budget | Items | Snippets | Use case |
|---|---|---|---|
auto |
adaptive | adaptive | Default — adapts to project size |
tiny |
3 | none | Quick pointers — "where to look" |
small |
5 | 20 lines | Signatures + context |
medium |
8 | 40 lines | Deeper exploration |
full |
12 | 100 lines | Complete picture |
kungfu mcpAdd to your agent config (Claude Code, Cursor, etc.):
{
"mcpServers": {
"kungfu": {
"command": "kungfu",
"args": ["mcp"]
}
}
}| Tool | Description |
|---|---|
project_status |
File count, symbol count, languages, git status |
repo_outline |
Top directories, language distribution, entrypoints |
file_outline |
Symbols, signatures, exports for a file |
find_symbol |
Search symbols by name (exact + fuzzy + stem) |
get_symbol |
Detailed symbol info by exact name |
search_text |
Text search across indexed files |
find_files |
Find files by path pattern |
semantic_search |
Concept-level search; vector cosine top-K when embeddings are built, keyword expansion otherwise. Pick this when you don't know the exact symbol name |
embeddings_status |
Are vectors wired up? Returns model id, dim, feature/install/index state, and a one-line hint for the next step |
embeddings_build |
Compute or refresh vectors for all indexed symbols. Idempotent. Call after index to keep vector search in sync |
find_related_symbols |
Related symbols in same file |
ask_context |
Smart retrieval: intent + multi-strategy search + rationale + project memory + conflict surfacing |
diff_context |
Context focused on git changes |
debug_trace |
Parse a stack trace (Rust panic, JS stack, Python traceback, Go panic) and return involved symbols + siblings |
commit_context |
Build a packet focused on a specific git commit |
pr_context |
Build a packet covering all commits in a GitHub PR (needs gh) |
explore_symbol |
Composite: find + detail + related + snippet in one call |
explore_file |
Composite: outline + related files + key symbols |
investigate |
Composite: ask_context + diff awareness |
callers |
Call graph: who calls this symbol? |
callees |
Call graph: what does this symbol call? |
file_history |
Git log for a file: recent commits |
symbol_history |
Git blame + commits for a symbol |
change_timeline |
How code evolved: introduced, churn, decisions, recent changes |
memory_add |
Add a project memory entry (fact/decision/warning/session_summary) |
memory_search |
Search project memory by query |
memory_list |
List project memory entries with filters |
memory_get |
Get a single memory entry by ID |
memory_update |
Update content, title, tags, or pinned state |
memory_archive |
Archive an entry (preserves for history) |
onboard |
Project summary: architecture, patterns, key symbols, naming |
affected |
Blast radius: transitive callers/dependents of a symbol (use staged: true for current diff) |
smart_test |
Minimal test set based on git diff |
test_subjects |
Reverse of smart_test: production code exercised by a given test |
review |
Code review context: risks, missing co-changes, untested code |
coupling |
Module coupling: fan-in, fan-out, co-change frequency |
usage_stats |
Token savings, cache hit rate, calls served |
While connected, the MCP server pushes notifications/resources/updated (URI kungfu://index) whenever the project index changes on disk — agents that subscribe can invalidate cached assumptions instead of acting on stale state.
Add to CLAUDE.md or system prompt. Keep it about policy, not tool catalogs — the agent already sees tool descriptions from MCP.
## kungfu
- Prefer kungfu MCP tools over reading files directly. Only open a file when kungfu confirms you need it.
- Default to tiny/small budget. Escalate only when the packet is clearly insufficient.
- Use `memory_search` before implementing — project may already have a decision or warning about this.
- Use `memory_add` to preserve facts, decisions, and warnings worth remembering across sessions. Prefer pinning sparingly.Four lines is enough. The agent will pick ask_context, explore_symbol, affected, etc. on its own from tool descriptions.
Task received
│
▼
ask_context("task description", budget: "tiny") ← MCP tool or: kungfu ask-context "..." --budget tiny --json
│
├── Got enough? → Start working
│
├── Need more detail? → escalate to budget: "small"
│
├── Need specific symbol? → find_symbol / get_symbol
│
├── Need file structure? → file_outline
│
└── Only then → Read full file
Agent receives task: "fix the authentication bug"
Without kungfu — agent explores blindly:
Glob("**/*.ts") → 200 files, which to read?
Grep("auth") → 30 matches across many files
Read("src/auth/service.ts") → 500 lines, 2000 tokens
Read("src/auth/middleware.ts") → 300 lines, 1200 tokens
Read("src/auth/controller.ts") → 400 lines, 1600 tokens
... more files, more tokens
With kungfu — one MCP call:
→ tools/call: ask_context({"query": "authentication bug fix", "budget": "small"})
← {
"intent": "debug",
"items": [
{
"name": "AuthController",
"path": "src/modules/auth/auth.controller.ts",
"signature": "class AuthController",
"why": "symbol name match",
"score": 0.50,
"snippet": "export class AuthController {\n constructor(\n private readonly authService: AuthService,\n ) {}\n\n @Post('login')\n async login(@Body() dto: LoginDto) {\n return this.authService.login(dto);\n }\n ..."
},
{
"name": "AuthService",
"path": "src/modules/auth/auth.service.ts",
"signature": "class AuthService",
"why": "symbol name match",
"score": 0.50,
"snippet": "export class AuthService {\n async login(dto: LoginDto) { ... }\n async validateToken(token: string) { ... }\n ..."
}
],
"rationale": [
{
"source": "docs/adr/001-auth-jwt.md",
"kind": "decision",
"text": "We chose JWT for stateless session management to avoid server-side session storage",
"relevance": 0.72
}
],
"evidence": [
{
"source": "docs/adr/001-auth-jwt.md",
"excerpt": "We chose JWT for stateless session management..."
}
]
}Agent immediately knows where to look and why it was designed that way. Then reads only the relevant file.
| Project | Lang | Files | Query | kungfu | naive grep+read | Savings |
|---|---|---|---|---|---|---|
| ruff | Rust | 9,702 | "how does the linter check rules" | 722 | 137,767 | 190x |
| ollama | Go | 1,834 | "how does model loading and inference work" | 1,100 | 110,122 | 100x |
| SolidJS | TS | 168 | "how does the reactive signal system work" | 459 | 33,577 | 73x |
| leptos | Rust | 1,453 | "how does reactive rendering work" | 593 | 40,772 | 68x |
| tRPC | TS | 1,290 | "how does the RPC client call server procedures" | 680 | 43,773 | 64x |
| pydantic | Python | 729 | "how does field validation work" | 977 | 53,864 | 55x |
| FastAPI | Python | 2,882 | "how does dependency injection work" | 624 | 17,089 | 27x |
- Scans project files respecting
.gitignoreand configurable ignore rules - Parses code with tree-sitter — Rust, TypeScript, JavaScript, Python, Go, Java, C#, Kotlin, C, C++
- Extracts symbols: functions, classes, structs, methods, traits, interfaces, types, constants
- Extracts imports from AST and resolves them to actual files in the project
- Builds relations:
imports,test_for,config_for,calls - Extracts function call graph from AST (callers/callees)
- Extracts structured comments (TODO, FIXME, NOTE, doc comments) and populates
doc_summaryon symbols - Parses markdown documentation and ADR files into searchable memory entries
- Incremental re-indexing via blake3 file fingerprints
- Exact match (1.0), prefix (0.9), contains (0.7), stem (0.6), fuzzy (0.4)
- Exact phrase matching:
snake_case,camelCase, space-separated - Simple English stemming: "ranking" finds "rank", "indexing" finds "index"
- Path matching with filename boost (0.9) vs directory match (0.6)
- Multi-strategy search: symbols, text, related files, import chains, semantic expansion
- Deduplication by (path, name)
- Changed-file bonus from git
- Test/config proximity bonuses based on query intent
- Language importance weighting (primary language prioritized)
- Budget-controlled output with code snippets
Kungfu answers not only "where is this?" but also "why is it like this?":
- Comment extraction — TODO, FIXME, NOTE, and doc comments are extracted from tree-sitter ASTs during indexing
- Doc & ADR parsing — markdown files in
docs/,adr/,decisions/are parsed into searchable memory entries; ADR "Decision" sections get higher ranking weight - Rationale in context —
ask_contextreturns arationale[]field with matched design decisions, doc excerpts, and annotated comments alongside code items - Evidence fragments — verbatim excerpts from source docs/comments are attached as
evidence[]for agent trust and traceability - Layered context — use
include: ["code", "rationale", "history"]to control what layers are returned - Change timeline —
change_timelineshows how code evolved: when introduced, churn rate, linked decisions, recent changes - Doc summaries —
doc_summaryon symbols is auto-populated from preceding doc comments (first sentence, up to 120 chars)
.kungfu/
config.toml # project configuration
project.json # project metadata
index/
files.json # indexed files with hashes
symbols.json # extracted symbols with spans
relations.json # import/test/config relations
fingerprints.json # blake3 hashes for incremental rebuilds
memories.json # rationale: comments, doc sections, ADR decisions
.kungfu/config.toml:
project_name = "my-project"
[ignore]
paths = ["node_modules", "dist", "build", ".git", "target"]
[languages]
enabled = ["typescript", "javascript", "rust", "go", "python", "java", "csharp", "kotlin", "c", "cpp", "json", "markdown", "yaml", "toml"]
[search]
default_budget = "small"
default_top_k = 5
[index]
incremental = true
[git]
enabled = trueTested across popular open-source projects (Apple Silicon):
| Project | Language | Files | Symbols | Index | ask-context |
|---|---|---|---|---|---|
| express | JS | 201 | 1,948 | 0.5s | 227ms |
| flask | Python | 217 | 1,629 | 0.6s | 228ms |
| gin | Go | 118 | 1,487 | 0.7s | 217ms |
| axum | Rust | 474 | 2,771 | 1.3s | 269ms |
| cargo | Rust | 2,718 | 12,009 | 17.4s | 783ms |
| django | Python | 6,907 | 42,917 | 37.9s | 2,125ms |
| ruff | Rust | 9,702 | 42,239 | 67.2s | 2,300ms |
| go | Go | 14,022 | 105,497 | 186.8s | 4,661ms |
See BENCHMARKS.md for full results across all 21 tools.
MIT