cs ranks code search results without building an index
codespelunker - CLI code search tool that understands code structure and ranks results by relevance. No indexing required with CLI, TUI, MCP and HTTP support.
At a glance
- What is it?
- cs is a Go command line search tool that parses each file as it goes, tells code from comments and strings, and ranks matches with BM25 plus a cyclomatic complexity signal, with a TUI, an HTTP mode and an MCP server. It is aimed at the case where grep returns two hundred config lines before the implementation you wanted, and it deliberately keeps a text matcher available for the cases that need one.
- Who is it for?
- cs is the right tool when a query has a right answer and a text matcher cannot tell you which file it is in, and it is the wrong tool when you need exact line output, a fixed pattern, or the throughput of a pure line matcher over a large tree. It should sit beside grep rather than replace it, and the honest caveat is that its structural intelligence only covers the languages it lists, with everything else falling back to text relevance.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 4 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem is 200 hits before the one file you wanted
The pitch is stated in a single scenario. You searched for authenticate and got 200 results from configuration files, comments and test stubs before you found the actual implementation. Text search has no way to know the difference, because a byte is a byte. cs addresses that by parsing every file on the fly, working out what is a comment, what is a string and what is code, and then ranking by relevance instead of listing matches in the order they were found. The claim is that it combines the speed of a command line tool with the relevance ranking usually reserved for heavy indexed search engines like Sourcegraph or Zoekt, but without needing to maintain an index. The project also states that it powers searchcode.com, which is worth knowing because it means the tool is exercised on large real repositories rather than only on toy examples.
Code scores 1.0 and comments score 0.2, and you can filter instead
The structural awareness is expressed as a ranking weight, and the weight is configurable. A match in code ranks higher than the same word in a comment, with the documentation giving the values 1.0 against 0.2. You can also drop ranking subtleties entirely and filter, which is the mode that is most useful in scripts:
cs "database" --only-code
cs "FIXME" --only-comments
cs "error" --only-stringsThose three are mutually exclusive with each other, and there are two more for structure rather than content:
cs "handleRequest" --only-declarations
cs "handleRequest" --only-usagesDeclarations cover func, class and def style lines, so the first of those jumps to where something is defined and the second lists every call site without the definition. The declaration boost is language aware for Go, Python, JavaScript, TypeScript, TSX, Rust, Java, C, C++, C#, Ruby, PHP, Kotlin, Swift, Shell, Lua, Scala, Elixir, Haskell, Perl, Zig, Dart, Julia, Clojure, Erlang, Groovy, OCaml, MATLAB, Powershell, Nim, Crystal and V. For languages outside that list, all matches are treated as usages and ranked on text relevance only, while the comment, code and string filters still work for any language that the scc project recognises.
Complexity gravity puts the implementation above the interface
The second ranking signal is cyclomatic complexity, and it is the one that changes what you find. The idea is that when you search for Authenticate, the file with the complex implementation should rank above the file that defines the interface, because the interface is a few lines of signature and the implementation is where the behaviour lives. The switch is called gravity, and it has two named ends:
cs "login" --gravity=brain
cs "login" --gravity=lowbrain boosts complex files, low boosts simple ones, which is the opposite direction and useful when you are looking for a configuration or an interface definition rather than the machinery. This is a ranking heuristic over cyclomatic complexity, not a semantic analysis, so it is a proxy: a large file with high complexity may be a generated file, and a simple file may still be the one you wanted. What it reliably does is push the answer you usually want to the top of a query whose results are dominated by declarations.
Three ranking profiles that override the individual knobs
Individual switches multiply quickly, so the project offers presets that tune several parameters at once:
cs "authenticate" --profile=precise
cs "authenticate" --profile=broad
cs "authenticate"The table in the documentation is explicit about what each one does. balanced is the default, with standard BM25 tuning, a moderate complexity boost and test files dampened. precise is for finding the one file that matters, saturating quickly on term frequency, penalising long files, and aggressively filtering noise and test files. broad is for showing everything relevant, rewarding repeated matches, boosting long files slightly and including test files at full weight. Setting a profile overrides --gravity, --noise and --test-penalty, so the two mechanisms do not compose, they replace each other. The full ranking stack underneath is BM25 relevance scoring, file-location boosting, a noise penalty for data blobs and automatic test-file dampening, all computed on the fly with no pre-built index.
Console, TUI, HTTP server and an MCP server over the same engine
One engine, four front ends, and the choice is a flag:
cs
cs -dRunning with no arguments enters the TUI, and the -d flag enters HTTP mode on port 8080 by default. The console output is the ranked list with the best snippet extracted, which is the difference from a line matcher that prints lines. The HTTP server serves results with syntax highlighting, and the project describes that mode as being for interactive exploration, alongside the TUI. A fourth front end is an MCP server for LLM tooling, so an agent can call the same ranked search rather than shelling out to a text matcher and parsing its output. The MCP client library in go.mod is mcp-go, and the TUI is built on bubbletea, bubbles and lipgloss, which is the usual stack for a terminal application of this kind.
git-sync keeps long-lived search targets current
The long-lived modes, meaning the TUI, the HTTP server and the MCP server, can keep their targets up to date on their own. The --git-sync flag periodically runs git pull on repositories found in the search directory:
cs -d --git-sync --dir /path/to/repo
cs -d --git-sync --dir /path/to/projects
cs -d --git-sync --git-sync-interval 2m --git-sync-workers 4 --dir /path/to/projects
cs --mcp --git-sync --dir /path/to/codebaseThe interval and the number of concurrent workers are both configurable, which is what you need when the search directory holds many repositories. Discovery is based on the filesystem: if the directory itself contains a .git entry, that single repository is synced, and otherwise the repositories under the directory are. There is a related mechanism in the tree, a cache.go file with its own test, and a simplecache dependency, which suggests results are cached between runs. The caveat is the obvious one. A background git pull on your working repositories changes their state without you asking, and it will conflict with uncommitted work rather than resolve it.
Why cs does not replace grep, and where it is the wrong tool
The project is explicit that the two tools solve different problems, that ripgrep is the best at what it does, and that you will probably want both. That is the correct framing for the failure modes. cs is wrong when you want exact line output for a specific pattern, when you want to pipe results into another tool that expects lines, or when you are scanning a very large tree and only need the first few matches, since parsing every file costs more than matching bytes. The structural intelligence is also partial by design. It knows declarations for the listed languages, and for everything else it falls back to text relevance, so on an unfamiliar language you get ranking without understanding. On maintenance, the project is active: v3.0.0 was published on 2026-02-22, v3.1.0 on 2026-03-04, v3.2.0 on 2026-09-11, and the last push was on 2026-10-19. The licence is MIT, and the module path carries a v3 major suffix, so a dependency on it is a commitment to that major version. The go.mod requires Go 1.26.4, which is a real constraint for build environments that have not moved that far.
Editorial conclusion
cs is the right tool when a query has a right answer and a text matcher cannot tell you which file it is in, and it is the wrong tool when you need exact line output, a fixed pattern, or the throughput of a pure line matcher over a large tree. It should sit beside grep rather than replace it, and the honest caveat is that its structural intelligence only covers the languages it lists, with everything else falling back to text relevance. Before adopting it, compare a real query from your own codebase against grep output, decide whether the TUI, the HTTP mode or the MCP server is the interface your tools should call, and pin a version, since the module path carries a v3 major suffix and the project ships frequent releases.
Frequently asked questions
How does cs rank a match in code differently from a match in a comment?
A match in code ranks higher than the same word in a comment, with the documentation giving 1.0 for code against 0.2 for comments, and the weight is configurable. You can also filter strictly with --only-code, --only-comments and --only-strings.
Does cs need an index like Sourcegraph or Zoekt?
No. The documentation states that the ranking is computed on the fly with no pre-built index, and that cs finds files, ranks by relevance, extracts the best snippet and shows the most relevant results.
Which languages does cs understand structurally?
Declaration detection currently works for Go, Python, JavaScript, TypeScript, TSX, Rust, Java, C, C++, C#, Ruby, PHP, Kotlin, Swift, Shell, Lua, Scala, Elixir, Haskell, Perl, Zig, Dart, Julia, Clojure, Erlang, Groovy, OCaml, MATLAB, Powershell, Nim, Crystal and V. For other languages all matches are treated as usages, while comment, code and string filtering still applies to any language recognised by scc.
How do I run cs as an MCP server for an LLM tool?
Start it with the --mcp flag, for example cs --mcp --git-sync --dir /path/to/codebase. The HTTP mode is started with cs -d and listens on port 8080 by default, and running cs with no arguments enters the TUI.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/boyter-cs)Was this useful?