From revdiff
Review diffs and files with inline annotations in a terminal TUI overlay. Works with git, hg, and jj repos. Also answers usage and configuration questions.
How this skill is triggered — by the user, by Claude, or both
Slash command
/revdiff:revdiff optional: ref(s), "all files", or file pathoptional: ref(s), "all files", or file pathThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Review diffs with inline annotations using revdiff TUI in a terminal overlay. Works in git, hg, and jj repos (auto-detected).
Review diffs with inline annotations using revdiff TUI in a terminal overlay. Works in git, hg, and jj repos (auto-detected).
--only mode)If the user asks a question about revdiff (configuration, themes, keybindings, installation, usage) rather than requesting a review session, consult the reference files in references/ and answer directly. Do NOT launch the TUI for informational questions.
references/install.md — installation methods and plugin setupreferences/config.md — config file, options, colors, chroma themesreferences/usage.md — examples, key bindings, output formatIf the user says things like "locate my review", "use my latest revdiff annotations", "pull up the review I just did in another terminal", or "what did I annotate earlier" — the user ran revdiff outside this plugin flow and wants Claude to process the stored annotations. Read the most recent file from the persistent history directory via the helper script, then process the annotations through Step 3.5 classification as if they had come from a fresh launcher call:
${CLAUDE_SKILL_DIR}/scripts/read-latest-history.sh
The script resolves the history dir from $REVDIFF_HISTORY_DIR (default ~/.config/revdiff/history), finds the repo subdir via VCS root basename (jj/git/hg), and prints the newest .md file found. Each history file contains a header (path, refs, and — when available — a git commit hash), the annotations in ## file:line (type) format, and the raw git diff for annotated files. The commit: line and diff block are captured from git only; in hg/jj repos the diff block will be empty and no commit hash is recorded. See references/usage.md "Review History" section for directory layout, stdin/only handling, and override options.
When the user asks to open an in-session review in revdiff (the conversation already contains review comments produced earlier in the session), write those comments to a temp file (e.g. /tmp/revdiff-review-XXXXXX.md) using the format documented in references/usage.md ("Output Format" section), then run the normal launcher flow (Step 1 ref detection, Step 2 invocation) with --annotations=<temp-path> appended. Step 3 onward handles the curated annotations as usual.
Some review targets are not the current repo state: a GitHub PR diff, a patch file on disk, or git format-patch -1 --stdout output. Pipe the unified diff into revdiff --stdin and the input is parsed as a real multi-file diff (one tree entry per file, hunk navigation, per-file annotations) instead of a context-only buffer. revdiff auto-detects the unified-diff signature; on a malformed patch the input falls back silently to raw-text mode.
Use this instead of the normal launcher flow when:
gh pr diff output", or supplies a patch URL/pathExample invocations (route through the same launcher resolver as the normal flow):
gh pr diff 123 | "$("${CLAUDE_SKILL_DIR}/scripts/resolve-launcher.sh" launch-revdiff.sh "${CLAUDE_PLUGIN_DATA}")" --stdin
git format-patch -1 --stdout | "$("${CLAUDE_SKILL_DIR}/scripts/resolve-launcher.sh" launch-revdiff.sh "${CLAUDE_PLUGIN_DATA}")" --stdin
cat /tmp/feature.patch | "$("${CLAUDE_SKILL_DIR}/scripts/resolve-launcher.sh" launch-revdiff.sh "${CLAUDE_PLUGIN_DATA}")" --stdin
--stdin is mutually exclusive with refs, --staged, --only, --all-files, --include, --exclude, and --annotations, so do not combine with the Step 1 ref detection — go directly to Step 3 once the launcher returns. Annotations come back keyed by the real file paths from the diff (not by --stdin-name).
which revdiff
If not found, guide installation:
brew install umputun/apps/revdiffAll-files mode: If $ARGUMENTS matches "all files", "all-files", or "browse all files" (with optional "exclude " parts), use all-files mode:
--all-files to the launcher--exclude=<prefix>--all-files --exclude=vendorFile review mode: If $ARGUMENTS is a single token that points at a file on disk (e.g., docs/plans/feature.md, /tmp/notes.txt, README.md, main.go, file.blah), treat it as file review:
test -f "$ARGUMENTS" — if the file exists, it's file review mode/ or ./, or contains / and has a file extension (e.g., src/app.go), even when the file is not yet reachable from the current directory--only=<filepath> (no ref argument)main — both a branch name and a potential filename without extension) → prefer ref mode; ask the user only if neither test -f nor git rev-parse --verify resolvesRef mode: If $ARGUMENTS contains explicit ref(s) (e.g., HEAD~1, main, or main feature for two-ref diff), use as-is.
Auto-detect: If no ref provided, run the smart detection script:
${CLAUDE_SKILL_DIR}/scripts/detect-ref.sh
The script outputs structured fields:
branch, main_branch, is_main, has_uncommitted, has_staged_onlysuggested_ref — the ref to pass to revdiff (empty = uncommitted changes)use_staged — if true, pass --staged to the launcher (staged-only changes detected)needs_ask — if true, ask the user before proceedingWhen use_staged: true, pass --staged to the launcher. This means all changes are in the index (staged) with nothing unstaged — without --staged, revdiff would show an empty diff.
When needs_ask: true (on a feature branch with uncommitted changes), use AskUserQuestion:
When needs_ask: false, use suggested_ref directly:
--staged (staged changes)HEAD~1 (last commit)When you are launching revdiff for the user (e.g., right after a refactor or analysis), pass --description="..." so the info popup (i key) explains what the change is and what to look at — markdown is supported. For longer prose, write the markdown to a temp file and pass --description-file=/tmp/revdiff-desc-XXXXXX.md. The two flags are mutually exclusive; both are optional. Skip when there's no useful context to add.
When the recent change likely created new untracked files (new packages, new test files, new docs, new scripts that haven't been git add-ed yet), pass --untracked so those files appear in the tree. Use this in working-tree mode (no ref, no --staged); skip it for ref-to-ref reviews where untracked files are not part of the historical diff.
Run the launcher through the override-chain resolver:
"$("${CLAUDE_SKILL_DIR}/scripts/resolve-launcher.sh" launch-revdiff.sh "${CLAUDE_PLUGIN_DATA}")" [base] [against] [--staged] [--untracked] [--only=file1] [--all-files] [--exclude=prefix] [--description=text|--description-file=path]
The resolver and launcher MUST run in the same bash invocation — the resolver runs as a sub-shell substitution so the resolved path is consumed immediately as the executable. The resolver checks user → bundled (see references/install.md for override paths) and prints the first-found absolute path. Fall-through to the bundled launcher is the default when no overrides exist.
Failure mode: if the resolver fails (no launcher in any layer), the command substitution produces an empty string and bash reports : command not found with exit 127. The resolver's stderr (error: launcher not found in override chain: launch-revdiff.sh) is preserved on the same output stream — check it to confirm the override path is correct (executable bit set, file present in one of the two layers).
IMPORTANT — long-running command: The launcher blocks until the user finishes reviewing in the TUI overlay, which can exceed the default bash tool timeout on many harnesses. Set the bash timeout parameter to the maximum your harness allows (e.g. 1800000 or higher on OpenCode). The resolver itself returns in milliseconds — the timeout cap applies to the launcher only. Do NOT use run_in_background for this — background-task handling is unreliable for interactive TUI launchers (processes may be killed unprompted, and polling loops can leave the session idle after the review finishes). If the review outlasts the timeout cap, the fallback in Step 3 handles it.
The script:
The bundled launcher sets REVDIFF_EXIT_CODE_ON_ANNOTATIONS; exit 10 means annotations were captured and is not a launcher failure. Treat other nonzero statuses as failures.
Collecting launcher output: In the normal case the launcher returns synchronously with annotations on stdout — process them as described below. If the bash tool reports exit 10, read stdout and process it as annotations; do not call it a failure. If the bash tool instead reports a timeout (on Claude Code the task keeps running in the background after the 10-minute cap; on other harnesses it may be killed outright), revdiff is almost certainly still open in the overlay. Do NOT retry the launcher. Use the fallback:
$TMPDIR when set, falling back to /tmp):
output_file="$(ls -t "${TMPDIR:-/tmp}"/revdiff-output-* 2>/dev/null | head -1)"
if [ -n "$output_file" ] && [ -f "$output_file" ]; then
cat "$output_file"
fi
This fallback is safe because revdiff writes the output file atomically on exit — there is never a partial read.
If the script produces output, the user made annotations. The output format is:
## file.go:43 (+)
use errors.Is() instead of direct comparison
## store.go:18 (-)
don't remove this validation
Each annotation block has:
## filename:line (type) — which file and line, (+) = added, (-) = removed, (file-level) = file noteSplit annotations into two categories:
Explanation requests — annotation matches either rule (case-insensitive):
??, ???, etc.) — a language-neutral shortcut for "please explain"explain, remind, describe, what is, what are, how does, how do, clarifyThese are questions the user wants answered, not code changes.
Code-change directives — everything else. These are instructions to modify code.
If explanation requests are found:
Answer each explanation request — read the referenced code, generate a clear markdown explanation
If there are also code-change directives in the same batch, note them as pending (they carry over to Step 4 after the explanation loop)
Enter the explanation loop:
a. Write the explanation to a temp markdown file (e.g., /tmp/revdiff-explain-XXXXXX.md)
b. Launch revdiff with --only=/tmp/revdiff-explain-XXXXXX.md via the launcher script — this opens the explanation as a scrollable markdown view with TOC sidebar
c. If user quits without annotations → explanation accepted, clean up temp file, proceed:
The explanation loop continues until the user quits without annotating. This allows a natural back-and-forth dialogue where the user can ask for more detail or corrections on specific parts of the explanation.
If no explanation requests — all annotations are code-change directives, proceed directly to Step 4.
Enter plan mode (EnterPlanMode) to analyze code-change annotations:
After plan approval, fix the actual source code. Each annotation is a directive.
After fixing (or after "Continue review" from Step 3.5), run the launcher script again with the same ref. The user can:
When the script produces no output, the review is complete. Inform the user.
User: "revdiff HEAD~1"
→ launch revdiff in tmux popup with HEAD~1 diff
→ user annotates: "handler.go:43 - use errors.Is()"
→ user quits
→ annotations captured
→ enter plan mode: "add errors.Is() check at handler.go:43"
→ user approves
→ fix applied
→ re-launch revdiff HEAD~1
→ user sees fix, quits without annotations
→ "review complete"
User: "revdiff HEAD~3"
→ launch revdiff in tmux popup with HEAD~3 diff
→ user annotates: "server.go:72 - explain what this mutex protects"
→ user quits
→ annotation classified as explanation request (starts with "explain")
→ Claude reads server.go:72, generates markdown explanation
→ writes to /tmp/revdiff-explain-XXXXXX.md
→ launch revdiff --only=/tmp/revdiff-explain-XXXXXX.md (explanation view with TOC)
→ user reads explanation, annotates: "what about the race condition on line 80?"
→ Claude refines explanation, rewrites temp file
→ re-launch revdiff --only=/tmp/revdiff-explain-XXXXXX.md
→ user reads updated explanation, quits without annotations
→ explanation accepted, clean up temp file
→ re-launch revdiff HEAD~3 (back to diff review)
→ user quits without annotations
→ "review complete"
User: "revdiff all files exclude vendor"
→ launch revdiff with --all-files --exclude=vendor
→ user browses all tracked files, annotates as needed
→ same annotation loop as above
User: "revdiff docs/plans/feature.md"
→ test -f docs/plans/feature.md succeeds → file review mode
→ launch revdiff with --only=docs/plans/feature.md (context-only view, no ref)
→ user annotates prose: "section 'Open questions':3 - drop this, resolved"
→ user quits
→ same annotation loop as above (applies to the file content)
npx claudepluginhub umputun/revdiff --plugin revdiffDisplays git diffs with syntax highlighting, split view, and word-level diff in terminal UI or web preview. Supports watching changes, filtering files, comparing branches, and AI-powered code review explanations.
Self-review code changes using GitHub-style visual diff viewer (difit). Add comments on specific lines in browser; comments output to Claude for applying edits via git diff.
Code review with semantic diffs, expert routing by file type, and auto-task creation for critical issues. Works on staged changes, files, or PRs.