summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAiden Cline <[email protected]>2025-11-25 23:51:46 -0600
committerAiden Cline <[email protected]>2025-11-25 23:51:46 -0600
commit2fbd462e6e35263274e975a2084324b8721f886f (patch)
treec1d19ec2b61e58e88807e36c6f7fbe9d655756c3
parente1cc98d448f8db971fc2fbed6055839b2f233d2f (diff)
downloadopencode-2fbd462e6e35263274e975a2084324b8721f886f.tar.gz
opencode-2fbd462e6e35263274e975a2084324b8721f886f.zip
Reapply "feat(github): add ability to react to PR Review Comments in Workflow (#4705)"
This reverts commit e1cc98d448f8db971fc2fbed6055839b2f233d2f.
-rw-r--r--.github/workflows/opencode.yml2
-rw-r--r--github/README.md26
-rw-r--r--github/index.ts51
-rw-r--r--packages/opencode/src/cli/cmd/github.ts46
-rw-r--r--packages/web/src/content/docs/github.mdx19
5 files changed, 131 insertions, 13 deletions
diff --git a/.github/workflows/opencode.yml b/.github/workflows/opencode.yml
index 75533df70..f7d7f6081 100644
--- a/.github/workflows/opencode.yml
+++ b/.github/workflows/opencode.yml
@@ -3,6 +3,8 @@ name: opencode
on:
issue_comment:
types: [created]
+ pull_request_review_comment:
+ types: [created]
jobs:
opencode:
diff --git a/github/README.md b/github/README.md
index 8e5b6d813..36342b409 100644
--- a/github/README.md
+++ b/github/README.md
@@ -30,6 +30,24 @@ Leave the following comment on a GitHub PR. opencode will implement the requeste
Delete the attachment from S3 when the note is removed /oc
```
+#### Review specific code lines
+
+Leave a comment directly on code lines in the PR's "Files" tab. opencode will automatically detect the file, line numbers, and diff context to provide precise responses.
+
+```
+[Comment on specific lines in Files tab]
+/oc add error handling here
+```
+
+When commenting on specific lines, opencode receives:
+
+- The exact file being reviewed
+- The specific lines of code
+- The surrounding diff context
+- Line number information
+
+This allows for more targeted requests without needing to specify file paths or line numbers manually.
+
## Installation
Run the following command in the terminal from your GitHub repo:
@@ -51,6 +69,8 @@ This will walk you through installing the GitHub app, creating the workflow, and
on:
issue_comment:
types: [created]
+ pull_request_review_comment:
+ types: [created]
jobs:
opencode:
@@ -135,3 +155,9 @@ Replace the image URL `https://github.com/user-attachments/assets/xxxxxxxx` with
```
MOCK_EVENT='{"eventName":"issue_comment","repo":{"owner":"sst","repo":"hello-world"},"actor":"fwang","payload":{"issue":{"number":4,"pull_request":{}},"comment":{"id":1,"body":"hey opencode, summarize thread"}}}'
```
+
+### PR review comment event
+
+```
+MOCK_EVENT='{"eventName":"pull_request_review_comment","repo":{"owner":"sst","repo":"hello-world"},"actor":"fwang","payload":{"pull_request":{"number":7},"comment":{"id":1,"body":"hey opencode, add error handling","path":"src/components/Button.tsx","diff_hunk":"@@ -45,8 +45,11 @@\n- const handleClick = () => {\n- console.log('clicked')\n+ const handleClick = useCallback(() => {\n+ console.log('clicked')\n+ doSomething()\n+ }, [doSomething])","line":47,"original_line":45,"position":10,"commit_id":"abc123","original_commit_id":"def456"}}}'
+```
diff --git a/github/index.ts b/github/index.ts
index b681ff92f..6d826326e 100644
--- a/github/index.ts
+++ b/github/index.ts
@@ -5,7 +5,7 @@ import { graphql } from "@octokit/graphql"
import * as core from "@actions/core"
import * as github from "@actions/github"
import type { Context as GitHubContext } from "@actions/github/lib/context"
-import type { IssueCommentEvent } from "@octokit/webhooks-types"
+import type { IssueCommentEvent, PullRequestReviewCommentEvent } from "@octokit/webhooks-types"
import { createOpencodeClient } from "@opencode-ai/sdk"
import { spawn } from "node:child_process"
@@ -124,7 +124,7 @@ let exitCode = 0
type PromptFiles = Awaited<ReturnType<typeof getUserPrompt>>["promptFiles"]
try {
- assertContextEvent("issue_comment")
+ assertContextEvent("issue_comment", "pull_request_review_comment")
assertPayloadKeyword()
await assertOpencodeConnected()
@@ -241,19 +241,43 @@ function createOpencode() {
}
function assertPayloadKeyword() {
- const payload = useContext().payload as IssueCommentEvent
+ const payload = useContext().payload as IssueCommentEvent | PullRequestReviewCommentEvent
const body = payload.comment.body.trim()
if (!body.match(/(?:^|\s)(?:\/opencode|\/oc)(?=$|\s)/)) {
throw new Error("Comments must mention `/opencode` or `/oc`")
}
}
+function getReviewCommentContext() {
+ const context = useContext()
+ if (context.eventName !== "pull_request_review_comment") {
+ return null
+ }
+
+ const payload = context.payload as PullRequestReviewCommentEvent
+ return {
+ file: payload.comment.path,
+ diffHunk: payload.comment.diff_hunk,
+ line: payload.comment.line,
+ originalLine: payload.comment.original_line,
+ position: payload.comment.position,
+ commitId: payload.comment.commit_id,
+ originalCommitId: payload.comment.original_commit_id,
+ }
+}
+
async function assertOpencodeConnected() {
let retry = 0
let connected = false
do {
try {
- await client.app.get<true>()
+ await client.app.log<true>({
+ body: {
+ service: "github-workflow",
+ level: "info",
+ message: "Prepare to react to Github Workflow event",
+ },
+ })
connected = true
break
} catch (e) {}
@@ -383,11 +407,24 @@ async function createComment() {
}
async function getUserPrompt() {
+ const context = useContext()
+ const payload = context.payload as IssueCommentEvent | PullRequestReviewCommentEvent
+ const reviewContext = getReviewCommentContext()
+
let prompt = (() => {
- const payload = useContext().payload as IssueCommentEvent
const body = payload.comment.body.trim()
- if (body === "/opencode" || body === "/oc") return "Summarize this thread"
- if (body.includes("/opencode") || body.includes("/oc")) return body
+ if (body === "/opencode" || body === "/oc") {
+ if (reviewContext) {
+ return `Review this code change and suggest improvements for the commented lines:\n\nFile: ${reviewContext.file}\nLines: ${reviewContext.line}\n\n${reviewContext.diffHunk}`
+ }
+ return "Summarize this thread"
+ }
+ if (body.includes("/opencode") || body.includes("/oc")) {
+ if (reviewContext) {
+ return `${body}\n\nContext: You are reviewing a comment on file "${reviewContext.file}" at line ${reviewContext.line}.\n\nDiff context:\n${reviewContext.diffHunk}`
+ }
+ return body
+ }
throw new Error("Comments must mention `/opencode` or `/oc`")
})()
diff --git a/packages/opencode/src/cli/cmd/github.ts b/packages/opencode/src/cli/cmd/github.ts
index 1f60e81e9..5f9bcded1 100644
--- a/packages/opencode/src/cli/cmd/github.ts
+++ b/packages/opencode/src/cli/cmd/github.ts
@@ -7,7 +7,7 @@ import { graphql } from "@octokit/graphql"
import * as core from "@actions/core"
import * as github from "@actions/github"
import type { Context } from "@actions/github/lib/context"
-import type { IssueCommentEvent } from "@octokit/webhooks-types"
+import type { IssueCommentEvent, PullRequestReviewCommentEvent } from "@octokit/webhooks-types"
import { UI } from "../ui"
import { cmd } from "./cmd"
import { ModelsDev } from "../../provider/models"
@@ -328,6 +328,8 @@ export const GithubInstallCommand = cmd({
on:
issue_comment:
types: [created]
+ pull_request_review_comment:
+ types: [created]
jobs:
opencode:
@@ -378,7 +380,7 @@ export const GithubRunCommand = cmd({
const isMock = args.token || args.event
const context = isMock ? (JSON.parse(args.event!) as Context) : github.context
- if (context.eventName !== "issue_comment") {
+ if (context.eventName !== "issue_comment" && context.eventName !== "pull_request_review_comment") {
core.setFailed(`Unsupported event type: ${context.eventName}`)
process.exit(1)
}
@@ -387,9 +389,13 @@ export const GithubRunCommand = cmd({
const runId = normalizeRunId()
const share = normalizeShare()
const { owner, repo } = context.repo
- const payload = context.payload as IssueCommentEvent
+ const payload = context.payload as IssueCommentEvent | PullRequestReviewCommentEvent
const actor = context.actor
- const issueId = payload.issue.number
+
+ const issueId =
+ context.eventName === "pull_request_review_comment"
+ ? (payload as PullRequestReviewCommentEvent).pull_request.number
+ : (payload as IssueCommentEvent).issue.number
const runUrl = `/${owner}/${repo}/actions/runs/${runId}`
const shareBaseUrl = isMock ? "https://dev.opencode.ai" : "https://opencode.ai"
@@ -531,11 +537,39 @@ export const GithubRunCommand = cmd({
throw new Error(`Invalid share value: ${value}. Share must be a boolean.`)
}
+ function getReviewCommentContext() {
+ if (context.eventName !== "pull_request_review_comment") {
+ return null
+ }
+
+ const reviewPayload = payload as PullRequestReviewCommentEvent
+ return {
+ file: reviewPayload.comment.path,
+ diffHunk: reviewPayload.comment.diff_hunk,
+ line: reviewPayload.comment.line,
+ originalLine: reviewPayload.comment.original_line,
+ position: reviewPayload.comment.position,
+ commitId: reviewPayload.comment.commit_id,
+ originalCommitId: reviewPayload.comment.original_commit_id,
+ }
+ }
+
async function getUserPrompt() {
+ const reviewContext = getReviewCommentContext()
let prompt = (() => {
const body = payload.comment.body.trim()
- if (body === "/opencode" || body === "/oc") return "Summarize this thread"
- if (body.includes("/opencode") || body.includes("/oc")) return body
+ if (body === "/opencode" || body === "/oc") {
+ if (reviewContext) {
+ return `Review this code change and suggest improvements for the commented lines:\n\nFile: ${reviewContext.file}\nLines: ${reviewContext.line}\n\n${reviewContext.diffHunk}`
+ }
+ return "Summarize this thread"
+ }
+ if (body.includes("/opencode") || body.includes("/oc")) {
+ if (reviewContext) {
+ return `${body}\n\nContext: You are reviewing a comment on file "${reviewContext.file}" at line ${reviewContext.line}.\n\nDiff context:\n${reviewContext.diffHunk}`
+ }
+ return body
+ }
throw new Error("Comments must mention `/opencode` or `/oc`")
})()
diff --git a/packages/web/src/content/docs/github.mdx b/packages/web/src/content/docs/github.mdx
index 359f696fc..19c7782ef 100644
--- a/packages/web/src/content/docs/github.mdx
+++ b/packages/web/src/content/docs/github.mdx
@@ -45,6 +45,8 @@ Or you can set it up manually.
on:
issue_comment:
types: [created]
+ pull_request_review_comment:
+ types: [created]
jobs:
opencode:
@@ -129,3 +131,20 @@ Here are some examples of how you can use opencode in GitHub.
```
opencode will implement the requested change and commit it to the same PR.
+
+- **Review specific code lines**
+
+ Leave a comment directly on code lines in the PR's "Files" tab. opencode automatically detects the file, line numbers, and diff context to provide precise responses.
+
+ ```
+ [Comment on specific lines in Files tab]
+ /oc add error handling here
+ ```
+
+ When commenting on specific lines, opencode receives:
+ - The exact file being reviewed
+ - The specific lines of code
+ - The surrounding diff context
+ - Line number information
+
+ This allows for more targeted requests without needing to specify file paths or line numbers manually.