Skip to content

imcarlost/codeseekr

Repository files navigation

Codeseekr

Semantic code search that runs entirely on your Mac. Point it at one repo or many, ask in plain English ("where do we validate JWTs?"), and get back the exact code, with file paths, line numbers, and symbol names.

No cloud. No code ever leaves your laptop. The chunking, embeddings, vector store, and search all run locally; the only "server" is a tiny embedding daemon that manages itself.

codeseekr index ~/work/api
codeseekr search "where do we validate JWTs?"

Why it's nice

  • 🔒 Local-first. Everything runs on-device via Apple's MLX on Apple Silicon. No network at query time.
  • 🧠 Understands meaning. It finds the right code even when your words don't match the source, by combining semantic (vector) and keyword (BM25) search.
  • 📦 Zero setup. No npm install, no build, no native compiling. Install and go.
  • 🤖 Built for agents. It ships as a Claude Code plugin and speaks JSON for scripts.
  • Fast. A warm model daemon keeps searches sub-second.

Get started

You need an Apple Silicon Mac, Node.js 24, and Python 3. That's it.

As a Claude Code plugin

/plugin marketplace add bcilabs/codeseekr      # one time
/plugin install codeseekr@mach-marketplace

Then just ask the agent to find something. It indexes the project on first use and searches it for you.

As a CLI

git clone git@github.com:bcilabs/codeseekr.git
alias codeseekr='node "$PWD/codeseekr/bin/codeseekr.js"'

codeseekr setup                          # one-time: provision the model
codeseekr index ~/work/api               # index a repo (incremental on re-run)
codeseekr search "retry logic" -k 5 --repo api

Re-running index is always safe; only changed files are re-embedded.

How it works

1. Your code gets split into pieces. Codeseekr reads each file and cuts it along real code boundaries (a function, a class, a method) using tree-sitter. Each piece is called a chunk. This keeps related lines together instead of cutting through the middle of a function.

2. Each chunk gets turned into a list of numbers. A small AI model reads a chunk and outputs a vector: a long list of numbers that captures what the code means. Two chunks that do similar things end up with similar numbers, even if they don't share any words. This is what lets you search by meaning.

Text is fed through an embedding model that outputs a vector of numbers, which becomes a point in embedding space

The model maps each input to a fixed-length vector, and it learns to place related things along consistent directions in that space. The classic illustration uses words: the step from King to Queen points the same way as Man to Woman, and every country sits a parallel hop from its capital. Codeseekr applies the exact same idea to code, so a function that retries failed requests lands near one that "backs off and tries again," even with no shared words.

Word analogies as parallel vectors: King→Queen mirrors Man→Woman, and each country→capital pair points the same direction

3. Those vectors get placed in a "vector database." Think of every chunk as a dot in space, positioned by its numbers. Chunks that mean similar things land near each other, forming clusters. When you ask a question, your question becomes a dot too, and the search finds the nearest dots. The picture below shows what that space looks like once thousands of chunks are placed in it, similar things naturally group together:

A vector space where similar items cluster together

4. At the same time, a keyword index is built. Vectors are great at meaning but blurry on exact spellings, so during indexing Codeseekr also feeds every chunk into a BM25 keyword index (an FTS5 full-text index over the chunk's code and its symbol name). BM25 is the classic ranking used by search engines: it splits text into tokens, then scores a chunk for a query by how often the query's terms appear in it (term frequency), damped so that rare, distinctive terms like RETRY_MAX_ATTEMPTS count for more than common ones, and normalized so long chunks don't win just by being big. This index is built once at index time and updated incrementally as files change, right alongside the vectors, so both ways of searching are ready before you ever type a query.

5. Your question is searched two ways at once. Codeseekr runs your query through both kinds of search and merges the results:

Dense (vector) search Sparse (BM25 keyword) search
What it matches Meaning and intent Exact words and symbols
Good for questions like "where do we validate JWTs?" — finds verifyToken() even though "JWT" never appears "find RETRY_MAX_ATTEMPTS" — finds the exact constant by name
Strength Catches the right code when your words don't match the source Nails exact identifiers, error strings, rare names
Weakness Can miss an exact literal you typed verbatim Misses anything phrased differently from the code

6. The two result lists are fused into one. A step called RRF fusion (Reciprocal Rank Fusion) takes both ranked lists and combines them, so a chunk that scored well on either search rises to the top. That's what hybrid means: you get the meaning-matching of dense search and the exact-match precision of sparse search in a single ranked, de-duplicated result list. A question like "how is rate limiting configured?" benefits from both, dense search finds the throttling logic by concept, while sparse search locks onto the literal rateLimit config key.

Learn more

The full story lives in the knowledge base: architecture, configuration, the CLI reference, the agent contract, and contributor docs.

About

Semantic search across projects

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages