-
-
Notifications
You must be signed in to change notification settings - Fork 75
Open
Description
clojure_edit replace reformats entire file via cljfmt, not just the target form
Summary
When using clojure_edit with operation: "replace" to replace a single top-level form, the pipeline applies cljfmt to the entire file — not just the replaced form. This causes collateral whitespace changes in unrelated code throughout the file causing Noisy diffs and Stale REPL vars
Steps to reproduce
- Create a Clojure file with intentional alignment spacing:
(ns example.core)
(def ob-yes-heavy
{:orderbook {:yes [[60 200] [55 150]]
:no [[40 80] [35 60]]}})
(def ob-empty-no {:orderbook {:yes [[60 100]] :no []}})
(def ob-empty {:orderbook {:yes [] :no []}})
(defn foo [x] (+ x 1))
(defn bar [x] (+ x 2))- Use
clojure_editto replace only thebarfunction:
file_path: "example.clj"
form_type: "defn"
form_identifier: "bar"
operation: "replace"
content: "(defn bar [x] (+ x 3))"
- Observe the diff — changes appear outside the replaced form:
(def ob-yes-heavy
{:orderbook {:yes [[60 200] [55 150]]
- :no [[40 80] [35 60]]}})
+ :no [[40 80] [35 60]]}})
-(def ob-empty-no {:orderbook {:yes [[60 100]] :no []}})
-(def ob-empty {:orderbook {:yes [] :no []}})
+(def ob-empty-no {:orderbook {:yes [[60 100]] :no []}})
+(def ob-empty {:orderbook {:yes [] :no []}})
(defn foo [x] (+ x 1))
-(defn bar [x] (+ x 2))
+(defn bar [x] (+ x 3))Only the bar defn should have changed.
Root cause
In pipeline.clj, the edit-form-pipeline function:
- Locates the target form via
find-top-level-form✅ - Replaces it with
z/replace(scoped to the single form) ✅ - Converts the zipper back to a string ✅
- Applies
format-source(cljfmt) to the entire file string ← problem
The default cljfmt options in core.clj include aggressive normalizers:
{:remove-surrounding-whitespace? true
:remove-trailing-whitespace? true
:remove-consecutive-blank-lines? true
:insert-missing-whitespace? true
:remove-multiple-non-indenting-spaces? true}These rewrite whitespace across the entire file, not just the replaced region.
Impact
For Claude Code / AI agent workflows, this is particularly disruptive:
- Noisy diffs: A single-form replacement produces a diff with dozens of unrelated whitespace changes, making review harder.
- Stale REPL vars: When cljfmt reformats forms surrounding a replaced
deftest, Babashka'srequire :reloaddoesn't remove old vars that were defined at now-shifted line positions. This causes phantom test failures from ghost test vars that no longer exist in the file but persist in the REPL namespace.
Suggested fix
Any of these would resolve it:
- Scope cljfmt to only the replaced form — format the new content before inserting it, rather than formatting the whole file after.
- Make whole-file formatting opt-in — default to formatting only the replaced form; add a config flag like
:format-whole-file? truefor users who want it. - Respect the file's existing style — skip formatting entirely when the operation is
replace(the user's provided content is already how they want it).
Workaround: Users can set :cljfmt false in their MCP config to disable the formatting step entirely.
Environment
- clojure-mcp v0.2.6 (commit 35a660b)
- Babashka nREPL
- Claude Code with
:cli-assistconfig profile
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels