Skip to content

miltonlaufer/diffgraph

Repository files navigation

DiffGraph (AKA "Tell me WTF the LLM did to my code")

npm version license

Package: diffgraph on npm.

Developed with partial support of neural frames

DiffGraph is a CLI + browser app that compares code changes as interactive graphs instead of only line-by-line diffs. It is optimized for large diffs where plain patch views become hard to reason about quickly.

It supports:

  • logic-flow change view
  • knowledge-graph change view (classes, functions, services, etc.)
  • React/frontend structure change view
  • split screen visualization (old on the left, new on the right)

60-Second Quick Start

Install from npm (recommended)

npm install -g diffgraph

Then go to the repo you want to inspect and run:

cd /path/to/your-repo
diffgraph

Install from source

git clone https://github.com/miltonlaufer/diffgraph.git
cd diffgraph
npm install
npm run build
npm link

Then go to the repo you want to inspect and run:

cd /path/to/your-repo
diffgraph

Behavior:

  • If the folder is not a Git repo, DiffGraph exits with a clear error and a help hint (suggesting to cd into a repo or run diffgraph --help for commands like file-to-file that may not require a repo).
  • Inside a Git repo, it opens an interactive menu (numbers + Enter, q or esc to exit):
    • 1) Staged changes
    • 2) Only staged changes
    • 3) Branch to branch (latest 10 branches)
    • 4) Commit to commit (latest 10 commits)
    • 5) Pull requests (latest 10 open PRs)
    • 6) File to file (prints the exact -ff command)

Direct commands:

diffgraph staged
diffgraph staged --staged-only
diffgraph -b main feature/your-feature-branch
diffgraph -r <oldCommit> <newCommit>
diffgraph -pr <number>

From any folder, pass --repo:

diffgraph staged --repo /path/to/your-repo
diffgraph -b main feature/your-feature-branch --repo /path/to/your-repo

What happens:

  1. npm link installs a global diffgraph command from this project.
  2. Run diffgraph inside the target repo or from another folder with --repo.
  3. DiffGraph analyzes uncommitted changes, starts a local server on http://localhost:4177 (or next free port), and opens the browser.

Visual Preview

DiffGraph

Code Diff Logic View Symbols View
Code Diff Logic View Symbols View

When To Use DiffGraph vs git diff

  • Use git diff when:
    • you need exact line patches quickly
    • the change is very small and localized
    • you are editing commit messages or patching directly
  • Use DiffGraph when:
    • you want fast understanding of flow/structure impact
    • changes touch multiple files/symbols
    • you want to jump graph <-> code and review by risk first

Requirements

  • Node.js 20+ (recommended)
  • npm 10+
  • Python 3 (for Python AST analysis)
  • Git (for staged/branch diff modes)
  • Optional: Neo4j (if you want persisted snapshots)

Installation

From npm

npm install -g diffgraph

From source

git clone https://github.com/miltonlaufer/diffgraph.git
cd diffgraph
npm install

Build CLI + web assets:

npm run build

Run all validations:

npm run test
npm run type-check
npm run lint-staged
npx tsc --noEmit

Neo4j Setup (Optional)

If set, DiffGraph stores snapshots in Neo4j. Otherwise, it falls back to in-memory storage.

export NEO4J_URI=neo4j://localhost:7687
export NEO4J_USER=neo4j
export NEO4J_PASSWORD=your_password

CLI Usage

After npm run build, run:

node dist/src/cli/index.js <command>

Using DiffGraph in Other Repositories

You can use this project as a central tool to analyze any repository on your machine.

Option 1: Run from this project with --repo (recommended)

node dist/src/cli/index.js staged --repo /path/to/other-repo
node dist/src/cli/index.js -b main feature --repo /path/to/other-repo
node dist/src/cli/index.js -pr 123 --repo /path/to/other-repo
node dist/src/cli/index.js analyze --repo /path/to/other-repo --ref WORKTREE

For file-to-file mode, pass two explicit file paths:

node dist/src/cli/index.js -ff /path/to/file_old.ts /path/to/file_new.ts

Option 2: Install a global local command with npm link

npm install
npm run build
npm link

Run from anywhere:

diffgraph staged --repo /path/to/other-repo
diffgraph -b main feature --repo /path/to/other-repo
diffgraph -pr 123 --repo /path/to/other-repo

If you are already inside the target repo, --repo is optional:

cd /path/to/other-repo
diffgraph staged

Option 3: Run directly with npx tsx (development mode)

npx tsx /path/to/diffgraph/src/cli/index.ts staged --repo /path/to/other-repo --no-open

This is useful while developing, but the built CLI is preferred for normal usage.

1) Uncommitted changes (staged)

By default, staged compares all uncommitted changes (staged + unstaged) against HEAD:

node dist/src/cli/index.js staged

Use only staged changes:

node dist/src/cli/index.js staged --staged-only

Quick rule of thumb:

  • diffgraph staged = equivalent scope to git diff HEAD (everything not committed yet)
  • diffgraph staged --staged-only = equivalent scope to git diff --staged (only what is in the index)

If --staged-only appears empty, run git add <files> first.

Use custom repo path and port:

node dist/src/cli/index.js staged --repo /path/to/repo --port 4177

2) File-to-file mode (-ff)

Compare two files directly:

node dist/src/cli/index.js -ff old_version.ts new_version.ts

3) Branch-to-branch mode (-b)

Compare two branches:

node dist/src/cli/index.js -b main feature

With repo path:

node dist/src/cli/index.js -b branch1 branch2 --repo /path/to/repo

4) Ref-to-ref mode (-r)

Compare any two Git refs (commits, tags, or branches) using exact git diff <oldRef> <newRef> semantics:

node dist/src/cli/index.js -r <oldRef> <newRef>

Example with commit SHAs:

node dist/src/cli/index.js -r a1b2c3d e4f5g6h

5) Pull request mode (-pr)

Compare a GitHub pull request by number (fetched from origin as pull/<number>/head):

node dist/src/cli/index.js -pr 123

With repo path:

node dist/src/cli/index.js -pr 123 --repo /path/to/repo

6) Analyze snapshot only

Create and persist a graph snapshot for a reference label:

node dist/src/cli/index.js analyze --repo /path/to/repo --ref WORKTREE

Browser Behavior

Each diff command starts a local server and opens a browser automatically:

http://localhost:<port>/?diffId=<generated-id>

If the default port (4177) is busy, the server automatically finds the next available port.

Tabs

  • Logic -- flowchart with control-flow branches (if/switch/for/return as diamonds/pills), showing code snippets inline
  • Knowledge -- structural graph of classes, functions, services, imports, etc.
  • React -- component render tree and hook usage (shown only when the diff contains React-relevant symbols)

Pull request (PR) mode

When you open a diff via Pull request mode (CLI: diffgraph -pr <number> or interactive menu option 5) Pull requests), the app runs in PR mode and exposes the following.

CLI

  • -pr <number> — Compare a GitHub pull request by number. The diff is fetched from origin as pull/<number>/head.
  • Interactive menu — Option 5) Pull requests lists the latest 10 open PRs; choose one to open its diff in the browser.

Header controls (PR mode only)

  • PR ↗ — Opens the current PR on GitHub in a new tab. Uses the PR URL resolved from gh pr view or from the origin remote.
  • PR Description — Opens a modal with the full PR description rendered as markdown. Useful to keep context while reviewing the graph and code diff.

PR description in context

  • The server fetches and exposes the PR description (and a short excerpt). The Ask-LLM prompt (Copy prompt / Open ChatGPT) automatically includes the PR description excerpt when available, so generated prompts carry review context.

PR review comments and conversations (since v.0.1.7)

