Clojure REPL Skill
Start or connect to an nREPL server for REPL-driven development.
Quick Reference
| Command | Action |
|---|---|
/clj-repl |
Auto-detect project, start REPL (asks if existing found) |
/clj-repl fresh |
Restart all - stop existing, start new (no prompts) |
/clj-repl keep |
Use existing REPLs if running (no prompts) |
/clj-repl bb |
Start Babashka nREPL |
/clj-repl clj |
Start JVM Clojure nREPL |
/clj-repl shadow |
Start shadow-cljs nREPL |
/clj-repl status |
Check tracked + discovered REPLs |
/clj-repl stop |
Stop all tracked REPLs |
/clj-repl --verbose |
Show detailed MCP tool output |
Output Style
Default: Concise - Show brief status updates, final summary table only.
With --verbose - Show MCP tool call details and intermediate output.
Example concise output:
Starting backend + shadow-cljs...
✓ Backend REPL on port 1669
✓ shadow-cljs on port 9630
✓ App server started
| Service | Port | URL |
|---------|------|-----|
| Backend | 1669 | - |
| shadow-cljs | 9630 | http://localhost:9630 |
| Web App | 8080 | http://localhost:8080 |
DO NOT show raw MCP JSON responses or tool call internals unless --verbose is specified.
CRITICAL: Use MCP Tools for REPL Operations
After REPLs are running, ALWAYS use forj MCP tools instead of bash commands:
| Task | MCP Tool | NOT bash |
|---|---|---|
| Evaluate code | repl_eval |
clj-nrepl-eval -p ... |
| Find REPLs | discover_repls |
clj-nrepl-eval --discover-ports |
| Reload namespace | reload_namespace |
|
| Look up docs | doc_symbol |
(doc ...) |
| Eval at line | eval_at |
|
| Run tests | run_tests |
clj -M:test |
Bash is ONLY for starting/stopping REPL servers. All evaluation should use MCP tools.
You Are NOT Done Until...
Before reporting success, ALL of these must be true:
- All required processes started for the project type
- Application server is running (not just REPL process)
- Checked shadow-cljs.edn for
:dev-httpports (web apps served here!) - You've reported ALL ports AND URLs to user
⚠️ COMMON MISS: The :dev-http port in shadow-cljs.edn!
grep ":dev-http" shadow-cljs.edn
# Example output: :dev-http {8080 "resources/public"}
# This means web app is at http://localhost:8080 - REPORT IT!
Instructions
Step 1: Check for Existing Processes
Handle flags first:
| Flag | Behavior |
|---|---|
fresh |
Skip prompts. Stop all tracked processes, start new ones. |
keep |
Skip prompts. Use existing REPLs if found, start only what's missing. |
| (none) | Ask user if existing processes found. |
Check for tracked processes:
list_tracked_processes
discover_repls
With fresh flag:
stop_project # Stop all tracked processes
# Then proceed to start new ones (Step 2+)
With keep flag:
- If REPLs are already running for this project → report them and done
- If some are missing → start only what's missing
- No prompts, just proceed
With no flag (default): If there are tracked processes that are still alive:
- Tell the user what's already running (brief: "Found backend on 1669, shadow on 9630")
- Ask: "Keep existing, restart fresh, or stop only?"
If processes are tracked but all dead, clean them up silently:
stop_project
Step 2: Detect Project Type & What to Start
Check which config files exist:
| Files Present | What to Start |
|---|---|
deps.edn + shadow-cljs.edn + app.json |
Backend + shadow-cljs (check for multiple builds!) + Expo |
deps.edn + shadow-cljs.edn |
Backend + shadow-cljs (check for multiple builds!) |
shadow-cljs.edn + app.json |
shadow-cljs + Expo |
deps.edn only |
Backend REPL |
shadow-cljs.edn only |
shadow-cljs |
bb.edn only |
Babashka REPL |
⚠️ CHECK FOR MULTIPLE SHADOW BUILDS:
Many projects have BOTH :web and :mobile builds. Check shadow-cljs.edn:
grep -E "^\s+:(web|mobile|app|main)" shadow-cljs.edn
If you see multiple builds (e.g., :web AND :mobile):
- Start both shadow builds:
bb shadow:webANDbb shadow:mobile - Don't assume one is enough!
Example project with both:
bb dev # Backend REPL
bb shadow:web # Web frontend (port 8080)
bb shadow:mobile # Mobile frontend
bb expo:web # Expo dev server
Do NOT stop after starting REPLs. The app server must also be running.
Step 3: Use bb Tasks (Required When Available)
If bb.edn has relevant tasks, you MUST use them instead of raw commands.
| Task | Use For |
|---|---|
bb dev or bb repl |
Backend REPL |
bb shadow:mobile |
ClojureScript for Expo (outputs to app/) |
bb shadow:web |
ClojureScript for browser |
bb expo / bb expo:android / bb expo:ios / bb expo:web |
Expo dev server |
Only fall back to raw commands if NO bb task exists.
Step 4: Start ALL Required Processes
Use run_in_background: true for each. Start them all before moving on.
First, create log directory:
mkdir -p .forj/logs
Use setsid -f with tee to daemonize processes (survive terminal close):
# Pattern: setsid -f bash -c 'command 2>&1 | tee .forj/logs/<name>.log' &
setsid -f bash -c 'bb dev 2>&1 | tee .forj/logs/backend.log' &
setsid -f bash -c 'bb shadow:web 2>&1 | tee .forj/logs/shadow.log' & # or shadow:mobile for Expo
IMPORTANT: Track each process after starting!
After each background process starts, call track_process with the PID (from Bash tool output):
track_process with pid=<PID> name="backend-repl" port=<PORT> command="bb dev"
track_process with pid=<PID> name="shadow-cljs" port=9630 command="bb shadow:mobile" # or shadow:web
track_process with pid=<PID> name="expo" port=8081 command="bb expo"
This enables /clj-repl stop to cleanly shut down all processes later.
To view logs later, use the view_repl_logs MCP tool:
view_repl_logswithlog: "all"- see all logs at onceview_repl_logswithlog: "backend"/"shadow"/"expo"- specific loglines: 50(default) - number of lines to return
Full-stack with web + mobile (check shadow-cljs.edn for builds!):
mkdir -p .forj/logs
setsid -f bash -c 'bb dev 2>&1 | tee .forj/logs/backend.log' &
# Check shadow-cljs.edn for builds - start ALL that exist:
setsid -f bash -c 'bb shadow:web 2>&1 | tee .forj/logs/shadow-web.log' & # If :web build exists
setsid -f bash -c 'bb shadow:mobile 2>&1 | tee .forj/logs/shadow-mobile.log' & # If :mobile build exists
# Then prompt for device (see Step 4a)
Step 4a: Mobile Device Selection (For Mobile Projects)
First, check if Expo Web is available:
# Check package.json for react-native-web
grep -q "react-native-web" package.json && echo "WEB_ENABLED"
Ask the user (include Web option only if detected):
"How do you want to run the app?"
- Android emulator (launches automatically)
- iOS simulator (launches automatically)
- Physical device (enter URL in Expo Go)
- Web browser (if react-native-web installed) - great for Playwright/automation testing
Use bb tasks (NOT npx):
Android:
setsid -f bash -c 'bb expo:android 2>&1 | tee .forj/logs/expo.log' &
iOS:
setsid -f bash -c 'bb expo:ios 2>&1 | tee .forj/logs/expo.log' &
Physical Device (Manual URL):
# Get local IP
LOCAL_IP=$(ip route get 1 | awk '{print $7; exit}')
# Start Expo in background with logging (daemonized)
setsid -f bash -c 'bb expo 2>&1 | tee .forj/logs/expo.log' &
# Wait for startup
sleep 3
# Display connection info
echo "Open Expo Go on your device and enter URL:"
echo " exp://${LOCAL_IP}:8081"
Web Browser (if react-native-web installed):
setsid -f bash -c 'bb expo:web 2>&1 | tee .forj/logs/expo.log' &
# Wait for startup
sleep 3
echo "Open http://localhost:8081 in your browser"
Full-stack web:
mkdir -p .forj/logs
setsid -f bash -c 'bb dev 2>&1 | tee .forj/logs/backend.log' &
setsid -f bash -c 'bb shadow:web 2>&1 | tee .forj/logs/shadow.log' &
Backend only:
mkdir -p .forj/logs
setsid -f bash -c 'bb dev 2>&1 | tee .forj/logs/backend.log' &
Step 5: Start Application Server (REQUIRED - Don't Skip!)
REPL running ≠ Done. The application server must be started from within the REPL.
Detection priority:
Common functions - Search
src/**/core.cljfor:(defn start-server→ eval(<namespace>/start-server)(defn start→ eval(<namespace>/start)(defn go→ eval(<namespace>/go)
State management - Check requires:
mount.core→ eval(mount.core/start)integrant.core→ look for(ig/init ...)component→ look for(component/start ...)
Comment blocks - Read them for startup examples
Fallback - Read core.clj and figure out how to start it
Example:
repl_eval with code="(require '[myapp.core :as core] :reload)"
repl_eval with code="(core/start-server {:port 3000})"
Step 6: Verify Everything Works
Wait 2-5 seconds after starting, then:
discover_repls
Test each REPL:
repl_eval with code="(+ 1 2)" port=<backend-port>
repl_eval with code="(+ 1 2)" port=<shadow-port>
Step 7: Report Success (Only After Everything Running)
Final checklist before reporting:
- All REPLs started for project type?
- Application server running and accepting requests?
- Expo running (if mobile project)?
Check shadow-cljs.edn for dev-http ports:
grep -o ':dev-http {[0-9]*' shadow-cljs.edn
If :dev-http is present (e.g., {8080 "resources/public"}), the web app is served on that port.
Report all services in a table:
| Service | Port | URL |
|---|---|---|
| Backend API | 3000 | http://localhost:3000 |
| Web App (dev-http) | 8080 | http://localhost:8080 |
| Expo Web | 8081 | http://localhost:8081 |
| shadow-cljs UI | 9630 | http://localhost:9630 |
Include only the services actually running for this project type.
Tell the user:
- All processes started
- Port numbers for each service
- URLs for accessing each service
- They can now use forj MCP tools
Evaluating Code
When user asks to evaluate code, ALWAYS use repl_eval MCP tool:
repl_eval with code="(+ 1 2 3)"
The tool auto-discovers ports. Do NOT shell out to clj-nrepl-eval.
Checking Status (/clj-repl status)
Run both tools and summarize:
list_tracked_processes
discover_repls
Report to user:
- Tracked processes: What this session started (with alive/dead status)
- Discovered REPLs: All nREPL servers found (may include untracked ones)
Stopping REPLs
Use the stop_project MCP tool to stop all tracked processes:
stop_project
This kills all REPLs, shadow-cljs, and Expo processes that were tracked with track_process.
To check what's tracked before stopping:
list_tracked_processes
Manual fallback (if processes weren't tracked):
lsof -i :<port>
kill $(lsof -t -i :<port>)
Monorepo Handling
For projects with multiple build files:
- Use
discover_replsto see what's running - Ask user which to start if needed
- Multiple REPLs can run on different ports
- MCP tools auto-discover all ports
Troubleshooting
No REPLs found:
- Use
discover_replsto check what's running - Try starting one with
/clj-repl bbor/clj-repl clj
Wrong project context: The nREPL's working directory determines the project. Make sure to start the REPL from the correct directory.
Port conflicts: Each REPL needs a unique port. Use different ports for multiple REPLs.
Notes
- Babashka nREPL is fast to start - good for quick scripts
- JVM Clojure takes 5-10 seconds to start but has full Clojure
- Shadow-cljs provides ClojureScript REPL with hot reload
- All REPL interaction should use MCP tools, not bash