Skip to content

heyhuynhgiabuu/pi-diff

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

21 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

pi-diff

npm version GitHub release

A pi extension that replaces the default write and edit tool output with Shiki-powered, syntax-highlighted diffs — side-by-side split view, unified stacked view, and word-level change emphasis, all rendered directly in your terminal.

Status: Early release.

Unified view — stacked single-column diff

pi-diff unified view

Split view — side-by-side comparison

pi-diff split view

Features

  • Syntax-highlighted diffs — full Shiki grammar highlighting (190+ languages) composited with diff background colors
  • Split view — side-by-side comparison for edit tool, auto-falls back to unified on narrow terminals
  • Unified view — stacked single-column layout for write tool overwrites
  • Word-level emphasis — changed characters get brighter backgrounds so you see exactly what changed
  • New file preview — syntax-highlighted preview when creating files
  • Adaptive layout — auto-detects terminal width; wraps intelligently on wide terminals, truncates on narrow ones
  • LRU cache — singleton Shiki highlighter with 192-entry cache for fast re-renders
  • Large diff fallback — gracefully degrades (skips highlighting, still shows diff structure) for files > 80k chars
  • Review context exportpi-diff-review exports Git changes as agent-ready markdown for Warp-style local code review
  • Fully customizable — every color and threshold is overridable via environment variables

Install

pi install npm:@heyhuynhgiabuu/pi-diff

Latest release: https://github.com/buddingnewinsights/pi-diff/releases/latest

Or load directly for development:

pi -e ./src/index.ts

Git Review in Pi TUI

pi-diff provides two review surfaces:

  1. /review-diff — the primary interactive local-review command. If Hunk is installed, pi-diff opens Hunk's review-first TUI.
  2. review_git_diff — a read-only markdown/tool path for agent context and scripted review.

Install Hunk for /review-diff

/review-diff works best with Hunk installed:

# npm
npm i -g hunkdiff

# or Homebrew
brew install modem-dev/tap/hunk

Verify Hunk is available:

hunk --version

If Hunk is not installed, pi-diff falls back to its built-in legacy review overlay and shows a warning.

/review-diff — Hunk-backed interactive review

Run inside Pi:

/review-diff
/review-diff --base main
/review-diff working-tree
/review-diff feature/base-branch

What happens:

/review-diff
  → pi-diff reads the current Git diff
  → pipes the patch to `hunk patch -`
  → Hunk opens its sidebar + diff review TUI
  → when you exit Hunk, pi-diff returns to Pi

You can test the same Hunk view outside Pi:

git diff | hunk patch -
# or
hunk diff

Hunk is responsible for the review UI: multi-file sidebar, split/stack layouts, keyboard navigation, mouse support, and agent-note/comment workflows. pi-diff intentionally does not maintain a second in-memory comment system.

Hunk configuration

Hunk reads config from:

~/.config/hunk/config.toml   # global defaults
.hunk/config.toml            # repo-local override

Recommended global starter config:

# ~/.config/hunk/config.toml

theme = "midnight"      # graphite, midnight, paper, ember, catppuccin-latte, catppuccin-mocha, custom
mode = "auto"           # auto, split, stack
vcs = "git"
watch = false

line_numbers = true
wrap_lines = false
agent_notes = true
exclude_untracked = false

Example repo-local override:

mkdir -p .hunk
$EDITOR .hunk/config.toml
# .hunk/config.toml

theme = "graphite"
mode = "split"
line_numbers = true
wrap_lines = false
agent_notes = true

Hunk also supports custom themes:

theme = "custom"

[custom_theme]
base = "graphite"
label = "Pi Diff"
accent = "#7fd1ff"
panel = "#10161d"
noteBorder = "#c49bff"

[custom_theme.syntax]
keyword = "#8ed4ff"
string = "#c7b4ff"
comment = "#6e85a7"

All custom theme colors must be #rrggbb hex values.

review_git_diff — read-only markdown review tool

review_git_diff remains available for agent/tool use and for non-destructive markdown exports.

// Open the read-only review markdown for working-tree changes, including untracked files
review_git_diff({})

// Review branch changes as main...HEAD
review_git_diff({ base: "main" })

// Focus a file from the changed-file list
review_git_diff({ file: "src/index.ts" })

// Focus a hunk ID shown by the panel
review_git_diff({ file: "src/index.ts", hunkId: "src/index.ts:10:12" })

