Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
"name": "utils",
"source": "./plugins/utils",
"description": "A generic utilities plugin serving as a catch-all for various helper commands",
"version": "0.0.7"
"version": "0.0.8"
},
{
"name": "olm",
Expand Down
2 changes: 1 addition & 1 deletion PLUGINS.md
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ See [plugins/testing/README.md](plugins/testing/README.md) for detailed document
A generic utilities plugin serving as a catch-all for various helper commands and agents

**Commands:**
- **`/utils:address-reviews` `[PR number (optional - uses current branch if omitted)]`** - Fetch and address all PR review comments
- **`/utils:address-reviews` `[PR number (optional - uses current branch if omitted)] [--preview]`** - Fetch and address all PR review comments
- **`/utils:auto-approve-konflux-prs` `<target-repository>`** - Automate approving Konflux bot PRs for the given repository by adding /lgtm and /approve
- **`/utils:find-konflux-images` `<PR-URL>`** - Find and verify Konflux-built container images from a GitHub PR
- **`/utils:generate-test-plan` `[GitHub PR URLs]`** - Generate test steps for one or more related PRs
Expand Down
4 changes: 2 additions & 2 deletions docs/data.json
Original file line number Diff line number Diff line change
Expand Up @@ -817,7 +817,7 @@
{
"commands": [
{
"argument_hint": "[PR number (optional - uses current branch if omitted)]",
"argument_hint": "[PR number (optional - uses current branch if omitted)] [--preview]",
"description": "Fetch and address all PR review comments",
"name": "address-reviews",
"synopsis": ""
Expand Down Expand Up @@ -876,7 +876,7 @@
"hooks": [],
"name": "utils",
"skills": [],
"version": "0.0.7"
"version": "0.0.8"
},
{
"commands": [
Expand Down
2 changes: 1 addition & 1 deletion plugins/utils/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "utils",
"description": "A generic utilities plugin serving as a catch-all for various helper commands and agents",
"version": "0.0.7",
"version": "0.0.8",
"author": {
"name": "github.com/openshift-eng"
}
Expand Down
87 changes: 60 additions & 27 deletions plugins/utils/commands/address-reviews.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
---
description: Fetch and address all PR review comments
argument-hint: "[PR number (optional - uses current branch if omitted)]"
argument-hint: "[PR number (optional - uses current branch if omitted)] [--preview]"
---

## Name
utils:address-reviews

## Synopsis
/utils:address-reviews [PR number (optional - uses current branch if omitted)]
/utils:address-reviews [PR number (optional - uses current branch if omitted)] [--preview]

## Description
This command automates the process of addressing PR review comments by fetching all comments from a pull request, categorizing them by priority (blocking, change requests, questions, suggestions), and systematically addressing each one. It intelligently filters out outdated comments, bot-generated content, and oversized responses to optimize context usage. The command handles code changes, posts replies to reviewers, and maintains a clean git history by amending relevant commits rather than creating unnecessary new ones.
Expand Down Expand Up @@ -109,11 +109,25 @@ This command automates the process of addressing PR review comments by fetching

### Step 3: Address Comments

#### Interactive Preview (`--preview`)

When `--preview` is passed, preview each comment before acting:

1. Show the reviewer's comment
2. Show your proposed action: code change diff, explanation, or decline reasoning
3. Show the draft reply you plan to post
4. **Wait for user approval** before proceeding — the user can:
- **Approve** as-is
- **Edit** the proposed reply or approach
- **Skip** the comment entirely

This applies to all comment types below. Without `--preview`, act autonomously.

#### Grouped Comments

When multiple comments relate to the same concern/fix:
- Make the code change once
- Reply to EACH comment individually (don't copy-paste, tailor each reply)
- Track replies for EACH comment individually (posted in Step 4 — don't copy-paste, tailor each reply)
- Optional reference: `Done. (Also addresses feedback from @user)`

#### Code Change Requests
Expand All @@ -122,7 +136,7 @@ When multiple comments relate to the same concern/fix:

**b. If requested change is valid**:
- Plan and implement changes
- Commit and Push **(ALL sub-steps are MANDATORYdo not skip any)**
- Commit locally **(do NOT push yetall pushes are batched in Step 4)**
1. **Review changes**: `git diff`

2. **Sync with remote first**: `git pull --rebase origin <branch>` to ensure local branch is up to date. If the branch is behind or diverged, you MUST rebase before committing.
Expand All @@ -139,48 +153,66 @@ When multiple comments relate to the same concern/fix:
- **When unsure**: Amend (keep git history clean)
- **Multiple commits**: Use `git rebase -i origin/main` to amend the specific relevant commit

5. **Create commit AND push (both required)**:
5. **Create commit locally**:
- Follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) format
- Always include body explaining "why"
- **Amend**: `git commit --amend --no-edit && git push --force-with-lease` (or update message if scope changed)
- **New commit**: Standard commit with message, then `git push`
- **⚠️ A commit without a push is incomplete. You MUST push.**
- **Amend**: `git commit --amend --no-edit` (or update message if scope changed)
- **New commit**: Standard commit with message

6. **Verify push succeeded (MANDATORY before replying)**:
- Run `git log -1 --format='%H'` locally and `git ls-remote origin <branch>` to confirm the remote has your commit
- **If they differ**: The push failed or was never executed. Do NOT post a "Done" reply. Diagnose and retry, or report the failure to the user.
- **If uncommitted changes remain** (`git status`): The commit failed. Fix it first.
- **⚠️ NEVER post a "Done" or "Fixed" reply unless the push is verified on the remote.** Posting false claims of completion erodes reviewer trust and wastes human reviewers' time.

- **Concise Reply template**: `Done. [1-line what changed]. [Optional 1-line why]`
- Max 2 sentences + attribution footer
- Post reply:
```
gh api repos/{owner}/{repo}/pulls/<PR_NUMBER>/comments/<comment_id>/replies -f body="<reply>"
```
If fails: `gh pr comment <PR_NUMBER> --body="@<author> <reply>"`
- Track what was done for each comment (change description, comment ID, author) so replies can be posted in Step 4

**c. If declining change**:
- **Reply with technical explanation** (3-5 sentences):
- **Prepare technical explanation** (3-5 sentences):
- Why current implementation is correct
- Specific reasoning with file:line references
- Use same posting method as (b)
- Track for reply in Step 4

**d. If unsure**: Ask user for clarification

#### Clarification Requests

- Provide clear, detailed answer (2-4 sentences)
- Prepare clear, detailed answer (2-4 sentences)
- Include file:line references when applicable
- Post using same method as code changes
- Track for reply in Step 4

#### Informational Comments

- No action unless response is courteous

### Step 4: Post Replies and Push

After ALL comments from Step 3 are processed, post replies and push in this order:

#### 4a. Post all replies

For each comment addressed in Step 3, post the reply:

- **Concise Reply template**: `Done. [1-line what changed]. [Optional 1-line why]`
- Max 2 sentences + attribution footer
- Post reply:
```
gh api repos/{owner}/{repo}/pulls/<PR_NUMBER>/comments/<comment_id>/replies -f body="<reply>"
```
Comment on lines +193 to +195

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add a language tag to the fenced code block.

This triggers markdownlint MD040 (fenced-code-language).

📝 Proposed fix
-  ```
+  ```bash
   gh api repos/{owner}/{repo}/pulls/<PR_NUMBER>/comments/<comment_id>/replies -f body="<reply>"
</details>

<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion

🧰 Tools
🪛 markdownlint-cli2 (0.22.0)

[warning] 193-193: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/utils/commands/address-reviews.md` around lines 193 - 195, Add a
language tag to the fenced code block containing the GitHub CLI API command to
satisfy markdownlint MD040; locate the fenced block that wraps the command "gh
api repos/{owner}/{repo}/pulls/<PR_NUMBER>/comments/<comment_id>/replies -f
body=\"<reply>\"" and change the opening fence from ``` to ```bash so the block
is explicitly marked as bash.

If fails: `gh pr comment <PR_NUMBER> --body="@<author> <reply>"`

Comment on lines +194 to +197

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, locate and read the file
find . -name "address-reviews.md" -type f 2>/dev/null | head -5

Repository: openshift-eng/ai-helpers

Length of output: 111


🏁 Script executed:

# Read the file around lines 194-197 to see context
wc -l ./plugins/utils/commands/address-reviews.md

Repository: openshift-eng/ai-helpers

Length of output: 115


🏁 Script executed:

# Read lines around 194-197 with context
sed -n '185,210p' ./plugins/utils/commands/address-reviews.md | cat -n

Repository: openshift-eng/ai-helpers

Length of output: 999


🌐 Web query:

GitHub API pull request comments replies endpoint REST

💡 Result:

For pull request review comments (inline diff comments), GitHub’s REST API supports creating replies via:

  • Create a reply to a review comment: POST /repos/{owner}/{repo}/pulls/{pull_number}/comments/{comment_id}/replies (where comment_id must be a top-level review comment; replies-to-replies aren’t supported). [1]

There is no separate “list replies” REST endpoint; to fetch replies you typically:

  • List all review comments on the PR and then filter items where in_reply_to_id equals the top-level comment’s id. [1]

Source


🌐 Web query:

GitHub API pull request review types issue_comment review_comment feedback channels

💡 Result:

PR “feedback channels” in GitHub APIs (and how they map)

  1. PR conversation comments (a.k.a. “issue comments” on the PR)
  • Where they appear in UI: PR Conversation tab (not tied to a diff line).
  • REST API: Issue comments endpoints (/repos/{owner}/{repo}/issues/{issue_number}/comments)—because every pull request is also an issue. [1]
  • GitHub Actions trigger: issue_comment (for comments created/edited/deleted on the PR conversation). [2]
  1. Inline diff comments (a.k.a. “pull request review comments”)
  • Where they appear in UI: on specific files/lines in the Files changed diff (threaded as part of review discussions).
  • REST API: Pull request review comments endpoints (/repos/{owner}/{repo}/pulls/comments/...). [3]
  • GitHub Actions trigger: pull_request_review_comment. [2]
  1. A submitted review (the “review type”: approve / request changes / comment)
  • Where they appear in UI: the review “summary” (optionally with inline comments included).
  • REST API: Pull request reviews endpoint (POST /repos/{owner}/{repo}/pulls/{pull_number}/reviews) with event:
    • APPROVE, REQUEST_CHANGES, COMMENT
    • If event is omitted, the review is created as PENDING (a pending review you submit later). [4]
  • GitHub Actions trigger: pull_request_review with activity types submitted, edited, dismissed (and you can check github.event.review.state). [2]
  1. GraphQL naming (helpful when you see different words)
  • Mutating a review uses PullRequestReviewEvent: APPROVE, REQUEST_CHANGES, COMMENT, DISMISS. [5]
  • Stored review state is PullRequestReviewState: APPROVED, CHANGES_REQUESTED, COMMENTED, DISMISSED, PENDING. [5]

Sources

[1] (docs.github.com)
[2] (docs.github.com)
[3] (docs.github.com)
[4] (docs.github.com)
[5] (docs.github.com)

Citations:


🏁 Script executed:

# Search for the "one response mechanism" rule or similar constraints in the file
rg -i "one response|mechanism|feedback type|channel" ./plugins/utils/commands/address-reviews.md -A 2 -B 2

Repository: openshift-eng/ai-helpers

Length of output: 616


🏁 Script executed:

# Read Step 3 to understand what types of feedback are extracted
sed -n '1,193p' ./plugins/utils/commands/address-reviews.md | tail -60 | cat -n

Repository: openshift-eng/ai-helpers

Length of output: 2806


🏁 Script executed:

# Find Step 3 content
sed -n '1,150p' ./plugins/utils/commands/address-reviews.md | grep -n "Step 3" -A 40 | head -50

Repository: openshift-eng/ai-helpers

Length of output: 1793


🏁 Script executed:

# Read the full Step 3 section
sed -n '110,180p' ./plugins/utils/commands/address-reviews.md | cat -n

Repository: openshift-eng/ai-helpers

Length of output: 3376


🌐 Web query:

GitHub API comment type detection review comment issue comment determine type

💡 Result:

REST API (v3): distinguish by the resource type / fields

1) Pull request review comment (inline diff comment) = PullRequestReviewComment

  • Comes from PR review comment endpoints like GET /repos/{owner}/{repo}/pulls/comments / .../pulls/comments/{comment_id}.
  • Has review/diff-specific fields such as pull_request_review_id, diff_hunk, path, position, commit_id, and sometimes in_reply_to_id. [1]

2) Issue comment (works for both Issues and PR “conversation” comments) = IssueComment

  • Comes from issue comment endpoints like GET /repos/{owner}/{repo}/issues/comments or GET /repos/{owner}/{repo}/issues/{issue_number}/comments.
  • Has fields like issue_url and URLs shaped like /issues/comments/{id}. [2]

3) If it’s an IssueComment, how to tell “issue vs PR conversation”?

  • Fetch the parent issue via the comment’s issue_url, then check whether the returned issue JSON contains the pull_request key:
    • present ⇒ it’s a pull request (conversation comment)
    • absent ⇒ it’s a plain issue
      GitHub explicitly notes you can identify PRs from Issues endpoints by the pull_request key. [3]

GraphQL API (v4): use __typename

Query the node and branch by type:

query($id: ID!) {
  node(id: $id) {
    __typename
    ... on IssueComment { id url }
    ... on PullRequestReviewComment { id url path diffHunk }
  }
}

__typename will be IssueComment vs PullRequestReviewComment.


Sources

  1. GitHub REST: Pull request review comments (example response includes pull_request_review_id, diff_hunk, path, etc.). [1]
  2. GitHub REST: Issue comments (example response includes issue_url; used for issues and PR conversation comments). [2]
  3. GitHub REST: Issues endpoints note PRs can be identified by the pull_request key. [3]

[1] (docs.github.com)
[2] (docs.github.com)
[3] (docs.github.com)

Citations:


🌐 Web query:

GitHub GraphQL mutation review thread reply create

💡 Result:

To create a reply in an existing PR review thread with the GitHub GraphQL v4 API, use the mutation addPullRequestReviewThreadReply, which takes:

  • pullRequestReviewThreadId (required, the thread’s node ID)
  • body (required, reply text)
  • pullRequestReviewId (optional, if you want the reply to belong to a pending review) [1], [2]

1) Get the review thread node ID

Query the PR’s review threads and pick the right id:

query($owner:String!, $name:String!, $number:Int!) {
  repository(owner:$owner, name:$name) {
    pullRequest(number:$number) {
      reviewThreads(first: 50) {
        nodes {
          id
          isResolved
          comments(first: 20) {
            nodes { id body author { login } }
          }
        }
      }
    }
  }
}

(You’ll use the thread id as pullRequestReviewThreadId.) [1]

2) Reply to that thread

mutation($threadId:ID!, $body:String!) {
  addPullRequestReviewThreadReply(input:{
    pullRequestReviewThreadId: $threadId,
    body: $body
  }) {
    comment { id url body }
  }
}

This creates a new reply comment in that thread. [1], [2]


🌐 Web query:

GitHub REST API PullRequestReviewComment IssueComment type name terminology

💡 Result:

In GitHub REST API, the “type names” like PullRequestReviewComment and IssueComment don’t exist as formal REST resource names—they’re GraphQL object type names. In REST, GitHub uses endpoint terminology instead:

  • Issue (and PR “Conversation”) comments
    REST name/area: Issue comments — “comments on issues and pull requests” (because every pull request is an issue).
    Typical REST endpoints: /repos/{owner}/{repo}/issues/{issue_number}/comments, /repos/{owner}/{repo}/issues/comments/{comment_id}. [1]

  • Inline code review comments on diffs (“Files changed”)
    REST name/area: Pull request review comments.
    Typical REST endpoints: /repos/{owner}/{repo}/pulls/{pull_number}/comments, /repos/{owner}/{repo}/pulls/comments/{comment_id}. [2]

In GraphQL, the corresponding object types are explicitly named:

  • IssueComment = comment on an Issue (and PR conversation, since PRs are Issues). [1][3]
  • PullRequestReviewComment = inline review comment on a PR diff. [2][3]

Rule of thumb: if the comment is on the PR Conversation timeline, it’s an issue comment; if it’s anchored to a file/line in the diff, it’s a pull request review comment. [1][2]

Sources: [1] https://docs.github.com/en/rest/issues/comments, [2] https://docs.github.com/en/rest/pulls/comments, [3] https://docs.github.com/en/graphql/reference/objects


Update Step 4a to route replies by feedback type instead of using a single endpoint with fallback.

The current implementation tries posting all replies via the inline review comment endpoint and falls back to a general PR comment on failure. This violates the documented "One response per feedback" rule which explicitly states: "NEVER respond to the same feedback via both mechanisms."

Replace the single endpoint with type-specific routing:

  • Inline diff comments (review_comment): gh api repos/{owner}/{repo}/pulls/<PR_NUMBER>/comments/<comment_id>/replies -f body="<reply>"
  • PR conversation comments (issue_comment): gh pr comment <PR_NUMBER> --body="@<author> <reply>"
  • Review threads (if tracked): use the GraphQL addPullRequestReviewThreadReply mutation

For each type, if posting fails, report the failure and continue—do not silently switch to another channel.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/utils/commands/address-reviews.md` around lines 194 - 197, Update
Step 4a to route replies by feedback type instead of attempting a single inline
endpoint with fallback: for inline diff comments use the review_comment endpoint
(gh api repos/{owner}/{repo}/pulls/<PR_NUMBER>/comments/<comment_id>/replies -f
body="<reply>"), for PR conversation comments use the issue_comment flow (gh pr
comment <PR_NUMBER> --body="@<author> <reply>"), and for review threads (if
tracked) call the GraphQL addPullRequestReviewThreadReply mutation; ensure the
document and any implementation references to "Step 4a" explicitly map each
feedback type to its specific endpoint and state that failures must be reported
and ignored (do not fallback to another channel).

**All replies must include**: `---\n*AI-assisted response via Claude Code*`

### Step 4: Summary
#### 4b. Push once

After all replies are posted, push all committed changes in a single push:

```bash
git push --force-with-lease
```

#### 4c. Verify push

- Run `git log -1 --format='%H'` locally and `git ls-remote origin <branch>` to confirm the remote has your commit
- **If they differ**: The push failed or was never executed. Diagnose and retry.
- **If uncommitted changes remain** (`git status`): The commit failed. Fix it before pushing.
- **If push cannot be verified**: Report the failure to the user. Do not silently proceed — replies have already been posted claiming changes were made.

### Step 5: Summary

Show user:
- Total comments found (raw count from API)
Expand Down Expand Up @@ -229,4 +261,5 @@ Where `<type>` is one of: `issue_comment`, `review_thread`, or `review_comment`


## Arguments:
- $1: [PR number to address reviews (optional - uses current branch if omitted)]
- $1: [PR number (optional - uses current branch if omitted)]
- --preview: Preview each comment's proposed action and reply before proceeding