pi-pr-review 1.6.2 → 1.6.4
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/.github/workflows/pull-request.yml +2 -2
- package/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +14 -0
- package/README.md +131 -229
- package/RELEASING.md +1 -1
- package/extensions/pr-review-subagent.ts +227 -45
- package/lib/pr-review-concurrency.ts +17 -0
- package/lib/pr-review-context.ts +71 -0
- package/lib/pr-review-policy.ts +10 -1
- package/lib/pr-review-publish.ts +6 -0
- package/lib/pr-review-verify.ts +10 -0
- package/package.json +1 -1
- package/prompts/pr-review.md +47 -24
- package/release-please-config.json +1 -0
- package/tests/pr-review-concurrency.test.ts +45 -0
- package/tests/pr-review-context.test.ts +66 -0
- package/tests/pr-review-policy.test.ts +3 -0
- package/tests/pr-review-prompt.test.ts +105 -9
- package/tests/pr-review-publish.test.ts +6 -0
- package/tests/pr-review-subprocess.test.ts +39 -0
- package/tests/pr-review-verify.test.ts +8 -3
|
@@ -24,12 +24,12 @@ jobs:
|
|
|
24
24
|
run: |
|
|
25
25
|
node <<'NODE'
|
|
26
26
|
const title = process.env.PR_TITLE ?? "";
|
|
27
|
-
const conventionalTitle = /^(feat|fix|perf|revert|docs|style|refactor|test|chore)(\([a-z0-9][a-z0-9._/-]*\))?!?: \S.*$/;
|
|
27
|
+
const conventionalTitle = /^(feat|fix|perf|revert|docs|style|refactor|test|chore|release)(\([a-z0-9][a-z0-9._/-]*\))?!?: \S.*$/;
|
|
28
28
|
|
|
29
29
|
if (!conventionalTitle.test(title)) {
|
|
30
30
|
console.error(`Invalid PR title: ${title}`);
|
|
31
31
|
console.error("Use: <type>(optional-scope): description");
|
|
32
|
-
console.error("Allowed types: feat, fix, perf, revert, docs, style, refactor, test, chore");
|
|
32
|
+
console.error("Allowed types: feat, fix, perf, revert, docs, style, refactor, test, chore, release");
|
|
33
33
|
console.error("Add ! before : for a breaking change, for example feat!: description");
|
|
34
34
|
process.exit(1);
|
|
35
35
|
}
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.6.4](https://github.com/10ego/pi-pr-review/compare/v1.6.3...v1.6.4) (2026-07-13)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **release:** use release commits and simplify docs ([#7](https://github.com/10ego/pi-pr-review/issues/7)) ([4c89c4c](https://github.com/10ego/pi-pr-review/commit/4c89c4c67bf6300fd544fbc8ea9a99079fb1f8aa))
|
|
9
|
+
|
|
10
|
+
## [1.6.3](https://github.com/10ego/pi-pr-review/compare/v1.6.2...v1.6.3) (2026-07-13)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Performance Improvements
|
|
14
|
+
|
|
15
|
+
* **review:** optimize parallel review execution ([#5](https://github.com/10ego/pi-pr-review/issues/5)) ([4ea1a76](https://github.com/10ego/pi-pr-review/commit/4ea1a7627f5c5fb7a8022a97fe2c24bb44643d2a))
|
|
16
|
+
|
|
3
17
|
## [1.6.2](https://github.com/10ego/pi-pr-review/compare/v1.6.1...v1.6.2) (2026-07-11)
|
|
4
18
|
|
|
5
19
|
|
package/README.md
CHANGED
|
@@ -1,131 +1,117 @@
|
|
|
1
1
|
# pi-pr-review
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Parallel, model-agnostic AI code review for GitHub pull requests in the [Pi coding agent](https://pi.dev).
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Give it a PR number and it will:
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
- fetch the PR metadata and diff with `gh`;
|
|
8
|
+
- run focused review passes in parallel using models you choose;
|
|
9
|
+
- validate candidate findings before reporting them;
|
|
10
|
+
- render a structured review with severity, location, confidence, and verdict;
|
|
11
|
+
- optionally publish one safe GitHub `COMMENT` review with inline comments.
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
No model name is hardcoded anywhere. The package ships an extension that adds **tiered review subagents** — `light` / `medium` / `heavy` — that you map to whatever models you like. Independent review passes fan out through a batch tool so overview, conventions/maintainability, correctness, and security/performance can run in parallel. If the extension isn't loaded, the same prompt runs every pass inline on your current session model, so it always works.
|
|
13
|
+
The default review prioritizes P0–P2 defects and allows up to three minor findings. Use `--full` for exhaustive convention, maintainability, and minor coverage.
|
|
16
14
|
|
|
17
15
|
## Requirements
|
|
18
16
|
|
|
19
|
-
- [`gh`](https://cli.github.com/) installed and authenticated
|
|
20
|
-
-
|
|
17
|
+
- [`gh`](https://cli.github.com/) installed and authenticated with `gh auth login`.
|
|
18
|
+
- Pi running inside the repository that owns the PR.
|
|
21
19
|
|
|
22
20
|
## Install
|
|
23
21
|
|
|
24
|
-
Install the published npm package in user scope:
|
|
25
|
-
|
|
26
22
|
```bash
|
|
23
|
+
# User scope
|
|
27
24
|
pi install npm:pi-pr-review
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
Or install it in project scope so the dependency is shareable with your team and automatically installed when the project is trusted:
|
|
31
25
|
|
|
32
|
-
|
|
26
|
+
# Project scope
|
|
33
27
|
pi install -l npm:pi-pr-review
|
|
34
28
|
```
|
|
35
29
|
|
|
36
|
-
For local development
|
|
37
|
-
|
|
38
|
-
Releases are versioned from conventional squash-merged PR titles and published automatically through npm trusted publishing. See [RELEASING.md](RELEASING.md) for the release and merge policy.
|
|
30
|
+
For local development, replace the package name with a checkout path such as `./pi-pr-review`.
|
|
39
31
|
|
|
40
|
-
|
|
32
|
+
## Quick start
|
|
41
33
|
|
|
42
|
-
|
|
34
|
+
Configure the reviewer models:
|
|
43
35
|
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
# or per-project
|
|
48
|
-
mkdir -p .pi/prompts && cp prompts/pr-review.md .pi/prompts/
|
|
36
|
+
```text
|
|
37
|
+
/pr-review-config light=<fast-model> medium=<balanced-model> heavy=<strong-model>
|
|
38
|
+
/pr-review-config light_thinking=low medium_thinking=medium heavy_thinking=high
|
|
49
39
|
```
|
|
50
40
|
|
|
51
|
-
|
|
41
|
+
Then review a PR in the current repository:
|
|
52
42
|
|
|
53
|
-
|
|
43
|
+
```text
|
|
44
|
+
/pr-review 123
|
|
45
|
+
```
|
|
54
46
|
|
|
55
|
-
|
|
47
|
+
In the TUI, the result is rendered as a readable review. In `print`, `json`, and `rpc` modes, Pi returns the raw JSON payload.
|
|
56
48
|
|
|
57
|
-
|
|
49
|
+
## Review modes
|
|
58
50
|
|
|
59
|
-
|
|
|
60
|
-
|
|
61
|
-
| `
|
|
62
|
-
| `
|
|
63
|
-
| `
|
|
51
|
+
| Command | Behavior |
|
|
52
|
+
|---|---|
|
|
53
|
+
| `/pr-review 123` | Balanced default: all validated P0–P2 findings plus up to three direct-diff P3/nits. |
|
|
54
|
+
| `/pr-review 123 --major-only` | P0–P2 only. |
|
|
55
|
+
| `/pr-review 123 --full` | Adds convention/maintainability review and reports all qualifying severities. |
|
|
56
|
+
| `/pr-review 123 --balanced` | Explicit alias for the default mode. |
|
|
57
|
+
| `/pr-review 123 --include-closed` | Reviews a closed or merged PR without asking first. |
|
|
64
58
|
|
|
65
|
-
|
|
66
|
-
/pr-review-config # open the settings menu (like /settings & /nervous:config)
|
|
67
|
-
/pr-review-config show # print the current mapping
|
|
68
|
-
/pr-review-config light=<spec> medium=<spec> heavy=<spec> # set primary tier models
|
|
69
|
-
/pr-review-config heavy_fallbacks=<spec>,<spec> # retry chain for quota/rate-limit failures
|
|
70
|
-
/pr-review-config light_thinking=low medium_thinking=medium heavy_thinking=high
|
|
71
|
-
/pr-review-config light_tool_policy=none # tier default when a pass omits tool_policy
|
|
72
|
-
/pr-review-config auto_post_reviews=true # opt in to automatic GitHub COMMENT reviews
|
|
73
|
-
/pr-review-config auto_post_reviews=false # disable automatic posting (the default)
|
|
74
|
-
/pr-review-config medium=unset # clear a tier (back to pi default)
|
|
75
|
-
/pr-review-config heavy_fallbacks=unset # clear a fallback chain
|
|
76
|
-
/pr-review-config light_thinking=unset # inherit the ambient pi default
|
|
77
|
-
/pr-review-config light_tool_policy=unset # restore legacy configured-tool behavior
|
|
78
|
-
/pr-review-config tools=read,bash,grep,find,ls # allowlist used by configured policy
|
|
79
|
-
```
|
|
59
|
+
`--full`, `--major-only`, and `--balanced` are mutually exclusive. Without `--include-closed` or `--review-closed`, Pi asks before reviewing a non-open PR.
|
|
80
60
|
|
|
81
|
-
|
|
61
|
+
A review uses five focused passes by default:
|
|
82
62
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
63
|
+
1. overview and minor hygiene (`light`);
|
|
64
|
+
2. state, lifecycle, and concurrency correctness (`heavy`);
|
|
65
|
+
3. contracts, data, and integration correctness (`heavy`);
|
|
66
|
+
4. security (`heavy`);
|
|
67
|
+
5. performance and resource ownership (`heavy`).
|
|
87
68
|
|
|
88
|
-
|
|
69
|
+
`--full` adds a convention and maintainability pass (`medium`). Large multi-file diffs are split by whole-file boundaries and reviewed in parallel. If the extension is unavailable, the prompt falls back to the current Pi session model.
|
|
89
70
|
|
|
90
|
-
|
|
71
|
+
## Configure models
|
|
91
72
|
|
|
92
|
-
|
|
93
|
-
/pr-review-config light_thinking=off # or minimal|low|medium|high|xhigh|max
|
|
94
|
-
/pr-review-config medium_thinking=minimal # or off|low|medium|high|xhigh|max
|
|
95
|
-
/pr-review-config heavy_thinking=max # or off|minimal|low|medium|high|xhigh
|
|
96
|
-
/pr-review-config light_thinking=unset medium_thinking=unset heavy_thinking=unset
|
|
97
|
-
```
|
|
73
|
+
`/pr-review-config` opens an interactive settings menu in the TUI. Use `/pr-review-config show` for a text summary or `key=value` arguments for direct changes.
|
|
98
74
|
|
|
99
|
-
|
|
75
|
+
| Tier | Purpose |
|
|
76
|
+
|---|---|
|
|
77
|
+
| `light` | Fast overview and risk scan. |
|
|
78
|
+
| `medium` | Convention and maintainability review in `--full` mode. |
|
|
79
|
+
| `heavy` | Correctness, security, and performance review. |
|
|
100
80
|
|
|
101
|
-
|
|
81
|
+
Common settings:
|
|
102
82
|
|
|
103
|
-
```
|
|
83
|
+
```text
|
|
84
|
+
/pr-review-config light=provider/model heavy=provider/model:high
|
|
85
|
+
/pr-review-config heavy_fallbacks=provider/backup:high,provider/backup2
|
|
104
86
|
/pr-review-config light_thinking=low medium_thinking=medium heavy_thinking=high
|
|
87
|
+
/pr-review-config heavy_tool_policy=configured
|
|
88
|
+
/pr-review-config tools=read,bash,grep,find,ls
|
|
89
|
+
/pr-review-config auto_post_reviews=true
|
|
90
|
+
/pr-review-config medium=unset
|
|
105
91
|
```
|
|
106
92
|
|
|
107
|
-
|
|
93
|
+
Supported thinking levels are `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. A thinking suffix in a model spec, such as `provider/model:xhigh`, takes precedence over the tier's thinking setting. `unset` restores inherited behavior.
|
|
108
94
|
|
|
109
|
-
|
|
95
|
+
Tool policy can be `none` or `configured`. `configured` uses the `tools` allowlist; because an allowlist containing `bash` is not technically read-only, remove it if you need stricter reviewer isolation.
|
|
110
96
|
|
|
111
|
-
|
|
112
|
-
- **Project:** `<repo>/.pi/pr-review.json` (overlays model/tool/publication settings only when the project is trusted)
|
|
97
|
+
Configuration is stored in:
|
|
113
98
|
|
|
114
|
-
|
|
99
|
+
- user scope: `~/.pi/agent/pr-review.json`;
|
|
100
|
+
- project scope: `<repo>/.pi/pr-review.json`, applied only when the project is trusted.
|
|
115
101
|
|
|
116
|
-
|
|
102
|
+
A trusted project can override model, tool, and publication settings. Verification profiles are always user-only.
|
|
103
|
+
|
|
104
|
+
Example:
|
|
117
105
|
|
|
118
106
|
```json
|
|
119
107
|
{
|
|
120
108
|
"tiers": {
|
|
121
|
-
"light": "
|
|
122
|
-
"medium": "
|
|
123
|
-
"heavy": "
|
|
109
|
+
"light": "provider/fast-model",
|
|
110
|
+
"medium": "provider/balanced-model",
|
|
111
|
+
"heavy": "provider/strong-model:high"
|
|
124
112
|
},
|
|
125
113
|
"fallbacks": {
|
|
126
|
-
"
|
|
127
|
-
"medium": ["<backup-balanced-model>"],
|
|
128
|
-
"heavy": ["<backup-strong-model:high>", "<balanced-model-spec>"]
|
|
114
|
+
"heavy": ["provider/backup-model:high"]
|
|
129
115
|
},
|
|
130
116
|
"thinkingLevels": {
|
|
131
117
|
"light": "low",
|
|
@@ -137,189 +123,105 @@ Example `pr-review.json`:
|
|
|
137
123
|
"medium": "configured",
|
|
138
124
|
"heavy": "configured"
|
|
139
125
|
},
|
|
140
|
-
"
|
|
141
|
-
"
|
|
142
|
-
"unit": {
|
|
143
|
-
"description": "Run the fixed unit-test entry point",
|
|
144
|
-
"repository": { "host": "github.com", "owner": "YOUR-ORG", "repo": "YOUR-REPO" },
|
|
145
|
-
"argv": ["/canonical/absolute/path/to/bun", "test"],
|
|
146
|
-
"platforms": ["darwin", "linux"],
|
|
147
|
-
"totalTimeoutMs": 120000,
|
|
148
|
-
"allowForks": false,
|
|
149
|
-
"acknowledgeUnsandboxedPrCodeRisk": true
|
|
150
|
-
}
|
|
151
|
-
},
|
|
152
|
-
"tools": ["read", "bash", "grep", "find", "ls"]
|
|
126
|
+
"tools": ["read", "bash", "grep", "find", "ls"],
|
|
127
|
+
"autoPostReviews": false
|
|
153
128
|
}
|
|
154
129
|
```
|
|
155
130
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
Thinking resolution and tool policy are independent. For thinking, a supported model-spec `:thinking` suffix wins; otherwise `thinkingLevels[tier]` is passed to the child process, and an unset tier inherits the ambient pi default. Tool policy remains 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.
|
|
159
|
-
|
|
160
|
-
### Optional trusted verification baselines
|
|
161
|
-
|
|
162
|
-
Verification is disabled when the user config has no `verificationBaselines`. The orchestrator first calls `pr_review_verify` with `action: "list"`; it may then call `action: "run"` with only `pr_number`, the exact 40-character `head_sha`, and one returned `baseline_name`. The tool schema rejects legacy model-supplied `command` and `timeout_ms` overrides.
|
|
163
|
-
|
|
164
|
-
Every profile is strict: a repository `{host, owner, repo}` identity, a nonempty fixed `argv` whose executable is an absolute canonical executable file, one or more applicable POSIX `platforms`, `totalTimeoutMs`, optional `description`, optional `allowForks` (default `false`), and `acknowledgeUnsandboxedPrCodeRisk: true`. Unknown fields invalidate the profile. Verification is disabled by default, profiles are user-file-only, and this acknowledgement must be exactly `true`; project config cannot enable or acknowledge it. Windows fails closed. Before fetching or executing PR code, the extension resolves canonical `git` and `gh` executables from the PATH captured when the extension starts and queries the profile's canonical repository for the current PR head and cross-repository status. It fails clearly if either trusted executable is unavailable. A cross-repository/fork PR is rejected unless that trusted profile explicitly sets `allowForks: true`.
|
|
165
|
-
|
|
166
|
-
The only network fetch runs in a freshly initialized extension-owned bare staging repository under the verification temporary directory. Its fetch has no system, global, or local Git config and no installed hooks. Private HTTPS setup fetches use `gh auth token --hostname <profile-host>` inside the profile's total setup deadline. The token and mode-0700 askpass helper exist only for that staging fetch; all authenticated stdout/stderr is zeroed and suppressed, every observed byte is counted as dropped, and failures return only generic trusted context. After deleting the token/helper, the extension verifies the staged ref against the exact captured SHA, imports only that already-fetched ref into the original repository over a local path with a secret-free minimal Git environment and `--no-write-fetch-head`, and verifies the imported SHA again. Original local hooks or URL rewrites may observe this secret-free local import, but never the token. `FETCH_HEAD` remains unchanged. If `gh` has no token, public repositories still get an unauthenticated HTTPS staging fetch and retain bounded diagnostics plus observed/dropped-byte accounting, including on timeout or abort; private fetch failures explicitly identify missing authentication.
|
|
167
|
-
|
|
168
|
-
`totalTimeoutMs` bounds the normal setup, command, termination, and reserved-cleanup lifecycle. A fixed 2-second emergency cleanup allowance is unconditionally available to bounded cleanup beyond that budget, so a pathological run may take up to 2 seconds longer than `totalTimeoutMs`.
|
|
131
|
+
Tier subprocesses retry configured fallbacks only for retryable quota, rate-limit, or capacity failures. If a tier is unset, it uses the nearest configured tier and then Pi's default model.
|
|
169
132
|
|
|
170
|
-
|
|
133
|
+
## Publish to GitHub
|
|
171
134
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
The orchestrator (which fetches the PR, merges findings, and emits the JSON) and the inline fallback path both run on your pi session model:
|
|
175
|
-
|
|
176
|
-
- **Per run:** `pi --model <model-id> "/pr-review 123"`
|
|
177
|
-
- **Persistent default (user):** `~/.pi/agent/settings.json` → `{ "defaultModel": "<model-id>", "defaultThinkingLevel": "high" }`
|
|
178
|
-
- **Persistent default (project):** the same keys in `.pi/settings.json`
|
|
179
|
-
- **Switch mid-session:** `/model`, or `Ctrl+P` to cycle.
|
|
180
|
-
|
|
181
|
-
## Usage
|
|
182
|
-
|
|
183
|
-
Type `/` in the pi editor and pick `pr-review`, or:
|
|
135
|
+
Publishing is off by default.
|
|
184
136
|
|
|
137
|
+
```text
|
|
138
|
+
/pr-review 123 --comment # publish this run
|
|
139
|
+
/pr-review 123 --no-comment # never publish this run
|
|
140
|
+
/pr-review-config auto_post_reviews=true
|
|
185
141
|
```
|
|
186
|
-
/pr-review 123 # use autoPostReviews (false by default)
|
|
187
|
-
/pr-review 123 --comment # force one COMMENT review for this run
|
|
188
|
-
/pr-review 123 --no-comment # suppress posting for this run
|
|
189
|
-
/pr-review 123 --include-closed # review a closed/merged PR without a confirmation prompt
|
|
190
|
-
/pr-review 123 --review-closed --comment # review and attempt a body-only COMMENT review
|
|
191
|
-
/pr-review-publish 123 # publish the completed review cached in this session
|
|
192
|
-
/pr-review-publish 123 --allow-stale # publish it after the PR head changed; never rerun the model
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
`123` is the PR number in the current repo.
|
|
196
|
-
|
|
197
|
-
### Closed or merged PRs
|
|
198
|
-
|
|
199
|
-
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.
|
|
200
142
|
|
|
201
|
-
|
|
143
|
+
The extension—not the model—owns publishing. It creates one formal review with the event hardcoded to `COMMENT`; it never submits `APPROVE` or `REQUEST_CHANGES`. Before writing, it verifies the current PR head, validates inline anchors, and checks for a review of the same head by the current GitHub identity.
|
|
202
144
|
|
|
203
|
-
|
|
145
|
+
Closed or merged PRs use a body-only review. Open PRs attach eligible P0–P3 findings as inline comments and keep nits or off-diff findings in the review body.
|
|
204
146
|
|
|
205
|
-
|
|
147
|
+
If a new commit makes a completed review stale, publish the cached result without rerunning the model:
|
|
206
148
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
The latest valid, reviewed final result for each repository and PR is cached before GitHub publication preflight for the current extension session. If a commit lands after review generation and the normal stale-head guard refuses to post, do **not** rerun `/pr-review` with `--comment` and do not ask the model to bypass its write policy. Use the extension-owned publish-only command:
|
|
210
|
-
|
|
211
|
-
```
|
|
149
|
+
```text
|
|
212
150
|
/pr-review-publish 123 --allow-stale
|
|
213
151
|
```
|
|
214
152
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
The in-memory cache is discarded when a new pi session starts or extensions reload. `/pr-review-publish` explicitly refuses to start or rerun a review when no cached result exists, and it will not publish an older cached result while a new review of the same PR is active.
|
|
218
|
-
|
|
219
|
-
### Duplicate review handling
|
|
220
|
-
|
|
221
|
-
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.
|
|
222
|
-
|
|
223
|
-
### Response format
|
|
153
|
+
Inline comments are intentionally disabled for stale reviews because the original anchors may no longer be valid. The stale review is body-only and identifies both the reviewed and current SHAs. The cache lasts for the current Pi session.
|
|
224
154
|
|
|
225
|
-
|
|
155
|
+
## Optional verification
|
|
226
156
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
Example payload:
|
|
157
|
+
You can define fixed test commands in `verificationBaselines` in the **user** config. Project config cannot add or override these profiles. The reviewer may select at most one applicable profile and runs it against the exact captured PR head.
|
|
230
158
|
|
|
231
159
|
```json
|
|
232
160
|
{
|
|
233
|
-
"
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
"
|
|
242
|
-
"
|
|
243
|
-
"
|
|
244
|
-
"
|
|
245
|
-
"
|
|
246
|
-
"absolute_file_path": "pkg/store/cache.go",
|
|
247
|
-
"line_range": { "start": 42, "end": 45 },
|
|
248
|
-
"side": "RIGHT",
|
|
249
|
-
"commentable": true
|
|
250
|
-
}
|
|
161
|
+
"verificationBaselines": {
|
|
162
|
+
"unit": {
|
|
163
|
+
"description": "Run the unit tests",
|
|
164
|
+
"repository": {
|
|
165
|
+
"host": "github.com",
|
|
166
|
+
"owner": "YOUR-ORG",
|
|
167
|
+
"repo": "YOUR-REPO"
|
|
168
|
+
},
|
|
169
|
+
"argv": ["/absolute/canonical/path/to/bun", "test"],
|
|
170
|
+
"platforms": ["darwin", "linux"],
|
|
171
|
+
"totalTimeoutMs": 120000,
|
|
172
|
+
"allowForks": false,
|
|
173
|
+
"acknowledgeUnsandboxedPrCodeRisk": true
|
|
251
174
|
}
|
|
252
|
-
|
|
253
|
-
"notes": { "correctness": "build confirms; no unused imports", "security": "none", "performance": "negligible" },
|
|
254
|
-
"verdict": "approve",
|
|
255
|
-
"overall_correctness": "patch is correct",
|
|
256
|
-
"overall_explanation": "Clean, well-scoped contract-alignment change with matching tests.",
|
|
257
|
-
"overall_confidence_score": 0.9
|
|
175
|
+
}
|
|
258
176
|
}
|
|
259
177
|
```
|
|
260
178
|
|
|
261
|
-
|
|
179
|
+
Profiles require an exact repository identity, a canonical absolute executable, an applicable POSIX platform, a total timeout, and explicit risk acknowledgement. Fork PRs are rejected unless `allowForks` is `true`; Windows fails closed.
|
|
262
180
|
|
|
263
|
-
**
|
|
181
|
+
> **Risk:** verification executes PR code without a filesystem or network sandbox. Cleanup supervises the original POSIX process group, but deliberately detached processes can escape it. Use an external sandbox or container for untrusted PRs.
|
|
264
182
|
|
|
265
|
-
|
|
183
|
+
Verification fetches into extension-owned temporary Git state, checks the exact SHA before and after importing it, runs in a detached worktree with a minimal secret-scrubbed environment, and leaves the user's checkout and `FETCH_HEAD` unchanged.
|
|
266
184
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
├─ prompts/pr-review.md # the /pr-review orchestrator prompt
|
|
271
|
-
├─ lib/pr-review-policy.ts # pure tool-policy resolution/argv helpers
|
|
272
|
-
├─ lib/pr-review-publish.ts # safe COMMENT-review payload, validation, and gh publisher
|
|
273
|
-
├─ lib/pr-review-telemetry.ts # monotonic invocation intervals and overlap-safe summaries
|
|
274
|
-
├─ lib/pr-review-thinking.ts # tier thinking resolution and warnings
|
|
275
|
-
├─ lib/pr-review-verify.ts # strict profiles, exact-head verification, group supervision + cleanup
|
|
276
|
-
├─ extensions/pr-review-subagent.ts # review tools, named verification + /pr-review-config
|
|
277
|
-
├─ extensions/review-table.ts # renders JSON and triggers trusted configured publishing
|
|
278
|
-
├─ tests/pr-review-policy.test.ts # focused policy compatibility tests
|
|
279
|
-
├─ tests/pr-review-prompt.test.ts # orchestrator scheduling and safety contract tests
|
|
280
|
-
├─ tests/pr-review-publish.test.ts # posting gate, payload, marker, and anchor tests
|
|
281
|
-
├─ tests/pr-review-telemetry.test.ts # monotonic lifecycle and overlap accounting tests
|
|
282
|
-
├─ tests/pr-review-thinking.test.ts # tier thinking compatibility tests
|
|
283
|
-
└─ tests/pr-review-verify.test.ts # verification success/failure/timeout/abort cleanup tests
|
|
284
|
-
```
|
|
185
|
+
## Review output
|
|
186
|
+
|
|
187
|
+
Each finding includes:
|
|
285
188
|
|
|
286
|
-
|
|
189
|
+
- severity: `P0`, `P1`, `P2`, `P3`, or `nit`;
|
|
190
|
+
- whether it blocks the verdict;
|
|
191
|
+
- an explanation and confidence score;
|
|
192
|
+
- a diff-anchored file and line range when available.
|
|
287
193
|
|
|
288
|
-
|
|
194
|
+
| Severity | Meaning |
|
|
195
|
+
|---|---|
|
|
196
|
+
| `P0` | Drop everything; blocking. |
|
|
197
|
+
| `P1` | Urgent; blocking. |
|
|
198
|
+
| `P2` | Normal defect. |
|
|
199
|
+
| `P3` | Low-priority improvement. |
|
|
200
|
+
| `nit` | Trivial or optional. |
|
|
289
201
|
|
|
290
|
-
|
|
291
|
-
- After gathering PR metadata, the complete diff, and applicable conventions, the orchestrator uses `pr_review_verify` `action=list` to discover zero or more applicable **user-level** names, then selects at most one. Missing config means verification is disabled; project-local profiles are ignored. The model never supplies argv or a timeout.
|
|
292
|
-
- When a profile is selected, the review batch and `action=run` are emitted in the **same assistant turn**. Run accepts only `pr_number`, exact `head_sha`, and `baseline_name`. Before PR-code setup, the extension resolves canonical Git/gh from its startup PATH, uses trusted `gh` metadata to revalidate the exact head, and rejects cross-repository PRs unless `allowForks` is true.
|
|
293
|
-
- The sole network fetch is isolated in a fresh extension-owned bare staging repository with config and hooks absent. Authentication and askpass exist only during that staging fetch, whose output is fully suppressed and accounted. After exact staged-SHA verification, a secret-free local-path fetch imports the ref into the original repository with `--no-write-fetch-head`, followed by a second exact-SHA check. Original hooks/URL rewrites never see the token. Unauthenticated timeout/abort paths retain bounded diagnostics and byte accounting.
|
|
294
|
-
- The fixed argv runs without a shell/stdin, with a minimal secret-scrubbed environment and temporary HOME/cache. `totalTimeoutMs` bounds the normal setup+command+termination+reserved-cleanup lifecycle; a fixed 2-second emergency cleanup allowance is unconditionally available to bounded cleanup beyond it. Output uses a shared raw-byte cap, UTF-8/control sanitization, exact dropped-byte counts, and a final serialized cap. Timeout/abort or residual members of the original process group trigger group TERM, then unconditional group KILL after grace, followed by bounded drain. `primaryOutcome`, `terminationOutcome`, and `cleanupOutcome` remain independent.
|
|
295
|
-
- Verification still executes unsandboxed PR code. Supervision covers only the original POSIX process group; deliberate `setsid`/session escape can survive. Use an external sandbox/container wrapper for untrusted PRs. If no profile applies or the tool is unavailable, verification is skipped rather than replaced with prompt-owned bash.
|
|
202
|
+
The verdict is `request_changes` only when a validated P0 or P1 finding exists. Otherwise it is `approve` or `comment`. The displayed verdict is advisory even when the review is published, because publication always uses the GitHub `COMMENT` event.
|
|
296
203
|
|
|
297
|
-
|
|
204
|
+
## Safety and cost
|
|
298
205
|
|
|
299
|
-
|
|
206
|
+
- Reviewers receive the captured diff and are instructed not to modify files.
|
|
207
|
+
- The orchestrator does not check out, commit, or push PR code.
|
|
208
|
+
- GitHub writes require `--comment` or an effective `autoPostReviews: true` setting.
|
|
209
|
+
- Publication authority is captured before review or optional verification begins, so PR code cannot enable it mid-run.
|
|
210
|
+
- Multiple model calls run per PR. Use a cheaper `light` model and reserve stronger models for `heavy` passes to control cost.
|
|
211
|
+
- Same-head review markers prevent duplicate publication by the same GitHub identity.
|
|
300
212
|
|
|
301
|
-
|
|
302
|
-
- `totalWallMs` is measured directly from accepted input to that boundary and includes any human-confirmation wait. Publication occurs afterward and is excluded.
|
|
303
|
-
- `activeReviewMs` is the active timeline after human-confirmation wait is removed.
|
|
304
|
-
- `phases.humanConfirmationWait.elapsedMs` reports time paused after the one-shot question for a non-open PR until affirmative input resumes the review or the invocation reaches its recorded completion. This wait is also removed from active interval offsets rather than attributed to model or orchestration time.
|
|
305
|
-
- `phases.reviewSubagentTools` and `phases.baselineVerificationTool` report interval-unioned `elapsedMs` plus observed tool intervals. Baseline timing is attributed only to `pr_review_verify` calls with `action=run`; `action=list` discovery and bash calls remain aggregate orchestration. Each interval contains `toolCallId`, `toolName`, `startOffsetMs`, `endOffsetMs`, `elapsedMs`, and `endObserved` on the active timeline.
|
|
306
|
-
- `phases.overlapMs` is the intersection of those two phase interval sets. `phases.observableToolWallMs` is their union, so concurrent work is not double-counted.
|
|
307
|
-
- `phases.aggregateOrchestration.elapsedMs` is the remaining active time. It intentionally groups metadata/context gathering, model orchestration, targeted checks, and final validation because their individual lifecycle boundaries are not directly observed; it is not a model-only timing claim.
|
|
308
|
-
- `notes` records these accounting boundaries in the durable entry so downstream consumers do not have to infer them.
|
|
213
|
+
## Development
|
|
309
214
|
|
|
310
|
-
|
|
215
|
+
Run the test suite with:
|
|
311
216
|
|
|
312
|
-
|
|
217
|
+
```bash
|
|
218
|
+
bun test
|
|
219
|
+
```
|
|
313
220
|
|
|
314
|
-
|
|
315
|
-
- Project-local model/tool/publication settings in `pr-review.json` are read only when the project is trusted; project-local `verificationBaselines` is always ignored. Because project `autoPostReviews: true` causes writes under your authenticated `gh` identity, its full effective decision is snapshotted before any review tools or optional unsandboxed verification execute and the source is surfaced when publication occurs. PR code cannot gain write authority by changing config mid-review.
|
|
316
|
-
- 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. The performance changes above do not alter these publishing gates or opt-in defaults.
|
|
317
|
-
- **`gh api -f` caution:** `gh api ... -f body=@/tmp/file.md` posts the literal text `@/tmp/file.md`; unlike `gh pr comment --body-file`, `-f` does not expand `@file`. Use a JSON payload via `gh api ... --input -` for API requests. The built-in publisher already does this.
|
|
318
|
-
- Tiered review calls multiple models per PR, concurrently for independent passes. Point `light` at a model suitable for overview/risk scan; reserve `heavy` for deep passes, and configure per-tier `fallbacks` only for acceptable backup models because retries can increase cost.
|
|
221
|
+
The package consists of the `/pr-review` prompt, tiered subagent and rendering extensions, and supporting libraries under `lib/`. To use only the prompt template:
|
|
319
222
|
|
|
320
|
-
|
|
223
|
+
```bash
|
|
224
|
+
cp prompts/pr-review.md ~/.pi/agent/prompts/
|
|
225
|
+
```
|
|
321
226
|
|
|
322
|
-
|
|
323
|
-
- **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.
|
|
324
|
-
- **Verification is non-destructive to Git state:** a selected user-level named baseline runs through `pr_review_verify` in an extension-owned worktree pinned to the captured full PR SHA. Staging and local import use `--no-write-fetch-head`; cleanup reports worktree/ref/temp removal separately. The prompt never owns cleanup and never checks out, commits, or pushes in your working tree. Verification is nevertheless unsandboxed PR-code execution, so it is disabled by default and requires explicit user acknowledgement plus an external sandbox/container wrapper when the PR is untrusted.
|
|
325
|
-
- 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.
|
|
227
|
+
Releases use conventional squash-merged PR titles and npm trusted publishing. See [RELEASING.md](RELEASING.md).
|
package/RELEASING.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Releasing
|
|
2
2
|
|
|
3
|
-
[Release Please](https://github.com/googleapis/release-please) watches conventional commits merged into `main`. It opens or updates a release PR containing the calculated version change, `CHANGELOG.md`, and release manifest. Merging that release PR creates a GitHub release and, after the test suite passes, publishes `pi-pr-review` to npm with signed provenance.
|
|
3
|
+
[Release Please](https://github.com/googleapis/release-please) watches conventional commits merged into `main`. It opens or updates a release PR containing the calculated version change, `CHANGELOG.md`, and release manifest. The generated PR title—and therefore its squash commit—uses `release(main): release <version>`. Merging that release PR creates a GitHub release and, after the test suite passes, publishes `pi-pr-review` to npm with signed provenance.
|
|
4
4
|
|
|
5
5
|
## Semver
|
|
6
6
|
|