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 da4516a

Description

  • Add PR pre-flight quality check system supporting Gemini and Claude AI backends

  • Implement GitHub Actions workflows for automated PR code reviews with artifact uploads

  • Create Python wrapper scripts for AI CLI execution with verdict parsing and error handling

  • Set up Docker testing environment and utility functions for CI integration

Changes walkthrough

Relevant files
Enhancement
7 files
pr_preflight_launcher.py
Main launcher script for PR pre-flight checks                       
+151/-0 
ai_cli_wrapper.py
Core wrapper for Gemini/Claude CLI execution                         
+117/-0 
git_helpers.py
Git utility functions for SHA resolution                                 
+34/-0   
utils.py
Common utility functions for CI scripts                                   
+34/-0   
claude-code-review.yml
GitHub Actions workflow for Claude PR reviews                       
+187/-0 
gemini-cli-review.yml
GitHub Actions workflow for Gemini PR reviews                       
+165/-0 
proxy.js
Request transformation proxy handler                                         
+29/-0   
Tests
4 files
review_test.json
Test configuration for workflow testing                                   
+11/-0   
Dockerfile
Docker test environment for AI CLI tools                                 
+25/-0   
build_ai_cli_docker
Docker build script for test environment                                 
+5/-0     
run_ai_cli_docker
Docker run script for test environment                                     
+6/-0     

PR Reviewer Guide

Here are some key observations to aid the review process:

🧪 No relevant tests
⚡ Recommended focus areas for review
Security Risk

The subprocess call on line 51-57 executes arbitrary commands passed from the AI tool without proper validation. While the launcher restricts allowed tools to 'git', 'ls', 'grep', 'stat', the AI could potentially craft malicious inputs within these commands (e.g., command injection via filenames or arguments). Consider adding input sanitization or using safer alternatives like shlex.quote().

result = subprocess.run(
   [tool] + tool_args + [prompt],
   capture_output=True,
   text=True,
   timeout=timeout_seconds,
   check=False,
)
Disabled Workflow

The Gemini workflow is disabled with 'if: false' on line 15. This means the Gemini integration won't actually run, which contradicts the PR description stating it supports Gemini (tested). Either enable the workflow or update the documentation to reflect the current status.

# Temporarily disabled to avoid burning API tokens
if: false
# Skip if PR is in draft
Unclear Purpose

The proxy.js file manipulates request parameters (deleting enable_thinking, reasoning, and setting thinking budget) but it's unclear how this proxy is integrated into the system. The Claude workflow installs claude-code-router but doesn't clearly use this proxy. Document the proxy's role or remove unused code.

class ProxyHandler {
  name = "proxy-handler";

  constructor(options) {
    this.options = options;
  }

  async transformRequestIn(request, provider) {
    // delete request.stream;
    if (request.thinking) {
        request.thinking = {
            "type": "enabled",
            "budget_tokens": 4000
        }
    }
    if (request.enable_thinking) {
        delete request.enable_thinking;
    }
    if (request.reasoning) {
        delete request.reasoning;
    }

    return {
      body: request,
    };
  }
}

module.exports = ProxyHandler

@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 implements automated PR review capabilities using AI CLIs (Gemini and Claude) through GitHub Actions workflows. The implementation includes:

  • Two GitHub Actions workflows: claude-code-review.yml for Claude Code reviews (active) and gemini-cli-review.yml for Gemini reviews (currently disabled)
  • Python orchestration layer: pr_preflight_launcher.py coordinates the review process, resolves git SHAs, and computes merge-base for accurate diff analysis
  • AI CLI wrapper: ai_cli_wrapper.py handles subprocess execution, timeout management, and verdict parsing with comprehensive exit code handling
  • Supporting utilities: Git helpers for SHA resolution, proxy handler for API request transformation, and Docker test environment

Key Architecture Decisions:

  • Uses merge-base computation for GitHub-style three-dot diffs to minimize noise from base branch commits
  • Claude backend routes through claude-code-router to NVIDIA NIM API with models like moonshotai/kimi-k2-thinking
  • Verdict-based flow control with standardized exit codes (0=pass, 1=error, 2=API error, 3=fail, 4=parsing error)

Issues Found:

  • claude-code-review.yml:79 uses ccr restart instead of ccr start, which will fail on first initialization
  • gemini-cli-review.yml:163-165 has trailing blank lines creating incomplete JavaScript syntax in the github-script block

