| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent e1cc98d commit 2fbd462
5 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3,6 +3,8 @@ name: opencode | |||
| 3 | 3 | on: | |
| 4 | 4 | issue_comment: | |
| 5 | 5 | types: [created] | |
| 6 | + pull_request_review_comment: | ||
| 7 | + types: [created] | ||
| 6 | 8 | ||
| 7 | 9 | jobs: | |
| 8 | 10 | opencode: | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -30,6 +30,24 @@ Leave the following comment on a GitHub PR. opencode will implement the requeste | |||
| 30 | 30 | Delete the attachment from S3 when the note is removed /oc | |
| 31 | 31 | ``` | |
| 32 | 32 | ||
| 33 | + #### Review specific code lines | ||
| 34 | + | ||
| 35 | + 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. | ||
| 36 | + | ||
| 37 | + ``` | ||
| 38 | + [Comment on specific lines in Files tab] | ||
| 39 | + /oc add error handling here | ||
| 40 | + ``` | ||
| 41 | + | ||
| 42 | + When commenting on specific lines, opencode receives: | ||
| 43 | + | ||
| 44 | + - The exact file being reviewed | ||
| 45 | + - The specific lines of code | ||
| 46 | + - The surrounding diff context | ||
| 47 | + - Line number information | ||
| 48 | + | ||
| 49 | + This allows for more targeted requests without needing to specify file paths or line numbers manually. | ||
| 50 | + | ||
| 33 | 51 | ## Installation | |
| 34 | 52 | ||
| 35 | 53 | 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 | |||
| 51 | 69 | on: | |
| 52 | 70 | issue_comment: | |
| 53 | 71 | types: [created] | |
| 72 | + pull_request_review_comment: | ||
| 73 | + types: [created] | ||
| 54 | 74 | ||
| 55 | 75 | jobs: | |
| 56 | 76 | opencode: | |
@@ -135,3 +155,9 @@ Replace the image URL `https://github.com/user-attachments/assets/xxxxxxxx` with | |||
| 135 | 155 | ``` | |
| 136 | 156 | 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"}}}' | |
| 137 | 157 | ``` | |
| 158 | + | ||
| 159 | + ### PR review comment event | ||
| 160 | + | ||
| 161 | + ``` | ||
| 162 | + 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"}}}' | ||
| 163 | + ``` | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -5,7 +5,7 @@ import { graphql } from "@octokit/graphql" | |||
| 5 | 5 | import * as core from "@actions/core" | |
| 6 | 6 | import * as github from "@actions/github" | |
| 7 | 7 | import type { Context as GitHubContext } from "@actions/github/lib/context" | |
| 8 | - import type { IssueCommentEvent } from "@octokit/webhooks-types" | ||
| 8 | + import type { IssueCommentEvent, PullRequestReviewCommentEvent } from "@octokit/webhooks-types" | ||
| 9 | 9 | import { createOpencodeClient } from "@opencode-ai/sdk" | |
| 10 | 10 | import { spawn } from "node:child_process" | |
| 11 | 11 | ||
@@ -124,7 +124,7 @@ let exitCode = 0 | |||
| 124 | 124 | type PromptFiles = Awaited<ReturnType<typeof getUserPrompt>>["promptFiles"] | |
| 125 | 125 | ||
| 126 | 126 | try { | |
| 127 | - assertContextEvent("issue_comment") | ||
| 127 | + assertContextEvent("issue_comment", "pull_request_review_comment") | ||
| 128 | 128 | assertPayloadKeyword() | |
| 129 | 129 | await assertOpencodeConnected() | |
| 130 | 130 | ||
@@ -241,19 +241,43 @@ function createOpencode() { | |||
| 241 | 241 | } | |
| 242 | 242 | ||
| 243 | 243 | function assertPayloadKeyword() { | |
| 244 | - const payload = useContext().payload as IssueCommentEvent | ||
| 244 | + const payload = useContext().payload as IssueCommentEvent | PullRequestReviewCommentEvent | ||
| 245 | 245 | const body = payload.comment.body.trim() | |
| 246 | 246 | if (!body.match(/(?:^|\s)(?:\/opencode|\/oc)(?=$|\s)/)) { | |
| 247 | 247 | throw new Error("Comments must mention `/opencode` or `/oc`") | |
| 248 | 248 | } | |
| 249 | 249 | } | |
| 250 | 250 | ||
| 251 | + function getReviewCommentContext() { | ||
| 252 | + const context = useContext() | ||
| 253 | + if (context.eventName !== "pull_request_review_comment") { | ||
| 254 | + return null | ||
| 255 | + } | ||
| 256 | + | ||
| 257 | + const payload = context.payload as PullRequestReviewCommentEvent | ||
| 258 | + return { | ||
| 259 | + file: payload.comment.path, | ||
| 260 | + diffHunk: payload.comment.diff_hunk, | ||
| 261 | + line: payload.comment.line, | ||
| 262 | + originalLine: payload.comment.original_line, | ||
| 263 | + position: payload.comment.position, | ||
| 264 | + commitId: payload.comment.commit_id, | ||
| 265 | + originalCommitId: payload.comment.original_commit_id, | ||
| 266 | + } | ||
| 267 | + } | ||
| 268 | + | ||
| 251 | 269 | async function assertOpencodeConnected() { | |
| 252 | 270 | let retry = 0 | |
| 253 | 271 | let connected = false | |
| 254 | 272 | do { | |
| 255 | 273 | try { | |
| 256 | - await client.app.get<true>() | ||
| 274 | + await client.app.log<true>({ | ||
| 275 | + body: { | ||
| 276 | + service: "github-workflow", | ||
| 277 | + level: "info", | ||
| 278 | + message: "Prepare to react to Github Workflow event", | ||
| 279 | + }, | ||
| 280 | + }) | ||
| 257 | 281 | connected = true | |
| 258 | 282 | break | |
| 259 | 283 | } catch (e) {} | |
@@ -383,11 +407,24 @@ async function createComment() { | |||
| 383 | 407 | } | |
| 384 | 408 | ||
| 385 | 409 | async function getUserPrompt() { | |
| 410 | + const context = useContext() | ||
| 411 | + const payload = context.payload as IssueCommentEvent | PullRequestReviewCommentEvent | ||
| 412 | + const reviewContext = getReviewCommentContext() | ||
| 413 | + | ||
| 386 | 414 | let prompt = (() => { | |
| 387 | - const payload = useContext().payload as IssueCommentEvent | ||
| 388 | 415 | const body = payload.comment.body.trim() | |
| 389 | - if (body === "/opencode" || body === "/oc") return "Summarize this thread" | ||
| 390 | - if (body.includes("/opencode") || body.includes("/oc")) return body | ||
| 416 | + if (body === "/opencode" || body === "/oc") { | ||
| 417 | + if (reviewContext) { | ||
| 418 | + return `Review this code change and suggest improvements for the commented lines:\n\nFile: ${reviewContext.file}\nLines: ${reviewContext.line}\n\n${reviewContext.diffHunk}` | ||
| 419 | + } | ||
| 420 | + return "Summarize this thread" | ||
| 421 | + } | ||
| 422 | + if (body.includes("/opencode") || body.includes("/oc")) { | ||
| 423 | + if (reviewContext) { | ||
| 424 | + return `${body}\n\nContext: You are reviewing a comment on file "${reviewContext.file}" at line ${reviewContext.line}.\n\nDiff context:\n${reviewContext.diffHunk}` | ||
| 425 | + } | ||
| 426 | + return body | ||
| 427 | + } | ||
| 391 | 428 | throw new Error("Comments must mention `/opencode` or `/oc`") | |
| 392 | 429 | })() | |
| 393 | 430 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -7,7 +7,7 @@ import { graphql } from "@octokit/graphql" | |||
| 7 | 7 | import * as core from "@actions/core" | |
| 8 | 8 | import * as github from "@actions/github" | |
| 9 | 9 | import type { Context } from "@actions/github/lib/context" | |
| 10 | - import type { IssueCommentEvent } from "@octokit/webhooks-types" | ||
| 10 | + import type { IssueCommentEvent, PullRequestReviewCommentEvent } from "@octokit/webhooks-types" | ||
| 11 | 11 | import { UI } from "../ui" | |
| 12 | 12 | import { cmd } from "./cmd" | |
| 13 | 13 | import { ModelsDev } from "../../provider/models" | |
@@ -328,6 +328,8 @@ export const GithubInstallCommand = cmd({ | |||
| 328 | 328 | on: | |
| 329 | 329 | issue_comment: | |
| 330 | 330 | types: [created] | |
| 331 | + pull_request_review_comment: | ||
| 332 | + types: [created] | ||
| 331 | 333 | ||
| 332 | 334 | jobs: | |
| 333 | 335 | opencode: | |
@@ -378,7 +380,7 @@ export const GithubRunCommand = cmd({ | |||
| 378 | 380 | const isMock = args.token || args.event | |
| 379 | 381 | ||
| 380 | 382 | const context = isMock ? (JSON.parse(args.event!) as Context) : github.context | |
| 381 | - if (context.eventName !== "issue_comment") { | ||
| 383 | + if (context.eventName !== "issue_comment" && context.eventName !== "pull_request_review_comment") { | ||
| 382 | 384 | core.setFailed(`Unsupported event type: ${context.eventName}`) | |
| 383 | 385 | process.exit(1) | |
| 384 | 386 | } | |
@@ -387,9 +389,13 @@ export const GithubRunCommand = cmd({ | |||
| 387 | 389 | const runId = normalizeRunId() | |
| 388 | 390 | const share = normalizeShare() | |
| 389 | 391 | const { owner, repo } = context.repo | |
| 390 | - const payload = context.payload as IssueCommentEvent | ||
| 392 | + const payload = context.payload as IssueCommentEvent | PullRequestReviewCommentEvent | ||
| 391 | 393 | const actor = context.actor | |
| 392 | - const issueId = payload.issue.number | ||
| 394 | + | ||
| 395 | + const issueId = | ||
| 396 | + context.eventName === "pull_request_review_comment" | ||
| 397 | + ? (payload as PullRequestReviewCommentEvent).pull_request.number | ||
| 398 | + : (payload as IssueCommentEvent).issue.number | ||
| 393 | 399 | const runUrl = `/${owner}/${repo}/actions/runs/${runId}` | |
| 394 | 400 | const shareBaseUrl = isMock ? "https://dev.opencode.ai" : "https://opencode.ai" | |
| 395 | 401 | ||
@@ -531,11 +537,39 @@ export const GithubRunCommand = cmd({ | |||
| 531 | 537 | throw new Error(`Invalid share value: ${value}. Share must be a boolean.`) | |
| 532 | 538 | } | |
| 533 | 539 | ||
| 540 | + function getReviewCommentContext() { | ||
| 541 | + if (context.eventName !== "pull_request_review_comment") { | ||
| 542 | + return null | ||
| 543 | + } | ||
| 544 | + | ||
| 545 | + const reviewPayload = payload as PullRequestReviewCommentEvent | ||
| 546 | + return { | ||
| 547 | + file: reviewPayload.comment.path, | ||
| 548 | + diffHunk: reviewPayload.comment.diff_hunk, | ||
| 549 | + line: reviewPayload.comment.line, | ||
| 550 | + originalLine: reviewPayload.comment.original_line, | ||
| 551 | + position: reviewPayload.comment.position, | ||
| 552 | + commitId: reviewPayload.comment.commit_id, | ||
| 553 | + originalCommitId: reviewPayload.comment.original_commit_id, | ||
| 554 | + } | ||
| 555 | + } | ||
| 556 | + | ||
| 534 | 557 | async function getUserPrompt() { | |
| 558 | + const reviewContext = getReviewCommentContext() | ||
| 535 | 559 | let prompt = (() => { | |
| 536 | 560 | const body = payload.comment.body.trim() | |
| 537 | - if (body === "/opencode" || body === "/oc") return "Summarize this thread" | ||
| 538 | - if (body.includes("/opencode") || body.includes("/oc")) return body | ||
| 561 | + if (body === "/opencode" || body === "/oc") { | ||
| 562 | + if (reviewContext) { | ||
| 563 | + return `Review this code change and suggest improvements for the commented lines:\n\nFile: ${reviewContext.file}\nLines: ${reviewContext.line}\n\n${reviewContext.diffHunk}` | ||
| 564 | + } | ||
| 565 | + return "Summarize this thread" | ||
| 566 | + } | ||
| 567 | + if (body.includes("/opencode") || body.includes("/oc")) { | ||
| 568 | + if (reviewContext) { | ||
| 569 | + return `${body}\n\nContext: You are reviewing a comment on file "${reviewContext.file}" at line ${reviewContext.line}.\n\nDiff context:\n${reviewContext.diffHunk}` | ||
| 570 | + } | ||
| 571 | + return body | ||
| 572 | + } | ||
| 539 | 573 | throw new Error("Comments must mention `/opencode` or `/oc`") | |
| 540 | 574 | })() | |
| 541 | 575 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -45,6 +45,8 @@ Or you can set it up manually. | |||
| 45 | 45 | on: | |
| 46 | 46 | issue_comment: | |
| 47 | 47 | types: [created] | |
| 48 | + pull_request_review_comment: | ||
| 49 | + types: [created] | ||
| 48 | 50 | ||
| 49 | 51 | jobs: | |
| 50 | 52 | opencode: | |
@@ -129,3 +131,20 @@ Here are some examples of how you can use opencode in GitHub. | |||
| 129 | 131 | ``` | |
| 130 | 132 | ||
| 131 | 133 | opencode will implement the requested change and commit it to the same PR. | |
| 134 | + | ||
| 135 | + - **Review specific code lines** | ||
| 136 | + | ||
| 137 | + 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. | ||
| 138 | + | ||
| 139 | + ``` | ||
| 140 | + [Comment on specific lines in Files tab] | ||
| 141 | + /oc add error handling here | ||
| 142 | + ``` | ||
| 143 | + | ||
| 144 | + When commenting on specific lines, opencode receives: | ||
| 145 | + - The exact file being reviewed | ||
| 146 | + - The specific lines of code | ||
| 147 | + - The surrounding diff context | ||
| 148 | + - Line number information | ||
| 149 | + | ||
| 150 | + This allows for more targeted requests without needing to specify file paths or line numbers manually. | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments