Exploring Modern Tools: Seed4J, Lagrange, Gemini CLI, Research Agents & lsyncd

Introduction

Innovation happens at every layer of computing — from user-facing browsers to backend automation daemons.
In this post, I explore a collection of tools that embody that diversity:

  1. Seed4J
  2. Lagrange
  3. Gemini CLI
  4. A LangChain + DeepAgents research agent
  5. lsyncd

1. Seed4J

Seed4J is a promising Java-focused framework for bootstrapping projects quickly and consistently. It appears to provide conventions for initialization, configuration, and dependency setup — helping developers start new modules or microservices with best practices baked in.

compare_reviews.clj — A tiny, configurable LLM-powered reviewer in Clojure

Overview

This tool assembles Markdown from patterns, calls an LLM with a structured prompt, and writes a report as Markdown (and optionally PDF). It’s driven by EDN config, meaning you can change behavior by editing data, not code.

Why it’s cool:

  • Configuration-as-data (EDN): composable, Git-friendly, reproducible.
  • Clear pipeline: collect → call LLM → write → optional PDF and summary.
  • Smart outputs: timestamped filenames avoid collisions with minimal friction.
  • Extensible “agent” model: the LLM call is just a map merged into agent/call.
  • Minimal code, lots of leverage (Pandoc, glob patterns, simple IO).

High-level data flow

flowchart TD
    A[Start] --> B[load-config (EDN path or map)]
    B --> C[mru/aggregate-md-from-patterns]
    C --> D[agent/call (merge model + {:system :pre :prompt})]
    D --> E[resolve-output-file]
    E --> F[Write main .md]
    F --> G{pdf?}
    G -- Yes --> H[Pandoc md->pdf]
    G -- No --> I[Skip]
    F --> J{summary?}
    J -- Yes --> K[agent/call for summary]
    K --> L[Write _summary.md]
    J -- No --> I
    H --> I[Done]
    L --> I

Code walkthrough (what each function does)

(ns margin-mania.reporting.compare-reviews
  (:require [clojure.edn :as edn]
            [pyjama.core :as agent]
            [margin-mania.reporting.utils :as mru]
            [pyjama.tools.pandoc]
            [clojure.java.io :as io])
  (:import (java.io File PushbackReader)
           (java.time LocalDateTime)
           (java.time.format DateTimeFormatter)))
  • pyjama.core/agent: abstraction over the LLM call (agent/call).
  • mru/aggregate-md-from-patterns: globs files and concatenates Markdown (plus optional metadata).
  • pyjama.tools.pandoc: converts Markdown to PDF.

load-config

(defn load-config [cfg]
  (cond
    (string? cfg)
    (with-open [r (io/reader cfg)]
      (edn/read (PushbackReader. r)))

    (map? cfg) cfg

    :else (throw (ex-info "Unsupported config type" {:given cfg}))))
  • Accepts either a path to EDN or a pre-built map.
  • Encourages configuration-as-data and REPL ergonomics.

Timestamp and output resolution

(defn ^:private timestamp []
  (.format (LocalDateTime/now)
           (DateTimeFormatter/ofPattern "yyyy-MM-dd_HH-mm-ss")))

