Summary
Extract the terminal UI layer (formatting, event handling, text buffering, logging, REPL) into a standalone clemitui crate published on crates.io. This enables any ACP-compatible agent to use the TUI, similar to how Toad works with Claude.
After extraction, clemini becomes an ACP-only agent with no built-in terminal UI.
Motivation
- Reusability: The TUI can work with any ACP agent, not just clemini
- Separation of concerns: Agent logic vs presentation layer
- Ecosystem: Other LLM CLI projects can use clemitui
Approach: Minimal Extraction
Move code as-is with minimal changes. Only refactor coupling points.
What moves to clemitui
| Module |
Description |
format.rs |
TextBuffer, markdown rendering, format_tool_* functions |
events.rs |
EventHandler trait, TerminalEventHandler |
logging.rs |
OutputSink trait, log_event functions |
| REPL loop |
From main.rs, spawns ACP subprocess |
Key refactor
Change format functions to take primitives instead of genai-rs types:
// Before (coupled to genai-rs)
pub fn format_tool_result(result: &FunctionExecutionResult) -> String
// After (standalone)
pub fn format_tool_result(name: &str, duration_ms: u64, tokens: Option<u64>) -> String
Target architecture
clemitui crate (new):
├── format.rs # Pure formatting (no genai-rs types)
├── events.rs # EventHandler trait
├── logging.rs # OutputSink trait
├── text_buffer.rs # TextBuffer for streaming markdown
└── repl.rs # REPL loop (spawns ACP subprocess)
clemini (simplified):
├── agent.rs # Core interaction logic
├── acp.rs # ACP server (becomes default mode)
├── tools/ # Tool implementations
└── main.rs # Starts ACP server
Dependencies
- clemitui will depend on
agent-client-protocol for ACP types
- clemitui will NOT depend on genai-rs
Design decisions
- ACP coupling is intentional: Since clemitui targets ACP agents, depending on ACP types in the public API is correct (ensures protocol compatibility)
- Primitives in format functions: Makes formatting testable and documentation-friendly without requiring protocol knowledge
- YAGNI on abstractions: Deferred introducing
UiMode trait or AcpClient trait until there's concrete need
Tasks
Phase 1: Core extraction (PR #101) ✅
Phase 2: EventHandler extraction (future)
Phase 3: REPL extraction (future)
Phase 4: Publishing (future)
Future considerations
If needed later, these can be added without breaking changes:
UiMode trait for different rendering strategies (TUI library, headless)
AcpClient trait for testing without real subprocess
SessionEvent abstraction to hide ACP types from users
Summary
Extract the terminal UI layer (formatting, event handling, text buffering, logging, REPL) into a standalone
clemituicrate published on crates.io. This enables any ACP-compatible agent to use the TUI, similar to how Toad works with Claude.After extraction, clemini becomes an ACP-only agent with no built-in terminal UI.
Motivation
Approach: Minimal Extraction
Move code as-is with minimal changes. Only refactor coupling points.
What moves to clemitui
format.rsformat_tool_*functionsevents.rslogging.rsKey refactor
Change format functions to take primitives instead of genai-rs types:
Target architecture
Dependencies
agent-client-protocolfor ACP typesDesign decisions
UiModetrait orAcpClienttrait until there's concrete needTasks
Phase 1: Core extraction (PR #101) ✅
format.rsto take primitives instead of genai-rs typesTextBufferto clemituiOutputSinktrait and logging to clemituiPhase 2: EventHandler extraction (future)
EventHandlertrait to clemituiTerminalEventHandlerto clemituiPhase 3: REPL extraction (future)
Phase 4: Publishing (future)
Future considerations
If needed later, these can be added without breaking changes:
UiModetrait for different rendering strategies (TUI library, headless)AcpClienttrait for testing without real subprocessSessionEventabstraction to hide ACP types from users