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

Merge pull request #5411 from anthropics/boris/gityBrowse files
Update auto-close-duplicates script to actually close issues
Boris Cherny authored on Aug 8, 2025 · GitHub committed on Aug 8, 2025 parents 483e0e8 + 1cc90e9 · commit 072856dd5b77
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(