Claude Code doesn't index your codebase
Claude Code finds code with grep and find, not an index. Where that works, where it misses, and what an index changes, measured on SWE-bench Verified.
Checked on 29 October 2026 against Claude Code’s docs and changelog up to version 2.1.312, with semlith 0.49.0.
What Claude Code does today
Claude Code keeps no index of your code. Anthropic’s own words are that it “doesn’t require a codebase index to be built, maintained, or uploaded to a server” (Claude blog, May 2026). When it needs to find something it runs a search, reads what comes back, and searches again.
On macOS, Linux and WSL it searches with grep and find through its shell. Since version 2.1.117 those are embedded copies of ugrep and bfs, and the separate Grep and Glob tools are off by default there (tools reference, changelog). On Windows it keeps its Grep tool, which is built on ripgrep. Either way it opens files with its Read tool.
This was a choice, not a gap. Boris Cherny, who created Claude Code, has said the early versions used RAG with a local vector database (X, February 2026) and that searching like an agent did better for the way people use it (Hacker News, February 2025). His reasons were that an index drifts out of date, has to be stored somewhere safe and adds moving parts. The cost he named was latency and tokens (Latent Space, May 2025).
Those are good reasons, and grep is very good at what it does. If you know the name of the thing you want, nothing beats it.
Where grep misses
grep matches words. The agent has to guess the word the code uses, and when the question is asked in other words the first search lands somewhere else.
Here is one question asked both ways on Flask 3.1.3, a public repository. The question is how to limit how big an uploaded request body can be.
git clone --depth 1 --branch 3.1.3 https://github.com/pallets/flask && cd flask
rg -n -i "upload|big" srcsrc/flask/helpers.py:542: @app.route("/uploads/<path:name>")
src/flask/helpers.py:545: app.config['UPLOAD_FOLDER'], name, as_attachment=True
src/flask/helpers.py:549: files or uploads. Uses :func:`~werkzeug.security.safe_join` to
src/flask/cli.py:297: a bigger role. Typically it's created automatically by theNone of these lines is the answer. The limit is max_content_length in src/flask/wrappers.py, and that file never says “upload” or “big”. An agent gets there in the end by trying more words and opening more files, and that is where the latency and the tokens go.
The same question against an index of the repository.
semlith index .
semlith search -k 1 "limit how big an uploaded request body can be"1. 0.087 vf src/flask/wrappers.py:59-71
@property
def max_content_length(self) -> int | None:
"""The maximum number of bytes that will be read during this request. If
this limit is exceeded, a 413 :exc:`~werkzeug.exceptions.RequestEntityTooLarge`
error is raised. If it is set to ``None``, no limit is enforced at the
Flask application level. However, if it is ``None`` and the request has
no ``Content-Length`` header and the WSGI server does not indicate that
it terminates the stream, then no data is read to avoid an infinite
stream.
Each request defaults to the :data:`MAX_CONTENT_LENGTH` config, which
defaults to ``None``. It can be set on a specific ``request`` to apply
the limit to that specific view. This should be set appropriately based
1 hits in 602.316209ms
question-shaped · vector and keyword equalSemlith ranks by meaning and by keywords together, which is what vf on the hit says (both lists found it). Once you have the name, grep is the right tool again.
rg -n max_content_length srcsrc/flask/wrappers.py:55: _max_content_length: int | None = None
src/flask/wrappers.py:60: def max_content_length(self) -> int | None:
src/flask/wrappers.py:80: if self._max_content_length is not None:
src/flask/wrappers.py:81: return self._max_content_length
src/flask/wrappers.py:84: return super().max_content_length
src/flask/wrappers.py:88: @max_content_length.setter
src/flask/wrappers.py:89: def max_content_length(self, value: int | None) -> None:
src/flask/wrappers.py:90: self._max_content_length = valueSo an index doesn’t replace grep. It finds the name, and grep or a read takes it from there.
What it changes, measured
We measured this on SWE-bench Verified, 500 real GitHub issues. The issue text is the question and the answer is every file the fix edits. It measures retrieval only, not whether an agent then fixes the bug.
The file the fix edits is in Semlith’s top 10 for 76.8% of the issues, against 47.0% for grep then read. Before it reaches the first right file, grep then read goes through a median of 15,491 tokens, and Semlith 224.
Finding the file is not the whole job, though. On 50 questions about a codebase it had never seen, headless Claude Code on Opus answered 92.0% correctly with grep alone, and 92.0% with Semlith installed beside it. On one repository a strong agent gets to the answer with grep. It just reads more on the way. When Claude Code was set to ask Semlith before it greps, it answered 96.0%.
Every figure here is on the benchmarks page, and the scorecard has the harness and the command behind each one, so you can run it yourself.
When an index is worth it
Grep alone is fine when the repository is small, when you know the names, or when the agent is fixing something it can already see. An index starts to pay when questions come in words the code doesn’t use, when there are many repositories to look through, and when the answer sits in a PDF, Word or Excel file that grep can’t read at all (Search code and documents together).
Anthropic’s guide for large codebases says the same. If you already run a code search or RAG index, it suggests exposing it to Claude Code as an MCP tool (large codebases).
Try it with Claude Code
cargo install semlith
semlith setup
semlith index .semlith setup registers Semlith in Claude Code and in the other agent clients it finds. Connect Claude Code shows what it writes, and how to add it by hand with claude mcp add. Claude Code keeps its own grep and reads, and gains semlith_search beside them.
All guides
Why coding agents grep instead of indexing, and how to search code, PDFs, Word and Excel files together over MCP, with commands you can run.
Code and documents
One local index over a repository and the PDF, Word and Excel files beside it, searched by Claude Code, Codex or Cursor over MCP, with commands you can run.