Liking cljdoc? Tell your friends :D

hive-dirge

ci release Clojars Project License: MIT

The hive side of the dirge integration. It holds:

  • hive.dirge, the hive-mcp harness as dirge slash commands (see below);
  • portable (.cljc) IAddons that dirge loads under clojurust (cljrs), written against the hive-addon IAddon contract, which also load under JVM Clojure;
  • hive.dirge.host: the JVM vessel target :dirge (hive-vessel SSE bridge, 0600 discovery file $XDG_RUNTIME_DIR/hive-vessel/dirge.json, /reply key routing to hive.olympus) and the hive.olympus.dirge harness manifest.

It depends on hive-addon (the contract), hive-dsl, hive-vessel and hive-olympus (for the harness init-ns). It never requires hive-mcp.*: addons depend on the contract, never on a host.

hive.dirge: hive from dirge's command line

With this repo installed as a dirge addon and hive-mcp configured as a dirge MCP server (mcp_servers.hive in dirge's config.json), dirge gains:

CommandWhat it does
/hive catchupruns the project catchup and hands it to the model as the next prompt
/hive wraprecords a hive session wrap for this project
/hive kanban [todo\|inprogress\|inreview\|done]this project's tasks in a side panel
/hive memory <query>semantic search over hive memory, hits in the chat
/hive swarm [all\|project\|<project-id>]hive agents and their status, working ones first, in a side panel
/hive shout <message>posts progress to the hivemind

The model also gets two tools, hive_memory_search and hive_kanban_list, and a system-prompt note naming the commands. Everything calls hive-mcp through dirge's own MCP connection (dirge.harness/mcp-call), so a command costs no model turn. The server name comes from :hive/server in the manifest's :addon/config (default "hive").

Install: symlink this checkout into dirge's addon directory, then run /addons reload in dirge (or restart it).

ln -s "$PWD" ~/.config/dirge/addons/hive-dirge

hive.dirge.host: hive senses in dirge's agent loop

MCP piggyback blocks reach the dirge model only inside the result of the next hive tool call, as text the model may or may not act on. The sense relay is the push path: it changes the control flow of the running loop.

hive-agent's sixth-sense turns hivemind shouts into senses: a ling asks, is blocked, failed, completed, or ran out of context. The host listens on sixth-sense (hive-agent.sixth-sense.api/listen!), drains a consume-once batch for consumer dirge, and sends each sense as one loop op on the SSE feed dirge already subscribes to:

opwhat dirge doesdefault for
loop/steerinjects it before the next model call of the running turn, or starts a turn when idleask, blocked, error
loop/interjectends the running turn at its next boundary; the message opens the next onecontext-death
loop/followupdelivers it when the current run finishes, or starts a run when idlecompleted

Each op carries prompt, the text the model reads, including how to answer (swarm ss reply to the ask id or agent id). dirge acknowledges every op it injected with the reply {"action":"ack","target":<sense id>}. Unacknowledged ops are sent again when a client reconnects. Nothing is drained while no connected client subscribed with the loop feature, so the senses stay in sixth-sense (persisted) until one does.

Config on hive.dirge.host: :dirge/senses? (default true), :dirge/sense-policy (for example {"completed" "steer", "error" "ignore"}) and :dirge/sense-receptor (a sixth-sense receptor, for example {:parent "coordinator"}).

Checking the /swarm setup: clojure -M:doctor

/swarm in dirge stays empty until both of these are done:

  1. hive-mcp mounted hive.dirge.host (hive-dirge is on its classpath), which writes $XDG_RUNTIME_DIR/hive-vessel/dirge.json;
  2. dirge's config.json has "panel_feed": {"discovery_dir": "hive-vessel"}.

The doctor checks both and prints a fix for each step that fails:

clojure -M:doctor
clojure -M:doctor --discovery PATH --dirge-config PATH
hive-dirge doctor: /swarm setup
  [ok] step 1: hive.dirge.host is mounted: $XDG_RUNTIME_DIR/hive-vessel/dirge.json -> http://127.0.0.1:4100/vessel
  [FAIL] step 2: ~/.config/dirge/config.json has no panel_feed
         fix: add { "panel_feed": { "discovery_dir": "hive-vessel" } } to dirge's config.json, then restart dirge
setup incomplete

Step 1 passes only when the discovery file is for vessel dirge, its pid is running and its port accepts connections. A leftover file from a hive-mcp that has exited fails as stale. When the file is missing, the doctor looks for running hive-mcp JVMs and reports whether any of them has hive-dirge on its classpath. It exits 0 when both steps pass and 1 otherwise.

Layout

.hive-project.edn                      project-id hive-dirge, parent hive
deps.edn                               clojure + hive-addon + hive-dsl; :dev, :test, :build
version.edn, VERSION                   hive-build release config (:publish :clojars)
src/hive_dirge/hive/addon.cljc         hive.dirge IAddon (record HiveDirgeAddon): /hive, tools, effects via a ports map
src/hive_dirge/hive/domain.cljc        pure: config, /hive parsing, MCP requests, answers -> text and panels
src/hive_dirge/harness.cljc            dirge.harness from portable code (notify, mcp-call, panel!, refresh!, ...)
src/hive_dirge/live.cljc               live addon instances + tools/hooks added from the REPL (defonce atoms)
src/hive_dirge/dev.cljc                REPL helpers: inspect, add-tool!/add-hook!, refresh!
src/hive_dirge/probe/addon.cljc        probe IAddon (record DirgeProbeAddon, ctor addon-ctor)
src/hive_dirge/economy/addon.cljc      hive.dirge.economy IAddon: hooks + context_retrieve, wiring only
src/hive_dirge/economy/ports.cljc      IObservationLog, IObservationIndex, IDigestor
src/hive_dirge/economy/{domain,digest,markdown}.cljc
                                       pure: Observation/Handle, Digest build + budget fit, markdown render/parse
src/hive_dirge/economy/pipeline/       watch (:dirge/event), observe, retrieve (tool), compact (compact hooks)
src/hive_dirge/economy/adapters/       local observation log, structured digestor
src/hive_dirge/economy/registry.cljc   strategy registry selected by :addon/config
src/hive_dirge/host.clj                hive.dirge.host IAddon (JVM); host/{domain,ports,boundary}.clj strata
src/hive_dirge/doctor.clj              clojure -M:doctor: gathers facts through a ports map
src/hive_dirge/doctor/domain.clj       pure: the two /swarm setup checks, report, render
test/hive_dirge/host_test.clj          discovery 0600, token/Origin, reply routing, SSE frames, mount e2e
resources/META-INF/hive-addons/
  hive-dirge.edn                       :addon/id "hive.dirge"
  hive-dirge-probe.edn                 mount manifest, :addon/id "hive.dirge.probe"
  hive-dirge-host.edn                  :addon/id "hive.dirge.host"
  hive-dirge-economy.edn               :addon/id "hive.dirge.economy"
  hive-olympus-dirge.edn               olympus harness, host hive.dirge.host
test/hive_dirge/probe/addon_test.clj   manifest -> ctor -> IAddon -> lifecycle
test/fixtures/probe/                   hot-reload fixtures (v1 / v2 of probe.addon)
rescue/                                cljrs spike material, kept as found

How dirge discovers addons

dirge scans .dirge/addons/ and ~/.config/dirge/addons/ for META-INF/hive-addons/*.edn (and META-INF/addons/*.edn). Every manifest names :addon/init-ns and :addon/init-fn. dirge requires the namespace under cljrs, calls the constructor with :addon/config, and then drives the IAddon lifecycle (initialize!, tools, health, shutdown!). A JVM host does the same thing from the classpath (hive-addon.mount.boundary/discover-specs). The same manifest is used for both.

To install this repo's addon into a dirge workspace, put (or symlink) src/ and resources/ under .dirge/addons/hive-dirge/.

The hive swarm in dirge (/swarm)

dirge's /swarm grid shows hive's lings when hive-mcp runs hive.dirge.host and dirge subscribes to it. Both sides need one setup step. If either is missing, /swarm stays empty while hive.olympus still lists the lings.

  1. hive-mcp: put hive-dirge on its classpath. hive.dirge.host and the hive.olympus.dirge harness are JVM addons that hive-mcp mounts from its classpath. Add this repo to hive-mcp's local.deps.edn:

    {:deps {io.github.hive-agi/hive-dirge {:local/root "../hive-dirge"}}}
    

    Or mount it into a running hive-mcp without a restart, with hive's hot tool: inject path=/path/to/hive-dirge resolve_deps=false. Pass resolve_deps=false when hive-vessel, hive-olympus and hive-addon are already local roots of hive-mcp, because this repo's deps.edn pins their released versions. Once mounted, the host writes its discovery file $XDG_RUNTIME_DIR/hive-vessel/dirge.json.

  2. dirge: subscribe to the feed. Add this to ~/.config/dirge/config.json:

    { "panel_feed": { "discovery_dir": "hive-vessel" } }
    

    dirge reads panel_feed only at startup, so restart it after the change. A later change of port or token in the discovery file is picked up live. See dirge's docs/panel-feed.md.

To find the missing step, check in this order:

  • ls $XDG_RUNTIME_DIR/hive-vessel/: no dirge.json means step 1;
  • hive-mcp's classpath (/proc/<pid>/cmdline) does not name hive-dirge: step 1;
  • dirge's config has no panel_feed: step 2.

One log line is expected and does not mean a failure. When this whole checkout is symlinked into ~/.config/dirge/addons/, dirge's own addon host logs at DEBUG skipped: no .cljc/.cljrs source for the init namespace for hive-dirge.host and hive-olympus.harness. Those manifests are JVM-only addons for hive-mcp, not for dirge's cljrs interpreter.

hive.dirge session hooks

hive.dirge (manifest hive-dirge.edn) registers two dirge hooks, available from the dirge release "dirge addon session hooks" (older dirge ignores them; /hive catchup and /hive wrap still work by hand):

  • :dirge/session-start runs hive workflow catchup for the session cwd and injects the result into the first turn, bounded by :hive/max-context-chars (default 12000, truncated with a marker). Skipped when :hive/auto-catchup? is false or the :hive/server MCP server is not connected.
  • :dirge/session-end runs hive session wrap when :hive/auto-wrap? is on and the session ends by :exit; a :swap wraps only with :hive/wrap-on-swap? true.

hive.dirge.economy: context economy hooks

hive.dirge.economy (manifest hive-dirge-economy.edn) keeps a session's context bounded without losing what was folded away:

  • :dirge/event only watches. A :tool-call and its :tool-result (paired by :id) are logged as one result under a short content handle (§1a2b3c4d), in memory and in .dirge/economy/<session>.edn. Turn, usage, run and compaction events are counted into the addon's health details. The context_retrieve tool reads a handle back, either whole or as a line or char range. The hook answers nil.

  • :dirge/compact receives {:span [{:role :text :tool :tool-use-id} ...] :tokens :reason :focus :ctx-max :pressure :session-id} and answers {:summary markdown}, or nil so that dirge's built-in summarizer runs. The summary is a digest that uses dirge's own summary section names. It opens with a REFERENCE-ONLY line and holds:

    • Active Task: the latest user message, verbatim.
    • Goal: the first task statement, verbatim.
    • Completed Actions: a numbered, past-tense list (E<epoch>.<step>).
    • Relevant Files, Key Decisions, and errors (under Critical Context).
    • Remaining Work: the TODO/checkbox lines in their latest state.
    • Source Coverage: one citation per folded tool result, with its context_retrieve §handle hint.

    A later fold copies an earlier digest's lines and citations forward instead of summarizing it again, so handles stay valid across compactions. The digest fits a budget of :economy/digest-ratio (0.2) times the span's tokens, clamped to :economy/digest-min-tokens (400) and :economy/digest-max-tokens (3000), at about 4 chars per token. When it cannot fit, it answers nil.

  • :dirge/before-compact only observes: fold count, tokens and the highest pressure show up in the addon's health details.

The digestor is picked from a strategy registry by :economy/digestor in :addon/config (default :structured, which makes no model call). Adding a strategy means adding an entry to that map.

Developing from the REPL

Both addons build their tools and hooks on every call, from vars and from hive-dirge.live, and read their harness ports at call time. Re-evaluating a defn and then asking dirge to refresh is enough; no re-initialize is needed.

(require '[hive-dirge.dev :as dev])
(dev/inspect)                                   ; every live addon: tools, hooks, health, extras
(dev/add-hook! "hive.dirge" :dirge/on-prompt (fn [_] "hi"))
(dev/add-tool! "hive.dirge.economy" {:name "probe" :description "p" :inputSchema {} :handler (fn [_] "ok")})
(dev/reset-extras!)
(dev/refresh!)                                  ; true inside dirge, false elsewhere

Portability rules for addon code

  • One .cljc file with no host interop and no reader conditionals, unless it really needs them.
  • Give every record a globally unique name. cljrs keys protocol impls by the unqualified record name, so two namespaces that each define Addon would overwrite each other's IAddon impl.
  • The constructor is pure (config -> addon). State lives in an atom that initialize! and shutdown! reset.

Running

clojure -M:test                        # JVM suite (cognitect test-runner)
clojure -M:dev                         # against the sibling ../hive-addon checkout

# cljrs smoke: a main.cljc that requires hive-dirge.probe.addon
cljrs run --src-path src --src-path ../hive-addon/src main.cljc

clojure -T:build jar                   # local jar, as the release builds it
clojure -T:build verify-license        # LICENSE vs version.edn vs SPDX headers

Releases

Published to Clojars as io.github.hive-agi/hive-dirge through hive-build:

io.github.hive-agi/hive-dirge {:mvn/version "RELEASE"}

A push to main that changes src/, resources/, test/, deps.edn, version.edn or the workflows runs .github/workflows/release.yml: the suite gates the release, then clojure -T:build bump :level :patch, the changelog, an annotated v<version> tag and clojure -T:build deploy. README-only pushes do not mint a version, because a published pom is immutable. A push to staging runs staging-gate.yml (the suite on the declared classpath) and never publishes.

License

MIT. Copyright (C) 2026 Pedro Gomes Branquinho (BuddhiLW). See LICENSE.

Can you improve this documentation? These fine people already did:
Pedro Gomes Branquinho & blw
Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
←Move to previous article
→Move to next article
Ctrl+/Jump to the search field
× close