Download scripts/close_duplicate_issues.js from SaylorTwift/gemini-cli: direct link, hf CLI and curl.
- Browser
- Download file 4 kB
-
https://huggingface.co/SaylorTwift/gemini-cli/resolve/main/scripts/close_duplicate_issues.js
- Command line
-
hf download hf://SaylorTwift/gemini-cli/scripts/close_duplicate_issues.js
-
curl -L -o close_duplicate_issues.js https://huggingface.co/SaylorTwift/gemini-cli/resolve/main/scripts/close_duplicate_issues.js
4 kB
| /** | |
| * @license | |
| * Copyright 2026 Google LLC | |
| * SPDX-License-Identifier: Apache-2.0 | |
| */ | |
| import { Octokit } from '@octokit/rest'; | |
| import yargs from 'yargs'; | |
| import { hideBin } from 'yargs/helpers'; | |
| import prompts from 'prompts'; | |
| if (!process.env.GITHUB_TOKEN) { | |
| console.error('Error: GITHUB_TOKEN environment variable is required.'); | |
| process.exit(1); | |
| } | |
| const argv = yargs(hideBin(process.argv)) | |
| .option('query', { | |
| alias: 'q', | |
| type: 'string', | |
| description: | |
| 'Search query to find duplicate issues (e.g. "function response parts")', | |
| demandOption: true, | |
| }) | |
| .option('canonical', { | |
| alias: 'c', | |
| type: 'number', | |
| description: 'The canonical issue number to duplicate others to', | |
| demandOption: true, | |
| }) | |
| .option('pr', { | |
| type: 'string', | |
| description: | |
| 'Optional Pull Request URL or ID to mention in the closing comment', | |
| }) | |
| .option('owner', { | |
| type: 'string', | |
| default: 'google-gemini', | |
| description: 'Repository owner', | |
| }) | |
| .option('repo', { | |
| type: 'string', | |
| default: 'gemini-cli', | |
| description: 'Repository name', | |
| }) | |
| .option('dry-run', { | |
| alias: 'd', | |
| type: 'boolean', | |
| default: false, | |
| description: 'Run without making actual changes (read-only mode)', | |
| }) | |
| .option('auto', { | |
| type: 'boolean', | |
| default: false, | |
| description: | |
| 'Automatically close all duplicates without prompting (batch mode)', | |
| }) | |
| .help() | |
| .parse(); | |
| const octokit = new Octokit({ | |
| auth: process.env.GITHUB_TOKEN, | |
| }); | |
| const { query, canonical, pr, owner, repo, dryRun, auto } = argv; | |
| // Construct the full search query ensuring it targets the specific repo and open issues | |
| const fullSearchQuery = `repo:${owner}/${repo} is:issue is:open ${query}`; | |
| async function run() { | |
| console.log(`Searching for issues matching: ${fullSearchQuery}`); | |
| if (dryRun) { | |
| console.log('--- DRY RUN MODE: No changes will be made ---'); | |
| } | |
| try { | |
| const issues = await octokit.paginate( | |
| octokit.rest.search.issuesAndPullRequests, | |
| { | |
| q: fullSearchQuery, | |
| }, | |
| ); | |
| console.log(`Found ${issues.length} issues.`); | |
| for (const issue of issues) { | |
| if (issue.number === canonical) { | |
| console.log(`Skipping canonical issue #${issue.number}`); | |
| continue; | |
| } | |
| console.log( | |
| `Processing issue #${issue.number}: ${issue.title} (by @${issue.user?.login})`, | |
| ); | |
| if (!auto && !dryRun) { | |
| const response = await prompts({ | |
| type: 'confirm', | |
| name: 'value', | |
| message: `Close issue #${issue.number} "${issue.title}" created by @${issue.user?.login}?`, | |
| initial: true, | |
| }); | |
| if (!response.value) { | |
| console.log(`Skipping issue #${issue.number}`); | |
| continue; | |
| } | |
| } | |
| let commentBody = `Closing this issue as a duplicate of #${canonical}.`; | |
| if (pr) { | |
| commentBody += ` Please note that this issue should be resolved by PR ${pr}.`; | |
| } | |
| try { | |
| if (!dryRun) { | |
| // Add comment | |
| await octokit.rest.issues.createComment({ | |
| owner, | |
| repo, | |
| issue_number: issue.number, | |
| body: commentBody, | |
| }); | |
| console.log(` Added comment.`); | |
| // Close issue | |
| await octokit.rest.issues.update({ | |
| owner, | |
| repo, | |
| issue_number: issue.number, | |
| state: 'closed', | |
| state_reason: 'duplicate', | |
| }); | |
| console.log(` Closed issue.`); | |
| } else { | |
| console.log(` [DRY RUN] Would add comment: "${commentBody}"`); | |
| console.log(` [DRY RUN] Would close issue #${issue.number}`); | |
| } | |
| } catch (error) { | |
| console.error( | |
| ` Failed to process issue #${issue.number}:`, | |
| error.message, | |
| ); | |
| } | |
| } | |
| } catch (error) { | |
| console.error('Error searching for issues:', error.message); | |
| process.exit(1); | |
| } | |
| } | |
| run().catch(console.error); | |