pi-pr-review 1.2.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -17
- package/extensions/pr-review-subagent.ts +231 -27
- package/extensions/review-table.ts +152 -11
- package/lib/pr-review-policy.ts +32 -0
- package/lib/pr-review-publish.ts +878 -0
- package/package.json +4 -1
- package/prompts/pr-review.md +38 -43
- package/tests/pr-review-policy.test.ts +72 -0
- package/tests/pr-review-publish.test.ts +261 -0
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ Pass a PR number and pi will:
|
|
|
8
8
|
2. Derive the **base branch** and **head (merging) branch** automatically from the PR.
|
|
9
9
|
3. Review the base↔head diff with disciplined passes (overview, convention compliance, bugs, security/perf, readability), best-effort build/test verification, then validate each candidate.
|
|
10
10
|
4. Return a **full structured review**: overview, strengths, verification, findings at **every** severity (`nit → P3 → P2 → P1 → P0`) with a blocking flag, correctness/security/performance notes, and a verdict.
|
|
11
|
-
5. Optionally
|
|
11
|
+
5. Optionally publish one formal GitHub `COMMENT` review with a top-level body and associated inline comments.
|
|
12
12
|
|
|
13
13
|
**Captures everything, then ranks it.** Unlike a high-signal-only reviewer, it does not discard minor issues — nits, style, naming, missing edge cases, and "worth confirming" observations are all reported as low-severity findings. The **verdict** depends only on *blocking* (P0/P1) findings, so a clean PR is still approved while its nits are still recorded.
|
|
14
14
|
|
|
@@ -65,15 +65,19 @@ The `/pr-review-config` command maps three labels to models:
|
|
|
65
65
|
/pr-review-config show # print the current mapping
|
|
66
66
|
/pr-review-config light=<spec> medium=<spec> heavy=<spec> # set primary tier models
|
|
67
67
|
/pr-review-config heavy_fallbacks=<spec>,<spec> # retry chain for quota/rate-limit failures
|
|
68
|
+
/pr-review-config light_tool_policy=none # tier default when a pass omits tool_policy
|
|
69
|
+
/pr-review-config auto_post_reviews=true # opt in to automatic GitHub COMMENT reviews
|
|
70
|
+
/pr-review-config auto_post_reviews=false # disable automatic posting (the default)
|
|
68
71
|
/pr-review-config medium=unset # clear a tier (back to pi default)
|
|
69
72
|
/pr-review-config heavy_fallbacks=unset # clear a fallback chain
|
|
70
|
-
/pr-review-config
|
|
73
|
+
/pr-review-config light_tool_policy=unset # restore legacy configured-tool behavior
|
|
74
|
+
/pr-review-config tools=read,bash,grep,find,ls # allowlist used by configured policy
|
|
71
75
|
```
|
|
72
76
|
|
|
73
77
|
Running `/pr-review-config` with no arguments in the TUI opens an interactive settings menu that mirrors pi's `/settings` and the NERVous `/nervous:config`:
|
|
74
78
|
|
|
75
|
-
- One primary-model
|
|
76
|
-
- Press Enter on a primary or fallback row to pick a model from a searchable list (or unset it); Enter/Space
|
|
79
|
+
- One primary-model, fallback-model, and tool-policy row per tier (`light` / `medium` / `heavy`), an automatic-posting toggle, plus a configured-tool allowlist row.
|
|
80
|
+
- Press Enter on a primary or fallback row to pick a model from a searchable list (or unset it); Enter/Space cycles tool policies and allowlist presets. The menu sets one fallback model at a time; use the `key=value` form for longer fallback chains.
|
|
77
81
|
- Selections apply and persist **immediately**; Esc closes the menu.
|
|
78
82
|
- Type to search, and tab-completion is available for the `key=value` form.
|
|
79
83
|
|
|
@@ -98,12 +102,20 @@ Example `pr-review.json`:
|
|
|
98
102
|
"medium": ["<backup-balanced-model>"],
|
|
99
103
|
"heavy": ["<backup-strong-model:high>", "<balanced-model-spec>"]
|
|
100
104
|
},
|
|
105
|
+
"toolPolicies": {
|
|
106
|
+
"light": "none",
|
|
107
|
+
"medium": "configured",
|
|
108
|
+
"heavy": "configured"
|
|
109
|
+
},
|
|
110
|
+
"autoPostReviews": false,
|
|
101
111
|
"tools": ["read", "bash", "grep", "find", "ls"]
|
|
102
112
|
}
|
|
103
113
|
```
|
|
104
114
|
|
|
105
115
|
Each tier runs in an **isolated `pi` subprocess** on its configured model. The `review_subagents` batch tool runs independent passes concurrently (default `max_parallel: 4`, capped at 6) and returns ordered per-pass results; the older single-pass `review_subagent` tool remains available as a compatibility fallback. If a tier model fails with a retryable quota/rate-limit/capacity error, the subprocess retries that tier's configured `fallbacks` in order. Non-quota failures do not blindly cycle through fallbacks. If a tier is unset, that subagent falls back to the nearest configured tier, then to your pi default model.
|
|
106
116
|
|
|
117
|
+
Tool policy is additive and backward compatible: `none` emits Pi's explicit `--no-tools`; `configured` uses the existing `tools` allowlist. A tool call's optional `tool_policy` overrides `toolPolicies[tier]`, which in turn falls back to legacy `configured` behavior. The shipped `/pr-review` prompt explicitly uses `none` only for overview because its complete evidence is supplied in context. Conventions/maintainability and both heavy specialist passes use `configured` repository-context tools so they can inspect surrounding files when needed. Fallback model attempts keep the original pass policy.
|
|
118
|
+
|
|
107
119
|
### 2. The orchestrator / inline-fallback model
|
|
108
120
|
|
|
109
121
|
The orchestrator (which fetches the PR, merges findings, and emits the JSON) and the inline fallback path both run on your pi session model:
|
|
@@ -118,21 +130,28 @@ The orchestrator (which fetches the PR, merges findings, and emits the JSON) and
|
|
|
118
130
|
Type `/` in the pi editor and pick `pr-review`, or:
|
|
119
131
|
|
|
120
132
|
```
|
|
121
|
-
/pr-review 123 #
|
|
122
|
-
/pr-review 123 --comment #
|
|
133
|
+
/pr-review 123 # use autoPostReviews (false by default)
|
|
134
|
+
/pr-review 123 --comment # force one COMMENT review for this run
|
|
135
|
+
/pr-review 123 --no-comment # suppress posting for this run
|
|
123
136
|
/pr-review 123 --include-closed # review a closed/merged PR without a confirmation prompt
|
|
124
|
-
/pr-review 123 --review-closed --comment # review
|
|
137
|
+
/pr-review 123 --review-closed --comment # review and attempt a body-only COMMENT review
|
|
125
138
|
```
|
|
126
139
|
|
|
127
140
|
`123` is the PR number in the current repo.
|
|
128
141
|
|
|
129
142
|
### Closed or merged PRs
|
|
130
143
|
|
|
131
|
-
Closed/merged PRs no longer hard-skip. If you run `/pr-review 123` on a non-open PR, the prompt asks whether to continue before producing a review. Use `--include-closed` or `--review-closed` to proceed non-interactively.
|
|
144
|
+
Closed/merged PRs no longer hard-skip. If you run `/pr-review 123` on a non-open PR, the prompt asks whether to continue before producing a review. Use `--include-closed` or `--review-closed` to proceed non-interactively. When publication is enabled, the extension preemptively folds inline findings into one body-only formal `COMMENT` review; if GitHub rejects it, publication fails explicitly without creating an issue comment.
|
|
145
|
+
|
|
146
|
+
### Automatic GitHub review posting
|
|
147
|
+
|
|
148
|
+
`autoPostReviews` is a strict top-level boolean and defaults to `false`. A trusted project `.pi/pr-review.json` value overlays the user value; malformed effective values never enable posting. `/pr-review-config` edits user scope only and displays the effective value/source—if a trusted project overlay wins, edit that project file or use `--no-comment` for the run. Invocation flags are captured before prompt expansion: `--comment` forces posting for one run, `--no-comment` suppresses it, and using both is rejected.
|
|
149
|
+
|
|
150
|
+
Publishing is extension-owned—the model never constructs the write request, and final JSON marked `disposition: "skipped"` is never published. It creates exactly one formal pull-request review containing a top-level body and associated P0–P3 inline comments. The GitHub event is hardcoded to `COMMENT`; the publisher cannot send `APPROVE` or `REQUEST_CHANGES`, even when the review's suggested verdict is `request_changes`. It validates the current head and every inline diff anchor before the single POST, so an invalid open-PR inline comment fails without leaving a partial review. Closed/merged PRs require either `--include-closed`/`--review-closed` or the one-shot affirmative confirmation, then use one body-only `COMMENT` review with inline findings folded into the body. Unknown or unconfirmed non-open lifecycle states fail without posting.
|
|
132
151
|
|
|
133
152
|
### Duplicate review handling
|
|
134
153
|
|
|
135
|
-
|
|
154
|
+
Published reviews include a hidden `pi-pr-review` marker with the reviewed `headRefOid`. A later run skips only when it finds a same-head marker authored by the current authenticated GitHub identity in either formal reviews or legacy issue comments. If new commits were pushed, the head SHA changes and `/pr-review` reviews the PR again. Older or unmarked content is not proof that the current head was reviewed.
|
|
136
155
|
|
|
137
156
|
### Response format
|
|
138
157
|
|
|
@@ -144,7 +163,8 @@ Example payload:
|
|
|
144
163
|
|
|
145
164
|
```json
|
|
146
165
|
{
|
|
147
|
-
"pr": { "number": 33, "title": "fix(logs): parse date-time log timestamps" },
|
|
166
|
+
"pr": { "number": 33, "title": "fix(logs): parse date-time log timestamps", "head_sha": "0123456789abcdef0123456789abcdef01234567" },
|
|
167
|
+
"disposition": "reviewed",
|
|
148
168
|
"verification": "`go build ./...` ✅, `go test ./...` ✅ (130 passed)",
|
|
149
169
|
"overview": "Migrates log timestamps from epoch-ms to RFC3339 to match the endpoint contract.",
|
|
150
170
|
"strengths": ["Reuses FormatTimestamp instead of ad-hoc formatting; net -3 lines."],
|
|
@@ -173,7 +193,7 @@ Example payload:
|
|
|
173
193
|
|
|
174
194
|
Severity tags: `[P0]` blocking/drop-everything · `[P1]` blocking/urgent · `[P2]` normal · `[P3]` low · `[nit]` trivial/optional. Verdict is `approve` (no blocking findings), `request_changes` (a blocking finding exists), or `comment`.
|
|
175
195
|
|
|
176
|
-
**Inline-comment ready.** Each finding's `code_location` is diff-anchored — repo-relative `absolute_file_path`, `line_range` on `side` (`RIGHT` for added/context lines, `LEFT` for removed), and `commentable` (whether the lines are inside a diff hunk). The rendered table shows an **Inline** ✎ column
|
|
196
|
+
**Inline-comment ready.** Each finding's `code_location` is diff-anchored — repo-relative `absolute_file_path`, `line_range` on `side` (`RIGHT` for added/context lines, `LEFT` for removed), and `commentable` (whether the lines are inside a diff hunk). The rendered table shows an **Inline** ✎ column. When publishing is enabled, the extension validates these anchors against current GitHub diff metadata and attaches eligible P0–P3 findings under the single formal review; nits and off-diff observations stay in the top-level body.
|
|
177
197
|
|
|
178
198
|
## What's in the package
|
|
179
199
|
|
|
@@ -181,19 +201,26 @@ Severity tags: `[P0]` blocking/drop-everything · `[P1]` blocking/urgent · `[P2
|
|
|
181
201
|
pi-pr-review/
|
|
182
202
|
├─ package.json # pi manifest: prompts + extensions
|
|
183
203
|
├─ prompts/pr-review.md # the /pr-review orchestrator prompt
|
|
204
|
+
├─ lib/pr-review-policy.ts # pure tool-policy resolution/argv helpers
|
|
205
|
+
├─ lib/pr-review-publish.ts # safe COMMENT-review payload, validation, and gh publisher
|
|
184
206
|
├─ extensions/pr-review-subagent.ts # review_subagents/review_subagent tools + /pr-review-config command
|
|
185
|
-
|
|
207
|
+
├─ extensions/review-table.ts # renders JSON and triggers trusted configured publishing
|
|
208
|
+
├─ tests/pr-review-policy.test.ts # focused policy compatibility tests
|
|
209
|
+
└─ tests/pr-review-publish.test.ts # posting gate, payload, marker, and anchor tests
|
|
186
210
|
```
|
|
187
211
|
|
|
188
|
-
##
|
|
212
|
+
## Speed, security & cost notes
|
|
189
213
|
|
|
190
|
-
- The
|
|
191
|
-
-
|
|
192
|
-
-
|
|
214
|
+
- The four independent review lenses remain intact. Only overview runs context-only with `--no-tools`; medium and both heavy specialists retain configured tools for surrounding-file validation. All subprocesses use `--no-context-files` because the orchestrator supplies the base review context explicitly, and convention excerpts are sent only to the medium pass instead of every model.
|
|
215
|
+
- Tool results include effective `toolPolicy` and `elapsedMs` telemetry (plus per-attempt timing) so repeated representative runs can be compared at p50/p95 without guessing. Restore tools for a custom pass by sending `tool_policy: "configured"`; callers that omit policy retain legacy behavior unless `toolPolicies` config says otherwise.
|
|
216
|
+
- The `review_subagents` batch tool and `review_subagent` fallback spawn isolated `pi` subprocesses (`--mode json -p --no-session`) on your configured tier models. Reviewer prompts prohibit modifications, but a configured allowlist containing `bash` is not technically read-only; use a narrower allowlist if stronger enforcement is required.
|
|
217
|
+
- Project-local `pr-review.json` is only read when the project is trusted. Because project `autoPostReviews: true` causes writes under your authenticated `gh` identity, the effective setting and source are surfaced when publication occurs.
|
|
218
|
+
- GitHub publication uses `gh` only, verifies the current identity and PR head, checks paginated formal reviews and legacy comments for same-head markers, and hardcodes `event: COMMENT`. Ambiguous transport failures are reconciled once and never blindly retried.
|
|
219
|
+
- Tiered review calls multiple models per PR, concurrently for independent passes. Point `light` at a cheap model for overview/risk scan; reserve `heavy` for deep passes, and configure per-tier `fallbacks` only for acceptable backup models because retries can increase cost.
|
|
193
220
|
|
|
194
221
|
## Design notes
|
|
195
222
|
|
|
196
|
-
- **Process**
|
|
223
|
+
- **Process** is PR-number driven: confirm non-open PRs, skip draft/same-head-already-reviewed work, fan out four review lenses, verify, validate/classify, emit JSON, then optionally publish one extension-owned formal `COMMENT` review.
|
|
197
224
|
- **Captures every severity** (`nit → P0`) with a `blocking` flag; the verdict depends only on blocking findings, so nothing minor is lost but a clean PR still gets approved.
|
|
198
225
|
- **Verification is non-destructive:** any build/test runs in an isolated `git worktree` on the PR head — the prompt never checks out, commits, or pushes in your working tree.
|
|
199
226
|
- pi has no built-in sub-agents, so tiering is implemented as an extension that spawns isolated `pi` subprocesses per tier; the batch tool gives deterministic parallelism, and the prompt degrades gracefully to single-pass or inline review when the extension is absent.
|