name: Claude Code on: pull_request: types: [opened, synchronize, ready_for_review, reopened] paths-ignore: - '.github/workflows/**' - '*.md' - 'docs/**' issue_comment: types: [created] pull_request_review_comment: types: [created] issues: types: [opened, assigned] pull_request_review: types: [submitted] jobs: # Automatic PR review (can fix linting issues and push) pr-review: if: github.event_name == 'pull_request' runs-on: ubuntu-latest permissions: contents: write pull-requests: write issues: read id-token: write actions: read defaults: run: working-directory: django/aiservice steps: - name: Checkout repository uses: actions/checkout@v6 with: fetch-depth: 0 ref: ${{ github.event.pull_request.head.ref }} - name: Install uv uses: astral-sh/setup-uv@v8.1.0 - name: Install dependencies run: | uv venv --seed uv sync - name: Configure AWS Credentials (OIDC) uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} aws-region: us-east-1 - name: Run Claude Code id: claude uses: anthropics/claude-code-action@v1 with: use_bedrock: "true" use_sticky_comment: true allowed_bots: "claude[bot],codeflash-ai[bot]" prompt: | REPO: ${{ github.repository }} PR NUMBER: ${{ github.event.pull_request.number }} EVENT: ${{ github.event.action }} IMPORTANT: This repo has Python code in `django/aiservice/`. Run uv/prek commands from that directory. ## STEP 1: Run prek and mypy checks, fix issues First, run these checks on files changed in this PR: 1. `cd django/aiservice && uv run prek run --from-ref origin/main` - linting/formatting issues 2. `cd django/aiservice && uv run mypy ` - type checking issues If there are prek issues: - For SAFE auto-fixable issues (formatting, import sorting, trailing whitespace, etc.), run `cd django/aiservice && uv run prek run --from-ref origin/main` again to auto-fix them - For issues that prek cannot auto-fix, do NOT attempt to fix them manually — report them as remaining issues in your summary If there are mypy issues: - Fix type annotation issues (missing return types, Optional/None unions, import errors for type hints, incorrect types) - Do NOT add `type: ignore` comments - always fix the root cause After fixing issues: - Stage the fixed files with `git add` - Commit with message "style: auto-fix linting issues" or "fix: resolve mypy type errors" as appropriate - Push the changes with `git push` IMPORTANT - Verification after fixing: - After committing fixes, run `cd django/aiservice && uv run prek run --from-ref origin/main` ONE MORE TIME to verify all issues are resolved - If errors remain, either fix them or report them honestly as unfixed in your summary - NEVER claim issues are fixed without verifying. If you cannot fix an issue, say so Do NOT attempt to fix: - Type errors that require logic changes or refactoring - Complex generic type issues - Anything that could change runtime behavior ## STEP 2: Review the PR ${{ github.event.action == 'synchronize' && 'This is a RE-REVIEW after new commits. First, get the list of changed files in this latest push using `gh pr diff`. Review ONLY the changed files. Check ALL existing review comments and resolve ones that are now fixed.' || 'This is the INITIAL REVIEW.' }} Review this PR focusing ONLY on: 1. Critical bugs or logic errors 2. Security vulnerabilities 3. Breaking API changes 4. Test failures (methods with typos that wont run) IMPORTANT: - First check existing review comments using `gh api repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/comments`. For each existing comment, check if the issue still exists in the current code. - If an issue is fixed, use `gh api --method PATCH repos/${{ github.repository }}/pulls/comments/COMMENT_ID -f body="✅ Fixed in latest commit"` to resolve it. - Only create NEW inline comments for HIGH-PRIORITY issues found in changed files. - Limit to 5-7 NEW comments maximum per review. - Use CLAUDE.md for project-specific guidance. - Use `mcp__github_inline_comment__create_inline_comment` sparingly for critical code issues only. ## STEP 3: Coverage analysis Analyze test coverage for changed files: 1. Get the list of Python files changed in this PR (excluding tests): `git diff --name-only origin/main...HEAD -- '*.py' | grep -v test` 2. Run tests with coverage on the PR branch (from django/aiservice): `cd django/aiservice && uv run coverage run -m pytest -q --tb=no` `cd django/aiservice && uv run coverage json -o coverage-pr.json` 3. Get coverage for changed files only: `cd django/aiservice && uv run coverage report --include=""` 4. Compare with main branch coverage: - Checkout main: `git checkout origin/main` - Run coverage: `cd django/aiservice && uv run coverage run -m pytest -q --tb=no && uv run coverage json -o coverage-main.json` - Checkout back: `git checkout -` 5. Analyze the diff to identify: - NEW FILES: Files that don't exist on main (require good test coverage) - MODIFIED FILES: Files with changes (changes must be covered by tests) 6. Report in PR comment with a markdown table: - Coverage % for each changed file (PR vs main) - Overall coverage change - For NEW files: Flag if coverage is below 75% - For MODIFIED files: Flag if the changed lines are not covered by tests - Flag if overall coverage decreased Coverage requirements: - New implementations/files: Must have ≥75% test coverage - Modified code: Changed lines should be exercised by existing or new tests - No coverage regressions: Overall coverage should not decrease ## STEP 4: Post ONE consolidated summary comment CRITICAL: You must post exactly ONE summary comment containing ALL results (pre-commit, review, coverage). DO NOT post multiple separate comments. Use this format: ``` ## PR Review Summary ### Prek Checks [status and any fixes made] ### Code Review [critical issues found, if any] ### Test Coverage [coverage table and analysis] --- *Last updated: * ``` To ensure only ONE comment exists: 1. Find existing claude[bot] comment: `gh api repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments --jq '.[] | select(.user.login == "claude[bot]") | .id' | head -1` 2. If found, UPDATE it: `gh api --method PATCH repos/${{ github.repository }}/issues/comments/ -f body=""` 3. If not found, CREATE: `gh pr comment ${{ github.event.pull_request.number }} --body ""` 4. Delete any OTHER claude[bot] comments to clean up duplicates: `gh api repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments --jq '.[] | select(.user.login == "claude[bot]") | .id' | tail -n +2 | xargs -I {} gh api --method DELETE repos/${{ github.repository }}/issues/comments/{}` claude_args: '--model us.anthropic.claude-sonnet-4-6 --allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr list:*),Bash(gh issue view:*),Bash(gh issue list:*),Bash(gh api:*),Bash(cd django/aiservice*),Bash(uv run prek *),Bash(uv run mypy *),Bash(uv run coverage *),Bash(uv run pytest *),Bash(git status*),Bash(git add *),Bash(git commit *),Bash(git push*),Bash(git diff *),Bash(git checkout *),Read,Glob,Grep,Edit"' additional_permissions: | actions: read env: DATABASE_URL: ${{ secrets.DATABASE_URL }} DJANGO_SETTINGS_MODULE: aiservice.settings # @claude mentions (can edit and push) claude-mention: if: | (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) || (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) || (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) || (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude'))) runs-on: ubuntu-latest permissions: contents: write pull-requests: write issues: read id-token: write actions: read defaults: run: working-directory: django/aiservice steps: - name: Get PR head ref id: pr-ref working-directory: . env: GH_TOKEN: ${{ github.token }} run: | # For issue_comment events, we need to fetch the PR info if [ "${{ github.event_name }}" = "issue_comment" ]; then PR_REF=$(gh api repos/${{ github.repository }}/pulls/${{ github.event.issue.number }} --jq '.head.ref') echo "ref=$PR_REF" >> $GITHUB_OUTPUT else echo "ref=${{ github.event.pull_request.head.ref || github.head_ref }}" >> $GITHUB_OUTPUT fi - name: Checkout repository uses: actions/checkout@v6 with: fetch-depth: 0 ref: ${{ steps.pr-ref.outputs.ref }} - name: Install uv uses: astral-sh/setup-uv@v8.1.0 - name: Install dependencies run: | uv venv --seed uv sync - name: Configure AWS Credentials (OIDC) uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} aws-region: us-east-1 - name: Run Claude Code id: claude uses: anthropics/claude-code-action@v1 with: use_bedrock: "true" claude_args: '--model us.anthropic.claude-sonnet-4-6 --allowedTools "Read,Edit,Write,Glob,Grep,Bash(git status*),Bash(git diff*),Bash(git add *),Bash(git commit *),Bash(git push*),Bash(git log*),Bash(git merge*),Bash(git fetch*),Bash(git checkout*),Bash(git branch*),Bash(cd django/aiservice*),Bash(uv run prek *),Bash(prek *),Bash(uv run ruff *),Bash(uv run pytest *),Bash(uv run mypy *),Bash(uv run coverage *),Bash(gh pr comment*),Bash(gh pr view*),Bash(gh pr diff*),Bash(gh pr merge*),Bash(gh pr close*)"' additional_permissions: | actions: read