sp lgtm
sp lgtm generates a few multiple-choice questions about a range of commits and
asks them in your terminal. The questions are about what the change does: what
a new guard prevents, what the error path now returns, which edit can touch
existing rows. Never about statistics, naming, or formatting.
sp lgtm # what this branch adds since it left main
sp lgtm main...feature/x # someone else's branch, before you merge it
sp lgtm -n 3 -d hard
sp lgtm --dry-run # what it would ask about, no model callPress ? on any question to print the hunk it came from.
Options
| Option | |
|---|---|
revspec |
What to review, such as main...HEAD or abc123..def456. Defaults to what this branch adds since it left the default branch. |
--staged |
Quiz what is staged rather than a range. What the pre-commit hook uses. |
-n, --questions |
How many questions to ask, 1 to 5. Overrides the config file. |
-d, --difficulty |
easy, medium or hard. Overrides the config file. |
-p, --provider |
Who writes the questions: anthropic, openai, gemini, optionally vendor:model. |
--verifier |
Who tries to refute them. Defaults to the proposer. |
--model |
Model for the proposer, overriding the vendor's default. |
--dry-run |
Show what would be quizzed and stop, without calling the model. |
--hook, --blocking, --force |
Hook management, below. |
Configuration
Reads .github/lgtm.yml from the repository you are
in, the same file the Action uses, so a repository is configured once.
questions: 2 # 1-5
difficulty: medium # easy | medium | hard
provider: anthropic # anthropic | openai | gemini, or vendor:model
verifier: openai # optional, defaults to the proposer
exemptPaths:
- "docs/**"provider and verifier are read here but validated by the provider layer, so
a typo is reported with the list of known vendors rather than silently ignored.
Keys that only mean something to the Action are accepted and ignored. -n and
-d override the file.
Lockfiles, dist/, vendor/, *.pbxproj and their kin are never quizzed.
As a git hook
sp lgtm install # pre-push, advisory
sp lgtm install --hook pre-commit # earlier, and once per commit
sp lgtm install --blocking # wrong answers stop the push
sp lgtm uninstallpre-push is the default. When a model wrote the diff, nobody in the loop
wrote it, and the author is as much a reader as anyone. So the useful question
is not who typed it, it is where the last cheap moment to catch it is, and that
is before the code leaves your machine.
It also matches how the cost lands. Generation is three model calls and about a
minute. Per push that is fine. Per commit it taxes every checkpoint you save.
--hook pre-commit is there if you want it, and quizzes the staged diff.
Advisory by default. A hook that costs a minute and can stop you is one
you delete within a week. --blocking is opt-in, and even then only a wrong
answer blocks. Could-not-ask, meaning no API key, a vendor outage, or nothing
quizzable, never costs you a push.
Escape hatches, in the order you will want them:
SP_LGTM_SKIP=1 git push ... # skip this once
git push --no-verify ... # skip every hooksp lgtm install refuses to overwrite a hook it did not write, which --force
overrides, and it honours core.hooksPath. Writing to an assumed .git/hooks
when that is set installs a hook that never runs, which looks exactly like
success.
What the hook reads
pre-push quizzes exactly what you are about to push. Git names the refs on
stdin, so the hook takes the range the remote does not have yet. A branch the
remote has never seen has no such range, so it falls back to what the branch
adds since it left the default branch. A push that only deletes a remote branch
reads nothing at all.
That ref list arrives on the same stdin the quiz needs for answers, so the hook consumes it before attaching to your terminal.
It skips itself silently when there is no terminal, such as a rebase, a GUI client, or CI. Git runs hooks with stdin closed, and where nobody can answer, nobody failed.
Exit codes
| Code | Meaning |
|---|---|
0 |
Confirmed |
1 |
Not confirmed, meaning wrong answers or you quit |
2 |
Could not ask, meaning no API key, git failed, or nothing quizzable |
If you wire this into anything of your own, treat only 1 as a failure. A tool
that cannot run must not block a commit.