Explorar el Código

finalize to copy the style of the PR review for enhanced security

Ryan Vogel hace 2 meses
padre
commit
7560913b7d
Se han modificado 3 ficheros con 197 adiciones y 154 borrados
  1. 50 145
      .github/workflows/duplicate-issues.yml
  2. 68 9
      .opencode/agent/duplicate-issue.md
  3. 79 0
      script/duplicate-issue.ts

+ 50 - 145
.github/workflows/duplicate-issues.yml

@@ -2,11 +2,10 @@ name: duplicate-issues
 
 on:
   issues:
-    types: [opened, edited]
+    types: [opened]
 
 jobs:
   check-duplicates:
-    if: github.event.action == 'opened'
     runs-on: blacksmith-4vcpu-ubuntu-2404
     permissions:
       contents: read
@@ -19,159 +18,65 @@ jobs:
 
       - uses: ./.github/actions/setup-bun
 
+      - name: Install dependencies
+        run: bun install
+
       - name: Install opencode
         run: curl -fsSL https://opencode.ai/install | bash
 
+      - name: Build prompt
+        uses: actions/github-script@v8
+        with:
+          script: |
+            const fs = require("fs")
+            const issue = context.payload.issue
+            const body = issue.body ?? ""
+            const text = [
+              "Check this new issue for compliance and duplicates:",
+              "",
+              `CURRENT_ISSUE_NUMBER: ${issue.number}`,
+              "",
+              `Title: ${issue.title}`,
+              "",
+              "Description:",
+              body,
+            ].join("\n")
+
+            fs.writeFileSync("issue_info.txt", text)
+
       - name: Check duplicates and compliance
         env:
           OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-          OPENCODE_PERMISSION: |
-            {
-              "bash": {
-                "*": "deny",
-                "gh issue*": "allow"
-              },
-              "webfetch": "deny"
-            }
         run: |
