1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
# CLI — Design Scratch
> **Status:** IDEATION / scratch. NOT decided, NOT building yet. This is the HOME for the
> CLI design pass (per user: **CLI first, then web frontend**). Promote settled parts into
> `notes/restructure-plan.md` + harness files when we commit to building.
>
> **Read order (fresh agent picking this up):** `ORCHESTRATOR.md` → `AGENTS.md` (the
> backend methodology we are MIRRORING) → `GLOSSARY.md` → this file.
> **Mode = IDEATION WITH the user** (design/discuss, do NOT build yet). The user owns the
> boundary (§5.2) + vocabulary (§5.6) calls.
> **Constraints:** line-oriented CLI (NOT a TUI); may have basic selectors for e.g.
> picking a conversation, but standard/basic stuff only. Same methodology as the backend
> (minimal core, pure-core/inject-effects, typed contracts, one owner per unit,
> asymmetric testing).
---
## 0. Goal
A terminal CLI client for Dispatch: send a message, stream the multi-turn response
(`conversationId`). Interactive: you're in a chat session, type a line, hit enter, see
the streamed response line-by-line.
## 1. Hard constraints (mirroring the backend methodology)
- **Minimal core + feature modules.** Core = message loop + transport client + state
manager; features = output renderers, conversation picker, etc. Core never references
a concrete output mechanism directly — injected.
- **Typed contracts as the only cross-unit surface.** Cross-module coupling anchored to
typed symbols (no string keys). The CLI↔backend seam is the `AgentEvent` union +
`/chat` NDJSON stream — ideally a shared typed contract so `lsp references` spans the
boundary.
- **Pure-core / inject-effects / no ambient state.** Pure functions for every decision
(format output, merge state, parse input), zero I/O; the shell = `readline`/`stdin` +
`fetch` transport + `stdout`/`process.stdout.write` + filesystem — all injected.
- **One owner per unit; asymmetric testing** — strict zero-internal-mock on pure logic;
lenient integration on the shell.
## 2. Open questions (DECIDE in the design pass)
- **Pure-core / shell split:** what's pure (message formatting, state reducer, event
parser) vs. what's the shell (readline, fetch/stream, stdout, ANSI/colour).
- **Unit boundaries / first units:** transport client, message-loop engine, output
renderer, conversation store (persisted across sessions?). Granularity = USER's call.
- **Conversation management:** list recent, pick one, start new — basic selectors (arrow
keys + enter, or numbered list). In-memory vs. a small local store.
- **Output rendering:** streaming is incremental (ProviderEvent deltas → printed as they
arrive); tool calls / thinking — how to render (collapsible? plain text? ANSI
indentation?).
- **Transport:** reuse the same `/chat` NDJSON fetch+ReadableStream path the web FE
would use. `trace-replay` could even feed CLI transport tests hermetically.
- **Persistence:** remember the active `conversationId` across sessions? history? simple
JSON file or the conversation-store extension via HTTP.
- **Testing tools:** vitest for pure logic (already in repo); shell integration tests
via PTY / spawned process? Or keep it thin-integration only (asymmetric).
- **Monorepo placement:** `packages/cli/` — run as `bun packages/cli/src/main.ts`.
- **Harness artifacts:** `.dispatch/rules/cli-*.md`, GLOSSARY terms (no synonym-drift),
ORCHESTRATOR additions for CLI summons.
## 3. Decisions settled
- (none yet — IDEATION.)
|