# tuicr - Terminal UI Code Review Tool
## Overview
A Rust terminal UI for reviewing code changes made by coding agents. Features vim keybindings, side-by-side diffs, file/line comments, and markdown output for feeding instructions back to agents.
## Requirements Summary
- **Input**: Git working tree changes (`git diff HEAD`)
- **Comments**: Both file-level and line-level
- **File Status**: Binary (reviewed / not reviewed)
- **Output**: Structured Markdown optimized for LLM consumption
- **Navigation**: Vim keybindings
- **Diff View**: Side-by-side (primary)
- **Persistence**: JSON files on disk
---
## Technology Stack
```toml
[dependencies]
ratatui = "0.29" # TUI framework
crossterm = "0.29" # Terminal backend
git2 = "0.19" # Git operations
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
chrono = { version = "0.4", features = ["serde"] }
thiserror = "2.0"
anyhow = "1.0"
directories = "5.0" # XDG paths
unicode-width = "0.2"
uuid = { version = "1.0", features = ["v4"] }
```
---
## Module Structure
```
src/
├── main.rs # Entry point, CLI args, main loop
├── app.rs # Application state
├── error.rs # Error types
│
├── git/
│ ├── mod.rs
│ ├── diff.rs # Diff parsing via git2
│ └── repository.rs # Repo discovery
│
├── model/
│ ├── mod.rs
│ ├── review.rs # ReviewSession, FileReview
│ ├── comment.rs # Comment, CommentType
│ └── diff_types.rs # DiffFile, DiffHunk, DiffLine
│
├── ui/
│ ├── mod.rs
│ ├── app_layout.rs # Main layout
│ ├── file_list.rs # File list panel
│ ├── diff_view.rs # Side-by-side diff
│ ├── comment_panel.rs # Comments display/input
│ ├── status_bar.rs # Header and status
│ ├── help_popup.rs # Keybinding help
│ └── styles.rs # Colors and styling
│
├── input/
│ ├── mod.rs
│ ├── handler.rs # Event processing
│ ├── keybindings.rs # Key -> Action mapping
│ └── mode.rs # Normal, Comment, Search modes
│
├── persistence/
│ ├── mod.rs
│ └── storage.rs # Save/load sessions
│
└── output/
├── mod.rs
└── markdown.rs # Export to markdown
```
---
## Core Data Model
### ReviewSession
```rust
pub struct ReviewSession {
pub id: String,
pub repo_path: PathBuf,
pub base_commit: String,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
pub files: HashMap<PathBuf, FileReview>,
pub session_notes: Option<String>,
}
```
### FileReview
```rust
pub struct FileReview {
pub path: PathBuf,
pub reviewed: bool,
pub file_comments: Vec<Comment>,
pub line_comments: HashMap<u32, Vec<Comment>>,
pub status: FileStatus, // Added, Modified, Deleted, Renamed
}
```
### Comment
```rust
pub struct Comment {
pub id: String,
pub content: String,
pub comment_type: CommentType, // Note, Suggestion, Issue, Praise
pub created_at: DateTime<Utc>,
pub line_context: Option<LineContext>,
}
```
---
## UI Layout
```
+------------------------------------------------------------------+
| FILES | ═══ src/main.rs [M] ══════════════════════ |
| [x] src/main.rs | OLD (HEAD) │ NEW (working) |
| [ ] src/lib.rs | 1 fn main() { │ 1 fn main() { |
| ▶[ ] src/app.rs | 2 let x = 1; │ 2 let x = 42; |
| | 3 } │ 3 } |
| | ─────────────────────────────────────────────|
| | 💬 Line 2: [ISSUE] Magic number... |
| | ═══ src/lib.rs [M] ═══════════════════════ |
| | OLD (HEAD) │ NEW (working) |
| | 1 pub fn foo() {} │ 1 pub fn foo() { |
| | │ 2 bar(); |
| | │ 3 } |
+------------------+-----------------------------------------------+
| [N]ormal j/k:scroll Enter:jump r:reviewed c:comment ?:help |
+------------------------------------------------------------------+
```
### Infinite Scroll Design (GitHub-style)
- **Diff panel**: Shows ALL changed files in one continuous scroll
- Each file has a header separator (`═══ filename [status] ═══`)
- Side-by-side diff for each file
- Inline comments displayed below relevant lines
- Scroll through entire review with `j/k` or `Ctrl-d/u`
- **File list panel**: Quick navigation aid (not selection)
- Shows review status `[x]/[ ]` for each file
- `▶` indicator shows which file is currently visible in diff
- `Enter` on file list jumps to that file in the scroll
- File list auto-highlights based on scroll position
- **Layout proportions**:
- File list: 20% width (collapsible with `h`)
- Diff view: 80% width (full width when file list collapsed)
---
## Vim Keybindings
### Navigation (Infinite Scroll)
| `j/k` | Scroll diff down/up one line |
| `Ctrl-d/u` | Half page down/up |
| `Ctrl-f/b` | Full page down/up |
| `gg/G` | Go to first/last file |
| `{/}` | Jump to previous/next file header |
| `[c/]c` | Jump to previous/next comment |
| `[[/]]` | Jump to previous/next hunk |
### File List Navigation
| `Tab` | Toggle focus between file list and diff |
| `Enter` | Jump to selected file in diff (when in file list) |
| `h` | Collapse file list panel |
| `l` | Expand file list panel / focus diff |
### Actions
| `r` | Toggle current file reviewed |
| `c` | Add line comment at cursor |
| `C` | Add file comment for current file |
| `e` | Edit comment under cursor |
| `dd` | Delete comment under cursor |
| `1-4` | Set comment type (1=Note, 2=Suggestion, 3=Issue, 4=Praise) |
### Session
| `:w` | Save session |
| `:e` | Reload diff files |
| `:clip` (`:export`) | Export markdown |
| `:q` | Quit |
| `:wq` / `ZZ` | Save and quit |
| `?` | Show help |
---
## Persistence
**Location**: `~/.local/share/tuicr/reviews/` (XDG compliant)
**Filename**: `{repo}_{commit}_{timestamp}.json`
Auto-saves every 60 seconds and on file navigation.
---
## Markdown Output Format
```markdown
# Code Review: {repo_name}
**Reviewed:** {timestamp}
**Base Commit:** `{commit_sha}`
**Files Reviewed:** {reviewed}/{total}
## Summary
{session_notes}
## Files
### M `src/auth.rs` [REVIEWED]
#### File Comments
> **[SUGGESTION]** Consider adding unit tests
#### Line Comments
```rust
// Line 42
let expiry = now + 3600;
```
> **[ISSUE]** Magic number should be a named constant
---
## Action Items
1. **`src/auth.rs`:42** - Magic number should be a named constant
2. **`src/auth.rs`:15** - Use `?` operator instead of unwrap
```
---
## Implementation Order
### Phase 0: Overview
1. Setup README.md
2. Setup AGENTS.md
### Phase 1: Foundation
1. Set up Cargo.toml with dependencies
2. Create module structure (empty mod.rs files)
3. Implement `model/` structs with serde derives
4. Implement `error.rs` with thiserror
### Phase 2: Git Integration
5. Implement `git/repository.rs` - repo discovery
6. Implement `git/diff.rs` - parse `git diff HEAD` into DiffFile structs
### Phase 3: Persistence
7. Implement `persistence/storage.rs` - save/load JSON sessions
### Phase 4: TUI Core
8. Implement `app.rs` - application state container
9. Implement `input/mode.rs` and `input/keybindings.rs`
10. Implement `ui/styles.rs` - color definitions
11. Implement `ui/status_bar.rs` - header and footer
### Phase 5: TUI Widgets
12. Implement `ui/file_list.rs` - file list panel
13. Implement `ui/diff_view.rs` - side-by-side diff (most complex)
14. Implement `ui/comment_panel.rs` - comment display/input
15. Implement `ui/help_popup.rs` - keybinding overlay
16. Implement `ui/app_layout.rs` - compose all widgets
### Phase 6: Input Handling
17. Implement `input/handler.rs` - event loop and action dispatch
### Phase 7: Output
18. Implement `output/markdown.rs` - export functionality
### Phase 8: Main
19. Implement `main.rs` - CLI args, startup, main loop, shutdown
---
## Verification Plan
1. **Build**: `cargo build` should compile without errors
2. **Format**: `cargo fmt` after each file modification
3. **Manual test flow**:
- Create a test git repo with multiple staged/unstaged changed files
- Run `tuicr` in that directory
- Scroll through all diffs with `j/k` and `Ctrl-d/u`
- Verify file list `▶` indicator updates as you scroll
- Use `{/}` to jump between file headers
- Press `Tab` to focus file list, use `Enter` to jump to a file
- Add line comments with `c`, file comments with `C`
- Mark files reviewed with `r`
- Save with `:w`, verify JSON file created
- Export with `:clip`, verify markdown output
- Quit with `:q`, restart, verify session loads with scroll position
---
## Key Files to Create
| File | Purpose |
|------|---------|
| `src/model/review.rs` | Core domain types |
| `src/git/diff.rs` | Git diff parsing |
| `src/ui/diff_view.rs` | Infinite-scroll side-by-side diff (all files in one view) |
| `src/input/handler.rs` | Vim keybinding implementation |
| `src/output/markdown.rs` | LLM-optimized output |