pi-pr-review 1.6.3 → 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.
@@ -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
  }
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "1.6.3"
2
+ ".": "1.6.4"
3
3
  }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
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
+
3
10
  ## [1.6.3](https://github.com/10ego/pi-pr-review/compare/v1.6.2...v1.6.3) (2026-07-13)
4
11
 
5
12
 
package/README.md CHANGED
@@ -1,131 +1,117 @@
1
1
  # pi-pr-review
2
2
 
3
- A parallel, model-agnostic AI code reviewer for GitHub pull requests in the [Pi coding agent](https://pi.dev).
3
+ Parallel, model-agnostic AI code review for GitHub pull requests in the [Pi coding agent](https://pi.dev).
4
4
 
5
- Pass a PR number and pi will:
5
+ Give it a PR number and it will:
6
6
 
7
- 1. Resolve the PR in the **current directory's git repo** via `gh`.
8
- 2. Derive the **base branch** and **head (merging) branch** automatically from the PR.
9
- 3. Review the base↔head diff with four focused heavy lenses plus a light overview/minor scan, optional trusted user-level named baseline verification, then validate each candidate. Use `--full` to add convention/maintainability review.
10
- 4. Return a **structured review**: overview, strengths, verification, P0–P2 findings plus at most three direct-diff P3/nits by default, correctness/security/performance notes, and a verdict. `--full` reports every qualifying severity (`nit → P0`).
11
- 5. Optionally publish one formal GitHub `COMMENT` review with a top-level body and associated inline comments.
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
- **Major coverage by default, exhaustive minors on demand.** The default runs four independent heavy P0–P2 lenses and permits at most three direct-diff P3/nits from the light overview, reducing model and validation work without dropping lifecycle, contracts, security, or resource coverage. Use `--full` for exhaustive conventions, maintainability, style, naming, and minor findings. The **verdict** always depends only on blocking P0/P1 findings.
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 (`gh auth login`).
20
- - Run pi from inside the git repository that hosts the PR (so `gh` auto-detects the repo).
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
- ```bash
26
+ # Project scope
33
27
  pi install -l npm:pi-pr-review
34
28
  ```
35
29
 
36
- For local development only, you can point pi at a checkout with `pi install ./pi-pr-review` (add `-l` for project scope).
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
- ### Alternative: use the template without packaging
32
+ ## Quick start
41
33
 
42
- The prompt is a plain template — just copy it into a prompts directory pi already scans:
34
+ Configure the reviewer models:
43
35
 
44
- ```bash
45
- # global (all projects)
46
- cp prompts/pr-review.md ~/.pi/agent/prompts/
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
- ## Configure the review models
41
+ Then review a PR in the current repository:
52
42
 
53
- Model selection is configuration, not code. There are two layers:
43
+ ```text
44
+ /pr-review 123
45
+ ```
54
46
 
55
- ### 1. Tiered subagent models (recommended)
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
- The `/pr-review-config` command maps three labels to models:
49
+ ## Review modes
58
50
 
59
- | Tier | Used for | Pick a model that is… |
60
- |------|----------|----------------------|
61
- | `light` | overview / strengths / high-level risk scan | fast + cheap |
62
- | `medium` | `--full` convention compliance + readability / maintainability | balanced |
63
- | `heavy` | bug + security/logic review | strongest |
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
- 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`:
61
+ A review uses five focused passes by default:
82
62
 
83
- - One primary-model, fallback-model, thinking-level, and tool-policy row per tier (`light` / `medium` / `heavy`), an automatic-posting toggle, plus a configured-tool allowlist row.
84
- - Press Enter on a primary or fallback row to pick a model from a searchable list (or unset it); Enter/Space cycles thinking levels, tool policies, and allowlist presets. The menu sets one fallback model at a time; use the `key=value` form for longer fallback chains.
85
- - Selections apply and persist **immediately**; Esc closes the menu.
86
- - Type to search, and tab-completion is available for the `key=value` form.
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
- Outside the TUI (or with `show`), the command posts a Markdown summary table of your settings and the effective values instead.
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
- A `<spec>` is any pi model pattern, e.g. `provider/model` or `provider/model:high` (with a thinking level). Each of `light_thinking`, `medium_thinking`, and `heavy_thinking` accepts every pi-supported level: `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, or `max`:
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
- `unset` removes the tier override, so that child pi process inherits the ambient pi default; it does not mean `off`. When an effective tier or fallback model has no supported `:thinking` suffix and its tier thinking is unset, `/pr-review-config show` and review-tool output warn that the ambient default is inherited. If a model spec does include a supported suffix, such as `<model-spec>:xhigh`, its `:thinking` suffix takes precedence over `<tier>_thinking`; output warns when it shadows an explicit tier setting. This precedence applies to primary, nearest-tier, and fallback model attempts.
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
- A model-agnostic starting profile is:
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
- This is an opt-in cost/quality balance, not a speed guarantee: lower thinking can reduce reasoning work but may miss subtle risks, while higher thinking can improve depth at greater latency and token cost. Adjust against your own review results and model capabilities.
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
- Review-model settings are stored in:
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
- - **User:** `~/.pi/agent/pr-review.json`
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
- Verification profiles are different: `verificationBaselines` is read **only** from the user file. A project-local field with that name is always ignored, including in trusted projects.
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
- Example `pr-review.json`:
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": "<fast-model-spec>",
122
- "medium": "<balanced-model-spec>",
123
- "heavy": "<strong-model-spec:high>"
109
+ "light": "provider/fast-model",
110
+ "medium": "provider/balanced-model",
111
+ "heavy": "provider/strong-model:high"
124
112
  },
125
113
  "fallbacks": {
126
- "light": ["<backup-fast-model>"],
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,194 +123,105 @@ Example `pr-review.json`:
137
123
  "medium": "configured",
138
124
  "heavy": "configured"
139
125
  },
140
- "autoPostReviews": false,
141
- "verificationBaselines": {
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
- 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 18) and returns ordered per-pass results. `/pr-review` defaults to 5 slots for overview, two focused correctness specialists, security, and performance; at 200–399 KB it uses two balanced whole-file shards (10 concurrent passes), and at 400 KB or larger it uses three (15 concurrent passes). `--full` adds conventions/maintainability and uses 6/12/18 slots. When a caller requests a lower cap and passes must queue, heavy specialists dispatch before medium/light work while results remain in deterministic request order. The complete diff is captured once in a mode-0600 temporary file and loaded by the extension through `context_file`; the assembled child task is piped over stdin instead of expanded into process argv, keeping large diffs out of the parent conversation, tool arguments, and platform argument-size limits. Ordinary passes receive the full diff; sharded passes collectively cover every changed file, and configured specialists can inspect the full path for concrete cross-shard interactions. The two correctness specialists preserve cross-file analysis while narrowing their objectives to reduce critical-path reasoning; error propagation belongs to contracts/data, while ownership/cleanup belongs to the resource specialist and remains correctness-significant. 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.
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. The `--full` conventions/maintainability pass and all 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
- **Risk disclosure:** the command executes code from the pull request without a filesystem or network sandbox. The lifecycle code supervises only the original POSIX process group; PR code can deliberately call `setsid` or otherwise create a new session and survive supervision and cleanup. This is not full process-tree containment. Use an external sandbox or container wrapper for untrusted pull requests, and configure a profile only when you explicitly accept this residual risk.
133
+ ## Publish to GitHub
171
134
 
172
- ### 2. The orchestrator / inline-fallback model
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 # balanced default: P0-P2 plus up to three P3/nits
187
- /pr-review 123 --full # comprehensive six-pass, all-severity review
188
- /pr-review 123 --comment # force one COMMENT review for this run
189
- /pr-review 123 --no-comment # suppress posting for this run
190
- /pr-review 123 --major-only # P0-P2 only; omits style/nit discovery
191
- /pr-review 123 --balanced # explicit alias for the balanced default
192
- /pr-review 123 --include-closed # review a closed/merged PR without a confirmation prompt
193
- /pr-review 123 --review-closed --comment # review and attempt a body-only COMMENT review
194
- /pr-review-publish 123 # publish the completed review cached in this session
195
- /pr-review-publish 123 --allow-stale # publish it after the PR head changed; never rerun the model
196
- ```
197
-
198
- `123` is the PR number in the current repo.
199
-
200
- The no-flag default retains four independent heavy full-diff lenses (lifecycle, contracts, security, and resources), configured repository tools, model/thinking settings, and source-grounded validation. Heavy passes report P0–P2, while the light overview may contribute at most three direct-diff P3/nits. `--balanced` is an explicit backward-compatible alias for this default. `--major-only` removes minor discovery entirely. `--full` adds the conventions/maintainability pass and exhaustive all-severity reporting. These three flags are mutually exclusive.
201
-
202
- ### Closed or merged PRs
203
142
 
204
- 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.
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.
205
144
 
206
- ### Automatic GitHub review posting
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.
207
146
 
208
- `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. The full effective automatic-posting decision (value, validity, source, and error) is frozen when the invocation is accepted, before review tools or optional verification can execute, and is never reread at publication time. Later config changes therefore cannot grant or revoke that invocation's automatic authority; an initially malformed value remains fail-closed. Invocation flags are captured before prompt expansion: `--comment` forces posting for one run independently of automatic config, `--no-comment` suppresses it, and using both is rejected.
147
+ If a new commit makes a completed review stale, publish the cached result without rerunning the model:
209
148
 
210
- 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. The top-level body contains overview/verdict information, inline-vs-summary counts, nits, and findings that cannot be posted inline; successfully attached P0–P3 findings appear only in their associated inline comments, not duplicated in the top-level body. 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 each formerly-inline finding folded into the body exactly once. Unknown or unconfirmed non-open lifecycle states fail without posting.
211
-
212
- ### Publishing a completed review after a new commit
213
-
214
- 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:
215
-
216
- ```
149
+ ```text
217
150
  /pr-review-publish 123 --allow-stale
218
151
  ```
219
152
 
220
- This command calls no review model and reuses the completed result for PR `123`. The default `/pr-review-publish 123` still rejects a stale head; `--allow-stale` is the explicit acknowledgement. A stale review is posted as one body-only formal `COMMENT` review against the head observed during preflight, begins with a warning that shows both the reviewed and observed full SHAs, and keeps every finding in the body. Inline comments are intentionally disabled because their original diff anchors may no longer be valid. Repository binding, duplicate, draft, lifecycle, payload-size, identity, and final head-stability checks remain active. If another commit lands during the publish preflight, run the same publish-only command again to acknowledge that newer observed head.
221
-
222
- 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.
223
-
224
- ### Duplicate review handling
225
-
226
- 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.
227
-
228
- ### 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.
229
154
 
230
- For actual review output, the assistant replies with **only** the JSON object (no prose, no fences). The only exception is the pre-review confirmation question for closed/merged PRs when `--include-closed` / `--review-closed` was not supplied.
155
+ ## Optional verification
231
156
 
232
- In the **interactive TUI**, the final JSON is rendered as a full review: a `## Code Review — PR #N: <title>` header, a **Verification** line, **Overview**, **Strengths**, a **Findings** table (sorted `P0 nit`, with a blocking column, location, and confidence) plus per-finding details, **Correctness / Security / Performance** notes, and a **Verdict**. In `print` / `json` / `rpc` modes the raw JSON is left untouched so piping and automation keep a machine-readable payload.
233
-
234
- 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.
235
158
 
236
159
  ```json
237
160
  {
238
- "pr": { "number": 33, "title": "fix(logs): parse date-time log timestamps", "head_sha": "0123456789abcdef0123456789abcdef01234567" },
239
- "disposition": "reviewed",
240
- "verification": "`go build ./...` ✅, `go test ./...` ✅ (130 passed)",
241
- "overview": "Migrates log timestamps from epoch-ms to RFC3339 to match the endpoint contract.",
242
- "strengths": ["Reuses FormatTimestamp instead of ad-hoc formatting; net -3 lines."],
243
- "findings": [
244
- {
245
- "title": "[P1] Guard against nil map before write",
246
- "severity": "P1",
247
- "blocking": true,
248
- "body": "Panics under concurrent writes; guard with a mutex.",
249
- "confidence_score": 0.9,
250
- "code_location": {
251
- "absolute_file_path": "pkg/store/cache.go",
252
- "line_range": { "start": 42, "end": 45 },
253
- "side": "RIGHT",
254
- "commentable": true
255
- }
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
256
174
  }
257
- ],
258
- "notes": { "correctness": "build confirms; no unused imports", "security": "none", "performance": "negligible" },
259
- "verdict": "approve",
260
- "overall_correctness": "patch is correct",
261
- "overall_explanation": "Clean, well-scoped contract-alignment change with matching tests.",
262
- "overall_confidence_score": 0.9
175
+ }
263
176
  }
264
177
  ```
265
178
 
266
- 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`.
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.
267
180
 
268
- **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.
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.
269
182
 
270
- ## What's in the package
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.
271
184
 
272
- ```
273
- pi-pr-review/
274
- ├─ package.json # pi manifest: prompts + extensions
275
- ├─ prompts/pr-review.md # the /pr-review orchestrator prompt
276
- ├─ lib/pr-review-policy.ts # pure tool-policy resolution/argv helpers
277
- ├─ lib/pr-review-publish.ts # safe COMMENT-review payload, validation, and gh publisher
278
- ├─ lib/pr-review-telemetry.ts # monotonic invocation intervals and overlap-safe summaries
279
- ├─ lib/pr-review-thinking.ts # tier thinking resolution and warnings
280
- ├─ lib/pr-review-verify.ts # strict profiles, exact-head verification, group supervision + cleanup
281
- ├─ extensions/pr-review-subagent.ts # review tools, named verification + /pr-review-config
282
- ├─ extensions/review-table.ts # renders JSON and triggers trusted configured publishing
283
- ├─ tests/pr-review-policy.test.ts # focused policy compatibility tests
284
- ├─ tests/pr-review-prompt.test.ts # orchestrator scheduling and safety contract tests
285
- ├─ tests/pr-review-publish.test.ts # posting gate, payload, marker, and anchor tests
286
- ├─ tests/pr-review-telemetry.test.ts # monotonic lifecycle and overlap accounting tests
287
- ├─ tests/pr-review-thinking.test.ts # tier thinking compatibility tests
288
- └─ tests/pr-review-verify.test.ts # verification success/failure/timeout/abort cleanup tests
289
- ```
185
+ ## Review output
186
+
187
+ Each finding includes:
290
188
 
291
- ## Speed, security & cost notes
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.
292
193
 
293
- ### Concurrent review and verification
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. |
294
201
 
295
- - The four independent major-defect lenses remain intact. The default executes five focused base passes: overview/minor hygiene, state/lifecycle correctness, contracts/data correctness, security, and performance. Diffs from 200–399 KB with multiple changed files run every base pass over two whole-file-balanced shards (10 concurrent passes); diffs at least 400 KB use three shards (15 concurrent passes). `--full` adds conventions/maintainability and expands those counts to 6/12/18. Smaller or single-file diffs remain unsharded. The orchestrator captures the diff once without dumping it into its conversation, removes it on every early exit or confirmation pause, and performs final cleanup after validation. The extension appends the bounded regular `context_file` internally for every full-diff pass and pipes the assembled task to child stdin rather than argv; the orchestrator later reads only candidate-specific hunks for independent validation. Only overview runs context-only with `--no-tools`; all heavy specialists and the optional `--full` medium pass retain configured tools for surrounding-file validation. Unsharded correctness specialists receive the complete diff; in sharded reviews every correctness lens covers every shard, and configured passes can inspect the full captured path to validate concrete cross-shard defects. Specialists start from that diff, batch independent evidence reads/checks for concrete candidates, and allow one evidence-driven follow-up instead of serially browsing the repository; every reported finding still requires substantiation. All subprocesses use `--no-context-files`, `--no-skills`, `--no-prompt-templates`, and `--no-themes` because the orchestrator supplies the complete review task explicitly; extension discovery and configured tools remain enabled. In `--full`, convention excerpts are sent only to the medium pass instead of every model.
296
- - The orchestrator gathers independent PR metadata, diff, identity, convention paths, and `pr_review_verify` `action=list` discovery in one concurrent initial tool turn, then reads only applicable convention files. It selects at most one applicable **user-level** verification name. Missing config means verification is disabled; project-local profiles are ignored. The model never supplies argv or a timeout.
297
- - 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.
298
- - 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.
299
- - 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.
300
- - 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.
301
203
 
302
- ### Performance telemetry
204
+ ## Safety and cost
303
205
 
304
- Review-tool results expose the effective `toolPolicy`, monotonic `elapsedMs`, per-attempt timing, observable batch scheduling offsets, and child phase diagnostics: `firstEventMs`, `firstAssistantMs`, and overlap-aware `toolElapsedMs`. In addition, when an accepted `/pr-review` reaches a recorded terminal, cleared, or replaced boundary, it appends a durable `pr-review-telemetry` session entry with these fields:
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.
305
212
 
306
- - `schemaVersion` (currently `2`), `clock`, `prNumber`, and `completion` identify the schema, monotonic clock, PR, and terminal boundary (`terminal_response`, `cleared`, or `replaced`).
307
- - `totalWallMs` is measured directly from accepted input to that boundary and includes any human-confirmation wait. Publication occurs afterward and is excluded.
308
- - `activeReviewMs` is the active timeline after human-confirmation wait is removed.
309
- - `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.
310
- - `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.
311
- - `phases.overlapMs` is the intersection of those two phase interval sets. `phases.observableToolWallMs` is their union, so concurrent work is not double-counted.
312
- - `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. The prompt schedules independent initial gathering together and batches known candidate-validation reads/checks into one parallel wave (plus at most one evidence-driven follow-up) without weakening the requirement to confirm every surviving finding.
313
- - `notes` records these accounting boundaries in the durable entry so downstream consumers do not have to infer them.
213
+ ## Development
314
214
 
315
- These measurements describe observed execution and do not guarantee speed. The balanced five-pass topology is the default; use `--full` when exhaustive convention/minor coverage justifies the additional work. Cache reordering and further context/tool reduction remain deferred pending evidence that review quality is preserved. Restore tools for a custom pass with `tool_policy: "configured"`; omitted policy retains legacy behavior unless `toolPolicies` config says otherwise.
215
+ Run the test suite with:
316
216
 
317
- ### Security, cost, and publishing
217
+ ```bash
218
+ bun test
219
+ ```
318
220
 
319
- - 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.
320
- - 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.
321
- - 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.
322
- - **`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.
323
- - 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:
324
222
 
325
- ## Design notes
223
+ ```bash
224
+ cp prompts/pr-review.md ~/.pi/agent/prompts/
225
+ ```
326
226
 
327
- - **Process** is PR-number driven: confirm non-open PRs, skip draft/same-head-already-reviewed work, fan out four major-defect lenses as five focused default passes (10/15 passes for adaptive large-diff shards), verify, validate/classify, emit JSON, then optionally publish one extension-owned formal `COMMENT` review. `--full` uses 6/12/18 passes.
328
- - **Bounds minor work by default:** retain every validated P0–P2 plus at most three direct-diff P3/nits. `--full` captures every qualifying severity (`nit → P0`). The verdict depends only on blocking findings.
329
- - **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.
330
- - 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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-pr-review",
3
- "version": "1.6.3",
3
+ "version": "1.6.4",
4
4
  "description": "Parallel AI code review for GitHub pull requests in the Pi coding agent, with model-agnostic tiered subagents, structured findings, optional verification, and safe COMMENT-only publishing.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -6,6 +6,7 @@
6
6
  "release-type": "node",
7
7
  "package-name": "pi-pr-review",
8
8
  "changelog-path": "CHANGELOG.md",
9
+ "pull-request-title-pattern": "release${scope}: release${component} ${version}",
9
10
  "include-component-in-tag": false,
10
11
  "changelog-sections": [
11
12
  { "type": "feat", "section": "Features", "hidden": false },