GitHub Action that reviews a single commit with Claude, GPT, Gemini, and Grok, deduplicates findings, files them as a GitHub Issue, and optionally opens a draft PR with high-confidence fixes that multiple models agree on.
Designed to be called inside a per-commit matrix on push events. The action reviews one commit per invocation — your workflow handles enumeration.
name: AI Commit Review
on:
push:
branches: [main]
permissions:
contents: write
issues: write
pull-requests: write
jobs:
enumerate:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.commits.outputs.matrix }}
count: ${{ steps.commits.outputs.count }}
steps:
- uses: actions/checkout@v6
with: { fetch-depth: 0 }
- id: commits
env:
BEFORE: ${{ github.event.before }}
AFTER: ${{ github.event.after }}
run: |
if [[ "$BEFORE" == "0000000000000000000000000000000000000000" ]]; then
SHAS=$(git log --format='%H' -1 "$AFTER")
else
SHAS=$(git log --format='%H' "${BEFORE}..${AFTER}")
fi
MATRIX=$(echo "$SHAS" | jq -R -s -c 'split("\n") | map(select(. != "")) | map({sha: .})')
echo "count=$(echo "$MATRIX" | jq 'length')" >> "$GITHUB_OUTPUT"
echo "matrix=${MATRIX}" >> "$GITHUB_OUTPUT"
review:
needs: enumerate
if: needs.enumerate.outputs.count != '0'
runs-on: ubuntu-latest
strategy:
fail-fast: false
max-parallel: 5
matrix:
commit: ${{ fromJson(needs.enumerate.outputs.matrix) }}
steps:
- uses: actions/checkout@v6
with: { fetch-depth: 0 }
- uses: leek/ai-commit-review@v1
with:
commit-sha: ${{ matrix.commit.sha }}
anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
gemini-api-key: ${{ secrets.GEMINI_API_KEY }}
grok-auth-json: ${{ secrets.GROK_AUTH_JSON }}Any provider whose credentials are empty is skipped. Run with any combination of the four providers.
Claude and OpenAI can run through their local coding CLIs instead of direct API calls. Grok runs through its CLI only:
- Claude CLI mode runs
claude -pthrough Claude Code. - OpenAI CLI mode runs
codex execthrough Codex. The provider is still namedopenaiin reports so existing digesting and agreement logic keeps working. - Grok CLI mode runs the official
grokCLI in headless mode with structured output and read-only repository tools. - Gemini remains API-key only for now.
The default auth mode for Claude, OpenAI, and Grok is auto, which preserves existing workflows:
- Claude uses CLI mode when
claude-code-oauth-tokenis set; otherwise it usesanthropic-api-key. - OpenAI uses CLI mode when
codex-access-tokenorcodex-auth-jsonis set; otherwise it usesopenai-api-key. - Grok uses CLI mode when
grok-auth-jsonis set. - Set
claude-authoropenai-authtoapiorclito force a mode. Setgrok-auth: clito use credentials already present ingrok-homeor the runner's default~/.grokdirectory.
The action installs missing claude, codex, and grok commands when their CLI mode is selected. Set install-cli-tools: false if your runner already has them.
CLI modes run from the caller's checked-out repository and receive the commit-sha explicitly. Use actions/checkout with fetch-depth: 0 so the CLIs can inspect the commit and surrounding repository context. Unlike API mode, CLI mode does not embed the filtered diff in the prompt; Claude, Codex, and Grok inspect the commit from the checkout themselves. Claude and Grok are limited to read/search/git shell tools. Codex defaults to codex-sandbox: danger-full-access because its Linux read-only sandbox depends on user namespaces that may be unavailable on GitHub-hosted runners. The action verifies afterward that the checkout has no tracked changes and no unexpected untracked files.
Codex CLI mode writes project_doc_fallback_filenames = ["CLAUDE.md"] to CODEX_HOME/config.toml, so repositories that use CLAUDE.md instead of AGENTS.md are still picked up by Codex's project-doc discovery.
The default Claude model is claude-opus-4-8. If you set install-cli-tools: false, make sure the runner's Claude Code install supports Opus 4.8.
The Grok model input is empty by default, so the CLI selects the model available to the authenticated account. Set grok-model to override it.
- uses: leek/ai-commit-review@v1
with:
commit-sha: ${{ matrix.commit.sha }}
claude-auth: cli
openai-auth: cli
claude-code-oauth-token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
codex-access-token: ${{ secrets.CODEX_ACCESS_TOKEN }}
grok-auth-json: ${{ secrets.GROK_AUTH_JSON }}Use this only on trusted private automation. auth.json contains access tokens.
- uses: leek/ai-commit-review@v1
with:
commit-sha: ${{ matrix.commit.sha }}
openai-auth: cli
codex-auth-json: ${{ secrets.CODEX_AUTH_JSON }}- Claude Code: run
claude setup-token, then store the printed token asCLAUDE_CODE_OAUTH_TOKEN. Anthropic documents this in Generate a long-lived token. - Codex access token: create a token in ChatGPT admin settings and store it as
CODEX_ACCESS_TOKEN. OpenAI documents this in Codex access tokens. - Codex
auth.json: sign in locally withcodex login, then seed a trusted runner or CI secret with the file contents. By default the file is at~/.codex/auth.json; on macOS you can copy it withpbcopy < ~/.codex/auth.json. OpenAI documents the advanced workflow in Maintain Codex account auth in CI/CD. - Grok
auth.json: sign in locally withgrok login, then store the contents of~/.grok/auth.jsonasGROK_AUTH_JSON; on macOS you can copy it withpbcopy < ~/.grok/auth.json. Grok refreshes credentials during local use, but seeded CI credentials still need to be replaced when they expire. See the official Grok CLI authentication guide.
Do not expose CLI account credentials to public repositories, fork-triggered workflows, or untrusted runners.
| Input | Default | Description |
|---|---|---|
commit-sha |
required | Commit SHA to review. Caller must actions/checkout with fetch-depth: 0. |
anthropic-api-key |
empty | Anthropic API key. Provider runs only when set. |
openai-api-key |
empty | OpenAI API key. |
gemini-api-key |
empty | Gemini API key. |
claude-auth |
auto |
Claude provider auth mode: auto, api, or cli. |
openai-auth |
auto |
OpenAI provider auth mode: auto, api, or cli. |
grok-auth |
auto |
Grok provider auth mode: auto or cli. auto selects Grok when grok-auth-json is set. |
claude-code-oauth-token |
empty | Claude Code OAuth token from claude setup-token. Enables Claude CLI mode in auto. |
codex-access-token |
empty | Codex access token passed as CODEX_ACCESS_TOKEN. Enables Codex CLI mode in auto. |
codex-auth-json |
empty | Contents of a Codex auth.json file for Codex CLI mode. Use only on trusted private runners. |
codex-home |
empty | Optional CODEX_HOME path for Codex CLI mode. Useful for self-hosted runners with persistent auth. |
grok-auth-json |
empty | Contents of a Grok auth.json file. Enables Grok CLI mode in auto. Use only on trusted private runners. |
grok-home |
empty | Optional GROK_HOME path for Grok CLI mode. Useful for self-hosted runners with persistent auth. |
install-cli-tools |
true |
Install missing Claude Code, Codex, or Grok CLI tools when a CLI mode is selected. |
claude-cli-path |
claude |
Claude Code CLI command path used in Claude CLI mode. |
codex-cli-path |
codex |
Codex CLI command path used in OpenAI CLI mode. |
grok-cli-path |
grok |
Grok CLI command path used in Grok CLI mode. |
codex-sandbox |
danger-full-access |
Codex sandbox mode used in OpenAI CLI mode. Use read-only only on runners where Codex's Linux sandbox can create user namespaces. |
claude-model |
claude-opus-4-8 |
Anthropic model id. |
openai-model |
gpt-5.5 |
OpenAI model id. |
gemini-model |
gemini-3.5-flash |
Gemini model id. |
grok-model |
empty | Optional Grok model id. Empty uses the Grok CLI default. |
claude-context-file |
empty | Project context file injected into the Claude prompt. |
openai-context-file |
empty | Project context file injected into the OpenAI prompt. |
gemini-context-file |
empty | Project context file injected into the Gemini prompt. |
grok-context-file |
empty | Project context file injected into the Grok prompt. |
prompt-file |
empty | Path to a custom prompt template. Overrides the bundled generic prompt. |
exclude-paths |
empty | Newline-separated git pathspecs excluded from the diff. Use the :!path syntax. |
max-diff-lines |
5000 |
Skip review if filtered diff exceeds this many added/changed lines. |
skip-message-patterns |
Merge* |
Newline-separated bash globs matched against the commit subject. |
skip-author-patterns |
empty | Newline-separated bash globs matched against the commit author name. |
min-severity-for-issue |
warning |
One of critical, warning, info. |
min-models-for-fix-pr |
2 |
Number of providers that must agree on a high-confidence fix before a fix PR is opened. 0 disables. |
issue-label |
ai-review |
Label applied to created issues. |
issue-title-prefix |
[AI Review] |
Issue title prefix. |
fix-pr-title-prefix |
[AI Fix] Suggested fixes for |
Fix PR title prefix. |
fix-branch-prefix |
ai-fix/ |
Fix branch prefix. Short SHA is appended. |
base-branch |
main |
Base branch for fix PRs. |
github-token |
${{ github.token }} |
Token used to create issues, comments, branches, and PRs. |
node-version |
20 |
Node.js version. |
| Output | Description |
|---|---|
reviewed |
true if the commit was reviewed, false if skipped. |
skip-reason |
Reason the commit was skipped, if any. |
diff-line-count |
Added/changed line count of the filtered diff. |
critical-count |
Critical findings after dedup. |
warning-count |
Warning findings after dedup. |
info-count |
Info findings after dedup. |
issue-url |
URL of the created issue, if any. |
fix-pr-url |
URL of the created draft fix PR, if any. |
provider-failures |
Comma-separated provider names that failed to produce a valid review. |
provider-successes |
Comma-separated provider names that produced a valid review. |
provider-skips |
Comma-separated provider names skipped because credentials or supported auth modes were not provided. |
- uses: leek/ai-commit-review@v1
with:
commit-sha: ${{ matrix.commit.sha }}
anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
gemini-api-key: ${{ secrets.GEMINI_API_KEY }}
grok-auth-json: ${{ secrets.GROK_AUTH_JSON }}
claude-context-file: CLAUDE.md
openai-context-file: AGENTS.md
gemini-context-file: GEMINI.md
grok-context-file: AGENTS.md
prompt-file: .github/ai-review-prompt.txt
exclude-paths: |
:!package-lock.json
:!yarn.lock
:!vendor/
:!node_modules/
:!tests/
skip-message-patterns: |
Merge*
build(deps)*
*skip-review*
*skip-ci*
*fix code style*
*Fix Code Style*
ai-review:*
skip-author-patterns: |
*dependabot*Pattern syntax:
skip-message-patternsandskip-author-patternsare bash glob patterns. Avoid[...]— bash treats it as a character class, not a literal substring. Write*skip-review*, not*[skip-review]*.
- Skip check — matches the commit subject and author against your skip patterns.
- Diff filter —
git diff sha~1 shawith yourexclude-pathsapplied. Skips if larger thanmax-diff-lines. - Provider fan-out — runs Claude, GPT/OpenAI, Gemini, and Grok in sequence. Claude and OpenAI can use direct API calls or their CLI modes; Grok uses its CLI. Each provider receives the bundled (or custom) prompt and optional project context. API providers receive the diff, while CLI providers inspect the checked-out commit.
- Digest — merges findings, dedupes by file + line proximity + severity, builds a markdown report, files it as an issue. Optionally opens a draft fix PR for high-confidence findings that multiple models agree on.
The workflow needs:
permissions:
contents: write # for the fix PR branch
issues: write # for finding issues
pull-requests: write # for the fix PR- The action does not enumerate commits. Drive the matrix from your workflow so failures isolate per-commit.
- Existing issues for the same short SHA are detected and creation is skipped.
- All API calls have two retries on 5xx responses.
- CLI modes normalize their output through the same findings parser and self-retraction filter as API modes.
- Selected providers that fail to produce valid review JSON are reported in
provider-failuresand logged as warnings when at least one other provider succeeds. The action fails only when no selected provider produces a valid review.
MIT