From 1cc90e9b788c47fafaac31b09ee8d3407775d8d6 Mon Sep 17 00:00:00 2001 From: Boris Cherny Date: Fri, 8 Aug 2025 11:48:27 -0700 Subject: [PATCH] Update auto-close-duplicates script to actually close issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove dry run mode and implement actual issue closing - Extract duplicate issue number from bot comments - Close issues via GitHub API with proper state and comments - Add error handling for API failures - Use Claude Code comment format with reopening instructions 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .claude/commands/dedupe.md | 5 ++- scripts/auto-close-duplicates.ts | 66 ++++++++++++++++++++++++++++++-- 2 files changed, 65 insertions(+), 6 deletions(-) diff --git a/.claude/commands/dedupe.md b/.claude/commands/dedupe.md index c675614..9cd83aa 100644 --- a/.claude/commands/dedupe.md +++ b/.claude/commands/dedupe.md @@ -28,9 +28,10 @@ Found 3 possible duplicate issues: 2. 3. -If your issue is a duplicate, please close it and 👍 the existing issue instead. +This issue will be automatically closed as a duplicate in 3 days. -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. +- If your issue is a duplicate, please close it and 👍 the existing issue instead +- To prevent auto-closure, add a comment or 👎 this comment 🤖 Generated with [Claude Code](https://claude.ai/code) diff --git a/scripts/auto-close-duplicates.ts b/scripts/auto-close-duplicates.ts index 1b35894..fd82d6b 100644 --- a/scripts/auto-close-duplicates.ts +++ b/scripts/auto-close-duplicates.ts @@ -25,13 +25,16 @@ interface GitHubReaction { content: string; } -async function githubRequest(endpoint: string, token: string): Promise { +async function githubRequest(endpoint: string, token: string, method: string = 'GET', body?: any): Promise { const response = await fetch(`https://api.github.com${endpoint}`, { + method, headers: { Authorization: `Bearer ${token}`, Accept: "application/vnd.github.v3+json", "User-Agent": "auto-close-duplicates-script", + ...(body && { "Content-Type": "application/json" }), }, + ...(body && { body: JSON.stringify(body) }), }); if (!response.ok) { @@ -43,6 +46,42 @@ async function githubRequest(endpoint: string, token: string): Promise { return response.json(); } +function extractDuplicateIssueNumber(commentBody: string): number | null { + const match = commentBody.match(/#(\d+)/); + return match ? parseInt(match[1], 10) : null; +} + +async function closeIssueAsDuplicate( + owner: string, + repo: string, + issueNumber: number, + duplicateOfNumber: number, + token: string +): Promise { + await githubRequest( + `/repos/${owner}/${repo}/issues/${issueNumber}`, + token, + 'PATCH', + { + state: 'closed', + state_reason: 'not_planned' + } + ); + + await githubRequest( + `/repos/${owner}/${repo}/issues/${issueNumber}/comments`, + token, + 'POST', + { + body: `This issue has been automatically closed as a duplicate of #${duplicateOfNumber}. + +If this is incorrect, please re-open this issue or create a new one. + +🤖 Generated with [Claude Code](https://claude.ai/code)` + } + ); +} + async function autoCloseDuplicates(): Promise { console.log("[DEBUG] Starting auto-close duplicates script"); @@ -187,11 +226,30 @@ async function autoCloseDuplicates(): Promise { continue; } + const duplicateIssueNumber = extractDuplicateIssueNumber(lastDupeComment.body); + if (!duplicateIssueNumber) { + console.log( + `[DEBUG] Issue #${issue.number} - could not extract duplicate issue number from comment, skipping` + ); + continue; + } + candidateCount++; const issueUrl = `https://github.com/${owner}/${repo}/issues/${issue.number}`; - console.log( - `[DRY RUN] Would auto-close issue #${issue.number} as duplicate: ${issueUrl}` - ); + + try { + console.log( + `[INFO] Auto-closing issue #${issue.number} as duplicate of #${duplicateIssueNumber}: ${issueUrl}` + ); + await closeIssueAsDuplicate(owner, repo, issue.number, duplicateIssueNumber, token); + console.log( + `[SUCCESS] Successfully closed issue #${issue.number} as duplicate of #${duplicateIssueNumber}` + ); + } catch (error) { + console.error( + `[ERROR] Failed to close issue #${issue.number} as duplicate: ${error}` + ); + } } console.log(