@@ -236,3 +236,95 @@ jobs:
236236 # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
237237 # or https://docs.claude.com/en/docs/claude-code/cli-reference for available options
238238 claude_args : ' --allowedTools "Task,Skill,Read,Glob,Grep,Write,TodoWrite,mcp__cloudflare-docs__search_cloudflare_documentation,mcp__exa__get_code_context_exa,mcp__exa__web_search_exa,Bash(gh pr view:*),Bash(gh pr diff:*),Bash(gh repo view:*),Bash(gh api:*),Bash(git log:*),Bash(git cat-file:*),Bash(git rev-parse:*),Bash(jq:*)"'
239+
240+ - name : Minimize outdated reviews
241+ env :
242+ GH_TOKEN : ${{ github.token }}
243+ run : |
244+ # Minimize reviews that are outdated:
245+ # 1. Reviews where ALL inline threads are resolved
246+ # 2. Older reviews with NO threads (keep only the latest threadless review)
247+
248+ QUERY='query($owner: String!, $name: String!, $pr: Int!) {
249+ repository(owner: $owner, name: $name) {
250+ pullRequest(number: $pr) {
251+ reviews(first: 100) {
252+ nodes {
253+ id
254+ isMinimized
255+ createdAt
256+ author { login }
257+ }
258+ }
259+ reviewThreads(first: 100) {
260+ nodes {
261+ isResolved
262+ comments(first: 1) {
263+ nodes {
264+ pullRequestReview { id }
265+ }
266+ }
267+ }
268+ }
269+ }
270+ }
271+ }'
272+
273+ # Fetch data
274+ DATA=$(gh api graphql \
275+ -F owner="${{ github.repository_owner }}" \
276+ -F name="${{ github.event.repository.name }}" \
277+ -F pr=${{ github.event.pull_request.number }} \
278+ -f query="$QUERY")
279+
280+ # Write jq filter to file to avoid escaping issues
281+ cat > /tmp/minimize-filter.jq << 'JQEOF'
282+ .data.repository.pullRequest as $pr |
283+ # Only consider reviews from Claude bot
284+ ($pr.reviews.nodes | map(select(.author.login == "claude"))) as $claudeReviews |
285+ # Build set of review IDs that have threads
286+ ($pr.reviewThreads.nodes | map(
287+ select(.comments.nodes[0].pullRequestReview != null) |
288+ .comments.nodes[0].pullRequestReview.id
289+ ) | unique) as $reviewsWithThreads |
290+ # Build map of review_id -> { allResolved: bool }
291+ ($pr.reviewThreads.nodes | map(
292+ select(.comments.nodes[0].pullRequestReview != null) |
293+ {
294+ reviewId: .comments.nodes[0].pullRequestReview.id,
295+ isResolved: .isResolved
296+ }
297+ ) | group_by(.reviewId) | map({
298+ key: .[0].reviewId,
299+ value: { allResolved: (map(.isResolved) | all) }
300+ }) | from_entries) as $threadStatusByReview |
301+ # Find the latest Claude review (by createdAt) that has no threads
302+ (($claudeReviews | map(select(. as $r | $reviewsWithThreads | any(. == $r.id) | not)) | sort_by(.createdAt) | last) // null) as $latestThreadless |
303+ ($latestThreadless | if . then .id else null end) as $latestThreadlessId |
304+ # Select Claude reviews to minimize
305+ $claudeReviews[] |
306+ select(.isMinimized == false) |
307+ . as $review |
308+ select(
309+ # Case 1: Has threads and all are resolved
310+ ($threadStatusByReview[$review.id] != null and $threadStatusByReview[$review.id].allResolved == true) or
311+ # Case 2: No threads and not the latest threadless review
312+ (($reviewsWithThreads | any(. == $review.id) | not) and $review.id != $latestThreadlessId)
313+ ) |
314+ .id
315+ JQEOF
316+
317+ # Minimize each review
318+ echo "$DATA" | jq -r -f /tmp/minimize-filter.jq | while read -r id; do
319+ if [ -n "$id" ]; then
320+ echo "Minimizing review: $id"
321+ gh api graphql -f query='
322+ mutation($id: ID!) {
323+ minimizeComment(input: {subjectId: $id, classifier: OUTDATED}) {
324+ minimizedComment { isMinimized }
325+ }
326+ }' -f id="$id" || true
327+ fi
328+ done
329+
330+ echo "Done minimizing outdated reviews"
0 commit comments