Skip to content

Commit ace339a

Browse files
evansenterclaude
andauthored
feat: extract TUI layer into standalone clemitui crate (#101)
* feat: extract TUI layer into standalone clemitui crate (#100) Extract formatting, logging, and text buffering into a reusable clemitui crate that can be used by any ACP-compatible AI agent. Changes: - Create crates/clemitui/ with format.rs, logging.rs, text_buffer.rs - Refactor clemini to re-export from clemitui, keeping genai-rs wrappers - Update CLAUDE.md with workspace structure documentation - Fix -p mode hang by explicitly dropping events_guard before await The new crate takes primitive types (strings, durations, token counts) instead of genai-rs types, enabling reuse without model-specific deps. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: disambiguate doc link for format module Use `mod@format` to distinguish from the built-in `format!` macro. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore: address review feedback - Remove unused agent-client-protocol dependency from clemitui - Update issue #100 to track extraction phases Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(clemitui): remove unused tokio and tracing dependencies These dependencies were never used in clemitui: - tokio: logging.rs uses std::sync::RwLock, not tokio::sync - tracing: no warn!/info!/debug! macros used anywhere Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(clemitui): address audit findings API improvements: - Make SKIN and render_markdown_nowrap pub(crate) - internal only - Check LOGGING_DISABLED flag in log_event()/log_event_line() - Add enable_logging() for test state reset Test improvements: - Add boundary tests for string truncation (79/80/81 chars) - Add edge cases for estimate_tokens (null, empty, large) - Strengthen render_markdown_nowrap test (verify ANSI codes) - Add plain text and header tests for markdown rendering Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(tests): make test_edit_error_recovery deterministic The test was flaky because: 1. Model behavior varied (different case handling approaches) 2. Validator interpretation of "appropriate" was subjective Fix: - Add deterministic check: verify file contains "Goodbye" (edit happened) - Use lenient semantic check: "Did model attempt the edit?" (not "was case handling appropriate?") This separates "did it work" (deterministic) from "did it communicate" (lenient), eliminating the subjective judgment that caused flakiness. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * docs: remove outdated clemitui dependency comment The comment referenced agent-client-protocol which was removed. The Design note below already explains the genai-rs separation. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 94c9688 commit ace339a

11 files changed

Lines changed: 1126 additions & 841 deletions

File tree

CLAUDE.md

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,19 @@ Logs are stored in `~/.clemini/logs/` with daily rotation.
3333

3434
The CLI has three modes: single-prompt (`-p "prompt"`), interactive REPL, and MCP server (`--mcp-server`).
3535

36-
### Module Structure
36+
### Workspace Structure
37+
38+
This project is a Cargo workspace with two crates:
39+
40+
```
41+
.
42+
├── Cargo.toml # Workspace root
43+
├── src/ # clemini crate (AI agent)
44+
└── crates/
45+
└── clemitui/ # TUI library crate (reusable by any ACP agent)
46+
```
47+
48+
#### clemini (AI Agent)
3749

3850
```
3951
src/
@@ -45,14 +57,34 @@ src/
4557
├── diff.rs # Diff formatting for edit tool output
4658
├── event_bus.rs # Cross-session event bus (SQLite-backed)
4759
├── events.rs # EventHandler trait, TerminalEventHandler
48-
├── format.rs # Pure formatting functions, TextBuffer, markdown rendering
49-
├── logging.rs # OutputSink trait, log_event functions
60+
├── format.rs # Re-exports clemitui + genai-rs-specific formatters
61+
├── logging.rs # Re-exports clemitui::logging
5062
├── mcp.rs # MCP server implementation
5163
├── plan.rs # Plan mode manager
5264
├── system_prompt.md # System prompt for Gemini (included at compile time)
53-
└── tools/ # Tool implementations (bash, read_file, etc.)
65+
└── tools/ # Tool implementations
66+
├── mod.rs # CleminiToolService, ToolEmitter trait, EventsGuard
67+
├── tasks.rs # Unified task registry (Task enum, namespaced IDs)
68+
├── bash/ # BashTool (mod.rs) + safety patterns (safety.rs)
69+
└── ... # Individual tool modules (edit, read, grep, etc.)
5470
```
5571

72+
#### clemitui (TUI Library)
73+
74+
Standalone crate for terminal UI, usable by any ACP-compatible agent:
75+
76+
```
77+
crates/clemitui/
78+
├── Cargo.toml
79+
└── src/
80+
├── lib.rs # Re-exports
81+
├── format.rs # Primitive formatting functions (tool output, warnings)
82+
├── logging.rs # OutputSink trait, log_event functions
83+
└── text_buffer.rs # TextBuffer for streaming markdown
84+
```
85+
86+
**Design**: clemitui takes primitive types (strings, durations, token counts), not genai-rs types. This allows it to work with any ACP agent. clemini's format.rs re-exports these and adds genai-rs-specific wrappers.
87+
5688
### Event-Driven Architecture
5789

5890
The agent (`src/agent.rs`) is decoupled from UI via channel-based events:
@@ -162,6 +194,8 @@ If you're unsure whether coverage is sufficient, add more tests. Undertesting ca
162194
- `confirmation_tests.rs` - Confirmation flow for destructive commands
163195
- `tool_output_tests.rs` - Tool output events and model interpretation
164196
- `semantic_integration_tests.rs` - Multi-turn state, error recovery, code analysis
197+
- `acp_integration_tests.rs` - ACP subagent spawning and communication
198+
- `background_tasks_tests.rs` - Background task execution and output retrieval
165199
- `event_ordering_tests.rs` - Tool output event ordering (no API key required)
166200

167201
Run locally with: `cargo test --test <name> -- --include-ignored --nocapture`

Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
[workspace]
2+
members = [".", "crates/clemitui"]
3+
resolver = "3"
4+
15
[package]
26
name = "clemini"
37
version = "0.2.0"
@@ -7,6 +11,8 @@ description = "A Gemini-powered coding CLI, built with genai-rs"
711
license = "MIT"
812

913
[dependencies]
14+
# Internal crates
15+
clemitui = { path = "crates/clemitui" }
1016
# AI
1117
genai-rs = "0.7.2"
1218

crates/clemitui/Cargo.toml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
[package]
2+
name = "clemitui"
3+
version = "0.1.0"
4+
edition = "2024"
5+
rust-version = "1.88"
6+
description = "Terminal UI for ACP-compatible AI agents"
7+
license = "MIT"
8+
repository = "https://github.com/evansenter/clemini"
9+
keywords = ["cli", "tui", "ai", "llm", "acp"]
10+
categories = ["command-line-interface", "text-processing"]
11+
12+
[dependencies]
13+
# Terminal formatting
14+
colored = "2"
15+
termimad = "0.30"
16+
17+
# Serialization (for tool args formatting)
18+
serde_json = "1"
19+
20+
[dev-dependencies]
21+
tempfile = "3.10"

0 commit comments

Comments
 (0)