Provider-agnostic code reviewer for GitHub, GitLab, and Gitea. Seven hosted model providers, local ollama, and any OpenAI-compatible endpoint β one flag, no static keys for cloud providers. Posts inline review comments and a summary.
π Full documentation: https://lgtmaybe.coles.codes/
lgtmaybe fetches the diff through your code host's API and reviews the lines a pull request (or merge request) changes. It never checks out or runs your code. To judge each change in context it also reads a few surrounding lines from the file. It only ever comments on what the PR actually changed, not the whole repository.
Reviews surface the kind of thing a careful reviewer would flag, each graded from
info up to critical:
- Logic and correctness bugs β edge cases, null dereferences, off-by-one and
boundary errors, mismatched ranges, unhandled error paths, races and TOCTOU,
missed
awaits, numeric and timezone bugs. - Security vulnerabilities β the model is prompted with an OWASP-aligned checklist: injection, XSS, CSRF and open redirects, hardcoded secrets, broken authn/authz (including JWT pitfalls), path traversal, unrestricted uploads, SSRF, insecure deserialization and XXE, mass assignment, weak crypto, resource/DoS safety (including ReDoS), secrets or PII (passwords, tokens, SSNs, card data) leaking into logs, and CI/IaC misconfiguration (workflow script injection, unpinned actions, broad IAM, public buckets). Security findings are first-class, not an afterthought.
- Missing or weak tests for changed code paths, with a suggested test to drop in.
- Undocumented public APIs or stale docs the change just made wrong.
- Factually outdated code β deprecated APIs, end-of-life or vulnerable dependencies, typosquat-looking additions β when the diff shows them.
- Performance regressions β N+1 queries, accidentally quadratic work, redundant computation, allocations or blocking I/O on hot paths, unbounded queries, caches that never evict.
- Needless complexity β deep nesting / high cyclomatic complexity, over-long functions, duplicated logic.
- Intent β does the PR do what it says? lgtmaybe reads the PR title,
description, and commit names (or your
git logcommit names on the CLI) and flags out-of-scope hunks, code that contradicts the stated intent, and promised behaviour the diff never implements. - Ponytail β the "lazy senior dev" lens: the best code is the code you never wrote. Flags code that needn't exist at all β YAGNI, reach for the standard library, do it in fewer lines.
Generated and non-reviewable files (lockfiles, minified bundles, vendored directories, binaries) are skipped automatically, and secrets are redacted from the diff before it is sent to the model.
Hardened against malicious PRs. lgtmaybe never checks out or runs PR code, treats the diff as untrusted input, defends against prompt injection (including forged delimiter break-out attempts), and redacts a broad set of secret formats (cloud keys, GitHub/Slack/Google/Stripe tokens, private keys, passwords, and credentials in connection strings) before anything leaves your environment. See Data and Privacy.
Fast by default. Reviews run the fast preset by default: all nine
categories in four model calls, one per concern β security, correctness
(including stated intent), code health
(performance/complexity/ponytail/deprecation), and artefacts
(tests/documentation). The same four run on every provider; worker count
changes only how they are scheduled, so a cloud review overlaps them while a
single-slot local one runs them in turn. --preset full (or preset: full in
.lgtmaybe.yml) restores the one-call-per-lens deep audit for release branches.
On top of that, all calls across all batches share one concurrency pool
(max_concurrency, default 6 on cloud providers) and share a cached
preamble-plus-diff prefix on the routes that support it, so the diff is
processed once per batch, not once per lens. Add --profile to any run to see exactly where
the time and tokens went.
How the scope is bounded. Every run is capped so a large PR can't blow up latency:
preset(defaultfast) β four calls, one per concern (security, correctness, code health, artefacts), the same on every provider;fullruns one call per lens.max_files(default 50) β reviews the top-N changed files and notes how many were skipped.max_input_tokens(default 100k) β batches the diff to fit the model's budget.max_concurrency(default 6, every provider) β concurrent model calls across the whole fan-out. What decides local throughput is the server's own setting (OLLAMA_NUM_PARALLEL, llama.cpp-np, vLLM batching); set1to run serially.recursive(default on) β when a single file's diff exceeds that budget, walks it hunk-by-hunk instead of sending it whole;--no-recursivesends files whole.categories(default all nine) β which review lenses to run; an explicit list overrides the preset grouping and runs exactly those lenses, one call each.min_severity(defaultlow) plusinclude_paths/exclude_pathsβ focus the review on what you care about.
See Configure .lgtmaybe.yml for every knob.
Big files, small models. When one file's diff is too big for a single model
call, lgtmaybe reviews it hunk-by-hunk rather than whole, so the model never
loses the tail of a large file. On by default; --no-recursive turns it off.
Smaller local models gain the most β across 8 runs on two fixtures, a local
qwen3.5:4b averaged 88% recall reviewing hunk-by-hunk versus 61%
reviewing files whole, with its worst run matching the whole-file method's
best (a real effect, not noise), at ~2.4Γ the tokens. See
the benchmark.
What you get back. Each finding is structured data β file, line, severity, a title, an explanation, and an optional suggested fix β so it renders the same everywhere:
- On a pull or merge request β an inline comment on the exact changed line for each finding, plus one summary comment naming the model used. Re-running updates the summary instead of duplicating it, never repeats a finding it already posted, and a clean change gets a π LGTM!. On GitHub and GitLab a conversation auto-resolves once its finding is verified fixed; Gitea has no thread-resolution API, so its comments stay open. See Where it posts.
- On the CLI β
lgtmaybe reviewreads your localgitdiff and prints the findings (a readable listing, a JSON array with--json, or--format agentfor an AI coding agent to read and apply); nothing is posted anywhere.
Beyond the review, slash commands on the PR route to the same engine: /review and /improve post (or refresh) the review, /ask <question> answers a question about the change in-thread, /describe (auto_describe) posts a structured description, and /diagram (auto_diagram) posts a compact change diagram β an automatically laid-out Mermaid flowchart of the components the PR touches, with changes marked on nodes, plus a Mermaid sequence diagram of the run-time flow the change alters (omitted when there isn't one), both rendered natively in the comment, with a text fallback that also prints from lgtmaybe diagram locally. See Generate a change diagram.
On re-runs and big PRs the review stays cheap: a synchronize push triggers an incremental review of just the new commits, an optional cheap triage model (triage_model) skips plainly-non-substantive files before the strong model runs, and optional static-analysis fusion (static_analysis) runs deterministic tools over the changed files β ruff/bandit/mypy/semgrep feed the review as untrusted hints, while gitleaks (secrets), zizmor (GitHub Actions workflow security), ast-grep (your own structural rules) and osv-scanner (known dependency vulnerabilities) post findings directly with no model call. semgrep ships with a small MIT rule pack, so it works without configuration.
On a GitHub PR β an inline comment on the changed line. The same findings on the CLI:
A fuller walkthrough with example output is in What gets reviewed.
From inside a git repo, on a branch with changes, review your diff against the remote primary branch and print the findings:
pip install lgtmaybe # or Homebrew β see docs/how-to/install-the-cli.md
lgtmaybe review \
--provider ollama \
--model qwen3.6:27b \
--api-base http://localhost:11434No GitHub token and no pull request needed β lgtmaybe review reads your local
git diff and prints the findings. Its companion, lgtmaybe diagram, takes the
same flags and prints a picture of the components your change touches and the
flow it alters β review then diagram is the pair to run before opening a
pull request. See
Generate a change diagram.
lgtmaybe --help lists every command with usage examples; lgtmaybe review --help
shows the full option reference. To post reviews on real pull requests, wire up
the GitHub Action β or, on another host,
GitLab CI or
Gitea Actions. See
Getting Started for the full walkthrough.
Picking a model: compare cloud and local models separately. In the current cloud breadth results, Qwen 3.8 Max and GLM 5.2 scored 71.4% and 72.2% balanced F1; Qwen reported 8 false positives and GLM reported 24. The only current local breadth run is Qwen 3.6 35B at 57.1% balanced F1. See the cloud and local model guide, or inspect the live benchmark results.
| Provider | Auth | Guide |
|---|---|---|
openai |
OPENAI_API_KEY |
OpenAI |
anthropic |
ANTHROPIC_API_KEY |
Claude |
openrouter |
OPENROUTER_API_KEY |
OpenRouter |
zai |
ZAI_API_KEY β GLM / Zhipu AI (glm-4.6, glm-4.7, glm-4.5-air, β¦; newer glm-5.x too). Optional --api-base for the China / coding-plan endpoint |
z.ai (GLM) |
bedrock |
Ambient AWS creds β GitHub OIDC, no static key | Bedrock |
vertex |
Ambient GCP creds β Workload Identity Federation, no key | Vertex |
azure |
Ambient Azure AD creds β GitHub OIDC, no static key (or AZURE_API_KEY) + endpoint |
Azure |
ollama |
None β local only, zero cost | ollama |
openai-compatible |
Any OpenAI /v1 endpoint via --api-base (DeepSeek, llama.cpp, LM Studio, vLLM). Key optional β --api-key / OPENAI_COMPATIBLE_API_KEY, or none for local servers |
Local & OpenAI-compatible |
The model provider and the code host are independent choices β any provider above works on any host below.
| Host | How it runs | Token | Guide |
|---|---|---|---|
| GitHub | GitHub Action (MattJColes/lgtmaybe@v2) |
GITHUB_TOKEN |
GitHub Action |
| GitLab | GitLab CI job (lgtmaybe gitlab-ci) |
GITLAB_TOKEN |
Review on GitLab |
| Gitea | Gitea Actions (same container) | GITEA_TOKEN |
Review on Gitea |
| None | lgtmaybe review on your local diff |
β | Install the CLI |
The review is the same everywhere β same lenses, same reflection pass, same findings. What differs is what each host's API can do with the result:
| GitHub | GitLab | Gitea | |
|---|---|---|---|
| Inline comments + summary | β | β | β |
| Slash commands | β | β | β |
| Auto-resolve a fixed finding | β | β | β no thread API |
| Incremental re-review | β | not yet | β no compare diff |
| Keyless cloud auth (OIDC/WIF) | β | β use an API key | β use an API key |
Browse the rendered docs at https://lgtmaybe.coles.codes/, or read the
Markdown sources below. For LLM agents, a curated
llms.txt index (and a
whole-corpus llms-full.txt)
are published at the docs root.
Tutorial β learn by doing
- Getting Started β your first review with ollama
How-to guides β task recipes
- Choose a review model
- Review with OpenAI
- Review with Claude (Anthropic)
- Review with OpenRouter
- Review with z.ai (GLM)
- Review with Bedrock OIDC
- Review with Vertex WIF
- Review with Azure OpenAI
- Run locally with ollama
- Local models & other OpenAI providers
- Use as a GitHub Action
- Review on GitLab
- Review on Gitea
- Configure .lgtmaybe.yml
- Releasing (maintainers)
Reference β look things up
- Configuration Reference β all config fields and schemas (generated)
Explanation β understand the design
- What gets reviewed β scope, caps, and what the output looks like
- Architecture β ports and adapters, the review pipeline
- Auth Model β why keyless cloud, how credential resolution works
- Data and Privacy β what is sent where, secret redaction, ollama local mode
- Trust and Cost β choosing who reviews run for (everyone, trusted contributors, or admins) and the small cost angle
Use lgtmaybe from the
GitHub Marketplace. It is a
GitHub Action, so its settings live in your workflow. In
.github/workflows/lgtmaybe.yml, set provider, model, and the matching
authentication input in the step's with: block. This complete example uses
OpenAI:
name: lgtmaybe
on:
pull_request_target:
issue_comment:
types: [created]
permissions:
contents: read
pull-requests: write
jobs:
review:
# A comment only starts a job when it carries one of lgtmaybe's slash
# commands β issue_comment fires on every comment on every PR.
if: >-
github.event_name == 'pull_request_target' ||
(github.event.issue.pull_request &&
(contains(github.event.comment.body, '/review') ||
contains(github.event.comment.body, '/improve') ||
contains(github.event.comment.body, '/ask') ||
contains(github.event.comment.body, '/describe') ||
contains(github.event.comment.body, '/diagram')))
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: MattJColes/lgtmaybe@v2
with:
provider: openai
model: gpt-5.5
api_key: ${{ secrets.OPENAI_API_KEY }}Using a different model provider? Copy-paste workflows for every cloud and
API-key provider live in
examples/workflows/. Cloud providers (Bedrock, Vertex,
Azure) are keyless β pass aws_role_arn / gcp_wif_provider /
azure_client_id and the action does the OIDC/WIF exchange for you (needs
id-token: write). See
Use as a GitHub Action. ollama is local
only β run it through the CLI instead.
lgtmaybe runs the same review on GitLab and Gitea. Only the wiring changes:
- GitLab β a CI job running
lgtmaybe gitlab-ci, gated on merge request pipelines. See Review on GitLab andexamples/gitlab/. - Gitea β Gitea Actions runs the same container as GitHub, because it
reimplements the same runtime. See
Review on Gitea and
examples/gitea/.
Keyless cloud auth is a GitHub Actions feature, so on GitLab and Gitea use an
API-key provider β or ollama against a runner-local model for zero cost.
By default, reviews post as github-actions[bot]. To post as
lgtmaybe[bot], install the public
lgtmaybe App, grant the
workflow id-token: write, and add github_identity: lgtmaybe beside the
provider settings. You never receive or manage the App's private key. lgtmaybe
is still the Action running in your workflow; the App changes only the GitHub
author identity. See
Post as lgtmaybe[bot].
π§ Choose who can trigger reviews. You decide who reviews run for β everyone, trusted contributors, or just admins. The example workflows default to trusted contributors (
OWNER,MEMBER,COLLABORATOR), and it's a one-line change to open it up or tighten it. With ollama this is free; on a hosted provider it also keeps token spend predictable. See Who can trigger a review and Trust and Cost.
- CLI (PyPI) β
pip install lgtmaybe - CLI (Homebrew) β
brew tap MattJColes/tap && brew trust MattJColes/tap && brew install lgtmaybe(details β thebrew truststep is required for third-party taps) - CLI (WinGet, Windows x64) β
winget install --id MattJColes.lgtmaybe --exact(details) - GitHub Action β
uses: MattJColes/lgtmaybe@v2
Test-first, green CI, scope is the gate. See CONTRIBUTING.md.
MIT β see LICENSE.
![An inline lgtmaybe review comment on a GitHub pull request flagging a [CRITICAL] SQL injection vulnerability, with an explanation and a suggested parameterized-query fix](/MattJColes/lgtmaybe/raw/main/docs/assets/marketplace/marketplace-screenshot-1.png)
