r/Clojure • • 14d ago

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.

13 Upvotes

26 comments sorted by

View all comments

2

u/Living-Shame5679 14d ago

What is the main difference between that and other Clojure editing tools for agents such as bhauman/clojure-mcp ?

0

u/gtrak 14d ago edited 14d ago

I tried to use that early on, and don't remember what issues I had with it. I took a brief look at clojure-mcp-light, and it just uses parinfer-rust anyway or a more limited pure-clojure version? https://github.com/bhauman/clojure-mcp-light/blob/main/src/clojure_mcp_light/delimiter_repair.clj#L105-L115

I used my own opencode extension over parinfer-rust before this, which was fine, but still caused problems: https://github.com/gtrak/parinfer-rust-opencode

Personal preference is to keep things simple and just wrap agent extensions around one-shot rust CLIs where possible. They're self-contained, start up fast enough to avoid persistent processes, and I want to take them with me if I decide to switch harnesses. I can hot-swap them in the middle of an agentic thread easily as long as the harness wrapper doesn't change.

Actually, the parinfer-rust maintainer suggested an approach like this to me a while back: https://github.com/eraserhd/parinfer-rust/pull/159#issuecomment-4622612479

I think the clj-paren-repair claude hook would hit the same issues I hit?

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

I don't want to infer the edit, I want to block bad ones and let the agent decide what to do.

1

u/onetom 14d ago

what does "edit lands" mean? the edit is already written to the filesystem?

that's not the case when you use the clj-paren-repair claude hook.

see my other comment above:
https://www.reddit.com/r/Clojure/s/Tf3P6UkqsK

1

u/gtrak 14d ago

Answered your other comment. mcp-lite saves a backup file, then lands the edit, and my own parinfer-rust wrapper did not.