GitHub PRs as narrated stories.
dad 139 turns a file-by-file diff into a semantic walkthrough with AI-generated chapters, reviewer concerns, a recap tab, inline comments, and live sync.
brew install nicknisi/formulae/dadOr, equivalently:
brew tap nicknisi/formulae
brew install dadgit clone https://github.com/nicknisi/diffdad && cd diffdad
bun install && bun run build
bun linkRequires Bun when building from source. The Homebrew install is a standalone binary — no runtime needed.
dad <pr> # Open a PR as a narrated review
dad review <pr> # Same as above (explicit subcommand)
dad config # Print a link to the in-app settings page (opens it if the daemon is up)
dad cache clear # Clear cached narratives
dad daemon # Start the per-machine review command center
dad --version # Print versionFlags (can go in any position):
--with=claude|codex|pi # Force a specific local AI CLI
--no-cache # Regenerate narrative even if cached
--no-open # Don't auto-open the browser
--port=3000 # Use a specific portPR argument formats:
https://github.com/owner/repo/pull/123
owner/repo#123
139 # bare number — infers repo from git remote
The CLI fetches the PR diff, generates a semantic narrative, and opens a local web UI in your browser. The review view starts with verdict, reading plan, and concerns; the Recap tab lazily generates an orientation brief for in-flight work.
dad daemon runs a long-lived, per-machine review hub: one cross-repo queue of everything waiting on you, served at a stable local URL (http://localhost:4319) and grouped by status — needs you, in flight, cleared.
dad daemon # Start the command center
dad daemon status # Is it running?
dad daemon install # Run it under launchd — survives terminal close, restarts at login
dad daemon uninstall # Remove the launchd agentWith a GitHub token configured (see GitHub Token), the daemon polls every 60 seconds for open PRs where you're a requested reviewer or assignee and mints a queue unit for each. Open a review request on GitHub and it shows up here.
- Lazy narration — a unit's walkthrough is generated the first time you open it, not on poll, so PRs you never click cost nothing.
- Review from the drill-in — a queue row only opens the PR; you approve or request changes from inside the review, which posts a real GitHub review. There is no queue-level verdict. A PR you've reviewed stays out of the queue until its author pushes past the commit you reviewed.
- Re-read (⟳) — from a PR's review, re-fetch its live head SHA and regenerate the walkthrough from scratch, bypassing the cache — for when the author has pushed since you last looked.
- Refresh (↻) — the header button runs a poll on demand instead of waiting for the next tick; a "checked … ago" caption shows how fresh the queue is, and a toast reports what changed.
- Self-cleaning queue — every poll reconciles against GitHub: closed or merged PRs drop immediately; PRs no longer requested of you drop after two consecutive polls without them.
- Repo filter — a sidebar facets the queue by repo with per-repo "needs you" counts (a native dropdown on narrow screens), so a busy inbox stays scannable.
- Errors, not spinners — if narration fails, the review shows an explicit panel with a Retry button (and the raw diff below) instead of spinning forever.
Instead of reviewing files one by one, Diff Dad groups code changes into chapters by semantic behavior. An AI reads the entire diff and produces a reading order — setup first, then core changes, then wiring, then edge cases — with prose explaining what each group does and why.
Diff Dad picks a provider in this order:
--with=<cli>flag (forces a local CLI)- Provider configured on the settings page
- API key env vars —
ANTHROPIC_API_KEY, thenOPENAI_API_KEY, auto-route through the matching API when no provider is configured claude -p(Claude Code CLI),codex, thenpi— uses your existing subscriptions, no API key needed, but significantly slower due to harness overhead
Open the settings page (/settings in the command center, or the Settings view in a PR review) to choose between:
- Anthropic API — requires
ANTHROPIC_API_KEY(recommended) - OpenAI — requires OpenAI API key
- Ollama — local models, no key needed
- Claude CLI / Codex CLI / pi CLI — uses your existing subscription, no API key
dad --with=claude owner/repo#123 # force the Claude CLI even if a key is setDiff Dad needs a GitHub token to fetch PR data and post comments. It checks, in order:
DIFFDAD_GITHUB_TOKENenvironment variablegh auth token(GitHub CLI)- Token saved on the settings page
Narratives and recaps are cached at ~/.cache/diffdad/, keyed by the PR and its head commit — same commit = instant reload. Use --no-cache to regenerate, or dad cache clear to wipe the cache.
The daemon's review queue is durable state, not a cache, so it lives in the app-data dir instead: ~/Library/Application Support/diffdad on macOS, else $XDG_DATA_HOME/diffdad or ~/.local/share/diffdad. dad cache clear never touches it.
Before the chapters, Diff Dad surfaces what a reviewer needs first:
- TL;DR + verdict — one-line summary plus a
safe/caution/riskycall - Reading plan — an ordered list of where to start and what to look at next, with one-click jumps to the relevant chapter
- Concerns — Socratic questions about likely defect classes (logic, state, timing, validation, security, test gaps, API contracts, error handling) with citations to the diff
Per-file risk is computed from churn, criticality keywords (auth, migration, payment, …), inbound import refs, and test-gap heuristics, then fed to the LLM as hints so the reading plan is risk-ordered. Concerns can be dismissed, restored, jumped to in the diff, or turned directly into a GitHub comment / draft review comment.
For drive-by help or returning to a stale PR, the Recap tab answers “what is going on here?” instead of “what might be wrong?” It gathers PR body text, linked issues, commits, force-push events, review threads, CI status, and latest reviews, then summarizes:
- Goal — the PR’s intended outcome
- State of play — done, WIP, and not started
- Decisions & alternatives — cited choices and direction changes
- Blockers — failing checks, unanswered review questions, TODOs, or thrash
- Mental model — core files, touchpoints, and a small ASCII sketch
- How to help — concrete ways a teammate can unblock the work
Recaps are generated on demand and cached separately from review narratives.
The AI groups hunks across files by behavior, not by filename. Each chapter has a title, a why-it-matters block, and narrative prose explaining the change. Chapters can reference the same hunk when it's relevant to multiple behaviors.
Review comments from GitHub appear inline next to the relevant code lines, including existing multi-line ranges. Comments you post from Diff Dad sync back to GitHub as real review comments, with support for added lines, removed lines, replies, and shift-click multi-line selections. Bot comments (Greptile, CodeRabbit, etc.) are clustered into collapsible groups with replies.
An SSE connection streams narrative generation progress, partial narratives from API providers, recap completion, and GitHub updates. New comments, CI status changes, and check runs appear in real time. Comments you post are broadcast instantly via the server — no waiting for the next poll.
- Density — toggle narration between terse (1 sentence), normal, and verbose per chapter
- Re-narrate — rewrite a chapter's narration through a different lens (security, performance, API consumer)
- Ask AI — ask a question about a specific chapter's code changes
- Mark reviewed — track your progress through the PR, persisted across page reloads
Submit reviews directly from the UI — Comment, Approve, or Request Changes. Inline comments are posted to GitHub along with your summary, and the submit dialog can draft or polish that summary with AI using reviewed chapters, draft comments, and raised concerns as context.
| Key | Action |
|---|---|
j / k |
Next / previous chapter |
r |
Toggle reviewed on current chapter |
c |
Open comment composer on hovered line |
s |
Open / close submit review dialog |
? |
Show shortcuts help |
Esc |
Close open panels |
Configurable on the settings page:
- Story structure — chapters (cards), linear (continuous flow), outline (collapsed)
- Layout — TOC sidebar or full-width linear
- Density — comfortable or compact
- Narration density — terse, normal, or verbose default
Monorepo with three packages:
packages/cli/ Bun CLI + Hono server
packages/web/ React + Vite reviewer UI
packages/site/ Astro marketing site
The CLI fetches the PR, generates the narrative, starts a local Hono server, and opens the browser. The frontend is a static Vite build served by the Hono server. All GitHub API calls go through the CLI server — the frontend never talks to GitHub directly.
- Runtime: Bun
- Server: Hono
- Frontend: React 19, Vite, Zustand, Tailwind CSS v4
- Site: Astro
- AI: Vercel AI SDK (multi-provider) or local CLIs (
claude,codex,pi) - Syntax highlighting: Shiki (github-light/dark themes)
- Markdown: Custom renderer with DOMPurify sanitization
bun install
bun run dev # Start Vite dev server (frontend only)
bun run build # Build frontend
bun run build:bin # Build standalone binary
bun run test # Run tests
bun run typecheck # Type-check CLI, web, and site
bun run eval # Run narrative eval fixturesTo test end-to-end, build the frontend first, then run the CLI:
bun run build
cd packages/cli && bun run src/cli.ts review owner/repo#123MIT