Getting started¶
The fastest path to a working milk setup — no specific backend required. milk works with any provider in either role; see docs/providers.md for the full catalog once you're past this page.
Prerequisites¶
| Dependency | Required | Notes |
|---|---|---|
| Go 1.21+ | no | only if building from source |
| An inference server or cloud API key | no | any backend from docs/providers.md; milk degrades gracefully if the primary or escalation agent is absent — see docs/operations.md — Graceful degradation |
claude CLI |
no | only if you want Claude Code as an agent; not required otherwise |
| Git Bash or WSL2 (Windows only) | yes (Windows) | milk's primary agent uses sh, find, grep — cmd.exe/PowerShell are not supported. See docs/providers.md — Windows and WSL2 |
Install¶
Pre-built binary — no Go toolchain needed:
curl -fsSL https://raw.githubusercontent.com/scoutme/milk/main/install.sh | sh
Installs to ~/.local/bin/milk, verifying the release checksum. Pin a version with MILK_VERSION=v0.2.0. On Windows, run this inside WSL2 (see docs/providers.md — Windows and WSL2).
Or build from source (requires Go 1.21+):
task build:local # builds ./milk in the current directory
# or, to install to ~/.local/bin:
task build
install-from-source.sh does the equivalent in one line without a manual clone: curl -fsSL https://raw.githubusercontent.com/scoutme/milk/main/install-from-source.sh | sh.
Configure a backend¶
You need at least one working agent to do anything useful — it can serve as primary, escalation, or both. Launch milk with no config and it starts in setup mode:
milk
Add a backend interactively:
/agent add
The wizard asks for a name, URL/model (or claude-cli/aider-cli/subprocess provider), and credentials, and appends the entry to ~/.milk/config.json. Assign it to a role:
/agent switch <name> as primary
/agent switch <name> as escalation
There is no preferred pairing. Every backend, including claude-cli, can serve either role — the only constraint milk expects you to honor yourself is that the escalation agent should be smarter (and usually pricier) than the primary agent. A local model as escalation and a cloud model as primary is a perfectly valid, if unusual, setup. claude-cli as primary specifically works but isn't a smart everyday choice (every turn spawns a full subprocess, and it can't self-escalate); see docs/providers.md for where it's actually useful. See docs/workflows.md for how routing decides which agent handles a given prompt.
If you don't already have a backend in mind, docs/providers.md has copy-paste examples for local llama.cpp/Ollama/LM Studio, Claude Code CLI, AWS Bedrock, OpenRouter, Together.ai, Groq, Azure OpenAI, aider, and smolagents — including a full local-hardware reference setup (NVIDIA GPU, WSL2, llama.cpp from source) if you want to run inference yourself rather than call an API.
Check what's configured any time with /agent (or /agent list).
Your first turn¶
Launch the TUI with no arguments — this is how milk is meant to be used day to day:
milk
You land in an interactive REPL: a ❯ prompt, a scrollable transcript above it, and a status bar showing the current routing state and active agent. Type a prompt and press Enter:
❯ list Go files in the current directory
This routes to the primary agent, which runs its bash tool and returns the result. Keep going in the same session:
❯ now show only the test files
Force a specific agent for one turn with /primary <prompt> or /escalate <prompt>:
❯ /escalate explain the session state machine design
Once escalation fires — whether you asked for it or the primary agent called escalate() on its own — milk keeps subsequent turns on the escalation agent automatically (auto-sticky, shown as <agent> (sticky) in the status bar) until you type /primary. See docs/workflows.md for the full routing and session-state model.
Manage sessions from inside the TUI with /new (start fresh), /drop (delete the current one), and /list (sessions for this directory). Type /help for the full command list, /exit or Ctrl-D to quit.
A realistic session¶
A real (lightly trimmed) transcript from a two-agent setup, showing routing, memory, and escalation in one session:
❯ list the go files under internal/router
mimo-local: [thinking…]
⚙ find_files: internal/router
Here are the Go files under `internal/router`:
- internal/router/router.go
- internal/router/rules.go
- internal/router/rules_test.go
A simple lookup stays on the primary agent, which runs find_files itself — no escalation needed.
❯ /learn this project prefers editing over rewriting files when possible
/learn records the fact as a percept. It shows up in the memory panel (/panel memory) immediately, marked with a star for "just updated":
GLOBAL (core)
★ #5028a1 this project prefers
editing over rewriting files …
❯ /escalate explain the tradeoffs of the router weighted-scorer design versus a pure keyword approach
mimo-pro-local: [thinking…]
⚙ read_file: internal/router/router.go
⚙ read_file: internal/router/rules.go
⚙ read_file: internal/router/rules_test.go
Here's a breakdown of the tradeoffs between the current weighted-scorer design and a pure keyword approach:
## Weighted-Scorer Design (what you have)
...
1. Composability / nuance. A single signal isn't decisive — signals *combine*. "Why should
we evaluate this approach?" fires both open-question (+3) and escalate-verb (+4) = 7,
crossing the escalate threshold of 6. A keyword-only router would need an explicit entry
for every combination or miss the escalation.
...
The escalation agent reads the actual source files rather than answering from assumptions, then gives a genuine multi-point analysis. After this turn, the status bar shows mimo-pro-local (sticky) — auto-sticky kept the session on the escalation agent, so a follow-up question continues the conversation there without you having to type /escalate again. /primary breaks back out whenever you're ready.
Single-prompt mode¶
milk [flags] <prompt> runs one turn non-interactively and exits — useful for scripting or a quick one-off check, but a secondary mode, not how milk is meant to be used day to day:
milk "list Go files in the current directory"
milk --escalate "explain the session state machine design"
milk --primary "grep for TODO comments"
milk --new "start a fresh session"
milk --list
milk --drop
Sessions are shared with the TUI's session model — the same --list/--new/--drop flags here correspond to /list//new//drop there.
Graceful degradation¶
Stop either agent (or never configure one) and confirm milk warns rather than crashing, in either mode — see docs/operations.md — Graceful degradation for the full behavior table.
Where to go next¶
| If you want to… | See |
|---|---|
| Pick and configure a specific backend | docs/providers.md |
Understand routing, sticky escalation, or the native /workflow engine |
docs/workflows.md |
| Give an agent tools, other agents, or MCP servers | docs/tooling.md |
| Tune memory, observability, loop detection, or remote oversight | docs/operations.md |
| Compare agents/models on real scenarios | docs/eval.md |
| Understand why milk is built the way it is | docs/adr/ |
Troubleshooting¶
Session in a bad state: drop it and start fresh — /drop in the TUI, or milk --drop from the shell.
For backend-specific troubleshooting (e.g. local llama.cpp server issues), see the relevant section in docs/providers.md.