Skip to content

Navigation Menu

Sign in
Sign up

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
  • Fully customizable — every color and threshold is overridable via environment variables
  • Strict edit safety — delegates matching, uniqueness, overlap checks, mutation queues, aborts, BOM, and EOL preservation to Pi's SDK edit tool

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

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

For edit calls, Pi's SDK performs matching, uniqueness and overlap validation, mutation queueing, and the file write. pi-diff adapts the SDK's returned unified patch into its syntax-highlighted renderer, so the preview reflects the actual matched source rather than the requested text.

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

Disable pi-diff tools

Add disabledTools to pi-diff.json to omit pi-diff tools. Disabling write or edit leaves Pi's built-in tool available; disabling apply_patch removes that custom tool.

{
 "disabledTools": ["apply_patch"]
}

See CONFIG.md for the full pi-diff.json reference.

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
├── core/ # Pure data layer: diff parsing, conflict detection, line resolution
│ ├── config.ts
│ ├── conflicts.ts
│ ├── diff.ts # parseDiff, parsePatchFiles, resolveSepStyle
│ ├── replace.ts # conservative apply_patch matcher and legacy helpers
│ ├── resolve-lines.ts
│ └── *.test.ts
└── review/ # Shared diff-rendering primitives used by the main extension
 ├── git.ts # read git diffs from disk (execFileSync)
 └── hunk-preview.ts # renderSplit, renderUnified, theme helpers (re-exported via __testing)

Key internals

All listed symbols are exposed under the __testing export for unit tests:

Symbol Source Purpose
parseDiff() core/diff.ts Convert old/new content to structured DiffLine[] (added/removed/chars)
parsePatchFiles() core/diff.ts Parse a full git apply patch into per-file diff objects
resolveSepStyle() core/diff.ts Pick the per-side separator style ( vs ) for split view
getSepStyle() review/hunk-preview.ts Same as above for the shared rendering layer
computeHunkBlocks() review/hunk-preview.ts Build add/remove/modify line blocks with column ranges for split view
renderSplit() review/hunk-preview.ts Side-by-side renderer with diagonal stripe fillers
renderUnified() review/hunk-preview.ts Stacked single-column renderer
normalizeShikiContrast() review/hunk-preview.ts Lighten/darken Shiki output to match the surrounding TUI theme

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: ExtensionAPI): void {
 // Wrap the built-in write/edit tools with diff rendering
 const origWrite = createWriteTool(cwd);
 const origEdit = createEditTool(cwd);
 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" | "tool_call" | "session_shutdown" | ...)
    • tool_call handlers can block: true to prevent the call from running
  • Register commandspi.registerCommand("/name", handler)

See the pi docs for the full extension API.

Design notes

How hashline (hashline_read, hashline_edit, edit hashlineChanges) relates to Pi’s stock edit tool (exact edits[], bounded fuzzy match, loud failures) is documented in .pi/artifacts/DECISIONS.md (ADR-001). Summary: stock edit stays the portable default; hashline is an opt-in strict anchor path for agents that adopt it.

License

MIT — huynhgiabuu

About

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

Resources

Stars

18 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

AltStyle によって変換されたページ (->オリジナル) /