Skip to content

Conversation

@mattwalsh
Copy link
Collaborator

This update supports Gemini (tested) and Claude (untested)

@github-actions
Copy link

github-actions bot commented Nov 20, 2025

Review updated until commit 4137af0

Description

  • Add AI-powered PR review system supporting Gemini and Claude backends

  • Implement GitHub Actions workflows for automated code review on PR events

  • Create Python wrapper scripts for CLI execution with structured verdict output

  • Include Docker testing setup and utility functions for environment handling

Changes walkthrough

Relevant files
Enhancement
7 files
ai_cli_wrapper.py
Core wrapper for Gemini/Claude CLI execution with verdict parsing
+115/-0 
pr_preflight_launcher.py
Main launcher orchestrating PR review process and context building
+151/-0 
git_helpers.py
Git utility functions for SHA resolution and merge base computation
+34/-0   
utils.py
Utility functions for environment variables and file operations
+34/-0   
proxy.js
Proxy handler for request transformation in Claude workflow
+29/-0   
gemini-cli-review.yml
GitHub Actions workflow for Gemini-based automated PR reviews
+163/-0 
claude-code-review.yml
GitHub Actions workflow for Claude-based automated PR reviews
+135/-0 
Tests
4 files
review_test.json
Test data configuration for review workflow testing           
+11/-0   
Dockerfile
Docker container setup for testing AI CLI review tools     
+25/-0   
build_ai_cli_docker
Build script for Docker image used in testing                       
+5/-0     
run_ai_cli_docker
Run script for Docker container testing environment           
+6/-0     

PR Reviewer Guide

Here are some key observations to aid the review process:

🧪 No relevant tests
⚡ Recommended focus areas for review
Formatting and Argument Handling

Line 27 has extra whitespace before the function definition. Also, line 52 passes the prompt as a single argument which may not work correctly for all CLI tools - some expect prompts via stdin or specific flag formats.

"""
Run Gemini / Claude CLI with the given prompt, check for verdict, and write outputs.
Returns an exit code: 0 (success), 3 (review failed), 4 (parsing error), 1/2 (errors).
"""
OUTPUT_DIR = Path(output_dir)
VERDICT_MARKER = verdict_marker.strip().upper()
ensure_dir(OUTPUT_DIR)

if tool is None:
   write_to_path(OUTPUT_DIR, "error.txt", f"Error (Exit 1 - no tool specified)")
   return 1

