Skip to content
photostructurePublic
forked from yoshiko-pg/difit

About

A lightweight command-line tool that spins up a local web server to display Git commit diffs in a GitHub-like Files changed view

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

 
 

Latest commit

 

History

752 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@photostructure/difit

PhotoStructure's fork of difit, originally by yoshiko-pg.

What this fork adds

This fork builds on Yoshiko's diff viewer with a review workflow for coding agents:

  • Conversation drawer: discuss the whole review alongside inline code threads, reply in either place, and retain drafts when moving between a discussion and code.
  • Live agent feedback: submitted comments reach the agent immediately through JSON events; exact-thread replies appear in the viewer while the review continues.
  • Comment progress: see Delivered, Read, Thinking, Replying, Replied, or Noted when the agent reports its activity. This does not wake an idle agent automatically.
  • Reversible LGTM: approval stays visible and can be revoked. Conversations and the approval toggle are enabled by default; approval starts off.
  • Intent and retained discussions: choose Comment, Change request, or Question in one radio group, resolve and reopen it without losing the history, or explicitly delete it.
  • Repository information: expand the Repository info node in the file tree to see the reviewed repository, worktree, server, and launch directory. The browser title identifies the repository and port; the disclosure fits mobile screens.

The question marker and explicit thread deletion incorporate upstream PR #482. Reusable UI and conversation changes are being prepared as focused commits for upstream; scoped packaging, publishing, and default flags belong to this fork.

Run the PhotoStructure package

npx @photostructure/difit .

Live agent conversations and the reversible LGTM approval toggle are enabled by default. Approval starts off; click LGTM to approve or revoke. Use --no-lgtm for a read-only review and --no-conversation to disable live events. --lgtm and --ship-it are aliases. The agent must actively watch and answer comments; the viewer does not start an agent. See the agent conversation protocol.

Install with npm install -g @photostructure/difit to use the difit command. Node.js 22.14 or newer is required. On other machines, verify the package version with npx @photostructure/difit --version before beginning a review.

The LGTM image was supplied from this video; its rights remain with the respective rights holders. Upstream code attribution and license are retained below.


difit

npm version CI

English | 日本語 | 简体中文 | 한국어

difit screenshot

difit is a CLI tool that lets you view and review local git diffs with a GitHub-style viewer. In addition to clean visuals, comments can be copied as prompts for AI. The local code review tool for the AI era!

⚡ Quick Start

Try it first

npx @photostructure/difit  # View the latest commit diff in WebUI

Install and use

npm install -g @photostructure/difit
difit  # View the latest commit diff in WebUI

Enable use from AI agents

npx skills add photostructure/difit # Add the Skills to your agent

Installed skills include:

  • difit: ask the user for a review in the difit viewer when the user explicitly opts in to difit
  • difit-review: review a specific diff or PR and show the findings inside the difit viewer, when the user explicitly asks for difit

🚀 Usage

Basic Usage

difit <target>                    # View single commit diff
difit <target> [compare-with]     # Compare two commits/branches

Single commit review

difit          # HEAD (latest) commit
difit 6f4a9b7  # Specific commit
difit feature  # Latest commit on feature branch

Compare two commits

difit @ main         # Compare with main branch (@ is alias for HEAD)
difit feature main   # Compare branches
difit . origin/main  # Compare working directory with remote main

Special Arguments

difit supports special keywords for common diff scenarios:

difit .        # All uncommitted changes (staging area + unstaged)
difit staged   # Staging area changes
difit working  # Unstaged changes only

GitHub PR

difit --pr https://github.com/owner/repo/pull/123

--pr mode fetches patches by running gh pr diff --patch under the hood. It also imports unresolved inline review threads from the PR so they appear as startup comments in difit.

Authentication is handled by GitHub CLI:

  1. Login once (recommended): gh auth login
  2. Token-based auth (CI/non-interactive): set GH_TOKEN or GITHUB_TOKEN

GitHub Enterprise Server

For Enterprise Server PRs, authenticate GitHub CLI against your Enterprise host:

  1. gh auth login --hostname YOUR-ENTERPRISE-SERVER
  2. Or set GH_HOST=YOUR-ENTERPRISE-SERVER with GH_TOKEN/GITHUB_TOKEN

Initial Comments

You can inject initial review comments when launching difit:

difit --comment '{"type":"thread","filePath":"src/example.ts","position":{"side":"new","line":10},"body":"The background for this change is..."}'

--comment is repeatable and accepts either a single JSON object or a JSON array. Supported types:

  • thread: create a new thread at the specified diff position
  • reply: add a reply to the latest existing thread at the same diff position

If the same comment already exists, difit skips importing it.

Stdin

By using a pipe to pass unified diffs via stdin, you can view diffs from any tool with difit.

# View diffs from other tools
diff -u file1.txt file2.txt | difit

# Review saved patches
cat changes.patch | difit

# Compare against merge base
git diff --merge-base main feature | difit

# Review an entire existing file as newly added
git diff -- /dev/null path/to/file | difit

# Explicit stdin mode
git diff --cached | difit -

Stdin mode is selected with intent-first rules:

  • - explicitly enables stdin mode
  • If positional arguments (<target> / [compare-with]) or --pr are provided, difit treats the command as Git/PR mode and does not auto-read stdin
  • Auto stdin detection applies only when no explicit mode is selected and stdin is a pipe/file/socket

⚙️ CLI Options

Flag Default Description
<target> HEAD Commit hash, tag, HEAD~n, branch, or special arguments
[compare-with] - Optional second commit to compare with (shows diff between the two)
--merge-base false Resolve the base revision with git merge-base before diffing (Git revision mode only)
--pr <url> - GitHub PR URL to review (e.g., https://github.com/owner/repo/pull/123)
--comment <json> - Inject initial comments (repeatable; accepts a JSON object or array)
--commit-message <text> - Seed the proposed commit message for the initial review
--why <text> - Seed the rationale for the initial review
--port 4966 Preferred port; falls back to +1 if occupied
--host 127.0.0.1 Host address to bind server to (use 0.0.0.0 for external access)
--no-open false Don't automatically open browser
--clean false Clear all existing comments and viewed files on startup
--lgtm, --ship-it true Show the LGTM toggle to grant or revoke approval while the review stays open
--conversation true Enable live comment events and exact-thread agent replies
--include-untracked false Automatically include untracked files in diff (only with . or working)
--keep-alive false Keep server running after browser disconnects (stop manually with Ctrl+C)
--background false Keep the server running in the background and output JSON connection info
--context <lines> git default (3) Limit surrounding context lines per change (0 shows changes only; not available with --pr or stdin)

💬 Comment System

difit includes a review comment system that makes it easy to provide feedback to AI coding agents:

  1. Add Comments: Click the comment button on any diff line or drag to select a range
  2. Edit Comments: Edit existing comments with the edit button
  3. Generate Prompts: Comments include a "Copy Prompt" button that formats the context for AI coding agents
  4. Copy All: Use "Copy All Prompt" to copy all comments in a structured format
  5. Persistent Storage: Comments are saved in browser localStorage per commit

Review-wide conversations

Open Conversation in the header to see the review's discussions in a side panel. Start a general discussion there when feedback applies to the whole review rather than one line. General threads support replies, editing, and Copy Prompt just like inline threads, and are saved with the selected comparison. Click an inline thread to jump to its code; general threads stay in the panel. Copy All and CLI comment output include both kinds of discussion.

Comment Prompt Format

src/components/Button.tsx:L42   # This line is automatically added
Make this variable name more descriptive

For range selections:

src/components/Button.tsx:L42-L48   # This line is automatically added
This section is unnecessary

🤖 Calling from Agents

You can install the following Skills to work with difit from AI agents.

npx skills add photostructure/difit

Installed skills include:

  • difit: ask the user for a review in the difit viewer when the user explicitly opts in to difit
  • difit-review: review a specific diff or PR and show the findings inside the difit viewer, when the user explicitly asks for difit

After code edits or automated review, the agent can start the difit server with the appropriate skill.

🎨 Syntax Highlighting Languages

  • JavaScript/TypeScript: .js, .jsx, .ts, .tsx, .svelte
  • Web Technologies: HTML, CSS, JSON, XML, Markdown
  • Shell Scripts: .sh, .bash, .zsh, .fish
  • Backend Languages: PHP, SQL, Ruby, Java, Groovy, Scala, Perl, Elixir, Haskell, Clojure
  • Systems Languages: C, C++, C#, Rust, Go
  • Mobile Languages: Swift, Kotlin, Dart
  • Infrastructure as Code: Terraform (HCL), Nix
  • Others: Python, Protobuf, YAML, Solidity, Vim script, GDScript

🔍 Auto-collapsed Files

difit automatically identifies and collapses certain files to keep your view clean:

  • Deleted files: Removed files are auto-collapsed since they don't require close review
  • Generated files: Auto-generated code is collapsed by default. This includes:
    • Lock files (package-lock.json, go.sum, Cargo.lock, Gemfile.lock, etc.)
    • Minified files (*.min.js, *.min.css)
    • Source maps (*.map)
    • Generated code:
      • Orval (*.msw.ts, *.zod.ts, *.api.ts)
      • Dart (*.g.dart, *.freezed.dart)
      • C# (*.g.cs, *.designer.cs)
      • Protobuf (*.pb.go, *.pb.cc, *.pb.h)
    • Frameworks:
      • Ruby on Rails (db/schema.rb)
      • Laravel (_ide_helper.php)
      • Gradle (gradle.lockfile)
      • Python (uv.lock, pdm.lock)
    • Generic generated files (*.generated.cs, *.generated.ts, *.generated.js)
    • Content-based detection:
      • Files containing @generated marker
      • Files containing DO NOT EDIT header
      • Language-specific generated headers (Go, Python, etc.)

Agent conversations

Seed editable review intent with difit . --commit-message 'fix: preserve review intent' --why 'Explain why the change is needed.'. Each diff selection keeps its own proposed message and rationale for the server's lifetime. Saving a change revokes that selection's LGTM approval. Agents can update the same fields with difit comment summary --port <port> < /tmp/difit-summary.json; see the proposal protocol.

Use difit <target> <base> --conversation --keep-alive to let an agent answer submitted comments while you continue reviewing. In another process, difit comment watch --port <port> streams JSON Lines; comment reply sends answers to the exact review thread. A bounded comment watch --once wait is available for tool-driven agents. See the conversation protocol and agent workflow, including conditional LGTM approval.

HTTPS and remote browsers

The browser's Clipboard API requires a secure context. Localhost is treated as trustworthy for local development; a remote HTTP address such as http://10.1.1.24:4966 generally is not. difit tries a legacy copy fallback, but trusted HTTPS is the reliable setup for remote browsers. Browser permissions can still prevent copying.

Start the built difit server on the machine containing the repository:

difit . --host localhost --port 4966 --no-open --keep-alive

Use the actual port printed at startup if 4966 is occupied. From a source checkout, run pnpm build, then use node dist/cli/index.js in place of difit. Proxy this server, which serves both the UI and /api, at the root of one HTTPS origin. The development server (pnpm dev, usually port 5173) can embed a localhost API address for event streams, so it is not the target for these recipes.

The proxy must stream Server-Sent Events (SSE) on /api/watch and /api/heartbeat without buffering or caching. --keep-alive prevents a dropped browser connection from ending the review. difit has no built-in login: anyone who can reach the service can access the review and its enabled actions. Use an authenticated proxy for private repositories; HTTPS alone does not restrict access.

Cloudflare Tunnel (cloudflared)

For ongoing remote reviews, use a named Cloudflare Tunnel with a hostname such as review.example.com, routed to service type HTTP at localhost:4966. Configure a Cloudflare Access policy for the intended reviewers before exposing private code, and open the resulting HTTPS hostname. Keep /api/* uncached and confirm live updates using the checks below.

For a temporary preview, an installed cloudflared can create a Quick Tunnel:

cloudflared tunnel --url http://localhost:4966

Open the HTTPS URL printed by the command. However, Quick Tunnels do not support SSE, which difit uses for live updates. They are unsuitable for the full conversation workflow: a page loading and Copy working do not establish that replies or approval changes will update live. The URL is public unless access restrictions are configured.

localtunnel

With localtunnel, run a second process:

pnpm dlx localtunnel --port 4966 --local-host localhost

Open its printed HTTPS URL. This exposes the review through a third-party service; use non-sensitive diffs for a public preview. Verify streaming replies and updates before relying on a hosted tunnel for a review. Stop the tunnel when finished.

local-ip.sh on a LAN

local-ip.sh provides DNS names for IP addresses. For example, 10-1-1-24.local-ip.sh resolves to 10.1.1.24. The reviewing device must be able to reach that address; this does not create a tunnel or turn difit's HTTP server into an HTTPS server.

Run a TLS reverse proxy on the repository machine. For example, with Caddy installed, use this Caddyfile, replacing the address with that machine's LAN IP:

https://10-1-1-24.local-ip.sh:8443 {
    tls internal
    reverse_proxy localhost:4966
}

Run caddy run --config Caddyfile, allow the reviewing device to reach port 8443, and open https://10-1-1-24.local-ip.sh:8443. Caddy's tls internal uses a local CA; install and trust its root certificate on each reviewing device before using the page. Alternatively, configure a certificate trusted by those devices with Caddy's tls directive. Caddy streams SSE responses.

local-ip.sh also publishes a wildcard certificate and its private key. Because that key is public, use your own certificate for private code. The hostname alone, or bypassing a certificate warning, is not a substitute for trusted HTTPS.

Check copying and live conversations

Open the HTTPS page without a certificate warning, click Copy Prompt, and paste into a text editor. For conversations, add --conversation when starting difit and run difit comment watch --port 4966 on the repository machine. Submit a comment from the remote browser, verify it appears in the local stream, then send a reply and confirm it appears without refreshing. Agent commands still connect to localhost on the repository machine; the browser can use the HTTPS proxy. They do not acquire remote-host support from the URL.

🛠️ Development

# Install dependencies
pnpm install

# Start development server (with hot reload)
# This runs both Vite dev server and CLI with NODE_ENV=development
pnpm run dev

# Build and start production server
pnpm run start <target>

# Build for production
pnpm run build

# Run tests
pnpm test

# Run typecheck, lint, and format
pnpm run check
pnpm run format

Development Workflow

  • pnpm run dev: Starts both Vite dev server (with hot reload) and CLI server simultaneously
  • pnpm run start <target>: Builds everything and starts production server (for testing final build)
  • Development mode: Uses Vite's dev server for hot reload and fast development
  • Production mode: Serves built static files (used by npx and production builds)

🏗️ Architecture

  • CLI: Commander.js for argument parsing with comprehensive validation
  • Backend: Express server with simple-git for diff processing
  • GitHub Integration: GitHub CLI (gh pr diff --patch) for PR patch retrieval
  • Frontend: React 18 + TypeScript + Vite
  • Styling: Tailwind CSS v4 with GitHub-like dark theme
  • Syntax Highlighting: Prism.js with dynamic language loading
  • Testing: Vitest for unit tests with co-located test files
  • Quality: oxlint, oxfmt, lefthook pre-commit hooks

📋 Requirements

  • Node.js ≥ 21.0.0
  • Git repository with commits to review
  • GitHub CLI (gh) for --pr mode

📄 License

MIT

The optional --ship-it button uses a user-supplied LGTM screenshot from this video. The image belongs to its respective rights holders and is not covered by this project's MIT license.

About

A lightweight command-line tool that spins up a local web server to display Git commit diffs in a GitHub-like Files changed view

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages