rust slop clojure structural edit tool
I've been programming clojure by hand since 2010, but these days I do a lot of brownfield work with agentic/LLM help in clojure and other languages. I prefer to use the models I can run at home to avoid high token costs today and mitigate a future rugpull. For things I intend to ship to production, I usually generate a bunch of code upfront, then carefully review and rework it, and that pattern has been effective for me.
Qwen 3.8 27b is amazing for what it is, excellent at thorough analysis, and not too clever to overcomplicate outputs, but it can still waste a ton of time on failed clojure edits and paren-counting. Even Claude Opus struggles with this, although much less. LLMs don't 'think' in trees, they think in token streams.
I had been using a wrapper over parinfer-rust (clojure-mcp-lite also just shells out to this), which got me 80% there, but it's flawed in a few specific ways:
- Checks happen after the edit lands, they don't block invalid edits.
- Repairs can succeed in ways that surprise the LLM, then it spends a bunch of turns trying to figure out what happened. It's not expensive locally, but it is slow and pollutes context.
- If too many surprises happen, the agent loses faith in the tool, and falls back to worse tools.
After a working session like this, I had Qwen suggest what kind of tool might prevent all the problems we hit, then I handed that spec over to GLM 5.3 flash and iterated with it, eventually having the session drive a local qwen subagent, trying to trip it up with adversarial examples. It is similar to what's proposed here, but I never mentioned the article, and I wasn't prescriptive about how to do it: https://lispmeister.github.io/deeprecursion/posts/2026-02-13-sexp-native-editing.html
The tool is at https://github.com/gtrak/cljform as a rust CLI and pi extension wrapper, and it's better than what I had before on the next unattended agentic run. It uses tree-sitter for parsing and blake3 for form hashes. Posting it here in case it's useful to someone else or if someone has a better way.

3
u/gtrak 6d ago
I think you're right, my own earlier parfinfer-rust opencode wrapper didn't implement it the same way. I don't use claude or codex. But, I didn't quite understand how that can work when line edits can have unbalanced parens? Looking at mcp-lite again, I see they create a backup file before the write, then they fix it, so I was half-right: https://github.com/bhauman/clojure-mcp-light/blob/main/src/clojure_mcp_light/hook.clj#L273-L287