ADR-0010: Interactive REPL mode¶
Status¶
Superseded by ADR-0014
Context¶
milk was initially single-prompt only: one invocation, one response, exit. For iterative work (debugging sessions, multi-step code edits) this creates friction — the user must retype flags and wait for session load on every turn. An interactive mode removes that overhead.
Decision¶
milk with no prompt argument enters a REPL loop. Key design choices:
Readline library: chzyer/readline over peterh/liner¶
liner was the first choice (minimal dependency) but it rejects ANSI escape codes in prompt strings via a control-character validation step, making colored prompts impossible without pre-printing the label and calling Prompt("") — which breaks line redraw on backspace.
chzyer/readline accepts ANSI codes natively in the prompt string and redraws them correctly on every keystroke. It also exposes a Painter interface for colorizing the input buffer as the user types.
Prompt label reflects routing state¶
The prompt label ([local] >, [claude] >, [claude:waiting] >) uses the same color scheme as the agent speaker labels on output — green for local, blue for Claude, yellow for claude:waiting. This makes the current state visible without extra status lines.
Slash commands for in-session control¶
Force-mode flags (--escalate, --local) become /escalate and /primary slash commands (/local was renamed to /primary in ADR-0030). Other commands: /new, /drop, /list, /help, /exit. Slash prefix is conventional for chat-style REPLs and avoids ambiguity with prompt text.
Tab completion¶
/prefix: completes slash commands@prefix: completes file paths from cwd usingos.ReadDir
The @path token is passed as-is in the prompt text to the agent rather than being expanded — the agent resolves it. This keeps the completion lightweight (no file reads at completion time) and preserves the original intent in session history.
Painter: colorize tokens as typed¶
milkPainter.Paint colorizes /commands in yellow and @paths in dim as the user types, using the Painter hook in readline's Config. This provides visual feedback without altering the submitted string.
Ctrl-C / Ctrl-D behaviour¶
- Ctrl-C (
ErrInterrupt): if a force-mode flag is active (/escalateor/local), clears it and stays in the session; otherwise exits - Ctrl-D (
io.EOF): always exits
Consequences¶
- The readline dependency adds ~400 KB to the binary.
- Completion candidates cannot be individually colorized — readline's candidate display loop is hardcoded (not hookable without forking).
- The
@pathpass-through means the local model must understand the@convention; this is documented in the system prompt context passed on escalation.