jevgrep
★ 866dzhng/jevgrep
Finds code by what it does rather than what it matches: a CLI for coding agents that asks which files and which regions are relevant to a description.
Semantic grep with no embeddings, index, or daemon: it searches the live tree on every run and matches concepts rather than strings.
View on GitHub →Hands-on review
A cascade that narrows by filename, then a 384-byte sketch, then the real text. It learned what a credential file is the day we reported it.
Good for
Watch out for
Tested Sep 20, 2026 at a280f14 · re · Rust in Docker, offline against a local stand-in; the re-check used the project's own endpoint override
How we reviewed this: we built it in Docker and drove it offline against a local stand-in. At a280f14 its endpoint was two hardcoded constants with no environment override, so reviewing it at all required patching those two lines; we reverted and diffed afterwards. The re-check at 0.1.2 needed no patch — --endpoint local with JEGREP_ENDPOINT_URL is in the tool now. We made no Jev calls.
Three passes. A ripgrep-style keyword scan orders candidates locally. Filenames go out in batches to be scored on name, size and position in the tree. Selected files are cut into 8 KiB windows, a 384-byte sketch of each window is routed first, and only windows that survive have their full text sent back for confirmation. Line numbers come from the confirmed ones.
Spending the cheapest question first is the right instinct, and the local pass means an unrelated tree costs almost nothing.
There were three lists: 22 directories, 14 filenames, 61 binary extensions. We read all three. The filename list was lockfiles and .DS_Store. Not one entry in any of them was a credential file. .pem, .key, .p12, credentials.json, id_rsa and *.tfvars were ordinary text files to this tool, and their contents were read and sent.
.env survived, but only incidentally:
if !self.include_hidden && name.starts_with('.') {
The protection was “the name begins with a dot”, and there was a flag to turn it off.
This is the third tool this month with the same shape — JevLint and perch had versions of it — and it is worth stating as a rule: an exclusion list written against build noise is not a secret filter.
Fixed in 0.1.2, the same day we reported it. src/tree.rs has SECRET_FILES (20 names) and SECRET_EXT (19 extensions) now, checked before a file is eligible at all, and the directory-sampling path applies .gitignore as well, so ignored filenames stop leaking too.
We rebuilt it at e6d5b84 and re-ran our tree, a canary string planted in each file, watching the wire:
| file | at a280f14 |
at 0.1.2 | with --hidden |
|---|---|---|---|
server.pem |
contents sent | not sent | not sent |
config/credentials.json |
contents sent | not sent | not sent |
prod.tfvars |
contents sent | not sent | not sent |
.env |
not sent | not sent | not sent |
src/generated.rs (gitignored) |
name sent | not sent | not sent |
config/service-account-key.json |
contents sent | contents sent | contents sent |
That last row is the one to know about, and it is the example from our own report. The deny list is a list of names: it has service-account.json, credentials.json and id_rsa, but a Google service-account key downloads as whatever the console names it — service-account-key.json, my-project-4f3a1c.json — and .json cannot be denied as an extension. In our run the file was not only sent but returned as the top hit, with the private key on screen.
So the shape is much better and the common cases are closed. A key saved under an unusual name is still yours to keep out of the tree — we reported the gap with the same tree extended: a service-account key named after its project (my-project-4f3a1c.json), an aws_credentials.txt and a kubeconfig all went out whole, while service-account-key.pem did not. The suggestion there is a content check where the bytes are already read, since a name list can only describe files someone else named correctly.
--bytes, --ranges, --min-hits and -k appeared in --help and in the README’s option table for the default command. Under the default cascade strategy, none of them was read — they belonged to other strategies. Setting --bytes to limit how much of each file is sent had no effect at all, which was unfortunate given the section above.
Fixed in the same release. --help now says “Whole-file strategies only” on the three that are, and names the environment variables (JEGREP_CASCADE_BYTES, JEGREP_WINDOW_BYTES) that budget cascade; -k applies to cascade and is documented as doing so.
The cascade is a good design and the repository is better maintained than most here: real CI across five targets, published to crates.io with checksummed release binaries. Two days old, 15 commits, one author, and the version on crates.io now matches a tagged release — 0.1.2 in both places, which it didn’t before.
Everything we reported was fixed within hours, including the testability ask: a local endpoint you can point somewhere else. Point it at source trees rather than at homes or infrastructure repositories, and remember the deny list works by name — a service-account key saved under its own name goes out whole.
For the same idea with different trade-offs, see jgrep and semgrep.
See how it compares with other tools in Best Jev tools, tested hands-on.
Review updated Sep 21, 2026. Numbers quoted from the project are its author's own; we don't publish our own measurements of Jev.
dzhng/jevgrep
Finds code by what it does rather than what it matches: a CLI for coding agents that asks which files and which regions are relevant to a description.
Alex314618-create/JevRev
Puts an LLM and Jev in one workflow, with sift, loop and long modes, a TUI, and a CLI that can be pointed at any /v1/systemone host. English and Chinese.
uehaj/jev-semgrep
Grep by meaning: Jev scores each line against a description in any language, with AND, OR, and NOT. A single dependency-free Node file.
New Jev releases, pricing changes, and the best new projects, once a week. No spam; unsubscribe anytime.
Powered by Buttondown. See our privacy policy.