An AI-powered code review agent built with LangGraph that analyses GitHub pull requests using parallel specialist agents and posts structured feedback as PR comments.
┌─────────────────┐
│ fetch_diff │
│ (pulls PR diff) │
└────────┬────────┘
│
┌────────▼────────┐
│ planner │
│ (routes by file │
│ type + size) │
└────────┬────────┘
│ Send API (parallel)
┌──────────────────┼──────────────────┐
│ │ │
┌────────▼───────┐ ┌────────▼───────┐ ┌────────▼───────┐
│ security_agent │ │ style_agent │ │ logic_agent │
│ secrets, OWASP │ │ lint, complex- │ │ bugs, N+1s, │
│ CVEs, injection│ │ ity, naming │ │ missing tests │
└────────┬───────┘ └────────┬───────┘ └────────┬───────┘
│ │ │
└──────────────────┼──────────────────┘
│ (auto-joined)
┌────────▼────────┐
│ supervisor │
│ synthesizes, │
│ scores, drafts │
└────────┬────────┘
│
┌────────▼────────┐
│ output │
│ GitHub comment │
│ or dry-run │
└─────────────────┘
| Agent | Checks |
|---|---|
| Security | Hardcoded secrets, SQL/command injection, XSS, insecure deserialisation (pickle, yaml.load), OWASP Top-10, known CVEs via NVD API |
| Style | Cyclomatic complexity (radon), pylint / ESLint violations, magic numbers, missing type hints, commented-out code |
| Logic | N+1 query patterns, swallowed exceptions, off-by-one risks, untested new functions, None/null dereference risks |
| Supervisor | Deduplicates all findings, assigns a 1–10 severity score, drafts a structured GitHub-ready markdown comment |
The three specialist agents run in parallel using LangGraph's Send API. The planner skips agents that are irrelevant for the file types in the PR (e.g. no Python files → style agent skipped).
- Python 3.11+
- An Anthropic API key
- A GitHub personal access token with
reposcope (only needed for private repos or to post comments)
# 1. Clone and enter the project
git clone <repo-url>
cd code-review-agent
# 2. Create a virtual environment
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
# 3. Install dependencies
pip install -e .
# Install optional dev tools (pytest, langgraph-cli)
pip install -e ".[dev]"
# 4. Configure environment
cp .env.example .env
# Edit .env with your keys (see Configuration section)All configuration is via environment variables. Copy .env.example to .env and fill in:
# Required
ANTHROPIC_API_KEY=sk-ant-...
# Required for private repos or to post GitHub comments
GITHUB_TOKEN=ghp_...
# Optional: dry-run mode (print review, don't post to GitHub)
DRY_RUN=false
# Optional: LangSmith tracing
LANGCHAIN_TRACING_V2=true
LANGCHAIN_API_KEY=ls__...
LANGCHAIN_PROJECT=code-review-agent| Use case | Required scope |
|---|---|
| Public repos, dry-run only | No token needed |
| Read private repos | repo (read) |
| Post/edit PR comments | repo |
python main.py <pr_url> [--dry-run] [--approve]| Argument | Type | Description |
|---|---|---|
pr_url |
positional | Full GitHub PR URL |
--dry-run |
flag | Print the review to stdout instead of posting to GitHub |
--approve |
flag | Pause after drafting the review and ask for confirmation before posting |
See HOWTO.md for detailed examples.
You can run the agent visually with the LangGraph development server:
pip install "langgraph-cli[inmem]"
langgraph devOpen http://localhost:8123 in your browser. The code_review graph will be available. Pass an initial state like:
{
"pr_url": "https://github.com/owner/repo/pull/42",
"diff": "",
"files_changed": [],
"security_findings": [],
"style_findings": [],
"logic_findings": [],
"agents_to_run": [],
"final_review": "",
"severity_score": 0,
"messages": []
}code-review-agent/
├── main.py # CLI entry point
├── langgraph.json # LangGraph Studio config
├── pyproject.toml # Dependencies
├── .env.example # Environment variable template
└── src/
├── state.py # ReviewState TypedDict + Finding type
├── graph.py # Graph assembly (build_graph)
├── nodes/
│ ├── fetch_diff.py # Pulls diff + file list from GitHub
│ ├── planner.py # Routes to specialists via Send API
│ ├── security_agent.py # ReAct agent: secrets, CVEs, OWASP
│ ├── style_agent.py # ReAct agent: lint, complexity
│ ├── logic_agent.py # ReAct agent: bugs, N+1, tests
│ ├── supervisor.py # Synthesises findings, assigns score
│ └── output.py # Posts GitHub comment or dry-runs
└── tools/
├── github_tools.py # parse_pr_url, fetch_pr_diff
├── security_tools.py # detect_secret_patterns, search_nvd_cve, check_owasp_top10
├── style_tools.py # run_linter, check_complexity
└── logic_tools.py # check_test_coverage, detect_n_plus_one, find_unhandled_errors
ReviewState is a TypedDict shared across all nodes. The three findings lists use operator.add as their reducer, so parallel writes from different agents merge safely:
security_findings: Annotated[List[Finding], operator.add]
style_findings: Annotated[List[Finding], operator.add]
logic_findings: Annotated[List[Finding], operator.add]The planner writes agents_to_run to state. The route_to_specialists conditional edge reads it and emits Send objects — LangGraph launches all three agents simultaneously and auto-joins them before the supervisor:
def route_to_specialists(state):
return [Send(f"{agent}_agent", state) for agent in state["agents_to_run"]]Passing --approve compiles the graph with interrupt_before=["output"]. Execution pauses after the supervisor drafts the review, prints it, and waits for your confirmation before posting.
The output node searches for an existing <!-- code-review-agent --> comment on the PR. If one is found it edits it; otherwise it creates a new one. Re-running the agent on the same PR never creates duplicate comments.
The supervisor assigns a score from 1–10:
| Score | Meaning |
|---|---|
| 1–3 | Critical security or correctness issues |
| 4–5 | Multiple warnings across categories |
| 6–7 | Minor warnings and suggestions only |
| 8–9 | Only suggestions, no real issues |
| 10 | Clean diff, nothing to flag |
- Create
src/nodes/my_agent.pyfollowing the same pattern assecurity_agent.py - Add its tools to
src/tools/ - Register the node in
src/graph.py:builder.add_node("my_agent", my_agent_node) - Add the new extension set to
src/nodes/planner.pyand append"my"toagents_to_run - Add
builder.add_edge("my_agent", "supervisor")insrc/graph.py
Each agent constructs its own ChatAnthropic instance. Change the model= argument in any agent file:
_model = ChatAnthropic(model="claude-opus-4-7", temperature=0)Set in .env:
LANGCHAIN_TRACING_V2=true
LANGCHAIN_API_KEY=ls__...
LANGCHAIN_PROJECT=code-review-agentEvery run will appear in your LangSmith project with a full trace of the parallel agent execution.
| Package | Purpose |
|---|---|
langgraph |
Graph execution, Send API, interrupt support |
langchain-anthropic |
Claude model integration |
langchain-core |
@tool decorator, message types |
PyGithub |
GitHub REST API (fetch diff, post comments) |
radon |
Python cyclomatic complexity analysis |
requests |
NVD CVE API calls |
python-dotenv |
.env file loading |