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
48 changes: 34 additions & 14 deletions .github/workflows/automated_pr_review.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ name: Automated Code Review
# to secrets (like GEMINI_API_KEY) even for fork PRs.
# Using pull_request for now during setup/testing.
on:
pull_request:
types: [opened]
issue_comment:
types: [created]

Expand All @@ -15,21 +13,24 @@ permissions:
pull-requests: read

jobs:
# Always runs so the workflow run exits cleanly without a "No jobs ran" error
# when the review job's if-condition evaluates to false.
noop:
runs-on: ubuntu-latest
steps:
- name: Workflow trigger check
run: echo "Workflow triggered successfully."

review:
runs-on: ubuntu-latest
# Trigger only if:
# 1. It is a pull_request event, it is NOT a draft, and the author is a maintainer
# (OWNER, MEMBER, or COLLABORATOR).
# 2. OR it is a regular conversation comment on a pull request, the comment body has a line starting with "/review",
# and the commenter is a maintainer.
# Trigger only if it is a regular comment on a pull request, the comment body has a line
# starting with "/review", and the commenter is a maintainer (OWNER, MEMBER, or COLLABORATOR).
if: >
(github.event_name == 'pull_request' && !github.event.pull_request.draft &&
contains(fromJson('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.pull_request.author_association)) ||
(github.event_name == 'issue_comment' && github.event.issue.pull_request != null &&
(startsWith(github.event.comment.body, '/review') ||
contains(github.event.comment.body, '\n/review') ||
contains(github.event.comment.body, '\r\n/review')) &&
contains(fromJson('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association))
github.event_name == 'issue_comment' && github.event.issue.pull_request != null &&
(startsWith(github.event.comment.body, '/review') ||
contains(github.event.comment.body, '\n/review') ||
contains(github.event.comment.body, '\r\n/review')) &&
contains(fromJson('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association)
steps:
- name: Checkout PR Branch
uses: actions/checkout@v7
Expand All @@ -40,6 +41,25 @@ jobs:
ref: refs/pull/${{ github.event.pull_request.number || github.event.issue.number }}/head
persist-credentials: false

- name: Fetch Base Branch and Negotiate Minimal Diff History
env:
PR_COMMITS: ${{ github.event.pull_request.commits }}
run: |
# 1. Fetch the tip of main
git fetch origin main:refs/remotes/origin/main --depth=1

# 2. Check if we already have the merge base (common ancestor)
if ! git merge-base origin/main HEAD >/dev/null 2>&1; then
# If we know the exact number of commits in the PR, deepen by (PR_COMMITS + 10)
if [ -n "$PR_COMMITS" ] && [ "$PR_COMMITS" != "null" ]; then
git fetch --deepen="$((PR_COMMITS + 10))"
fi
# 3. If it's an issue_comment event (where PR_COMMITS is null) or still shallow, unshallow/deepen
if ! git merge-base origin/main HEAD >/dev/null 2>&1; then
git fetch --unshallow || git fetch --deepen=50
fi
fi

- name: Checkout Reviewbot (Base Branch)
uses: actions/checkout@v7
with:
Expand Down
41 changes: 35 additions & 6 deletions tools/private/reviewbot/antigravity_review.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@
# requires-python = ">=3.11"
# dependencies = [
# "google-antigravity",
# "requests",
# ]
# ///
import argparse
import asyncio
import subprocess
from pathlib import Path

from google.antigravity import Agent, CapabilitiesConfig, LocalAgentConfig
from google.antigravity.models import GeminiAPIEndpoint, ModelTarget
from google.antigravity.types import BuiltinTools


def parse_args():
Expand All @@ -18,27 +20,54 @@ def parse_args():
return parser.parse_args()


def get_pr_diff() -> str:
"""Fetches the git diff for the current pull request against origin/main."""
try:
return subprocess.check_output(
["git", "diff", "origin/main...HEAD"], text=True, stderr=subprocess.DEVNULL
)
except Exception:
return "No diff could be automatically extracted via git commands."


async def main():
args = parse_args()

# Read prompt file
prompt = Path(args.prompt).read_text()
# Read prompt file and pre-hydrate with the exact PR code diff
base_prompt = Path(args.prompt).read_text()
diff_text = get_pr_diff()
prompt = (
f"{base_prompt}\n\n## Pull Request Git Diff\n"
f"Here is the exact code diff for this pull request:\n```diff\n{diff_text}\n```"
)

# General coordinator instructions for the reviewer agent.
system_instructions = (
"You are a code review assistant. Use your available skills to perform "
"reviews on pull requests."
)

# Create the default endpoint picking up GEMINI_API_KEY from the environment.
endpoint = GeminiAPIEndpoint()

# Initialize the Antigravity Agent in read-only mode for security.
# Register the review-pr skill from the local reviewbot folder.
# Provide a comprehensive prioritized cascade across Gemini 3 models
# to automatically fall back if any model hits free-tier quota limits (429)
# or temporary unavailability.
config = LocalAgentConfig(
models=[
ModelTarget(name="gemini-3.5-flash", endpoint=endpoint),
ModelTarget(name="gemini-3.1-pro-preview", endpoint=endpoint),
ModelTarget(name="gemini-3.1-flash-lite", endpoint=endpoint),
ModelTarget(name="gemini-3-pro-preview", endpoint=endpoint),
ModelTarget(name="gemini-flash-latest", endpoint=endpoint),
ModelTarget(name="gemini-pro-latest", endpoint=endpoint),
Comment thread
rickeylev marked this conversation as resolved.
],
system_instructions=system_instructions,
skills_paths=[str(Path(__file__).parent / "skills" / "review-pr" / "SKILL.md")],
capabilities=CapabilitiesConfig(
allow_filesystem_read=True,
allow_filesystem_write=False,
allow_network=False,
enabled_tools=BuiltinTools.read_only(),
),
)

Expand Down
2 changes: 2 additions & 0 deletions tools/private/reviewbot/skills/review-pr/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ description: Perform a read-only code review on a pull request.

# review-pr

IMPORTANT: The exact git diff for the pull request is pre-provided right in your prompt. Analyze this provided diff directly in a single pass without calling exploratory directory listing or file reading tools unless you specifically need surrounding lines of context from a modified file.

You are an expert Starlark, Python, and Bazel code reviewer. Analyze the changed files for
correctness, edge cases, and performance. Focus strictly on logical
correctness, concurrency safety, system architecture, performance bottlenecks,
Expand Down