try:
   # Invoke CLI; pass prompt as a single argument
   safety_instructions = (
       "CRITICAL RULE: If a tool execution fails, "
       "OR if I have exceeded my API quote, DO NOT retry. "
       "Stop immediately and report the error."
   )

   prompt = f"{safety_instructions}\n\n{prompt}"

   result = subprocess.run(
      [tool] + tool_args + [prompt],
Unexpected stderr Output

The write_to_path function prints content to stderr (line 27) which may not be intended behavior when writing files. This could clutter logs unnecessarily.

def write_to_path(output_path: Path | str, filename: str, content: str) -> None:
    """
    Write content to a file inside the given directory path, creating the directory if needed.
    """
    print(content, file=sys.stderr)
Missing Error Handling

The code doesn't handle the case where context.output_dir is None and the default directory creation might fail due to permissions or path issues.

default_dir = f"artifacts/ai_pr_preflight_review/{(context.pr_number or 'local')}-{context.head_sha}"
output_dir = context.output_dir or default_dir

@mattwalsh mattwalsh self-assigned this Nov 20, 2025
@mattwalsh mattwalsh requested a review from xwang233 November 20, 2025 00:50
@mattwalsh mattwalsh marked this pull request as ready for review November 20, 2025 00:50
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Nov 20, 2025

Greptile Overview

Greptile Summary

This PR adds GitHub Actions workflows for automated PR reviews using Gemini and Claude AI backends. The implementation includes a Python launcher that orchestrates git operations, calls AI CLI tools with review prompts, and parses verdict markers from outputs.

Key changes:

  • New workflows for Gemini and Claude PR reviews with proper concurrency control
  • Python scripts for SHA resolution, merge-base computation, and AI CLI wrapping
  • Docker test environment for local development and testing
  • Request transformer proxy for Claude thinking parameter normalization

Critical issues found:

  • Claude workflow router config has escaped env vars (\$ANTHROPIC_BASE_URL) preventing proper substitution at runtime
  • Dockerfile module paths are incorrect - Python imports from tools/ will fail because launcher is at /usr/local/bin/ but modules are at /usr/local/bin/tools/
  • proxy.js logic unconditionally replaces any truthy request.thinking value, losing custom configurations
  • Multiple missing f-string prefixes in ai_cli_wrapper.py causing literal strings instead of interpolation

Notes:

  • Gemini workflow is more complete with PR comment posting logic
  • Claude workflow only uploads artifacts, PR commenting still TODO
  • All previously reported syntax errors appear to be fixed (duplicate "ls", type annotations, f-strings, parenthesis)

Confidence Score: 2/5

  • This PR has several critical issues that will prevent the Claude workflow from functioning correctly and cause runtime failures in the Docker test environment
  • Score reflects multiple blocking issues: (1) Claude router config with escaped env vars will fail to connect to API, (2) Dockerfile has incorrect Python module paths causing import errors, (3) proxy.js has flawed logic that breaks valid thinking configs, (4) missing f-string prefixes in error messages. The Gemini workflow is more solid but shares the Dockerfile issues. Core Python modules are well-structured, but integration points have critical bugs
  • Pay close attention to .github/workflows/claude-code-review.yml (router config), tools/ai_cli_docker_test/Dockerfile (import paths), tools/proxy.js (logic error), and tools/ai_cli_wrapper.py (f-strings)

Important Files Changed

File Analysis

Filename Score Overview
.github/workflows/claude-code-review.yml 2/5 New workflow for Claude PR reviews with escaped env vars in router config and missing fetch-depth: 0 preventing git operations
.github/workflows/gemini-cli-review.yml 4/5 New workflow for Gemini PR reviews with proper fetch-depth and PR comment posting logic
tools/ai_cli_wrapper.py 3/5 AI CLI wrapper with missing f-string prefixes causing literal string output instead of interpolation
tools/proxy.js 2/5 Request transformer that incorrectly replaces valid thinking configurations with hardcoded values
tools/ai_cli_docker_test/Dockerfile 1/5 Docker test image with incorrect Python module paths causing import failures

Sequence Diagram

sequenceDiagram
    participant GH as GitHub Actions
    participant Launcher as pr_preflight_launcher.py
    participant GitHelpers as git_helpers.py
    participant Wrapper as ai_cli_wrapper.py
    participant AI as AI CLI (Claude/Gemini)
    participant Router as Claude Code Router
    
    GH->>GH: Trigger on PR opened/synchronize
    GH->>GH: Checkout code with fetch-depth: 0
    
    alt Claude Backend
        GH->>GH: Install Claude Code + Router
        GH->>GH: Write router config with escaped env vars
        GH->>Router: Start ccr (with env vars available)
        Note over Router: May fail if config has literal $VAR strings
    else Gemini Backend
        GH->>GH: Install Gemini CLI
    end
    
    GH->>Launcher: Run with --ai-backend and env vars
    Launcher->>Launcher: Parse args and build context
    Launcher->>GitHelpers: resolve_base_sha_from_ref(base_ref)
    GitHelpers-->>Launcher: base_sha
    Launcher->>GitHelpers: compute_merge_base_sha(base, head)
    GitHelpers-->>Launcher: merge_base_sha
    Launcher->>Launcher: Build review prompt with SHAs
    Launcher->>Wrapper: launch_ai_cli(tool, prompt, verdict_marker)
    
    Wrapper->>AI: Execute CLI with prompt and timeout
    AI->>AI: Analyze PR changes
    AI-->>Wrapper: Output with VERDICT: PASSED/FAILED
    
    Wrapper->>Wrapper: Parse output for verdict marker
    alt Verdict Found
        Wrapper->>Wrapper: Write success_raw_output.txt
        Wrapper->>Wrapper: Write review_verdict.txt
        Wrapper-->>Launcher: Exit code (0=passed, 3=failed)
    else Verdict Missing
        Wrapper->>Wrapper: Write error.txt (parsing error)
        Wrapper-->>Launcher: Exit code 4
    end
    
    Launcher-->>GH: Return exit code
    
    alt Gemini Workflow
        GH->>GH: Read success_raw_output.txt
        GH->>GH: Post/update PR comment via GitHub API
    else Claude Workflow
        GH->>GH: Upload artifacts (no PR comment yet)
    end
Loading

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

11 files reviewed, 7 comments

Edit Code Review Agent Settings | Greptile
React with 👍 or 👎 to share your feedback on this new summary format


def launch_ai_cli(
prompt: str,
tool: None,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

syntax: type annotation is incorrect - tool: None should be tool: str | None

Suggested change
tool: None,
tool: str | None,

return 4

except subprocess.TimeoutExpired:
error_msg = "{tool} command timed out after {timeout_seconds} seconds"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

syntax: missing 'f' prefix for f-string - this will output literal string instead of interpolating {tool} and {timeout_seconds}

Suggested change
error_msg = "{tool} command timed out after {timeout_seconds} seconds"
error_msg = f"{tool} command timed out after {timeout_seconds} seconds"

return 1

except FileNotFoundError:
error_msg = "Error: '{tool}' command not found. Is it installed and in PATH?"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

syntax: missing 'f' prefix for f-string - this will output literal string instead of interpolating {tool}

Suggested change
error_msg = "Error: '{tool}' command not found. Is it installed and in PATH?"
error_msg = f"Error: '{tool}' command not found. Is it installed and in PATH?"



tool_args = []
ok_commands = ["git", "ls", "grep", "ls","stat"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

syntax: "ls" appears twice in the list

Suggested change
ok_commands = ["git", "ls", "grep", "ls","stat"]
ok_commands = ["git", "ls", "grep", "stat"]

Comment on lines +29 to +30
- name: Checkout code
uses: actions/checkout@v4
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: missing fetch-depth: 0 - without full git history, git merge-base in pr_preflight_launcher.py:85 will fail when computing the merge base between branches

Suggested change
- name: Checkout code
uses: actions/checkout@v4
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0

ensure_dir(OUTPUT_DIR)

if tool == None:
write_to_path(OUTPUT_DIR, "error.txt", f"Error (Exit 1 - no tool specified")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

syntax: missing closing parenthesis in error message

Suggested change
write_to_path(OUTPUT_DIR, "error.txt", f"Error (Exit 1 - no tool specified")
write_to_path(OUTPUT_DIR, "error.txt", f"Error (Exit 1 - no tool specified)")

@@ -0,0 +1,6 @@
# once inside, can test with
# python3 /usr/local/bin/pr_preflight_launcher_gemini.py --output-dir=/tmp --base-sha=f8b8551a720cd5c3a9aa8950e5a50fb7d420cbe5 --head-sha=9f9a6b2f91519b3dc02fe7ec7a5f2a3b98398338
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

syntax: outdated reference - file is now pr_preflight_launcher.py, not pr_preflight_launcher_gemini.py

Suggested change
# python3 /usr/local/bin/pr_preflight_launcher_gemini.py --output-dir=/tmp --base-sha=f8b8551a720cd5c3a9aa8950e5a50fb7d420cbe5 --head-sha=9f9a6b2f91519b3dc02fe7ec7a5f2a3b98398338
# python3 /usr/local/bin/pr_preflight_launcher.py --output-dir=/tmp --base-sha=f8b8551a720cd5c3a9aa8950e5a50fb7d420cbe5 --head-sha=9f9a6b2f91519b3dc02fe7ec7a5f2a3b98398338

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

11 files reviewed, 5 comments

Edit Code Review Agent Settings | Greptile
React with 👍 or 👎 to share your feedback on this new summary format

Comment on lines 102 to 111
- name: Run Claude Code via wrapper
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
ANTHROPIC_BASE_URL: http://localhost:3456
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_SHA: ${{ github.event.pull_request.head.sha }}
PR_BASE_REF: ${{ github.event.pull_request.base.ref }}
PR_HEAD_REF: ${{ github.event.pull_request.head.ref }}
run: |
python -m tools.pr_preflight_launcher --ai-backend claude --output-dir "${CLAUDE_OUTPUT_DIR}"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: missing PR_BASE_SHA environment variable - pr_preflight_launcher.py:52 expects either PR_BASE_SHA or PR_BASE_REF to be set, but only PR_BASE_REF is provided, so the workflow will attempt to resolve it via git rev-parse origin/{base.ref} which requires fetch-depth: 0

Suggested change
- name: Run Claude Code via wrapper
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
ANTHROPIC_BASE_URL: http://localhost:3456
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_SHA: ${{ github.event.pull_request.head.sha }}
PR_BASE_REF: ${{ github.event.pull_request.base.ref }}
PR_HEAD_REF: ${{ github.event.pull_request.head.ref }}
run: |
python -m tools.pr_preflight_launcher --ai-backend claude --output-dir "${CLAUDE_OUTPUT_DIR}"
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
ANTHROPIC_BASE_URL: http://localhost:3456
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_SHA: ${{ github.event.pull_request.head.sha }}
PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
PR_BASE_REF: ${{ github.event.pull_request.base.ref }}
PR_HEAD_REF: ${{ github.event.pull_request.head.ref }}

Comment on lines 56 to 66
- name: Run Gemini PR review
env:
#GEMINI_MODEL: gemini-1.5-flash-latest
GEMINI_OUTPUT_DIR: artifacts/gemini_review/${{ github.event.pull_request.number }}-${{ github.event.pull_request.head.sha }}
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_SHA: ${{ github.event.pull_request.head.sha }}
PR_BASE_REF: ${{ github.event.pull_request.base.ref }}
PR_HEAD_REF: ${{ github.event.pull_request.head.ref }}
run: |
python -m tools.pr_preflight_launcher --ai-backend gemini --output-dir "${GEMINI_OUTPUT_DIR}"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: missing PR_BASE_SHA environment variable - pr_preflight_launcher.py:52 expects either PR_BASE_SHA or PR_BASE_REF to be set, but only PR_BASE_REF is provided, so the workflow will attempt to resolve it via git rev-parse origin/{base.ref} which requires fetch-depth: 0

Suggested change
- name: Run Gemini PR review
env:
#GEMINI_MODEL: gemini-1.5-flash-latest
GEMINI_OUTPUT_DIR: artifacts/gemini_review/${{ github.event.pull_request.number }}-${{ github.event.pull_request.head.sha }}
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_SHA: ${{ github.event.pull_request.head.sha }}
PR_BASE_REF: ${{ github.event.pull_request.base.ref }}
PR_HEAD_REF: ${{ github.event.pull_request.head.ref }}
run: |
python -m tools.pr_preflight_launcher --ai-backend gemini --output-dir "${GEMINI_OUTPUT_DIR}"
- name: Run Gemini PR review
env:
#GEMINI_MODEL: gemini-1.5-flash-latest
GEMINI_OUTPUT_DIR: artifacts/gemini_review/${{ github.event.pull_request.number }}-${{ github.event.pull_request.head.sha }}
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_SHA: ${{ github.event.pull_request.head.sha }}
PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
PR_BASE_REF: ${{ github.event.pull_request.base.ref }}
PR_HEAD_REF: ${{ github.event.pull_request.head.ref }}

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

11 files reviewed, no comments

Edit Code Review Agent Settings | Greptile
React with 👍 or 👎 to share your feedback on this new summary format

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

11 files reviewed, no comments

Edit Code Review Agent Settings | Greptile
React with 👍 or 👎 to share your feedback on this new summary format

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

11 files reviewed, no comments

Edit Code Review Agent Settings | Greptile

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

11 files reviewed, no comments

Edit Code Review Agent Settings | Greptile

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

11 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

@github-actions
Copy link

🤖 Gemini PR Review

Commit: 4137af0

Comparing mattw/pr_preflight_action (4137af0) with main (722fc4f).
Comparing mattw/pr_preflight_action (4137af0) with main (722fc4f).

This pull request introduces a new CI workflow to automatically review pull requests using AI models (Gemini and Claude). The changes consist of new GitHub Actions workflows, Python scripts to drive the AI CLI tools, and a Docker setup for testing.

Code Quality and Style

The code is well-structured and follows good practices. The Python scripts are modular, with clear responsibilities for handling Git operations, wrapping the AI CLIs, and launching the review process. The code is readable, well-documented with docstrings and comments, and uses type hints. The GitHub Actions workflows are well-organized, using environment variables for configuration and secrets. The shell scripts are simple and serve their purpose.

Test Coverage

While there are no formal unit tests for the Python scripts, the pull request includes a Docker setup for testing the entire workflow in an isolated environment. This allows for integration testing of the scripts and the AI CLI tools. The inclusion of a review_test.json file suggests that testing with mock GitHub event payloads has been considered. Given the nature of the code (integrating with external tools), the Docker-based testing approach is a reasonable and effective way to ensure the functionality works as expected.

Documentation

The code is well-documented. The Python scripts have docstrings explaining the purpose of each module and function, as well as the meaning of different exit codes. The command-line arguments for the launcher script are also well-described. The GitHub Actions workflows contain comments that clarify the purpose of each step.

Potential Bugs or Issues

  1. External Service Reliability: The workflows depend on the availability and performance of the Gemini and Claude APIs. Any issues with these services could cause the CI checks to fail. The wrapper script includes some basic error handling for API errors and timeouts, which is good.
  2. Incomplete Claude Workflow: The claude-code-review.yml workflow has a "TODO" for posting the review results back to the pull request. This means that while the Gemini review is fully integrated, the Claude review is not yet providing feedback on the PR.
  3. Security: The workflows use API keys stored as GitHub secrets, which is the correct approach. The Gemini workflow also wisely restricts the shell tools that the AI can use, which is a good security measure.

Conclusion

This pull request is a well-engineered solution for automating pull request reviews using AI. The code is of high quality, the testing strategy is practical, and the documentation is clear. The only minor issue is the incomplete implementation of the Claude review posting, but the Gemini workflow is complete and provides a solid foundation. The addition of this automated review system has the potential to significantly improve the development workflow.

VERDICT: PASSED


Review generated at 2025-11-20T21:31:49.715Z

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants