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.
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.
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.
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.
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.