diff --git a/.opencode/commands/review-pr.md b/.opencode/commands/review-pr.md index c3a0edc..4cc8dc8 100644 --- a/.opencode/commands/review-pr.md +++ b/.opencode/commands/review-pr.md @@ -1,11 +1,11 @@ --- -description: Comprehensive GitHub PR review — gathers the PR via gh, runs specialized review subagents in parallel, normalizes and deduplicates findings, validates inline comment anchoring, and posts a single review with inline and summary comments back to the PR. +description: Comprehensive GitHub PR review — gathers the PR via gh, runs specialized review subagents in parallel, normalizes and deduplicates findings, validates file:line references against the diff, and returns a single review response for the OpenCode GitHub integration to post. agent: general --- # Comprehensive PR Review -Perform a comprehensive pull request review by orchestrating specialized review subagents in parallel. Each subagent returns only noteworthy findings in a normalized format. You then deduplicate and filter across agents, validate that findings can be anchored to the diff, and post a single review back to the PR. +Perform a comprehensive pull request review by orchestrating specialized review subagents in parallel. Each subagent returns only noteworthy findings in a normalized format. You then deduplicate and filter across agents, validate file:line references against the diff, and return a single review response. The surrounding `opencode github run` integration posts that response to the PR as `opencode-agent[bot]`; do not post to GitHub yourself. **Requested review aspects (optional):** "$ARGUMENTS" @@ -32,7 +32,7 @@ fi ``` - If `PR_NUMBER` is set, run `gh pr view "$PR_NUMBER" --json number,title,body,baseRefName,headRefName,files,url` to confirm a PR is available. - - If it succeeds, you are in **PR mode**: post results back with `gh` (step 7). + - If it succeeds, you are in **PR mode**: return a review response (step 7). - If it fails (no PR or not in CI), fall back to **local mode**: review `git diff` and `git status`, and report findings directly to the user without posting anything to GitHub. - Do not rely on the current git branch to identify the PR. GitHub Actions pull request workflows usually check out a detached merge ref. @@ -56,7 +56,7 @@ Parse `$ARGUMENTS` (the requested aspects). Supported aspect keywords: - `comments` → `comment-analyzer` - `errors` → `silent-failure-hunter` - `types` → `type-design-analyzer` -- `simplify` → run `code-simplifier` as a refinement step only; **do not post a review**; stop after simplification +- `simplify` → run `code-simplifier` as a refinement step only; **do not return a review**; stop after simplification - `all` or no argument → run all applicable reviewers (see below) ### Deterministic reviewer set for `all` @@ -106,7 +106,7 @@ Instruct every subagent to: - If no noteworthy findings exist, return an empty list and a one-line "no issues" note. -Do **not** let subagents post comments themselves. The orchestrator is the only one that posts. +Do **not** let subagents post comments themselves. The orchestrator returns the final response; the `opencode github run` integration posts it. ## 5. Normalize, filter, and aggregate findings @@ -120,7 +120,7 @@ Collect all findings from all subagents. Each finding must have `file`, `line`, 2. **Drop nitpicks**: remove cosmetic preferences, style-only feedback, or issues with no real-world impact. 3. **Drop findings not supported by the diff**: if the file referenced is not in the changed-file set, drop the finding. Do **not** compare `line` against the changed-file set — it contains paths, not line numbers. Leave line validation to the diff-anchoring step (step 6). 4. **Deduplicate across agents**: if two or more agents report substantially the same issue at the same location, keep the most specific one (usually from the specialized agent) and discard the duplicate. -5. **Aggregate by root cause**: when several findings share the same underlying root cause (for example, the same inconsistency repeated across README, SKILL.md, and command files), keep **one representative finding** as an inline comment and collapse the remaining occurrences into the summary body. Prefer root-cause aggregation over line-by-line repetition. Cross-document or cross-file consistency problems should usually be summarized once in the review body instead of repeated at each affected location. +5. **Aggregate by root cause**: when several findings share the same underlying root cause (for example, the same inconsistency repeated across README, SKILL.md, and command files), keep **one representative finding** as a `file:line` reference and collapse the remaining occurrences into the summary body. Prefer root-cause aggregation over line-by-line repetition. Cross-document or cross-file consistency problems should usually be summarized once in the review body instead of repeated at each affected location. 6. **Orchestrator second filter**: review every remaining finding yourself and remove any you do not also deem noteworthy. This filter keeps the signal high. ### Grouping @@ -133,54 +133,25 @@ Group surviving findings by severity: Prefer fewer, higher-signal comments over exhaustive lists. Optional guardrail suggestions (such as adding tests for agent frontmatter or reference validation) should be downgraded to `suggestion` severity at most. -## 6. Validate inline comment anchoring +## 6. Validate file:line references against the diff -Before building the review payload, validate every finding against the PR diff. +Before building the review response, validate every finding against the PR diff so the `file:line` references in the single OpenCode comment stay accurate. -### Anchoring rules +### Validation rules - Obtain the diff hunk list from `gh pr diff "$PR_NUMBER"` (already gathered in step 2). - For each finding, check whether its `line` appears in the diff or diff context for `file`. - - A line is anchored if: it falls within a `+` hunk in the diff for the given file, or within the surrounding unchanged-context lines of that hunk. - - Use `side: "RIGHT"` and the head-file line number for every inline comment. -- If a finding's line **cannot be safely anchored** to the diff, move it to the summary body as a `file:line` reference instead of an inline comment. Do **not** drop it — the file is a changed file, only the line is unanchorable. -- Never fail the entire review because one comment is unanchorable. Move it and continue. + - A line is valid if: it falls within a `+` hunk in the diff for the given file, or within the surrounding unchanged-context lines of that hunk. +- If a finding's line **cannot be safely matched** to the diff, keep it in the summary body but reference the nearest valid anchor line (or the file alone) instead of an unverified line number. Do **not** drop it — the file is a changed file, only the line is unverifiable. +- Never fail the entire review because one finding's line is unverifiable. Adjust the reference and continue. -## 7. Post the results +## 7. Return the results ### PR mode -Post a **single review** via: +Return a **single concise markdown review response**. Do **not** call `gh api`, `gh pr review`, or `gh pr comment` — the surrounding `opencode github run` integration is responsible for posting the final response to the PR as `opencode-agent[bot]`. Calling GitHub write APIs directly would create a duplicate `github-actions[bot]` posting. -```bash -gh api -X POST repos/{owner}/{repo}/pulls/$PR_NUMBER/reviews --input - <<'EOF' -{ - "event": "COMMENT", - "body": "", - "comments": [ - {"path": "src/foo.ts", "line": 42, "side": "RIGHT", "body": "**important**: ..."} - ] -} -EOF -``` - -Use `{owner}` and `{repo}` from the git remote. Use the explicit `PR_NUMBER` from step 1. - -**Review event policy:** - -- Use `event: "COMMENT"` for normal reviews. -- Use `event: "REQUEST_CHANGES"` only when there are critical findings that block merge. - -**Posting policy:** - -- **Anchored findings** → inline comments on the specific representative line. -- **Unanchorable findings** (changed file but no safe diff line) → summary body as `file:line` references. -- **Duplicated root causes** → one representative inline comment plus a summary-body entry listing all affected files. -- Reserve inline comments for issues tied to a specific representative line. Cross-document or cross-file consistency problems should usually be summarized once in the review body instead of repeated across every affected file. - -**Inline comment format:** - -Each inline comment body should begin with the severity tag: `**critical**:`, `**important**:`, or `**suggestion**:`, followed by the finding message. +Include all findings (whether or not their line matched the diff) in the summary body as `file:line` references so the single top-level OpenCode comment stays actionable. **Summary body format:** @@ -191,20 +162,20 @@ Reviewed files across areas: . ### Critical () -- `file:line` — issue (unanchorable findings and aggregated root-cause occurrences; anchored ones appear inline) +- `file:line` — issue ### Important () -- `file:line` — issue (unanchorable findings and aggregated root-cause occurrences) +- `file:line` — issue ### Suggestions () -- `file:line` — issue (unanchorable findings and aggregated root-cause occurrences) +- `file:line` — issue ``` -When a root cause affects multiple files, list the representative inline comment once and add a single summary entry naming all affected files (e.g. `Same inconsistency across README.md, SKILL.md, review-pr.md — see inline comment on README.md:42`). +When a root cause affects multiple files, add a single summary entry naming all affected files (e.g. `Same inconsistency across README.md, SKILL.md, review-pr.md`). -If there are **no findings at all**, post: +If there are **no findings at all**, return: ```text No noteworthy issues found. @@ -212,19 +183,6 @@ No noteworthy issues found. Do not spam praise or padding. One concise confirmation is sufficient. -### Failure fallback - -If `gh api .../reviews` fails: - -1. **Retry without invalid inline comments**: if the error message identifies a specific invalid comment, remove it (move that finding to the summary body) and retry once. -2. **Fall back to top-level comment**: if still failing, post a single top-level comment with the full summary plus all findings as `file:line` references: - -```bash -gh pr comment "$PR_NUMBER" --body "$SUMMARY" -``` - -Never leave the user with no feedback. - ### Local mode Print the same summary to the user. Do not call `gh`. @@ -232,7 +190,7 @@ Print the same summary to the user. Do not call `gh`. ## 8. Notes - Keep feedback concise. A short review with real signal beats a long review with padding. -- Never post secrets, tokens, or full file contents in comments. -- The workflow grants `pull-requests: write`, so `GH_TOKEN` or `GITHUB_TOKEN` can post reviews when passed to the OpenCode step. Do not attempt to push commits or merge. +- Never include secrets, tokens, or full file contents in the review response. +- The `opencode github run` integration posts your final response to the PR as `opencode-agent[bot]`; do not call `gh api`, `gh pr review`, or `gh pr comment` yourself. The workflow may still grant `pull-requests: write` for the integration's posting, and `GH_TOKEN` or `GITHUB_TOKEN` is needed only for `gh pr diff` / `gh pr view` reads. Do not attempt to push commits or merge. - If `$ARGUMENTS` lists specific aspects, respect them and skip the rest. - Re-run after fixes to verify issues are resolved. diff --git a/.opencode/skills/review-pr/SKILL.md b/.opencode/skills/review-pr/SKILL.md index 10aaf64..021c460 100644 --- a/.opencode/skills/review-pr/SKILL.md +++ b/.opencode/skills/review-pr/SKILL.md @@ -1,11 +1,11 @@ --- name: review-pr -description: Run a comprehensive pull request review across changed files using Claude Code Action-compatible core reviewers and pr-review-toolkit specialty agents. Gathers the PR via gh, runs agents in parallel, normalizes and deduplicates findings, validates inline comment anchoring, and posts a single review with inline and summary comments back to the PR. Use when reviewing a PR before merge, before requesting review, after addressing feedback, or when the user asks to review a diff or recent changes. +description: Run a comprehensive pull request review across changed files using Claude Code Action-compatible core reviewers and pr-review-toolkit specialty agents. Gathers the PR via gh, runs agents in parallel, normalizes and deduplicates findings, validates file:line references against the diff, and returns a single review response for the OpenCode GitHub integration to post. Use when reviewing a PR before merge, before requesting review, after addressing feedback, or when the user asks to review a diff or recent changes. --- # Comprehensive PR Review -Run a comprehensive pull request review by orchestrating specialized review agents, each focusing on one aspect of code quality. The orchestrator gathers the PR, spawns agents as subagents via the `task` tool, normalizes and deduplicates findings, validates inline comment anchoring, and posts the results back to the PR. +Run a comprehensive pull request review by orchestrating specialized review agents, each focusing on one aspect of code quality. The orchestrator gathers the PR, spawns agents as subagents via the `task` tool, normalizes and deduplicates findings, validates file:line references against the diff, and returns a single review response. The surrounding `opencode github run` integration posts that response to the PR as `opencode-agent[bot]`; the skill must not post to GitHub directly. ## When to Use @@ -35,7 +35,7 @@ If no aspects are specified, run all applicable reviews. | `comments` | `comment-analyzer` | Code comment accuracy and maintainability | | `errors` | `silent-failure-hunter` | Silent failures, broad catch blocks, error handling | | `types` | `type-design-analyzer` | Type design and invariant expression | -| `simplify` | `code-simplifier` | Refinement only — does not post a review | +| `simplify` | `code-simplifier` | Refinement only — does not return a review | | `all` (default) | All applicable (see below) | Deterministic full review | ## Deterministic `all` Behavior @@ -100,58 +100,45 @@ Every subagent returns findings in this normalized structure: message: ``` -### Filtering rules applied before posting +### Filtering rules applied before returning the response 1. Drop praise-only items (no actionable issue). 2. Drop nitpicks (cosmetic preferences, no real-world impact). 3. Drop findings not supported by the diff — check **file membership only**; the changed-file set contains paths, not line numbers. Leave line validation to the anchoring step. 4. Deduplicate across agents (keep the most specific finding when two agents report the same issue at the same location). -5. **Aggregate by root cause**: when several findings share the same underlying root cause (e.g. the same inconsistency repeated across README, SKILL.md, and command files), keep one representative finding as an inline comment and collapse the rest into the summary body. Prefer root-cause aggregation over line-by-line repetition. Cross-document or cross-file consistency problems should usually be summarized once in the review body instead of repeated at each affected location. +5. **Aggregate by root cause**: when several findings share the same underlying root cause (e.g. the same inconsistency repeated across README, SKILL.md, and command files), keep one representative finding as a `file:line` reference and collapse the rest into the summary body. Prefer root-cause aggregation over line-by-line repetition. Cross-document or cross-file consistency problems should usually be summarized once in the review body instead of repeated at each affected location. 6. Orchestrator second filter: the orchestrator reviews every remaining finding and discards any it does not also deem noteworthy. Prefer fewer, higher-signal comments over exhaustive lists. Optional guardrail suggestions (such as adding tests for agent frontmatter or reference validation) should be downgraded to `suggestion` severity at most. -## Inline Comment Anchoring +## File:line Reference Validation -Before building the review payload, every finding is validated against the PR diff: +Before building the review response, every finding is validated against the PR diff so the `file:line` references in the single OpenCode comment stay accurate: -- Inline comments use `side: "RIGHT"` and the head-file line number. -- A line is anchored if it falls within a `+` hunk or surrounding unchanged-context lines of that hunk for the given file. -- Findings whose line cannot be safely anchored are **moved to the summary body** as `file:line` references — not dropped, since the file is a changed file and only the line is unanchorable. -- One invalid comment never fails the entire review. +- A line is valid if it falls within a `+` hunk or surrounding unchanged-context lines of that hunk for the given file. +- Findings whose line cannot be safely matched are **kept in the summary body** with the nearest valid anchor line (or the file alone) instead of an unverified line number — not dropped, since the file is a changed file and only the line is unverifiable. +- One unverifiable reference never fails the entire review. -## Review Posting Policy +## Review Output Policy -Post one review via `gh api -X POST repos/{owner}/{repo}/pulls/$PR_NUMBER/reviews`. +Return one final markdown review response. Do **not** call `gh api`, `gh pr review`, or `gh pr comment` — the surrounding `opencode github run` integration posts the response to the PR as `opencode-agent[bot]`. Posting directly would create a duplicate `github-actions[bot]` posting. -- `event: "COMMENT"` — normal reviews. -- `event: "REQUEST_CHANGES"` — only when critical findings block merge. +All findings (whether or not their line matched the diff) go in the summary body as `file:line` references so the single top-level OpenCode comment stays actionable. -**Posting policy:** +**Output policy:** -- **Anchored findings** → inline comments on the specific representative line. -- **Unanchorable findings** (changed file but no safe diff line) → summary body as `file:line` references. -- **Duplicated root causes** → one representative inline comment plus a summary-body entry listing all affected files. -- Reserve inline comments for issues tied to a specific representative line. Cross-document or cross-file consistency problems should usually be summarized once in the review body instead of repeated across every affected file. +- **Duplicated root causes** → one summary entry listing all affected files. +- Reserve `file:line` references for issues tied to a specific line. Cross-document or cross-file consistency problems should usually be summarized once in the review body instead of repeated across every affected file. Use the review body for: - Concise summary of areas reviewed -- Unanchorable findings +- All findings as `file:line` references - Aggregated root-cause occurrences and their affected files - General observations - No-finding confirmation -If no findings: post `No noteworthy issues found.` — one line, no padding. - -## Fallback Behavior - -If the reviews API call fails: - -1. Retry once after removing the invalid inline comment (move it to the summary body). -2. If still failing, fall back to one top-level `gh pr comment "$PR_NUMBER" --body "$SUMMARY"`. - -Never leave the user with no feedback. +If no findings: return `No noteworthy issues found.` — one line, no padding. ## Workflow @@ -160,8 +147,8 @@ Never leave the user with no feedback. 3. **Choose reviewers** — based on `$ARGUMENTS` and diff content. 4. **Launch subagents in parallel** — pass diff, files, and PR metadata. 5. **Normalize, filter, and aggregate** — apply filtering rules, root-cause aggregation, and orchestrator second filter. -6. **Validate anchoring** — check each finding against the diff; move unanchored findings to summary (do not drop them). -7. **Post** — single review in PR mode; print summary in local mode. +6. **Validate references** — check each finding against the diff; adjust unverifiable line references (do not drop them). +7. **Return** — single markdown review response in PR mode; print summary in local mode. The `opencode github run` integration posts the PR-mode response as `opencode-agent[bot]`. ## Workflow Integration @@ -183,7 +170,7 @@ Never leave the user with no feedback. **In GitHub Actions:** 1. The `opencode.yml` workflow runs `/review-pr` on PR open / ready for review. -2. The orchestrator gathers the PR via `gh`, runs the agents, and posts a review with inline comments. +2. The orchestrator gathers the PR via `gh`, runs the agents, and returns a single markdown review response. The `opencode github run` integration posts that response to the PR as `opencode-agent[bot]` — no separate `github-actions[bot]` review is produced. 3. Re-run by commenting `/opencode` or `/oc` on the PR. **After PR feedback:** @@ -198,4 +185,5 @@ Never leave the user with no feedback. - Agents run autonomously and return structured findings. - Each agent focuses on its specialty for deep analysis. - Results are actionable with specific `file:line` references. -- Never post secrets, tokens, or full file contents in review comments. +- The skill returns one final markdown response; it never posts to GitHub directly. +- Never include secrets, tokens, or full file contents in the review response. diff --git a/README.md b/README.md index efcd430..910ae8d 100644 --- a/README.md +++ b/README.md @@ -80,11 +80,9 @@ When `use-github-token: true`, pass `GITHUB_TOKEN` in `env` and grant the workfl ## Pull Request Reviews -The bundled `/review-pr` command posts reviews back to GitHub with `gh`. Workflows that invoke it must provide: +The bundled `/review-pr` command produces a single markdown review response that the OpenCode GitHub integration posts to the PR as `opencode-agent[bot]`. The command does not post to GitHub directly, so a review run yields one OpenCode PR comment rather than a separate `github-actions[bot]` review. Workflows that invoke it must provide: -- `pull-requests: write` -- `issues: write` when falling back to `gh pr comment` -- `GH_TOKEN: ${{ github.token }}` or `GITHUB_TOKEN: ${{ github.token }}` +- `GH_TOKEN: ${{ github.token }}` or `GITHUB_TOKEN: ${{ github.token }}` for `gh pr diff` / `gh pr view` reads - A valid API key for the selected model provider with available credits or quota Example OpenCode step: @@ -116,7 +114,7 @@ The bundled toolkit combines Claude Code Action-style core reviewers with `pr-re | `/review-pr errors` | `silent-failure-hunter` | | `/review-pr comments` | `comment-analyzer` | | `/review-pr types` | `type-design-analyzer` | -| `/review-pr simplify` | `code-simplifier` — refinement only, does not post a review | +| `/review-pr simplify` | `code-simplifier` — refinement only, does not return a review | #### Core reviewers (Claude Code Action-compatible) @@ -134,7 +132,7 @@ The bundled toolkit combines Claude Code Action-style core reviewers with `pr-re - **`comment-analyzer`** — comment accuracy, completeness, and comment rot - **`type-design-analyzer`** — type invariants, encapsulation, and design quality -Findings are normalized, deduplicated across agents, and validated against the PR diff before posting. **Posting policy:** anchored findings become inline comments on the specific representative line; unanchorable findings (changed file but no safe diff line) move to the summary body as `file:line` references; duplicated root causes produce one representative inline comment plus a summary-body entry listing all affected files. Cross-document or cross-file consistency problems are summarized once in the review body instead of repeated at each location. If the reviews API call fails, the skill falls back to a single top-level PR comment. +Findings are normalized, deduplicated across agents, and validated against the PR diff before being returned. All findings go into a single markdown response as `file:line` references; duplicated root causes produce one summary entry listing all affected files, and cross-document or cross-file consistency problems are summarized once instead of repeated at each location. The surrounding `opencode github run` integration posts that response to the PR as `opencode-agent[bot]`. ## Examples diff --git a/action.yml b/action.yml index 3f5f788..db25113 100644 --- a/action.yml +++ b/action.yml @@ -59,9 +59,15 @@ runs: shell: bash -euo pipefail {0} env: INPUT_VERSION: ${{ inputs.version }} + GITHUB_TOKEN: ${{ github.token }} run: | if [[ "${INPUT_VERSION}" == "latest" ]]; then - OPENCODE_VERSION="$(curl -fsSL https://api.github.com/repos/anomalyco/opencode/releases/latest | jq -r '.tag_name')" + if [[ -n "${GITHUB_TOKEN:-}" ]]; then + auth_header=(-H "Authorization: token ${GITHUB_TOKEN}") + else + auth_header=() + fi + OPENCODE_VERSION="$(curl -fsSL "${auth_header[@]}" https://api.github.com/repos/anomalyco/opencode/releases/latest | jq -r '.tag_name')" else OPENCODE_VERSION="${INPUT_VERSION}" fi