Skip to content

Extract TUI layer into standalone clemitui crate #100

Description

@evansenter

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) ✅

  • Create clemitui crate structure
  • Refactor format.rs to take primitives instead of genai-rs types
  • Move TextBuffer to clemitui
  • Move OutputSink trait and logging to clemitui
  • Update documentation

Phase 2: EventHandler extraction (future)

  • Move EventHandler trait to clemitui
  • Move TerminalEventHandler to clemitui

Phase 3: REPL extraction (future)

  • Extract REPL loop with ACP subprocess spawning
  • Update clemini to be ACP-only (remove terminal UI code)

Phase 4: Publishing (future)

  • Add clemitui to crates.io

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions