spacehub

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 issuesBrowse files
- 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 <noreply@anthropic.com>
Boris Cherny authored on Aug 8, 2025 parent 483e0e8 ยท commit 1cc90e9b788c
2 changed files+65 -6compared with 483e0e8
M.claude/commands/dedupe.md
Mscripts/auto-close-duplicates.ts
@@ -28,9 +28,10 @@
2828 2. <link to issue>
2929 3. <link to issue>
3030
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.
3232
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
3435
3536 ๐Ÿค– Generated with [Claude Code](https://claude.ai/code)
3637
@@ -25,13 +25,16 @@
2525 content: string;
2626 }
2727
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> {
2929 const response = await fetch(`https://api.github.com${endpoint}`, {
30+ method,
3031 headers: {
3132 Authorization: `Bearer ${token}`,
3233 Accept: "application/vnd.github.v3+json",
3334 "User-Agent": "auto-close-duplicates-script",
35+ ...(body && { "Content-Type": "application/json" }),
3436 },
37+ ...(body && { body: JSON.stringify(body) }),
3538 });
3639
3740 if (!response.ok) {
@@ -43,6 +46,42 @@
4346 return response.json();
4447 }
4548
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+
4685 async function autoCloseDuplicates(): Promise<void> {
4786 console.log("[DEBUG] Starting auto-close duplicates script");
4887
@@ -187,11 +226,30 @@
187226 continue;
188227 }
189228
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+
190237 candidateCount++;
191238 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+ }
195253 }
196254
197255 console.log(