The review skill¶
The report format is not only for the daemon. pr-review-agent skill install
copies two Claude Code skills into ~/.claude/skills. This page is about
review-report, which makes a review you write by hand — on this repository
or any other — come out in the same shape as one the agent posts; the other
is the description skill.
1 | |
Then start a Claude Code session and ask for a review. The skill supplies:
| Part | What it is |
|---|---|
SKILL.md |
The workflow: collect the header facts, read the contract, sweep, discard, write findings as JSON, render, check. |
references/finding-contract.md |
What is in scope, what to sweep, how a title and a body are written, what each severity means, and how to fill in the assessment. The same file engine/prompt.py reads. |
references/report-contract.md |
The rendering rules — sections, ordering, numbering, the empty report, the trailer. See Report template. |
references/smell-baseline.md |
Twelve code smells checked on every review, capped at minor and overridden by the repository's standards. Also read by engine/prompt.py. |
references/false-positives.md |
What not to report. Also read by engine/prompt.py. |
THIRD-PARTY.md |
Where the adapted text came from, at which upstream commit, and the MIT licences it is used under. |
scripts/collect_context.py |
Head sha, merge base, commit count and changed paths, out of git — and which standards files exist at the merge base. |
scripts/render_report.py |
findings.json → a report, through report.render itself. |
scripts/check_report.py |
A hand-written or hand-edited report, checked against the contract. |
assets/ |
The findings schema, an example findings file, and the report it renders to. |
Nothing in the skill posts anything. It writes files and prints them;
publishing stays a separate, explicit act. render_report.py writes to
--out or to stdout, check_report.py prints violations and exits 1, and
collect_context.py prints JSON. None of them opens a socket, touches the
daemon's database or knows a GitHub token exists — getting a report onto a
pull request means pasting it, or gh pr comment --body-file.
What the scripts need¶
collect_context.py is stdlib-only. render_report.py and
check_report.py import pr_review_agent, because they call the renderer
and the numbering the daemon calls rather than a copy of them.
They do not need it installed. skill install also copies the renderer's
whole import closure into <skill>/scripts/_vendor/pr_review_agent/:
| Module | Why it travels |
|---|---|
report.py |
render — the sections, ordering, truncation and trailer. |
numbering.py |
assign — stable numbers, and the gaps that mean "fixed". |
findings.py |
Finding, Severity and Assessment. |
description.py |
The pr-description skill's renderer, vendored into both skills alike. |
sanitise.py, triggers/mention.py |
What render puts every title and body through before assembling them. |
_compat.py |
The StrEnum shim for Python 3.10. |
That list is short because report.py and findings.py were split out of
publisher.py and engine/models.py to keep it short. None of them
imports the config schema, the HTTP client or sqlite, and none of them may
start to: test_skill.py installs the skill and renders the worked example
in an interpreter where the only importable pr_review_agent is the
vendored one, so a rich import fails the suite rather than shipping a skill
that breaks off the developer's machine.
The copy is a fallback, not a fork. The scripts append _vendor to
sys.path rather than inserting it, so an installed pr_review_agent
always wins and upgrading the package upgrades the renderer for a skill
directory installed months earlier. If neither is there, both scripts say
so and name the two ways to fix it.
The standards the reviewed repository sets¶
engine.standards_paths is how the daemon learns what a repository expects
of a change, and engine/standards.py reads those paths at the merge
base — never at the head, because a pull request that can rewrite the
reviewer's instructions has talked its way past the review. See
CONFIG.md for the full argument.
A person running the skill had no equivalent, and needs the rule more than
the daemon does: an interactive reviewer works in a tree checked out at the
head, where reading AGENTS.md means reading whatever the pull request
made of it. So collect_context.py reports which standards files exist at
the merge base — AGENTS.md, CLAUDE.md and CONTRIBUTING.md by default,
or --standards <path> for the paths a config.yaml names — and step 2 of
the workflow says to read them with git show <merge_base>:<path>.
It reports the paths rather than the contents: what they say is for the reviewer to read, and the value the script adds is the revision.
Why the daemon does not load it¶
The daemon's own reviewer runs claude with --setting-sources "",
--restricted and --disable-slash-commands. Between them, no settings
file, plugin or skill is discovered — which is the point: a reviewer that
loads configuration out of the tree it is reviewing takes its instructions
from whoever opened the pull request. Three of the four injection defences in
DESIGN.md are those flags, and none of them will be relaxed to
make a directory loadable.
The engine is given the same text by a route that widens nothing:
engine/prompt.py reads finding-contract.md, smell-baseline.md and
false-positives.md out of the package and splices them into the prompt. report-contract.md is not
sent and does not need to be — the sections, the ordering, the numbering and
the trailer are report.render's, decided in code, so the daemon's report
format does not depend on a model complying with a description of it. If a future release runs the engine inside bubblewrap
(roadmap A2), that route still works unchanged — the
text is already in the process, so there is no directory for the sandbox
profile to bind and nothing new for it to allow.