Confidence Score: 3/5

  • This PR has critical workflow initialization issues that will cause failures, but the core Python logic is solid
  • Score of 3 reflects two critical issues: (1) ccr restart will fail on first run when router isn't running yet, preventing Claude reviews from working, and (2) incomplete JavaScript syntax in gemini workflow from trailing blank lines. The Python orchestration code is well-structured with proper error handling, but the workflow configuration issues will block functionality. Many other reported issues (escaped env vars, missing PR_BASE_SHA) were reviewed by developers and are either intentional or non-blocking.
  • .github/workflows/claude-code-review.yml needs ccr start fix on line 79, .github/workflows/gemini-cli-review.yml needs trailing blank lines removed after line 163

Important Files Changed

File Analysis

Filename Score Overview
.github/workflows/claude-code-review.yml 3/5 New workflow for Claude Code reviews. Has ccr restart instead of ccr start on line 79, which may fail on first run. Otherwise properly configured with fetch-depth: 0 and environment variables.
.github/workflows/gemini-cli-review.yml 2/5 New workflow for Gemini reviews. Currently disabled (if: false). Has trailing blank lines after closing brace creating incomplete JavaScript syntax in the github-script block.
tools/pr_preflight_launcher.py 5/5 Core launcher orchestrating PR reviews. Clean implementation with proper SHA resolution, merge-base computation, and backend selection for Claude/Gemini.

Sequence Diagram

sequenceDiagram
    participant GHA as GitHub Actions
    participant Launcher as pr_preflight_launcher.py
    participant Git as git_helpers.py
    participant Wrapper as ai_cli_wrapper.py
    participant AI as AI CLI (Gemini/Claude)
    participant Router as Claude Code Router
    participant NIM as NVIDIA NIM API
    
    GHA->>GHA: Trigger on PR event
    GHA->>GHA: Setup Node.js & Python
    
    alt Claude Backend
        GHA->>Router: Install & start ccr
        GHA->>Router: Write config with NIM provider
        Router->>NIM: Proxy requests
    else Gemini Backend
        GHA->>GHA: Install Gemini CLI
    end
    
    GHA->>Launcher: python -m tools.pr_preflight_launcher
    Launcher->>Launcher: Parse args & environment vars
    Launcher->>Git: resolve_base_sha_from_ref(base_ref)
    Git-->>Launcher: base_sha
    Launcher->>Git: compute_merge_base_sha(base_sha, head_sha)
    Git-->>Launcher: merge_base_sha
    
    Launcher->>Launcher: Build review prompt with SHAs
    Launcher->>Wrapper: launch_ai_cli(tool, prompt, verdict_marker)
    
    Wrapper->>AI: subprocess.run([tool, args, prompt])
    
    alt Claude
        AI->>Router: API request
        Router->>NIM: Forward to NIM API
        NIM-->>Router: Response
        Router-->>AI: Transformed response
    else Gemini
        AI->>AI: Direct Gemini API call
    end
    
    AI->>AI: Execute git/grep/ls commands
    AI->>AI: Analyze PR changes
    AI-->>Wrapper: Output with VERDICT: PASSED/FAILED
    
    Wrapper->>Wrapper: Parse verdict from output
    Wrapper->>Wrapper: Write success_raw_output.txt
    Wrapper->>Wrapper: Write review_verdict.txt
    Wrapper-->>Launcher: Exit code (0=pass, 3=fail)
    Launcher-->>GHA: Exit code
    
    GHA->>GHA: Read output files
    GHA->>GHA: Post comment to PR via github-script
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

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

- Add Post Claude review to PR step that posts review comments to PRs
- Post comments even when verdict is FAILED to help developers fix issues
- Only skip posting if error occurred or no review output
- Temporarily disable Gemini workflow with if: false to avoid burning API tokens

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
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, 1 comment

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, 1 comment

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, 1 comment

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, 2 comments

Edit Code Review Agent Settings | Greptile

console.log('Created new PR comment');
}


Copy link
Contributor

Choose a reason for hiding this comment

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

syntax: file ends with blank lines after closing brace - syntax error leaves JavaScript incomplete

Suggested change
}

"transformers": []
}
EOF
nohup ccr restart &
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: ccr restart requires router already running - use ccr start for initial launch

Suggested change
nohup ccr restart &
nohup ccr start &

This reverts commit 9439709.
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, 2 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +163 to +165
}


Copy link
Contributor

Choose a reason for hiding this comment

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

syntax: trailing blank lines after closing brace - the github-script block's JavaScript is incomplete and will cause syntax errors when the workflow runs

Suggested change
}
}

"transformers": []
}
EOF
nohup ccr restart &
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: use ccr start for first-time initialization - restart requires router already running and will fail

Suggested change
nohup ccr restart &
nohup ccr start &

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