Every "AI code search" tool released this year does the same thing under the hood: chunk your repo, embed every chunk, stuff the vectors into an index, and keep that index in sync every time you commit. GitLab Duo does it. Sourcegraph does it. JetBrains shipped a whole RAG pipeline for it. It works, but it means you're maintaining a second copy of your codebase that can drift out of sync with the first one, and on a repo that changes fast, "rebuild the index" becomes its own chore.
I wanted semantic search that didn't need that. So I built jevgrep, and it skips the index entirely.
The actual mechanism
jevgrep does two passes, and only one of them touches a model.
Pass one is plain old ripgrep, doing a fast keyword sweep over the repo to pull maybe 30 to 50 candidate lines. This part is dumb on purpose. It doesn't need to understand your query, it just needs to narrow a whole repo down to a short list fast, which is the one thing keyword search is genuinely good at.
Pass two hands that shortlist to Jev, TypeSafe's decision model, in a single batched call. Jev isn't a chat model, it's what they call a System One model: it doesn't generate text token by token, it reads probabilities over a fixed set of options in one forward pass. So instead of asking an LLM to write out a ranked list (slow, and it'll happily hallucinate a line number that doesn't exist), you hand it the query plus the candidates as typed choice options, and it hands back a probability distribution over which candidate actually answers the query. No decoding loop, no JSON the model might get wrong, just a ranking.
That's the whole trick. Keyword search does recall, Jev does relevance, and there's nothing to rebuild when you push a commit because there's no index in the first place. The tradeoff is honest: you're bounded by whatever ripgrep's keyword pass surfaces in the first place, and Jev's context window caps you around 50 candidates per call. This isn't a replacement for a real embedding index on a codebase where the right answer literally doesn't share a single word with the query. It's a much cheaper tool for the far more common case, where the right answer is a few keyword-adjacent hops away from what you typed.
Does it actually work
I ran it against Flask's real source, not a toy repo, because Flask's config.py has genuine config-parsing logic (from_pyfile, from_prefixed_env, from_envvar) that isn't reducible to a single obvious keyword.
Query: "where do we load environment variables"
flask/cli.py:701 27% Load "dotenv" files to set environment variables...
flask/config.py:129 12% Load any environment variables that start with FLASK_
Plain rg "load environment variables" returns 2 hits, both docstrings, missing the actual mechanism. rg "os.environ" returns 14 hits with no ranking, meaning you already need to know the exact symbol and then eyeball all 14 yourself.
Query: "where do we parse the config file"
flask/config.py:209 68% exec(compile(config_file.read(), filename, "exec")...)
flask/config.py:270 5% app.config.from_file("config.json", load=json.load)
flask/config.py:273 3% app.config.from_file("config.toml", load=tomllib.load...)
That top hit is the line that actually reads and executes a Python config file. The words "parse" and "config file" don't appear on it together anywhere. rg "parse config file" returns zero results, because those words never co-occur in Flask's source at all. That's the actual gap between text matching and understanding intent, on a real production codebase, not a cherry-picked demo repo.
Worth being straight about the failure mode too: run jevgrep on its own repo (which has no real config-file parser) and it correctly surfaces argument-parsing code instead, because that's the closest real match that exists. It doesn't invent a config parser that isn't there. For a tool whose whole pitch is trustworthy ranking, that matters more than a good demo would.
Cost and speed
Jev's list price is $0.042 per million input tokens with output free, and each jevgrep query is maybe 30 to 60 tokens of state. End to end it ran at 945ms in my test, which was a cold call on Windows without ripgrep on PATH, falling back to a pure Python scan for pass one. With ripgrep actually resolving, the shortlist pass drops to single-digit milliseconds and the Jev call itself is typically 70 to 500ms, so the realistic number is well under a second, consistently.
Try it
pip install git+https://github.com/zaydmulani09/jevgrep
jevgrep "where do we parse the config file"
You'll need a free OpenRouter API key to call Jev (new accounts get $1 in credit, which at Jev's pricing is on the order of tens of thousands of queries). Full setup is in the README.
Repo: github.com/zaydmulani09/jevgrep, MIT licensed.
If you're building something in this space, I'd genuinely like to hear what tradeoffs you hit, especially anywhere the shortlist-then-rank approach falls over on a codebase bigger than what I've tested against so far.
Top comments (2)
The main advantage here is avoiding write amplification. On active repos with frequent commits, maintaining an HNSW index or embedding store turns into a persistent background tax for chunks that might only be read once before being rewritten.
The structural limitation of using ripgrep as the candidate generator is vocabulary mismatch. If a developer queries for retry backoff but the codebase names the helper reconnect_jitter_policy, keyword matching yields zero candidates before the scoring model ever sees a single option.
Where this two-stage pipeline pays off is in interface and call-site lookup. When the query shares at least one token with the target, letting a single-pass scoring model rank 50 candidates side-steps all the synchronization debt of a dedicated vector database.
yeah, vocabulary mismatch is the actual ceiling on this right now, you're right that "retry backoff" against reconnect_jitter_policy returns nothing before jev ever gets a shot at it. keyword pass has zero fuzzy matching by design, that was a simplicity tradeoff not an oversight, but it's the honest weak point.
been thinking the fix isn't embeddings, it's cheaper than that: if the ripgrep pass returns under some threshold of candidates, fall back to a second pass matching on identifier tokens split by case/underscore instead of literal substrings, so "retry backoff" at least catches reconnect_jitter_policy on the "retry"/"reconnect" edit distance even without semantic understanding. still zero index, still free, just a slightly smarter pass 1.
your framing of where it pays off (call sites, interfaces, anything with token overlap) is exactly right and probably belongs in the readme, better to be upfront about the shape of the tool than let people assume it's doing full semantic recall.