semble Documentation

Fast and Accurate Code Search for Agents. Uses 99% fewer tokens than grep+read

RAW Doc

Technical Documentation: MinishLab/semble

โ„น๏ธ Provenance: Multi-Tier Ingestion ยท CodeWiki Reference

Overview (`README.md`)

Fast and Accurate Code Search for Agents
Uses ~99% fewer tokens than grep+read

Semble is a code search library built for agents. It returns the exact code snippets they need instantly, using ~99% fewer tokens than grep+read. Indexing and searching a full codebase end-to-end takes under a second for most repos, matching the retrieval quality of a code-specialized transformer while indexing ~480x faster and querying ~23x faster (see benchmarks). Everything runs on CPU with no API keys, GPU, or external services. Use it as an MCP server, a CLI tool via AGENTS.md, or a dedicated sub-agent, and any coding agent (Claude Code, Cursor, Codex, OpenCode, etc.) gets instant access to any repo.

Quickstart

Your agent queries Semble in natural language (e.g. "How is authentication handled?") and gets back only the relevant code snippets, without grepping or reading full files.

The fastest way to get started is the interactive installer. Install uv, then run:

bash
uv tool install semble
semble install

semble install detects installed coding agents such as Claude Code, Codex, and OpenCode, and then lets you choose which integrations to enable:

  • MCP server: lets the agent call Semble directly as a tool.
  • Instructions: adds CLI usage guidance to AGENTS.md / CLAUDE.md.
  • Sub-agent: installs a dedicated semble-search sub-agent.

To undo the setup, run semble uninstall.

For manual setup instructions (MCP config per agent, AGENTS.md snippet, sub-agent files), see the installation docs.

Updating Semble

bash
uv tool upgrade semble   # upgrade
uv cache clean semble    # for MCP users (restart your MCP client after)

Unattended install

For sandboxed or scripted environments, skip the prompts with --agent and, optionally, --type:

bash
semble install --agent claude --type mcp subagent --yes

--agent accepts one or more agent ids (e.g. claude, codex, pi); --type accepts mcp, instructions, subagent, or all (default: all); --yes skips the confirmation prompt (requires --agent for a fully non-interactive run).

Main Features

  • Fast: indexes an average repo in ~500 ms and answers queries in ~1 ms, all on CPU.
  • Accurate: NDCG@10 of 0.855 on our benchmarks, on par with code-specialized transformer models, at a fraction of the size and cost.
  • Token-efficient: returns only the relevant chunks, using ~99% fewer tokens than grep+read.
  • Zero setup: runs on CPU with no API keys, GPU, or external services required.
  • MCP server: works with Claude Code, Cursor, Codex, OpenCode, VS Code, and any other MCP-compatible agent.
  • Local and remote: pass a local path or a git URL, or several of them to search related repos together.

CLI

Semble also ships as a standalone CLI. This is useful in scripts or anywhere you want search results without an MCP session. Indexes are built and cached on first run, and invalidated automatically when files change.

bash
# Search a local repo (index is built and cached automatically)
semble search "authentication flow" ./my-project

# Search a remote repo (cloned on demand)
semble search "save model to disk" https://github.com/MinishLab/model2vec

# Search several repos at once (results are prefixed with the repo name)
semble search "invoice endpoint" ./service-a ./service-b

# Limit results
semble search "save model to disk" ./my-project --top-k 10

# Search docs/config/everything instead of just code
semble search "deployment guide" ./my-project --content docs   # or: config, all

# Find code similar to a known location
semble find-related src/auth.py 42 ./my-project

# Show only the first N lines of each result's snippet (0 = path/line range only)
semble search "authentication flow" ./my-project --max-snippet-lines 10

--content accepts code (default), docs, config, or all. --format accepts json (default) or text. path defaults to the current directory when omitted; git URLs are accepted. If semble is not on $PATH, use uvx --from "semble[mcp]" semble in its place. semble --version (or -V) prints the installed version.

Passing several paths or URLs searches them as one corpus, so a query from repo A can find an endpoint defined in repo B. Each index is cached per repo and merged at query time. Result paths are prefixed with the repo name (service-b/api/invoices.py) and the output includes a repos map from prefix to absolute path or URL. Pass the prefixed path to find-related to search across all repos from a known location.

Controlling which files are indexed

Semble reads .gitignore and .sembleignore files to determine which files to index. Both files use standard gitignore syntax and their patterns are merged. .sembleignore lets you add semble-specific rules without touching .gitignore. Rules are applied recursively, so a .sembleignore in a subdirectory applies to that subtree.

Excluding files: add patterns the same way you would in .gitignore:

text
# .sembleignore
generated/     # exclude generated dir
*.pb.go.       # exclude Go protobuf files

Including non-default extensions: prefix the extension pattern with ! to force-include files that semble wouldn't index by default:

text
# .sembleignore
!*.proto       # include Protobuf files
!*.cob         # include COBOL files

Run semble file-types to see which extensions are indexed per content type. Extensions not listed (including data files such as .json and .csv) are not indexed by default and can be force-included with a ! pattern as above.

Semble also always skips a set of well-known non-source directories regardless of ignore files (e.g. node_modules/, .venv/, dist/, build/, __pycache__/, and similar).

Savings

semble savings shows how many tokens semble has saved across all your searches:

bash
semble savings
text
Semble Token Savings
  โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

  Total saved:  ~714.2M tokens  (94%)
  Total calls:  14.3k
  Efficiency:  โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘  94%

  By Period
  โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
  Period             Calls           Saved  Ratio
  โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
  Today                198    ~1.4M tokens  โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘  95%
  Last 7 days        13.1k  ~707.2M tokens  โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘  94%
  All time           14.3k  ~714.2M tokens  โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘  94%

  By Call Type
  โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
  #     Call type            Calls  Share
  โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
  1.    search               14.1k  โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ    99%
  2.    find_related           205  โ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘     1%
  โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

Savings are calculated as follows: for each call, semble records the total character count of the unique files containing returned chunks and the character count of the snippets returned. Estimated tokens saved is (file chars โˆ’ snippet chars) / 4 (4 chars per token). This is a conservative estimate: the baseline is reading matched files in full, which is how coding agents often explore unfamiliar code.

Storage

By default, your Semble savings statistics and any saved indexes are stored in the OS cache folder (~/Library/Caches/semble/ on macOS, ~/.cache/semble/ on Linux, %LOCALAPPDATA%\semble\Cache\ on Windows). To override this location you can supply an environment variable SEMBLE_CACHE_LOCATION which should be the full path to the target cache location e.g. ~/my-folder/my-caches/semble.

Files larger than 1 MB are skipped during indexing to keep index builds lean. Skipped files are reported as a warning at index time. If you work with large generated or ingested documents, you can raise (or lower) this limit with the SEMBLE_MAX_FILE_BYTES environment variable (in bytes).

On first use, Semble also downloads the embedding model from Hugging Face and caches it in the standard Hugging Face cache (~/.cache/huggingface/ by default, or $HF_HOME if set); this only happens once and requires network access.

Use semble clear to remove cached data: semble clear index (saved indexes), semble clear savings (usage stats), semble clear orphans (indexes for repos no longer present on disk), or semble clear all (everything).

Library usage

Semble can also be used as a Python library for programmatic access, useful when building custom tooling or integrating search directly into your own code.

python
from semble import ContentType, SembleIndex

# Index a local directory (code only, the default)
index = SembleIndex.from_path("./my-project")

# Index docs and prose (markdown, rst, etc.)
index = SembleIndex.from_path("./my-project", content=ContentType.DOCS)

# Index everything (code, docs, and config)
index = SembleIndex.from_path("./my-project", content=[ContentType.CODE, ContentType.DOCS, ContentType.CONFIG])

# Index code and docs together
index = SembleIndex.from_path("./my-project", content=[ContentType.CODE, ContentType.DOCS])

# Index a remote git repository
index = SembleIndex.from_git("https://github.com/MinishLab/model2vec")

# Merge indexes from several repos into one (chunk paths are prefixed with the repo name)
index = SembleIndex.merge([("./service-a", SembleIndex.from_path("./service-a")), ("./service-b", SembleIndex.from_path("./service-b"))])

# Search the index with a natural-language or code query
results = index.search("save model to disk", top_k=3)

# Find code similar to a specific result
related = index.find_related(results[0], top_k=3)

# Each result exposes the matched chunk
result = results[0]
result.chunk.file_path   # "model2vec/model.py"
result.chunk.start_line  # 127
result.chunk.end_line    # 150
result.chunk.content     # "def save_pretrained(self, path: PathLike, ..."

When many files need indexing, the CLI and MCP server chunk them in 4 worker processes (SEMBLE_INDEX_WORKERS sets the number, 0 turns them off). In the Python API they are off by default. To turn them on, set SEMBLE_INDEX_WORKERS and call Semble from inside an if __name__ == "__main__": block, since each worker re-imports your script.

MCP Server

Semble runs as an MCP server so agents can search any codebase directly as a native tool call. Repos are indexed on demand and cached; local paths are re-indexed automatically on file changes.

Tool Description
search Search a codebase with a natural-language or code query. Pass repo as a local path or an https:// git URL (or a list of them to search several repos together) and content as code, docs, config, or all (default: code).
find_related Given a file path and line number, return chunks semantically similar to the code at that location.

For per-agent setup instructions, see the installation docs.

Benchmarks

We benchmark quality and speed across ~1,250 queries over 63 repositories in 19 languages (left), and token efficiency against grep+read at equivalent recall levels (right).

The quality benchmark (left) scores retrieval quality (NDCG@10) against total latency; semble matches the quality of the 137M-parameter CodeRankEmbed while indexing 480x faster. The token efficiency benchmark (right) measures how many tokens each method needs to reach a given recall level; semble uses 99% fewer tokens on average and hits 96% recall at only 2k tokens, while grep+read needs a full 100k context window to reach 85%. See benchmarks for per-language results, ablations, and full methodology.

How it works

Semble splits each file into code-aware chunks using tree-sitter, then scores every query against the chunks with two complementary retrievers: static Model2Vec embeddings using the code-specialized potion-code-16M-v2 model for semantic similarity, and BM25 for lexical matches on identifiers and API names. The two score lists are fused with Reciprocal Rank Fusion (RRF).

After fusing, results are reranked with a set of code-aware signals:

Ranking signals

  • Adaptive weighting. Symbol-like queries (Foo::bar, _private, getUserById) get more lexical weight, while natural-language queries stay balanced between semantic and lexical retrievers.
  • Definition boosts. A chunk that defines the queried symbol (a class, def, func, etc.) is ranked above chunks that merely reference it.
  • Identifier stems. Query tokens are stemmed and matched against identifier stems in a chunk, giving an additional weight to chunks that contain them. For example, querying parse config boosts chunks containing parseConfig, ConfigParser, or config_parser.
  • File coherence. When multiple chunks from the same file match the query, the file is boosted so the top result reflects broad file-level relevance rather than a single out-of-context chunk.
  • Noise penalties. Test files, compat//legacy/ shims, example code, and .d.ts declaration stubs are down-ranked so canonical implementations surface first.

Because the embedding model is static with no transformer forward pass at query time, all of this runs in milliseconds on CPU.

Indexes are cached to disk automatically on the first search. On subsequent runs, Semble walks the file tree and compares modification times; added, removed, or changed files are reindexed incrementally, without rebuilding the rest of the index. A full rebuild only happens if the indexing settings change (e.g., after a semble upgrade that changes the model, chunking, or cache format). In MCP mode, the index is checked and refreshed automatically as files change, so results stay current across the session.

Using a custom model

If you would like to use another model, you can set your SEMBLE_MODEL_NAME environment variable to a local path or Hugging Face repository. This path is read verbatim, and should contain a Model2Vec compatible model. This is particularly useful if you can't access Hugging Face at runtime.

Acknowledgements

Thanks to Greptile for providing free access to their AI code review platform.

License

MIT

Citing

If you use Semble in your research, please cite the following:

bibtex
@software{minishlab2026semble,
  author       = {{van Dongen}, Thomas and Stephan Tulkens},
  title        = {Semble: Fast and Accurate Code Search for Agents},
  year         = {2026},
  publisher    = {Zenodo},
  doi          = {10.5281/zenodo.19785932},
  url          = {https://github.com/MinishLab/semble},
  license      = {MIT}
}

Overview (`benchmarks/README.md`)

Benchmarks

Quality and speed benchmarks for semble.

Main results

Quality and speed across all methods.

Method NDCG@10 Index Query p50
semble 0.855 240 ms 0.69 ms
CodeRankEmbed 0.839 116 s 16 ms
ColGREP 0.693 5.4 s 122 ms
BM25 0.673 47 ms 0.17 ms
zvec-grep 0.670 3.4 s 391 ms
ck 0.642 96 s 187 ms
codebase-memory-mcp 0.630 454 ms 46 ms
grepai 0.561 35 s 48 ms
probe 0.387 โ€” 207 ms
cs 0.200 โ€” 22 ms
ripgrep 0.126 โ€” 14 ms
Time to first result (index + query) vs NDCG@10 Query latency on a warm index vs NDCG@10

semble matches the NDCG@10 of the 137M-param CodeRankEmbed while winning index time by ~480x and query latency by ~23x.

NDCG@10 is averaged across all queries. Speed numbers use one repo per language, CPU only: cold-start index time and warm query p50 (median across 5 consecutive runs).

Token efficiency

Coding agents (Claude Code, OpenCode, etc.) typically find code by running grep on keywords and reading the matched files. We model that workflow and compare it against semble's chunk retrieval across our full benchmark of 1251 queries.

Expected tokens per query

For each query: tokens consumed at first relevant hit, or 32k if the method never finds anything. Averaged across all 1251 queries.

Method Expected tokens Savings
ripgrep + read file 45,587 baseline
semble 353 99% fewer

Recall at fixed token budgets

A relevant file is "covered" once any retrieved unit comes from it.

Method 500 1k 2k 4k 8k 16k 32k
semble 0.843 0.921 0.963 0.989 0.995 0.996 0.996
ripgrep + read file 0.001 0.008 0.037 0.086 0.207 0.374 0.583

Methodology

semble returns the top-50 ranked chunks. ripgrep+read splits the query into keywords (dropping stopwords and short words), runs rg --fixed-strings --ignore-case for each keyword, then reads matched files in full ranked by how many distinct keywords they contain. Both methods search the same set of file types and ignored directories. Tokens are counted with cl100k_base via tiktoken. A relevant file is "covered" once any retrieved unit overlaps its annotated span.

By language

NDCG@10 per language, sorted by semble. CRE = CodeRankEmbed, cbm = codebase-memory-mcp. Best score per row is bolded.

Language semble CRE ColGREP zvec-grep ck cbm grepai probe cs ripgrep
scala 0.921 0.925 0.765 0.799 0.717 0.704 0.330 0.392 0.111 0.180
javascript 0.911 0.925 0.823 0.760 0.772 0.770 0.675 0.588 0.171 0.176
go 0.910 0.713 0.785 0.512 0.458 0.506 0.722 0.410 0.183 0.133
ruby 0.909 0.905 0.708 0.758 0.738 0.689 0.643 0.382 0.255 0.230
elixir 0.905 0.893 0.808 0.811 0.786 0.506 0.669 0.412 0.397 0.134
zig 0.899 0.911 0.474 0.664 0.511 0.766 0.755 0.369 0.121 0.000
cpp 0.881 0.897 0.626 0.677 0.687 0.630 0.731 0.375 0.262 0.126
python 0.876 0.878 0.777 0.704 0.721 0.643 0.634 0.488 0.305 0.202
csharp 0.872 0.848 0.614 0.609 0.548 0.775 0.277 0.392 0.248 0.117
php 0.864 0.847 0.663 0.583 0.615 0.608 0.402 0.340 0.180 0.123
swift 0.848 0.845 0.710 0.709 0.672 0.630 0.429 0.280 0.151 0.160
bash 0.840 0.834 0.706 0.725 0.677 0.768 0.723 0.226 0.170 0.000
lua 0.835 0.829 0.798 0.736 0.738 0.591 0.699 0.336 0.050 0.000
java 0.828 0.790 0.641 0.685 0.606 0.554 0.386 0.536 0.136 0.198
rust 0.826 0.754 0.662 0.541 0.419 0.454 0.519 0.242 0.193 0.162
kotlin 0.803 0.823 0.637 0.628 0.587 0.611 0.478 0.335 0.170 0.166
haskell 0.775 0.811 0.683 0.689 0.733 0.624 0.483 0.313 0.160 0.000
c 0.750 0.771 0.676 0.598 0.606 0.655 0.555 0.384 0.175 0.000
typescript 0.742 0.671 0.430 0.476 0.456 0.455 0.394 0.354 0.145 0.128
overall 0.855 0.839 0.693 0.670 0.642 0.630 0.561 0.387 0.200 0.126

Ablations

raw returns retrieval scores directly; + ranking feeds them through semble's hybrid ranker.

Retrieval Raw + ranking
BM25 0.686 0.838
potion-code-16M-v2 0.652 0.824
BM25 + potion-code-16M-v2 โ€” 0.855

By query category

Mode Architecture Semantic Symbol
BM25 raw 0.642 0.680 0.748
potion-code-16M raw 0.636 0.671 0.620
semble BM25 (+ ranking) 0.794 0.817 0.962
semble potion-code-16M (+ ranking) 0.768 0.819 0.917
semble hybrid 0.808 0.847 0.954

Dataset

~1,250 queries over 63 repositories in 19 languages, grouped into three categories:

Category Queries What it tests
semantic 711 Code that implements a specific behavior or concept
architecture 343 Design decisions, module boundaries, structural patterns
symbol 204 Named entity lookup (function, class, type, variable)

Notes

Languages: three repos per language (nine for Python): bash, C, C++, C#, Elixir, Go, Haskell, Java, JavaScript, Kotlin, Lua, PHP, Python, Ruby, Rust, Scala, Swift, TypeScript, Zig. Repos are pinned by revision in repos.json.

How the benchmark was built: queries and ground-truth relevance labels are generated by Claude Sonnet 4.6. The same model is used as LLM-as-judge to verify label quality.

Methods

  • ripgrep: fast regex search over files, included as a raw keyword-match baseline.
  • probe: BM25 keyword ranking backed by tree-sitter parse trees. We use its free probe search mode; its probe agent mode (which does query rewriting) needs a paid LLM API key and isn't tested here.
  • cs (Code Spelunker): structural BM25 ranker which ranks matches differently in code/comments/strings).
  • ColGREP: late-interaction code retrieval built on next-plaid with the LateOn-Code-edge model.
  • grepai: semantic search using nomic-embed-text (137M params) via a local Ollama daemon.
  • codebase-memory-mcp: code intelligence engine that indexes a repo into a SQLite/graph store. We benchmark its search_graph tool in fast mode, which does BM25 full-text search with structural boosting.
  • ck: hybrid regex + semantic search using BAAI/bge-small-en-v1.5.
  • zvec-grep: potion-code-16M-v2 static embeddings + BM25. Benchmarked in direct mode.
  • CodeRankEmbed: 137M-param transformer embedding model for code retrieval, used for semantic-only dense search.
  • semble: this library. potion-code-16M-v2 static embeddings + BM25 + the semble reranking stack.

Excluded methods

The following tools were considered but not included in the main comparison:

  • codanna: symbol-level semantic search with fastembed. Excluded because it does not support Haskell, Bash, Zig, Scala, Elixir, or Ruby (6 of the 19 benchmark languages).
  • claude-context: retrieval-augmented code search using OpenAI embeddings and a vector database. Excluded because it requires a paid OpenAI API key and a running vector-DB service.
  • GitNexus: knowledge-graph code search (BM25 + local embeddings + RRF). Excluded because it does not support Bash, Elixir, Haskell, Lua, Scala, or Zig (6 of the 19 benchmark languages).
  • codegraph: SQLite FTS5 symbol search + graph traversal. Excluded because it does not support Haskell, Bash, Elixir, or Zig (4 of the 19 benchmark languages).
  • tgrep: indexed regex search. Excluded
    because its results are nearly identical to ripgrep (0.1255 vs 0.1259 NDCG@10), and
    ripgrep is already fast enough that indexing adds nothing in our benchmarks.

Running the benchmarks

Repos are pinned in repos.json and cloned into ~/.cache/semble-bench:

bash
uv run python -m benchmarks.sync_repos          # clone / update
uv run python -m benchmarks.sync_repos --check  # verify only

All tools run CPU-only. semble uses minishlab/potion-code-16M-v2; CodeRankEmbed uses nomic-ai/CodeRankEmbed (137M params). The speed benchmark touches one repo per language with a cold-start index and 5 query runs per repo.

semble

bash
uv run python -m benchmarks.run_benchmark
uv run python -m benchmarks.run_benchmark --repo fastapi --repo axios
uv run python -m benchmarks.run_benchmark --language python

Full runs write to benchmarks/results/semble-hybrid-<sha12>.json.

Speed benchmark

bash
uv run python -m benchmarks.speed_benchmark
uv run python -m benchmarks.speed_benchmark --tools semble zvec-grep   # subset of tools
uv run python -m benchmarks.speed_benchmark --repo flask --repo axios  # subset of repos

Writes to benchmarks/results/speed-<sha12>.json.

Ablations

bash
uv run python -m benchmarks.baselines.ablations
uv run python -m benchmarks.baselines.ablations --mode bm25
uv run python -m benchmarks.baselines.ablations --mode semble-semantic

probe

Needs probe on $PATH (npm install @buger/probe).

bash
uv run python -m benchmarks.baselines.probe
uv run python -m benchmarks.baselines.probe --repo fastapi --repo axios

grepai

Needs grepai on $PATH and Ollama running with nomic-embed-text pulled:

bash
ollama pull nomic-embed-text
bash
uv run python -m benchmarks.baselines.grepai
uv run python -m benchmarks.baselines.grepai --repo fastapi --repo axios

Large repos take several minutes to index. Use --timeout <seconds> (default 120) for repos with many files:

bash
uv run python -m benchmarks.baselines.grepai --timeout 1800 --output results.json

The --output flag enables resume mode: already-completed repos are skipped on restart.

cs (Code Spelunker)

Needs cs on $PATH (go install github.com/boyter/cs/v3@latest).

bash
uv run python -m benchmarks.baselines.cs
uv run python -m benchmarks.baselines.cs --repo fastapi --repo axios

codebase-memory-mcp

Needs codebase-memory-mcp on $PATH (uv tool install codebase-memory-mcp, or pip/npm/brew). First run downloads a small platform binary.

bash
uv run python -m benchmarks.baselines.codebase_memory
uv run python -m benchmarks.baselines.codebase_memory --repo fastapi --repo axios

Each repo is indexed under a semble-bench-<repo> project name and deleted again after evaluation, so it doesn't collide with any projects you have indexed for real use.

ck

Needs ck on $PATH (cargo install ck-search or npm install @beaconbay/ck-search). Requires outbound HTTPS to Hugging Face to download its BAAI/bge-small-en-v1.5 embedding model on first run.

bash
uv run python -m benchmarks.baselines.ck
uv run python -m benchmarks.baselines.ck --repo fastapi --repo axios

zvec-grep

Needs zg on $PATH (npm install @zvec/[email protected], requires Node.js 22+). Downloads the potion-code-16m-v2 embedding model on first index. The published numbers were measured with 0.2.1; the runner records the installed version in its results file, and pinning matters because it parses zg's human-readable output.

bash
uv run python -m benchmarks.baselines.zvecgrep
uv run python -m benchmarks.baselines.zvecgrep --repo fastapi --repo axios

ripgrep

Needs rg on $PATH (brew install ripgrep / apt install ripgrep).

bash
uv run python -m benchmarks.baselines.ripgrep
uv run python -m benchmarks.baselines.ripgrep --no-fixed-strings

ColGREP

Needs the colgrep binary on $PATH.

bash
uv run python -m benchmarks.baselines.colgrep
uv run python -m benchmarks.baselines.colgrep --repo fastapi --repo axios

Runs with --code-only everywhere except bash repos (bash-it, bats-core, nvm), which use --no-code-only because ColGREP's code filter excludes .sh/.bash files.

CodeRankEmbed

Requires the benchmark extra (uv sync --extra benchmark).

bash
uv run python -m benchmarks.baselines.coderankembed
uv run python -m benchmarks.baselines.coderankembed --repo fastapi --repo axios

Context-efficiency benchmark

Requires the benchmark extra (uv sync --extra benchmark) and rg on $PATH.

bash
# Recall vs. token-budget across all queries; plots automatically.
uv run python -m benchmarks.token_efficiency recall
uv run python -m benchmarks.token_efficiency recall --repo fastapi

# Regenerate the plot from a saved recall payload.
uv run python -m benchmarks.token_efficiency plot

Writes benchmarks/results/token-efficiency-<sha12>.json and assets/images/token_efficiency.png.

Plots

bash
uv run python -m benchmarks.plot

Writes speed_vs_ndcg_cold.png and speed_vs_ndcg_warm.png to assets/images/.


Installation (`docs/installation.md`)

Installation

The interactive installer detects your installed agents and configures any combination of three integrations globally:

  • MCP server: exposes Semble as a native tool your agent can call directly.
  • AGENTS.md: adds a Semble usage guide to the agent's config file (CLAUDE.md, AGENTS.md, etc.).
  • Sub-agent: installs a dedicated semble-search sub-agent for harnesses that support it.

Install the CLI with uv, then run:

bash
uv tool install semble
semble install

To undo:

bash
semble uninstall

Supported agents: Claude Code, Cursor, Gemini CLI, Kiro, OpenCode, GitHub Copilot, Codex, VS Code, Windsurf, Zed, Reasonix, Pi, Command Code, Antigravity, ZCode, Grok Build, Qwen Code, Cline, and Kilo Code.

Pi prerequisite: Pi requires the MCP extension to be installed before semble can connect. Run pi install npm:pi-mcp-extension once, then semble install.

Unattended install

For sandboxed or scripted environments, pass --agent to skip the interactive prompts:

bash
semble install --agent claude pi --type mcp subagent --yes
  • --agent โ€” one or more agent ids (see the list above; use the lowercase form, e.g. claude, codex, pi).
  • --type โ€” one or more of mcp, instructions, subagent, or all (default: all). Requires --agent.
  • -y/--yes โ€” skip the confirmation prompt. Requires --agent for a fully non-interactive run.

semble uninstall accepts the same flags.

Keeping installed configuration up to date

The MCP server config, AGENTS.md/CLAUDE.md instructions, and sub-agent files that semble install writes all pin uvx to the exact semble version you have installed (semble[mcp]==X.Y.Z), so agents keep calling the version the instructions were written for rather than whatever is newest on PyPI. After upgrading (uv tool upgrade semble or pip install --upgrade semble), rerun semble install. This is idempotent and rewrites the pin (and anything else that changed) in place for every agent you select.


Manual setup

MCP server

Requires uv to be installed.

Claude Code

bash
claude mcp add semble -s user -- uvx --from "semble[mcp]" semble

Cursor

Add to ~/.cursor/mcp.json (or .cursor/mcp.json in your project):

json
{
  "mcpServers": {
    "semble": {
      "command": "uvx",
      "args": ["--from", "semble[mcp]", "semble"]
    }
  }
}

Codex

Add to ~/.codex/config.toml:

toml
[mcp_servers.semble]
command = "uvx"
args = ["--from", "semble[mcp]", "semble"]

OpenCode

Add to ~/.config/opencode/opencode.jsonc:

json
{
  "mcp": {
    "semble": {
      "type": "local",
      "command": ["uvx", "--from", "semble[mcp]", "semble"]
    }
  }
}

VS Code

Add to .vscode/mcp.json in your project (or your user profile's mcp.json):

json
{
  "servers": {
    "semble": {
      "command": "uvx",
      "args": ["--from", "semble[mcp]", "semble"]
    }
  }
}

GitHub Copilot CLI

Add to ~/.copilot/mcp-config.json:

json
{
  "mcpServers": {
    "semble": {
      "command": "uvx",
      "args": ["--from", "semble[mcp]", "semble"]
    }
  }
}

Windsurf

Add to ~/.codeium/windsurf/mcp_config.json:

json
{
  "mcpServers": {
    "semble": {
      "command": "uvx",
      "args": ["--from", "semble[mcp]", "semble"]
    }
  }
}

Gemini CLI

Add to ~/.gemini/settings.json:

json
{
  "mcpServers": {
    "semble": {
      "command": "uvx",
      "args": ["--from", "semble[mcp]", "semble"]
    }
  }
}

Kiro

Add to ~/.kiro/settings/mcp.json (or .kiro/settings/mcp.json in your project):

json
{
  "mcpServers": {
    "semble": {
      "command": "uvx",
      "args": ["--from", "semble[mcp]", "semble"]
    }
  }
}

Zed

Add to ~/.config/zed/settings.json (or .zed/settings.json in your project):

json
{
  "context_servers": {
    "semble": {
      "source": "custom",
      "command": "uvx",
      "args": ["--from", "semble[mcp]", "semble"]
    }
  }
}

Reasonix

Add to ~/.reasonix/config.json (the backwards-compatible MCP config path read by all Reasonix versions):

json
{
  "mcpServers": {
    "semble": {
      "command": "uvx",
      "args": ["--from", "semble[mcp]", "semble"]
    }
  }
}

Pi

First install the Pi MCP extension (one-time prerequisite):

bash
pi install npm:pi-mcp-extension

Then add to ~/.pi/agent/mcp.json:

json
{
 "mcpServers": {
 "semble": {
 "command": "uvx",
 "args": ["--from", "semble[mcp]", "semble"]
 }
 }
}

Antigravity

Add to ~/.gemini/config/mcp_config.json:

json
{
  "mcpServers": {
    "semble": {
      "command": "uvx",
      "args": ["--from", "semble[mcp]", "semble"]
    }
  }
}

Command Code

Add to ~/.commandcode/mcp.json:

json
{
 "mcpServers": {
 "semble": {
 "command": "uvx",
 "args": ["--from", "semble[mcp]", "semble"]
 }
 }
}

Or use the CLI:

bash
cmd mcp add --scope user semble -- uvx --from "semble[mcp]" semble

ZCode

Add to ~/.zcode/cli/config.json under the nested mcp.servers key (or use Settings -> MCP Servers -> Full configuration mode):

json
{
  "mcp": {
    "servers": {
      "semble": {
        "command": "uvx",
        "args": ["--from", "semble[mcp]", "semble"],
        "type": "stdio"
      }
    }
  }
}

Grok Build

Add to ~/.grok/config.toml:

toml
[mcp_servers.semble]
command = "uvx"
args = ["--from", "semble[mcp]", "semble"]

Qwen Code

Add to ~/.qwen/settings.json:

json
{
  "mcpServers": {
    "semble": {
      "command": "uvx",
      "args": ["--from", "semble[mcp]", "semble"]
    }
  }
}

Cline

Add to ~/.cline/data/settings/cline_mcp_settings.json (shared by the Cline IDE extensions and CLI):

json
{
  "mcpServers": {
    "semble": {
      "transport": {
        "type": "stdio",
        "command": "uvx",
        "args": ["--from", "semble[mcp]", "semble"]
      }
    }
  }
}

Or use the CLI:

bash
cline mcp add semble --yes -- uvx --from "semble[mcp]" semble

Kilo Code

Add to ~/.config/kilo/kilo.jsonc:

json
{
  "mcp": {
    "semble": {
      "type": "local",
      "command": ["uvx", "--from", "semble[mcp]", "semble"]
    }
  }
}

The MCP server indexes each requested content selection on first use and caches it separately. Searches default to code; append --content docs, --content config, or --content all to the server command to change that default. The content argument on an individual MCP search overrides it. For example, in Claude Code:

bash
claude mcp add semble -s user -- uvx --from "semble[mcp]" semble --content all

Indexes stay in memory for the lifetime of the server. To free memory while idle, set SEMBLE_MCP_IDLE_TIMEOUT to a number of seconds; indexes unused for that long are dropped from memory and reloaded from the disk cache on the next search.

Instructions (AGENTS.md / CLAUDE.md)

Add the snippet below to your AGENTS.md or CLAUDE.md so your agent knows when and how to call the semble CLI:

markdown
## Code Search

Use `semble search` to find code by describing what it does or naming a symbol/identifier, instead of grep:

โ€‹```bash
semble search "authentication flow" ./my-project --max-snippet-lines 10  # first 10 lines only, concise
semble search "save_pretrained" ./my-project                          # full chunk content
semble search "save model to disk" ./my-project --top-k 10           # more results
โ€‹```

The index is built on first run (and cached for subsequent runs) and invalidated automatically when files change.

Use `--content docs` to search documentation and prose, `--content config` for config files (yaml, toml, etc.), or `--content all` to search code, docs, and config:

โ€‹```bash
semble search "deployment guide" ./my-project --content docs
semble search "database host port" ./my-project --content config
semble search "authentication" ./my-project --content all
โ€‹```

Use `semble find-related` to discover code similar to a known location (pass `file_path` and `line` from a prior search result):

โ€‹```bash
semble find-related src/auth.py 42 ./my-project
โ€‹```

`path` defaults to the current directory when omitted; git URLs are accepted. Pass several paths or URLs to search related repos together, e.g. when this project calls a service defined in a sibling repo. Result paths are then prefixed with the repo name and the output includes a `repos` map from prefix to location:

```bash
semble search "invoice endpoint" ./service-a ../service-b

If semble is not on $PATH, use uvx --from "semble[mcp]" semble in its place.

Workflow

  1. Start with semble search to find relevant chunks. The index is built and cached automatically.
  2. Use --content docs for documentation, --content config for config files, or --content all for everything.
  3. Navigate directly to the returned file and line โ€” do not re-search or grep for the same content.
  4. Optionally use semble find-related with a promising result's file_path and line to discover related implementations.
  5. If the answer may live in a dependent or sibling repo, pass all relevant repo paths to one semble search call.
  6. Use grep only when you need every occurrence of a literal string across the whole repo (e.g., all callers of a renamed function).
text
### Sub-agent

For harnesses that support sub-agents (Claude Code, Cursor, Gemini CLI, Kiro, OpenCode, GitHub Copilot, Codex, Reasonix, Pi, Command Code, Antigravity, ZCode, Grok Build, Qwen Code, Kilo Code), you can install a dedicated `semble-search` sub-agent. Copy the appropriate file from [`src/semble/agents/`](../src/semble/agents/) to your agent's agents directory:

> **Pi prerequisite:** Pi sub-agents require the Pi agents extension. Run `pi install npm:pi-agents` once before installing.

| Agent | File | Destination |
|---|---|---|
| Claude Code | `claude.md` | `~/.claude/agents/semble-search.md` |
| Cursor | `cursor.md` | `~/.cursor/agents/semble-search.md` |
| Gemini CLI | `gemini.md` | `~/.gemini/agents/semble-search.md` |
| Kiro | `kiro.md` | `~/.kiro/agents/semble-search.md` |
| OpenCode | `opencode.md` | `~/.config/opencode/agents/semble-search.md` |
| GitHub Copilot | `copilot.md` | `~/.copilot/agents/semble-search.agent.md` |
| Codex | `codex.toml` | `~/.codex/agents/semble-search.toml` |
| Reasonix | `reasonix.md` | `~/.reasonix/skills/semble-search.md` |
| Pi | `pi.md` | `~/.pi/agents/semble-search.md` |
| Command Code | `commandcode.md` | `~/.commandcode/agents/semble-search.md` |
| Antigravity | `antigravity.md` | `~/.gemini/config/skills/semble-search/SKILL.md` |
| ZCode | `zcode.md` | `~/.zcode/agents/semble-search.md` |
| Grok Build | `grok.md` | `~/.grok/agents/semble-search.md` |
| Qwen Code | `qwen.md` | `~/.qwen/agents/semble-search.md` |
| Kilo Code | `kilo.md` | `~/.config/kilo/agents/semble-search.md` |

---

--- METRICS ---
- Files Extracted: 3
- Estimated Token Budget: ~11560 tokens
- Canonical Reference: https://codewiki.google/github.com/MinishLab/semble