surshar / claude-code
Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflows - all through natural language commands.
Built by Anthropic · github.com/anthropics
Update auto-close-duplicates script to actually close issues
| @@ -28,9 +28,10 @@ | ||
| 28 | 28 | 2. <link to issue> |
| 29 | 29 | 3. <link to issue> |
| 30 | 30 | |
| 31 | -If your issue is a duplicate, please close it and 👍 the existing issue instead. | |
| 31 | +This issue will be automatically closed as a duplicate in 3 days. | |
| 32 | 32 | |
| 33 | -<sub>This issue will be automatically closed as a duplicate in 3 days if there are no additional comments. To prevent auto-closure, please 👎 this comment.</sub> | |
| 33 | +- If your issue is a duplicate, please close it and 👍 the existing issue instead | |
| 34 | +- To prevent auto-closure, add a comment or 👎 this comment | |
| 34 | 35 | |
| 35 | 36 | 🤖 Generated with [Claude Code](https://claude.ai/code) |
| 36 | 37 |
| @@ -25,13 +25,16 @@ | ||
| 25 | 25 | content: string; |
| 26 | 26 | } |
| 27 | 27 | |
| 28 | -async function githubRequest<T>(endpoint: string, token: string): Promise<T> { | |
| 28 | +async function githubRequest<T>(endpoint: string, token: string, method: string = 'GET', body?: any): Promise<T> { | |
| 29 | 29 | const response = await fetch(`https://api.github.com${endpoint}`, { |
| 30 | + method, | |
| 30 | 31 | headers: { |
| 31 | 32 | Authorization: `Bearer ${token}`, |
| 32 | 33 | Accept: "application/vnd.github.v3+json", |
| 33 | 34 | "User-Agent": "auto-close-duplicates-script", |
| 35 | + ...(body && { "Content-Type": "application/json" }), | |
| 34 | 36 | }, |
| 37 | + ...(body && { body: JSON.stringify(body) }), | |
| 35 | 38 | }); |
| 36 | 39 | |
| 37 | 40 | if (!response.ok) { |
| @@ -43,6 +46,42 @@ | ||
| 43 | 46 | return response.json(); |
| 44 | 47 | } |
| 45 | 48 | |
| 49 | +function extractDuplicateIssueNumber(commentBody: string): number | null { | |
| 50 | + const match = commentBody.match(/#(\d+)/); | |
| 51 | + return match ? parseInt(match[1], 10) : null; | |
| 52 | +} | |
| 53 | + | |
| 54 | +async function closeIssueAsDuplicate( | |
| 55 | + owner: string, | |
| 56 | + repo: string, | |
| 57 | + issueNumber: number, | |
| 58 | + duplicateOfNumber: number, | |
| 59 | + token: string | |
| 60 | +): Promise<void> { | |
| 61 | + await githubRequest( | |
| 62 | + `/repos/${owner}/${repo}/issues/${issueNumber}`, | |
| 63 | + token, | |
| 64 | + 'PATCH', | |
| 65 | + { | |
| 66 | + state: 'closed', | |
| 67 | + state_reason: 'not_planned' | |
| 68 | + } | |
| 69 | + ); | |
| 70 | + | |
| 71 | + await githubRequest( | |
| 72 | + `/repos/${owner}/${repo}/issues/${issueNumber}/comments`, | |
| 73 | + token, | |
| 74 | + 'POST', | |
| 75 | + { | |
| 76 | + body: `This issue has been automatically closed as a duplicate of #${duplicateOfNumber}. | |
| 77 | + | |
| 78 | +If this is incorrect, please re-open this issue or create a new one. | |
| 79 | + | |
| 80 | +🤖 Generated with [Claude Code](https://claude.ai/code)` | |
| 81 | + } | |
| 82 | + ); | |
| 83 | +} | |
| 84 | + | |
| 46 | 85 | async function autoCloseDuplicates(): Promise<void> { |
| 47 | 86 | console.log("[DEBUG] Starting auto-close duplicates script"); |
| 48 | 87 | |
| @@ -187,11 +226,30 @@ | ||
| 187 | 226 | continue; |
| 188 | 227 | } |
| 189 | 228 | |
| 229 | + const duplicateIssueNumber = extractDuplicateIssueNumber(lastDupeComment.body); | |
| 230 | + if (!duplicateIssueNumber) { | |
| 231 | + console.log( | |
| 232 | + `[DEBUG] Issue #${issue.number} - could not extract duplicate issue number from comment, skipping` | |
| 233 | + ); | |
| 234 | + continue; | |
| 235 | + } | |
| 236 | + | |
| 190 | 237 | candidateCount++; |
| 191 | 238 | const issueUrl = `https://github.com/${owner}/${repo}/issues/${issue.number}`; |
| 192 | - console.log( | |
| 193 | - `[DRY RUN] Would auto-close issue #${issue.number} as duplicate: ${issueUrl}` | |
| 194 | - ); | |
| 239 | + | |
| 240 | + try { | |
| 241 | + console.log( | |
| 242 | + `[INFO] Auto-closing issue #${issue.number} as duplicate of #${duplicateIssueNumber}: ${issueUrl}` | |
| 243 | + ); | |
| 244 | + await closeIssueAsDuplicate(owner, repo, issue.number, duplicateIssueNumber, token); | |
| 245 | + console.log( | |
| 246 | + `[SUCCESS] Successfully closed issue #${issue.number} as duplicate of #${duplicateIssueNumber}` | |
| 247 | + ); | |
| 248 | + } catch (error) { | |
| 249 | + console.error( | |
| 250 | + `[ERROR] Failed to close issue #${issue.number} as duplicate: ${error}` | |
| 251 | + ); | |
| 252 | + } | |
| 195 | 253 | } |
| 196 | 254 | |
| 197 | 255 | console.log( |