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)
npm install -g diffgraphThen go to the repo you want to inspect and run:
cd /path/to/your-repo
diffgraphgit clone https://github.com/miltonlaufer/diffgraph.git
cd diffgraph
npm install
npm run build
npm linkThen go to the repo you want to inspect and run:
cd /path/to/your-repo
diffgraphBehavior:
- If the folder is not a Git repo, DiffGraph exits with a clear error and a help hint (suggesting to
cdinto a repo or rundiffgraph --helpfor commands like file-to-file that may not require a repo). - Inside a Git repo, it opens an interactive menu (
numbers+Enter,qorescto exit):1) Staged changes2) Only staged changes3) 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-ffcommand)
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-repoWhat happens:
npm linkinstalls a globaldiffgraphcommand from this project.- Run
diffgraphinside the target repo or from another folder with--repo. - DiffGraph analyzes uncommitted changes, starts a local server on
http://localhost:4177(or next free port), and opens the browser.
| Code Diff | Logic View | Symbols View |
|---|---|---|
![]() |
![]() |
![]() |
- Use
git diffwhen:- 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
- 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)
npm install -g diffgraphgit clone https://github.com/miltonlaufer/diffgraph.git
cd diffgraph
npm installBuild CLI + web assets:
npm run buildRun all validations:
npm run test
npm run type-check
npm run lint-staged
npx tsc --noEmitIf 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_passwordAfter npm run build, run:
node dist/src/cli/index.js <command>You can use this project as a central tool to analyze any repository on your machine.
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 WORKTREEFor 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.tsnpm install
npm run build
npm linkRun 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-repoIf you are already inside the target repo, --repo is optional:
cd /path/to/other-repo
diffgraph stagednpx tsx /path/to/diffgraph/src/cli/index.ts staged --repo /path/to/other-repo --no-openThis is useful while developing, but the built CLI is preferred for normal usage.
By default, staged compares all uncommitted changes (staged + unstaged) against HEAD:
node dist/src/cli/index.js stagedUse only staged changes:
node dist/src/cli/index.js staged --staged-onlyQuick rule of thumb:
diffgraph staged= equivalent scope togit diff HEAD(everything not committed yet)diffgraph staged --staged-only= equivalent scope togit 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 4177Compare two files directly:
node dist/src/cli/index.js -ff old_version.ts new_version.tsCompare two branches:
node dist/src/cli/index.js -b main featureWith repo path:
node dist/src/cli/index.js -b branch1 branch2 --repo /path/to/repoCompare 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 e4f5g6hCompare a GitHub pull request by number (fetched from origin as pull/<number>/head):
node dist/src/cli/index.js -pr 123With repo path:
node dist/src/cli/index.js -pr 123 --repo /path/to/repoCreate and persist a graph snapshot for a reference label:
node dist/src/cli/index.js analyze --repo /path/to/repo --ref WORKTREEEach 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.
- 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)
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.
-pr <number>— Compare a GitHub pull request by number. The diff is fetched fromoriginaspull/<number>/head.- Interactive menu — Option
5) Pull requestslists the latest 10 open PRs; choose one to open its diff in the browser.
- PR ↗ — Opens the current PR on GitHub in a new tab. Uses the PR URL resolved from
gh pr viewor from theoriginremote. - PR Description — Opens a modal with the full PR description rendered as markdown. Useful to keep context while reviewing the graph and code diff.
- 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.
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).
| 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. |
| 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). |
| 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. |
| 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. |
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]
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]
- 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
- ASK LLM from any hovered node -- hover a node and use the floating actions outside the node:
Copy prompt(clipboard) orOpen 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
Rscore:- 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/ArrowDownfollow this priority: (1) ifSearch code...is filled, navigate code-search matches, (2) else if graph search is filled andexcludeis 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
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 filechurnScore:min(10, floor(churn / 15)), wherechurnis added/removed diff lines from hunksconnectivityScore:min(6, floor(hotspotFanIn / 2))statusBoost:renamed:+2deletedoradded:+2type-changed:+3- otherwise:
+0
Risk level thresholds (this is where 12 and 25 come from):
low:< 12medium:>= 12and< 25high:>= 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
- 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;
Escexits fullscreen) - Whitespace-only/blank-line-only changes are ignored for semantic change detection to reduce false modified markers
- 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
- Start with Changes Only enabled.
- Use Graph diff navigation arrows to inspect each structural change quickly.
- Click a focused node to inspect exact code in the diff panel.
- If needed, click a code line to jump back to the graph context.
- Use Show calls to simplify/expand execution-flow context.
- Use graph Search when you know symbol names.
Quick local run without build:
npx tsx src/cli/index.ts staged --no-openRun web dev server only:
npm --prefix web run devCommand 'tsc' not found: usenpx tsc --noEmitinstead of baretsc.- Empty graph results: ensure the selected mode actually has file changes.
- If
stagedshows empty: there may be no uncommitted changes in that repo. - If
staged --staged-onlyshows empty: changes exist but are not staged yet (git addnot 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.
This project is licensed under the MIT License.


