git review · local · zero dependencies

Review your agent's diff
in a real git client

diffotator opens a Fork-style diff viewer in your browser. Comment on any line, hit send, and the notes land in your Claude Code session as instructions it acts on.

$diffotator
Install Read the docs GitHub
diffotator reviewing a TypeScript change: file list on the left, split diff on the right, and an inline review comment threaded under the line it discusses.

Split diff, folded context, and a comment threaded under the line it's about.

Two tools, one gap

A desktop git client reads a diff beautifully and can't tell your agent anything. A browser review tool can talk to your agent and tends to read badly. This is both halves.

Traverse the whole repo

Not just the changed lines. Walk commits, open any file at any revision, and comment on code the diff never touched.

Comments become instructions

Labels follow Conventional Comments, with a blocking flag and optional suggested code. They arrive as markdown your agent acts on.

Fires on its own

A Stop hook opens the review when a turn leaves real changes — so review happens even when you forget to ask for it. Silent otherwise.

Nothing to install into your repo

One Node process, no dependencies, no config, no service. It reads git and exits when you're done.

The All Commits scope: commit graph with branch lanes and refs, and the diff of the selected merge commit below it.

The commit graph, and a merge shown against its first parent — what it brought in.

The review loop

An agent run leaves you fifty changed files. The loop is v to mark a file reviewed and jump to the next unviewed one, c when something looks wrong, repeat. The header tracks how far you've got.

KeyDoes
vMark viewed and jump to the next unviewed file
j kNext / previous file
n pNext / previous change within the file
Move the line cursor
cComment on the focused line
⌘FFind in file — the browser's own Find can't see a windowed diff
⌘↵Save the comment, or send the review

What your agent receives

The CLI blocks until you submit. Whatever it prints on stdout becomes your agent's next input — so a slash command is the whole integration.

# Code review feedback

Reviewed `checkout` — origin/main...HEAD. 1 comment.

> **1 blocking comment** — these must be resolved.

## `src/checkout/pricing.ts`

### src/checkout/pricing.ts:23 — issue (blocking)

*L23*

```ts
  return roundMinor(subtotal + tax - orderDiscount);
```

Order discount is applied after tax here, but the doc comment says the
authority treats it as a post-tax adjustment — pin that with a test.
You pressAgent gets on stdout
Send feedbackThe markdown above — grouped by file, blocking comments called out first
ApproveThe user approved.
Close or Ctrl-CReview session closed without feedback.

Or let it fire on its own

diffotator hook --install adds a Stop hook, so a review opens whenever a turn leaves real changes. Blocking comments return {"decision":"block","reason":…} — the agent is sent back to work with your review as the reason. It stays silent on a clean tree, on small turns, and on a tree you already reviewed.

Fast because it doesn't ship the review to you

Browser diff viewers usually inline the entire review into one HTML document — we measured one session at 17 MB. diffotator sends nothing up front and fetches one file at a time.

0

dependencies

Node's standard library and git. No bundle to download, nothing to audit.

61k lines

diff, opens instantly

Rows are windowed — only what's on screen exists in the DOM.

~50 ms

per file

One git diff per file, on demand. A 54-file list resolves in about 300 ms.

How

Git shapes that bite

Most diff viewers are written against a straight line of ordinary commits. Each of these was a real bug during development, and each one has a test.

ShapeWhat goes wrong if you don't handle it
Root commitsha^! has no parent to exclude — the diff comes out reversed
Merge commitsha^! excludes every parent, so the merge shows zero files
Renamenumstat compresses the path to {old => new} — renames read +0/−0
CRLF filea stray carriage return renders at the end of every line
File ending in a newlinea phantom empty last line
Binarya blank pane instead of saying "binary"
Untrackedmissing from the file tree — exactly the files an agent just wrote

Commits resolve to an explicit two-dot pair against the first parent, or the repo's empty tree at a root. That's also what makes a merge show what it merged in.

Where it fits

diffotatorDesktop git clientBrowser review tool
Browse commits, branches, stashesYesYesDiff only
Open any file at any revisionYesYesNo
Comment on a lineYesNoYes
Comment on untouched codeYesNoNo
Feedback reaches your agentYesNoYes
Commit, push, rebase, resolve conflictsNoYesNo

It's a reviewer, not a git client replacement. Keep using Fork for the things Fork is for.

Try it on your current branch

One command in any git repository.

$diffotator --base origin/main
Install