lsd lazy-senior-dev
GitHub
v0.2.0 · plugin for 14 agents · GitHub Action · Apache-2.0

Your agent writes fast.
The Grump reads first.

The staff engineer who has rejected 4,000 pull requests, inside your coding agent. Ten questions, one verdict, and a hook that denies the write when the answer is BLOCK. Fourteen agents and a GitHub Action. Zero false alarms in the benchmark. He approves with one word.

The Grump: a change flows in, is reviewed and gated, and the branch stays clean.
Agentabout to writeEdit · Write · git commit
Checklistten questionsanswered in writing
VerdictGRUMP: BLOCKfile:line — failure — fix
GatePreToolUse hookdeny · nag · allow
Branchonly what passedAPPROVE → Fine.

BLOCK is never downgraded. That is the promise.

Live

What getting grumped looks like

A PreToolUse hook reads the verdict the agent just printed. BLOCK denies the write in every mode. REQUEST_CHANGES denies only in gate mode. APPROVE goes through.

Recorded

Watch him work on every agent

The same staged diff, one CLI, 4 agents. Real runs captured from the terminal transcript and rendered frame by frame, nothing typed by hand and nothing cut. Captured 2026-09-13.

Verdict
What The Grump concluded. APPROVE lets the change through, REQUEST_CHANGES asks for fixes, BLOCK stops it.
Findings
How many numbered problems were listed. Each one names a file, a line, what breaks, and the smallest fix.
Time
How long the review took, start to finish, on this machine.
Tokens
What the host reported reading and writing. Some hosts report nothing, and the card says so rather than guessing.
Terminal recording of the Grump reviewing a staged diff with Claude Code: GRUMP: REQUEST_CHANGES
Claude Code
Verdict
GRUMP: REQUEST_CHANGES
Findings
2
Time
10 s
Tokens
7,966 in / 594 out
Terminal recording of the Grump reviewing a staged diff with Codex CLI: GRUMP: BLOCK
Codex CLI
Verdict
GRUMP: BLOCK
Findings
1
Time
7 s
Tokens
15,580 in / 309 out
Terminal recording of the Grump reviewing a staged diff with Antigravity CLI: GRUMP: REQUEST_CHANGES
Antigravity CLI
Verdict
GRUMP: REQUEST_CHANGES
Findings
1
Time
117 s
Tokens
29,104 in / 76,769 out
Terminal recording of the Grump reviewing a staged diff with IBM Bob Shell: GRUMP: BLOCK
IBM Bob Shell
Verdict
GRUMP: BLOCK
Findings
3
Time
15 s
Tokens
not reported
Why this and not that

You already have a rules file and a review bot

They fail in opposite directions. One is advice the agent may ignore; the other arrives once the code already exists.

A rules fileA pull-request reviewergrumpy-reviewer
When it runsEvery turn, as contextAfter the code is written and pushedBefore the write is allowed to land
When it disagreesNothing happensLeaves a comment to readThe Grump denies the write until it is fixed
What you can gate onNothingProseAPPROVE · REQUEST_CHANGES · BLOCK, as JSON
Where it worksOne format per host, by handThe forge you host on14 agents, any MCP client, a GitHub Action
How you know it helpsYou do notThe vendor's own blogTwo benchmarks here, raw replies committed

The first column is not a strawman. Anthropic's documentation calls a rules file “context, not enforced configuration” and says that to block an action regardless of what the model decides, you need a PreToolUse hook. That hook is what this is.

Try it

Sixty seconds, nothing installed

You already have a coding agent signed in. Point the Grump at your working tree with it. Exits 1 on anything but APPROVE, so it drops straight into a pre-commit hook.

Any repo, any agent you have
npx github:lazy-senior-dev/grumpy-reviewer review
A pull request, via gh
npx github:lazy-senior-dev/grumpy-reviewer pr 123

Finds claude, codex, agy, or bob (with BOB_API_KEY) on your PATH; add --agent codex to choose. Nothing leaves your machine except the diff, sent to the agent you already trust.

Install

Two commands, then forget he is there

Claude Code
/plugin marketplace add lazy-senior-dev/grumpy-reviewer
/plugin install grumpy-reviewer@lazy-senior-dev
Antigravity CLI
git clone https://github.com/lazy-senior-dev/grumpy-reviewer ~/.grumpy-reviewer
agy plugin install ~/.grumpy-reviewer
GitHub Action
- uses: lazy-senior-dev/grumpy-reviewer@v1
  env:
    ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}