(defn resolve-output-file
  "Return the actual File to write to.
   If out-file is a directory or has no extension, use <dir>/<yyyy-MM-dd_HH-mm-ss>.md."
  [out-file]
  (let [f (io/file out-file)
        as-dir? (or (.isDirectory f)
                    (not (re-find #"\.[^/\\]+$" (.getName f))))]
    (if as-dir?
      (io/file f (str (timestamp) ".md"))
      f)))
  • If :out-file is a directory or lacks an extension, it auto-generates a timestamped filename, e.g. 2025-08-27_16-30-12.md.

Summary file helper

(defn ^:private summary-file
  "Given the primary output file, return the summary file: <same path> with `_summary.md`."
  ^File [^File final-file]
  (let [parent (.getParentFile final-file)
        name (.getName final-file)
        base (if (re-find #"\.md$" name)
               (subs name 0 (- (count name) 3))
               name)]
    (io/file parent (str base "_summary.md"))))
  • Takes the main report path and returns the companion summary filename (e.g., report.mdreport_summary.md).

The main engine: process-review

(defn process-review
  "If :summary true, performs a second LLM call over the first call's output and writes
   `<previous out-file>_summary.md`."
  [{:keys [patterns model out-file system pre pdf summary]}]
  (let [combined-md (mru/aggregate-md-from-patterns patterns)
        result-1 (agent/call
                   (merge model
                          {:system system
                           :pre    pre
                           :prompt [combined-md]}))
        final-file (resolve-output-file out-file)
        out-1-str (with-out-str (println result-1))]
    ;; write main result
    (io/make-parents final-file)
    (spit final-file out-1-str)

    ;; optional PDF for main result
    (when pdf
      (pyjama.tools.pandoc/md->pdf
        {:input  final-file
         :output (str final-file ".pdf")}))

    ;; optional summary step
    (when summary
      (let [sum-pre "Generated a short summary, (with title and points just like a ppt slide)  of %s"
            result-2 (agent/call
                       (merge model
                              {:system system
                               :pre    sum-pre
                               :prompt [out-1-str]}))
            sum-file (summary-file final-file)]
        (io/make-parents sum-file)
        (spit sum-file (with-out-str (println result-2)))))

    ;; return the path(s) for convenience
    {:out     (.getPath final-file)
     :summary (when summary (.getPath (summary-file final-file)))
     :pdf     (when pdf (str final-file ".pdf"))}))
  • Aggregates input Markdown per :patterns, then calls the LLM once to produce the main report.
  • Writes the main .md, and if :pdf true, renders a PDF via Pandoc.
  • If :summary true, performs a second LLM call on the first output and writes ..._summary.md.
  • Returns a map of produced paths for convenience.

Notes:

Transparent GPT-5 Support in My Clojure ChatGPT Library

🐾 Pyjama, the Ollama/ChatGPT Clojure Wrapper Now Speaks Fluent GPT-5 (and Still Recognizes Cats)

It’s always a good day when your code gets smarter — and an even better day when it can still identify a cute kitten.

With OpenAI rolling out the GPT-5 family (gpt-5, gpt-5-mini, gpt-5-nano), I wanted my Clojure ChatGPT wrapper to transparently support the new models without breaking a sweat.


Calling GPT-5 Is This Simple

Whether you’re sending text or adding vision, the call looks almost the same:

Ollama Clojure Frontend - Breeze

Introduction

Breeze is a small ollama frontend, with few features, expect that it just works. It allows to talk to ollama in realtime, with a really small memory/disk footprint.

../13_01.png

Prerequisites are:

  • Ollama, to run the models
  • Docker, to run this image from dockerhub.

Start the docker image

docker run -it --rm -p 3000:3000 hellonico/breeze

Breeze UI

The setup screen includes:

  • ollama url
  • ollama model
  • system prompt

../13_02.png

ClojureScript and Dynamic!? Re-charts

I simply forgot how easy it was to use ClojureScript for charting.

Someone got in touch with me recently to give them an example on how things are again, so here it is.

The data set is an atom, so that eventually the reagent/react reactive rendering framework can be used as its best.

(defonce data (r/atom [  {:name "Page A" :uv 4000 :pv 2400}
                         {:name "Page B" :uv 3000 :pv 1398}
                         {:name "Page C" :uv 2000 :pv 9800}
                         {:name "Page D" :uv 2780 :pv 3908}
                         {:name "Page E" :uv 1890 :pv 4800}
                         {:name "Page F" :uv 2390 :pv 3800}
                         {:name "Page G" :uv 3490 :pv 4300}]))

The chart is a two lines chart, one for the uv series, and one for the pv series.

What surprising skills do leaders need to innovate with generative AI?

This article from the Guardian, picked up my curiosity.

“What surprising skills do leaders need to innovate with generative AI?”

So I asked my model, to come up with a mindmap summary of the main concepts of the article:

../12_02.png

It’s a bit short on details and underlying concepts.

Asking the same question directly to the model was (not-) surprisingly more verbose and interesting.