Bracket-fixing REPL for Claude Code and other AI-assisted Clojure development.
brepl (Bracket-fixing REPL) enables AI-assisted Clojure development by solving the notorious parenthesis problem. It's primarily designed for Claude Code through its hook system, providing three essential capabilities:
- 🔧 Automatic bracket fixing - Intelligently corrects mismatched parentheses, brackets, and braces using parmezan
- ⚡ Simple REPL evaluation - Gives AI agents a straightforward way to evaluate code in your running REPL, enabling truly interactive development
- 🔄 Live file synchronization - Automatically evaluates edited files in the REPL, providing early feedback on evaluation errors before they become problems
Primary use case: Claude Code and other AI coding agents that need reliable Clojure syntax handling and immediate REPL feedback.
Versatile tool: While designed for AI workflows, brepl is equally capable as a lightweight CLI nREPL client for one-shot evaluations, scripts, and automation—making it useful for both AI-assisted and traditional development workflows.
brepl uses parmezan for intelligent bracket correction:
- Pure Clojure: No external binary dependencies required
- Automatic fixing: Corrects mismatched delimiters, missing brackets, and extra closing parens
- Graceful fallback: Blocks with syntax errors when auto-fix is not possible
Assuming brepl is already installed (see Installation):
# Start your nREPL server
bb nrepl-server
# Install hooks and skill in your project
brepl hook installThe brepl hook install command configures Claude Code to:
- Validate and auto-fix brackets before every file edit
- Evaluate changed Clojure files in your running REPL after edits
- Provide immediate feedback on syntax and evaluation errors
- Install the brepl skill that teaches Claude:
- Heredoc pattern for reliable code evaluation
- Error recovery workflows
Now Claude can write Clojure code confidently without worrying about parentheses or missing REPL feedback.
# Evaluate expressions (auto-detects .nrepl-port)
brepl -e '(+ 1 2 3)'
# => 6
# Load and execute files
brepl -f script.clj
# Bracket fixing is automatic in hook mode- Intelligent auto-correction - Uses parmezan to fix mismatched delimiters automatically
- Pre-edit validation - Catches and fixes bracket problems before they're written to files
- Detailed error reporting - When auto-fix isn't possible, provides clear syntax errors for AI agents
- No external dependencies - Pure Clojure solution, no binary installation required
- Direct nREPL integration - AI agents can evaluate code in your running REPL with simple commands
- Heredoc pattern - Skill teaches reliable evaluation pattern that eliminates shell quoting issues
- Automatic bracket correction - Bracket errors are fixed automatically in hook mode
- Project-aware discovery - Automatically finds the right REPL for each file (v1.3.0)
- Full protocol support - Access any nREPL operation, not just evaluation
- Fast Babashka runtime - Instant startup for responsive AI interactions
- Automatic evaluation - Files are evaluated in REPL immediately after editing
- Early error feedback - AI agents see evaluation errors right away, not later
- Session-based backups - Automatic backup/restore protects against bad edits
- One-command setup -
brepl hook installenables everything in seconds
- 🚀 Fast command-line evaluation - Quick one-liners with
brepl -e '(+ 1 2)' - 📁 File loading - Execute entire Clojure files with
brepl -f script.clj - 💬 Raw nREPL messages - Send any protocol message for advanced operations
- 🔍 Smart port discovery - Automatically detects
.nrepl-portfiles - ⚙️ Flexible configuration - Environment variables and CLI arguments
- 🐛 Proper error handling - Shows exceptions and stack traces
- 📊 Verbose mode - Debug nREPL communication with
--verbose
bbin install io.github.licht1stein/brepl{ pkgs ? import <nixpkgs> {} }:
let
brepl = pkgs.callPackage (pkgs.fetchFromGitHub {
owner = "licht1stein";
repo = "brepl";
rev = "v2.3.1";
hash = "sha256-Y3fjpNcAvnjSvgxEP/9mD4YcoTtjvgSpjgzMDObR028=";
} + "/package.nix") {};
in
pkgs.mkShell {
buildInputs = [ brepl ];
}Then run nix-shell to enter a shell with brepl available.
git clone https://github.com/licht1stein/brepl.git
cd brepl
# Add to PATH
ln -s $(pwd)/brepl ~/.local/bin/breplGet help: brepl --help
-e, --e <expr> Expression to evaluate
-f, --f <file> File to load and execute
-m, --m, --message <message> Raw nREPL message (EDN format)
-h, --h <host> nREPL host (default: localhost or BREPL_HOST)
-p, --p <port> nREPL port (required - auto-detects from .nrepl-port or BREPL_PORT)
--verbose Show raw nREPL messages instead of parsed output
--version Show brepl version
-?, --help Show help message
brepl hook install # Install hooks to .claude/settings.local.json (includes skill)
brepl hook uninstall # Remove hooks
brepl hook validate <file> <content> # Pre-edit validation with auto-fix
brepl hook eval <file> # Post-edit evaluation
brepl hook session-end <id> # Cleanup session backupsbrepl skill install # Install brepl skill to .claude/skills/brepl
brepl skill uninstall # Remove brepl skillNote: The skill is automatically installed when you run brepl hook install. Use brepl skill install only if you want to install the skill separately without hooks.
What the skill teaches Claude:
- Heredoc pattern for reliable code evaluation
- In-place file fixing workflows
- Error recovery patterns
Examples:
# Fix brackets in an expression
# => (defn foo [])
# Fix brackets in a file
# Evaluate an expression (auto-detects port from .nrepl-port)
brepl -e '(+ 1 2 3)'
# Load and execute a file
brepl -f script.clj
# Use single quotes to avoid escaping double quotes
brepl -e '(println "Hello, World!")'For AI agents (and humans) working with Clojure code that contains complex quoting, multi-line expressions, or nested structures, the heredoc pattern provides a consistent, foolproof approach to evaluation:
# Standard heredoc pattern - works for all cases
brepl -e "$(cat <<'EOF'
(require '[clojure.string :as str])
(str/join ", " ["a" "b" "c"])
EOF
)"Why use heredoc?
- No quoting issues: Everything between
<<'EOF'andEOFis treated as literal input - Consistent pattern: One approach for all evaluations, from simple to complex
- Multi-line friendly: Natural formatting for readable code
- Easy to extend: Add more forms without changing syntax
Examples:
# Multi-line expressions with complex quoting
brepl -e "$(cat <<'EOF'
(println "String with 'single' and \"double\" quotes")
(+ 10 20)
EOF
)"
# Namespace reloading and testing
brepl -e "$(cat <<'EOF'
(require '[myapp.core] :reload)
(myapp.core/some-function "test" 123)
EOF
)"
# Data structures with nested quotes
brepl -e "$(cat <<'EOF'
(def config
{:database {:host "localhost"
:port 5432}
:api {:key "secret-key"}})
(println (:database config))
EOF
)"Note: Always use <<'EOF' (with single quotes) to prevent shell variable expansion. The brepl skill (installed via brepl hook install) teaches Claude Code to use this pattern automatically.
The port is required and resolved in this order:
- Command line:
-p 7888 - Auto-detect:
.nrepl-portfile- For
-fflag: searches from the file's directory upward (v1.3.0+) - For
-e/-mflags: uses current directory
- For
- Environment:
BREPL_PORT=7888
# Explicit port
brepl -p 7888 -e '(+ 1 2)'
# Using environment variable
BREPL_PORT=7888 brepl -e '(+ 1 2)'
# Auto-detect from .nrepl-port (most common)
brepl -e '(+ 1 2)'
# NEW in v1.3.0: File-based project detection
# If you have multiple projects with different nREPL servers:
# project1/.nrepl-port (port 7000)
# project2/.nrepl-port (port 8000)
brepl -f project1/src/core.clj # Uses port 7000
brepl -f project2/src/app.clj # Uses port 8000When using the -f flag, brepl now searches for .nrepl-port files starting from the file's directory and walking up the directory tree. This allows you to work with multiple projects simultaneously:
# Directory structure:
# ~/projects/
# ├── backend/
# │ ├── .nrepl-port (7000)
# │ └── src/api/handler.clj
# └── frontend/
# ├── .nrepl-port (8000)
# └── src/ui/core.cljs
# Automatically uses the correct nREPL server for each project:
brepl -f ~/projects/backend/src/api/handler.clj # Connects to port 7000
brepl -f ~/projects/frontend/src/ui/core.cljs # Connects to port 8000This is especially useful when:
- Working with monorepos containing multiple services
- Switching between different projects frequently
- Using editor integrations that operate on individual files
# Connect to remote host with specific port
brepl -h remote-server -p 7888 -e '(+ 1 2)'
# Using environment variables
BREPL_HOST=remote-server BREPL_PORT=7888 brepl -e '(+ 1 2)'Set these for default configuration:
export BREPL_HOST=localhost # Default host
export BREPL_PORT=7888 # Default portOr use them for one-off commands:
BREPL_PORT=7888 brepl -e '(+ 1 2 3)'# Start nREPL server (creates .nrepl-port file)
bb nrepl-server
# Basic evaluation
brepl -e '(+ 1 2 3)'
brepl -e '(require '[clojure.string :as str]) (str/upper-case "hello")'
# Load a script file
brepl -f my-script.clj
# Send raw nREPL messages
brepl -m '{"op" "describe"}'
brepl -m '{"op" "ls-sessions"}'
brepl -m '{"op" "eval" "code" "(+ 1 2)"}'
# Multi-line expressions (single quotes make it easier)
brepl -e '(let [x 10
y 20]
(+ x y))'
# Quick math
brepl -e '(reduce + (range 100))'
# Check Babashka version
brepl -e '(System/getProperty "babashka.version")'
# Development workflow
brepl -f test/my_test.clj
brepl -e '(require '[my.namespace :refer :all]) (my-function 123)'The -m/--message option allows you to send raw nREPL messages in EDN format. This makes brepl a full-fledged nREPL client capable of accessing all operations supported by the nREPL server.
# Get server capabilities
brepl -m '{"op" "describe"}'
# List active sessions
brepl -m '{"op" "ls-sessions"}'
# Clone a session
brepl -m '{"op" "clone"}'
# Get completions
brepl -m '{"op" "complete" "prefix" "str/" "ns" "user"}'
# Look up symbol documentation
brepl -m '{"op" "info" "symbol" "map" "ns" "clojure.core"}'
# Evaluate with specific session
brepl -m '{"op" "eval" "code" "(+ 1 2)" "session" "your-session-id"}'describe- Returns server capabilities and supported operationseval- Evaluate codeload-file- Load a file's contentsls-sessions- List active sessionsclone- Create a new sessionclose- Close a sessioninterrupt- Interrupt an evaluationcomplete- Get code completionsinfo- Get symbol informationlookup- Look up symbol documentationeldoc- Get function signature information
For a complete list of standard nREPL operations, see the nREPL documentation.
- Automatic ID generation: If you don't provide an
idfield, brepl will add one automatically - Response handling: Some operations return multiple messages. Use
--verboseto see the full conversation - Session management: Most operations work without a session, but some require one (like
interrupt) - Byte array conversion: All byte arrays in responses are automatically converted to strings for readability
- Debugging: Use
--verbosewith-mto see exactly what's sent and received
# Debug mode - see full message exchange
brepl -m '{"op" "describe"}' --verbosebrepl is specifically designed to integrate with Claude Code through its hook system, providing seamless REPL-driven development for AI agents.
Why brepl for Claude Code?
Claude (and other LLMs) often struggle with Lisp parentheses, leading to syntax errors that break the development flow. brepl solves this by intercepting Claude's file operations and:
- Fixing bracket errors before they're written to disk
- Evaluating code in your REPL immediately after edits
- Providing clear feedback so Claude can correct course quickly
Two Approaches to AI-Assisted Clojure:
- Protocol servers - Run MCP servers, configure protocol bridges, manage multiple processes
- brepl hooks - Direct integration with Claude Code using your existing REPL (our approach)
The brepl hook install command configures your project for Claude Code by creating or updating .claude/settings.local.json:
# In your Clojure project directory:
brepl hook installThis installs three hooks that run automatically during Claude Code sessions:
- Pre-edit hook: Intercepts Claude's file writes, validates syntax, and auto-fixes brackets
- Post-edit hook: Evaluates the edited file in your REPL and reports any runtime errors
- Session cleanup: Removes temporary backup files when Claude Code session ends
Once installed, Claude can edit your Clojure files without worrying about parentheses, and you'll see immediate REPL feedback for every change.
brepl hook install
Creates or updates .claude/settings.local.json to configure Claude Code hooks for the current project. This file tells Claude Code to run brepl for validation and evaluation on every Clojure file edit. Idempotent—safe to run multiple times.
brepl hook validate <file> <content>
Pre-edit syntax validation with automatic bracket correction. Recursively closes unclosed brackets and braces using the edamame parser. Returns corrected code or blocks with detailed error messages.
# Auto-fixes unclosed brackets
brepl hook validate src/core.clj "(defn foo ["
# => {"decision":"allow","correction":"(defn foo [])"}
# Blocks unfixable syntax errors
brepl hook validate src/core.clj "\"unclosed string"
# => {"decision":"block","reason":"Syntax error..."}brepl hook eval <file>
Post-edit validation and optional nREPL evaluation. Validates syntax first, then evaluates via nREPL if available. Warnings don't block—development stays fluid while catching real errors.
# With nREPL running - evaluates and warns on errors
brepl hook eval src/core.clj
# => {"decision":"allow","warning":"Undefined symbol..."}
# Without nREPL - validates syntax only
brepl hook eval src/core.clj
# => {"decision":"allow"} # Graceful degradationbrepl hook uninstall
Removes hooks from .claude/settings.local.json cleanly.
brepl hook session-end <session-id>
Cleanup command (called automatically by Claude Code) that removes session backup files.
When active, brepl hooks run automatically during AI-assisted edits:
- Before edit: Agent proposes code changes
- Validate: brepl checks syntax, auto-fixes brackets if needed
- Write: File is written with validated/corrected code
- Evaluate: brepl loads file into your running REPL (if available)
- Feedback: Agent sees warnings but continues unless syntax is invalid
This keeps your REPL state synchronized with file changes and catches errors early, without interrupting flow for recoverable issues like undefined symbols during incremental development.
brepl automatically creates session-specific backups before validating edits. If syntax errors are detected post-write, the original file is restored from backup. Backups are stored in /tmp/brepl-hooks-<session-id>/ and cleaned up automatically.
Hooks work with brepl's project-aware port discovery (v1.3.0+). When evaluating files, brepl walks up from the file's directory to find the correct .nrepl-port, so multi-project workflows just work:
# Directory structure:
# ~/projects/
# ├── service-a/
# │ ├── .nrepl-port (7000)
# │ └── src/api.clj
# └── service-b/
# ├── .nrepl-port (8000)
# └── src/handler.clj
# Each file evaluates against its own REPL
brepl hook eval ~/projects/service-a/src/api.clj # port 7000
brepl hook eval ~/projects/service-b/src/handler.clj # port 8000brepl takes a pragmatic approach to AI-assisted Clojure development: use battle-tested tools when available, provide clear feedback when not.
Minimal Core with Optional Enhancement:
- ✅ Syntax validation uses edamame (built into Babashka)
- ✅ No protocol servers or separate processes required
Direct REPL Integration:
- ✅ Uses your running nREPL connection (no separate context)
- ✅ Works with any nREPL server (Babashka, Clojure, ClojureScript)
- ✅ Minimal overhead (fast Babashka startup)
- ✅ Graceful degradation (works without nREPL for syntax checking)
- ✅ Project-aware (handles multiple REPLs automatically)
Installation Options:
Perfect for developers who want reliable AI assistance without managing multiple processes or protocol servers.
Error: No port specified, no .nrepl-port file found, and BREPL_PORT not set
- Start an nREPL server first:
bb nrepl-server - Or specify port manually:
brepl -p 7888 -e "(+ 1 2)"
Error connecting to nREPL server
- Check if nREPL server is running
- Verify the port number is correct
- For remote connections, ensure host is reachable
Get help anytime: brepl --help
- Babashka installed
- Running nREPL server (Babashka, Clojure, etc.)
The project includes a comprehensive test suite. To run tests:
# Run all tests
bb test
# Run specific test namespace
bb test --nses brepl-test
# Run specific test
bb test --vars brepl-test/basic-evaluation-testThe test suite covers:
- Basic expression evaluation
- File loading and execution
- Error handling and exceptions
- Output handling (stdout/stderr)
- Port and host resolution
- Environment variable handling
- Verbose mode functionality
- CLI argument validation
- Edge cases and error conditions
Use --verbose to debug nREPL communication:
brepl -p 1667 -e '(+ 1 2)' --verbose
# Shows the complete nREPL message exchange:
# {"op" "eval", "code" "(+ 1 2)", "id" "1749559876543"}
# {"id" "1749559876543", "ns" "user", "session" "none", "value" "3"}
# {"id" "1749559876543", "session" "none", "status" ["done"]}MPL-2.0 License
brepl follows break versioning:
- Version format:
<major>.<minor>.<non-breaking> - Breaking changes increment the minor version (e.g., 1.0.0 → 1.1.0)
- Non-breaking changes increment the patch version (e.g., 1.0.0 → 1.0.1)
Contributions are welcome! Please feel free to submit issues and pull requests.
Before submitting a PR:
- Ensure all tests pass:
bb test - Add tests for any new functionality
- Update documentation as needed