IBM Bob, Codex, Copilot CLI, OpenCode, Cursor, Windsurf, Cline, Kiro, OpenClaw, Devin, Qoder and any AGENTS.md host (npx github:lazy-senior-dev/grumpy-reviewer install <host>): install table · what each host enforces. Uninstall is one command everywhere.

Before / after

The 2 a.m. handler

The agent wrote

# app/api/profiles.py
@bp.get("/me")
def me():
    payload = request.get_json(silent=True) or {}
    user_id = payload.get("user_id", session.get("user_id"))
    if user_id is None:
        abort(401)
    row = query_one("select id, name, email from users where id = %s", (user_id,))
    if row is None:
        abort(404)
    return jsonify({**row, "notifications": settings_for(user_id)})

The Grump said

GRUMP: BLOCK
1. app/api/profiles.py:14 — user_id is read from the request body, so any logged-in caller can read any profile by changing one number — take user_id from the session and ignore the body

write denied

The three-line fix

-    payload = request.get_json(silent=True) or {}
-    user_id = payload.get("user_id", session.get("user_id"))
+    user_id = session.get("user_id")
GRUMP: APPROVE — app/api/profiles.py
Fine.
Numbers

Defects caught, not lines saved

Thirty small diffs, each with one seeded defect, plus ten clean diffs to count false alarms. Same diff, same agent, same model, three ways: no skill, a generic "review carefully" prompt, and the Grump. Current models find the seeded bugs either way; what changes is discipline.

TBDdefects caught, with him / without
TBDfalse alarms on 10 clean diffs, with / without
TBDreplies without a verdict, with / without
TBDmedian review time, with / without
AgentModelArmDefects caughtFalse alarmsNo verdict (per run)BLOCK precisionMedian latency
Results load from data/latest.json. If this row is still here, run npm run bench and npm run bench:report.

Method, per-diff table, raw replies and limitations: benchmarks/results. Reproduce with npm run bench.

How it works

Ten questions, in order, one verdict

  1. Scope. Does it do what the ticket asked and nothing else?
  2. Inputs. Empty, absent, oversized, malformed, duplicated, concurrent. Where does each go?
  3. Errors. Where does each error go, and does the caller find out?
  4. Off-diff changes. Schema, config, env, permissions, flags: in the change or in the runbook?
  5. Dependencies. Is every new one earning its place?
  6. Trust boundaries. Secrets, PII, authn and authz, injection at every crossing.
  7. Tests. At the boundary where it breaks, not where it is convenient.
  8. Rollback. Revert and deploy, or a migration and a prayer?
  9. Observability. Would on-call understand the log line at 3 a.m.?
  10. Naming and dead code. Last. Never first.

Then one fixed block: GRUMP: APPROVE | REQUEST_CHANGES | BLOCK with numbered file:line — what fails in production — smallest fix lines. Modes: nag (default), gate, off. The whole ruleset is one file: rules/grump.md.

The promise

Grumpy, not negligent

He never rewrites your code, never expands scope, never bikesheds style while a correctness finding exists, and never blocks on taste. A BLOCK is reserved for data loss, secrets, auth holes, injection, and destructive operations, and no mode, schedule, or diff size can downgrade one. He can be wrong: say so in your own words, and the override is logged to the scorecard instead of pretended away.

Same desk

The rest of the cast

Open standards

Standards this implements

Citing a standard is easy; implementing one is the part that can be checked. Everything below is running in this repository today, and every body listed governs its specification in the open.

StandardGoverned byWhere it runs here
Model Context ProtocolOpen specification, Anthropic-originated, community-governedmcp/server.mjs, five tools over stdio, listed as io.github.lazy-senior-dev/grumpy-reviewer
SLSA build provenanceOpenSSF, Linux FoundationAttested on every release artefact; verify with gh attestation verify
SigstoreOpenSSF, Linux FoundationThe container image is signed keyless; verify with cosign verify
CycloneDXOWASP, standardised as ECMA-424A bill of materials on every release
SPDXLinux Foundation, ISO/IEC 5962A second bill of materials in the format ISO recognises
OpenSSF ScorecardOpenSSF, Linux FoundationScored weekly, badge above, results public
REUSE licence identifiersFree Software Foundation EuropeSPDX-License-Identifier on the files this project authors
AGENTS.mdAgentic AI Foundation, Linux FoundationGenerated from the ruleset for any agent that reads it
Agent SkillsOpen specificationskills/ and .github/skills/
SARIF 2.1.0OASISThe Action writes findings to sarif_file for any SARIF consumer
Apache-2.0Apache Software FoundationLICENSE and NOTICE