tuicr 0.10.0

Review AI-generated diffs like a GitHub pull request, right from your terminal.
# AGENTS.md - tuicr

This document serves two purposes:
1. Help coding agents understand the tuicr codebase structure
2. Explain how to interpret review feedback generated by tuicr

---

## Project Structure

tuicr is a Rust terminal UI application for code reviews. Here's the module structure:

```
src/
├── main.rs              # Entry point, event loop, action dispatch
├── config/
│   └── mod.rs           # User config loading (XDG on Unix, %APPDATA% on Windows)
├── app.rs               # Application state (App struct, InputMode, etc.)
├── error.rs             # Error types (TuicrError enum)
├── tuicrignore.rs       # .tuicrignore loader + diff file filtering (gitignore-style patterns)
├── theme/
│   └── mod.rs           # Theme palette definitions + CLI theme parsing/resolution
│
├── vcs/                 # VCS abstraction layer
│   ├── mod.rs           # detect_vcs(): auto-detect VCS (jj first, then git, then hg)
│   ├── traits.rs        # VcsBackend trait, VcsInfo, VcsType, CommitInfo
│   ├── diff_parser.rs   # Unified diff text parser (shared by hg/jj)
│   │                    # DiffFormat enum: Hg (with timestamps), GitStyle (jj/git patches)
│   ├── git/             # Git backend (uses native git2 library, not diff_parser)
│   │   ├── mod.rs       # GitBackend: wraps git2 library
│   │   ├── repository.rs # CommitInfo, get_recent_commits()
│   │   ├── diff.rs      # get_working_tree_diff(), get_commit_range_diff()
│   │   └── context.rs   # fetch_context_lines() for gap expansion
│   ├── hg/              # Mercurial backend (always compiled)
│   │   └── mod.rs       # HgBackend: uses hg CLI, parses with diff_parser::Hg
│   └── jj/              # Jujutsu backend (always compiled)
│       └── mod.rs       # JjBackend: uses jj CLI, parses with diff_parser::GitStyle
│
├── model/
│   ├── mod.rs
│   ├── comment.rs       # Comment, CommentType (Note/Suggestion/Issue/Praise)
│   ├── diff_types.rs    # DiffFile, DiffHunk, DiffLine, FileStatus, LineOrigin
│   └── review.rs        # ReviewSession, FileReview (the persisted review state)
│
├── input/
│   ├── mod.rs
│   ├── keybindings.rs   # Action enum, map_key_to_action() for each InputMode
│   └── mode.rs          # InputMode enum definition (unused, defined in app.rs)
│
├── persistence/
│   ├── mod.rs
│   └── storage.rs       # save_session, load_session, find_session_for_repo
│
├── output/
│   ├── mod.rs
│   └── markdown.rs      # export_to_clipboard(): generate markdown, copy to clipboard
│
└── ui/
    ├── mod.rs
    ├── app_layout.rs    # Main render function, file list, diff view with inline comments
    ├── status_bar.rs    # Header, status bar, command line rendering
    ├── help_popup.rs    # Help overlay (? key)
    ├── comment_panel.rs # Comment input dialog, confirm dialog
    └── styles.rs        # Color constants and style helper functions
```

Repository-managed agent integrations:
- `skills/tuicr/` - Shared agent skill bundle for coding agents, for example Claude Code, Codex, and similar tools; launches tuicr in a tmux split pane

### Key Types

**App** (`src/app.rs`):
- Central application state
- Contains: `vcs` (Box<dyn VcsBackend>), `vcs_info`, `session`, `diff_files`, `input_mode`, scroll/cursor state
- Methods: `scroll_down/up`, `next/prev_file`, `next/prev_hunk`, `go_to_source_line`, `toggle_reviewed`, `save_comment`

**VcsBackend** (`src/vcs/traits.rs`):
- Trait abstracting VCS operations
- Methods: `info()`, `get_working_tree_diff()`, `fetch_context_lines()`, `get_recent_commits()`, `get_commit_range_diff()`
- Implementations: `GitBackend`, `HgBackend`, `JjBackend` (all always compiled)

**InputMode** (`src/app.rs`):
- `Normal` - default navigation mode
- `Command` - after pressing `:`, vim-style commands
- `Comment` - typing a comment (Ctrl-S saves, Ctrl-C cancels)
- `Search` - after pressing `/`, search pattern entry
- `Help` - showing help popup
- `Confirm` - Y/N confirmation dialog
- `CommitSelect` - selecting commits to review
- `VisualSelect` - visual mode for range comments

**ReviewSession** (`src/model/review.rs`):
- Persisted review state with `files: HashMap<PathBuf, FileReview>`
- Each `FileReview` has: `reviewed: bool`, `file_comments: Vec<Comment>`, `line_comments: HashMap<u32, Vec<Comment>>`

**Action** (`src/input/keybindings.rs`):
- All possible user actions (ScrollDown, NextFile, ToggleReviewed, AddLineComment, etc.)
- `map_key_to_action(key, mode)` returns the appropriate Action

### Data Flow

1. **Startup**: Parse CLI args (invalid `--theme` exits non-zero), load config from `$XDG_CONFIG_HOME/tuicr/config.toml` (default `~/.config/tuicr/config.toml`, or `%APPDATA%\tuicr\config.toml` on Windows), ignore unknown config keys with startup warnings, resolve theme precedence (`--theme` > config > dark), then call `App::new()`. `App::new()` calls `detect_vcs()` (Jujutsu first, then Git, then Mercurial), filters diff files via repo-root `.tuicrignore`, then enters commit selection mode by default. If staged/unstaged changes exist, the first selection rows are "Staged changes" and/or "Unstaged changes". With `-r/--revisions`, it opens the requested commit range directly. Config `show_file_list = false` hides the file list panel on startup (toggleable with `;e`). Config `diff_view = "side-by-side"` sets the default diff layout (toggleable with `:diff`). Config `wrap = true` enables line wrapping (toggleable with `:set wrap!`).
2. **Render**: `ui::render()` draws the TUI based on `App` state
3. **Input**: `crossterm` events → `map_key_to_action` → match on Action in main loop
4. **Persistence**: `:w` calls `save_session()`, writes JSON to `~/.local/share/tuicr/reviews/`
5. **Reload diff**: `:e` re-runs VCS diff loading and reapplies `.tuicrignore` filtering to refresh displayed files
6. **Export**: `:clip` (alias `:export`) calls `export_to_clipboard()`, generating markdown and copying it to the clipboard (or stdout with `--stdout` flag)

### Important Implementation Details

- **Infinite scroll**: All files rendered into one `Vec<Line>`, then sliced by `scroll_offset`
- **Inline comments**: Comments are rendered in `app_layout.rs` after file headers and after relevant diff lines
- **Session loading**: `App::new()` calls `find_session_for_repo()` to restore previous review
- **Clipboard**: Uses `arboard` crate for cross-platform clipboard support
- **Hunk navigation**: `next_hunk()`/`prev_hunk()` calculate positions by iterating through files
- **Ignore filtering**: `.tuicrignore` is applied whenever diffs are loaded/reloaded

### Dependencies

- `ratatui` + `crossterm`: TUI framework
- `git2`: Native git operations
- `serde` + `serde_json`: Session serialization
- `toml`: User config parsing
- `arboard`: Clipboard access
- `ignore`: Gitignore-style matcher for `.tuicrignore`
- `chrono`: Timestamps
- `thiserror` + `anyhow`: Error handling

### Keeping Docs Updated

When adding user-facing features, update the relevant documentation:

| Document | Update when adding/changing... |
|----------|-------------------------------|
| `README.md` | Keybindings, commands (`:*`), CLI flags, features list, installation methods, agent integration setup |
| `src/ui/help_popup.rs` | Keybindings or commands (update the `help_text` vector) |
| `AGENTS.md` | Module structure, repo-managed agent integrations, key types, data flow, dependencies |

---

## Review Format (for coding agents receiving tuicr output)

When a user reviews your changes with tuicr, they will provide you with a structured Markdown document optimized for agent consumption.

### Document Structure

```markdown
I reviewed your code and have the following comments. Please address them.

Comment types: ISSUE (problems to fix), SUGGESTION (improvements), NOTE (observations), PRAISE (positive feedback)

Summary: {optional overall notes}

1. **[ISSUE]** `src/auth.rs:42` - Magic number should be a named constant
2. **[SUGGESTION]** `src/api.rs` - Consider adding unit tests
3. **[NOTE]** `src/utils.rs:15` - Why was this approach chosen?
```

### Comment Types

| Type | Meaning | Action Required |
|------|---------|-----------------|
| **[ISSUE]** | Problem that must be fixed | Yes - address before proceeding |
| **[SUGGESTION]** | Improvement recommendation | Consider implementing |
| **[NOTE]** | Observation or question | Respond or acknowledge |
| **[PRAISE]** | Positive feedback | No action needed |

### Comment Format

Each comment is numbered and self-contained:
- `{n}. **[TYPE]** \`{file}:{line}\` - {content}` (line comment)
- `{n}. **[TYPE]** \`{file}\` - {content}` (file comment)

You can reference comments by number (e.g., "Regarding comment #2...").

### How to Respond

#### 1. Address Issues First

**[ISSUE]** comments are blocking and must be fixed:

```markdown
## Issues Addressed
- #1 `src/auth.rs:42` - Extracted magic number to `TOKEN_EXPIRY_SECONDS` constant
```

#### 2. Consider Suggestions

**[SUGGESTION]** comments are non-blocking but should be considered. Either implement or explain why not.

#### 3. Answer Questions

**[NOTE]** comments often contain questions. Provide brief explanations.

### Response Format

```markdown
## Changes Made

### Issues Addressed
- #1 - Extracted magic number to `TOKEN_EXPIRY_SECONDS` constant

### Suggestions Implemented
- #2 - Added unit tests for API module

### Suggestions Declined
- #5 - Kept current approach because [reason]

### Notes Addressed
- #3 - This approach was chosen for performance reasons
```

## Tips for Coding Agents

1. **Read the full review** before making changes
2. **Prioritize [ISSUE] items** - these are blocking
3. **Reference comments by number** when responding
4. **Preserve reviewer intent** - don't over-engineer solutions
5. **Be concise** in your response
6. **If unclear**, ask for clarification rather than guessing