Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 0 additions & 44 deletions .github/workflows/claude-code-review.yml

This file was deleted.

70 changes: 70 additions & 0 deletions .github/workflows/claude-mention.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
name: Claude Code

# Reactive: does nothing until someone writes @claude. Pairs with claude-pr-review.yml.
#
# >>> DO NOT MERGE THESE TWO INTO ONE JOB. <<<
# They look redundant and are not. The shared action for THIS file supplies no `prompt`,
# which is what keeps claude-code-action in TAG mode. Supplying one -- as the review
# action does -- puts it in AGENT mode for every event it sees, where an @claude comment
# gets no PR context, no tracking comment, and nothing posted back.
#
# NOTE: `issue_comment` workflows always run from the default branch, so edits to this
# file do nothing until they are merged.

on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
issues:
types: [opened, assigned]
pull_request_review:
types: [submitted]

jobs:
claude:
# >>> THIS GUARD IS LOAD-BEARING ON A PUBLIC REPOSITORY. <<<
# Two conditions, both required, both checked per-event.
#
# 1. author_association of the person who ACTUALLY TRIGGERED IT. This job has
# `contents: write` and blanket `Bash`, so without this check any passer-by who
# types @claude gets an arbitrary-code-execution agent run on the maintainer's
# OAuth token. The pairing matters: on `issue_comment`,
# `github.event.issue.author_association` is the issue's *opener*, not the
# commenter, so checking that field here would let a stranger trigger a run
# merely by commenting on a maintainer's own issue. OWNER covers the repository
# owner; COLLABORATOR/MEMBER cover anyone later given push access.
# 2. the @claude mention itself.
#
# The guard cannot live in the shared action: by the time a composite action runs,
# the runner is already up and the token already minted.
if: |
(github.event_name == 'issue_comment' &&
contains(github.event.comment.body, '@claude') &&
contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) ||
(github.event_name == 'pull_request_review_comment' &&
contains(github.event.comment.body, '@claude') &&
contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) ||
(github.event_name == 'pull_request_review' &&
contains(github.event.review.body, '@claude') &&
contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.review.author_association)) ||
(github.event_name == 'issues' &&
(contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')) &&
contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.issue.author_association))
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: write
id-token: write
actions: read # Required for Claude to read CI results on PRs

steps:
- name: Checkout repository
uses: actions/checkout@v7
with:
fetch-depth: 1

- uses: jantman/github-actions-workflows/claude-mention@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
48 changes: 48 additions & 0 deletions .github/workflows/claude-pr-review.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Claude PR Review

# Proactive: reviews every PR without being asked. Pairs with claude-mention.yml, the
# reactive half. Both are thin: the logic lives in
# jantman/github-actions-workflows/claude-pr-review.
#
# >>> A CHANGE TO *THIS FILE* CANNOT BE TESTED BY THE PR THAT MAKES IT. <<<
# claude-code-action refuses to run when this file differs from the copy on the default
# branch -- that is what stops a pull request from rewriting it to steal the OAuth
# token. It exits 0 when it skips, so such a PR goes green having reviewed nothing.
# Look for "Exiting due to workflow validation skip". Merge first, then the next PR
# gets a real review. A change to the shared ACTION has no such problem, because this
# file does not change.

on:
pull_request:
types: [opened, synchronize, ready_for_review, reopened]

# A new push supersedes the review it interrupted. Reviewing a commit that has already
# been replaced burns a full agent run on code that no longer exists.
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true

jobs:
claude-review:
# A pull request from a fork gets no secrets, so CLAUDE_CODE_OAUTH_TOKEN is empty
# and the action fails red rather than skipping -- a confusing failure on somebody
# else's contribution. Skip cleanly instead. Always true on a repository that takes
# no fork pull requests.
if: github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write # POST /pulls/N/reviews, and the agent reads the PR
issues: write # the action's own tracking/error comments, not the review
id-token: write
actions: read # lets the reviewer read this commit's CI results

steps:
- name: Checkout repository
uses: actions/checkout@v7
with:
fetch-depth: 1

- uses: jantman/github-actions-workflows/claude-pr-review@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
89 changes: 0 additions & 89 deletions .github/workflows/claude.yml

This file was deleted.

Loading