You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(review): remove --post, drop JSON contract, let Claude do posting (#70)
Third attempt at --post exposed that parser-side heuristics are the
wrong abstraction. v1.0.9 hit the undici body-timeout bug. v1.0.10
added a stderr handoff trailer to shift posting to Claude Code.
v1.0.11 tolerated unescaped inner quotes. Then a real run produced
*valid* JSON of a *different shape* (`{findings: [{summary, findings: [...]}]}`
with no top-level `verdict`), rejected by the schema-aware parser,
falling through to raw-JSON-in-chat and raw-JSON-in-the-PR-comment
again.
Chasing model-invented schemas is a losing game. Fix: remove the
whole flag, drop the JSON output contract entirely, and let Claude
Code handle posting via its own Bash tool when the user asks.
Changes:
* opencode-companion.mjs: remove --post/--confidence-threshold
flags from both review handlers, strip the tryParseReview +
renderReview path so review output is now plain prose passed
through unchanged (with the existing model header), delete
parseConfidenceThreshold and emitPostTrailer.
* Delete plugins/opencode/scripts/lib/pr-comments.mjs and
plugins/opencode/scripts/lib/review-parser.mjs entirely.
* Delete tests/pr-comments.test.mjs and tests/review-parser.test.mjs.
* render.mjs: drop renderReview (no callers left). render.test.mjs:
drop its test suite.
* prompts/adversarial-review.md: replace structured_output_contract
with an output_format section that asks for plain markdown
prose with a per-finding heading/file/confidence shape.
* lib/prompts.mjs: same treatment for the standard review prompt.
* commands/review.md and adversarial-review.md: remove --post /
--confidence-threshold from arg-hint and flag list, delete the
"Post-review publishing" trailer-parsing section, add an
"Optional: post the review to GitHub" section that fires ONLY
when the user explicitly asked in natural language. Claude
composes its own summary body, writes the gh review payload to
a temp file via the Write tool, POSTs via `gh api ... --input`,
and reports html_url. Never REQUEST_CHANGES — always COMMENT.
Net effect: the companion is simpler, the chat output is prose the
user can actually read, and posting is Claude's job (where it
belongs, because Claude has full editorial discretion and full
access to gh).
Tests: 220/220 pass (down from 268 because 48 cases covered the
removed post-plumbing). No regressions in remaining suites.
-`--model <id>` overrides the saved setup default model and OpenCode's own default model for this single review (e.g. `--model openrouter/anthropic/claude-opus-4-6`). Pass it through verbatim if the user supplied it.
41
41
-`--free` tells the companion script to shell out to `opencode models`, filter for first-party `opencode/*` free-tier models (those ending in `:free` or `-free`), and pick one at random for this review. Restricted to the `opencode/*` provider because OpenRouter free-tier models have inconsistent tool-use support, and the review agent needs `read`/`grep`/`glob`/`list`. Pass it through verbatim if the user supplied it. `--free` and `--model` are mutually exclusive — the companion will error if both are given.
42
42
-`--pr <number>` reviews a GitHub pull request via `gh pr diff` instead of the local working tree. The cwd must be a git repo whose remote points at the PR's repository, and `gh` must be installed and authenticated.
43
-
-`--post` (opt-in, requires `--pr`) publishes the adversarial review back to the PR as a GitHub review comment. The summary (verdict + findings table + collapsible full findings) is posted as the review body, and any finding with confidence at or above the threshold whose line is part of the PR diff is posted as an inline review comment. Findings below the threshold or pointing at lines outside the diff stay in the summary only. The review is always published with `event: "COMMENT"` — never `REQUEST_CHANGES` — because this tool is advisory. Pass `--post` through verbatim if the user supplied it.
44
-
-`--confidence-threshold <n>` (optional, default `0.8`) sets the confidence bar for inline comments when `--post` is set. Accepts `0..1` floats or percentages (`80`, `80%`). Pass through verbatim.
45
43
-`--path <path>` reviews a specific file or directory instead of git diff. Can be specified multiple times (`--path src --path lib`). When `--path` is set, the review is assembled from the actual file contents at those paths rather than from `git diff`. This is useful for reviewing specific directories, fixed sets of files, or large untracked/imported code drops. Mutually exclusive with `--pr` (paths take precedence over PR mode).
46
44
47
45
PR reference extraction (REQUIRED — read this carefully):
@@ -60,15 +58,15 @@ Focus text quoting (REQUIRED):
60
58
-`/opencode:adversarial-review --background look for race conditions in $RUNTIME` → `node ... adversarial-review --background 'look for race conditions in $RUNTIME'`
61
59
62
60
Foreground flow:
63
-
- First, transform `$ARGUMENTS` using the **PR reference extraction** and **Focus text quoting** rules above. Pass through `--wait`, `--background`, `--base`, `--scope`, `--model`, `--pr`, `--post`, and `--confidence-threshold` flags as-is; convert any `PR #N` reference in the user's text to `--pr N`; single-quote whatever free-form focus text remains.
61
+
- First, transform `$ARGUMENTS` using the **PR reference extraction** and **Focus text quoting** rules above. Pass through `--wait`, `--background`, `--base`, `--scope`, `--model`, and `--pr` flags as-is; convert any `PR #N` reference in the user's text to `--pr N`; single-quote whatever free-form focus text remains.
64
62
- Then run the resulting command (illustrative shape — substitute the actual transformed args):
- Return the command stdout verbatim, exactly as-is.
69
67
- Do not paraphrase, summarize, or add commentary before or after it.
70
68
- Do not fix any issues mentioned in the review output.
71
-
- If `--post` was set, ALSO follow the "Post-review publishing" steps below before your final turn output.
69
+
- If the user's original request asked for the review to be posted/commented/published to the PR, follow the "Optional: post the review to GitHub" section below after returning the review output.
72
70
73
71
Background flow:
74
72
- Apply the same `$ARGUMENTS` transformation as the foreground flow above (PR ref extraction + focus text single-quoting).
@@ -82,25 +80,19 @@ Bash({
82
80
```
83
81
- Do not call `BashOutput` or wait for completion in this turn.
84
82
- After launching the command, tell the user: "OpenCode adversarial review started in the background. Check `/opencode:status` for progress."
85
-
- When the user later asks to collect the result and `--post` was part of the original arguments, read the completed `BashOutput` and then follow the "Post-review publishing" steps below on the captured stderr.
83
+
- When the user later asks to collect the result and the original request asked for a PR post, read the completed `BashOutput`, return the review output, then follow the "Optional: post the review to GitHub" section below.
86
84
87
-
Post-review publishing (only when `--post` is set):
88
-
- After the companion script exits, scan its **stderr** (not stdout) for a block shaped like:
<command>gh api -X POST "repos/{owner}/{repo}/pulls/412/reviews" --input '/tmp/…json'</command>
96
-
<cleanup>rm -f '/tmp/…json'</cleanup>
97
-
</opencode_post_instructions>
98
-
```
99
-
- If the block is present:
100
-
1. Run the exact string inside `<command>` via a single `Bash` tool call. Do not edit the command, do not re-serialize the JSON, do not invent a different endpoint — the companion already quoted the path and picked the correct endpoint.
101
-
2. Parse the response body; GitHub returns a JSON object with an `html_url` field pointing at the newly-created review.
102
-
3. After the verbatim stdout from step "Return the command stdout verbatim" above, append a single-line status like: `Review posted to PR #<pr>: <html_url>`. Include "(N inline comments)" only when `<inline_count>` is greater than zero.
103
-
4. Run the exact string inside `<cleanup>` via a second `Bash` tool call to delete the temp payload file. If this fails, mention it once; do not retry or escalate.
104
-
5. If step 1 fails (non-zero exit, or `gh` returns an error), append `Failed to post review to PR #<pr>: <stderr snippet>` instead of the success line and still run `<cleanup>`. Do not retry.
105
-
- If the block is **not** present (either `--post` wasn't requested or the companion's preparation step failed — in which case stderr will contain a `[opencode-companion] Failed to prepare PR post …` line instead), do nothing extra.
106
-
- Never POST, comment, or otherwise mutate the PR unless you found an `<opencode_post_instructions>` block in stderr on THIS run. Do not "help" by reposting or retrying a previous run's block.
85
+
Optional: post the review to GitHub (only when the user explicitly asked):
86
+
- Triggers: the user's slash-command invocation said something like "and post it to the PR", "comment it back", "publish the review", "review on GitHub". A bare `/opencode:adversarial-review --pr 412` is NOT a request to post. If in doubt, do not post.
87
+
- Preconditions: `--pr <N>` was in the arguments AND the user's request explicitly asked for publishing. Never post a review for a `--path` run (paths have no PR to post to).
88
+
- What to post:
89
+
- A single GitHub PR review (`event: COMMENT`) containing a clean markdown summary body written in your own voice based on the review output. Do not paste the raw review output verbatim into the body; rewrite it as a digest with a short ship/no-ship line, the handful of material findings, and a closing note.
90
+
- Optionally, for findings where you are confident about the exact file and line, include inline review comments anchored to those lines. Only include an inline comment if the line is part of the PR's unified diff (`gh api repos/{owner}/{repo}/pulls/<N>/files` → `patch` fields). Skip inline anchoring when the review is prose-only or the lines are unclear.
91
+
- How to post:
92
+
1. Compose the payload as a single JSON object: `{ "commit_id": "<head SHA>", "event": "COMMENT", "body": "<markdown summary>", "comments": [ { "path": "...", "line": N, "side": "RIGHT", "body": "..." }, ... ] }`. Get the head SHA via `gh pr view <N> --json headRefOid --jq .headRefOid`.
93
+
2. Write the payload to a temp file (`/tmp/opencode-post-<pr>-<random>.json`) using the `Write` tool so you control escaping — do NOT try to build a heredoc inside a Bash call.
94
+
3. Run `gh api -X POST "repos/{owner}/{repo}/pulls/<N>/reviews" --input /tmp/...json` via a single `Bash` call.
95
+
4. Extract `html_url` from the response and append a one-line status to your final turn output: `Review posted to PR #<N>: <url>`.
96
+
5. Delete the temp file with `rm -f /tmp/...json`.
97
+
6. On failure, say `Failed to post review to PR #<N>: <error>` and do not retry.
98
+
- The review is always published with `event: "COMMENT"` — never `REQUEST_CHANGES`. This tool is advisory, not gatekeeping.
-`--model <id>` overrides the saved setup default model and OpenCode's own default model for this single review (e.g. `--model openrouter/anthropic/claude-opus-4-6`). Pass it through verbatim if the user supplied it.
43
43
-`--free` tells the companion script to shell out to `opencode models`, filter for first-party `opencode/*` free-tier models (those ending in `:free` or `-free`), and pick one at random for this review. Restricted to the `opencode/*` provider because OpenRouter free-tier models have inconsistent tool-use support, and the review agent needs `read`/`grep`/`glob`/`list`. Pass it through verbatim if the user supplied it. `--free` and `--model` are mutually exclusive — the companion will error if both are given.
44
44
-`--pr <number>` reviews a GitHub pull request via `gh pr diff` instead of the local working tree. The cwd must be a git repo whose remote points at the PR's repository, and `gh` must be installed and authenticated. Pass it through verbatim if the user supplied it.
45
-
-`--post` (opt-in, requires `--pr`) publishes the review as a PR review comment on GitHub. The summary (verdict + findings table) is posted as the review body, and any finding with confidence at or above the threshold whose line is part of the PR diff is posted as an inline review comment on that line. Findings below the threshold or pointing at lines outside the diff stay in the summary only. The review is always published with `event: "COMMENT"` — never `REQUEST_CHANGES` — because this tool is advisory. Pass `--post` through verbatim if the user supplied it.
46
-
-`--confidence-threshold <n>` (optional, default `0.8`) controls which findings become inline comments when `--post` is set. Accepts `0..1` floats or percentages (`80`, `80%`). Pass through verbatim if the user supplied it.
47
45
-`--path <path>` reviews a specific file or directory instead of git diff. Can be specified multiple times (`--path src --path lib`). When `--path` is set, the review is assembled from the actual file contents at those paths rather than from `git diff`. This is useful for reviewing specific directories, fixed sets of files, or large untracked/imported code drops. Mutually exclusive with `--pr` (paths take precedence over PR mode).
48
46
-**PR reference extraction (REQUIRED)**: if the user's input contains a PR reference like `PR #390`, `pr #390`, `PR 390`, or `pr 390` (e.g. `/opencode:review on PR #390`), you MUST extract the number yourself and pass it as `--pr 390`. Do not pass `PR #390` literally to bash — bash strips unquoted `#NNN` tokens as comments before they reach the companion script. Example: `node ... review --pr 390`, NOT `node ... review on PR #390`.
- Return the command stdout verbatim, exactly as-is.
56
54
- Do not paraphrase, summarize, or add commentary before or after it.
57
55
- Do not fix any issues mentioned in the review output.
58
-
- If `--post` was set, ALSO follow the "Post-review publishing" steps below before your final turn output.
56
+
- If the user's original request asked for the review to be posted/commented/published to the PR, follow the "Optional: post the review to GitHub" section below after returning the review output.
59
57
60
58
Background flow:
61
59
- Launch the review with `Bash` in the background:
@@ -68,25 +66,19 @@ Bash({
68
66
```
69
67
- Do not call `BashOutput` or wait for completion in this turn.
70
68
- After launching the command, tell the user: "OpenCode review started in the background. Check `/opencode:status` for progress."
71
-
- When the user later asks to collect the result and `--post` was part of the original arguments, read the completed `BashOutput` and then follow the "Post-review publishing" steps below on the captured stderr.
69
+
- When the user later asks to collect the result and the original request asked for a PR post, read the completed `BashOutput`, return the review output, then follow the "Optional: post the review to GitHub" section below.
72
70
73
-
Post-review publishing (only when `--post` is set):
74
-
- After the companion script exits, scan its **stderr** (not stdout) for a block shaped like:
<command>gh api -X POST "repos/{owner}/{repo}/pulls/412/reviews" --input '/tmp/…json'</command>
82
-
<cleanup>rm -f '/tmp/…json'</cleanup>
83
-
</opencode_post_instructions>
84
-
```
85
-
- If the block is present:
86
-
1. Run the exact string inside `<command>` via a single `Bash` tool call. Do not edit the command, do not re-serialize the JSON, do not invent a different endpoint — the companion already quoted the path and picked the correct endpoint.
87
-
2. Parse the response body; GitHub returns a JSON object with an `html_url` field pointing at the newly-created review.
88
-
3. After the verbatim stdout from step "Return the command stdout verbatim" above, append a single-line status like: `Review posted to PR #<pr>: <html_url>`. Include "(N inline comments)" only when `<inline_count>` is greater than zero.
89
-
4. Run the exact string inside `<cleanup>` via a second `Bash` tool call to delete the temp payload file. If this fails, mention it once; do not retry or escalate.
90
-
5. If step 1 fails (non-zero exit, or `gh` returns an error), append `Failed to post review to PR #<pr>: <stderr snippet>` instead of the success line and still run `<cleanup>`. Do not retry.
91
-
- If the block is **not** present (either `--post` wasn't requested or the companion's preparation step failed — in which case stderr will contain a `[opencode-companion] Failed to prepare PR post …` line instead), do nothing extra.
92
-
- Never POST, comment, or otherwise mutate the PR unless you found an `<opencode_post_instructions>` block in stderr on THIS run. Do not "help" by reposting or retrying a previous run's block.
71
+
Optional: post the review to GitHub (only when the user explicitly asked):
72
+
- Triggers: the user's slash-command invocation said something like "and post it to the PR", "comment it back", "publish the review", "review on GitHub". A bare `/opencode:review --pr 412` is NOT a request to post. If in doubt, do not post.
73
+
- Preconditions: `--pr <N>` was in the arguments AND the user's request explicitly asked for publishing. Never post a review for a `--path` run (paths have no PR to post to).
74
+
- What to post:
75
+
- A single GitHub PR review (`event: COMMENT`) containing a clean markdown summary body written in your own voice based on the review output. Do not paste the raw review output verbatim into the body; rewrite it as a digest with a short ship/no-ship line, the handful of material findings, and a closing note.
76
+
- Optionally, for findings where you are confident about the exact file and line, include inline review comments anchored to those lines. Only include an inline comment if the line is part of the PR's unified diff (`gh api repos/{owner}/{repo}/pulls/<N>/files` → `patch` fields). Skip inline anchoring when the review is prose-only or the lines are unclear.
77
+
- How to post:
78
+
1. Compose the payload as a single JSON object: `{ "commit_id": "<head SHA>", "event": "COMMENT", "body": "<markdown summary>", "comments": [ { "path": "...", "line": N, "side": "RIGHT", "body": "..." }, ... ] }`. Get the head SHA via `gh pr view <N> --json headRefOid --jq .headRefOid`.
79
+
2. Write the payload to a temp file (`/tmp/opencode-post-<pr>-<random>.json`) using the `Write` tool so you control escaping — do NOT try to build a heredoc inside a Bash call.
80
+
3. Run `gh api -X POST "repos/{owner}/{repo}/pulls/<N>/reviews" --input /tmp/...json` via a single `Bash` call.
81
+
4. Extract `html_url` from the response and append a one-line status to your final turn output: `Review posted to PR #<N>: <url>`.
82
+
5. Delete the temp file with `rm -f /tmp/...json`.
83
+
6. On failure, say `Failed to post review to PR #<N>: <error>` and do not retry.
84
+
- The review is always published with `event: "COMMENT"` — never `REQUEST_CHANGES`. This tool is advisory, not gatekeeping.
0 commit comments