-          opencode run -m opencode/claude-sonnet-4-6 "A new issue has been created:
-
-          Issue number: ${{ github.event.issue.number }}
-
-          Lookup this issue with gh issue view ${{ github.event.issue.number }}.
-
-          You have TWO tasks. Perform both, then post a SINGLE comment (if needed).
-
-          ---
-
-          TASK 1: CONTRIBUTING GUIDELINES COMPLIANCE CHECK
-
-          Check whether the issue follows our contributing guidelines and issue templates.
-
-          This project has three issue templates that every issue MUST use one of:
-
-          1. Bug Report - requires a Description field with real content
-          2. Feature Request - requires a verification checkbox and description, title should start with [FEATURE]:
-          3. Question - requires the Question field with real content
-
-          Additionally check:
-          - No AI-generated walls of text (long, AI-generated descriptions are not acceptable)
-          - The issue has real content, not just template placeholder text left unchanged
-          - Bug reports should include some context about how to reproduce
-          - Feature requests should explain the problem or need
-          - We want to push for having the user provide system description & information
-
-          Do NOT be nitpicky about optional fields. Only flag real problems like: no template used, required fields empty or placeholder text only, obviously AI-generated walls of text, or completely empty/nonsensical content.
-
-          ---
-
-          TASK 2: DUPLICATE CHECK
-
-          Search through existing issues (excluding #${{ github.event.issue.number }}) to find potential duplicates.
-          Consider:
-          1. Similar titles or descriptions
-          2. Same error messages or symptoms
-          3. Related functionality or components
-          4. Similar feature requests
-
-          Additionally, if the issue mentions keybinds, keyboard shortcuts, or key bindings, note the pinned keybinds issue #4997.
-
-          ---
-
-          POSTING YOUR COMMENT:
-
-          Based on your findings, post a SINGLE comment on issue #${{ github.event.issue.number }}. Build the comment as follows:
-
-          If the issue is NOT compliant, start the comment with:
-          <!-- issue-compliance -->
-          Then explain what needs to be fixed and that they have 2 hours to edit the issue before it is automatically closed. Also add the label needs:compliance to the issue using: gh issue edit ${{ github.event.issue.number }} --add-label needs:compliance
-
-          If duplicates were found, include a section about potential duplicates with links.
-
-          If the issue mentions keybinds/keyboard shortcuts, include a note about #4997.
-
-          If the issue IS compliant AND no duplicates were found AND no keybind reference, do NOT comment at all.
-
-          Use this format for the comment:
-
-          [If not compliant:]
-          <!-- issue-compliance -->
-          This issue doesn't fully meet our [contributing guidelines](../blob/dev/CONTRIBUTING.md).
-
-          **What needs to be fixed:**
-          - [specific reasons]
-
-          Please edit this issue to address the above within **2 hours**, or it will be automatically closed.
+          bun script/duplicate-issue.ts -f issue_info.txt "Check the attached file for issue details and return either a comment body or No action required" > issue_comment.txt
 
-          [If duplicates found, add:]
-          ---
-          This issue might be a duplicate of existing issues. Please check:
-          - #[issue_number]: [brief description of similarity]
-
-          [If keybind-related, add:]
-          For keybind-related issues, please also check our pinned keybinds documentation: #4997
-
-          [End with if not compliant:]
-          If you believe this was flagged incorrectly, please let a maintainer know.
-
-          Remember: post at most ONE comment combining all findings. If everything is fine, post nothing."
-
-  recheck-compliance:
-    if: github.event.action == 'edited' && contains(github.event.issue.labels.*.name, 'needs:compliance')
-    runs-on: blacksmith-4vcpu-ubuntu-2404
-    permissions:
-      contents: read
-      issues: write
-    steps:
-      - name: Checkout repository
-        uses: actions/checkout@v4
+      - name: Post comment and label issue
+        env:
+          COMMENT: ${{ github.workspace }}/issue_comment.txt
+        uses: actions/github-script@v8
         with:
-          fetch-depth: 1
-
-      - uses: ./.github/actions/setup-bun
+          script: |
+            const fs = require("fs")
+            const comment = fs.readFileSync(process.env.COMMENT, "utf8").trim()
 
-      - name: Install opencode
-        run: curl -fsSL https://opencode.ai/install | bash
-
-      - name: Recheck compliance
-        env:
-          OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-          OPENCODE_PERMISSION: |
-            {
-              "bash": {
-                "*": "deny",
-                "gh issue*": "allow"
-              },
-              "webfetch": "deny"
+            if (comment === "No action required") {
+              core.info("No comment needed")
+              return
             }
-        run: |
-          opencode run -m opencode/claude-sonnet-4-6 "Issue #${{ github.event.issue.number }} was previously flagged as non-compliant and has been edited.
-
-          Lookup this issue with gh issue view ${{ github.event.issue.number }}.
-
-          Re-check whether the issue now follows our contributing guidelines and issue templates.
-
-          This project has three issue templates that every issue MUST use one of:
-
-          1. Bug Report - requires a Description field with real content
-          2. Feature Request - requires a verification checkbox and description, title should start with [FEATURE]:
-          3. Question - requires the Question field with real content
-
-          Additionally check:
-          - No AI-generated walls of text (long, AI-generated descriptions are not acceptable)
-          - The issue has real content, not just template placeholder text left unchanged
-          - Bug reports should include some context about how to reproduce
-          - Feature requests should explain the problem or need
-          - We want to push for having the user provide system description & information
-
-          Do NOT be nitpicky about optional fields. Only flag real problems like: no template used, required fields empty or placeholder text only, obviously AI-generated walls of text, or completely empty/nonsensical content.
-
-          If the issue is NOW compliant:
-          1. Remove the needs:compliance label: gh issue edit ${{ github.event.issue.number }} --remove-label needs:compliance
-          2. Find and delete the previous compliance comment (the one containing <!-- issue-compliance -->) using: gh api repos/${{ github.repository }}/issues/${{ github.event.issue.number }}/comments --jq '.[] | select(.body | contains(\"<!-- issue-compliance -->\")) | .id' then delete it with: gh api -X DELETE repos/${{ github.repository }}/issues/${{ github.event.issue.number }}/comments/{id}
-          3. Post a short comment thanking them for updating the issue.
 
-          If the issue is STILL not compliant:
-          Post a comment explaining what still needs to be fixed. Keep the needs:compliance label."
+            await github.rest.issues.createComment({
+              owner: context.repo.owner,
+              repo: context.repo.repo,
+              issue_number: context.payload.issue.number,
+              body: `_The following comment was made by an LLM, it may be inaccurate:_\n\n${comment}`,
+            })
+
+            if (!comment.includes("<!-- issue-compliance -->")) return
+
+            await github.rest.issues.addLabels({
+              owner: context.repo.owner,
+              repo: context.repo.repo,
+              issue_number: context.payload.issue.number,
+              labels: ["needs:compliance"],
+            })

+ 68 - 9
.opencode/agent/duplicate-issue.md

@@ -6,22 +6,81 @@ color: "#E67E22"
 tools:
   "*": false
   "github-issue-search": true
-  "github-pr-search": true
 ---
 
 You are a duplicate issue detection agent. When an issue is opened, your job is to search for potentially duplicate or related open issues.
 
-Use the github-issue-search tool to search for issues that might be addressing the same issue or feature. You also have the github-pr-search tool to search for PRs that might be addressing the same issue or feature.
+You have two jobs:
 
-IMPORTANT: The input will contain a line `CURRENT_PR_NUMBER: NNNN`. This is the current PR number, you should not mark that the current PR as a duplicate of itself.
+1. Check if the issue follows our issue templates/contributing requirements.
+2. Check for potential duplicate issues.
 
-Search using keywords from the PR title and description. Try multiple searches with different relevant terms.
+Use the github-issue-search tool to find potentially related issues.
 
-If you find potential duplicates:
+IMPORTANT: The input will contain a line `CURRENT_ISSUE_NUMBER: NNNN`. Never mark that issue as a duplicate of itself.
 
-- List them with their titles and URLs
-- Briefly explain why they might be related
+## Compliance checks
 
-If no duplicates are found, say so clearly. BUT ONLY SAY "No duplicate PRs found" (don't say anything else if no dups)
+This project has three issue templates:
 
-Keep your response concise and actionable.
+1. Bug Report - needs a Description field with real content.
+2. Feature Request - title should start with `[FEATURE]:` and include verification checkbox + meaningful description.
+3. Question - needs a Question field with real content.
+
+Also check:
+
+- no AI-generated walls of text
+- required sections are not placeholder-only / unchanged template text
+- bug reports include some repro context
+- feature requests explain the problem/need
+- encourage system information where relevant
+
+Do not be nitpicky about optional fields. Only flag real issues (missing template/required content, placeholder-only content, obviously AI-generated wall of text, empty/nonsensical issue).
+
+## Duplicate checks
+
+Search for duplicates by trying multiple keyword combinations from the issue title/body. Prioritize:
+
+- similar title/description
+- same error/symptoms
+- same component/feature area
+
+If the issue mentions keybinds, keyboard shortcuts, or key bindings, include a note to check pinned issue #4997.
+
+## Output rules
+
+If the issue is compliant AND no duplicates are found AND no keybind note is needed, output exactly:
+
+No action required
+
+Otherwise output exactly one markdown comment body with this structure:
+
+- If non-compliant, start with:
+
+<!-- issue-compliance -->
+
+This issue doesn't fully meet our [contributing guidelines](../blob/dev/CONTRIBUTING.md).
+
+**What needs to be fixed:**
+
+- [specific reason]
+
+Please edit this issue to address the above within **2 hours**, or it will be automatically closed.
+
+- If duplicates were found, add:
+
+---
+
+This issue might be a duplicate of existing issues. Please check:
+
+- #1234: [brief reason]
+
+- If keybind-related, add:
+
+For keybind-related issues, please also check our pinned keybinds documentation: #4997
+
+- If non-compliant, end with:
+
+If you believe this was flagged incorrectly, please let a maintainer know.
+
+Keep output concise. Do not wrap output in code fences.

+ 79 - 0
script/duplicate-issue.ts

@@ -0,0 +1,79 @@
+#!/usr/bin/env bun
+
+import path from "path"
+import { pathToFileURL } from "bun"
+import { createOpencode } from "@opencode-ai/sdk"
+import { parseArgs } from "util"
+
+async function main() {
+  const { values, positionals } = parseArgs({
+    args: Bun.argv.slice(2),
+    options: {
+      file: { type: "string", short: "f" },
+      help: { type: "boolean", short: "h", default: false },
+    },
+    allowPositionals: true,
+  })
+
+  if (values.help) {
+    console.log(`
+Usage: bun script/duplicate-issue.ts [options] <message>
+
+Options:
+  -f, --file <path>   File to attach to the prompt
+  -h, --help          Show this help message
+
+Examples:
+  bun script/duplicate-issue.ts -f issue_info.txt "Check the attached file for issue details"
+`)
+    process.exit(0)
+  }
+
+  const message = positionals.join(" ")
+  if (!message) {
+    console.error("Error: message is required")
+    process.exit(1)
+  }
+
+  const opencode = await createOpencode({ port: 0 })
+
+  try {
+    const parts: Array<{ type: "text"; text: string } | { type: "file"; url: string; filename: string; mime: string }> =
+      []
+
+    if (values.file) {
+      const resolved = path.resolve(process.cwd(), values.file)
+      const file = Bun.file(resolved)
+      if (!(await file.exists())) {
+        console.error(`Error: file not found: ${values.file}`)
+        process.exit(1)
+      }
+      parts.push({
+        type: "file",
+        url: pathToFileURL(resolved).href,
+        filename: path.basename(resolved),
+        mime: "text/plain",
+      })
+    }
+
+    parts.push({ type: "text", text: message })
+
+    const session = await opencode.client.session.create()
+    const result = await opencode.client.session
+      .prompt({
+        path: { id: session.data!.id },
+        body: {
+          agent: "duplicate-issue",
+          parts,
+        },
+        signal: AbortSignal.timeout(120_000),
+      })
+      .then((x) => x.data?.parts?.find((y) => y.type === "text")?.text ?? "")
+
+    console.log(result.trim())
+  } finally {
+    opencode.server.close()
+  }
+}
+
+main()