LeastGrant

Generated from compatibility/claude-code.json, the same record leastgrant doctor reads. Last verified 2026-09-02.

all agents

Claude Code

Partial LIVE VERIFIED 2.1.240 win32

the real agent ran with LeastGrant installed and enforcement was observed happening.

How it attaches

A `PreToolUse` and `PostToolUse` command hook in settings.json. The payload arrives on stdin; the verdict goes to stdout as `hookSpecificOutput`.

Install it with leastgrant install. Configuration is written to ~/.claude/settings.json, alongside anything already there — LeastGrant never removes a hook it did not add.

What a verdict does here

The three verdicts do not travel equally well. This is the table that decides whether LeastGrant is a prompt, a veto, or a suggestion on this agent.

verdictlands?what actually happens
allowyesprobehookSpecificOutput.permissionDecision
askdegradesprobeReaches a human when a human is there. In non-interactive mode (claude -p) a hook ask becomes a DENY. The README claimed an ask reaches you in every mode; probing showed otherwise.
denyyesprobeTwo channels: permissionDecision 'deny' and exit code 2. Honoured in the most permissive mode.

Interactive and unattended

This agent tells the hook which permission mode it is in — permission_mode, optional, top-level.

Modes it reports: default, plan, acceptEdits, auto, dontAsk, bypassPermissions. An ask reaches a person in default, plan, acceptEdits, auto, dontAsk, bypassPermissions. Interactively. Non-interactive (-p) turns any hook ask into a deny regardless of mode.

When the hook breaks

The question nobody asks until it matters: if LeastGrant crashes, times out, or cannot start, does the tool call still run?

  • On crash — the call runs anyway. (probe) Measured: hook exits 1 with non-JSON stdout, the tool ran and permission_denials was empty.
  • On timeout — the call runs anyway, after 600s by default. Measured: timeout 2 against an 8s hook, the tool ran and the deny was discarded.
  • Can it be made to fail closed? — no. No per-hook fail-closed option.

What it can see

A verdict is only worth as much as the set of actions it is asked about. gated means LeastGrant is consulted before the thing happens; seen after means it is told afterwards and can at best withhold the result; not covered means the action happens with LeastGrant never hearing about it.

tool classcoveragedetail
shell commandsgatedprobe
file readsgatedprobe
file writesgatedprobe
deletionsgatedprobe
MCP callsgatedprobe
subagent spawngatedprobeThe model emits `Task`; the payload and matchers see `Agent`, because toolAliases resolves the name before hooks run.
network / webgatedsourceWebFetch/WebSearch are ordinary tools.

What has actually been run

Four different things, deliberately not collapsed into one badge. Reproducing an agent's invocation is not running the agent; a passing conformance suite says our side is right and nothing about whether the host ever calls us.

  • Live agent test — LeastGrant installed into a real Claude Code session; PreToolUse and PostToolUse observed firing, a credential read prompted, and a completed call recorded as evidence. 2.1.240 · win32 · 2026-09-02
  • Real transport probed — The hook is invoked over native stdin with the settings.json command string; exercised by every hook test in the suite driving the real binary. 2.1.240 · win32 · 2026-09-02
  • Contract read from the shipped build — Hook schema, permissionDecision values and mode list read from the shipped 337 MB bin/claude.exe bundle and cross-checked against code.claude.com/docs/en/hooks. 2.1.240 · win32 · 2026-09-02
  • Conformance suite — Driven by test/conformance.test.ts. 2.1.240 · win32 · 2026-09-02

Not exercised on darwin, linux.

What it cannot do

Not a disclaimer. The point of everything above is that this list exists and is specific.

  • Managed policy `disableAllHooks: true` switches every hook off, including managed ones, with no signal LeastGrant can see. It is silently absent.
  • Fails open on crash and on timeout, so a LeastGrant that cannot start enforces nothing.
  • A hook ask in non-interactive mode is a deny, not a prompt.
  • The public docs list PreModelSwitch and PostModelSwitch, which do not exist in the installed 2.1.240 binary.
  • Only PreToolUse and PostToolUse are registered, so failed calls are never observed and their pending entries are never resolved.
  • Verified on Windows only. macOS and Linux run the same code path but nobody has driven the real binary there.