Skip to content

primitives

primitives

GateVerdict

Bases: BaseModel

LLM response model for llm_gate. The LLM sets block=True to deny.

NudgeVerdict

Bases: BaseModel

LLM response model for llm_nudge. The LLM sets fire=True to trigger the nudge.

PromptCheckVerdict

Bases: BaseModel

LLM response model for prompt_check. Action is "ok", "warning", or "block".

session_id_for

session_id_for(evt: BaseHookEvent) -> str | None

Return a 12-char sha256 prefix of the transcript path, or None if unavailable.

block_command

block_command(
    pattern: str | list[str],
    *,
    reason: str,
    hint: str | None = None,
    tests: InlineTests | None = None,
) -> None

Register a declarative hook that blocks a Bash command matching a pattern.

Example

block_command(["git", "stash"], reason="git stash is not allowed", hint="Use jj")

warn_command

warn_command(
    pattern: str | list[str],
    *,
    message: str,
    tests: InlineTests | None = None,
    events: Event = Event.PostToolUse,
) -> None

Register a declarative hook that warns on a Bash command matching a pattern.

Example

warn_command(["python", "-c", "*"], message="Prefer uv run mtest")

llm_gate

llm_gate(
    prompt: str,
    *,
    message: str | Callable[[GateVerdict], str],
    response_model: type[GateVerdict] = GateVerdict,
    verdict: Callable[[GateVerdict], bool] = lambda r: (
        r.block
    ),
    signals: Sequence[Signal | NlpSignal]
    | Signals
    | None = None,
    when: Callable[[BaseHookEvent], bool] | None = None,
    only_if: Sequence[TCondition] = (),
    skip_if: Sequence[TCondition] = (),
    events: Event | None = None,
    max_fires: int | None = None,
    tests: InlineTests | None = None,
    max_context: int = 2000,
    specialty: TSpecialty = "review",
    model: TModel = "small",
    agent: bool = True,
    transcript: bool = True,
) -> None

Register an LLM-powered blocking gate.

Defaults are tuned for the common case: agent=True and transcript=True so the gate has tool access and full transcript context. Pass agent=False, transcript=False for cheap, stateless yes/no checks.

Example

llm_gate("Is the agent making excuses?", ... message=lambda r: f"Excuse detected: {r.reasoning}", ... signals=Signals([Signal(r"external.*service", weight=2)], threshold=2))

llm_nudge

llm_nudge(
    prompt: str,
    *,
    message: str | Callable[[NudgeVerdict], str],
    response_model: type[NudgeVerdict] = NudgeVerdict,
    verdict: Callable[[NudgeVerdict], bool] = lambda r: (
        r.fire
    ),
    signals: Sequence[Signal | NlpSignal]
    | Signals
    | None = None,
    when: Callable[[BaseHookEvent], bool] | None = None,
    only_if: Sequence[TCondition] = (),
    skip_if: Sequence[TCondition] = (),
    events: Event | None = None,
    max_fires: int | None = None,
    tests: InlineTests | None = None,
    async_: bool = False,
    max_context: int = 2000,
    specialty: TSpecialty = "review",
    model: TModel = "small",
    agent: bool = True,
    transcript: bool = True,
) -> None

Register an LLM-powered advisory nudge.

Defaults are tuned for the common case: agent=True and transcript=True so the nudge has tool access and full transcript context. Pass agent=False, transcript=False for cheap, stateless yes/no checks.

Example

llm_nudge("Is the agent speculating instead of observing?", ... message="Observe, don't infer -- check traces first", ... signals=Signals([Signal(r"should contain", weight=2)], threshold=3))

prompt_check

prompt_check(
    evt: BaseHookEvent,
    template: str | PromptMessage,
    fmt: dict[str, Any] | None = None,
    *,
    prefix: str,
    suffix: str = "",
    timeout: int = 45,
    include_reasoning: bool = True,
    response_model: type[
        PromptCheckVerdict
    ] = PromptCheckVerdict,
) -> HookResult | None

Run an LLM check with a formatted prompt and return block/warn/None.

gate

gate(message: str, **kwargs: Any) -> None

Register a blocking gate — shorthand for nudge(message, block=True, ...).

Example

gate("Run tests before committing", when=lambda evt: not has_tests(evt))