{
  "$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"
      }
    }
  }
}
