diff --git a/.claude/commands/dedupe.md b/.claude/commands/dedupe.md new file mode 100644 index 00000000000..2711e981a9a --- /dev/null +++ b/.claude/commands/dedupe.md @@ -0,0 +1,23 @@ +--- +allowed-tools: Bash(gh issue view:*), Bash(gh search:*), Bash(gh issue list:*), Bash(./scripts/comment-on-duplicates.sh:*) +description: Find duplicate GitHub issues +--- + +Find up to 3 likely duplicate issues for a given GitHub issue. + +To do this, follow these steps precisely: + +1. Use an agent to check if the Github issue (a) is closed, (b) does not need to be deduped (eg. because it is broad product feedback without a specific solution, or positive feedback), or (c) already has a duplicates comment that you made earlier. If so, do not proceed. +2. Use an agent to view a Github issue, and ask the agent to return a summary of the issue +3. Then, launch 5 parallel agents to search Github for duplicates of this issue, using diverse keywords and search approaches, using the summary from #2 +4. Next, feed the results from #2 and #3 into another agent, so that it can filter out false positives, that are likely not actually duplicates of the original issue. If there are no duplicates remaining, do not proceed. +5. Finally, use the comment script to post duplicates: + ``` + ./scripts/comment-on-duplicates.sh --base-issue --potential-duplicates + ``` + +Notes (be sure to tell this to your agents, too): + +- Use `gh` to interact with Github, rather than web fetch +- Do not use other tools, beyond `gh` and the comment script (eg. don't use other MCP servers, file edit, etc.) +- Make a todo list first diff --git a/.github/workflows/claude-dedupe-issues.yml b/.github/workflows/claude-dedupe-issues.yml new file mode 100644 index 00000000000..25b469653ff --- /dev/null +++ b/.github/workflows/claude-dedupe-issues.yml @@ -0,0 +1,34 @@ +name: Claude Issue Dedupe +description: Automatically dedupe GitHub issues using Claude Code +on: + issues: + types: [opened] + workflow_dispatch: + inputs: + issue_number: + description: 'Issue number to process for duplicate detection' + required: true + type: string + +jobs: + claude-dedupe-issues: + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + issues: write + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Run Claude Code slash command + uses: anthropics/claude-code-action@v1 + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + allowed_non_write_users: "*" + prompt: "/dedupe ${{ github.repository }}/issues/${{ github.event.issue.number || inputs.issue_number }}" + claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} diff --git a/scripts/comment-on-duplicates.sh b/scripts/comment-on-duplicates.sh new file mode 100755 index 00000000000..e2f2c2c4b81 --- /dev/null +++ b/scripts/comment-on-duplicates.sh @@ -0,0 +1,98 @@ +#!/usr/bin/env bash +# +# Comments on a GitHub issue with a list of potential duplicates. +# Usage: ./comment-on-duplicates.sh --base-issue 123 --potential-duplicates 456 789 101 +# + +set -euo pipefail + +REPO="${GITHUB_REPOSITORY:-lightningnetwork/lnd}" +BASE_ISSUE="" +DUPLICATES=() + +# Parse arguments +while [[ $# -gt 0 ]]; do + case $1 in + --base-issue) + BASE_ISSUE="$2" + shift 2 + ;; + --potential-duplicates) + shift + while [[ $# -gt 0 && ! "$1" =~ ^-- ]]; do + DUPLICATES+=("$1") + shift + done + ;; + *) + echo "Unknown option: $1" >&2 + exit 1 + ;; + esac +done + +# Validate base issue +if [[ -z "$BASE_ISSUE" ]]; then + echo "Error: --base-issue is required" >&2 + exit 1 +fi + +if ! [[ "$BASE_ISSUE" =~ ^[0-9]+$ ]]; then + echo "Error: --base-issue must be a number, got: $BASE_ISSUE" >&2 + exit 1 +fi + +# Validate duplicates +if [[ ${#DUPLICATES[@]} -eq 0 ]]; then + echo "Error: --potential-duplicates requires at least one issue number" >&2 + exit 1 +fi + +if [[ ${#DUPLICATES[@]} -gt 3 ]]; then + echo "Error: --potential-duplicates accepts at most 3 issues" >&2 + exit 1 +fi + +for dup in "${DUPLICATES[@]}"; do + if ! [[ "$dup" =~ ^[0-9]+$ ]]; then + echo "Error: duplicate issue must be a number, got: $dup" >&2 + exit 1 + fi +done + +# Validate that base issue exists +if ! gh issue view "$BASE_ISSUE" --repo "$REPO" &>/dev/null; then + echo "Error: issue #$BASE_ISSUE does not exist in $REPO" >&2 + exit 1 +fi + +# Validate that all duplicate issues exist +for dup in "${DUPLICATES[@]}"; do + if ! gh issue view "$dup" --repo "$REPO" &>/dev/null; then + echo "Error: issue #$dup does not exist in $REPO" >&2 + exit 1 + fi +done + +# Build comment body +COUNT=${#DUPLICATES[@]} +if [[ $COUNT -eq 1 ]]; then + HEADER="Found 1 possible duplicate issue:" +else + HEADER="Found $COUNT possible duplicate issues:" +fi + +BODY="$HEADER"$'\n\n' +INDEX=1 +for dup in "${DUPLICATES[@]}"; do + BODY+="$INDEX. https://github.com/$REPO/issues/$dup"$'\n' + ((INDEX++)) +done + +BODY+=$'\n'"If this issue is a duplicate, please close it and 👍 the existing issue instead."$'\n\n' +BODY+="🤖 Generated with [Claude Code](https://claude.ai/code)" + +# Post the comment +gh issue comment "$BASE_ISSUE" --repo "$REPO" --body "$BODY" + +echo "Posted duplicate comment on issue #$BASE_ISSUE"