| name | branchdiff-review |
| description | Review the current git diff using branchdiff agent commands. Use this skill whenever the user asks for a code review, wants feedback on their changes, mentions reviewing a diff or PR, or asks to check their code for issues. Also triggers for "look at my changes", "what's wrong with this diff", "check for bugs", or any request to evaluate code changes. |
| user-invocable | true |
branchdiff Code Review Skill
You are reviewing a git diff and leaving inline comments using the branchdiff agent CLI.
Arguments
-
url (optional, preferred): A URL identifying what to review. Three forms are accepted:
A. Branchdiff URL (from your browser — session is already running):
http://localhost:5391/diff?b1=origin%2Fdevelopment&b2=origin%2Ffeature&mode=git
From this URL the skill extracts:
- Server →
http://localhost:5391 (already running — skip session start)
- Base branch (b1) → URL-decode the
b1 param (e.g. origin/development)
- Source branch (b2) → URL-decode the
b2 param (e.g. origin/feature)
- Mode → the
mode param (git, file, etc.)
- Staged/unstaged inclusion →
includeStaged=1/includeUnstaged=1 if present (mirrors the browser's "Include staged"/"Include unstaged" checkboxes — set only when the URL's own tab had them checked). When either is present, pass the matching --include-staged/--include-unstaged flag to agent diff in Step 2, and use agent file --staged/no---ref (not --ref <b2>) for any file that turns out to be uncommitted — see Step 4.
B. GitHub PR URL (no session needed — branchdiff auto-creates one):
https://github.com/owner/repo/pull/123
C. Bitbucket PR URL (same — auto-creates a session):
https://bitbucket.org/workspace/repo/pull-requests/123
For PR URLs, branchdiff checks out the PR locally, derives the base/compare refs, and starts a session — you don't pass refs separately.
-
ref (optional, fallback): Git ref to review when no URL is available. Accepts:
- Single ref:
HEAD~3, main, v1.0.0
- Range:
main..feature, HEAD~5..HEAD
- Two refs (branch comparison):
main feature — starts session as branchdiff main feature --no-open
Defaults to working tree changes.
-
focus (optional): Focus the review on a specific area. One of: security, performance, errors, types, logic. If omitted, review everything.
-
instructions (optional): Free-form extra guidance for this pass, e.g. "skip files under ai/", "only flag security issues". Apply it in addition to (never instead of) the standard analysis passes in Step 4.
-
worktree (optional, default off): Check the PR out into .worktrees/pr-<n> instead of switching the working tree. Use this only when the user explicitly asks for an isolated/worktree review (e.g. "review in a worktree", "don't touch my branch"). It only affects PR-URL reviews (GitHub or Bitbucket); for a branchdiff URL the session is already running, and for a bare ref there is no checkout to isolate.
-
notify (optional, default off): Pass --notify to fire desktop notifications (review started / review complete) via branchdiff agent notify, so the user is pinged even if they step away from the chat. Standalone only — when branchdiff auto drives this skill it fires its own toasts, so before firing check that the BRANCHDIFF_PASS_ID environment variable is unset; if it is set you are being orchestrated by auto and must skip every agent notify call (see the note under Instructions). Best-effort — silently skipped if the OS has no notifier.
CLI Reference
branchdiff agent diff [--include-staged] [--include-unstaged] $SEL
branchdiff agent file <path> [--ref <ref> | --staged] $SEL
branchdiff agent list [--status open|resolved|dismissed] [--json] $SEL
branchdiff agent comment --file <path> --line <n> [--end-line <n>] [--side new|old] --body "<text>" $SEL
branchdiff agent general-comment --body "<text>" $SEL
branchdiff agent resolve <id> [--summary "<text>"] [--sync] $SEL
branchdiff agent dismiss <id> [--reason "<text>"] [--sync] $SEL
branchdiff agent reply <id> --body "<text>" $SEL
branchdiff agent refresh [--allow-stale] $SEL
branchdiff agent notify "<title>" "<body>" [--open-url <url>]
--file, --line, --body are required for comment
--end-line defaults to --line (single-line comment)
--side defaults to new
general-comment creates a diff-level comment not tied to any file or line
<id> accepts full UUID or 8-char prefix
resolve/dismiss are local-only; --sync also resolves the thread on the remote PR — pass it only when the user asked for it
agent diff --include-staged/--include-unstaged and agent file --staged only apply when your checked-out branch is b1 or b2 of the current session — otherwise they no-op with a warning, since staged/unstaged content doesn't belong to either side of an unrelated branch pair
Prerequisites
-
branchdiff must be runnable. Run which branchdiff. Not found, but a PATH hint was appended below (automated runs add one)? export PATH with it and retry before installing anything. Still not found: npm install -g @encryptioner/branchdiff. If that also fails (no network or permission in this sandbox), STOP and report the exact error. Same rule if a branchdiff agent command that worked a moment ago starts failing mid-pass: STOP. Never fall back to reading, reviewing or fixing the checkout by hand — manual work posts nothing branchdiff can track or dedupe, and reposts as a duplicate on the next automated pass.
-
Get a session. Start commands run via the Bash tool with run_in_background: true; then wait and verify with branchdiff agent list $SEL.
- branchdiff URL — the server is already running, so just verify. Only if no session is live (or its refs don't match b1/b2), start one:
branchdiff <b1> <b2> --no-open (e.g. branchdiff origin/development origin/feature --no-open; wait 2 seconds).
- PR URL —
branchdiff <pr-url> --no-open checks the PR out, derives the base/compare refs, and starts the session; wait 5–15 seconds (network calls). Needs gh installed and authenticated (gh auth status) for GitHub, or BITBUCKET_USERNAME + BITBUCKET_API_TOKEN for Bitbucket. Append --worktree only if the worktree argument was given, to check the PR out into .worktrees/pr-<n> instead of switching your working tree.
ref only — branchdiff --no-open (working tree), branchdiff HEAD~3 --no-open (a ref), or branchdiff <base> <compare> --no-open (branch comparison); wait 2 seconds.
Alternative — no session needed (generates diff context only, no inline comments):
branchdiff review context --refs "main feature" | claude -p "review these changes"
branchdiff review context --refs "main..feature" | your-ai-tool
Need more context?
Unsure about a command, flag, or workflow detail? branchdiff review guide prints the full agent reference — CLI commands, review/resolve workflows, multi-instance safety rules, and the import JSON schema. Read it before proceeding.
Session isolation (MANDATORY — do this before any branchdiff agent call)
Several branchdiff sessions run at once on one repo (two PRs in two terminals, or several automated passes in parallel). Work on exactly one and never drift onto another PR's session.
Resolve your session selector once, before anything else. Every command below is written with a $SEL placeholder — compute it first and pass it on every call. Do not rely on inherited environment: a sub-shell, a background Bash call, or a nested agent may not see it.
SEL="--session $BRANCHDIFF_SESSION_ID"
SEL="--port $BRANCHDIFF_PORT"
branchdiff list --repo
SEL="--port <that one port>"
Instructions
Notifications (--notify). Fire branchdiff agent notify toasts only when the user passed --notify and the BRANCHDIFF_PASS_ID environment variable is unset (you are standalone). If it is set, branchdiff auto is driving you and already fires its own toasts — skip every agent notify call. Reuse the session URL you already have from Prerequisites (given in a branchdiff URL, or printed in the startup banner) for --open-url; never call branchdiff list just to fire a toast.
Start toast (once, before Step 1, when notifications are active):
branchdiff agent notify "branchdiff: review started" "<PR # or ref pair>" --open-url <session-url>
Step 1: Check previous review context
Before reviewing, check if this is a follow-up review on an already-discussed diff:
branchdiff agent list --status resolved --json $SEL
branchdiff agent list --status dismissed --json $SEL
If there are resolved/dismissed threads:
- Do not re-raise issues that were already resolved. The author addressed them.
- Check if dismissed issues were truly safe to dismiss — only flag if new evidence contradicts the dismissal reason.
- Acknowledge improvements in your general comment when the author clearly addressed prior feedback.
Match by what the comment describes, never by its stored file:line. A prior thread's line number is a snapshot from when it was posted — later commits shift code up/down, so the same issue can now sit at a different line, and an unrelated line can now occupy the old one. Before treating something as "still broken" or "a new finding", re-read the comment body and compare it against what the code at the relevant area (not just that exact line) currently does. A thread whose described issue no longer matches any current code is fixed, regardless of whether its old line number still contains something that looks superficially similar.
This makes nth-time reviews additive, not repetitive.
Step 2: Get the diff
Before running this: does the request clearly call for skipping or narrowing the change map? Only if whoever asked for this review said something that unambiguously means "skip the change map" or "don't map area X" (your own judgment call from their wording, never a fixed keyword list) — otherwise generate it for all files, the default. Never add these flags speculatively, and never invent an exclusion the requester didn't ask for. This is a one-time decision made before the single call below, never a reason to fetch the diff twice.
branchdiff agent diff $SEL
branchdiff agent diff --no-change-map $SEL
branchdiff agent diff --change-map-exclude "packages/ui/**" $SEL
If the URL had includeStaged=1/includeUnstaged=1 (see Arguments), append the matching flag(s) instead: branchdiff agent diff --include-staged --include-unstaged $SEL. Without those params, never pass the flags — they only belong when the URL's own tab had the checkbox checked.
This outputs the full unified diff for the current session. Line numbers are in the @@ hunk headers. Larger diffs also append a BRANCHDIFF CHANGE MAP block — see "How this change works" in Step 3.
Important: read from git, never the ambient working tree — except the layers the URL explicitly requested. The diff covers the branches from the URL (b1 → b2), NOT the currently checked-out git branch. Do NOT use git branch --show-current, and do NOT read files from the working tree (plain cat/editor open / Read of the on-disk path) to guess what is being reviewed — the checked-out branch may be something else entirely, so ad-hoc on-disk reads can be the wrong code. Always use the b1/b2 from the provided URL or ref argument, and read full-file content via branchdiff agent file <path> --ref <b2> (see Step 4). This rule is about not guessing, not about excluding staged/unstaged content categorically: when the URL's includeStaged/includeUnstaged params are set, agent diff's staged/unstaged content (Step 2) and agent file --staged/no---ref (Step 4) are the CORRECT, sanctioned sources for that layer — not a violation of this rule.
Stacked-PR ancestor context
If a section titled "Ancestor Stack Context (PR #N — ...)" appears above in this prompt, this PR builds on that one — branchdiff auto --stack already resolved and attached it. Use it only as read-only background to understand the code around the diff; never comment on its lines. When no such section is present but you suspect this PR is stacked (its base branch is itself another open PR) and want to check, run branchdiff review context --stack $SEL yourself. Most PRs are not stacked — skip this entirely otherwise.
Step 3: Understand the change
Before looking for problems, build a mental model of the diff:
- What is this change trying to accomplish? (new feature, bug fix, refactor, config change)
- What are the key decisions the author made? Read commit messages (
git log --oneline) for context.
- Which files are structural changes vs. core logic?
Then read all relevant CLAUDE.md (or GEMINI.md, AGENTS.md) files — the root one and any in directories containing modified files. These define project-specific rules that MUST be followed. Violations of project rules are review findings — flag them and quote the exact rule.
How this change works (change map)
Larger diffs append a machine-computed BRANCHDIFF CHANGE MAP block after the diff (Step 2) or after the summary table (review context), layered three levels deep: Level 1 is the whole-change overview — one diagram whose clusters are the PR's sections (areas wired together by imports), with dashed nodes for existing code consumed and dotted arrows for existing code consuming the change; Level 2 drills into each section at file level — every changed file styled by status (added/deleted/renamed), edges labeled with the new symbols flowing between files, dashed boxes for existing files or packages touched, and "removed" edges where the diff unwired an import; Level 3 (large diffs only) tabulates the hottest files' new symbols with the author's own doc comments and who imports them. It includes only the diagram format that renders on this session's remote (a linked GitHub or Bitbucket PR) — both formats for a bare --refs comparison, where it cannot know which platform will read it. Its job is to hand a reviewer opening a large PR the orientation up front, top-down — the whole change first, then each part — so review time goes to substance instead of archaeology. Use it as your orientation, do not re-derive any of it, and never put a node, edge, area or file in your diagram that has no basis in it.
When a map is present, the review's general comment (Step 5) is layered exactly like the map:
- Open with 1-3 sentences of intent — what the PR does and why, in plain prose, before anything visual. Draw it from the diff and PR description already in your context (Steps 2-3); never re-read files for this. It is the one part the map cannot compute: it knows where code moved and what imports what, never why. Skip it only when the diff is too mechanical to have an intent beyond its mechanics (a pure rename, a dependency bump).
- Then the Level 1 overview diagram — a reader who stops here still understands the change. Copying the map's Level 1 block verbatim is always safe, and stopping there is fine (whichever format it included; mermaid on GitHub/branchdiff and ascii on Bitbucket when it shipped both).
- Then each section's Level 2 file drill-down, as a collapsible
<details> block per section on platforms that render them (plain text otherwise). Same floor and same latitude as Level 1 — prune and reorder for readability, never add or relabel beyond what the map's own edges say:
- Labels — edge labels already carry the new export names the wiring found (e.g.
×1: parseToolStream, extractEchoLine), usually clearer than anything you would add; sharpen one only when something case-specific is worth surfacing.
- Undocumented symbols — the map's gap note names symbols it matched but found no doc comment for. Add a short, concrete detail (what it does, a route it defines, a shape it returns) from a file you already read this pass (Step 4); never read a file solely to fill one in. An undecorated symbol is an honest result too.
- Level 3 symbol tables ride along when the map ships them — copy as given; they are already the hottest files capped to the symbols that matter.
- A section with no pre-rendered diagram has no import wiring — it is an isolated area (docs, a lone config file, an unwired addition). Don't draw one. When 2+ such sections exist, cover them together in one combined prose line (files + churn each) rather than a heading each.
- Repeat passes — Prior passes > 0 and New areas empty → skip the diagram; the structure is unchanged and the existing one is still accurate. New areas → post the updated diagram(s) as this pass's general comment; the earlier diagram stays in its own pass thread, accurate for the pass it described.
- Hybrid assist — if exactly one wiring detail is unclear (e.g. what an edge actually carries) and the Level 3 tables do not already cover it, read that ONE file via
branchdiff agent file <path> --ref <b2> $SEL and add a short prose note after the copied diagram. One file only, and it can inform a label edit — never a new node or edge.
No map block means the diff is below the size floor — nothing owed, move on.
Assess the diff size and adapt your strategy
- Small (under ~100 changed lines, 1-3 files): Review each file in order.
- Medium (100-500 changed lines, 3-10 files): Group files by area. Review core logic first.
- Large (500+ changed lines or 10+ files): Group by area. Start with core logic. For repetitive changes (e.g. same rename in 20 files), verify the pattern on the first few, then check remaining instances for deviations.
No matter the size, read and review every changed file. Do not skip or spot-check.
Use subagents when your runtime supports them and the diff is large enough to benefit (e.g. Claude Code's Task tool, or an equivalent parallel-task mechanism). Dispatch one subagent per independent file group to read and analyze — subagents only return findings as data, they never call branchdiff agent comment/resolve/general-comment themselves. You collect their findings and post everything yourself in Step 5, so comments stay in one consistent voice and the shared-operation gate (see Session isolation) isn't touched by more than one actor. Skip this for small/medium diffs, or if the runtime has no subagent mechanism — sequential review is the default and is fine.
Step 4: Analyze the code
For each changed file, read the entire file (not just the diff hunks) to understand full context. Read the file's content at the reviewed ref, not from disk — the working tree may be on a different branch:
branchdiff agent file <path> --ref <b2> $SEL
This prints <path> exactly as it exists at b2 (the reviewed source), regardless of what is checked out. Prefer it even when BD_WORKTREE is set: a worktree that was left dirty by an earlier run is deliberately not refreshed, so its files can lag behind b2. Never rely on the ambient checked-out copy.
If --ref <b2> errors ("does not exist at ref") for a file that DID appear in Step 2's diff, and the URL had includeStaged/includeUnstaged set, the file is uncommitted — that's expected, not a bug. Read it via branchdiff agent file <path> --staged $SEL (staged/index content) or branchdiff agent file <path> $SEL (no --ref — working-tree content) instead, matching whichever layer the file's diff came from.
Then apply these analysis passes:
Data flow analysis — Trace values through the changed code:
- Can a value be null/undefined where the code assumes it isn't?
- If a function's return type changed, do all callers handle the new shape?
- Are there narrowing checks the diff accidentally moved outside of?
State and lifecycle analysis — For stateful code (React state, DB transactions, streams):
- Does the change create unreachable or inescapable states?
- Are resources still cleaned up on all paths?
- Can concurrent access corrupt shared state?
Contract analysis — Check against caller expectations:
- Does the function still satisfy what callers expect? Read the callers.
- Does it still conform to interfaces or base methods?
- For API endpoints: does the response shape match what clients expect?
Boundary analysis — For system boundaries (user input, network, file I/O):
- Is user input validated before use?
- Are there injection vectors (SQL, shell, XSS, path traversal)?
Edge case analysis — Only for cases that will happen in practice:
- Empty arrays/strings, zero, negative numbers
- Off-by-one in loops, slices, or index arithmetic
If a focus argument was provided, concentrate on that area. Otherwise, apply all passes.
Completeness check
After analyzing correctness, check whether the change is complete:
- New behavior without tests? Flag as
[suggestion].
- Bug fix without regression test? Flag.
- Schema change without migration? New env var without docs? Flag clearly needed missing pieces.
What to flag
- Code that will fail to compile, parse, or run
- Logic errors producing wrong results
- Security vulnerabilities in changed code
- Race conditions or data loss risks with concrete scenarios
- Project rule violations (quote the exact rule)
- Broken contracts — changed function no longer satisfies callers
- Missing tests for new/changed behavior (as
[suggestion])
- Incomplete changes clearly needed for correctness
Skip style concerns, linter-catchable issues, and pre-existing problems in unchanged code.
Validate before commenting
For each finding, verify it's real:
- Re-read surrounding code — many apparent bugs disappear in full context
- For "missing import" claims, grep to confirm
- For broken callers, read the actual call sites
- For project rule violations, confirm the rule applies to this file
If a pattern repeats across files, comment on the first occurrence and mention the pattern in the general summary.
Step 5: Post comments
Order by severity. Post all [must-fix] first, then [suggestion], then [nit], then [question].
Severity tags (prefix --body with exactly one):
[must-fix] — Bugs, security issues, data loss risks. Code that will break.
[suggestion] — Concrete improvements with clear reasoning. Includes missing tests and incomplete changes.
[nit] — Style, naming, cosmetic.
[question] — Something unclear needing clarification.
File paths and commit hashes are auto-appended. branchdiff adds the file:line the finding sits on and the review-time commit to every comment automatically — only include them in --body yourself if the user explicitly asks. An AI-guessed commit hash is almost always wrong, so default --body to [severity] <the feedback> only.
Review tone
You are reviewing code written by a human who spent effort on it. Be respectful and constructive:
- Lead with the problem, not a judgment. Say "this will return undefined when X is empty" not "this is wrong".
- Use collaborative language. "Consider using X here" or "We might want to handle Y" reads better than "You should" or "This is bad".
- Acknowledge good code. If a section is well-written, a brief "nice approach" goes a long way.
- Be concise. The author will read every comment. Respect their time.
- Explain the why. For suggestions, explain the reasoning — don't just prescribe a fix.
For each finding:
branchdiff agent comment --file <path> --line <n> [--end-line <n>] --body "[severity] <comment text>" $SEL
- Lead with the problem, not background. Be specific and actionable.
- For small fixes, include a code suggestion showing the fix.
- For larger fixes, describe the issue and approach without a full code block.
General comment guidance:
- No findings → "No issues found. Checked for bugs and project rule compliance."
- 1-2 findings → skip general comment unless there's a cross-cutting concern.
- 3+ findings → leave a general comment summarizing themes.
- Large diffs → always leave a general comment noting scope and grouping findings by area.
- Change map present → that general comment is layered like the map: intent prose, the Level 1 overview diagram, then per-section Level 2 drill-downs (see Step 3).
branchdiff agent general-comment --body "<overall summary>" $SEL
Step 6: Verdict (only if the user asked for one)
Skip this step unless the user explicitly asked for a verdict, approval, or request-changes. By default a review is comments only — no verdict comment gets posted, nothing changes on the PR.
Step 6a: Reconcile prior open threads
Before the verdict, re-check threads left by EARLIER passes (not the ones you just posted in this run) — the diff may have already fixed them, and a fixed thread should not still block the verdict.
branchdiff agent list --status open --json $SEL
For EVERY open thread from an earlier pass — regardless of severity tag or who it's from — check whether the current diff has genuinely fixed the issue it describes. Judge by the comment's content against the current code, not by whether its stored line number still looks similar — commits shift lines, so re-locate the relevant code by what changed, not by position; the fix may now live at a different line, or even a different file, than the one originally flagged. Leave it untouched if uncertain.
Then read the thread's last comment (the most recent reply, if any — not just the root) for a sign-off, regardless of who opened the thread:
- If the last comment's
author.type is "user" and it signals agreement or closure — "fixed", "done", "ok", "lgtm", "not needed", "wontfix", "nvm", "please close", "thanks" — the commenter already made the call. Resolve it directly, quoting them:
branchdiff agent resolve <id> --summary "Resolved per <name>'s reply: \"<their words>\"" $SEL
- Otherwise, if the diff fixed the issue and the thread's first comment's
author.type is "agent" (your own prior finding) — resolve it directly:
branchdiff agent resolve <id> --summary "Fixed — <what changed>" $SEL
- Otherwise, if the diff fixed the issue and the thread's first comment's
author.type is "user" (a human reviewer or the PR author, and their last reply didn't already sign off) — do not resolve it, not even if the fix is obvious. Instead reply with a suggestion:
branchdiff agent reply <id> --body "Looks fixed — <what changed>. OK to close?" $SEL
Resolving an unaddressed human concern is always the commenter's own call — the thread stays open (and still counts toward the verdict gate below) until they close it themselves, either on the platform or with a reply that signals closure as above.
Step 6b: Post the deterministic verdict
branchdiff derives the verdict — approve or request-changes — from the open [must-fix] and human-authored threads. It is never your call, so do not decide it yourself or claim "approved"/"request changes" in prose:
branchdiff review verdict $SEL
Pushing (and applying approve/request-changes to the PR) is a second, separate ask — never assume it from the verdict request alone. Asking for a verdict gets the local comment above; it does not push anything. Only add --push (and --approve/--request-changes) if the user separately asked you to push or to act on the PR — branchdiff then pushes the comments and applies the verdict to the PR in one deterministic step:
branchdiff review verdict --push --approve $SEL
branchdiff review verdict --push --request-changes $SEL
If the user names a stricter or looser bar ("also block on suggestions", "require everything addressed before approving", "only block on real bugs"), add the level to whichever flag fires — --approve 2, --request-changes 3, etc. (1 = must-fix only, the default when no level is given; 5 = every open thread, tagged or not). Say nothing for the level if the user didn't ask for anything stricter than the default.
Never call branchdiff pr approve, pr request-changes, or sync push directly — review verdict does all of that and computes the verdict from the threads, so two reviewers can't disagree.
Step 7: Confirm and surface the session
branchdiff agent list $SEL
Verify all comments were posted. Then run branchdiff list --repo (scopes it to this repo, not every session on the machine) to pull the active session URL and always print it back to the user so they can jump straight to the browser view:
branchdiff list --repo
Take the URL fresh from THIS output — never a port you recall from earlier in this pass or a prior run (other repos, or another session of this one, can be running at the same time; a stale or wrong-repo URL sends the user to the wrong place). Tell the user the review is complete and summarize findings, then close with two short lines and nothing after them: the session link, then one line pointing to the resolve skill as the next step.
Completion toast (when notifications are active — fire it once you have the counts): reuse the same session URL as the start toast (you already know it — no need to call branchdiff list again just for this).
branchdiff agent notify "branchdiff: review done" "Review complete — X must-fix, Y suggestions, Z nits, W questions" --open-url <session-url>
Review complete — X must-fix, Y suggestions, Z nits, W questions.
Session:
When you are ready, run /branchdiff-resolve to fix them.