In PR mode, the app fetches review threads (line-level and general discussions) from GitHub and surfaces them in the UI.

  • Conversation badges — On graph nodes (Logic, Knowledge, React): each node that has review comments shows a badge with the number of conversations and whether any are unresolved. On the code diff: each line (or line range) that has comments shows a badge in the line number gutter. Clicking a badge opens the Conversation modal scoped to that node or those lines.
  • Conversation modal — Lists review threads with:
    • File path and line range (old / new side when applicable)
    • Link to the thread on GitHub
    • All comments in the thread: author, avatar, timestamp, and markdown body
    • General discussions (not tied to a specific line) grouped under "General Discussion"
    • Threads sorted by last activity
    • Close with the Close button or Escape

Review threads are loaded in the background (GraphQL with REST fallback).

Features

UI / UX

Feature Description
Graph logic tree modal See graph logic tree in node hover actions opens a fullscreen focused subgraph modal. Faster branch-level reasoning without losing full-graph context.
Code logic tree mode See code logic tree in node hover actions; code diff can filter to related logic ancestry lines. Moves from graph node to minimal code window quickly.
Pull request controls in header In PR mode: PR ↗ (open on GitHub), PR Description modal, and conversation badges/modal for review comments. See Pull request (PR) mode above.
Resizable layout Resizable panels for graph/code and Old/New split panes. Better ergonomics for large monitors and small laptops.
Performance guard modal Runtime lag detection modal with mitigations (hide call edges; optionally render one side only). Keeps app responsive on very large graphs.
Node action panel Ask-LLM floating panel includes graph/code logic-tree actions, tooltip anchoring, and copied-state messaging.
Group node detail strips Function parameter diffs and hook dependency diffs shown inline in group headers with status coloring. Signature-level API changes visible in graph view.
Graph canvas zoom Minimum zoom is 0.01 in split graph panels. Easier to zoom out for global structure scans.

Functionality / Workflow

Feature Description
PR URL handling PR mode resolves URL from gh pr view metadata, with fallback from origin remote parsing.
PR description propagation Server exposes PR description + excerpt; Ask-LLM prompt builders include the excerpt when available.
Interactive branch compare fallback In interactive menu, branch order is auto-reversed if selected order has zero diff files but reverse has changes.
Code diff navigation Code logic-tree requests route through view commands/store into diff drawer filtering and scrolling.
Escape-to-dismiss Keyboard close for modals (performance guard, PR description).

Analysis Engine / View Semantics

Feature Description
TypeScript control-flow coverage try/catch/finally branch nodes and flow edges; chained promise typing (then/catch/finally). Logic View captures exception and async error paths.
Python control-flow coverage Python analyzer preserves internal flow for for loops and try blocks (including nested).
Callback and hook metadata Callback name resolution and hook dependency extraction in TS analysis.
React view JSX edges React view infers JSX render edges from branch metadata and tag usage.
Class/method hierarchy inference Logic view builder infers class method grouping and parent-child structure.
Parameter/dependency diff tracking Token-level diffs for function params and hook deps. Shows what changed inside signatures.

Diff Accuracy / Performance

Feature Description
Comment-insensitive signatures Diff/analyzers normalize signatures by stripping comments before hashing. Comment-only edits do not appear as semantic changes.
Graph delta matching Node matching for duplicates/deep callbacks (signature + position-aware pairing).
Whitespace-aware side-by-side diff Ignores whitespace-only noise; preserves multiline wrapped visibility.
Neighborhood/derived caching Cached neighborhood strategies and memo-hash based worker inputs for derived/layout computations.
Debounced search Debounced graph/code search inputs.
Test coverage Tests for graph delta, control-flow analyzers, diff utils, node identity, and view commands.

Logic View Flow (Mermaid)

flowchart LR
  A[CLI mode<br/>staged/refs/branches/pr/files] --> B[DiffProvider]
  B --> C[Diff payload<br/>old/new files + hunks + PR metadata]
  C --> D[DiffEngine]
  D --> E[TS Analyzer]
  D --> F[Python Analyzer]
  E --> G[Snapshot Graphs old/new]
  F --> G
  G --> H[GraphDelta match and status]
  H --> I[buildLogicView]
  I --> J[ViewBase + SplitGraphPanel]
  J --> K[Old/New Logic canvases]
  K --> L[Node-edge interactions + code sync]
Loading
flowchart TD
  A[Hover graph node] --> B[AskLlmButton actions]
  B --> C{Action}

  C -->|See graph logic tree| D[Collect node neighborhood + ancestors]
  D --> E[Layout focused subgraph]
  E --> F[Open GraphLogicTreeModal fullscreen]
  F --> G[Click modal node]
  G --> H[Close modal + focus original graph node]

  C -->|See code logic tree| I[Collect related line range in same file]
  I --> J[commandOpenCodeLogicTree]
  J --> K[CodeDiffDrawer filters rows]
  K --> L[Gap markers keep skipped regions readable]

  C -->|Copy prompt / Open ChatGPT| M[Build prompt with old/new snippets, related nodes, PR excerpt]
  M --> N[Clipboard copy or capped URL]
Loading

Graph panels (Old / New)

  • Old graph shows only nodes that existed before; New graph shows only nodes that exist after
  • Added/Removed/Modified legend appears only on the New panel
  • Controls appear only on the New panel; MiniMap appears on both Old and New panels
  • Both panels share synchronized pan/zoom (with temporary hover-focus adjustments when needed)
  • In logic view, dragging the canvas works from node areas too (you are not limited to empty background)
  • In logic view, top-level alignment keeps Old/New function blocks comparable while preserving nested hierarchy
  • Parent/scope blocks run overlap resolution to reduce block collisions in dense graphs

Interactions

  • ASK LLM from any hovered node -- hover a node and use the floating actions outside the node: Copy prompt (clipboard) or Open ChatGPT ↗ (new tab). The generated prompt includes the current-side code, matching old/new counterpart code when available, connected-node context, and graph relation details. For very large prompts, the ChatGPT URL payload is capped and appends [PROMPT CUT BECAUSE OF URL LENGTH LIMIT] so truncation is explicit.
  • Click a node -- highlights it (cyan border + glow), selects its file below, and scrolls the code diff to that line
  • Changed Files auto-collapse on node click -- selecting a node collapses the Changed Files block; when collapsed it shows Selected: <filename> in the header
  • Click a connector/edge (Logic tab) -- first click focuses the source node; clicking the same connector again focuses the target node
  • Clicked connector highlight -- the selected connector remains emphasized for ~5 seconds
  • Hover a connector/edge -- shows a source/target tooltip for faster graph tracing
  • Click a file pill -- selects the file, scrolls graphs to related nodes, shows its code diff
  • File switch resets searches -- changing or clearing the selected file clears graph search and code search
  • Risk-ranked file list -- changed files are sorted by R score:
    • graph connectivity = how many other symbols call into changed symbols (high fan-in => wider impact)
    • change type = rename/add/delete/type-changed adds extra risk boost
    • churn = how many lines were added/removed
    • thresholds: R >= 25 = high, R >= 12 = medium, below 12 = low (see Risk score (Rxx) details below)
  • Escape -- deselects node and file
  • Changes Only (on by default) -- keeps changed nodes plus required context (hierarchy ancestors and relevant invoke neighbors in Logic view). Toggle off to show full graph.
  • Show calls (Logic tab) -- show/hide invoke edges to reduce visual noise
  • Graph diff navigation (Logic tab, up/down arrows) -- jumps across graph-level changes (added/removed/modified nodes) and highlights the focused node for ~5 seconds
  • Graph search (per panel) -- search nodes by text, use up/down arrows to jump matches; matched node is focused and highlighted, and related connections are emphasized during the flash window
  • Arrow keys behavior -- ArrowUp/ArrowDown follow this priority: (1) if Search code... is filled, navigate code-search matches, (2) else if graph search is filled and exclude is off, navigate graph-search matches, (3) else navigate graph differences in Logic view (same as the up/down diff buttons)
  • Hover function/group headers -- tooltip includes documentation (JSDoc/docstring), parameters with types, return type, and class/file metadata when available
  • Node hover -> code preview -- when code diff is open, hovering a graph node previews the corresponding code line; leaving hover restores the selected line
  • Risk-ranked symbols -- functions/methods/classes inside a file are sorted by deterministic risk score to prioritize review order
  • Code line -> graph focus -- clicking a code line finds the best matching graph node, highlights it for ~4.2 seconds, and keeps the graph area in view
  • Double-click code word -> code search -- double-clicking a word in the diff fills Search code... and jumps to the first textual match

Risk score (Rxx) details

The R38 / R28 badge on each file pill is the file-level riskScore computed server-side in src/server/app.ts.

File-level formula:

riskScore = symbolRisk + churnScore + connectivityScore + statusBoost
  • symbolRisk: sum of the top 8 changed symbols' scores in that file
  • churnScore: min(10, floor(churn / 15)), where churn is added/removed diff lines from hunks
  • connectivityScore: min(6, floor(hotspotFanIn / 2))
  • statusBoost:
    • renamed: +2
    • deleted or added: +2
    • type-changed: +3
    • otherwise: +0

Risk level thresholds (this is where 12 and 25 come from):

  • low: < 12
  • medium: >= 12 and < 25
  • high: >= 25

Symbol-level score (used by symbolRisk) is also computed in src/server/app.ts (computeSymbolRiskScore) and combines:

  • diff status weight (added/removed/modified)
  • symbol kind weight (Class, Function, Method, ReactComponent, Hook)
  • span size, call fan-in/out, ownership, and public API heuristic

Code diff panel

  • Side-by-side view: Old code on the left, New code on the right
  • Line-level change highlighting: green for added lines, red for removed lines
  • Hovering a line highlights that row for easier scanning
  • Graph-selected line uses a persistent white border; hover preview uses a blue border
  • Active code search highlights matching words inline inside code lines
  • Synchronized scrolling between left and right
  • Each side is fixed at 50% width and supports horizontal scrolling for long lines
  • New files show "File did not exist" on the old side; deleted files show "File was deleted" on the new side
  • Clicking a logic node scrolls the diff to the corresponding line number and scrolls the page to the code viewer
  • Clicking a code line can navigate back to the best matching node in the graph (including full-file add/remove views)
  • Code search matching runs across both Old and New columns
  • Fullscreen toggle (top-right icon in code diff panel; Esc exits fullscreen)
  • Whitespace-only/blank-line-only changes are ignored for semantic change detection to reduce false modified markers

Performance and responsiveness

  • Layout and large graph transforms are processed in Web Workers with guarded fallbacks
  • Derived computations (view filtering, search matching, hover neighborhoods, and diff stats) run off the main thread when available
  • Expensive interactions trigger an immediate local "Updating graph..." overlay before updates apply
  • The default review flow and graph interactions are tuned for large, multi-file diffs

Suggested workflow

  1. Start with Changes Only enabled.
  2. Use Graph diff navigation arrows to inspect each structural change quickly.
  3. Click a focused node to inspect exact code in the diff panel.
  4. If needed, click a code line to jump back to the graph context.
  5. Use Show calls to simplify/expand execution-flow context.
  6. Use graph Search when you know symbol names.

Development

Quick local run without build:

npx tsx src/cli/index.ts staged --no-open

Run web dev server only:

npm --prefix web run dev

Troubleshooting

  • Command 'tsc' not found: use npx tsc --noEmit instead of bare tsc.
  • Empty graph results: ensure the selected mode actually has file changes.
  • If staged shows empty: there may be no uncommitted changes in that repo.
  • If staged --staged-only shows empty: changes exist but are not staged yet (git add not run).
  • Branch mode errors: fetch branches and verify names are correct.
  • No browser opening: run without --no-open, or copy the printed URL manually.
  • Port busy: the server tries up to 20 consecutive ports starting from 4177 (or --port N).
  • Slow UI on large repos: keep Changes Only enabled (default). The CLI warns when the graph exceeds 2000 nodes.

License

This project is licensed under the MIT License.

About

DiffGraph is a CLI + browser app that compares code changes as interactive graphs instead of only line-by-line diffs.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors