From a62d36096f11de7b4613b53e3f75c09bf428f44f Mon Sep 17 00:00:00 2001 From: Paul Hernandez Date: Fri, 4 Apr 2025 13:02:47 -0500 Subject: [PATCH 1/2] Add example file with bugs for testing suggest mode --- README.md | 1 + scripts/test-sample.js | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 scripts/test-sample.js diff --git a/README.md b/README.md index 00f3c2f..60af2ea 100644 --- a/README.md +++ b/README.md @@ -135,6 +135,7 @@ Sends a query directly to Claude and saves the response to a file without PR con - `claude: Are there any security issues in these changes?` - `claude: How would you refactor this to be more maintainable?` - `claude: What tests should be added for this code?` +- `claude: Analyze the performance implications of these changes` ### Suggest Mode Examples (prefix: `claude-suggest:`) diff --git a/scripts/test-sample.js b/scripts/test-sample.js new file mode 100644 index 0000000..b126de7 --- /dev/null +++ b/scripts/test-sample.js @@ -0,0 +1,28 @@ +// Example file with a few bugs for testing claude-suggest +function calculateTotal(items) { + let total = 0; + for (let i = 0; i < items.length; i++) { + // Bug 1: No null check before accessing properties + total += items[i].price * items[i].quantity; + } + return total; +} + +// Bug 2: Potential division by zero +function calculateAverage(values) { + const sum = values.reduce((acc, val) => acc + val, 0); + return sum / values.length; +} + +// Bug 3: No error handling +async function fetchUserData(userId) { + const response = await fetch(`/api/users/${userId}`); + const data = await response.json(); + return data; +} + +module.exports = { + calculateTotal, + calculateAverage, + fetchUserData +}; \ No newline at end of file From 3c0253f4e3892056fe462615335792814016e0d3 Mon Sep 17 00:00:00 2001 From: Paul Hernandez Date: Fri, 4 Apr 2025 13:20:58 -0500 Subject: [PATCH 2/2] Add true GitHub suggestion support with suggest-review mode --- .github/workflows/claude-code.yml | 85 ++++++++++++++++++++++++ README.md | 25 ++++--- action.yml | 20 +++++- package.json | 7 +- scripts/suggest-review-mode.sh | 106 ++++++++++++++++++++++++++++++ 5 files changed, 229 insertions(+), 14 deletions(-) create mode 100755 scripts/suggest-review-mode.sh diff --git a/.github/workflows/claude-code.yml b/.github/workflows/claude-code.yml index 6fdfa42..cb7cce4 100644 --- a/.github/workflows/claude-code.yml +++ b/.github/workflows/claude-code.yml @@ -3,8 +3,11 @@ name: Claude Code Integration on: issue_comment: types: [created] + pull_request_review_comment: + types: [created] jobs: + # Handle PR comments process-pr-review: runs-on: ubuntu-latest if: ${{ github.event_name == 'issue_comment' && github.event.issue.pull_request && startsWith(github.event.comment.body, 'claude:') }} @@ -67,4 +70,86 @@ jobs: pr-number: ${{ steps.pr.outputs.number }} feedback: ${{ steps.pr.outputs.feedback }} anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }} + github-token: ${{ secrets.GITHUB_TOKEN }} + + # Handle code review comments + process-review-comment: + runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request_review_comment' && startsWith(github.event.comment.body, 'claude:') }} + permissions: + contents: read + pull-requests: write + issues: write + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Get PR and comment details + id: details + run: | + PR_NUMBER="${{ github.event.pull_request.number }}" + FEEDBACK="${{ github.event.comment.body }}" + # Remove the "claude:" prefix + FEEDBACK="${FEEDBACK#claude:}" + COMMENT_ID="${{ github.event.comment.id }}" + FILE_PATH="${{ github.event.comment.path }}" + LINE="${{ github.event.comment.line }}" + + echo "number=$PR_NUMBER" >> $GITHUB_OUTPUT + echo "feedback=$FEEDBACK" >> $GITHUB_OUTPUT + echo "comment_id=$COMMENT_ID" >> $GITHUB_OUTPUT + echo "file_path=$FILE_PATH" >> $GITHUB_OUTPUT + echo "line=$LINE" >> $GITHUB_OUTPUT + + - name: Process with Claude Code for code review comment + uses: fractureinc/claude-code-github-action@v0.2.0 + with: + mode: 'review' + pr-number: ${{ steps.details.outputs.number }} + feedback: ${{ steps.details.outputs.feedback }} + anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }} + github-token: ${{ secrets.GITHUB_TOKEN }} + + process-suggest-review-comment: + runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request_review_comment' && startsWith(github.event.comment.body, 'claude-suggest:') }} + permissions: + contents: read + pull-requests: write + issues: write + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Get PR and comment details + id: details + run: | + PR_NUMBER="${{ github.event.pull_request.number }}" + FEEDBACK="${{ github.event.comment.body }}" + # Remove the "claude-suggest:" prefix + FEEDBACK="${FEEDBACK#claude-suggest:}" + COMMENT_ID="${{ github.event.comment.id }}" + FILE_PATH="${{ github.event.comment.path }}" + LINE="${{ github.event.comment.line }}" + + echo "number=$PR_NUMBER" >> $GITHUB_OUTPUT + echo "feedback=$FEEDBACK" >> $GITHUB_OUTPUT + echo "comment_id=$COMMENT_ID" >> $GITHUB_OUTPUT + echo "file_path=$FILE_PATH" >> $GITHUB_OUTPUT + echo "line=$LINE" >> $GITHUB_OUTPUT + + - name: Process with Claude Code Suggestions for code review + uses: fractureinc/claude-code-github-action@v0.2.1 + with: + mode: 'suggest-review' + pr-number: ${{ steps.details.outputs.number }} + feedback: ${{ steps.details.outputs.feedback }} + file-path: ${{ steps.details.outputs.file_path }} + line-number: ${{ steps.details.outputs.line }} + comment-id: ${{ steps.details.outputs.comment_id }} + anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }} github-token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/README.md b/README.md index 60af2ea..2e4be1d 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,8 @@ This GitHub Action integrates Claude Code in your GitHub workflows, enabling AI- ## Features - Process PR comments prefixed with "claude:" for general analysis -- Process PR comments prefixed with "claude-suggest:" for clickable code suggestions +- Process PR comments prefixed with "claude-suggest:" for code suggestions +- Process code review comments to provide in-line analysis and suggestions - Provide rich context about the PR to Claude, including file diffs - Get AI-powered code analysis and suggestions - Create GitHub-compatible suggested changes that can be applied with one click @@ -48,7 +49,7 @@ jobs: echo "feedback=$FEEDBACK" >> $GITHUB_OUTPUT - name: Process with Claude Code - uses: fractureinc/claude-code-github-action@v0.2.0 + uses: fractureinc/claude-code-github-action@v0.2.1 with: mode: 'review' pr-number: ${{ steps.pr.outputs.number }} @@ -80,7 +81,7 @@ jobs: echo "feedback=$FEEDBACK" >> $GITHUB_OUTPUT - name: Process with Claude Code Suggestions - uses: fractureinc/claude-code-github-action@v0.2.0 + uses: fractureinc/claude-code-github-action@v0.2.1 with: mode: 'suggest' pr-number: ${{ steps.pr.outputs.number }} @@ -104,7 +105,7 @@ jobs: ## Enhanced Context for Claude -With version 0.2.0, Claude now receives complete context for your PRs, including: +With version 0.2.1, Claude now receives complete context for your PRs, including: - PR metadata (title, description, branch info) - List of all files changed @@ -120,7 +121,11 @@ Standard mode that provides Claude's analysis and feedback about your PR changes ### Suggest Mode (`mode: 'suggest'`) -Creates GitHub-compatible suggested changes that can be applied with one click directly from the PR interface. +Creates suggested changes in a PR comment that outline potential code improvements. + +### Suggest Review Mode (`mode: 'suggest-review'`) + +Creates true GitHub-compatible suggestions that can be applied with one click directly from the code review interface. These are attached to specific lines of code. ### Direct Mode (`mode: 'direct'`) @@ -146,17 +151,19 @@ Sends a query directly to Claude and saves the response to a file without PR con ## How It Works -1. The action is triggered when a comment with the appropriate prefix is detected on a PR -2. The action extracts the PR number and the user's query +1. The action is triggered when a comment with the appropriate prefix is detected (either on the PR or in code review) +2. The action extracts the PR number, user's query, and (for code review comments) the file path and line number 3. The repository is checked out to provide full code context 4. Using GitHub CLI, the action fetches comprehensive information about the PR including: - PR metadata - - List of files changed + - List of files changed - Complete diff of all changes + - For code review comments, specific file content and context around the commented line 5. This rich context is provided to Claude along with the user's query 6. Claude processes the information and provides a helpful response 7. For review mode: The response is posted as a comment on the PR -8. For suggest mode: Claude formats responses as GitHub suggested changes that can be applied with one click +8. For suggest mode: Claude formats responses with code suggestions in the PR comment +9. For suggest-review mode: Claude creates true GitHub-compatible suggestions attached to specific lines of code ## Permissions diff --git a/action.yml b/action.yml index 041f93c..6e16989 100644 --- a/action.yml +++ b/action.yml @@ -6,7 +6,7 @@ branding: inputs: mode: - description: 'The mode to run the action in (review, pr-comment, suggest, direct)' + description: 'The mode to run the action in (review, suggest, suggest-review, direct)' required: true default: 'review' pr-number: @@ -15,6 +15,15 @@ inputs: feedback: description: 'The feedback text from the comment' required: false + file-path: + description: 'Path to the file being reviewed (for suggest-review mode)' + required: false + line-number: + description: 'Line number in the file (for suggest-review mode)' + required: false + comment-id: + description: 'GitHub comment ID to reply to (for suggest-review mode)' + required: false anthropic-api-key: description: 'Anthropic API key for Claude access' required: true @@ -63,4 +72,11 @@ runs: shell: bash run: | chmod +x ${{ github.action_path }}/scripts/direct-mode.sh - ${{ github.action_path }}/scripts/direct-mode.sh "${{ inputs.feedback }}" "${{ inputs.anthropic-api-key }}" "${{ inputs.output-file }}" \ No newline at end of file + ${{ github.action_path }}/scripts/direct-mode.sh "${{ inputs.feedback }}" "${{ inputs.anthropic-api-key }}" "${{ inputs.output-file }}" + + - name: Process In-line Code Suggestions + if: inputs.mode == 'suggest-review' + shell: bash + run: | + chmod +x ${{ github.action_path }}/scripts/suggest-review-mode.sh + ${{ github.action_path }}/scripts/suggest-review-mode.sh "${{ inputs.pr-number }}" "${{ inputs.feedback }}" "${{ inputs.file-path }}" "${{ inputs.line-number }}" "${{ inputs.comment-id }}" "${{ inputs.anthropic-api-key }}" "${{ inputs.github-token }}" \ No newline at end of file diff --git a/package.json b/package.json index bda8596..18afcef 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "claude-code-github-action", - "version": "0.2.0", - "description": "GitHub action for Claude Code Integration in PR comments, reviews and code suggestions", + "version": "0.2.1", + "description": "GitHub action for Claude Code Integration in PR comments, reviews and inline code suggestions", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" @@ -19,7 +19,8 @@ "ai", "review", "pull-request", - "suggestions" + "suggestions", + "inline" ], "author": "Fracture Inc", "license": "MIT", diff --git a/scripts/suggest-review-mode.sh b/scripts/suggest-review-mode.sh new file mode 100755 index 0000000..3982826 --- /dev/null +++ b/scripts/suggest-review-mode.sh @@ -0,0 +1,106 @@ +#!/bin/bash +set -e + +# Get input parameters +PR_NUMBER=$1 +FEEDBACK=$2 +FILE_PATH=$3 +LINE_NUMBER=$4 +COMMENT_ID=$5 +ANTHROPIC_API_KEY=$6 +GITHUB_TOKEN=$7 + +# Set up authentication +echo "$GITHUB_TOKEN" | gh auth login --with-token +export ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" + +# Create a temp file for Claude's response +RESPONSE_FILE=$(mktemp) + +# Get PR details using GitHub CLI +echo "Fetching PR details for PR #$PR_NUMBER" +PR_DETAILS=$(gh pr view $PR_NUMBER --json title,body,baseRefName,headRefName,additions,deletions,changedFiles,state) + +PR_TITLE=$(echo "$PR_DETAILS" | jq -r '.title') +PR_BODY=$(echo "$PR_DETAILS" | jq -r '.body') +PR_BASE=$(echo "$PR_DETAILS" | jq -r '.baseRefName') +PR_HEAD=$(echo "$PR_DETAILS" | jq -r '.headRefName') +PR_STATE=$(echo "$PR_DETAILS" | jq -r '.state') + +# Checkout PR branch for full repo context +echo "Checking out PR branch: $PR_HEAD" +git fetch origin pull/$PR_NUMBER/head:$PR_HEAD +git checkout $PR_HEAD + +# Get the specific file content +FILE_CONTENT=$(cat "$FILE_PATH") + +# Get a context window around the line in question (10 lines before and after) +LINE_START=$((LINE_NUMBER - 10)) +if [ $LINE_START -lt 1 ]; then + LINE_START=1 +fi +LINE_END=$((LINE_NUMBER + 10)) +CONTEXT_CONTENT=$(sed -n "${LINE_START},${LINE_END}p" "$FILE_PATH") + +# Get repo information +REPO_INFO=$(gh repo view --json name,description,defaultBranchRef,languages) +REPO_NAME=$(echo "$REPO_INFO" | jq -r '.name') +REPO_DESC=$(echo "$REPO_INFO" | jq -r '.description') +REPO_DEFAULT_BRANCH=$(echo "$REPO_INFO" | jq -r '.defaultBranchRef.name') +REPO_LANGUAGES=$(echo "$REPO_INFO" | jq -r '.languages[].name' | tr '\n' ', ' | sed 's/,$//') + +# Build the prompt for Claude to generate a suggested change +PROMPT=$(cat < "$RESPONSE_FILE" + +# Reply to the specific comment with Claude's suggestion +echo "Posting Claude's suggested change as a reply to the comment" +gh api --method POST "/repos/:owner/:repo/pulls/$PR_NUMBER/comments/$COMMENT_ID/replies" \ + -F body="@$RESPONSE_FILE" + +# Clean up +rm -f "$RESPONSE_FILE" + +echo "Claude's in-line suggestion posted successfully!" \ No newline at end of file