| name | clojure-bracket-guard |
| description | 强制在编辑 Clojure 家族文件(.clj/.cljs/.cljc/.cljd/.edn)时使用 bracket-safe 流程。禁止直接使用内置 edit 工具,改用 rewrite-clj 验证的 safe-edit;写入前必须先 validate。支持文本匹配编辑、基于行列位置的结构化操作(swap/remove/insert/splice/wrap)、批量替换和模糊匹配。 |
Clojure Bracket Guard
Rule
When working with any Clojure-family file — .clj, .cljs, .cljc, .cljd, .edn — you MUST:
- Never use the built-in
edit tool. Use safe-edit instead.
- Never use
write without validating first. Use validate before writing.
Setup
Requires babashka (bb) on PATH.
Optional: clojure-lsp for --format flag.
The scripts are in the scripts/ directory of this skill. Resolve paths relative to the skill directory.
Writing — validate before write
bb scripts/validate.clj --code '<CONTENT>'
bb scripts/validate.clj --file <TARGET_FILE>
Only call write after validation passes.
Editing — use safe-edit instead of edit
Never use the built-in edit tool for Clojure-family files. It writes before validation, so broken brackets are already saved when the error is detected.
safe-edit is a feature-complete replacement with added bracket validation. It matches pi-style edit semantics:
- All edits matched against the original file (not sequential)
- Fuzzy matching — smart quotes, Unicode dashes, special spaces normalized
- BOM + CRLF handling
- Overlap & uniqueness checks
- Bracket validation via rewrite-clj
Single edit
bb scripts/safe-edit.clj --file src/core.clj --old 'old text' --new 'new text'
Multiple edits (all matched against original)
bb scripts/safe-edit.clj --file src/core.clj \
--edit '(defn foo [x]>(defn foo [x y]' \
--edit '(+ x 1)>(+ x y 1)'
bb scripts/safe-edit.clj --file src/core.clj \
--edits '[{:old "(defn foo [x]" :new "(defn foo [x y]"} {:old "(+ x 1)" :new "(+ x y 1)"}]'
Custom separator (when > appears in code)
bb scripts/safe-edit.clj --file src/core.clj --sep '|||' \
--edit '(-> x|||(->> x'
Format with clojure-lsp
bb scripts/safe-edit.clj --file src/core.clj --format --old 'old' --new 'new'
Dry run
bb scripts/safe-edit.clj --dry-run --file src/core.clj --old 'old' --new 'new'
Target-based editing (bypasses text matching)
Use --target <row:col> to locate a form by its source position, then
use --op swap|remove|insert|splice|wrap for structural operations.
Swap children (e.g. move docstring before args)
bb scripts/safe-edit.clj --file src/core.clj \
--target "23:1" --op swap --swap "2,3"
Remove child
bb scripts/safe-edit.clj --file src/core.clj \
--target "23:1" --op remove --idx 2
Insert child
bb scripts/safe-edit.clj --file src/core.clj \
--target "23:1" --op insert --idx 2 --new '"new docstring"'
Splice (remove parent, keep inner form)
bb scripts/safe-edit.clj --file src/core.clj \
--target "10:1" --op splice
Wrap (wrap target in outer form)
bb scripts/safe-edit.clj --file src/core.clj \
--target "10:1" --op wrap --new '(fn [x] %FORM%)'
Batch editing
Replace all occurrences (skip uniqueness check)
bb scripts/safe-edit.clj --file src/core.clj \
--edit '(m/Color 0xff333333)>theme/text-primary' \
--edit '(m/Color 0xff777777)>theme/text-hint' \
--replace-all
Mapping file (for bulk replacements)
File format: old text||new text (one per line)
bb scripts/safe-edit.clj --file src/core.clj \
--mapping-file color-map.txt
Unwrap by symbol (find form by its first child symbol)
bb scripts/safe-edit.clj --file src/core.cljs \
--unwrap-symbol "clj->js"
What safe-edit does
- Reads the target file (strips BOM, detects line endings)
- Finds all oldText matches against the original (fuzzy-aware)
- Checks for overlaps and duplicate matches
- Applies replacements in reverse order (stable offsets)
- Restores BOM and original line endings
- Validates the result with rewrite-clj
- If valid: writes back
- If
--format: runs clojure-lsp format on the file
- If invalid: prints error, file NOT modified, exits 1
On Failure
- Report the error to the user
- Fix the bracket issue
- Re-run safe-edit
- Only proceed after successful edit
Common LLM Bracket Mistakes
- Missing closing
) at the end of defn, let, if, etc.
- Extra
] or } from miscounting nested structures
- Unclosed string literals containing brackets
- Mismatched bracket types (e.g.,
(let [x 1) ...))