Configuration
🦉 HooKit
Reference

Configuration

The complete configuration contract for .pi/hookit.json.

Configuration is plain JSON in .pi/hookit.json for the current project, or ~/.pi/agent/hookit.json for global policies. Project storage is only read after Pi trusts the project. The JSON Schema at meffmadd/HooKit/schema.json is the validation authority: every designated JSON configuration example is validated against it, and editors that understand JSON Schema use it for autocompletion. Each npm release also includes that release’s unchanged schema.json; use the bundled copy for installed-release and offline validation. The default-branch URL can describe newer behavior. See schema guidance.

The persisted format has a top-level object with an optional $schema, an optional repos array, and one Section per Hook Source (local plus one owner/repo per installed repository). Every Section maps names to Catalog Entries, which are either a Hook or a Preset—never both.

{
  "$schema": "https://raw.githubusercontent.com/meffmadd/HooKit/main/schema.json",
  "repos": ["meffmadd/HooKit"],
  "local": {
    "no-env-access": {
      "description": "Blocks reads and writes to .env files.",
      "event": "tool_call",
      "filter": { "toolName": "^write$", "path": "(^|/)\\.env$" },
      "shell": "false"
    }
  },
  "meffmadd/HooKit": {
    "git-diff-check": {
      "description": "Checks tracked changes for whitespace errors and conflict markers.",
      "event": "agent_end",
      "shell": "git diff --check HEAD --"
    }
  }
}

Fields

Each config option has its own short page with an example at the top:

Storage root

FieldPage
$schemaJSON Schema reference
reposAvailable repositories
local / owner/repoSections map names to Catalog Entries (local plus declared owner/repo).

Hooks

A Hook subscribes to one Event and owns a shell decision and, optionally, one Action. A Hook requires description and event, and at least one of shell or action.

Presets

A Preset is a named list of unique Hook References. It enables a set of Hooks as one unit and cannot carry any executable field.

Entry identity

Each entry name is non-empty and contains neither / nor NUL. The source-qualified identity is the Hook Reference: local/name for local entries or owner/repo/name for repository entries. References are how Presets name members and how reports and Filters identify a Hook, so the restriction keeps identity unambiguous:

Catalog Entry nameValid?Notes
no-env-accessyesordinary local entry
“ (empty)nonames are non-empty
nested/guardno/ is reserved for Reference separators
nul\u0000guardnoNUL is reserved for persisted enablement keys

Explicitly invalid combinations

The Schema rejects combinations that cannot form a valid entry. This example illustrates a boolean shell, which is invalid:

{
  "local": {
    "mistake": {
      "description": "A Hook with an invalid boolean shell",
      "event": "tool_call",
      "shell": false
    }
  }
}

So are an entry with neither shell nor action (inert), and an entry that mixes a Hook’s fields with a Preset’s preset array. If a file fails validation during loading or refresh, /hooks reports the Catalog as broken, makes it unavailable, and clears in-memory entries and enablement. No guards from that Catalog run until it is repaired and loaded successfully; verify enablement afterward. By contrast, a rejected mutation leaves the caller’s previously accepted Catalog and enablement unchanged.