From 8be6893ce8857e244241aaa4bbfa6196a9451ba7 Mon Sep 17 00:00:00 2001 From: janhesters Date: Tue, 17 Mar 2026 10:53:19 +0100 Subject: [PATCH 1/3] ci: add automated PR review workflow using Claude Adds a GitHub Actions workflow that automatically reviews PRs using Claude Code with the aidd-review skill. Posts inline comments and a summary comment on each non-draft PR. --- .github/workflows/aidd-review-claude.yml | 105 +++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 .github/workflows/aidd-review-claude.yml diff --git a/.github/workflows/aidd-review-claude.yml b/.github/workflows/aidd-review-claude.yml new file mode 100644 index 00000000..e35bb01b --- /dev/null +++ b/.github/workflows/aidd-review-claude.yml @@ -0,0 +1,105 @@ +name: AIDD Review (Claude) + +on: + pull_request: + types: [opened, ready_for_review, reopened] + +permissions: + contents: read + pull-requests: write + issues: write + id-token: write + +concurrency: + group: aidd-review-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + aidd-review: + if: github.event.pull_request.draft == false + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - uses: anthropics/claude-code-action@v1 + continue-on-error: true + with: + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + classify_inline_comments: false + prompt: | + # Automated PR Review + + Act as a top-tier principal software engineer reviewing PR #${{ github.event.pull_request.number }} + in ${{ github.repository }}. + + Constraints { + Post inline comments only — do NOT post a summary comment. + Do NOT emit review text as plain messages between tool calls. + A post-processing step extracts your final message as the summary comment. + } + + ReviewProcess { + 1. Run `gh pr diff ${{ github.event.pull_request.number }}` to get the full diff. + 2. Read ai/skills/aidd-review/SKILL.md and relevant reference files it + points to. Execute the review process exactly as the skill defines it. + 3. For file-specific findings, call + mcp__github_inline_comment__create_inline_comment with the file path, + line number, recommendation, and confirmed=true. + match (tool availability) { + case (unavailable or call fails) => run: + echo "::warning::Inline comment MCP tool unavailable — falling back to summary" + default => post inline comment + } + 4. As your FINAL message, write the full review summary in markdown. + Follow the skill's output format. Be thorough — do not abbreviate. + At the very end, add a "Quick fix prompt" section with a fenced code + block (triple backticks) containing a standalone prompt a developer + can copy-paste into Claude Code to fix all findings: + + ### Quick fix prompt + ```text + Fix the following review findings in PR #: + 1. : + 2. : + ... + ``` + } + claude_args: | + --model claude-sonnet-4-6 --max-turns 20 --allowedTools "Read,Glob,Grep,Bash(gh pr diff:*),Bash(gh pr view:*),Bash(echo:*),mcp__github_inline_comment__create_inline_comment" --disallowedTools "Task,Write,Edit" + + - name: Post summary comment + if: always() + env: + GH_TOKEN: ${{ github.token }} + run: | + LOG="/home/runner/work/_temp/claude-execution-output.json" + if [ ! -f "$LOG" ]; then + echo "::warning::No execution log found — skipping summary comment" + exit 0 + fi + + # Extract the last assistant message as the summary + BODY=$(python3 -c " + import json, sys + with open('$LOG') as f: + entries = json.load(f) + # Walk backwards to find the last assistant text message + for entry in reversed(entries): + if entry.get('type') == 'assistant': + msg = entry.get('message', {}) + for block in msg.get('content', []): + if block.get('type') == 'text': + print(block['text']) + sys.exit(0) + print('Review completed but no summary was generated.') + ") + + gh pr comment "${{ github.event.pull_request.number }}" --body "$BODY" + + - uses: actions/upload-artifact@v4 + if: always() + with: + name: claude-review-log + path: /home/runner/work/_temp/claude-execution-output.json From d3b8aa5acf5766761f699b7145ca7d61776c2f02 Mon Sep 17 00:00:00 2001 From: janhesters Date: Wed, 18 Mar 2026 17:33:00 +0100 Subject: [PATCH 2/3] ci: address PR review feedback for aidd-review workflow - Add timeout-minutes: 30 to prevent runaway execution - Change fetch-depth to 0 for full git history (churn analysis) - Add setup-node + npm install so Claude can run npx aidd churn - Add Bash(npx:*) to allowedTools - Add skip-review label opt-out for cost control - Use steps.claude.outputs.execution_file instead of hardcoded log path - Update actions/checkout to v6 and actions/upload-artifact to v7 --- .github/workflows/aidd-review-claude.yml | 25 ++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/.github/workflows/aidd-review-claude.yml b/.github/workflows/aidd-review-claude.yml index e35bb01b..44133af0 100644 --- a/.github/workflows/aidd-review-claude.yml +++ b/.github/workflows/aidd-review-claude.yml @@ -16,14 +16,23 @@ concurrency: jobs: aidd-review: - if: github.event.pull_request.draft == false + if: github.event.pull_request.draft == false && !contains(github.event.pull_request.labels.*.name, 'skip-review') runs-on: ubuntu-latest + timeout-minutes: 30 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: - fetch-depth: 1 + fetch-depth: 0 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + + - run: npm install + name: Install dependencies - uses: anthropics/claude-code-action@v1 + id: claude continue-on-error: true with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} @@ -67,15 +76,15 @@ jobs: ``` } claude_args: | - --model claude-sonnet-4-6 --max-turns 20 --allowedTools "Read,Glob,Grep,Bash(gh pr diff:*),Bash(gh pr view:*),Bash(echo:*),mcp__github_inline_comment__create_inline_comment" --disallowedTools "Task,Write,Edit" + --model claude-sonnet-4-6 --max-turns 20 --allowedTools "Read,Glob,Grep,Bash(gh pr diff:*),Bash(gh pr view:*),Bash(echo:*),Bash(npx:*),mcp__github_inline_comment__create_inline_comment" --disallowedTools "Task,Write,Edit" - name: Post summary comment if: always() env: GH_TOKEN: ${{ github.token }} run: | - LOG="/home/runner/work/_temp/claude-execution-output.json" - if [ ! -f "$LOG" ]; then + LOG="${{ steps.claude.outputs.execution_file }}" + if [ -z "$LOG" ] || [ ! -f "$LOG" ]; then echo "::warning::No execution log found — skipping summary comment" exit 0 fi @@ -98,8 +107,8 @@ jobs: gh pr comment "${{ github.event.pull_request.number }}" --body "$BODY" - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 if: always() with: name: claude-review-log - path: /home/runner/work/_temp/claude-execution-output.json + path: ${{ steps.claude.outputs.execution_file }} From e49e5b43ecb88ecdc2356439be7093570dd4575f Mon Sep 17 00:00:00 2001 From: janhesters Date: Wed, 18 Mar 2026 18:06:26 +0100 Subject: [PATCH 3/3] ci: scope npx allowedTools to aidd only Tighten Bash(npx:*) to Bash(npx aidd:*) to reduce prompt-injection blast radius from arbitrary npx execution. --- .github/workflows/aidd-review-claude.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/aidd-review-claude.yml b/.github/workflows/aidd-review-claude.yml index 44133af0..4566fc81 100644 --- a/.github/workflows/aidd-review-claude.yml +++ b/.github/workflows/aidd-review-claude.yml @@ -76,7 +76,7 @@ jobs: ``` } claude_args: | - --model claude-sonnet-4-6 --max-turns 20 --allowedTools "Read,Glob,Grep,Bash(gh pr diff:*),Bash(gh pr view:*),Bash(echo:*),Bash(npx:*),mcp__github_inline_comment__create_inline_comment" --disallowedTools "Task,Write,Edit" + --model claude-sonnet-4-6 --max-turns 20 --allowedTools "Read,Glob,Grep,Bash(gh pr diff:*),Bash(gh pr view:*),Bash(echo:*),Bash(npx aidd:*),mcp__github_inline_comment__create_inline_comment" --disallowedTools "Task,Write,Edit" - name: Post summary comment if: always()