Installation: Direct action reference
Use this installation method to run without Docker. The root composite action installs shellcheck on the runner automatically. It does not install other static analyzer binaries (semgrep, trufflehog, ruff, golangci-lint, hadolint, checkov, phpcs, phpstan, kube-linter, tflint, or eslint). See Static analyzers for details. Each analyzer does nothing, without an error, when its binary is absent.
For most new installs, use the container action. It ships the analyzer binaries pre-installed at pinned versions.
Prerequisites
The tag1consulting/ai-pr-review repository is public. You do not need to change any Actions access setting to use it as an action.
If you use a private fork or mirror, set its access in Settings → Actions → General → Access. Choose the option that lets your other repositories use the action.
1. Create the workflow
Create .github/workflows/ai-review.yml in your repository:
name: AI PR Review
on:
pull_request:
types: [opened, synchronize, ready_for_review, labeled]
permissions:
contents: read
pull-requests: write
concurrency:
group: ai-review-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
review:
# The head.repo.full_name check is defense in depth against fork PRs.
# Fork PRs cannot access secrets anyway. They also should not start review
# jobs that hold pull-requests: write.
if: >-
github.event.pull_request.head.repo.full_name == github.repository &&
github.event.pull_request.draft == false &&
github.actor != 'dependabot[bot]' &&
github.actor != 'renovate[bot]' &&
!contains(github.event.pull_request.labels.*.name, 'skip-ai-review') &&
(github.event.action != 'labeled' ||
github.event.label.name == 'ai-review-full' ||
github.event.label.name == 'ai-review-rescan')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: tag1consulting/ai-pr-review@main
env:
FORCE_FULL_DIFF: ${{ contains(github.event.pull_request.labels.*.name, 'ai-review-rescan') }}
with:
provider: ${{ vars.AI_REVIEW_PROVIDER || 'anthropic' }}
api-key: ${{ secrets.AI_REVIEW_API_KEY }}
base-url: ${{ vars.AI_REVIEW_BASE_URL || '' }}
model-standard: ${{ vars.AI_REVIEW_MODEL_STANDARD || '' }}
model-premium: ${{ vars.AI_REVIEW_MODEL_PREMIUM || '' }}
review-mode: ${{ contains(github.event.pull_request.labels.*.name, 'ai-review-full') && 'full' || '' }}
pr-number: ${{ github.event.pull_request.number }}
base-ref: ${{ github.event.pull_request.base.ref }}
head-sha: ${{ github.event.pull_request.head.sha }}
github-token: ${{ secrets.GITHUB_TOKEN }}
max-diff-lines: ${{ vars.AI_REVIEW_MAX_DIFF_LINES || '5000' }}
max-inline: ${{ vars.AI_REVIEW_MAX_INLINE || '25' }}
max-tokens-per-agent: ${{ vars.AI_REVIEW_MAX_TOKENS_PER_AGENT || '32768' }}
enable-suggestions: ${{ vars.AI_REVIEW_ENABLE_SUGGESTIONS || 'true' }}
parallel: ${{ vars.AI_REVIEW_PARALLEL || 'true' }}
ignore-merge-commits: ${{ vars.AI_REVIEW_IGNORE_MERGE_COMMITS || 'true' }}
fail-on-findings: ${{ vars.AI_REVIEW_FAIL_ON_FINDINGS || 'true' }}
# Always try to remove the ai-review-rescan label after the review.
# This also runs if the concurrency rule cancelled the review job on a new push.
cleanup-rescan-label:
needs: review
if: >-
always() &&
github.event.pull_request.head.repo.full_name == github.repository &&
!contains(github.event.pull_request.labels.*.name, 'skip-ai-review')
runs-on: ubuntu-latest
permissions:
contents: read
issues: write
steps:
- name: Remove ai-review-rescan label
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh api \
--method DELETE \
repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/labels/ai-review-rescan \
|| true
The snippet above uses the @v4 tag for readability. To pin third-party actions to commit SHAs (recommended), copy examples/workflows/pr-review.yml. Renovate keeps that file pinned and up to date.
To pin a specific version, replace @main with a tag or commit SHA (for example @v2.24.1 or @cb9d7ee).
2. Configure secrets and variables
In the consuming repository’s settings:
Secrets:
AI_REVIEW_API_KEY: API key for your chosen LLM provider
Variables (optional): Set these in Settings → Secrets and variables → Actions → Variables.
| Variable | Default | Description |
|---|---|---|
AI_REVIEW_PROVIDER |
anthropic |
Provider name |
AI_REVIEW_BASE_URL |
'' |
Custom endpoint URL (for openai-compatible or bedrock-proxy) |
AI_REVIEW_MODEL_STANDARD |
Per-provider default | Override the standard model ID |
AI_REVIEW_MODEL_PREMIUM |
Per-provider default | Override the premium model ID (full mode only) |
AI_REVIEW_MAX_DIFF_LINES |
5000 |
Skip review when diff exceeds this many lines |
AI_REVIEW_MAX_INLINE |
25 |
Max inline comments per run. The summary body holds the excess |
AI_REVIEW_MAX_TOKENS_PER_AGENT |
32768 |
Output token budget per LLM agent call |
AI_REVIEW_ENABLE_SUGGESTIONS |
true |
Enable “Apply suggestion” buttons on inline comments |
AI_REVIEW_PARALLEL |
true |
Parallel tiered fan-out. Set to false if you hit provider rate limits |
AI_REVIEW_IGNORE_MERGE_COMMITS |
true |
Strip upstream base-branch merges from the diff before review |
AI_REVIEW_FAIL_ON_FINDINGS |
true |
Exit with code 2 when the review outcome is REQUEST_CHANGES or COMMENT (incomplete or unknown risk). This fails the workflow step. Note: The fail-on-findings input in action.yml defaults to 'false'. The workflow example above sets it to true to match the fail-closed default of the container-action starter templates. If you omit it from your own workflow, CI stays green even on a REQUEST_CHANGES outcome. Set to false to always exit 0. |
This table lists the inputs most consuming repositories need. action.yml has many more (SARIF ingestion, analyzer and agent allowlists and denylists, context enrichment, cost ceiling, judge pass, token-usage display, approval-ceiling, and others). For the full reference, see Configuration → Repository variables.
Runtime dependencies
The root composite action installs shellcheck automatically if the runner does not have it. To get findings from the other static analyzers, install them in the consuming workflow. If a binary is absent, the action writes a WARNING to stderr and continues without those findings.
| Analyzer | Language/files | Install |
|---|---|---|
| semgrep | Any source files | pip install semgrep |
| trufflehog | Secret scanning | curl -sSfL https://raw.githubusercontent.com/trufflesecurity/trufflehog/main/scripts/install.sh \| sh -s -- -b /usr/local/bin |
| ruff | Python | pip install ruff |
| golangci-lint | Go | curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh \| sh -s -- -b /usr/local/bin |
| hadolint | Dockerfiles | Download from GitHub releases |
| checkov | IaC (Terraform, K8s, CloudFormation) | pip install checkov |
| phpcs | PHP | composer global require squizlabs/php_codesniffer |
| phpstan | PHP | composer global require phpstan/phpstan |
| kube-linter | Kubernetes manifests | Download from GitHub releases |
| tflint | Terraform | Download from GitHub releases |
| eslint | JS/TS | Needs an eslint binary on PATH. It does nothing if no config is present |
For pinned, SHA-verified installs, use the container action. It ships the analyzer binaries (except eslint) at fixed versions, and you do not need to set up the workflow.