enact-config 0.0.2

Unified configuration management for Enact - secure storage with keychain and encrypted files
Documentation
//! Hook configuration — lifecycle event hooks (SessionStart, PreToolUse, etc.).
//!
//! Used by `~/.enact/hooks.yaml` and per-agent `hooks:` in agent.yaml.
//! Execution logic lives in enact-runner.

use serde::{Deserialize, Serialize};

/// Lifecycle event that triggers a hook.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub enum HookEvent {
    SessionStart,
    UserPromptSubmit,
    PreToolUse,
    PostToolUse,
    Stop,
    SessionEnd,
}

/// Handler type for a hook (shell command, LLM prompt, or sub-agent).
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum HookHandler {
    /// Run a shell command; receives JSON context on stdin; exit 0 = allow, non-zero = block.
    Command { script: String },
    /// Single-turn LLM call with template; returns allow/block.
    Prompt { template: String },
    /// Spawn a sub-agent for multi-turn verification.
    Agent { agent_name: String },
}

/// Decision returned by hook handlers for `PreToolUse`.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "decision", rename_all = "snake_case")]
pub enum HookDecision {
    /// Allow tool execution to continue.
    Allow,
    /// Block tool execution with an optional reason.
    Block {
        #[serde(default)]
        reason: Option<String>,
    },
    /// Mutate tool arguments before execution with an optional reason.
    Mutate {
        arguments: serde_json::Value,
        #[serde(default)]
        reason: Option<String>,
    },
}

/// Single hook definition (event + optional matcher + handler).
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct HookConfig {
    pub event: HookEvent,
    /// Optional regex to match tool names (e.g. "Edit|Write", "mcp__.*").
    #[serde(default)]
    pub matcher: Option<String>,
    pub handler: HookHandler,
    /// If true, run hook in background; don't block execution.
    #[serde(default)]
    pub async_mode: bool,
}

/// Root structure for hooks.yaml (global hooks).
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct HooksConfig {
    #[serde(default)]
    pub hooks: Vec<HookConfig>,
}