{ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://raw.githubusercontent.com/meffmadd/HooKit/main/schema.json", "title": "HooKit configuration", "description": "Schema for .pi/hookit.json — Hooks with optional owned Actions, and Presets for Pi.", "type": "object", "properties": { "$schema": { "type": "string", "description": "JSON Schema reference for editor autocompletion and validation." }, "local": { "type": "object", "description": "User-defined Hooks and Presets.", "propertyNames": { "$ref": "#/$defs/entryName" }, "additionalProperties": { "$ref": "#/$defs/entry" } }, "repos": { "type": "array", "description": "Repositories available for installing Hooks (owner/repo).", "items": { "type": "string", "pattern": "^[^/]+/[^/]+$" }, "uniqueItems": true } }, "additionalProperties": { "$ref": "#/$defs/repo" }, "$defs": { "entryName": { "type": "string", "minLength": 1, "pattern": "^[^/\\u0000]+$", "description": "Catalog Entry name. Source-qualified identity reserves slash and NUL." }, "entry": { "oneOf": [ { "$ref": "#/$defs/hook" }, { "$ref": "#/$defs/preset" } ] }, "hook": { "description": "A Hook. It must author a shell, an owned Action, or both. An omitted shell is canonicalized to the exact command `true`.", "type": "object", "required": [ "description", "event" ], "anyOf": [ { "required": [ "shell" ] }, { "required": [ "action" ] } ], "properties": { "description": { "type": "string", "description": "Human-readable description of the policy." }, "event": { "type": "string", "enum": [ "tool_call", "tool_result", "turn_end", "agent_end", "agent_settled", "session_before_switch", "session_before_fork", "hook_result" ], "description": "Supported Native Event or Hook Result Event." }, "filter": { "type": "object", "description": "ANDed candidate fields. Strings are JavaScript regular-expression sources; non-strings match strictly; arrays are any-of.", "additionalProperties": { "$ref": "#/$defs/filterValue" } }, "when": { "type": "string", "description": "Optional shell precondition. Ordinary non-zero exits skip the whole Hook." }, "shell": { "type": "string", "description": "Decision command. Omission is canonical `true`. Exact `true` and `false` are optimized without changing command accounting." }, "action": { "$ref": "#/$defs/action" }, "default": { "type": "boolean", "description": "Whether this Catalog Entry is enabled by default.", "default": false } }, "allOf": [ { "if": { "properties": { "event": { "const": "hook_result" } }, "required": [ "event" ] }, "then": { "properties": { "filter": { "$ref": "#/$defs/hookResultFilter" } } } }, { "if": { "properties": { "event": { "const": "tool_call" } }, "required": [ "event" ] }, "then": { "properties": { "action": { "properties": { "outcome": { "$ref": "#/$defs/toolCallOutcomeSelector" } }, "type": "object" } } } }, { "if": { "properties": { "event": { "const": "tool_result" } }, "required": [ "event" ] }, "then": { "properties": { "action": { "properties": { "outcome": { "$ref": "#/$defs/toolResultOutcomeSelector" } }, "type": "object" } } } }, { "if": { "properties": { "event": { "enum": [ "turn_end", "agent_end", "agent_settled", "hook_result" ] } }, "required": [ "event" ] }, "then": { "properties": { "action": { "properties": { "outcome": { "$ref": "#/$defs/reportOutcomeSelector" } }, "type": "object" } } } }, { "if": { "properties": { "event": { "enum": [ "session_before_switch", "session_before_fork" ] } }, "required": [ "event" ] }, "then": { "properties": { "action": { "properties": { "outcome": { "$ref": "#/$defs/cancelOutcomeSelector" } }, "type": "object" } } } } ], "additionalProperties": false }, "preset": { "description": "A named list of unique source-qualified Hook References. Unresolved references may dangle; references resolving to Presets are rejected.", "type": "object", "required": [ "description", "preset" ], "properties": { "description": { "type": "string" }, "preset": { "type": "array", "uniqueItems": true, "items": { "type": "string", "pattern": "^(local/[^/\\u0000]+|(?!local/)[A-Za-z0-9._-]+/[A-Za-z0-9._-]+/[^/\\u0000]+)$" } }, "default": { "type": "boolean", "default": false } }, "additionalProperties": false }, "filterValue": { "anyOf": [ { "type": "string" }, { "type": [ "number", "boolean", "null" ] }, { "type": "array", "items": { "anyOf": [ { "type": "string" }, { "type": [ "number", "boolean", "null" ] } ] } } ] }, "hookResultFilter": { "type": "object", "description": "Hook Result Event identity fields use regex matching; Hook outcome and code use exact shared selector matching.", "properties": { "event": { "$ref": "#/$defs/stringOrArray" }, "hookRef": { "$ref": "#/$defs/stringOrArray" }, "invocationId": { "$ref": "#/$defs/stringOrArray" }, "outcome": { "$ref": "#/$defs/outcomeSelectorAllowEmpty" }, "code": { "$ref": "#/$defs/codeSelectorAllowEmpty" } }, "additionalProperties": false }, "stringOrArray": { "anyOf": [ { "type": "string" }, { "type": "array", "items": { "type": "string" } } ] }, "outcomeSelector": { "anyOf": [ { "type": "string", "enum": [ "pass", "block", "patch", "cancel", "report" ] }, { "type": "array", "minItems": 1, "items": { "type": "string", "enum": [ "pass", "block", "patch", "cancel", "report" ] } } ] }, "outcomeSelectorAllowEmpty": { "anyOf": [ { "type": "string", "enum": [ "pass", "block", "patch", "cancel", "report" ] }, { "type": "array", "items": { "type": "string", "enum": [ "pass", "block", "patch", "cancel", "report" ] } } ] }, "toolCallOutcomeSelector": { "anyOf": [ { "type": "string", "enum": [ "pass", "block" ] }, { "type": "array", "minItems": 1, "items": { "type": "string", "enum": [ "pass", "block" ] } } ] }, "toolResultOutcomeSelector": { "anyOf": [ { "type": "string", "enum": [ "pass", "patch" ] }, { "type": "array", "minItems": 1, "items": { "type": "string", "enum": [ "pass", "patch" ] } } ] }, "reportOutcomeSelector": { "anyOf": [ { "type": "string", "enum": [ "pass", "report" ] }, { "type": "array", "minItems": 1, "items": { "type": "string", "enum": [ "pass", "report" ] } } ] }, "cancelOutcomeSelector": { "anyOf": [ { "type": "string", "enum": [ "pass", "cancel" ] }, { "type": "array", "minItems": 1, "items": { "type": "string", "enum": [ "pass", "cancel" ] } } ] }, "codeSelector": { "anyOf": [ { "type": [ "number", "null" ] }, { "type": "array", "minItems": 1, "items": { "type": [ "number", "null" ] } } ] }, "codeSelectorAllowEmpty": { "anyOf": [ { "type": [ "number", "null" ] }, { "type": "array", "items": { "type": [ "number", "null" ] } } ] }, "actionSelectorFeasibility": { "anyOf": [ { "not": { "required": [ "code" ], "type": "object" } }, { "allOf": [ { "properties": { "outcome": { "anyOf": [ { "const": "pass" }, { "type": "array", "contains": { "const": "pass" } } ] } }, "type": "object" }, { "properties": { "code": { "anyOf": [ { "const": 0 }, { "type": "array", "contains": { "const": 0 } } ] } }, "required": [ "code" ], "type": "object" } ] }, { "allOf": [ { "properties": { "outcome": { "anyOf": [ { "type": "string", "enum": [ "block", "patch", "cancel", "report" ] }, { "type": "array", "contains": { "type": "string", "enum": [ "block", "patch", "cancel", "report" ] } } ] } }, "type": "object" }, { "properties": { "code": { "anyOf": [ { "type": "null" }, { "type": "number", "not": { "const": 0 } }, { "type": "array", "contains": { "anyOf": [ { "type": "null" }, { "type": "number", "not": { "const": 0 } } ] } } ] } }, "required": [ "code" ], "type": "object" } ] } ] }, "action": { "description": "One owned Action. outcome/code select the owning Hook's immutable Hook Outcome and are removed from the Action Request Effect.", "allOf": [ { "$ref": "#/$defs/actionSelectorFeasibility" }, { "oneOf": [ { "type": "object", "required": [ "type", "outcome" ], "properties": { "type": { "const": "interrupt" }, "outcome": { "$ref": "#/$defs/outcomeSelector" }, "code": { "$ref": "#/$defs/codeSelector" } }, "additionalProperties": false }, { "type": "object", "required": [ "type", "outcome" ], "properties": { "type": { "const": "shutdown" }, "outcome": { "$ref": "#/$defs/outcomeSelector" }, "code": { "$ref": "#/$defs/codeSelector" }, "interrupt": { "type": "boolean", "default": false } }, "additionalProperties": false }, { "type": "object", "required": [ "type", "outcome" ], "properties": { "type": { "const": "compact" }, "outcome": { "$ref": "#/$defs/outcomeSelector" }, "code": { "$ref": "#/$defs/codeSelector" }, "instructions": { "type": "string" } }, "additionalProperties": false }, { "type": "object", "description": "A static custom-context or genuine user message delivered by Pi.", "required": [ "type", "outcome", "message", "delivery" ], "properties": { "type": { "const": "message" }, "outcome": { "$ref": "#/$defs/outcomeSelector" }, "code": { "$ref": "#/$defs/codeSelector" }, "message": { "type": "string", "pattern": "\\S", "description": "Static message text containing at least one non-whitespace character. The original string is delivered unchanged." }, "delivery": { "type": "string", "enum": [ "steer", "followUp", "nextTurn" ], "description": "Queue position. User messages support only steer and followUp." }, "sendAs": { "type": "string", "enum": [ "custom", "user" ], "default": "custom", "description": "Deliver as Pi custom context or as a genuine user message with normal input processing. Omission behaves as custom." }, "triggerTurn": { "type": "boolean", "default": false, "description": "Whether custom delivery starts an idle agent. Forbidden for user messages and ineffective with nextTurn." } }, "allOf": [ { "not": { "properties": { "delivery": { "const": "nextTurn" }, "triggerTurn": { "const": true } }, "required": [ "delivery", "triggerTurn" ] } }, { "if": { "properties": { "sendAs": { "const": "user" } }, "required": [ "sendAs" ] }, "then": { "properties": { "delivery": { "enum": [ "steer", "followUp" ] } }, "not": { "required": [ "triggerTurn" ] } } } ], "additionalProperties": false }, { "type": "object", "required": [ "type", "outcome", "name" ], "properties": { "type": { "const": "emit-custom-event" }, "outcome": { "$ref": "#/$defs/outcomeSelector" }, "code": { "$ref": "#/$defs/codeSelector" }, "name": { "type": "string", "pattern": "\\S" }, "data": {} }, "additionalProperties": false } ] } ] }, "repo": { "type": "object", "description": "Hooks and Presets installed from one repository.", "propertyNames": { "$ref": "#/$defs/entryName" }, "additionalProperties": { "$ref": "#/$defs/entry" } } } }