| name | dunk-review |
| description | Read user-authored review comments via `dunk comments` (or `.dunk/comments.json` for the active repo) and address each one. The TUI is for humans; agents drive the review through the CLI. |
Dunk review (extended with lazygit support)
Custom extensions on top of upstream (READ THIS BEFORE ACTING ON UPSTREAM RULES)
This skill is wrapped. The upstream dunk-review rules below have
been modified by these extensions — specifically how comment bodies
are parsed, how anchors are interpreted, and how drift state should be
read. Treat this section as authoritative wherever it conflicts with the
upstream rules below.
Body prefix
Every comment body in this project starts with a structured prefix:
[on=<scope>; in=<context>] <free text>
Parse the prefix BEFORE reading the rest of the entry. The prefix
determines how to interpret file, line, range, anchor, and
upstream's drift state.
Scope values (on=)
| value | meaning |
|---|
line | specific line(s) in a file |
file | all changes in this file |
dir | all changes under this directory tree |
commit | the whole commit (with sentinel file="@commit:<sha>") |
Context values (in=)
| value | meaning |
|---|
dirty | working tree (unstaged modifications) |
staged | the index (staged modifications, vs HEAD) |
commit:<40-hex-sha> | the diff of this specific commit vs its parent |
Anchor rule
Real anchor IFF on=line AND in=dirty. Sentinel 0000000000000000
for everything else.
Upstream's drift check ALWAYS reads the working tree to recompute the
anchor. An anchor only carries meaningful drift information when the
original line content came from the working tree too. For on=line; in=staged or on=line; in=commit:<sha>, the line numbers refer to
index or commit content (not the working tree), so a working-tree-based
anchor would silently lie. Those entries are sentinel-anchored.
Field conventions
| field | convention |
|---|
file = "<path>" | real repo-relative path; used for on=line, on=file, on=dir |
file = "@commit:<sha>" | sentinel; used only for on=commit |
anchor = "<16-hex-sha>" | real anchor; only present when on=line AND in=dirty |
anchor = "0000000000000000" | sentinel; ignore drift state when this appears |
line, range | real values when on=line; placeholder [1,1] for every other scope |
Drift state interpretation
Upstream's drift state (anchored or drifted: ...) is ONLY meaningful
when the anchor is real (on=line AND in=dirty). When the anchor is
sentinel, ignore drift state — the entry is intentionally non-anchored
by design, not "broken."
Resolution
Identical to upstream: dunk comments resolve <id>. The extension
markers don't change the resolution mechanism — only how to read entries.
Re-install / refresh
Regenerate this skill any time dunk or the extension definition changes:
dunk-lazygit skill path > <your-skills-dir>/dunk-review.md
Dunk is an interactive terminal diff viewer. The TUI is for the user — never run dunk diff, dunk show, or other interactive commands directly. Review comments live on disk in .dunk/comments.json (one committed file per repo). Drive them through the dunk comments CLI; only fall back to editing the JSON by hand when the binary isn't available.
If dunk comments list reports "No pending comments." (or .dunk/comments.json is missing/empty), the user has nothing pending; ask before doing speculative work.
Workflow
1. dunk comments list # see every pending comment with drift
2. for each comment: #
- dunk comments show <id> # comment + 10 lines of surrounding code
- fix the underlying issue # use Read / Edit, not the TUI
- dunk comments resolve <id> # remove that entry from the file
3. tell the user what you changed # one paragraph max, cite file:line
show is the preferred starting point: it carries enough context for most fixes without re-reading the whole file. Only fall back to a full Read when the change spans far outside the hunk.
CLI surface
dunk comments # bare, equivalent to `list`
dunk comments list [--json] # list pending comments + drift status
dunk comments show <id> [--json] # print one comment plus the hunk lines
dunk comments resolve <id> [<id>...] # atomically remove one or more entries
list prints #<id> <file>:<start-end> followed by the indented body. Drifted entries show drifted: missing-file | out-of-range | anchor-mismatch on the header line.
show defaults to 10 lines of surrounding post-image context; pass --context <N> to widen or narrow it. In-range lines are prefixed >, surrounding lines with a leading space.
--json on list returns { schema, comments: [{ id, file, line, range, anchor, body, state, reason? }] }. --json on show returns a single comment object plus a context field: { window: [start, end], total, lines: [{ number, text, inRange }] } — lines are byte-stable against the file on disk.
resolve is all-or-nothing: if any id is missing it errors and writes nothing. Pass several ids to remove them in one atomic write.
- Empty list exits 0; missing ids in
show/resolve exit non-zero with a clear message. No daemon, no MCP — the command writes .dunk/comments.json directly.
Operating principles
- No raw snippets in messages back to the user. If you summarize what was fixed, cite
file:line only — the user can open the file. Mirrors the on-disk schema, which itself stores zero snippet content.
- Resolve = remove the entry. When a comment is addressed, run
dunk comments resolve <id>. Don't renumber, don't edit other entries.
- Drifted comments are still real. A
drifted: ... marker means the recorded anchor no longer matches the file (file removed, line out of range, or content moved). Don't silently resolve to make it go away — re-read the surrounding code, address the underlying intent, then resolve.
- Don't add new comments. Authoring is a human action via
a in the TUI. If you need to flag something for the user, raise it in chat.
- Comments are hunk-scoped.
range is the inclusive 1-based [start, end] post-image line range of the hunk the comment is attached to. Read the whole range before deciding what to fix — the comment is about every line in it, not just one.
Refresh behaviour
The user's TUI watches .dunk/comments.json and the diff source files. After you dunk comments resolve or edit a tracked file, the active dunk session reloads on its own when the user has --watch on, or when they hit r. No daemon, no extra step.
Fallback: editing .dunk/comments.json directly
Use this only when dunk is unavailable in the environment.
{
"schema": 1,
"comments": [
{
"id": 1,
"file": "src/ui/App.tsx",
"line": 142,
"range": [136, 142],
"anchor": "7b8d4a9c2e1f3a06",
"body": "Why redeclare the theme here?"
}
]
}
file is repo-relative (POSIX).
range and body are described above.
line is the row used internally for drift detection — opaque, do not navigate by it.
anchor is a 16-hex SHA-256 prefix of context around line. The TUI uses it to decide whether the comment is still anchored or has drifted; agents can ignore it.
- To resolve manually, drop the matching
{ id, file, line, ... } object from comments and write the file back as one JSON document with a trailing newline. Preserve other entries' ids; do not renumber.