filter
🦉 HooKit
ReferenceConfiguration

filter

Filter candidate fields and matching semantics per Event.

A Hook’s optional filter decides whether the Hook applies to an Event. It is an implicit AND across fields. A miss runs nothing—no Precondition, no shell, no Hook Result, no Action, and no Evaluation Report row.

Matching semantics

Every field value matches in one of four ways:

Value typeSemantics
stringJavaScript regular-expression source. ^ and $ anchors produce exact matching; unanchored patterns match anywhere.
number, boolean, nullStrict equality against the candidate value.
arrayAny-of: passes if any element matches.
(absent)A Filter with no fields is an empty AND and matches everything.

Filters never match missing candidates silently—each specified field must exist and match. Dot-separated keys resolve nested tool input fields through own properties, so a filter of "input.path": "^src/" reaches inside a nested { "input": { "path": "..." } } candidate.

Regular-expression behavior. Exact regular-expression behavior is the JavaScript engine’s; HooKit passes the source through verbatim without stripping or re-parsing it.

Candidate fields per event

Tool events expose toolName together with the tool’s spread input: { ...event.input, toolName }. Filters can use toolName and the input fields the tool actually receives:

EventCandidate fields
tool_call, tool_resultall tool input fields plus toolName
turn_endevent, turnIndex
agent_endevent
agent_settledevent
session_before_switchevent, reason, targetSessionFile (when set)
session_before_forkevent, entryId, position
hook_resultevent, hookRef, invocationId, outcome, code

Lifecycle Events expose only these documented bounded scalars—the rich Pi callback objects are intentionally not projected into Filters.

The tool candidate

For a write tool call with an input of { "path": ".env" }, the candidate is { "path": ".env", "toolName": "write" }. This is the Filter from the tutorial’s first Hook:

{
  "local": {
    "no-env-access": {
      "description": "Blocks reads and writes to .env files.",
      "event": "tool_call",
      "filter": {
        "toolName": ["^read$", "^write$"],
        "path": "(^|/)\\.env$"
      },
      "shell": "false"
    }
  }
}
  • toolName: ["^read$", "^write$"] is an array, so it matches a tool named exactly read or exactly write.
  • path: "(^|/)\\.env$" matches a path that either is .env or ends in /.env. The two fields are ANDed.

hook_result filters

The Hook Result Event’s Filter is limited to event, source-qualified hookRef, invocationId, outcome, and code. Identity fields (event, hookRef, invocationId) use the regular-expression/any-of string rules described earlier; outcome and code use the exact shared selector semantics of owned Actions. The same matching implementation handles both outcome and code here and in Actions, so a hook_result Filter can select a block with a specific code:

{
  "local": {
    "audit-blocks": {
      "description": "Audit every block from local/no-env-access",
      "event": "hook_result",
      "filter": {
        "hookRef": "^local/no-env-access$",
        "outcome": "block",
        "code": [1, null]
      },
      "shell": "./scripts/audit-hook-result.sh"
    }
  }
}

The hook_result page describes what this Event carries and how handling stays bounded.