diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml index 888ddd4..9242537 100644 --- a/.github/workflows/claude-code-review.yml +++ b/.github/workflows/claude-code-review.yml @@ -22,18 +22,23 @@ jobs: outputs: run_review: ${{ steps.check.outputs.run_review }} steps: - - name: Check for exact @claude review command + - name: Check for @claude review on its own line id: check env: COMMENT_BODY: ${{ github.event.comment.body }} run: | - body="$(python - <<'PY' + has_review_command="$(python - <<'PY' import os + body = os.environ.get("COMMENT_BODY", "") - print(" ".join(body.split())) + print( + "true" + if any(line.strip() == "@claude review" for line in body.splitlines()) + else "false" + ) PY )" - if [ "$body" = "@claude review" ]; then + if [ "$has_review_command" = "true" ]; then echo "run_review=true" >> "$GITHUB_OUTPUT" else echo "run_review=false" >> "$GITHUB_OUTPUT" @@ -44,7 +49,15 @@ jobs: if: | always() && ( - (github.event_name == 'pull_request' && github.event.pull_request.user.type != 'Bot') || + ( + github.event_name == 'pull_request' && + github.event.pull_request.user.type != 'Bot' && + github.event.pull_request.draft == false && + contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.pull_request.author_association) && + !contains(github.event.pull_request.labels.*.name, 'skip-ai-review') && + !contains(github.event.pull_request.labels.*.name, 'dependencies') && + !contains(github.event.pull_request.labels.*.name, 'chore') + ) || needs.detect-claude-command.outputs.run_review == 'true' ) runs-on: @@ -53,9 +66,10 @@ jobs: PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number }} permissions: contents: read - pull-requests: read + pull-requests: write issues: read id-token: write + actions: read steps: - name: Checkout repository @@ -65,13 +79,24 @@ jobs: - name: Run Claude Code Review id: claude-review - uses: anthropics/claude-code-action@v1 + uses: anthropics/claude-code-action@88c168b39e7e64da0286d812b6e9fbebb6708185 with: claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} + track_progress: >- + ${{ + ( + github.event_name == 'pull_request' && + contains(fromJson('["opened","synchronize","ready_for_review","reopened"]'), github.event.action) + ) || + ( + github.event_name == 'issues' && + contains(fromJson('["opened","edited","labeled","assigned"]'), github.event.action) + ) + }} plugin_marketplaces: 'https://github.com/anthropics/claude-code.git' plugins: 'code-review@claude-code-plugins' prompt: '/code-review:code-review ${{ github.repository }}/pull/${{ env.PR_NUMBER }}' # Tools based on CLAUDE.md development commands # Includes: uv (pytest, alembic, python), pnpm (check, lint, test), and gh CLI - claude_args: '--allowed-tools "Bash(uv run pytest:*),Bash(uv run alembic:*),Bash(uv run python:*),Bash(uv sync:*),Bash(pnpm install:*),Bash(pnpm check:*),Bash(pnpm lint:*),Bash(pnpm format:*),Bash(pnpm test:*),Bash(pnpm build:*),Bash(gh issue:*),Bash(gh pr:*),Bash(gh search:*)"' + claude_args: '--allowed-tools "mcp__github_inline_comment__create_inline_comment,Bash(uv run pytest:*),Bash(uv run alembic:*),Bash(uv run python:*),Bash(uv sync:*),Bash(pnpm install:*),Bash(pnpm check:*),Bash(pnpm lint:*),Bash(pnpm format:*),Bash(pnpm test:*),Bash(pnpm build:*),Bash(gh issue:*),Bash(gh pr:*),Bash(gh search:*)" --model claude-opus-4-6' diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index 1b303ca..a66103b 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -13,12 +13,12 @@ on: jobs: detect-non-claude-review: name: Skip Exact @claude Review Command - runs-on: ubuntu-latest permissions: {} + runs-on: ubuntu-latest outputs: run_claude: ${{ steps.check.outputs.run_claude }} steps: - - name: Skip exact @claude review command + - name: Skip comments with @claude review on its own line id: check env: EVENT_NAME: ${{ github.event_name }} @@ -28,13 +28,18 @@ jobs: echo "run_claude=true" >> "$GITHUB_OUTPUT" exit 0 fi - body="$(python - <<'PY' + has_review_command="$(python - <<'PY' import os + body = os.environ.get("COMMENT_BODY", "") - print(" ".join(body.split())) + print( + "true" + if any(line.strip() == "@claude review" for line in body.splitlines()) + else "false" + ) PY )" - if [ "$body" = "@claude review" ]; then + if [ "$has_review_command" = "true" ]; then echo "run_claude=false" >> "$GITHUB_OUTPUT" else echo "run_claude=true" >> "$GITHUB_OUTPUT" @@ -75,8 +80,8 @@ jobs: runs-on: ubuntu-latest permissions: contents: read - pull-requests: read - issues: read + pull-requests: write + issues: write id-token: write actions: read # Required for Claude to read CI results on PRs steps: @@ -87,7 +92,7 @@ jobs: - name: Run Claude Code id: claude - uses: anthropics/claude-code-action@v1 + uses: anthropics/claude-code-action@88c168b39e7e64da0286d812b6e9fbebb6708185 with: claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}