From 1183388fdfc32a6eaf3a7c4ef18b5b4c23c44dad Mon Sep 17 00:00:00 2001 From: Ashwin Bhat Date: Mon, 14 Apr 2025 16:48:25 -0700 Subject: [PATCH] Add GitHub workflow for issue triage with available labels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .github/actions/claude-code-action/action.yml | 136 ++++++++++++++++++ .../claude-issue-triage-action/action.yml | 87 +++++++++++ .github/workflows/claude-issue-triage.yml | 23 +++ 3 files changed, 246 insertions(+) create mode 100644 .github/actions/claude-code-action/action.yml create mode 100644 .github/actions/claude-issue-triage-action/action.yml create mode 100644 .github/workflows/claude-issue-triage.yml diff --git a/.github/actions/claude-code-action/action.yml b/.github/actions/claude-code-action/action.yml new file mode 100644 index 00000000..8e57c63c --- /dev/null +++ b/.github/actions/claude-code-action/action.yml @@ -0,0 +1,136 @@ +name: "Claude Code Action" +description: "Run Claude Code in GitHub Actions workflows" + +inputs: + github_token: + description: "GitHub token with repo and issues permissions" + required: true + anthropic_api_key: + description: "Anthropic API key" + required: true + prompt: + description: "The prompt to send to Claude Code" + required: false + default: "" + prompt_file: + description: "Path to a file containing the prompt to send to Claude Code" + required: false + default: "" + allowed_tools: + description: "Comma-separated list of allowed tools for Claude Code to use" + required: false + default: "" + output_file: + description: "File to save Claude Code output to (optional)" + required: false + default: "" + timeout_minutes: + description: "Timeout in minutes for Claude Code execution" + required: false + default: "10" + install_github_mcp: + description: "Whether to install the GitHub MCP server" + required: false + default: "false" + +runs: + using: "composite" + steps: + - name: Install Claude Code + shell: bash + run: npm install -g @anthropic-ai/claude-code + + - name: Install GitHub MCP Server + if: inputs.install_github_mcp == 'true' + shell: bash + run: | + claude mcp add-json github '{ + "command": "docker", + "args": [ + "run", + "-i", + "--rm", + "-e", + "GITHUB_PERSONAL_ACCESS_TOKEN", + "ghcr.io/github/github-mcp-server:sha-ff3036d" + ], + "env": { + "GITHUB_PERSONAL_ACCESS_TOKEN": "${{ inputs.GITHUB_TOKEN }}" + } + }' + + - name: Prepare Prompt File + shell: bash + id: prepare_prompt + run: | + # Check if either prompt or prompt_file is provided + if [ -z "${{ inputs.prompt }}" ] && [ -z "${{ inputs.prompt_file }}" ]; then + echo "::error::Neither 'prompt' nor 'prompt_file' was provided. At least one is required." + exit 1 + fi + + # Determine which prompt source to use + if [ ! -z "${{ inputs.prompt_file }}" ]; then + # Check if the prompt file exists + if [ ! -f "${{ inputs.prompt_file }}" ]; then + echo "::error::Prompt file '${{ inputs.prompt_file }}' does not exist." + exit 1 + fi + + # Use the provided prompt file + PROMPT_PATH="${{ inputs.prompt_file }}" + else + mkdir -p /tmp/claude-action + PROMPT_PATH="/tmp/claude-action/prompt.txt" + echo "${{ inputs.prompt }}" > "$PROMPT_PATH" + fi + + # Verify the prompt file is not empty + if [ ! -s "$PROMPT_PATH" ]; then + echo "::error::Prompt is empty. Please provide a non-empty prompt." + exit 1 + fi + + # Save the prompt path for the next step + echo "PROMPT_PATH=$PROMPT_PATH" >> $GITHUB_ENV + + - name: Run Claude Code + shell: bash + id: run_claude + run: | + ALLOWED_TOOLS_ARG="" + if [ ! -z "${{ inputs.allowed_tools }}" ]; then + ALLOWED_TOOLS_ARG="--allowedTools ${{ inputs.allowed_tools }}" + fi + + # Set a timeout to ensure the command doesn't run indefinitely + timeout_seconds=$((${{ inputs.timeout_minutes }} * 60)) + + if [ -z "${{ inputs.output_file }}" ]; then + # Run Claude Code and output to console + timeout $timeout_seconds claude \ + -p \ + --verbose \ + --output-format stream-json \ + "$(cat ${{ env.PROMPT_PATH }})" \ + ${{ inputs.allowed_tools != '' && format('--allowedTools "{0}"', inputs.allowed_tools) || '' }} + else + # Run Claude Code and tee output to console and file + timeout $timeout_seconds claude \ + -p \ + --verbose \ + --output-format stream-json \ + "$(cat ${{ env.PROMPT_PATH }})" \ + ${{ inputs.allowed_tools != '' && format('--allowedTools "{0}"', inputs.allowed_tools) || '' }} | tee output.txt + + # Process output.txt into JSON in a separate step + jq -s '.' output.txt > output.json + + # Extract the result from the last item in the array (system message) + jq -r '.[-1].result' output.json > "${{ inputs.output_file }}" + + echo "Complete output saved to output.json, final response saved to ${{ inputs.output_file }}" + fi + env: + ANTHROPIC_API_KEY: ${{ inputs.anthropic_api_key }} + GITHUB_TOKEN: ${{ inputs.github_token }} diff --git a/.github/actions/claude-issue-triage-action/action.yml b/.github/actions/claude-issue-triage-action/action.yml new file mode 100644 index 00000000..666efa27 --- /dev/null +++ b/.github/actions/claude-issue-triage-action/action.yml @@ -0,0 +1,87 @@ +name: "Claude Issue Triage Action" +description: "Automatically triage GitHub issues using Claude Code" + +inputs: + timeout_minutes: + description: "Timeout in minutes for execution" + required: false + default: "5" + anthropic_api_key: + description: "Anthropic API key" + required: true + github_token: + description: "GitHub token with repo and issues permissions" + required: true + +runs: + using: "composite" + steps: + - name: Checkout repository code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Create prompt file + shell: bash + run: | + mkdir -p /tmp/claude-prompts + cat > /tmp/claude-prompts/claude-issue-triage-prompt.txt << 'EOF' + You're an issue triage assistant for GitHub issues. Your task is to analyze the issue and select appropriate labels from the provided list. + + IMPORTANT: Don't post any comments or messages to the issue. Your only action should be to apply labels. + + Issue Information: + - REPO: ${{ github.repository }} + - ISSUE_NUMBER: ${{ github.event.issue.number }} + + TASK OVERVIEW: + + 1. First, fetch the list of labels available in this repository by running: `gh label list`. Run exactly this command with nothing else. + + 2. Next, use the GitHub tools to get context about the issue: + - You have access to these tools: + - mcp__github__get_issue: Use this to retrieve the current issue's details including title, description, and existing labels + - mcp__github__get_issue_comments: Use this to read any discussion or additional context provided in the comments + - mcp__github__update_issue: Use this to apply labels to the issue (do not use this for commenting) + - mcp__github__search_issues: Use this to find similar issues that might provide context for proper categorization and to identify potential duplicate issues + - mcp__github__list_issues: Use this to understand patterns in how other issues are labeled + - Start by using mcp__github__get_issue to get the issue details + + 3. Analyze the issue content, considering: + - The issue title and description + - The type of issue (bug report, feature request, question, etc.) + - Technical areas mentioned + - Severity or priority indicators + - User impact + - Components affected + + 4. Select appropriate labels from the available labels list provided above: + - Choose labels that accurately reflect the issue's nature + - Be specific but comprehensive + - Select priority labels if you can determine urgency (high-priority, med-priority, or low-priority) + - Consider platform labels (android, ios) if applicable + - If you find similar issues using mcp__github__search_issues, consider using a "duplicate" label if appropriate. Only do so if the issue is a duplicate of another OPEN issue. + + 5. Apply the selected labels: + - Use mcp__github__update_issue to apply your selected labels + - DO NOT post any comments explaining your decision + - DO NOT communicate directly with users + - If no labels are clearly applicable, do not apply any labels + + IMPORTANT GUIDELINES: + - Be thorough in your analysis + - Only select labels from the provided list above + - DO NOT post any comments to the issue + - Your ONLY action should be to apply labels using mcp__github__update_issue + - It's okay to not add any labels if none are clearly applicable + EOF + + - name: Run Claude Code + uses: ./.github/actions/claude-code-action + with: + prompt_file: /tmp/claude-prompts/claude-issue-triage-prompt.txt + allowed_tools: "Bash(gh label list),mcp__github__get_issue,mcp__github__get_issue_comments,mcp__github__update_issue,mcp__github__search_issues,mcp__github__list_issues" + install_github_mcp: "true" + timeout_minutes: ${{ inputs.timeout_minutes }} + anthropic_api_key: ${{ inputs.anthropic_api_key }} + github_token: ${{ inputs.github_token }} diff --git a/.github/workflows/claude-issue-triage.yml b/.github/workflows/claude-issue-triage.yml new file mode 100644 index 00000000..e0abbd97 --- /dev/null +++ b/.github/workflows/claude-issue-triage.yml @@ -0,0 +1,23 @@ +name: Claude Issue Triage +description: "Automatically triage GitHub issues using Claude Code" + +on: + issues: + types: [opened] + +jobs: + triage-issue: + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + issues: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Run Claude Issue Triage + uses: ./.github/actions/claude-issue-triage-action + with: + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + github_token: ${{ secrets.GITHUB_TOKEN }}