PullGuard is a free BYOK GitHub Action for open-source maintainers. It scores pull request review risk and can independently comment, label, or close a PR based on repository policy.
It does not try to prove a PR was AI-generated. It flags review-risk patterns that waste maintainer time: missing tests, broad unrelated changes, risky refactors, duplicated logic, weak PR descriptions, and suspiciously shallow fixes.
PullGuard is early work. Score calibration is still being adjusted against real OSS PRs, and feedback on false positives, false negatives, and confusing comments is especially useful.
Fastest path:
npx pullguard initPreview without writing files:
npx pullguard init --dry-runThe CLI writes:
.github/workflows/pullguard.yml.github/pullguard.yml
It also prints the exact next steps for your chosen setup: which GitHub secret to add, whether to apply run-pullguard or comment /pullguard, and which labels PullGuard will create if missing.
To change the setup later, either edit .github/pullguard.yml directly or rerun:
npx pullguard initTo remove PullGuard:
npx pullguard uninstallThat removes .github/workflows/pullguard.yml and .github/pullguard.yml. You can preview removal with:
npx pullguard uninstall --dry-runManual setup:
Create .github/workflows/pullguard.yml:
name: PullGuard
on:
issue_comment:
types: [created]
permissions:
contents: read
pull-requests: write
issues: write
jobs:
pullguard:
runs-on: ubuntu-latest
steps:
- uses: vladzima/pullguard@v1
with:
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
config: .github/pullguard.ymlCreate .github/pullguard.yml:
model:
provider: openai
name: gpt-5.4-mini-2026-03-17
trigger:
mode: comment
label: run-pullguard
comment: /pullguard
allowCommentOverrides: true
allowedCommentAuthorAssociations:
- OWNER
- MEMBER
- COLLABORATOR
analysis:
depth: codebase
maxFiles: 20
maxPatchCharsPerFile: 4000
maxBaseFileCharsPerFile: 6000
maxFindings: 4
maxReviewFirstFiles: 5
actions:
comment:
enabled: true
labels:
enabled: true
rules:
- threshold: 50
label: needs-human-review
- threshold: 80
label: high-risk-pr
close:
enabled: false
threshold: 95PullGuard is BYOK. Add the selected provider key as a GitHub Actions secret:
- Open your GitHub repository.
- Go to
Settings -> Secrets and variables -> Actions. - Click
New repository secret. - Add one of:
OPENAI_API_KEYfor OpenAIANTHROPIC_API_KEYfor Anthropic
- Paste the API key as the secret value.
- Save the secret.
The workflow can include both keys; PullGuard uses the provider selected in .github/pullguard.yml.
PullGuard supports OpenAI and Anthropic BYOK.
Use OpenAI:
model:
provider: openai
name: gpt-5.4-mini-2026-03-17Use Anthropic:
model:
provider: anthropic
name: claude-sonnet-4-20250514The matching API key must be passed to the action. You may pass both keys in the workflow and choose the provider in .github/pullguard.yml.
trigger.mode controls when analysis runs:
always: run on every configured PR event.label: run only when the configured label is applied.comment: run only when an allowed maintainer comments the configured phrase.
With the default config, the manual PR comment is:
/pullguard
So a maintainer can trigger review by commenting /pullguard on the pull request.
Label-triggered review:
trigger:
mode: label
label: run-pullguardWith this config, a maintainer triggers review by applying the run-pullguard label to the PR.
Comment-triggered review:
trigger:
mode: comment
comment: /pullguard
allowedCommentAuthorAssociations:
- OWNER
- MEMBER
- COLLABORATORWith this config, a maintainer triggers review by commenting /pullguard on the PR.
Comment triggers are restricted by allowedCommentAuthorAssociations so random commenters cannot spend the repository's BYOK credits. The init wizard generates workflow events for the selected trigger mode: issue_comment for comment mode, pull_request_target labeled for label mode, and opened/synchronized/reopened PR events for always mode. If you change trigger.mode later, rerun npx pullguard init or update .github/workflows/pullguard.yml to match.
The default comment-triggered run is:
/pullguard
Trusted maintainers can optionally override a single comment-triggered run:
/pullguard --depth pr --comment --labels
/pullguard --depth codebase --close 95
/pullguard --provider anthropic --model claude-sonnet-4-20250514
Supported flags:
--depth pr|codebase--comment/--no-comment--labels/--no-labels--close <threshold>/--no-close--provider openai|anthropic--model <model-name>
Comment overrides apply only to that run. Disable them with:
trigger:
allowCommentOverrides: falseanalysis.depth controls token spend and context:
codebase: default. Fetches capped base-branch contents for changed files so the model can compare the patch against existing code.pr: cheapest mode. Sends PR metadata, file stats, and capped patches only.
Both modes keep output compact through maxFindings and maxReviewFirstFiles.
comment, labels, and close are separate choices. You can run observe-only by disabling all three, comment without labels, label without comments, or opt into automatic closing for very high-risk PRs.
Automatic closing is disabled by default because false positives are costly in public OSS.
Post or update a PR comment:
actions:
comment:
enabled: trueWhen a matching trigger starts analysis, PullGuard first posts a short "reviewing" placeholder comment and replaces it with the final result when analysis finishes.
Apply labels when score thresholds are met:
actions:
labels:
enabled: true
rules:
- threshold: 50
label: needs-human-review
- threshold: 80
label: high-risk-prWith this config, any PR with score 50 or higher gets needs-human-review; a PR with score 82 gets both needs-human-review and high-risk-pr.
If a configured output label does not exist yet, PullGuard creates it before applying it.
Close very high-risk PRs:
actions:
close:
enabled: true
threshold: 95With this config, PullGuard closes the PR only when the score is at least 95.
Observe-only mode:
actions:
comment:
enabled: false
labels:
enabled: false
close:
enabled: falseObserve-only mode still computes outputs for later workflow steps, but it does not modify the PR.
PullGuard reads pull request metadata and file patches through the GitHub API; it does not need to checkout or execute contributor code. The init wizard keeps the workflow trigger narrow for the selected mode so comment-triggered installs do not create skipped PR checks on every PR open.
Do not add a checkout of the pull request head to this workflow unless you fully understand the security implications.
score: review-risk score from0to100labels: comma-separated labels selected by policyshould-close: whether the policy selected the close actionskipped: whether the configured trigger did not match
Example:
steps:
- id: pullguard
uses: vladzima/pullguard@v1
with:
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
- if: ${{ steps.pullguard.outputs.skipped == 'false' }}
run: echo "Risk score is ${{ steps.pullguard.outputs.score }}"
- if: ${{ steps.pullguard.outputs['should-close'] == 'true' }}
run: echo "PullGuard selected close action"Use bracket syntax for should-close because the output name contains a hyphen.
If the configured trigger does not match, skipped is set to true and analysis does not run. If analysis starts and succeeds, skipped is set to false.
examples/always-comment.yml: comment on every configured PR event.examples/manual-comment-trigger.yml: run only when a maintainer comments/pullguard.examples/manual-label-trigger.yml: run only whenrun-pullguardis applied.examples/observe-only.yml: compute outputs without modifying PRs.examples/aggressive-close.yml: codebase-aware analysis with close enabled at95.