Skip to content
This repository was archived by the owner on Feb 19, 2026. It is now read-only.

Commit b934c22

Browse files
committed
ci: add duplicate PR detection bot
1 parent 72cef0d commit b934c22

File tree

5 files changed

+139
-0
lines changed

5 files changed

+139
-0
lines changed
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
name: Duplicate PR Check
2+
3+
on:
4+
pull_request:
5+
types: [opened]
6+
7+
jobs:
8+
check-duplicates:
9+
if: |
10+
github.event.pull_request.user.login != 'actions-user' &&
11+
github.event.pull_request.user.login != 'opencode' &&
12+
github.event.pull_request.user.login != 'rekram1-node' &&
13+
github.event.pull_request.user.login != 'thdxr' &&
14+
github.event.pull_request.user.login != 'kommander' &&
15+
github.event.pull_request.user.login != 'jayair' &&
16+
github.event.pull_request.user.login != 'fwang' &&
17+
github.event.pull_request.user.login != 'adamdotdevin' &&
18+
github.event.pull_request.user.login != 'iamdavidhill' &&
19+
github.event.pull_request.user.login != 'opencode-agent[bot]'
20+
runs-on: blacksmith-4vcpu-ubuntu-2404
21+
permissions:
22+
contents: read
23+
pull-requests: write
24+
steps:
25+
- name: Checkout repository
26+
uses: actions/checkout@v4
27+
with:
28+
fetch-depth: 1
29+
30+
- name: Setup Bun
31+
uses: ./.github/actions/setup-bun
32+
33+
- name: Install opencode
34+
run: curl -fsSL https://opencode.ai/install | bash
35+
36+
- name: Check for duplicate PRs
37+
env:
38+
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
39+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
40+
PR_NUMBER: ${{ github.event.pull_request.number }}
41+
PR_TITLE: ${{ github.event.pull_request.title }}
42+
PR_BODY: ${{ github.event.pull_request.body }}
43+
run: |
44+
COMMENT=$(opencode run --agent duplicate-pr --print "Check for duplicate PRs related to this new PR:
45+
46+
Title: $PR_TITLE
47+
48+
Description: $PR_BODY")
49+
50+
gh pr comment "$PR_NUMBER" --body "_The following comment was made by an LLM, it may be inaccurate:_
51+
52+
$COMMENT"

.opencode/agent/duplicate-pr.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
mode: primary
3+
hidden: true
4+
model: opencode/claude-haiku-4-5
5+
color: "#E67E22"
6+
tools:
7+
"*": false
8+
"github-pr-search": true
9+
---
10+
11+
You are a duplicate PR detection agent. When a PR is opened, your job is to search for potentially duplicate or related open PRs.
12+
13+
Use the github-pr-search tool to search for PRs that might be addressing the same issue or feature.
14+
15+
Search using keywords from the PR title and description. Try multiple searches with different relevant terms.
16+
17+
If you find potential duplicates:
18+
19+
- List them with their titles and URLs
20+
- Briefly explain why they might be related
21+
22+
If no duplicates are found, say so clearly.
23+
24+
Keep your response concise and actionable.

.opencode/opencode.jsonc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,6 @@
2323
},
2424
"tools": {
2525
"github-triage": false,
26+
"github-pr-search": false,
2627
},
2728
}

.opencode/tool/github-pr-search.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/// <reference path="../env.d.ts" />
2+
import { tool } from "@opencode-ai/plugin"
3+
import DESCRIPTION from "./github-pr-search.txt"
4+
5+
async function githubFetch(endpoint: string, options: RequestInit = {}) {
6+
const response = await fetch(`https://api.github.com${endpoint}`, {
7+
...options,
8+
headers: {
9+
Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
10+
Accept: "application/vnd.github+json",
11+
"Content-Type": "application/json",
12+
...options.headers,
13+
},
14+
})
15+
if (!response.ok) {
16+
throw new Error(`GitHub API error: ${response.status} ${response.statusText}`)
17+
}
18+
return response.json()
19+
}
20+
21+
interface PR {
22+
title: string
23+
html_url: string
24+
}
25+
26+
export default tool({
27+
description: DESCRIPTION,
28+
args: {
29+
query: tool.schema.string().describe("Search query for PR titles and descriptions"),
30+
limit: tool.schema.number().describe("Maximum number of results to return").default(10),
31+
offset: tool.schema.number().describe("Number of results to skip for pagination").default(0),
32+
},
33+
async execute(args) {
34+
const owner = "sst"
35+
const repo = "opencode"
36+
37+
const page = Math.floor(args.offset / args.limit) + 1
38+
const searchQuery = encodeURIComponent(`${args.query} repo:${owner}/${repo} type:pr state:open`)
39+
const result = await githubFetch(
40+
`/search/issues?q=${searchQuery}&per_page=${args.limit}&page=${page}&sort=updated&order=desc`,
41+
)
42+
43+
if (result.total_count === 0) {
44+
return `No PRs found matching "${args.query}"`
45+
}
46+
47+
const prs = result.items as PR[]
48+
const formatted = prs.map((pr) => `${pr.title}\n${pr.html_url}`).join("\n\n")
49+
50+
return `Found ${result.total_count} PRs (showing ${prs.length}):\n\n${formatted}`
51+
},
52+
})
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
Use this tool to search GitHub pull requests by title and description.
2+
3+
This tool searches PRs in the sst/opencode repository and returns LLM-friendly results including:
4+
- PR number and title
5+
- Author
6+
- State (open/closed/merged)
7+
- Labels
8+
- Description snippet
9+
10+
Use the query parameter to search for keywords that might appear in PR titles or descriptions.

0 commit comments

Comments
 (0)