@@ -2,10 +2,31 @@ name: Claude Code Review
22
33on :
44 pull_request_target :
5- types : [opened, synchronize, ready_for_review, reopened]
5+ types : [opened, synchronize, ready_for_review, reopened, labeled]
6+ issue_comment :
7+ types : [created]
68
79jobs :
810 claude-review :
11+ if : >
12+ (
13+ github.event_name == 'pull_request_target' &&
14+ (
15+ github.event.action == 'opened' ||
16+ github.event.action == 'ready_for_review' ||
17+ github.event.action == 'reopened' ||
18+ github.event.action == 'synchronize' ||
19+ (
20+ github.event.action == 'labeled' &&
21+ github.event.label.name == 'claude-full-review'
22+ )
23+ )
24+ ) ||
25+ (
26+ github.event_name == 'issue_comment' &&
27+ github.event.issue.pull_request != null &&
28+ contains(github.event.comment.body, '@claude full review')
29+ )
930 runs-on : ubuntu-latest
1031 permissions :
1132 contents : read
@@ -20,12 +41,37 @@ jobs:
2041 sudo apt-get update
2142 sudo apt-get install -y unzip
2243
23- # IMPORTANT: checkout BASE repo only (safe on forks)
44+ # Base checkout only
2445 - name : Checkout base repo (safe)
2546 uses : actions/checkout@v4
2647 with :
2748 fetch-depth : 1
2849
50+ - name : Determine PR number and review mode
51+ id : mode
52+ shell : bash
53+ run : |
54+ if [[ "${{ github.event_name }}" == "pull_request_target" ]]; then
55+ PR_NUMBER="${{ github.event.pull_request.number }}"
56+ if [[ "${{ github.event.action }}" == "opened" || "${{ github.event.action }}" == "ready_for_review" || "${{ github.event.action }}" == "reopened" ]]; then
57+ REVIEW_MODE="full"
58+ elif [[ "${{ github.event.action }}" == "synchronize" ]]; then
59+ REVIEW_MODE="incremental"
60+ elif [[ "${{ github.event.action }}" == "labeled" && "${{ github.event.label.name }}" == "claude-full-review" ]]; then
61+ REVIEW_MODE="full"
62+ else
63+ REVIEW_MODE="full"
64+ fi
65+ elif [[ "${{ github.event_name }}" == "issue_comment" ]]; then
66+ PR_NUMBER="${{ github.event.issue.number }}"
67+ REVIEW_MODE="full"
68+ else
69+ REVIEW_MODE="full"
70+ fi
71+
72+ echo "pr_number=$PR_NUMBER" >> "$GITHUB_OUTPUT"
73+ echo "review_mode=${REVIEW_MODE:-full}" >> "$GITHUB_OUTPUT"
74+
2975 - name : Run Claude Code Review
3076 uses : anthropics/claude-code-action@v1
3177 with :
@@ -35,36 +81,100 @@ jobs:
3581 plugin_marketplaces : " https://github.com/anthropics/claude-code.git"
3682 plugins : " code-review@claude-code-plugins"
3783
38- # NOTE: do NOT use --dangerouslyDisableSandbox (it can crash the CLI).
39- # This flag is for non-interactive CI runs (bypasses approval prompts).
4084 claude_args : >
4185 --dangerously-skip-permissions
4286 --max-turns 90
4387 --allowedTools
4488 "Bash"
4589
4690 prompt : |
47- You are running in pull_request_target.
91+ You are running in pull_request_target / issue_comment automation.
92+
93+ REVIEW MODE: ${{ steps.mode.outputs.review_mode }}
94+ PR NUMBER: ${{ steps.mode.outputs.pr_number }}
95+
4896 DO NOT read or inspect any checked-out PR/fork code. Review ONLY using GitHub API/gh commands.
4997
5098 You may read local guidance ONLY from:
5199 - ./CLAUDE.md (root) if present
52100 - ./.claude/rules/*.md if present (max 10 files)
53101
54102 Keep tool calls minimal and in this order:
103+
104+ Phase 1 — Local guidance (base branch only, safe):
55105 1) ls -1 .claude/rules 2>/dev/null || true
56106 2) cat CLAUDE.md 2>/dev/null || true
57107 3) find .claude/rules -maxdepth 1 -name "*.md" -print | head -n 10 | xargs -I{} cat "{}" 2>/dev/null || true
58- 4) gh pr view ${{ github.event.pull_request.number }} --repo ${{ github.repository }} --json title,body,files,changedFiles,additions,deletions,headRefOid
59- 5) gh pr diff ${{ github.event.pull_request.number }} --repo ${{ github.repository }}
60- 6) Post ONE top-level PR comment titled "Claude Code Review", then STOP.
61-
62- Output format:
63- - Head SHA
64- - Files changed count + list up to 10 file paths
65- - Summary (3–6 bullets, minimal)
66- - Findings with file + line numbers when possible
67- - If no issues: 0–3 improvement opportunities (only if confident)
108+
109+ Phase 2 — PR metadata and diff:
110+ 4) gh pr view ${{ steps.mode.outputs.pr_number }} --repo ${{ github.repository }} --json title,body,files,changedFiles,additions,deletions,headRefOid,comments
111+ 5) gh pr diff ${{ steps.mode.outputs.pr_number }} --repo ${{ github.repository }}
112+
113+ Phase 3 — Full file context (read via GitHub API, NOT local checkout):
114+ After reviewing the diff, fetch full contents of changed files to understand
115+ surrounding context. This is critical for catching issues the diff alone hides
116+ (e.g., duplicate code, broken callers, missing cleanup, variable shadowing).
117+
118+ Use this pattern to fetch file contents at the PR head SHA:
119+ gh api repos/${{ github.repository }}/contents/{path}?ref={head_sha} --jq '.content' | base64 -d
120+
121+ Rules for Phase 3:
122+ - Get the head SHA from step 4's headRefOid field.
123+ - Fetch up to 15 changed files (skip files >500 lines or binary files).
124+ - Prioritize: source code (.fpp, .f90, .py, .yml) over docs/config.
125+ - For Fortran/Fypp files: also fetch files that the changed file imports
126+ (look for "use m_<name>" or "#:include" in the fetched content) if they
127+ seem relevant to the review. Limit to 5 additional related files.
128+ - Do NOT fetch files that are unchanged and unrelated to the diff.
129+ - If a file fetch fails (404, too large), skip it and continue.
130+
131+ Review policy:
132+ - FULL mode:
133+ - Review the current PR normally.
134+ - Post or update ONE top-level PR comment titled "Claude Code Review".
135+ - INCREMENTAL mode:
136+ - Find the most recent prior Claude review comment on this PR.
137+ - Look for a hidden marker in the form:
138+ <!-- claude-review: reviewed_sha=<sha>; mode=<mode> -->
139+ - Compare the prior reviewed SHA to the current head SHA.
140+ - Review ONLY for newly introduced issues since the previous Claude-reviewed SHA.
141+ - DO NOT repeat earlier findings.
142+ - DO NOT restate the full PR summary.
143+ - If there are no new high-confidence findings, DO NOT post a new comment. STOP.
144+ - If there are new findings, update the existing Claude review comment if possible; otherwise post one new top-level comment.
145+
146+ Re-review policy:
147+ - A full review is explicitly requested only when:
148+ - the workflow was triggered by PR label "claude-full-review", or
149+ - the workflow was triggered by an issue comment containing "@claude full review"
150+
151+ Output format for FULL mode:
152+ Claude Code Review
153+
154+ Head SHA: <sha>
155+
156+ Files changed:
157+ - <count>
158+ - <up to 10 paths>
159+
160+ Summary:
161+ - <3-6 minimal bullets>
162+
163+ Findings:
164+ - <file + line numbers when possible>
165+ - <minimal, high-confidence only>
166+
167+ Output format for INCREMENTAL mode:
168+ Claude Code Review
169+
170+ Incremental review from: <previous_sha>
171+ Head SHA: <current_sha>
172+
173+ New findings since last Claude review:
174+ - <only genuinely new issues, file + line numbers when possible>
175+
176+ When posting a comment, include this hidden marker at the end:
177+ <!-- claude-review: reviewed_sha=<current_head_sha>; mode=${{ steps.mode.outputs.review_mode }} -->
68178
69179 If posting is blocked, write the full review to the GitHub Actions job summary instead, then STOP.
70180
0 commit comments