diffing
v0.4.0
Published
local-first CLI for reviewing, navigating, and discussing git diffs with AI
Readme
diffing
A local-first code review tool and double-sided bridge designed for the modern AI coding agent workflow. Review AI-generated changes in a high-fidelity, GitHub-like web UI, leave inline comments, and hand them back to your coding agent to fix in real time — and review the agent's plan the same way before it writes any code, approving, rejecting, or requesting changes on specific lines and sections.
Quick Start
1. Install
Install diffing globally via npm:
npm install -g diffing2. Run
Launch it within any active git repository:
diffingThis instantly spins up a local server, establishes an active repository watcher, and opens your default browser to an interactive code review dashboard.
Prefer the terminal? Add --tui for a native-Rust terminal UI — same review flow, no browser:
diffing --tui[!WARNING] The TUI is experimental in v0.3.0. The interface, keymap, and
server.json(mode: "tui") on-disk format may change in a minor release. The web UI is the supported path for production workflows. See Native Terminal UI (TUI) for the full feature set, keymap, and stability notes.
3. Update
Check if a new version is available on npm and upgrade instantly via the CLI:
diffing updateWeb UI Review Dashboard
A local Hono-powered review server delivers a full-featured GitHub-like code review interface directly in your browser.
- Split / Unified View — Toggle between side-by-side (
split) and inline (unified) diff layouts via toolbar or keyboard shortcutm. - Syntax Highlighting — Powered by Shiki via
@pierre/diffs, with high-fidelity highlighting for 200+ languages. - Interactive File Tree — Hierarchical file navigation sidebar with collapsible folders, viewed/unviewed tracking, and change-type indicators (added, modified, deleted).
- Status Dashboard (Comment Tracker) — Bottom panel tracking open, replied, and resolved comments with filter tabs and click-to-navigate references to the relevant file and line.
- Git Diff Stats — Toolbar displays repo name, branch, file count, and additions/deletions (
+X/-Y) computed from the patch. - Server-Side State & Drafts Persistence — No browser storage is used. All settings, UI panels sizing, session states, and comment drafts are securely stored and persisted server-side in the project's global
.diffingdirectory (and global settings inside~/.config/diffing/settings.json). - Dynamic Font Customization — Pick any Google Fonts family or locally-installed system font for both UI typography and monospace code rendering, configurable via the settings font picker. Google-hosted families are fetched automatically as web fonts and render everywhere; local fonts are applied by name (see the note on custom fonts).
- Resizable Panels — Drag-to-resize sidebar (240px–640px) and comment tracker panel (100px–600px). Panel states are instantly saved server-side for absolute consistency across browser sessions.
- Skeleton Loading Screen — Full shimmer placeholder UI for toolbar, sidebar, search, tree nodes, and file diffs during initial load.
- Image Diff Previews — Visual side-by-side comparison for added, changed, and deleted image files (PNG, JPEG, GIF, WebP, SVG, BMP, ICO, AVIF).
Themes
42+ built-in themes powered by Shiki, with instant switching and live preview.
| Category | Themes | |----------|--------| | GitHub Family | GitHub Dark, GitHub Light, GitHub Dark Dimmed, GitHub Dark High Contrast | | Popular Dark | Dracula, One Dark Pro, Monokai, Synthwave '84, Material Theme (Ocean/Palenight/Darker) | | Tokyo Night | Tokyo Night, Tokyo Night Storm, Tokyo Night Light | | Catppuccin | Mocha, Frappe, Macchiato, Latte | | Nord Family | Nord | | Nightfox Family | Nightfox, Nordfox, Duskfox, Terafox, Carbonfox, Dayfox, Dawnfox | | Rose Pine | Rose Pine, Rose Pine Dawn, Rose Pine Moon | | Solarized | Solarized Dark, Solarized Light | | VS Code | Dark+, Light+, Dark Modern, Light Modern | | Others | Andromeeda, Aurora X, Houston, Laserwave, Min Dark/Light, Night Owl, One Light, Plastic, Poimandres, Slack (Dark/Ochre), Vesper, Vitesse Dark/Light, Ayu Dark/Light |
- Searchable Theme Modal — Press
gtor use the toolbar to open a categorized, searchable theme picker with color swatches and live preview. - Dark/Light Dual Mode — Each theme maps to corresponding dark and light Shiki themes for accurate syntax highlighting in both modes.
- Instant Switching — CSS transitions suppressed during theme changes for a snappy, lag-free experience.
- Persistent Setting — Theme choice saved to
~/.config/diffing/settings.jsonand restored on next launch.
Rust-Powered Code Search (powered by fff)
Blazing-fast, native fuzzy code search integrated directly into the sidebar search palette via @ff-labs/fff-node:
- Fuzzy File Search (
Filesscope) — Error-tolerant fuzzy matching on workspace paths using a native Rust engine. - Codebase Grep (
Textscope) — Instant case-insensitive text search with full Regular Expression support. - Syntactic Symbol Search (
Symbolsscope) — Finds function declarations, class headers, type definitions, and variable assignments across JavaScript, TypeScript, Go, Rust, and Python (17+ language patterns). - Unified "All" Search — Concurrent search across files, text, and symbols with automatic deduplication.
- Frecency Ranking — SQLite-backed history database remembers which files you open for specific queries, floating high-value results to the top.
- "Changed Only" Filter — Restricts search scope exclusively to files changed in the active git diff.
- Git Status Chips — Search results display git status indicators (modified, untracked, added, deleted, renamed).
- Graceful Degradation — If the native Rust binary is unavailable, search reports as unavailable without crashing the server.
- Auto-Indexing — The Rust engine maintains its own file system watcher for real-time index updates as the working tree changes.
Vim-Style Keyboard Navigation
Full keyboard-driven navigation with vim-like motions and a modal status bar:
Scrolling & Diffs
| Key | Action |
|-----|--------|
| j / k | Scroll down/up by 100px |
| Ctrl+d / Ctrl+u | Scroll half-page down/up |
| g g | Jump to top of diff |
| G | Jump to bottom of diff |
| m | Toggle split/unified view |
| t | Cycle tab size (2 → 4 → 8) |
| w | Toggle line wrap |
| n | Toggle line numbers |
| i | Cycle diff indicators |
| I | Cycle inline diff type |
| Cmd+Shift+P | Toggle preview mode in comments |
File Navigation & UI
| Key | Action |
|-----|--------|
| J / K | Jump to next/previous file |
| v | Toggle file viewed/unviewed |
| b | Toggle sidebar visibility |
| / | Open text search |
| s | Open symbol search |
| g v | Open file browser |
| g t | Open theme picker |
| Cmd/Ctrl+K | Open global command palette |
| Cmd/Ctrl+, | Toggle settings panel |
| ? | Show shortcuts help modal |
A vim-style status bar at the bottom displays the current mode (NORMAL/INSERT), file path, and a help button. Multi-key sequences use an 800ms key buffer.
Native Terminal UI (TUI) — Experimental
[!WARNING] The TUI is experimental in v0.3.0. The interface, keymap, and on-disk format of
server.json(mode: "tui") may change in a minor release before stabilisation. The web UI is the supported path for production workflows; please open an issue before depending on the TUI for CI / agent automation. The web review flow, plan review, and PR review are unaffected.
diffing --tui opens an opt-in native-Rust terminal interface that mirrors the web review dashboard — no browser, no Electron, no port. It's a leaf renderer in a new crates/diffing-tui/ binary: the Node CLI is still the single source of truth for arg parsing, lockfile discovery, and agent handoff, and the TUI reads the same ~/.diffing/<repo>/* state on disk and writes a server.json with mode: "tui".
The default diffing behaviour is byte-identical with and without --tui — the TUI is strictly opt-in. If the env cannot support a TUI (piped stdin, CI, no raw mode) or the binary is missing, you get a one-line stderr note and the normal git diff output. The web mode is also unaffected by the TUI build; the same diffing install serves either.
diffing --tui # Open the current working tree in the TUI
diffing --tui --staged # Review staged changes in the TUI
diffing --tui HEAD~3 # Review working tree vs. 3 commits ago
diffing --tui main..feature # Compare two branches in the TUI
diffing --tui -- -- src/ # Limit a TUI review to a directoryStack — Rust 1.78+ workspace (crates/diffing-core/ shared lib + crates/diffing-tui/ binary), ratatui + crossterm for rendering, syntect for syntax highlighting, notify-debouncer-full for live updates.
Features
- Unified diff render with a virtualised file card, syntect highlighter, and the same 8 Shiki-compatible colour themes as the web UI.
- Vim-style file tree & keymap —
j/k/gg/G/J/K/Tab/w/t/m/?/motions, plusc(new comment),e/r/x(edit / reply / resolve),]/[(next / previous thread),?(help). - Full comment CRUD — mirrors
src/lib/comments.tsbyte-for-byte; the comment tracker at the bottom lists open / replied / resolved threads with click-to-jump navigation. - Multi-line
tui-textareaform with markdown rendering in the preview pane. - Live updates via
notifywatcher oncomments.jsonand the repo working tree — write a comment in another window and it appears immediately. - Send review & agent handoff — verdict radios + general-comment popover with a live XML preview. Submits via the same
format_commentsenvelope as the web UI, copies to clipboard (pbcopy/wl-copy/xclip/xsel/clip.exe), and persistspending-review.xml. The agent-status indicator in the status bar mirrors the web UI's "Send to agent" connection dot. - Cross-platform — macOS, Linux, and Windows are all first-class. The TUI liveness probe uses
kill(pid, 0)on Unix andtasklist /NH /FO CSVon Windows. Clipboard works on Wayland (wl-copy), X11 (xclip/xsel), macOS (pbcopy), and Windows (clip.exe/ PowerShellSet-Clipboard).
Caveat — the TUI's "Send to agent" writes the review to disk + clipboard but does not actively unblock a long-polling diffing await-review; that requires a Node-side change in a follow-up. The web UI's send button remains the supported native handoff path. The pending-review.xml is still produced and the review session is still round-incremented — only the long-poll wake-up is missing.
Building the binary locally
pnpm build:tui # debug build → target/debug/diffing-tui
pnpm build:tui --release # release build → target/release/diffing-tuiThe CLI auto-discovers the binary in the following search order: sibling of dist/cli.mjs, bin/, target/release/, target/debug/, then $PATH. A cargo build (debug) workflow is supported out of the box; you don't need --release just to use the TUI.
Console Startup Animations & Quotes
Every time you launch diffing in the terminal, it serves a highly polished, interactive greeting before the browser opens:
- 256-Color Monochromatic Palettes — Beautifully rendered box outlines across 6 monochromatic themes (Cyan, Green, Magenta, Yellow, Blue, Orange) with custom faint, base, glow, and text hues.
- Dynamic Startup Animations — Instantly runs one of 6 terminal-based micro-animations (Typewriter, Wave Reveal, Slide-In, Pulse Border, Glitch Noise, Matrix Rain) underneath the local server URL.
- Motivational Developer Quotes — Settles to display one of 30 curated, funny, philosophical, or motivational developer quotes to kick off your review session.
Performance & Speed
Built from the ground up for a fast, fluid experience—even in large repositories:
- Rust-Powered Search — Native engine handles indexing and querying outside the JS event loop.
- Async Diff Execution — Server fetches unstaged, staged, and untracked diffs concurrently via
Promise.all. - Web Worker Rendering —
@pierre/diffsuses a worker pool for syntax highlighting and diff computation off the main thread. - React.memo + useMemo — Extensive component memoization prevents unnecessary re-renders when files haven't changed.
- useTransition — Settings changes (theme, diff style, font size) wrapped in
startTransitionfor non-blocking UI updates. - Compositor-Only Resize — Sidebar and comment panel resize use GPU-composited transform guides; width/height committed only on mouseup for 60fps feel.
- Shiki Pre-Warming — Highlighter engines preloaded on theme change for instant first paint.
- Large Buffer Support — Git operations use 50–100MB max buffer to handle large diffs.
- Object Reference Stability — Diff metadata reuses previous object references when file contents haven't changed (JSON comparison of hunks).
- Stale Project Cleanup — Project storage directories older than 14 days or with missing repo paths are automatically purged.
Real-Time Communication
Bidirectional, event-driven sync between the browser UI and connected AI agents via Server-Sent Events (SSE):
┌──────────────┐ SSE (change/comments/agent-status) ┌──────────────┐
│ │◄───────────────────────────────────────────►│ │
│ Browser │ │ AI Agent │
│ UI │ ┌─────────────────────────────┐ │ (CLI/MCP) │
│ │ │ comments.json │ │ │
│ │ │ (FileCommentStore on disk) │ │ │
└──────┬───────┘ └──────────┬──────────────────┘ └──────┬───────┘
│ user writes comment │ │
│ ──────────────────────► │ fs.watch detects change │
│ │ ───────────────────────────────► │
│ │ agent posts reply │
│ SSE broadcasts toast │ ◄────────────────────────────── │
│ ◄───────────────────────│ │- Single SSE Endpoint (
/api/live) — Multiplexed event stream with named events:change(working tree),comments(store updated),agent-status(agent connect/disconnect/send),heartbeat(15s keep-alive). - File System Watcher —
fs.watchon repo root (recursive, 200ms debounce) triggers live diff refresh on working tree changes. Skips.git,node_modules,dist, and.changeset. - Comment Store Watcher —
fs.watchoncomments.json(120ms debounce) broadcasts updates when agents or humans write comments externally. - Agent Activity Toasts — Real-time toast notifications when an agent posts a reply, showing model name, file path, and body preview. Clickable to jump to the file; auto-dismisses after 8 seconds.
- Agent Status Indicator — Green dot on the "Send to agent" button when an agent process is connected and waiting (via SSE
agent-statusevents). - Bidirectional Sync — User adds comments in UI → saved to
comments.json→ watcher → SSE broadcast → agent picks up. Agent posts reply → written tocomments.json→ watcher → SSE → UI toast.
AI Agent Collaboration (Handoff Protocol)
diffing solves the friction of copy-pasting code review notes into LLM chat boxes. It establishes an "agent waits, human releases" pipeline using a robust, port-agnostic lockfile mechanism.
1. The agent runs a blocking command/tool and enters sleep mode.
2. You review code in your browser, leave inline comments, and click "Send to agent".
3. The agent wakes up instantly, receives comments as structured XML, applies edits, and posts replies.A. CLI Integration (For any terminal-based agent)
The diffing binary acts as a port-agnostic CLI client. It automatically discovers the running server by reading a local repository lockfile:
diffing await-review # Block process until you click "Send to agent"; outputs comments as XML
diffing comments [--open] [--json] # One-shot query of the comments database
diffing reply <id> --body "..." # Post an agent response or explanation
diffing resolve <id> # Mark a comment resolved, updating the UI live
diffing url # Retrieve the active server base URL
# Plan review (review a markdown plan before any code is written)
diffing plan submit <file> [--title T] [--model M] [--id <id>] [--wait] # Submit/resubmit a plan; prints its id
diffing plan await [--timeout N] # Block until the human approves/rejects/requests-changes; outputs the verdict XML
diffing plan list [--json] # List submitted plans with their verdicts
diffing plan show [<id>] [--json] [--version N] # Show one plan as <plan-review> XML (latest if omitted; use --version to read a historical body)
diffing plan versions <id> [--json] # List every submitted version of a plan, oldest-first (current marked with `*`)
diffing plan reply <commentId> --body "..." # Reply to an inline plan comment
diffing plan resolve <commentId> # Mark a plan comment resolvedB. Model Context Protocol (MCP) Server
If your agent supports MCP (such as Cursor, Claude Desktop, or Gemini), configure diffing as a stdio-based MCP server. No ports need to be configured:
{
"mcpServers": {
"diffing": {
"command": "diffing",
"args": ["mcp"]
}
}
}Exposes tools directly to your agent for both review flows — diff review: await_review, list_comments, reply_to_comment, resolve_comment; and plan review: submit_plan, await_plan_review, list_plans, get_plan, get_plan_versions, get_plan_version, reply_to_plan_comment, resolve_plan_comment.
C. Agent Skills
You can install diffing skills directly into your AI coding assistant:
npx skills add ahmedragab20/diffingProvides commands to coordinate reviews:
/diffing-start-review— Launches the review server./diffing-finish-review— Blocks the agent usingawait-reviewuntil comments are sent, then applies requested edits./diffing-review— Combined launch-and-wait flow./diffing-plan-review— Submit a markdown plan, block until the human approves/rejects/requests changes, then proceed or revise.
Send Review Popover
A GitHub-style "finish your review" popover with inline editing of each comment, an optional general/overall comment, and a visual indicator when an agent is waiting. The "Copy comments" toolbar button serializes all comments to the XML spec and copies them to the clipboard.
Port-Agnostic Discovery
A per-repo lockfile (server.json) in ~/.diffing/<repo-hash>/ enables all subcommands and MCP tools to discover the server's port with zero configuration. Stale or crashed server locks are automatically detected and treated as dead via process.kill(pid, 0).
Monotonic Round Sequencing
A ReviewSession class with a monotonic round counter and race-guard logic ensures that if a "Send to agent" lands between polling intervals, the cached payload is delivered immediately. Multiple agents can block on the same review session simultaneously—all are released together on send.
Plan Review
Review any agent plan — not just code. When an AI agent produces a plan
(an implementation outline, a design proposal, a migration strategy), diffing
renders the markdown line-by-line so you can comment on specific lines or
sections and approve, request changes, or reject it — then hands the
structured verdict back to the waiting agent. It's the "agent waits, human
releases" handoff, applied before any code is written.
1. The agent submits a markdown plan and blocks (diffing plan submit … --wait).
2. You open the "Plans" tab, read the plan, and comment on lines/sections.
3. You click Approve / Request changes / Reject (with an optional overall note).
4. The agent wakes instantly, receives the verdict + comments as <plan-review> XML,
and proceeds, revises-and-resubmits, or stops accordingly.- Renders any markdown plan — the plan body is shown via
@pierre/diffswith full syntax highlighting and line numbers, so it's addressable like a diff. - Line, range & section comments — hover the gutter
+or select a line range to comment; each comment auto-captures its enclosing markdown heading (section) and the exact anchored text for the agent. - General comments — attach notes scoped to the whole plan.
- Three-way verdict — Approve / Request changes / Reject, GitHub-style, with an optional overall comment. Resubmitting a revised plan (same id) bumps the version and re-opens it for review.
- Browse plan versions — every submitted body is kept on disk, oldest-first.
A version dropdown next to the
v{n}chip switches the viewer to any past version; comments are filtered to those anchored to the version you're reading; a "viewing v{N} of v{M}" banner makes the historical context obvious. Available from the CLI (diffing plan versions <id>,diffing plan show <id> --version N), MCP (get_plan_versions,get_plan_version), and the HTTP API (GET /api/plans/:id/versions,GET /api/plans/:id/versions/:n). - Live "Plans" badge — the diff toolbar shows a badge counting plans awaiting your review, with a green dot when an agent is connected and waiting.
- Same channels everywhere — drive it from the CLI (
diffing plan …), the MCP tools (submit_plan,await_plan_review, …), or the HTTP API (POST /api/plans,POST /api/plans/:id/decision,GET /api/plan-review/await).
Plan Review XML Specification
When a plan verdict is handed to a waiting agent, it is serialized into a
self-documenting <plan-review> envelope:
<plan-review>
<instructions>…how to act on the verdict; how to reply/resolve/resubmit…</instructions>
<plan id="…" title="…" version="2" decision="changes-requested" decided-at="2026-05-29T18:52:56.053Z">
<decision-summary><![CDATA[The reviewer REQUESTED CHANGES. Revise the plan…]]></decision-summary>
<decision-comment><![CDATA[Tighten the Phase 2 scope.]]></decision-comment>
<plan-body><![CDATA[# My Plan
## Phase 1
…full markdown of the plan being reviewed…]]></plan-body>
<comments>
<comment id="c1" line="4" section="Phase 1" status="open" created-at="2026-05-29T18:52:29.557Z">
<context><![CDATA[Do the first thing]]></context>
<body><![CDATA[Clarify what "the first thing" is.]]></body>
<replies>
<reply id="r1" created-at="…" role="agent" model="claude-opus-4-8"><![CDATA[Will do — splitting into 1a/1b.]]></reply>
</replies>
</comment>
</comments>
</plan>
</plan-review>Reviewing a GitHub PR
diffing can open a GitHub PR in the same diff UI you use for the working
tree, and push your review back to GitHub when you're done. The "Send to
agent" handoff is structurally absent in PR mode — there's no way to
accidentally route a PR review to a coding agent.
# All of these open PR #1234 in the current repo:
diffing "gh pr 1234"
diffing --gh-pr 1234
# Full URL form:
diffing "gh pr https://github.com/ahmedragab20/diffing/pull/1234"
diffing --gh-pr https://github.com/ahmedragab20/diffing/pull/1234
# owner/repo#N shorthand (skips the cwd-repo check):
diffing "gh pr ahmedragab20/diffing#1234"The PR diff loads in the existing <DiffViewer> / <FileTree> machinery.
Existing review comments on the PR are fetched and shown as a read-only
summary strip below each file so you can see what was already said before
adding your own. Comments you write are kept visually distinct.
When you click Submit to GitHub, diffing builds a POST
/repos/{owner}/{repo}/pulls/{pull_number}/reviews payload and POSTs it
to GitHub. Multi-line comments are expanded to N single-line comments
with a [part N/M] prefix. Existing comments are never re-POSTed —
only the new ones you wrote in this session.
Headless subcommands
For CI / agent use, the same flow is exposed as a gh subcommand with no
UI:
# Submit the current in-progress review to GitHub.
diffing gh pr-review 1234 --decision request-changes --body "Please address the range"
# Dump PR metadata (title, owner, head SHA, existing comments) as JSON.
diffing gh pr-fetch 1234
# List the PR-mode comments in this diffing session (mirrors `diffing comments`).
diffing gh pr-list-comments
# Show the active PR session (ref, owner/repo#n, comment count, submitted status).
diffing gh statusAuthentication
The submit path uses the same precedence as the rest of the GitHub ecosystem:
ghCLI — preferred; uses your existinggh auth loginsession.- Token env var —
$GH_TOKEN, then$GITHUB_TOKEN, then$GITHUB_API_TOKEN(any of the three). - If neither is available, the submit fails with a clear one-line
message telling you to run
gh auth loginor set$GITHUB_TOKEN.
The gh binary is only used for the submit and refresh steps; the
diff itself is rendered locally, so PR review works offline once the
session is open.
Storage
PR-mode state lives in pr-session.json (in the per-repo
~/.diffing/<repo>-<hash>/ directory) — a separate file from
comments.json and plans.json, so a local review and a PR review can
coexist without colliding.
Inline Comment System
Rich, real-time comment threads directly on diff lines:
- Inline Threads — Hover and click the
+button on any addition or deletion line to start a thread. Supports markdown (GFM + line breaks) with syntax-highlighted fenced code blocks. - Multi-Line Comments — Select a line range to comment on an entire block of code.
- File-Level Comments — Add general comments scoped to the entire file without targeting a specific line.
- Comment Drafts — Draft system (
diffing-draft-*keys) with 7-day TTL, stored in the global.diffingfolder so drafts survive page refreshes without any browser storage footprint. - Agent Attribution — Replies carry
role(user/agent) and the agent'smodelname for clear attribution. - Suggestion Application — Parse
```suggestioncode blocks from comment bodies and apply them to the file in one click viaPOST /api/comments/:id/apply-suggestion. - Full CRUD API — REST endpoints for creating, reading, updating, and deleting comments and replies.
Inline Diff Viewer Settings
Fine-grained control over how diffs are rendered:
| Setting | Options | Description |
|---------|---------|-------------|
| Inline Diff Type | word (default), word-alt, char, none | Pinpoint exactly what changed inside a modified line |
| Diff Indicators | classic (+/−), bars, none | Gutter markers for added and deleted lines |
| Line Numbers | on / off | Toggle gutter line numbers |
| Line Wrap | on / off | Soft-wrap long lines instead of horizontal scrolling |
| Hunk Separators | simple, metadata, line-info, line-info-basic | Style of the separator bar between diff hunks |
| Line Hover Highlight | both, line, number, disabled | Which element highlights on hover |
| Font Size | 11px – 16px | Configure globally |
| UI Font | Any system or Google font (default Geist Mono) | Customize typography for the Web UI layout |
| Mono Font | Any system or Google font (default JetBrains Mono) | Customize typography for code/diff block displays |
| Tab Size | 2 / 4 / 8 | Default tab width (overridden per-file by EditorConfig) |
| Expandable Context | expandContextByDefault, collapsedContextThreshold (default 10 lines), expansionLineCount (default 20) | Control how collapsed context regions behave |
| Haptic & Sound Feedback | on / off | Tactile feedback via web-haptics and synthesized audio cues (click, toggle, navigate, open, close, resolve, send, error) |
All settings persist to ~/.config/diffing/settings.json and the UI updates are wrapped in useTransition for non-blocking responsiveness.
A note on custom fonts
Google-hosted families (e.g. Fira Code, Source Code Pro, IBM Plex Mono, Roboto Mono) are fetched automatically as web fonts and render in any browser. A locally-installed font — a Nerd Font, or a commercial font like Dank Mono — is applied by name and renders only if it is installed on your machine and your browser allows pages to use local fonts.
Privacy-hardened browsers block this: Brave, with Shields / "Block fingerprinting" enabled (the default), refuses to render uncommon local fonts as an anti-fingerprinting measure, so the selection silently falls back to the default monospace even though it is installed. To use a local-only font there, either pick a Google-hosted equivalent, or open Shields for the diffing site, set fingerprinting to Allow all, and reload.
Merge Conflict Resolution
When your repository is in a merge state (.git/MERGE_HEAD detected):
- Conflict Banner — A prominent warning banner appears in the toolbar indicating the repo is in a merge conflict state.
UnresolvedFileRendering — Conflicted files are rendered using@pierre/diffs'sUnresolvedFilecomponent with color-coded conflict markers.- Custom Action Buttons — For each conflict region, choose Accept Current, Accept Incoming, or Accept Both.
- Save & Stage — After resolving all conflicts in a file, click Save & Stage to write the resolved content and
git addit in one step. - Merge Status API —
GET /api/merge-statusreturns conflict state and lists conflicted files viagit diff --name-only --diff-filter=U.
Hunk Revert & History
- Revert Individual Hunks — Undo a single hunk from the working tree via
POST /api/revert-hunkusinggit apply --reverse. - Blame & History — View
git blamefor deleted lines and recent commit history for any file viaGET /api/hunk-history, showing who authored deleted code and when.
Advanced Git Operations
- File Open in IDE — Open any repo file in VS Code, Zed, Vim, Neovim, or the system default editor via
POST /api/open-file. Configurable in settings. - File Save & Stage — Write file contents to disk and optionally
git addin one call viaPOST /api/save-file. - Repository File Lister —
GET /api/repo-fileslists all known files (tracked + untracked, respecting.gitignore). - File Content Retrieval —
GET /api/file-contentandGET /api/file-textreturn old (HEAD) or new (working tree) file versions as binary buffers or JSON text. - EditorConfig Integration — Respects your local
.editorconfigrules (tab_width,indent_size) for accurate, per-file tab sizing that overrides the default setting.
Git Diff Drop-in Compatibility
diffing is designed as a seamless, full drop-in replacement for git diff. It features a comprehensive option parser that understands standard git revisions, options, and pathspecs, forwarding them directly to your local git engine.
Whether you are comparing branches, reviewing staged changes, or filtering specific directories, simply swap git diff for diffing to instantly elevate your review into a premium, interactive browser interface:
diffing # Review working tree changes in the browser UI
diffing --staged # Review staged changes (drop-in for git diff --staged)
diffing HEAD~3 # Review working tree changes against 3 commits ago
diffing main..feature # Compare two branches (drop-in for git diff main..feature)
diffing -- --cached -- src/ # Staged changes specifically in the src/ directoryIntelligent Output Modes (TTY Auto-Detection)
To integrate flawlessly with your existing developer shell workflows, build pipelines, and command scripts, diffing automatically resolves the optimal output mode based on how stdout is directed:
- Web Mode (Default for interactive TTY): When executed in an interactive terminal session, it boots the local Hono review server, registers the repository lockfile, and opens your default browser.
- Terminal Mode (Default for pipes, redirects, or non-TTY): When output is piped (e.g.
diffing | grep "const") or redirected to a file, it falls back to behave exactly likegit diff, streaming clean, standard unified diff patch text directly to standard output and exiting.
[!TIP] Any standard output control or format-related flags (such as
--raw,--numstat,--stat,--exit-code,--quiet, or-o) will automatically force Terminal Mode fallback.
[!TIP] For a full list of all git option categories (algorithms, whitespace ignoring, context lines, word-level diffs, moved/copied detection, and path filtering) supported by
diffing, see the CLI Reference Manual.
diffing show — Review a Commit's Changes (Drop-in for git show)
diffing <sha> runs git diff <sha>, which means "diff between <sha> and the working tree" — not the changes that commit introduced. If the working tree matches <sha>, the patch is empty and the file list renders blank.
To review the changes of a commit, use the new show subcommand. It mirrors git show and surfaces each commit's metadata (subject, author, date, message body) above the diff in the web UI:
diffing show HEAD # Review the tip commit's metadata + diff
diffing show HEAD~3..HEAD # Review the last 3 commits as a series
diffing show v1.0 # The commit a tag points to
diffing show abc123 def456 # Two specific commits, oldest-first
diffing show HEAD -- src/ # Limit a commit review to a directory
diffing show HEAD~2..HEAD --terminal # Stream `git show` to your terminalshow accepts every form git show understands (single commit, range, tag, branch tip, multiple SHAs) and is strictly opt-in — diffing <sha> keeps its current git diff <sha> semantics so existing muscle memory and scripts are unaffected.
Integration & Configuration
- Persistent User Settings — Settings saved to
~/.config/diffing/settings.json, loaded on startup, synced to server viaPUT /api/settings. - Custom Host/Port —
--host 0.0.0.0exposes the review dashboard to the local network;--port <port>overrides random port selection. --no-openFlag — Prevents auto-browser opening on server start.- Git Config Alias — Register
diffingasgit reviewvia~/.gitconfig. - Shell Aliases —
.zshrc/.bashrcexamples:gd="diffing",gds="diffing --staged",gda="diffing & diffing await-review". - Configurable Browser & IDE — Choose which browser to auto-open (Chrome, Firefox, Edge, Brave, or system default) and which IDE to use for file opening (VS Code, Zed, Vim, Neovim, or system default).
- Graceful Shutdown —
SIGINT/SIGTERMhandlers remove the server lockfile on exit.
Comment XML Specification
When review comments are exported or streamed to a waiting agent, they are serialized into an optimized, self-documenting XML structure equipped with CDATA blocks:
<code-review-comments>
<instructions>
You are an AI coding assistant receiving a structured list of code review comments to address.
For each file, review the inline comments and apply the changes requested.
- Target lines are specified by the "line" attribute (e.g. line="42" for single lines, line="42-45" for multi-line blocks, or line="file" for file-level notes).
- "side" indicates whether the comment is on "additions" (new code) or "deletions" (old code).
- "status" indicates whether the comment is "open" or "resolved". Only address "open" comments.
- The <code> block contains the code context at the reviewed lines.
- The <body> tag contains the review feedback or request.
HOW TO REPLY OR MARK AS RESOLVED:
- Prefer using the diffing CLI or MCP server tools (reply_to_comment / resolve_comment).
- CLI: `diffing reply <id> --body "..."`
- CLI: `diffing resolve <id>`
</instructions>
<!-- [Optional] High-Level General Review Comment -->
<general-comment>
<![CDATA[Please refactor the parsing module to improve reliability.]]>
</general-comment>
<file path="src/utils/parser.ts">
<!-- [Example A] Multi-Line Selection Addition Comment -->
<comment id="c1" line="42-45" side="additions" status="open" created-at="2026-05-24T22:00:00.000Z">
<code><![CDATA[
+ const parsedToken = tokenize(input);
+ if (parsedToken.type === 'EOF') {
+ return null;
+ }
]]></code>
<body><![CDATA[Refactor this tokenization block to check for undefined inputs as well.]]></body>
<replies>
<reply id="r1" created-at="2026-05-24T22:05:00.000Z" role="agent" model="claude-3-5-sonnet">
<![CDATA[I agree, I will add a guard clause for undefined.]]>
</reply>
</replies>
</comment>
<!-- [Example B] Whole-File General Comment -->
<comment id="c2" line="file" side="additions" status="open" created-at="2026-05-24T22:08:00.000Z">
<body><![CDATA[This parser module needs additional unit tests to cover negative bounds.]]></body>
</comment>
</file>
</code-review-comments>Security
- Path Traversal Prevention — All file operations validate paths against the repository root, rejecting
.., null bytes, absolute paths, and URL-encoded bypass attempts. - 403 Forbidden Responses — File operations (
open-file,save-file,revert-hunk,hunk-history) reject paths outside the repo root. - Attachment Isolation — Uploaded attachments are restricted to
~/.diffing/<repo>/attachments/. - Client Directory Isolation — Static file serving resolves against the client directory and rejects paths outside it.
- XSS Prevention — HTML is escaped before markdown rendering;
markedis used for safe HTML generation. - Repo Path Verification —
repo_path.txtis written to storage directories for cross-checking.
Deep-Dive Documentation
For advanced features, internal API endpoints, sequence specifications, and configuration parameters, explore the complete guide:
[!IMPORTANT] Read the CLI & Protocol Reference Manual for detailed descriptions of:
- TTY Auto-Detection and Output Mode resolution.
- The port-agnostic discovery lockfile mechanics.
- Monotonic sequence
roundsynchronization for race-free polling.- Full Web API endpoints schema (
GET /api/review/await,POST /api/comments, etc.).- Custom git config shell aliases.
Landing Page
👉🏻 The project was intially forked from wong2/diffx
License
MIT
