tuicr 0.13.0

Review AI-generated diffs like a GitHub pull request, right from your terminal.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
# tuicr: TUI for Code Review

<a href="https://crates.io/crates/tuicr" target="_blank"><img src="https://img.shields.io/crates/v/tuicr" alt="Crates.io"></a>
<a href="https://github.com/agavra/tuicr/blob/main/LICENSE" target="_blank"><img src="https://img.shields.io/crates/l/tuicr" alt="License"></a>
<a href="https://tuicr.dev" target="_blank"><img src="https://img.shields.io/badge/website-tuicr.dev-green" alt="Website"></a>

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

![demo](./public/tuicr-demo.gif)

## Why I built this

I use Claude a lot but there's no middle ground between "review every change"
and "accept all edits". Reviewing every change slows things down to human speed,
but accepting all edits makes the final review painful since I end up leaving
comments one at a time and wait after each fix.

`tuicr` is the middle ground. Let the agent loose, review the changes like a
normal PR, drop comments where needed, and export everything as structured
feedback the agent can act on in one pass.

It makes my AI-assisted development go brrrrrr.

> [!TIP]
> I pronounce it "tweaker"

## Overview

A GitHub-style diff viewer in your terminal with vim keybindings. Scroll through
changed files, leave comments, mark files as reviewed, and copy your full review
to clipboard in a format ready to paste back to the agent.

## Features

- **Infinite scroll diff view** - All changed files in one continuous scroll (GitHub-style)
- **Vim keybindings** - Navigate with `j/k`, `Ctrl-d/u`, `g/G`, `{/}`, `[/]`
- **Expandable context** - Press Enter on "... expand (N lines) ..." to reveal hidden context between hunks
- **Comments** - Add review-level, file-level, or line-level comments with types
- **Visual mode** - Select line ranges with `v` / `V` and comment on multiple lines at once
- **Review tracking** - Mark files as reviewed, persist progress to disk
- **`.tuicrignore` support** - Exclude matching files from review diffs
- **Clipboard export** - Copy structured Markdown optimized for LLM consumption
- **Session persistence** - Reviews auto-save and reload on restart
- **Jujutsu support** - Built-in jj support (tried first since jj repos are Git-backed)
- **Mercurial support** - Built-in hg support

## Installation

### Homebrew (macOS/Linux)

```bash
brew install agavra/tap/tuicr
```

### Pre-built binaries

