Configure .lgtmaybe.yml¶
Place a .lgtmaybe.yml file at the root of your repository to control how
lgtmaybe reviews pull requests. CLI flags override file values; the file
provides defaults for all runs.
Upgrading from 1.14¶
Remove prompt_cache from existing .lgtmaybe.yml files and remove
--prompt-cache or --no-prompt-cache from scripts. Prompt caching is now
automatic where the provider supports it.
Contents¶
- Upgrading from 1.14
- Full example
- Field reference
- provider
- model
- fallback_model
- min_severity
- include_paths / exclude_paths
- max_files
- max_input_tokens
- preset
- categories
- context_lines
- function_context
- timeout
- structured_output
- reflect
- min_confidence
- mid_review_retrieval
- incremental
- static_analysis
- triage_model
- auto_diagram
- auto_describe
- high_impact
- pr_labels
- finding_rules
- summary_template
- resolve_fixed
- extra_lenses
- lens_paths
- directory_rules
- CLI flag overrides
Full example¶
provider: openai
model: gpt-5.5
min_severity: low
include_paths:
- "src/**"
- "lib/**"
exclude_paths:
- "**/__pycache__/**"
- "**/*.min.js"
max_files: 30
max_input_tokens: 80000
categories:
- security
- correctness
- tests
Field reference¶
See Reference: Config for the full schema with types and defaults.
provider¶
Which LLM backend to use. One of openai, openrouter, anthropic, zai,
bedrock, vertex, azure, ollama, openai-compatible.
provider: anthropic
model¶
The model identifier for the chosen provider. Format varies by provider:
| Provider | Example model IDs |
|---|---|
| openai | gpt-5.5 |
| anthropic | claude-sonnet-4-6, claude-haiku-4-5 |
| openrouter | anthropic/claude-sonnet-4-6 |
| zai | glm-4.6, glm-4.7, glm-4.5-air (GLM / Zhipu AI; newer glm-5.x pass through too) |
| bedrock | us.anthropic.claude-sonnet-4-6, us.anthropic.claude-haiku-4-5 (prefer the cross-region inference profile; a non-Bedrock id like openai.gpt-5.5 is invalid — see Review with Bedrock) |
| vertex | gemini-3-pro, gemini-3.5-flash |
| azure | your deployment name, e.g. my-gpt-4o-deployment (not the upstream model id — see Review with Azure) |
| ollama | qwen3.6:27b, gemma4:e4b |
| openai-compatible | the served model name, e.g. deepseek-chat or meta-llama/Llama-3.1-8B-Instruct (requires api_base — see Use a custom OpenAI-compatible endpoint) |
fallback_model¶
fallback_model names one second model for a review call that the primary
model cannot finish. It is off by default and adds no calls to a healthy
review.
Both model names go through the configured provider, api_base, and
credentials. This means an OpenRouter review can fall back from a Google model
to a Qwen model because OpenRouter serves both. An openai review cannot fall
back to an Anthropic account. Cross-provider failover needs a separate review
run.
provider: openrouter
model: google/gemini-3.7-flash
fallback_model: qwen/qwen3.8-max
The fallback runs at different points depending on the failure:
| Primary model result | What lgtmaybe does |
|---|---|
| Transient provider failure, such as a connection error, capacity rate limit, or 5xx | Exhausts the primary model's bounded retry policy, then tries the fallback model. |
| Permanent provider failure, such as an unknown model, rejected request, or exhausted quota | Skips repeated primary attempts and tries the fallback model. A shared credential, quota, or endpoint failure will usually reject both. |
Output reaches the max_tokens ceiling |
First retries a smaller payload or lowers reasoning_effort on the primary, based on the reported token counts. It tries the fallback only if that remedy also fails. |
The fallback does not judge the primary answer. A clean review, a low-quality finding, or zero findings will not trigger it. Output parsing has its own bounded repair path.
The fallback gets a fresh request and retry budget, so it adds to the primary
model's spent time and tokens. The profile keeps both models' calls and the
review summary names every lens answered by the fallback. Use --profile for
the human-readable models: line or --profile-json for the model attached to
each call. Its usage contributes to max_review_tokens after the call returns.
See Profile Reference.
If the fallback fails, lgtmaybe marks the lens incomplete and stops that recovery path. A review with no usable results fails instead of posting a clean result. lgtmaybe does not walk a list of models or fall back to the primary again. A lens that repeatedly needs the second model is a signal to make that model the primary.
Set the same value for one run with --fallback-model. The CLI flag and the
GitHub Action's fallback_model input override .lgtmaybe.yml.
lgtmaybe review --fallback-model qwen/qwen3.8-max
See Choose a Review Model for current model results.
min_severity¶
The minimum severity level to report. Findings below this threshold are
suppressed. Ordered low to high: info, low, medium, high, critical.
min_severity: medium # suppresses info and low findings
Default: low (suppresses only info findings).
include_paths / exclude_paths¶
Glob patterns to restrict which files in the diff are reviewed.
include_paths acts as an allowlist; exclude_paths acts as a denylist applied
after the allowlist, so an exclude always wins. Both default to empty (all files
included). Patterns match against the full repo-relative path, and a
**/-prefixed pattern also matches at the repo root (so **/*.lock covers a
root-level lockfile). The built-in skip filter for generated, vendored, and
binary files runs first either way — an include_paths entry can't resurrect a
lockfile.
include_paths:
- "src/**"
exclude_paths:
- "src/generated/**"
- "**/*.lock"
max_files¶
Maximum number of changed files to include in the review. Files beyond this limit are skipped. Reduces token usage on large PRs.
max_files: 30
Default: 50.
max_input_tokens¶
Token budget per model call. When the compressed diff exceeds this limit,
lgtmaybe splits it across multiple batched calls (and, with recursive on,
walks an over-budget single file hunk-by-hunk) — nothing is truncated or
dropped.
max_input_tokens: 80000
Default: 100000.
preset¶
How many model calls the review spends. fast (the default) covers all nine
categories in four calls, one per concern:
| Call | Covers |
|---|---|
| security | security |
| correctness | correctness, and stated intent when the PR states one |
| code health | performance, complexity, ponytail, deprecation |
| artefacts | tests, documentation |
The same four run on every provider — worker count changes only how they are
scheduled, not how many there are. full runs one focused call per lens for
release branches and deep audits.
preset: full
Default: fast. CLI: --preset fast|full (--full is shorthand). An explicit
categories list (below) overrides the preset grouping.
categories¶
Which review lenses to run. An explicit list disables the preset grouping: the
reviewer asks for each listed category in its own concurrent model call and
merges the findings, so a focused prompt concentrates on one concern at a
time. One or more of security, correctness, deprecation, tests,
documentation, performance, complexity, intent, ponytail. Narrowing
the list trades thoroughness for fewer model calls (and lower token usage).
The ponytail lens is the "lazy senior dev" check — the best code is the code
you never wrote — flagging code that needn't exist at all (YAGNI, reach for the
standard library, do it in fewer lines). See
What gets reviewed.
The intent lens checks the diff against the PR's stated intent — title,
description, and commit names on GitHub; your git log commit names on the
CLI (in both branch and --working mode). When nothing states an intent (e.g.
no commits beyond the base branch yet), it is skipped automatically, so it
never costs an extra call (under the fast preset it shares correctness's
call). It is also the only lens that sends the PR title/description/commit
names to the provider — drop it from categories if you don't want that text
sent at all.
categories:
- security
- correctness
Default: all nine categories.
context_lines¶
Ceiling on the number of unchanged lines added around each changed hunk. The
lines are read from the head revision of the file, so the model reviews a
change in the context of its surrounding code. The pad is asymmetric: the full budget goes
before the hunk (the enclosing signature and setup explain a change best) and a
quarter of it — at least one line — goes after. The actual number used is the
smaller of this ceiling and what the token budget allows, so it shrinks
automatically on large PRs. Set it to 0 to disable context expansion and
review the bare diff (no extra file content is fetched).
context_lines: 10 # at most 10 lines before each hunk (2 after); 0 disables
Default: 20.
function_context¶
Extend each hunk's leading pad up to the enclosing function or class
signature when it sits above the fixed context_lines window — the
signature and setup explain a change better than an arbitrary cut. Boundaries
are found structurally with ast-grep (already bundled for symbol resolution;
parsing only, never executing) for Python, JS/TS/TSX, Go, Rust, Java, and
Ruby, with a bounded reach so a distant definition can't drown the diff.
Unsupported languages and any ast-grep failure keep the plain fixed-line pad.
function_context: false # fixed-line padding only
Default: true.
timeout¶
Per-request timeout in seconds for each model call. Left unset, lgtmaybe picks a provider-aware default: 1800 s for ollama, openai-compatible, and openrouter (local models are slow, and openrouter can route to slow reasoning models) and 600 s for direct cloud providers. Set it explicitly to raise it for a large local model.
timeout: 3600 # an hour per call, e.g. for a big model on CPU
Default: auto (ollama/openai-compatible/openrouter 1800 s, cloud 600 s). See Run locally with ollama.
structured_output¶
Constrain the model to emit the findings JSON schema using the provider's native
JSON mode (litellm response_format). This keeps models — especially local ones —
from returning prose or reasoning instead of findings. Leave it on unless the
combination in front of you fails in one of two ways:
- A gateway rejects
response_formatoutright (someopenai-compatiblegateways return a400). The review fails loudly; turn this off. - A gateway accepts JSON-schema mode but returns replies the parser reads
as empty. This fails silently: every lens logs
findings: 0 parsed / 0 returned, the run exits clean, and a capable model looks like one that finds nothing. Seen withanthropic/claude-fable-5.1behind OpenRouter on lgtmaybe 2.3.0 and 2.7.0 — the same diff with the same flags returned 11 parsed findings once this was off. If a model is unexpectedly silent, re-run one diff with--no-structured-outputbefore concluding the model finds nothing.
In both cases the lenient parser still strips fences and pulls JSON out of any
surrounding prose. CLI: --no-structured-output.
structured_output: false # if your gateway rejects JSON-schema mode, or a model returns zero findings under it
Default: true. See
Use a custom OpenAI-compatible endpoint.
reflect¶
Run the self-reflection pass that audits the merged findings and drops the
ones the model marks low-confidence, before anything is posted. This trims false
positives, so leave it on for most models. Turn it off for a weaker or local
model that over-prunes and drops valid findings during the audit. CLI:
--no-reflect.
reflect: false # keep every finding; skip the false-positive audit
Default: true. To audit a weak reviewer's findings with a stronger model
instead of disabling the pass, set reflect_model to that model id (it uses the
same provider and credentials as model).
min_confidence¶
During reflection the auditor also scores each kept finding's confidence from
0 (certainly a false positive) to 10 (certain it is real), reached by actively
trying to disprove the finding against the diff and the file text. Findings
scored below min_confidence are dropped before posting; the surviving
score is shown in the CLI output, the JSON export, and — as a percentage — on the
posted GitHub comment itself: **[HIGH · security · 80%] Title**, alongside the
lens that raised it. A finding the auditor keeps but doesn't score always survives the
threshold — a missing score never drops a real finding, and the comment simply
omits that half of the badge (as it does for every finding when reflect:
false). CLI: --min-confidence.
min_confidence: 5 # drop findings the auditor scores 0-4
Default: 0 (no numeric filtering — reflection prunes only via its keep/drop
verdicts, as before the score existed).
mid_review_retrieval¶
Let a review lens defer once for bounded, read-only codebase context.
By default every lens is told the diff is only a slice of the codebase: if a finding depends on code it cannot see — a guard, a base class, the helper it calls — it must hedge the wording, lower the severity, or say nothing. That protects you from confident cross-file nonsense, and it also means a real bug whose evidence lives one file away is never reported.
With this on, the lens gets a third option: alongside its findings it names the files (or symbols) it must read. lgtmaybe fetches them read-only — the same API/worktree read the reflection auditor's deferral uses, never a checkout of PR code — redacts them, and re-runs that one lens with them in front of it. Both calls' findings are kept and deduped, so a deferral can only add findings.
mid_review_retrieval: true # trade tokens for cross-file recall
Default: false. It is opt-in because of the price: up to one extra model
call per (batch, lens), carrying the fetched text as well. Every other bound is
fixed — one hop (the re-run cannot defer again), at most five files inside a
quarter of max_input_tokens, and a deferral arriving past max_review_seconds
or max_review_tokens is skipped and reported in the summary. Read
Reduce Review Cost before
turning it on, and pair it with max_review_tokens if the worst case matters.
CLI: --mid-review-retrieval; Action input: mid_review_retrieval.
incremental¶
Commit-scoped incremental review, for the GitHub posting path. On a re-run lgtmaybe reads a hidden watermark (the head SHA its last completed review covered) from its own summary comment and reviews only the diff of the commits pushed since, instead of the whole PR — faster, cheaper, and no re-noise on code that was already reviewed. New findings post as inline comments; findings on files outside the increment stay open, and are only auto-resolved by a run that actually re-reviewed their file.
It always degrades to a full review when there is no watermark yet (first
review), after a force-push/rebase (the increment would be meaningless), or if
the compare fails. A failed review never moves the watermark, so no commit is
ever silently skipped. Comment /review full on the PR to force a full
re-review on demand.
incremental: false # every run reviews the whole PR
Default: auto — incremental on a synchronize push (new commits on an
already-reviewed PR), full review everywhere else (open/reopen, slash
commands, and the local CLI, which never uses it).
static_analysis¶
Static-analysis fusion: run fast, deterministic tools over the changed files. Each tool reaches the review in one of two modes:
hint— findings become hints for the model to confirm, contextualise, or discard. This raises recall on exactly the mechanical bugs LLMs miss without posting raw linter noise; only findings the model itself confirms are reported. The default for ruff, bandit, mypy and semgrep.finding— findings are posted directly, with no model call at all. The default for gitleaks, zizmor, ast-grep and osv-scanner. Deterministic, free, and identical run to run.
The split is about the tool, not taste: a committed credential is present or it
isn't, so asking a model to "confirm or discard" a regex match only adds latency
and a chance of it talking itself out of a real hit. A lint or a SAST heuristic
is the opposite — often technically true and beside the point — which is exactly
what the model is good at filtering. Override either way with tool_mode.
Supported tools: ruff, bandit, and mypy (Python), gitleaks
(secrets, any language), zizmor (GitHub Actions workflow security — template
injection, unpinned uses, over-broad permissions; it runs only when the PR
changes a workflow file), ast-grep (your own structural rules — the
deterministic sibling of extra_lenses; set ast_grep_rules), osv-scanner
(known vulnerabilities in your dependencies), and semgrep (multi-language
SAST).
osv-scanner is the one check a model genuinely cannot do: whether a pinned dependency has a published advisory depends on what was disclosed this week, and a model's knowledge cutoff makes any answer it gives unreliable. It reads the dependency manifests and lockfiles your PR changes — fetched into a scan-only channel that never enters the diff, a prompt, or the reflection pass, because nobody wants a model commenting on line 84,000 of a lockfile.
Its findings are about the dependency rather than a line, so they render in the
review body rather than as inline comments, and only high/critical
advisories surface by default (unanchored_min_severity).
It needs a local vulnerability database — the sandbox has no network, so it can
never fetch one. The GitHub Action image ships one, baked in at build time. On
the CLI, seed it once with osv-scanner --download-offline-databases; without a
database the scanner reports nothing, and the review says so rather than passing
it off as clean.
When osv-scanner is set to post findings, the review prompt stops asking the
model whether a version has a known advisory or a package is abandoned — a
knowledge cutoff cannot answer either, so asking would only put a confident
guess beside an accurate answer. Everything a vulnerability database cannot
answer stays with the model: deprecated APIs, end-of-life runtimes, typosquats
and licence conflicts. Demote osv-scanner to hint and the model takes those
claims back.
The same happens for gitleaks. When it is set to post findings, the review
prompt stops asking the model for hardcoded secrets — and here the ask was
close to unanswerable anyway: lgtmaybe redacts every secret it matches before
the diff leaves for the provider, so the model was being asked to spot what it
had been prevented from seeing, while gitleaks reads the unredacted file text
and answers exactly. Secrets reaching a log are a different defect that no
secret scanner reports, so the model keeps that one. Demote gitleaks to hint
and the model takes the claim back.
semgrep now works out of the box. It used to skip itself unless you set
semgrep_rules, which almost nobody did — so the one multi-language tool never
ran. It now falls back to a small, high-precision MIT rule pack shipped with
lgtmaybe, and semgrep_rules overrides which rules it uses. Rules are always
read from a local path: semgrep's registry configs need the network, which the
sandbox forbids.
We ship our own rules rather than bundling a well-known upstream pack because
the widely-used semgrep-rules / opengrep-rules collections are LGPL-2.1 plus a
Commons Clause — not an open-source licence, and not something an MIT package
can redistribute honestly. Point semgrep_rules at a directory to use a fuller
pack you have obtained yourself.
Two rules keep direct posting honest. Tools read whole files, but only the diff is under review, so a finding on a line this PR did not change is dropped and counted in the summary — otherwise a fake credential in a test fixture would post on every PR that touches that file, forever. And direct findings are capped per review, most severe first, since no model is there to filter volume.
Posted findings carry a scan:<tool> category, so finding_rules can drop or
re-grade a scanner without turning it off:
finding_rules:
- match: {category: "scan:gitleaks", path: "tests/fixtures/**"}
action: drop
mypy earns its place on unguarded-Optional bugs: a dict.get() narrowed
to str | None and then dereferenced is a crash a review lens reads straight
past, and mypy proves it from the file's own text in seconds. It runs with
--ignore-missing-imports --follow-imports=skip, because the sandbox holds only
the changed files and everything they import is absent by construction —
so it reports what it can prove from a single file, and stays quiet about the
rest (untyped code and unresolvable imports produce nothing).
The tools run against the already-fetched file texts in a throwaway directory
(never a checkout, never executing PR code), in a subprocess with a scrubbed
environment (no proxy or credential variables) and a hard timeout. A tool that
isn't installed is skipped silently — install them with
pip install lgtmaybe[static-analysis], or rely on whatever is already on
PATH. Tool output is treated as untrusted text: redacted and
injection-wrapped before it reaches the model. CLI:
--static-analysis/--no-static-analysis.
static_analysis:
enabled: true
tools: [ruff, bandit, mypy, gitleaks, zizmor] # default: all supported tools
min_severity: low # floor on mapped tool severity (default info)
tool_min_severity: # per-tool overrides of the global floor
ruff: medium # only medium+ from ruff; bandit keeps `low`
tool_mode: # per-tool overrides of hint vs finding
gitleaks: hint # route secrets through the model instead
# semgrep_rules: .semgrep.yml # override the bundled MIT pack
# ast_grep_rules: .ast-grep/ # your own structural rules (skipped when unset)
Default: enabled: false — no subprocess ever runs and behaviour is
unchanged.
triage_model¶
Two-stage model routing so routine PRs don't pay frontier prices while risky
ones still get the strong model. When set, this cheap model runs first
over the compressed per-file diffs. It skips files that plainly need no review
(pure formatting, trivial renames, generated churn) and scores the rest 0–10
by risk; the strong model then does the deep per-lens review only on the
survivors, riskiest first. Skipped files are listed in the review summary, and
/review full reviews everything on demand.
A deterministic security floor always escalates past triage, whatever the cheap model says: security-relevant paths (auth/crypto/session code, migrations, IaC, CI workflows, dependency manifests), patches carrying security-relevant tokens, files with static-analysis hits, and large hunks. Any triage failure — an unparseable verdict, a provider error — reviews everything.
All three model slots (triage_model, model, reflect_model) resolve
through the same provider and credentials, so pointing them all at one ollama
model costs nothing. Trade-off: cheaper, faster reviews at the risk of the
triage model under-rating a subtle change; the floor and the
review-when-unsure prompt bound that risk, but for maximum recall leave triage
off. CLI: --triage-model.
triage_model: claude-haiku-4-5 # cheap gatekeeper; unset = no triage
Default: unset (no triage — every file gets the full review, exactly as before).
auto_diagram¶
Post the change overview as a comment after the review, and refresh it on
every push: a description of the change, a High Impact Areas section, a
Mermaid flowchart of the components the PR touches, and — when the change alters
a run-time flow — a Mermaid sequence diagram of it. One comment, updated in
place, never duplicated. /diagram posts the same overview on demand.
This is the switch for the whole comment: with it off, nothing auto-posts besides the review itself.
auto_diagram: false
Default: true. See Generate a change
overview.
auto_describe¶
Head the change overview with a structured description: a suggested title, the change type, a short summary, a per-file walkthrough table, and — when the PR states an intent — a "does it do what it says" check. Its own model call, best-effort: a failure leaves a short note in that slot and never blocks the rest of the comment.
Because it rides the overview comment, it refreshes on every push rather than
only on open. /describe still posts a description as its own separate comment,
whether or not this is enabled.
auto_describe: false
Default: true.
high_impact¶
Include the High Impact Areas section in the change overview — the changes a reviewer must not miss, in ten areas: infrastructure, security posture, availability (anything that could cause a production outage), data migrations, backups and recovery, compatibility, observability, dependencies, cost, and compliance.
Deterministic path patterns ground the model call as untrusted hints and floor
its output, so a sensitive file (a *.tf, a workflow, a migration, a backup
policy, a lockfile) is named even when the model says nothing about it. A failed
call degrades to that floor rather than dropping the section, and an empty
result says what was checked.
high_impact: false
Default: true. See Generate a change
overview.
pr_labels¶
Attach labels derived from the finished review — no extra model calls:
review-effort/1…review-effort/5— a size estimate from the changed lines, so reviewers can gauge the PR at a glance;possible-security-issue— a high/critical finding from the security lens was posted;consider-splitting— the diff spans many unrelated top-level directories.
Labels are reconciled on each run (a stale review-effort/2 is removed when
the score changes) and only lgtmaybe's own label families are ever touched.
Best-effort: a labelling failure never fails the review.
pr_labels: true
Default: false.
finding_rules¶
Declarative post-processing applied to findings just before posting — the
safe alternative to arbitrary post-processing hooks (rules can only filter or
re-grade; no user code ever runs). Each rule has a match (all specified
fields must match) and an action; rules apply in order.
Match fields: path (glob, **/-prefix also matches at the repo root),
category (the lens that produced the finding — security, correctness,
…, or a custom lens id), title_contains (case-insensitive substring), and
min_severity (at or above). Actions: drop: true or set_severity.
finding_rules:
# complexity nits in tests aren't worth a comment
- match: {path: "tests/**", category: complexity}
action: {drop: true}
# documentation findings are informational for this repo
- match: {category: documentation}
action: {set_severity: info}
Default: no rules.
summary_template¶
Custom template for the review summary line, for teams matching a house
style. Placeholders: {count} (findings posted), {provider}, {model},
{version} (the lgtmaybe release that produced the review). A template that
fails to format falls back to the built-in line.
summary_template: "🤖 {count} finding(s) · {model} · lgtmaybe {version}"
Keep {version} if you can: the same model on the same provider reviews
differently across releases, so it is the handle that makes a surprising review
traceable to the code that produced it.
Default: unset (the built-in
N findings · provider X · model Y · lgtmaybe Z line).
resolve_fixed¶
Auto-resolve a review conversation once its finding is fixed. On a re-run, when a
finding lgtmaybe raised is no longer produced and GitHub marks that thread
outdated (the code under it changed), lgtmaybe posts a short ✅ Looks resolved.
reply and resolves the conversation. Both conditions must hold, so a thread is
never collapsed just because nearby lines shifted. Set it to false to leave
conversations for manual resolution.
GitHub posting only — the local CLI review has no conversations to resolve, so it
ignores this. Resolving a thread uses GitHub's GraphQL API; the default
GITHUB_TOKEN (pull-requests: write, already needed to post the review) is
sufficient.
resolve_fixed: false # leave fixed conversations open for manual resolution
Default: true.
extra_lenses¶
Define your own review lenses ("BYO skills") that run alongside the built-in
categories. Each one fans out as its own focused model call and its findings
merge into the same review. A lens needs an id (unique, and not one of the
built-in category names) and instructions describing what to look for; a
title, plus a worked example (example_diff + example_finding, supplied
together) are optional but sharply improve a small model's output.
extra_lenses:
- id: simplify
title: Simplify or delete
instructions: |
Flag code that should not exist at all. The best code is the code you never
wrote: prefer the standard library, an existing dependency, or one line over
a new abstraction. Call out needless wrappers, premature generality, and
"just in case" code with no caller.
example_diff: |
--- a/util.py
+++ b/util.py
@@ -4,1 +4,3 @@
def get_name(user):
+ name = user.name
+ return name
example_finding:
path: util.py
line: 5
severity: low
title: Needless local variable
body: The temporary adds nothing; return user.name directly.
suggestion: " return user.name"
Lens definitions are trusted config: they go into the system prompt, so only
define them in files you control (committed .lgtmaybe.yml or repo skill files),
never from PR-author content. See
Add a custom review lens for a full walk-through.
Default: none.
lens_paths¶
Load extra_lenses from separate skill files instead of inlining them — handy
for sharing a lens across repos or wiring lgtmaybe into an agent harness. Each
entry is a YAML file (one lens, or a list of lenses) or a directory of *.yml /
*.yaml lens files. Paths are resolved relative to where lgtmaybe runs (your repo
root). Lenses loaded this way are appended to any inline extra_lenses.
lens_paths:
- .lgtmaybe/skills # a directory of one-lens-per-file skill files
- team-lenses/house-style.yml # or a single file
Default: none.
directory_rules¶
Scope extra review instructions and reference files to part of the repo —
what a monorepo needs when payments/** and tests/** deserve different
treatment. Each rule has paths (fnmatch globs; a **/ prefix also matches at
the repo root, and an empty or omitted list applies the rule everywhere),
free-text instructions, and context_files read from the checked-out
workspace. A rule reaches every lens reviewing a file it matches, and no other.
directory_rules:
- paths: ["payments/**", "billing/**"]
instructions: |
Money-handling code. Treat rounding, currency-conversion and retry
changes as high severity, and flag any write path that is not idempotent.
- paths: ["tests/**"]
instructions: |
Test code — do not flag duplication, long functions, or missing
docstrings here.
- paths: ["src/**"]
instructions: Check changes against the architecture described below.
context_files: [ARCHITECTURE.md]
Context files are redacted like the diff, share max_input_tokens / 8, and are
capped at five files per review; a missing or over-budget path is skipped
silently. Both the instructions and the context text are trusted config — on
pull_request_target they come from the base branch, never the PR head. See
Scope review instructions to a directory.
Default: no rules.
CLI flag overrides¶
Every config field can be overridden at the command line:
lgtmaybe review \
--provider anthropic \
--model claude-sonnet-4-6 \
--min-severity high
Flags take precedence over .lgtmaybe.yml.