63 lines
3.1 KiB
Markdown
63 lines
3.1 KiB
Markdown
# CLAUDE.md
|
|
|
|
Rust TUI coding agent. Ratatui + Crossterm + Tokio. See DESIGN.md for architecture decisions and PLAN.md for implementation phases.
|
|
|
|
## Commands
|
|
|
|
- `cargo build`: Build the project
|
|
- `cargo test`: Run all unit and integration tests
|
|
- `cargo test --lib`: Unit tests only
|
|
- `cargo test --test '*'`: Integration tests only
|
|
- `cargo clippy -- -D warnings`: Lint (must pass with zero warnings)
|
|
- `cargo fmt --check`: Format check
|
|
- `cargo run -- --project-dir <path>`: Run against a project directory
|
|
|
|
## Architecture
|
|
|
|
Six modules with strict boundaries:
|
|
|
|
- `src/app/` — Wiring, lifecycle, tokio runtime setup
|
|
- `src/tui/` — Ratatui rendering, input handling, vim modes. Communicates with core ONLY via channels (`UserAction` → core, `UIEvent` ← core). Never touches conversation state directly.
|
|
- `src/core/` — Conversation tree, orchestrator loop, sub-agent lifecycle
|
|
- `src/provider/` — `ModelProvider` trait + Claude implementation. Leaf module, no internal dependencies.
|
|
- `src/tools/` — `Tool` trait, registry, built-in tools. Depends only on `sandbox`.
|
|
- `src/sandbox/` — Landlock policy, path validation, command execution. Leaf module.
|
|
- `src/session/` — JSONL logging, session read/write. Leaf module.
|
|
|
|
The channel boundary between `tui` and `core` is critical — never bypass it. The TUI is a frontend; core is the engine. This separation enables headless mode for benchmarking.
|
|
|
|
## Code Style
|
|
|
|
- Use `thiserror` for error types, not `anyhow` in library code (`anyhow` only in `main.rs`/`app`)
|
|
- Prefer `impl Trait` return types over boxing when possible
|
|
- All public types need doc comments
|
|
- No `unwrap()` in non-test code — use `?` or explicit error handling
|
|
- Async functions should be cancel-safe where possible
|
|
- Use `tracing` for structured logging, not `println!` or `log`
|
|
|
|
## Conversation Data Model
|
|
|
|
Events use parent IDs forming a tree (not a flat list). This enables future branching. Every event has: id, parent_id, timestamp, event_type, token_usage. A "turn" is all events between two user messages — this is the unit for token tracking.
|
|
|
|
## Testing
|
|
|
|
- Unit tests go in the same file as the code (`#[cfg(test)] mod tests`)
|
|
- Integration tests go in `tests/`
|
|
- TUI widget tests use `ratatui::backend::TestBackend` + `insta` snapshots
|
|
- Provider tests replay recorded SSE fixtures from `tests/fixtures/`
|
|
- Sandbox tests use `tempdir` and skip Landlock-specific assertions if kernel < 5.13
|
|
- Run `cargo test` before every commit
|
|
|
|
## Key Constraints
|
|
|
|
- All file I/O and process spawning in tools MUST go through `Sandbox` — never use `std::fs` or `std::process::Command` directly in tool implementations
|
|
- The `ModelProvider` trait must remain provider-agnostic — no Claude-specific types in the trait interface
|
|
- Session JSONL is append-only. Never rewrite history. Branching works by writing new events with different parent IDs.
|
|
- Token usage must be tracked per-event and aggregatable per-turn
|
|
|
|
## Do Not
|
|
|
|
- Add MCP support (deferred, but keep tool trait compatible)
|
|
- Use `unsafe` without discussion
|
|
- Add dependencies without checking if an existing dep covers the use case
|
|
- Modify test fixtures without re-recording from a real API session
|