فهرست منبع

ci: add duplicate PR detection bot

Aiden Cline 1 ماه پیش
والد
کامیت
b934c22d8d

+ 52 - 0
.github/workflows/duplicate-prs.yml

@@ -0,0 +1,52 @@
+name: Duplicate PR Check
+
+on:
+  pull_request:
+    types: [opened]
+
+jobs:
+  check-duplicates:
+    if: |
+      github.event.pull_request.user.login != 'actions-user' &&
+      github.event.pull_request.user.login != 'opencode' &&
+      github.event.pull_request.user.login != 'rekram1-node' &&
+      github.event.pull_request.user.login != 'thdxr' &&
+      github.event.pull_request.user.login != 'kommander' &&
+      github.event.pull_request.user.login != 'jayair' &&
+      github.event.pull_request.user.login != 'fwang' &&
+      github.event.pull_request.user.login != 'adamdotdevin' &&
+      github.event.pull_request.user.login != 'iamdavidhill' &&
+      github.event.pull_request.user.login != 'opencode-agent[bot]'
+    runs-on: blacksmith-4vcpu-ubuntu-2404
+    permissions:
+      contents: read
+      pull-requests: write
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 1
+
+      - name: Setup Bun
+        uses: ./.github/actions/setup-bun
+
+      - name: Install opencode
+        run: curl -fsSL https://opencode.ai/install | bash
+
+      - name: Check for duplicate PRs
+        env:
+          OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          PR_NUMBER: ${{ github.event.pull_request.number }}
+          PR_TITLE: ${{ github.event.pull_request.title }}
+          PR_BODY: ${{ github.event.pull_request.body }}
+        run: |
+          COMMENT=$(opencode run --agent duplicate-pr --print "Check for duplicate PRs related to this new PR:
+
+          Title: $PR_TITLE
+
+          Description: $PR_BODY")
+
+          gh pr comment "$PR_NUMBER" --body "_The following comment was made by an LLM, it may be inaccurate:_
+
+          $COMMENT"

+ 24 - 0
.opencode/agent/duplicate-pr.md

@@ -0,0 +1,24 @@
+---
+mode: primary
+hidden: true
+model: opencode/claude-haiku-4-5
+color: "#E67E22"
+tools:
+  "*": false
+  "github-pr-search": true
+---
+
+You are a duplicate PR detection agent. When a PR is opened, your job is to search for potentially duplicate or related open PRs.
+
+Use the github-pr-search tool to search for PRs that might be addressing the same issue or feature.
+
+Search using keywords from the PR title and description. Try multiple searches with different relevant terms.
+
+If you find potential duplicates:
+
+- List them with their titles and URLs
+- Briefly explain why they might be related
+
+If no duplicates are found, say so clearly.
+
+Keep your response concise and actionable.

+ 1 - 0
.opencode/opencode.jsonc

@@ -23,5 +23,6 @@
   },
   "tools": {
     "github-triage": false,
+    "github-pr-search": false,
   },
 }

+ 52 - 0
.opencode/tool/github-pr-search.ts

@@ -0,0 +1,52 @@
+/// <reference path="../env.d.ts" />
+import { tool } from "@opencode-ai/plugin"
+import DESCRIPTION from "./github-pr-search.txt"
+
+async function githubFetch(endpoint: string, options: RequestInit = {}) {
+  const response = await fetch(`https://api.github.com${endpoint}`, {
+    ...options,
+    headers: {
+      Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
+      Accept: "application/vnd.github+json",
+      "Content-Type": "application/json",
+      ...options.headers,
+    },
+  })
+  if (!response.ok) {
+    throw new Error(`GitHub API error: ${response.status} ${response.statusText}`)
+  }
+  return response.json()
+}
+
+interface PR {
+  title: string
+  html_url: string
+}
+
+export default tool({
+  description: DESCRIPTION,
+  args: {
+    query: tool.schema.string().describe("Search query for PR titles and descriptions"),
+    limit: tool.schema.number().describe("Maximum number of results to return").default(10),
+    offset: tool.schema.number().describe("Number of results to skip for pagination").default(0),
+  },
+  async execute(args) {
+    const owner = "sst"
+    const repo = "opencode"
+
+    const page = Math.floor(args.offset / args.limit) + 1
+    const searchQuery = encodeURIComponent(`${args.query} repo:${owner}/${repo} type:pr state:open`)
+    const result = await githubFetch(
+      `/search/issues?q=${searchQuery}&per_page=${args.limit}&page=${page}&sort=updated&order=desc`,
+    )
+
+    if (result.total_count === 0) {
+      return `No PRs found matching "${args.query}"`
+    }
+
+    const prs = result.items as PR[]
+    const formatted = prs.map((pr) => `${pr.title}\n${pr.html_url}`).join("\n\n")
+
+    return `Found ${result.total_count} PRs (showing ${prs.length}):\n\n${formatted}`
+  },
+})

+ 10 - 0
.opencode/tool/github-pr-search.txt

@@ -0,0 +1,10 @@
+Use this tool to search GitHub pull requests by title and description.
+
+This tool searches PRs in the sst/opencode repository and returns LLM-friendly results including:
+- PR number and title
+- Author
+- State (open/closed/merged)
+- Labels
+- Description snippet
+
+Use the query parameter to search for keywords that might appear in PR titles or descriptions.