Pull request reviews by a Gemini managed agent, triggered from GitHub Actions, with pluggable review criteria.
When a pull request is opened or updated, a workflow invokes a hosted Gemini managed agent that reviews the changes and posts the results on the PR: a summary comment plus line-anchored review comments on the diff.
The review criteria live in a skill file (skills/<name>/SKILL.md), selected
with the REVIEW_SKILL variable. The base instruction and the findings schema
are shared across review types. Two skills ship with the tool:
| Skill | Reviews for |
|---|---|
security-review (default) |
vulnerabilities, correctness bugs, risky changes |
code-quality-review |
design, readability, tests, performance, consistency with the codebase |
To add a review type, create skills/<my-review>/SKILL.md (frontmatter name
and description, body with the focus areas, category names, severity
semantics, and rules) and set REVIEW_SKILL: my-review.
- The workflow runs
review_pr.pywith the PR number and the selected skill. - The script fetches the PR metadata and the per-file diff from the GitHub API.
- It creates a managed-agent interaction. The sandbox is provisioned with the
repository mounted at
/workspace/repothrough therepositoryenvironment source, and with the selected review skill mounted at.agents/skills/<name>/SKILL.mdthrough aninlinesource (the runtime auto-registers skills under.agents/skills/; the same rubric also rides in the system instruction so it applies unconditionally). The agent runsgit fetch origin pull/<N>/headplus a checkout to bring the mounted repo to the exact PR state: two git commands on an already-present working tree. - The prompt carries the PR diff; the mounted repository provides the surrounding code, specs, and docs the agent reads for context.
- The agent gets an authenticated GitHub CLI through a shim wrapper mounted
at
/workspace/bin/gh, for read-only context gathering (linked issues, earlier review comments, CI check status). See "GitHub CLI inside the sandbox" below. - The agent returns findings as JSON matching the schema in
schema.py; the script posts a summary comment and one line-anchored review comment per finding. - The summary comment carries a hidden state marker. The next run on the same PR reads it and resumes the conversation, so follow-up reviews remember the previous round. See "Follow-up reviews" below.
- The
repositorysource mounts the repo's default branch at provision time (500 MB limit; GitHub is the documented provider). The PR state comes from fetchingrefs/pull/<N>/headinside the sandbox. - For private repositories, the clone and fetch authenticate with a Basic
header,
base64("x-access-token:<token>"), which works for both the ActionsGITHUB_TOKENand classic PATs. The header is delivered through thegithub.comentry of the network allowlist as atransform: the egress proxy adds it outside the sandbox, so the token stays out of the VM's filesystem and environment. The script detects private repos from the repository'sprivateflag; public repos run with a plaingithub.comallowlist entry. transformis a list of{header: value}objects.google-genai >= 2.10.0types it this way and rejects a single dict client-side.
Reviews of the same PR build on each other. After posting, the tool appends a hidden HTML marker to the summary comment with the interaction id, the environment id, and the head SHA. The next run for the same PR and skill finds the marker and resumes:
- Reused sandbox and conversation (public repos): the previous
environment id and
previous_interaction_idare passed together, so the installed gh CLI and the cloned repo carry over. - Fresh sandbox, resumed conversation: a new environment with
previous_interaction_id. This is the path for private repos, because a reused environment carries the previous job's expired token in its auth transform. It is also the fallback when the old sandbox is gone (sandboxes pause after 15 minutes idle and expire after 7 days). - Cold start: the fallback when the previous interaction is unavailable.
On a follow-up, the prompt tells the agent to report which previous findings
are fixed and which remain open, and to raise new findings only for the
current diff. Set PERSIST=0 to run every review stateless.
The agent can consult GitHub for context beyond the code: linked issues,
earlier review comments, CI check status. It does this through a shim wrapper
(bin/gh-shim.sh, mounted at /workspace/bin/gh) that follows the token
injection pattern from
Philipp Schmid's GitHub agent guide:
- The shim installs the gh CLI on first use (cached in the sandbox for
environment reuse) and exports a dummy
GH_TOKENthat satisfies the CLI's local auth check. - The egress proxy injects the real job token at the network layer:
Beareronapi.github.com,Basicongithub.com. The token value never enters the sandbox. - The prompt restricts usage to read-only context gathering; posting stays in
the runner. Set
AGENT_GH=0to remove the CLI and theapi.github.comallowlist entry entirely.
Capability note: with the CLI enabled, code running in the sandbox can
call the GitHub API with the job token's permissions (the workflow grants
contents: read and pull-requests: write) for the duration of the job. The
token expires when the job ends. If you review PRs from untrusted
contributors, weigh this against the value of the extra context and set
AGENT_GH=0 where it matters.
| File | Purpose |
|---|---|
review_pr.py |
Orchestrator: PR context, managed-agent review, posting |
github_client.py |
GitHub REST helpers (PR and files, diff prompt, comments, Basic auth header) |
schema.py |
Base instruction shared by all review types + JSON findings contract |
skills/security-review/SKILL.md |
Security rubric (default) |
skills/code-quality-review/SKILL.md |
Maintainability rubric |
bin/gh-shim.sh |
GitHub CLI wrapper mounted into the sandbox |
pr-review.yml |
The Actions workflow, to copy into .github/workflows/ |
requirements.txt |
google-genai>=2.10.0, requests |
- Vendor the tool: copy this folder to
tools/pr-reviewer/in the repo. - Add the workflow: copy
pr-review.ymlto.github/workflows/pr-review.yml. - Add one secret:
GEMINI_API_KEY(AI Studio key) under Settings > Secrets and variables > Actions.GITHUB_TOKENis provided by Actions; the workflow'spermissions:block grantspull-requests: writefor the comments andcontents: read. - Open a PR. The workflow also supports
workflow_dispatchwithpr_numberandreview_skillinputs, which lets you run two review types on the same PR.
To change the review type for automatic runs, edit REVIEW_SKILL in the
workflow, or duplicate the job with one skill each to post two reviews per PR.
export GEMINI_API_KEY=... GITHUB_TOKEN=... \
GITHUB_REPOSITORY=owner/repo PR_NUMBER=123 \
REVIEW_SKILL=code-quality-review DRY_RUN=1
python review_pr.py # prints the findings JSON, posts nothing- Parsing: the reply is parsed by stripping ```json fences, extracting the outermost JSON object, and escaping invalid backslash sequences (models echo regex and code into strings) before a retry parse.
- Posting: the summary lands as a conversation comment; each finding is posted as its own line-anchored review comment. Findings GitHub rejects with a 422 (line outside the diff) are grouped into one additional comment.
- One-click fixes: when a finding's fix is an exact replacement of the
anchored line(s), the agent includes a
suggestionfield and the comment carries a GitHub suggested change: the PR author applies it with the Commit suggestion button. Multi-line replacements anchor a range viastart_line. The skills instruct the agent to include a suggestion only when the full fix fits in the anchored lines, so the button always applies a complete fix. - Diff size: the per-file diff in the prompt is capped at 20,000 characters
(
build_diff_prompt); files without a textualpatch(binary or very large) are listed with a note. For truncated or patch-less files, the prompt instructs the agent to read the full change from the mounted repository withgit diff origin/<base>...HEAD -- <path>. - Model: the
BASE_AGENTenv var selects the agent id (defaultantigravity-preview-05-2026).
This reviewer authenticates to Gemini with an API key stored in the Actions
secrets, which fits a demonstration. Managed Agents are also available on the
Google Cloud Agent Platform,
in preview at the time of writing. Once that version reaches general
availability, the advice for production use is to run the reviewer against it
and authenticate with
Workload Identity Federation:
the Actions job exchanges its OIDC token for short-lived Google Cloud
credentials through
google-github-actions/auth,
the google-genai client targets the platform with vertexai=True (plus
GOOGLE_CLOUD_PROJECT, location="global"), and the repository stores zero
long-lived AI secrets.
- Managed-agent reviews are advisory and non-deterministic. Treat findings as suggestions and verify them; the JSON schema constrains the shape of the output, and the judgment remains the model's.
- Cost: model tokens per review. A single interaction can consume 100k to 3M tokens per the documentation.
- On
pull_requestevents from forks, Actions issues a read-onlyGITHUB_TOKEN, so posting comments fails. Run reviews on same-repo branches, or switch the trigger topull_request_targetafter evaluating its security implications.
- https://ai.google.dev/gemini-api/docs/agents
- https://ai.google.dev/gemini-api/docs/agent-environment (repository source, allowlist, private-repo auth)
- https://ai.google.dev/gemini-api/docs/custom-agents
