Automated code review is only useful when it runs before a human opens the pull request, not after. Most teams treat AI review as a manual step, which means it gets skipped under deadline pressure and the entire value disappears. This article walks through a concrete workflow where MonkeyCode's free model access and free server option turn code review into a CI job that runs on every pull request without costing a cent.
Disclosure: This article was prepared as part of MonkeyCode's product outreach.
The Problem: Review Tools That Require Human Initiation
A review bot that lives inside an IDE or a chat window depends on a developer remembering to run it. That dependency is fragile because developers forget, skip, or postpone anything that feels optional during a sprint. The result is that most AI review tools end up reviewing the same few files over and over while the rest of the codebase gets zero coverage.
The fix is to move the review step into the pipeline where it cannot be skipped. A CI job that runs on every pull request removes the human initiation problem entirely and creates a consistent baseline for code quality. The workflow described here uses MonkeyCode's free server to host the review agent and its free model access to generate comments, so the only cost is the CI minutes you already pay for.
The Workflow: Three Stages in One Pipeline
The pipeline has three stages, each with a distinct responsibility. The first stage collects the diff between the pull request branch and the target branch. The second stage sends that diff to the MonkeyCode endpoint with a prompt that asks for specific, actionable feedback. The third stage posts the result as a comment on the pull request using the GitHub API.
Stage 1: Collect the Diff
The diff collection is straightforward because GitHub Actions provides the necessary context through built-in environment variables. The script below extracts the changed files and their contents, then packages them into a JSON payload that the review endpoint can process:
#!/bin/bash
# collect_diff.sh — builds a review payload from the current PR
diff_files=$(git diff --name-only "$GITHUB_BASE_REF"..."$GITHUB_HEAD_REF")
payload='{"files":[]}'
for file in $diff_files; do
content=$(cat "$file" | jq -Rs .)
payload=$(echo "$payload" | jq --arg f "$file" --arg c "$content" '.files += [{"path": $f, "content": $c}]')
done
echo "$payload" > review_payload.json
The script filters out lock files and generated artifacts to keep the payload small and the review focused on real code. A production version should also respect a maximum file size, because sending a 10-megabyte file to any model endpoint is a waste of tokens.
Stage 2: Send the Diff to the Review Endpoint
The review request uses a prompt that asks for specific, structured feedback rather than general impressions. The key is to request a JSON response with a defined schema, which makes the output easier to parse and post back to GitHub:
# review_agent.py — sends the diff and parses structured feedback
import json
import os
import urllib.request
with open("review_payload.json") as f:
payload = json.load(f)
prompt = {
"model": os.environ["MONKEYCODE_MODEL"],
"messages": [
{
"role": "system",
"content": (
"You are a senior code reviewer. Analyze the provided diff "
"and return JSON with a 'comments' array. Each comment must "
"have 'path', 'line', and 'message' fields. Only report "
"issues that are concrete and actionable. Do not praise code."
),
},
{"role": "user", "content": json.dumps(payload)},
],
}
req = urllib.request.Request(
os.environ["MONKEYCODE_ENDPOINT"],
data=json.dumps(prompt).encode(),
headers={"Content-Type": "application/json", "Authorization": f"Bearer {os.environ['MONKEYCODE_API_KEY']}"},
)
with urllib.request.urlopen(req) as resp:
result = json.load(resp)
comments = result["choices"][0]["message"]["content"]
print(comments)
The script keeps the prompt minimal because a long system prompt consumes tokens on every call. The free tier's token allowance is generous enough for hundreds of reviews per day, but treating tokens as a finite resource is the right habit for any team.
Stage 3: Post Comments to the Pull Request
The final stage parses the model output and posts each comment through the GitHub API. The script below reads the JSON response and creates review comments using the standard endpoint:
#!/bin/bash
# post_comments.sh — posts review comments to the PR
comments=$(python3 -c "import json,sys; data=json.load(sys.stdin); print(json.dumps(data['comments']))")
commit_sha=$(git rev-parse "$GITHUB_SHA")
for row in $(echo "$comments" | jq -r '.[] | @base64'); do
decoded=$(echo "$row" | base64 --decode)
path=$(echo "$decoded" | jq -r .path)
line=$(echo "$decoded" | jq -r .line)
message=$(echo "$decoded" | jq -r .message)
curl -s -X POST \
-H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
https://api.github.com/repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/comments \
-d "{\"commit_id\": \"$commit_sha\", \"path\": \"$path\", \"line\": $line, \"body\": \"$message\"}"
done
This stage is deliberately simple, and it fails loudly when the model returns malformed JSON or missing fields. A production pipeline should add a retry loop and a fallback that posts a single summary comment if the structured output cannot be parsed.
The GitHub Actions Workflow File
The three stages combine into a single workflow file that runs on every pull request. The example below uses a free server for the review agent, which means no dedicated infrastructure is required:
name: ai-code-review
on:
pull_request:
types: [opened, synchronize]
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Collect diff
run: bash collect_diff.sh
- name: Run review agent
env:
MONKEYCODE_ENDPOINT: ${{ secrets.MONKEYCODE_ENDPOINT }}
MONKEYCODE_API_KEY: ${{ secrets.MONKEYCODE_API_KEY }}
MONKEYCODE_MODEL: ${{ secrets.MONKEYCODE_MODEL }}
run: python3 review_agent.py > review_output.json
- name: Post comments
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: bash post_comments.sh < review_output.json
The workflow uses three secrets for the endpoint, API key, and model name. Storing the model name as a secret feels odd at first, but it allows the team to swap models without editing the workflow file.
The Failure Mode That Taught Us the Most
The first version of this pipeline posted zero comments on every pull request, and the team assumed the model was too weak to find issues. The real cause was a schema mismatch: the model returned comments with a line field that referred to the diff position, while the GitHub API expected a line number in the final file. The comments were rejected silently, and the pipeline reported success because the HTTP calls returned 200.
The fix was to add a validation step that checks the response schema before posting anything. A simple Python assertion caught the mismatch within minutes:
for comment in comments:
assert "path" in comment, f"Missing path: {comment}"
assert "line" in comment, f"Missing line: {comment}"
assert "message" in comment, f"Missing message: {comment}"
The lesson is that every integration point between an AI system and an external API is a place where silent failures hide. The model was never the problem; the contract between the output schema and the GitHub API was the problem.
Limitations and Who Should Skip This
The workflow assumes the team already uses GitHub and GitHub Actions, and it produces comments that are only as good as the model behind them. The free model tier is strong enough for catching obvious bugs, missing error handling, and security anti-patterns, but it will not replace a thoughtful human reviewer on complex architectural decisions. Teams with strict data residency requirements should check where the free server processes requests before sending proprietary code.
The token allowance and server availability are subject to change, so teams should verify the current terms in the MonkeyCode documentation before building a permanent dependency on them. The pipeline itself is provider-agnostic, and the same scripts work with any OpenAI-compatible endpoint if the team decides to switch later.
A Practical Next Step
The full workflow is reproducible in an afternoon, and the scripts in this article are small enough to adapt to any existing CI setup. Start with a single repository and a single pull request, then inspect the comments carefully before enabling the job on every PR. If you want to experiment with the same free server and model access described here, the MonkeyCode documentation has the current details on availability and limits.
Top comments (0)