// Return the older full markdown export with raw unified diff included
review_git_diff({ base: "main", includeRawDiff: true })
  • No base means working-tree review, including untracked files.
  • base: "main" means main...HEAD branch review.
  • includeRawDiff returns the older full review export with raw unified diff.
  • maxLinesPerHunk, maxFiles, and maxHunks cap large markdown reviews.
  • The tool path is intentionally non-destructive: it never reverts, stages, commits, discards, or edits files.

Review Export CLI

pi-diff-review exports the same Git review context as markdown for use outside Pi TUI.

# Review uncommitted working-tree changes
pi-diff-review

# Review branch changes like a pull request
pi-diff-review --base main

# Include the raw git diff after the structured summary
pi-diff-review --raw

The export includes changed files, hunk headers, changed-line numbers, and review instructions focused on correctness, regressions, security, tests, and maintainability.

How It Works

pi-diff wraps the built-in write and edit tools from the pi SDK, including single-edit and multi-edit edit calls. When the agent writes or edits a file:

  1. Before the write — reads the existing file content
  2. Delegates to the original SDK tool (file is actually written)
  3. After the write — computes a structured diff between old and new content
  4. Renders the diff with syntax highlighting and word-level emphasis

The rendering pipeline:

Old content ──┐
              ├── diff (structuredPatch) ── parse ── highlight (Shiki → ANSI)
New content ──┘                                          │
                                                         ├── inject diff bg
                                                         ├── inject word-level bg
                                                         └── wrap/fit to terminal

Views

View Used by Description
Split edit tool Side-by-side with old on left, new on right. Diagonal stripes fill empty slots. Auto-falls back to unified when terminal < 150 cols or > 20% of lines would wrap.
Unified write tool Single column with +/- gutter. Compact, works at any terminal width.

Both views show:

  • Colored border bars () for changed lines
  • Line numbers in the gutter
  • Hunk separators (··· N unmodified lines ···)
  • Word-level emphasis on paired add/del lines

Configuration

Diff Theme Presets

pi-diff ships with built-in theme presets optimized for different terminal backgrounds. Add to your .pi/settings.json:

{
  "theme": "dark",
  "diffTheme": "midnight"
}
Preset Best for Description
default Dark theme bases (~#1e1e2e) Original pi-diff colors — balanced contrast
midnight Pure black (#000000) terminals Subtle tints that don't overwhelm on black
subtle Any dark theme Minimal backgrounds — barely-there tints for a clean look
neon Low-contrast displays Higher contrast backgrounds for better visibility

Per-Color Overrides

Override individual diff colors in .pi/settings.json using hex #RRGGBB values:

{
  "theme": "dark",
  "diffTheme": "midnight",
  "diffColors": {
    "bgAdd": "#0d1a12",
    "bgDel": "#1a0d0d",
    "bgAddHighlight": "#1a3825",
    "bgDelHighlight": "#381a1a",
    "bgGutterAdd": "#091208",
    "bgGutterDel": "#120908",
    "bgEmpty": "#080808",
    "fgAdd": "#64b478",
    "fgDel": "#c86464",
    "fgDim": "#404040",
    "fgLnum": "#505050",
    "fgRule": "#282828",
    "fgStripe": "#1e1e1e",
    "fgSafeMuted": "#8b949e",
    "shikiTheme": "github-dark"
  }
}

diffColors overrides take priority over diffTheme presets, so you can start from a preset and tweak individual colors.

Auto-Derive (Default Behavior)

When no diffTheme or diffColors is set, pi-diff automatically derives background colors from your pi theme's diff foreground colors and tool-state backgrounds. Added/context surfaces use toolSuccessBg; removed surfaces use toolErrorBg. This ensures diffs look good with any pi theme and terminal background — no configuration needed.

The auto-derive uses different intensity levels:

  • Line backgrounds: 8–10% of the theme's diff fg color mixed into the matching tool-state background (subtle tint)
  • Word highlights: 20–22% (more visible for changed characters)
  • Gutters: 5–6% (subtler than line backgrounds)

Color Resolution Order

For each color, pi-diff checks (highest priority first):

  1. Environment variable — e.g. DIFF_BG_ADD="#1a3320" (backward compatible)
  2. diffColors from .pi/settings.json (per-color hex overrides)
  3. diffTheme preset from .pi/settings.json (named preset bundle)
  4. Auto-derived from pi theme's toolDiffAdded/toolDiffRemoved colors
  5. Hardcoded fallback (original defaults)

Environment Variables

All settings are also controllable via environment variables. Add them to your shell profile or .envrc:

Theme

Variable Default Description
DIFF_THEME github-dark Shiki theme name (e.g., dracula, one-dark-pro, catppuccin-mocha)

Colors

Override any diff color with hex #RRGGBB format:

Variable Default Description
DIFF_BG_ADD #162620 Background for added lines
DIFF_BG_DEL #2d1919 Background for removed lines
DIFF_BG_ADD_HL #234b32 Word-level emphasis on added text
DIFF_BG_DEL_HL #502323 Word-level emphasis on removed text
DIFF_BG_GUTTER_ADD #12201a Gutter background for added lines
DIFF_BG_GUTTER_DEL #261616 Gutter background for removed lines
DIFF_FG_ADD #64b478 Foreground for + signs and add indicators
DIFF_FG_DEL #c86464 Foreground for - signs and del indicators

Layout

Variable Default Description
DIFF_SPLIT_MIN_WIDTH 150 Minimum terminal columns to use split view
DIFF_SPLIT_MIN_CODE_WIDTH 60 Minimum code columns per side in split view

Example .envrc

# Use a different Shiki theme
export DIFF_THEME="catppuccin-mocha"

# Brighter add backgrounds
export DIFF_BG_ADD="#1a3a25"
export DIFF_BG_ADD_HL="#2d6040"

# Allow split view on narrower terminals
export DIFF_SPLIT_MIN_WIDTH=120

Architecture

src/
└── index.ts    # Extension entry point — wraps write/edit tools with diff rendering

Key internals

Component Purpose
parseDiff() Converts old/new content to structured DiffLine[] using diff.structuredPatch
hlBlock() Shiki ANSI highlighting with LRU cache (192 entries)
injectBg() Composites diff backgrounds into Shiki ANSI output (fg + bg layering)
wordDiffAnalysis() Single-pass word diff → similarity score + character ranges
renderSplit() Side-by-side renderer with diagonal stripe fillers
renderUnified() Stacked single-column renderer
wrapAnsi() ANSI-aware line wrapping with state carry-forward
shouldUseSplit() Heuristic: split vs unified based on terminal width and wrap ratio

Rendering constants

Constant Value Description
MAX_PREVIEW_LINES 60 Max lines in edit preview (split view)
MAX_RENDER_LINES 150 Max lines in write result (unified view)
MAX_HL_CHARS 80,000 Skip syntax highlighting above this
CACHE_LIMIT 192 LRU cache entries for highlighted blocks
WORD_DIFF_MIN_SIM 0.15 Minimum similarity for word-level emphasis

Exports

The extension exports a __testing object for unit testing:

import { __testing } from "@heyhuynhgiabuu/pi-diff";

const { parseDiff, renderSplit, renderUnified, normalizeShikiContrast } = __testing;

Development

git clone https://github.com/buddingnewinsights/pi-diff.git
cd pi-diff
npm install
npm run typecheck   # TypeScript validation
npm run lint        # Biome linting
npm test            # Run tests

Load in pi for testing

# From the pi-diff directory
pi -e ./src/index.ts

# Or install globally
pi install .

How pi Extensions Work

pi-diff is a pi extension — a TypeScript file that exports a default function receiving the pi API:

export default function piDiffExtension(pi: any): void {
  // Get SDK tools
  const origWrite = createWriteTool(cwd);
  const origEdit = createEditTool(cwd);

  // Register enhanced versions
  pi.registerTool({
    ...origWrite,
    name: "write",
    execute: async (...) => { /* wrap + diff */ },
    renderCall: (...) => { /* preview */ },
    renderResult: (...) => { /* render diff */ },
  });
}

Extensions can:

  • Register toolspi.registerTool(definition)
  • Listen to eventspi.on("session_start" | "input" | "before_tool_call" | ...)
  • Register commandspi.registerCommand("/name", handler)
  • Register providerspi.registerProvider("name", config)

See the pi docs for the full extension API.

License

MIT — huynhgiabuu

About

Shiki-powered terminal diff renderer for pi — syntax-highlighted, word-level diffs in split and unified views

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors