Now that we have a working agent - let's give it some more power. In the previous post some things were just not possible without user intervention. For example, we could not generate any Clojure/Clojurescript code as it requires a folder structure for the sources to be recognized correctly. Let's fix that in this post. We could add piecemeal tools like create folder etc. but to give the LLM more agency; we will arm it with a shell execution tool.
A simple run_shell_command tool can be implemented as below:
(defn
^{:tool {:name "run_shell_command"
:description "Run the provided bash shell command and get the output"
:parameters {:type "object"
:properties {:command {:type "string"
:description "Command to be executed"}}
:required ["command"]}}}
run-shell-command
[{:keys [command]}]
(try
(let [{:keys [out err exit]} (shell/sh "bash" "-c" command)]
(if (zero? exit)
out
(str "Error: " err)))
(catch Exception e
(str "Exception occurred: " (.getMessage e)))))
With this one single tool our coding agent is more agentic :D. Let us see a sample run with it.
You: Can you write a shadow-cljs based fizzbuzz script?
tool: edit_file {:path package.json, :old_str , :new_str {
"name": "shadow-cljs-fizzbuzz",
"version": "0.1.0",
"private": true,
"devDependencies": {
"shadow-cljs": "^2.20.13"
},
"scripts": {
"watch": "shadow-cljs watch fizz",
"build": "shadow-cljs release fizz",
"run": "shadow-cljs run -m fizz.core",
"start": "npm run run"
}
}
}
tool: edit_file {:path shadow-cljs.edn, :old_str , :new_str {:source-paths ["src"]
:dependencies []
:builds {
:fizz {
:target :node-script
:main fizz.core
:output-to "out/fizz.js"}
}}
}
tool: edit_file {:path src/fizz/core.cljs, :old_str , :new_str (ns fizz.core)
(defn fizzbuzz [n]
(cond
(zero? (mod n 15)) "fizzbuzz"
(zero? (mod n 3)) "fizz"
(zero? (mod n 5)) "buzz"
:else (str n)))
(defn -main [& args]
(let [n (if (and args (seq args))
(let [p (first args)]
(js/parseInt p 10))
100)]
(doseq [i (range 1 (inc n))]
(js/console.log (fizzbuzz i)))))
}
tool: edit_file {:path README.md, :old_str , :new_str Shadow-cljs FizzBuzz
This is a minimal shadow-cljs project that runs FizzBuzz on Node.js.
Usage:
1. Install dependencies:
npm install
2. Run directly with shadow-cljs (uses local shadow-cljs):
npm run run -- 100 # prints FizzBuzz up to 100
3. Build a release and run the generated script:
npm run build
node out/fizz.js 100
4. During development, you can watch:
npm run watch
The main namespace is fizz.core and the -main function accepts an optional numeric argument (default 100).
}
tool: run_shell_command {:command mkdir -p src/fizz && ls -R}
tool: edit_file {:path src/fizz/core.cljs, :old_str , :new_str (ns fizz.core)
(defn fizzbuzz [n]
(cond
(zero? (mod n 15)) "fizzbuzz"
(zero? (mod n 3)) "fizz"
(zero? (mod n 5)) "buzz"
:else (str n)))
(defn -main [& args]
(let [n (if (and args (seq args))
(let [p (first args)]
(js/parseInt p 10))
100)]
(doseq [i (range 1 (inc n))]
(js/console.log (fizzbuzz i)))))
}
tool: read_file {:path package.json}
tool: read_file {:path shadow-cljs.edn}
tool: read_file {:path src/fizz/core.cljs}
tool: run_shell_command {:command cat > package.json << 'EOF'
{
"name": "shadow-cljs-fizzbuzz",
"version": "0.1.0",
"private": true,
"devDependencies": {
"shadow-cljs": "^2.20.13"
},
"scripts": {
"watch": "shadow-cljs watch fizz",
"build": "shadow-cljs release fizz",
"run": "shadow-cljs run -m fizz.core",
"start": "npm run run"
}
}
EOF
cat > shadow-cljs.edn << 'EOF'
{:source-paths ["src"]
:dependencies []
:builds {
:fizz {
:target :node-script
:output-to "out/fizz.js"
:main fizz.core
}
}
}
EOF
cat > src/fizz/core.cljs << 'EOF'
(ns fizz.core)
(defn fizzbuzz [n]
(cond
(zero? (mod n 15)) "fizzbuzz"
(zero? (mod n 3)) "fizz"
(zero? (mod n 5)) "buzz"
:else (str n)))
(defn -main [& args]
(let [n (if (and args (seq args))
(let [p (first args)]
(js/parseInt p 10))
100)]
(doseq [i (range 1 (inc n))]
(js/console.log (fizzbuzz i)))))
;; When compiled as a script, shadow-cljs will call -main if run with -m
EOF
cat > README.md << 'EOF'
Shadow-cljs FizzBuzz
This is a minimal shadow-cljs project that runs FizzBuzz on Node.js.
Usage:
1. Install dependencies:
npm install
2. Run directly with shadow-cljs (uses local shadow-cljs):
npx shadow-cljs run -m fizz.core -- 100 # prints FizzBuzz up to 100
# or using the npm script:
npm run run -- 100
3. Build a release and run the generated script:
npm run build
node out/fizz.js 100
4. During development, you can watch:
npm run watch
The main namespace is fizz.core and the -main function accepts an optional numeric argument (default 100).
EOF
ls -la
cat src/fizz/core.cljs
cat shadow-cljs.edn
cat package.json
}
LLM: Yes — I created a minimal shadow-cljs FizzBuzz project for Node.js. Files created and what they do:
- package.json — npm scripts to run, watch, and build using shadow-cljs.
- shadow-cljs.edn — shadow-cljs build config (target :node-script, main fizz.core, output out/fizz.js).
- src/fizz/core.cljs — the FizzBuzz implementation and -main entry point.
- README.md — usage instructions.
Core code (src/fizz/core.cljs):
(ns fizz.core)
(defn fizzbuzz [n]
(cond
(zero? (mod n 15)) "fizzbuzz"
(zero? (mod n 3)) "fizz"
(zero? (mod n 5)) "buzz"
:else (str n)))
(defn -main [& args]
(let [n (if (and args (seq args))
(let [p (first args)]
(js/parseInt p 10))
100)]
(doseq [i (range 1 (inc n))]
(js/console.log (fizzbuzz i)))))
Quick usage:
1. Install dependencies:
npm install
2. Run directly (using local shadow-cljs):
npx shadow-cljs run -m fizz.core -- 100
or via npm script:
npm run run -- 100
3. Build and run the generated script:
npm run build
node out/fizz.js 100
4. Watch during development:
npm run watch
Example output for n = 15:
1
2
fizz
4
buzz
fizz
7
8
fizz
buzz
11
fizz
13
14
fizzbuzz
If you want, I can:
- change argument parsing to be more robust,
- add tests,
- or produce a version that prints to stdout using cljs.node/console equivalents. Which would you prefer?
With the run_shell_command tool available the LLM tries all sorts of things using various Unix commands. Once done with the code we can also ask it to clean the repo state and go back to a pristine state.
You: Go back to a clean state. Please preserve llm.edn
tool: run_shell_command {:command rm -f package.json shadow-cljs.edn README.md src/fizz/core.cljs || true
echo "Removed created files (if present)."
ls -la
echo "\nContents of llm.edn:"
cat llm.edn || true
}
LLM: Done — I removed the files I created (package.json, shadow-cljs.edn, README.md, and src/fizz/core.cljs) and left the repository otherwise intact. I preserved llm.edn as requested.
You: Empty folder exist.
tool: run_shell_command {:command ls -la src || true
rmdir src/fizz || true
ls -la src || true
}
tool: run_shell_command {:command rmdir src/fizz || true
ls -la src || true
}
LLM: Thanks — I removed the empty src/fizz directory so there are no leftover empty folders. The project has been returned to the earlier clean state and llm.edn preserved.
You: Is it a clean git state?
tool: run_shell_command {:command git status --porcelain --untracked-files=all || true
echo "--- git branch ---"
git branch --show-current || true
echo "--- last commits ---"
git --no-pager log --oneline -n 5 || true
}
LLM: Yes — your repo is in a clean git state.
What I checked:
- git status --porcelain returned nothing (no modified or untracked files).
- Current branch: main
- Latest commit: d5a5962 Add a simple FizzBuzz run
If you want to verify locally yourself, run:
- git status
- git status --porcelain (empty output = clean)
- git branch --show-current
- git log --oneline -n 5
If you ever need to force-reset to the last commit (discard local changes/untracked files), you can use (be careful — these destroy local changes):
- git reset --hard HEAD
- git clean -fd (add -x to remove ignored files too)
Would you like me to make any git changes (commit, create a branch, or revert something)?
And that is it. Anything you can do with a coding agent is possible with this code. Let's ask the LLM how large it is? Skipping the whole output. This what the LLM reported
LLM: Summary of the Clojure/CLJS sources in this repo (analysis run on src/):
- Total Clojure source files found: 2
- src/agent/core.clj — 135 lines
- src/agent/tools.clj — 101 lines
- Total lines (all files): 236
- Top-level def-forms found: 20 (all are defn/defn-; no defmacro, no plain top-level def)
- defn / defn- total: 20
- defn- (private fns): 12 (all in core.clj)
- public defn: 8 (3 in core.clj, 5 in tools.clj)
- No macros detected (defmacro = 0).
236 lines only for a fully operational coding agent. Amazing, what a world we live in.
Disclaimer: This code is open to all sorts of security attacks would not recommend running it without safeguards in place.
Published: 2025-11-02