The dvergr command-line entry point. clojure -M:cli starts the daemon (the
system), an nREPL server on :7888, and — unless --no-tui — a terminal chat
UI over your rooms, built on spindel-tui
dvergr.runtime.bus + dvergr.discourse.llm substrate. The TUI is a frontend:
the same daemon also drives the web dashboard, Telegram, and nREPL clients.# From a dvergr checkout — daemon + nREPL(:7888) + TUI
clojure -M:cli
# Headless: daemon + nREPL only (a server box, or attach a client later)
clojure -M:cli --no-tui
# Server box: daemon + nREPL + web dashboard, no TUI
clojure -M:cli --no-tui --web
| Flag | Default | Meaning |
|---|---|---|
-p, --port PORT | 7888 | nREPL port (written to .nrepl-port for editor/clj-nrepl-eval auto-discovery) |
--nrepl-port-file PATH | .nrepl-port | where to write the nREPL port |
--no-tui | — | run without the TUI (daemon + nREPL only) |
--web | — | also start the web dashboard |
--web-port PORT | 17880 | web dashboard port (with --web) |
--web-bind IP | 127.0.0.1 | web bind address — the UI/API are unauthenticated, so default is loopback. Loopback does not keep out the pages your browser shows, so the server also checks the Host header (DNS rebinding), the Origin of every state-changing request and a per-session CSRF token; a reverse proxy's name and origin go in :http {:allowed-hosts […] :allowed-origins […]} |
--mcp | — | also serve MCP on loopback TCP (clients connect through bin/dvergr-mcp; mcp.md) |
--mcp-port PORT | 17888 | MCP port (with --mcp) |
-h, --help | — | show usage |
Provider, model, and agents are not CLI flags — they come from
config.local.edn (with an env fallback). The shipped default leaves the provider
unpinned and auto-selects the best one you have a key or login for (Anthropic →
Fireworks → OpenAI → native Codex subscription → isolated Codex CLI → the local
claude CLI). See provider-setup.md and
configuration.md.
┌─ rooms ─────────┬─ dvergr · room: scratch · $0.12 / $1.00 · last 8.3s 245tok $0.0012 · ● generating ─┐
│ rooms │ │
│ ──────────── │ you > Refactor src/app.clj │
│ ▸ scratch │ coder> I'll start by reading the file. │
│ plan │ [tool: read_file src/app.clj] │
│ triage │ ... │
│ │ │
│ Ctrl-N: new │ > _ │
└─────────────────┴────────────────────────────────────────────────────────────────────────────────────┘
Enter: send · Ctrl-N: new room · Tab: cycle · Esc: cancel · Ctrl-C/q: quit
▸you >, the agent's coder>; an in-progress reply streams into a draft line until the turn finishes| Key | Action |
|---|---|
| Enter | Send the input line to the active room's agent |
| Ctrl-N | Create a new room and switch to it |
| Tab | Cycle the active room |
| Esc | Cancel the current turn (interrupts the agent's generation) |
| Ctrl-R | Voice input — press to start recording, again to stop; transcribes and posts as a 🎤 message (setup) |
| Ctrl-T / Ctrl-E | Toggle the trace view / per-agent context bars |
| Ctrl-C / q (input empty) | Quit |
| Backspace / printable chars | Edit input |
Rooms, their message history, knowledge, and per-agent budget persist
automatically in Datahike under ./.dvergr/ (the daemon owns the stores). There
is no save/resume step — restart clojure -M:cli and your rooms rehydrate from the
registry + stores, the agent continuing with full conversational state. Forks made
with :isolation :ctx carry their own branched stores until merged or discarded.
(Long conversations are kept in bounds by context compaction — summarize + prune before a turn as the model's window fills; see tools-and-sandbox.md.)
What the TUI talks to are the agents joined to each room, configured in
config.local.edn / resources/agents/*.md — not a CLI flag. Each agent's tool set
is its capability boundary (a :tools allowlist or a role tag like #{:coding});
the default secretary (var) routes and chats. See
rooms-and-agents.md and
tools-and-sandbox.md.
The TUI renders each room from the live :room-messages signal (a fold of the room
bus) plus per-chat stats. The header reflects:
:budget-signal + dvergr.rooms.stats● generating / ○ idle / ✕ error(:partial/token streaming and :telemetry/* turn events are bus primitives — see
examples/scenario_streaming_partial.clj — not currently wired into the header.)
External consumers (the web dashboard, a billing system, a logging agent) subscribe
to the same bus channels.
Agent doesn't reply — make sure a provider is configured (a key in env or
config.local.edn). Reply errors print to stderr; the daemon log is .dvergr/dvergr.log.
List registered models with (dvergr.model.registry/list-models) over the nREPL.
Garbled output / Unicode issues — the terminal needs UTF-8 + 256-color. The TUI
uses JLine; in an unusual terminal set TERM=xterm-256color.
Tab doesn't cycle rooms — some terminals map Tab to focus-change; Ctrl-Tab is also wired.
.dvergr/ state layoutCan you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |