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 call

Press ? 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 uninstall

pre-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 hook

sp 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.