Download the latest release for your platform from [GitHub Releases](https://github.com/agavra/tuicr/releases).

### Mise (macOS/Linux/Windows)

```
mise use github:agavra/tuicr
```

### From crates.io

```bash
cargo install tuicr
```

### Using Nix

```bash
# build tuirc (links binary to ./result/bin/tuirc)
nix build github:agavra/tuicr

# or just run with
nix run github:agavra/tuicr
```

### From source

```bash
git clone https://github.com/agavra/tuicr.git
cd tuicr
cargo install --path .
```

## Usage

Run `tuicr` in any git, jujutsu, or mercurial repository:

```bash
cd /path/to/your/repo
tuicr
```

Detection order: Jujutsu → Git → Mercurial. Jujutsu is tried first because jj repos are Git-backed.

### Options

| Flag | Description |
|------|-------------|
| `-r` / `--revisions <REVSET>` | Commit range/Revision set to review. Exact syntax depends on VCS backend (Git, JJ, Hg) |
| `--theme <THEME>` | Color theme override (`dark`, `light`, `ayu-light`, `onedark`, `github-light`, `github-dark`, `catppuccin-latte`, `catppuccin-frappe`, `catppuccin-macchiato`, `catppuccin-mocha`, `gruvbox-dark`, `gruvbox-light`, `nord-dark`, `nord-light`, `nord-dark-high-contrast`, `nord-light-high-contrast`, `solarized-light`, `solarized-dark`, `tokyo-night-storm`) |
| `--appearance <MODE>` | Appearance mode for default theme (`dark`, `light`, `system`) |
| `--stdout` | Output to stdout instead of clipboard when exporting |
| `--no-update-check` | Skip checking for updates on startup |

By default, `tuicr` starts in commit selection mode.  
If staged or unstaged changes exist, the first selectable entries are `Staged changes` and/or `Unstaged changes`.  
When `-r` / `--revisions` is provided, `tuicr` opens that revision range directly.
On narrow terminals (less than 100 columns), `tuicr` starts with the file list hidden; toggle it with `;e`.

### Configuration

Set a default theme in:
- Linux/macOS: `$XDG_CONFIG_HOME/tuicr/config.toml` (default: `~/.config/tuicr/config.toml`)
- Windows: `%APPDATA%\tuicr\config.toml`

Examples:

```toml
theme = "catppuccin-mocha"

appearance = "system"
theme_dark = "gruvbox-dark"
theme_light = "gruvbox-light"

show_file_list = false
diff_view = "side-by-side"
backend = "libgit2"
wrap = true
cursor_line = false
transparent_background = false
scroll_offset = 5

comment_types = [
  { id = "note", label = "question", definition = "ask for clarification", color = "yellow" },
  { id = "suggestion", definition = "possible improvements" },
  { id = "issue", definition = "problems to fix" },
  { id = "praise", definition = "positive feedback" },
  { id = "nit", label = "nitpick", definition = "small optional tweaks", color = "#d19a66" }
]
```

`show_file_list` controls whether the file list panel is visible on startup (default: `true`). Toggle at runtime with `;e`.

`diff_view` sets the default diff layout: `"unified"` (default) or `"side-by-side"`. Toggle at runtime with `:diff`.

`backend` selects the Git implementation: `"libgit2"` (default for normal Git repos) or `"cli"`. Sparse checkout repositories are automatically routed to the Git CLI backend because libgit2 does not support sparse-index checkouts reliably.

`wrap` enables line wrapping in the diff view (default: `false`). Toggle at runtime with `:set wrap!`.

`cursor_line` highlights the current cursor line and visual selection in the diff view (default: `true`). Set to `false` to disable.

`transparent_background` lets the terminal background show through panels (default: `true`). Set to `false` to paint the theme's `panel_bg` color instead.

`scroll_offset` keeps a minimum number of lines visible above and below the cursor when scrolling (similar to Vim's `scrolloff`). Default: `0` (no margin).

`comment_types` replaces the default list and defines Tab cycle order.
Each entry requires `id` and can optionally set `label`, `definition`, and `color`.
Color accepts terminal names (for example `yellow`, `light_red`) or hex (`#RRGGBB`).

#### How `comment_types` works

- `id` is the stable internal value that is saved in sessions and used for matching.
- `label` is the visible tag shown in UI and export (`[QUESTION]`, `[NITPICK]`, etc.).
- `definition` is guidance text for LLMs, included in the exported `Comment types:` legend.
- `color` controls the comment badge/border color in the TUI.
- Declaring `comment_types` is a full replacement, if you define 2 types, only those 2 are available.
- If `comment_types` is missing, tuicr uses defaults: `note`, `suggestion`, `issue`, `praise`.
- Invalid entries are ignored with startup warnings, if all entries are invalid, tuicr falls back to defaults.

Minimal replacement example:

```toml
comment_types = [
  { id = "question", definition = "ask for clarification" },
  { id = "blocker", color = "red", definition = "must be fixed before merge" }
]
```

Theme resolution precedence:
1. `--theme <THEME>`
2. `theme` in config file path above (OS-specific)
3. `theme_dark` + `theme_light` in config (selected by appearance)
4. `theme_dark` only or `theme_light` only in config (appearance ignored)
5. `--appearance <MODE>` (only when no explicit theme or variants are set)
6. `appearance` in config (only when no explicit theme or variants are set)
7. built-in default (`system`)

Notes:
- Invalid `--theme` values cause an immediate non-zero exit.
- Unknown keys in `config.toml` are ignored with a startup warning.

### Ignoring Files With `.tuicrignore`

`tuicr` reads `.tuicrignore` from the repository root and excludes matching files from all review diffs.

Rules follow gitignore-style pattern matching, including `!` negation.

Example:

```gitignore
target/
dist/
*.lock
!Cargo.lock
```

### Mouse

Mouse support is **on by default**. To disable, set in your config:

```toml
mouse = false
```

| Action | Effect |
|--------|--------|
| Wheel up/down | Scroll the panel under the cursor (file list, diff, commit list, or help popup) without moving the cursor line |
| Click on a file | Jump to that file (lazygit-style) |
| Click on a directory | Expand or collapse it |
| Click on a diff line | Position the cursor on that line |
| Click on a commit | Toggle selection (or expand the row to load more) |
| Drag in diff | Highlight a range; press `y` to copy the selected source lines |

For full native terminal selection across the whole UI, hold your terminal's bypass modifier while dragging (commonly **Shift** or **Option/Alt**, depending on the terminal). Check your terminal's docs if neither works.

### Keybindings

#### Navigation

| Key | Action |
|-----|--------|
| `j` / `` | Scroll down |
| `k` / `` | Scroll up |
| `h` / `` | Scroll left |
| `l` / `` | Scroll right |
| `Ctrl-d` / `Ctrl-u` | Half page down/up |
| `Ctrl-f` / `Ctrl-b` | Full page down/up |
| `g` / `G` | Go to first/last file |
| `{N}G` | Go to source line N in current file |
| `{` / `}` | Jump to previous/next file |
| `[` / `]` | Jump to previous/next hunk |
| `/` | Search within diff |
| `n` / `N` | Next/previous search match |
| `Enter` | Expand/collapse hidden context between hunks |
| `zt` | Scroll cursor to top of screen |
| `zz` | Center cursor on screen |
| `zb` | Scroll cursor to bottom of screen |

#### File Tree

| Key | Action |
|-----|--------|
| `Space` | Toggle expand directory |
| `Enter` | Expand directory / Jump to file in diff |
| `o` | Expand all directories |
| `O` | Collapse all directories |

#### Panel Focus

| Key | Action |
|-----|--------|
| `Tab` / `Shift-Tab` | Toggle focus forward/backward between file list, diff, and commit selector |
| `;h` | Focus file list (left panel) |
| `;l` | Focus diff view (right panel) |
| `;k` | Focus commit selector (top panel) |
| `;j` | Focus diff view |
| `;e` | Toggle file list visibility |
| `Enter` | Select file (when file list is focused) |

#### Review Actions

| Key | Action |
|-----|--------|
| `r` | Toggle file reviewed |
| `c` | Add line comment (or file comment if not on a diff line) |
| `C` | Add file comment |
| `;c` | Add review comment |
| `v` / `V` | Enter visual mode for range comments |
| `dd` | Delete comment at cursor |
| `i` | Edit comment at cursor |
| `y` | Copy review to clipboard |

#### Visual Mode

| Key | Action |
|-----|--------|
| `j` / `k` | Extend selection down/up |
| `c` / `Enter` | Create comment for selected range |
| `Esc` / `v` / `V` | Cancel selection |

#### Comment Mode

| Key | Action |
|-----|--------|
| `Tab` / `Shift-Tab` | Cycle comment type forward/backward (from `comment_types` order) |
| `Enter` / `Ctrl-Enter` / `Ctrl-s` | Save comment |
| `Shift-Enter` / `Ctrl-j` | Insert newline |
| `` / `` | Move cursor |
| `Ctrl-w` / `Alt-Backspace` / `Cmd-Backspace` | Delete word |
| `Ctrl-u` | Clear line |
| `Esc` / `Ctrl-c` | Cancel |

#### Commands

| Command | Action |
|---------|--------|
| `:w` | Save session |
| `:e` (`:reload`) | Reload diff files |
| `:clip` (`:export`) | Copy review to clipboard |
| `:diff` | Toggle diff view (unified / side-by-side) |
| `:commits` | Select commits to review |
| `:set wrap` | Enable line wrap in diff view |
| `:set wrap!` | Toggle line wrap in diff view |
| `:set commits` | Show inline commit selector |
| `:set nocommits` | Hide inline commit selector |
| `:set commits!` | Toggle inline commit selector |
| `:clear` | Clear all comments |
| `:version` | Show tuicr version |
| `:update` | Check for updates |
| `:q` | Quit (warns if unsaved) |
| `:q!` | Force quit |
| `:x` / `:wq` | Save and quit (prompts to copy if comments exist) |
| `?` | Toggle help |
| `q` | Quick quit |

#### Commit Selection (startup)

| Key | Action |
|-----|--------|
| `j` / `k` | Move selection |
| `Space` | Toggle commit selection |
| `Enter` | Confirm and load diff |
| `q` / `Esc` | Quit |

#### Inline Commit Selector (multi-commit reviews)

When reviewing multiple commits, an inline commit selector panel appears at the top of the diff view. Focus it with `;k` or `Tab`.

| Key | Action |
|-----|--------|
| `j` / `k` | Navigate commits |
| `Space` / `Enter` | Toggle commit selection (updates diff) |
| `(` / `)` | Cycle through individual commits |
| `Esc` | Return focus to diff |

#### Confirm Dialogs

| Key | Action |
|-----|--------|
| `y` / `Enter` | Yes |
| `n` / `Esc` | No |

## Review Output

When you export your review (`:clip` or confirm on `:wq`), `tuicr` copies structured Markdown to your clipboard. The format is optimized for pasting into AI agent conversations:

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

1. **[SUGGESTION]** `src/auth.rs` - Consider adding unit tests
2. **[ISSUE]** `src/auth.rs:42` - Magic number should be a named constant
3. **[NOTE]** `src/auth.rs:50-55` - This block could be refactored
```

Each comment is numbered and self-contained with its file path and line number or range (if applicable).
If `comment_types` is configured, this legend and the `[TYPE]` tags reflect your configured labels and definitions.

## Session Persistence

Sessions are automatically saved to `~/.local/share/tuicr/reviews/` (XDG compliant). When you reopen `tuicr` in the same repository, your previous review progress (comments, reviewed status) is restored.

## Agent Integrations

tuicr ships a repo-managed skill bundle at `skills/tuicr/`.

It opens tuicr in a tmux split pane so you can review changes interactively and feed comments back to your coding agent.

**Usage:** `/tuicr` or ask your coding agent to "review my changes with tuicr".

### Claude Code

**Prerequisites:** Claude Code running inside tmux, tuicr installed.

**Installation** (choose one):

```bash
# Copy the shared skill into Claude's local skills directory
mkdir -p ~/.claude/skills
cp -r /path/to/tuicr/skills/tuicr ~/.claude/skills/tuicr
```

### Codex

**Prerequisites:** Codex running inside tmux, tuicr installed.

**Installation**:

```bash
# Copy the shared skill into the local agents skills directory
mkdir -p ~/.agents/skills
cp -r /path/to/tuicr/skills/tuicr ~/.agents/skills/tuicr
```