I wanted something faster than gradle build for my coding agent, so I made a Java checker in Rust A developer built grounds, an experimental static analyzer for Java written in Rust that runs as a Claude Code hook to catch errors after each agent edit in roughly 0.1 to 0.3 seconds, without needing Gradle, Maven or a JVM. The tool, at version 0.0.6 and tested on 21 open-source projects, parses files with tree-sitter and reports issues such as broken call sites after method renames, bad @Override and @Qualifier annotations, and constructor cycles, deliberately staying silent on unknown types to avoid false positives. Full-project runs ranged from 0.24 seconds on spring-petclinic to 10.2 seconds on camunda's 15,743 files, and the roughly 30k lines of Rust were mostly written by Claude over about two days. I use Claude Code on Java projects a lot. After almost every change, the agent wants to run ./gradlew build to see whether it broke anything. On the projects I work on that takes 2 to 5 minutes, and most of the time it finds nothing. If you tell the agent to skip the build, mistakes pile up until the test run at the very end, and then it has to untangle five of them at once. I wanted something in between: a check that runs after every edit, takes well under a second, and catches the boring stuff. A method that no longer exists, a bean Spring won't find, a null that's obviously going to blow up. So I built grounds https://github.com/mihaidaniel34/grounds . It's a static analyzer for Java written in Rust. It doesn't need Gradle, Maven or a JVM to run. Heads up: it's experimental. It's on version 0.0.6. It has been tested on 21 open-source projects and that's it. If your code looks different from those, expect false positives and setups it doesn't understand yet. It runs as a Claude Code hook. After the agent edits a file, grounds checks the lines that changed, usually in 0.1 to 0.3 seconds, and sends anything new back to the agent. When the agent tries to finish its turn, it runs the full set of rules on everything that changed and blocks the turn once if it found something. What it looks for: @Override that overrides nothing. If you rename a method, it lists every caller in other files that you haven't updated yet. For agents this has been the most useful part. @Qualifier with a typo, constructor cycles, Lombok quietly dropping your Optional.get without a check, dead stores. Here's what the agent sees after renaming a method: grounds: API changes in src/main/java/.../Owner.java broke 15 call site s in 6 other file s not yet updated: src/main/java/.../PetController.java:103:9: Owner has no method addPet ... You can use it without an agent too. grounds check runs on the whole project, and grounds changed only on what you changed since HEAD. It parses everything with tree-sitter and turns each file into a small stub: signatures, annotations, constants. Stubs are cached by file hash. Method bodies only get analyzed for the files you're actually checking. A small daemon keeps the index warm between edits. For dependencies it reads jars straight from your ~/.gradle and ~/.m2 caches, so it works best when the project has been built on that machine at least once. For the JDK it reads ct.sym . If you want an exact classpath, grounds classpath runs Gradle or Maven once to dump it. The rule I kept coming back to: if it doesn't know a type, it says nothing. A missing jar means fewer findings, not wrong ones. I'd rather it miss something than have the agent waste a turn on a false alarm. Some full-project runs on an M4 Pro: | project | files | time | |---|---|---| | spring-petclinic | 50 | 0.24 s | | micronaut-core | 6,371 | 3.4 s | | thingsboard | 6,884 | 4.9 s | | camunda | 15,743 | 10.2 s | A single-file check through the daemon is about 0.1 s. The biggest file I tried, a 6,800-line one, took 0.32 s. This is where most of the time went. Writing rules is quick. Getting them to shut up on correct code takes much longer. It did find real bugs in those projects too. An int overflow in 1000 60 60 24 365 10 . Two methods that call themselves unconditionally. A private @ParameterizedTest that never runs. About twenty AssertJ assertThat ... calls that don't actually check anything. Most of the code around 30k lines of Rust was written by Claude through Claude Code, over about two days. My job was mostly deciding what it should do, running it on real projects, and saying no a lot. I also had a second model OpenAI's review it and write Java test cases to try to break the rules. That found a good number of bugs I wouldn't have caught myself. DataSource or ObjectMapper aren't modeled, so it stays quiet about those. If you try it and it reports something wrong, a short code snippet that reproduces it is the most useful thing you can send me. Issues are open on GitHub https://github.com/mihaidaniel34/grounds .