hooks

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 14, 2026 License: Apache-2.0 Imports: 13 Imported by: 0

Documentation

Overview

Package hooks provides shell-command-based pre-tool hooks, adapted from Charm's crush project (MIT). Hooks fire before each tool execution and can:

  • Allow (exit 0, no JSON output or {"decision":"allow"})
  • Deny (exit 2, or {"decision":"deny","reason":"..."})
  • Halt turn (exit 49, or {"halt":true,"reason":"..."})
  • Rewrite input (exit 0, {"updatedInput":"...","decision":"allow"})
  • Inject context (exit 0, {"context":"..."})

Index

Constants

View Source
const HaltExitCode = 49

HaltExitCode is the exit code that halts the entire turn (crush convention).

Variables

This section is empty.

Functions

This section is empty.

Types

type ActiveHookInfo

type ActiveHookInfo struct {
	HookID     string
	Event      types.HookEvent
	StartTime  time.Time
	Context    context.Context
	CancelFunc context.CancelFunc
}

ActiveHookInfo tracks information about actively running hooks

type AggregateResult

type AggregateResult struct {
	Decision     Decision
	Halt         bool
	Reason       string
	UpdatedInput string
	Context      string
	HookCount    int
	Hooks        []HookInfo
}

AggregateResult is the merged outcome of all matching hooks.

type Decision

type Decision int

Decision is the outcome of a hook.

const (
	DecisionNone  Decision = iota // Hook didn't express a preference.
	DecisionAllow                 // Explicitly allows; skips normal permission prompt.
	DecisionDeny                  // Blocks the tool call.
)

func (Decision) String

func (d Decision) String() string

type Executor

type Executor struct {
	// contains filtered or unexported fields
}

Executor handles hook execution with timeout and error handling.

func NewExecutor

func NewExecutor(registry *Registry) *Executor

NewExecutor creates a new hook executor.

func (*Executor) Execute

func (e *Executor) Execute(ctx context.Context, event types.HookEvent, data map[string]any) ([]types.HookResult, error)

Execute executes all hooks registered for a specific event. Hooks are executed in priority order (highest first). Returns aggregated results from all hooks.

func (*Executor) ExecuteAsync

func (e *Executor) ExecuteAsync(ctx context.Context, event types.HookEvent, data map[string]any)

ExecuteAsync executes hooks asynchronously and returns immediately. Useful for non-blocking hooks (e.g., logging, analytics).

func (*Executor) ExecuteFirst

func (e *Executor) ExecuteFirst(ctx context.Context, event types.HookEvent, data map[string]any) *types.HookResult

ExecuteFirst executes hooks until one returns a non-continue action. Returns the first hook result that is not "continue".

func (*Executor) IsEnabled

func (e *Executor) IsEnabled() bool

IsEnabled returns whether hook execution is enabled.

func (*Executor) SetEnabled

func (e *Executor) SetEnabled(enabled bool)

SetEnabled enables or disables hook execution.

func (*Executor) SetTimeout

func (e *Executor) SetTimeout(timeout time.Duration)

SetTimeout sets the default timeout for hook execution.

type HookConfig

type HookConfig struct {
	// Matcher is a regex against the tool name. Empty = match all.
	Matcher string `yaml:"matcher" json:"matcher,omitempty"`
	// Command is the shell command to execute.
	Command string `yaml:"command" json:"command"`
	// Timeout in seconds (default 30).
	Timeout int `yaml:"timeout" json:"timeout,omitempty"`
}

HookConfig declares a shell hook. Stored in .seshat.yaml under hooks.pre_tool_use.

type HookHandler

type HookHandler = types.HookHandler

HookHandler is an alias for types.HookHandler. Returning (nil, nil) is equivalent to HookActionContinue.

type HookInfo

type HookInfo struct {
	Name         string `json:"name"`
	Matcher      string `json:"matcher,omitempty"`
	Decision     string `json:"decision"`
	Halt         bool   `json:"halt,omitempty"`
	Reason       string `json:"reason,omitempty"`
	InputRewrite bool   `json:"input_rewrite,omitempty"`
}

HookInfo identifies a single hook that ran and its individual result.

type HookLifecycleConfig

type HookLifecycleConfig struct {
	// CleanupInterval is how often to check for expired hooks
	CleanupInterval time.Duration

	// DefaultHookLifetime is the default lifetime for hooks
	DefaultHookLifetime time.Duration

	// MaxExecutionRetries is the maximum number of retries before marking a hook as dead
	MaxExecutionRetries int

	// ExecutionTimeout is the maximum time a hook can run before being cancelled
	ExecutionTimeout time.Duration

	// EnableAutoCleanup enables automatic cleanup of expired/dead hooks
	EnableAutoCleanup bool

	// EnablePerformanceTracking enables tracking of hook performance metrics
	EnablePerformanceTracking bool

	// HookTimeoutManager is the manager for hook timeouts
	HookTimeoutManager *HookTimeoutManager
}

HookLifecycleConfig configures the hook lifecycle manager behavior

func DefaultHookLifecycleConfig

func DefaultHookLifecycleConfig() *HookLifecycleConfig

DefaultHookLifecycleConfig returns default lifecycle configuration

type HookLifecycleEvent

type HookLifecycleEvent struct {
	EventType string
	HookID    string
	Timestamp time.Time
	Error     error
	Metadata  map[string]interface{}
}

HookLifecycleEvent represents a lifecycle event that occurred

type HookLifecycleManager

type HookLifecycleManager struct {
	// contains filtered or unexported fields
}

HookLifecycleManager manages the lifecycle of hooks including registration, execution tracking, cleanup, and state management.

func NewHookLifecycleManager

func NewHookLifecycleManager(registry *Registry) *HookLifecycleManager

NewHookLifecycleManager creates a new hook lifecycle manager

func NewHookLifecycleManagerWithConfig

func NewHookLifecycleManagerWithConfig(registry *Registry, config *HookLifecycleConfig) *HookLifecycleManager

NewHookLifecycleManagerWithConfig creates a new hook lifecycle manager with custom config

func (*HookLifecycleManager) ExecuteHook

func (m *HookLifecycleManager) ExecuteHook(
	ctx context.Context,
	hook HookRegistration,
	progress types.HookProgress,
) error

ExecuteHook executes a hook with lifecycle management

func (*HookLifecycleManager) GetActiveHooks

func (m *HookLifecycleManager) GetActiveHooks() map[string]*ActiveHookInfo

GetActiveHooks returns all currently active hooks

func (*HookLifecycleManager) GetConfig

func (m *HookLifecycleManager) GetConfig() *HookLifecycleConfig

GetConfig returns the current configuration

func (*HookLifecycleManager) GetHookState

func (m *HookLifecycleManager) GetHookState(hookID string) (HookState, error)

GetHookState returns the current state of a hook

func (*HookLifecycleManager) GetLifecycleEvents

func (m *HookLifecycleManager) GetLifecycleEvents() <-chan HookLifecycleEvent

GetLifecycleEvents returns the channel for lifecycle events

func (*HookLifecycleManager) GetMetrics

func (m *HookLifecycleManager) GetMetrics() HookMetrics

GetMetrics returns current hook metrics

func (*HookLifecycleManager) PauseHook

func (m *HookLifecycleManager) PauseHook(hookID string) error

PauseHook pauses a hook temporarily

func (*HookLifecycleManager) RegisterHook

func (m *HookLifecycleManager) RegisterHook(hook HookRegistration) error

RegisterHook registers a new hook with lifecycle management

func (*HookLifecycleManager) RemoveHook

func (m *HookLifecycleManager) RemoveHook(hookID string) error

RemoveHook removes a hook from the registry

func (*HookLifecycleManager) ResumeHook

func (m *HookLifecycleManager) ResumeHook(hookID string) error

ResumeHook resumes a paused hook

func (*HookLifecycleManager) Shutdown

func (m *HookLifecycleManager) Shutdown()

Shutdown gracefully shuts down the lifecycle manager

func (*HookLifecycleManager) UpdateConfig

func (m *HookLifecycleManager) UpdateConfig(config *HookLifecycleConfig)

UpdateConfig updates the lifecycle configuration

func (*HookLifecycleManager) WaitShutdown

func (m *HookLifecycleManager) WaitShutdown()

WaitShutdown waits for the shutdown to complete

type HookMetadata

type HookMetadata struct {
	HookCount    int        `json:"hook_count"`
	Decision     string     `json:"decision"`
	Halt         bool       `json:"halt,omitempty"`
	Reason       string     `json:"reason,omitempty"`
	InputRewrite bool       `json:"input_rewrite,omitempty"`
	Hooks        []HookInfo `json:"hooks,omitempty"`
}

HookMetadata is embedded in tool response metadata so the UI can display a hook indicator.

type HookMetrics

type HookMetrics struct {
	TotalExecutions      int64
	SuccessfulExecutions int64
	FailedExecutions     int64
	AverageExecutionTime time.Duration
	TotalExecutionTime   time.Duration
	MaxExecutionTime     time.Duration
	MinExecutionTime     time.Duration
}

HookMetrics tracks performance metrics for hooks

type HookRegistration

type HookRegistration struct {
	// ID uniquely identifies this hook registration
	ID string

	// Event is the event this hook is registered for
	Event types.HookEvent

	// Handler is the function to call when the hook is triggered
	Handler HookHandler

	// Priority determines execution order (higher = earlier)
	Priority int

	// Optional metadata for debugging and tracking
	Metadata map[string]any

	// Lifecycle management fields
	State        HookState     // Current state of the hook
	CreatedAt    time.Time     // When the hook was registered
	LastExecuted time.Time     // When the hook was last executed
	ExecCount    int           // Number of times the hook has been executed
	ErrorCount   int           // Number of times the hook has errored
	LastError    error         // Last error encountered by the hook
	IsPermanent  bool          // If true, hook cannot be auto-cleaned
	MaxLifetime  time.Duration // Maximum lifetime before auto-cleanup (0 = infinite)
}

HookRegistration represents a registered hook with lifecycle support.

type HookResult

type HookResult struct {
	Decision     Decision
	Halt         bool
	Reason       string
	UpdatedInput string // non-empty = rewrite tool input JSON
	Context      string // non-empty = append to tool result
}

HookResult is the outcome of one hook run.

type HookState

type HookState string

HookState represents the current state of a hook

const (
	// HookStateActive means the hook is active and will be executed
	HookStateActive HookState = "active"
	// HookStatePaused means the hook is temporarily paused
	HookStatePaused HookState = "paused"
	// HookStateInactive means the hook is inactive and won't be executed
	HookStateInactive HookState = "inactive"
	// HookStateDead means the hook has encountered an error and is dead
	HookStateDead HookState = "dead"
)

type HookTimeoutEvent

type HookTimeoutEvent struct {
	HookID    string
	Timestamp time.Time
	Timeout   time.Duration
}

HookTimeoutEvent represents a timeout event that occurred

type HookTimeoutInfo

type HookTimeoutInfo struct {
	HookID      string
	StartTime   time.Time
	Timeout     time.Duration
	Context     context.Context
	CancelFunc  context.CancelFunc
	Expired     bool
	ExpiredTime time.Time
}

HookTimeoutInfo tracks timeout information for a hook

type HookTimeoutManager

type HookTimeoutManager struct {
	// contains filtered or unexported fields
}

HookTimeoutManager manages timeouts for hook executions

func NewHookTimeoutManager

func NewHookTimeoutManager() *HookTimeoutManager

NewHookTimeoutManager creates a new hook timeout manager

func NewHookTimeoutManagerWithTimeout

func NewHookTimeoutManagerWithTimeout(defaultTimeout time.Duration) *HookTimeoutManager

NewHookTimeoutManagerWithTimeout creates a new hook timeout manager with default timeout

func (*HookTimeoutManager) CheckTimeout

func (m *HookTimeoutManager) CheckTimeout(hookID string) bool

CheckTimeout checks if a hook has timed out

func (*HookTimeoutManager) ClearAllTimeouts

func (m *HookTimeoutManager) ClearAllTimeouts()

ClearAllTimeouts removes all timeout information

func (*HookTimeoutManager) ClearExpiredTimeouts

func (m *HookTimeoutManager) ClearExpiredTimeouts() int

ClearExpiredTimeouts removes all expired timeout information

func (*HookTimeoutManager) GetActiveCount

func (m *HookTimeoutManager) GetActiveCount() int

GetActiveCount returns the number of active timeout trackers

func (*HookTimeoutManager) GetActiveTimeouts

func (m *HookTimeoutManager) GetActiveTimeouts() map[string]*HookTimeoutInfo

GetActiveTimeouts returns all active timeout information

func (*HookTimeoutManager) GetDefaultTimeout

func (m *HookTimeoutManager) GetDefaultTimeout() time.Duration

GetDefaultTimeout returns the default timeout duration

func (*HookTimeoutManager) GetExpiredCount

func (m *HookTimeoutManager) GetExpiredCount() int

GetExpiredCount returns the number of expired timeout trackers

func (*HookTimeoutManager) GetExpiredTimeouts

func (m *HookTimeoutManager) GetExpiredTimeouts() map[string]*HookTimeoutInfo

GetExpiredTimeouts returns all expired timeout information

func (*HookTimeoutManager) GetTimeoutEvents

func (m *HookTimeoutManager) GetTimeoutEvents() <-chan HookTimeoutEvent

GetTimeoutEvents returns the channel for timeout events

func (*HookTimeoutManager) GetTimeoutInfo

func (m *HookTimeoutManager) GetTimeoutInfo(hookID string) (*HookTimeoutInfo, error)

GetTimeoutInfo returns timeout information for a hook

func (*HookTimeoutManager) SetDefaultTimeout

func (m *HookTimeoutManager) SetDefaultTimeout(timeout time.Duration)

SetDefaultTimeout sets the default timeout duration

func (*HookTimeoutManager) Shutdown

func (m *HookTimeoutManager) Shutdown()

Shutdown gracefully shuts down the timeout manager

func (*HookTimeoutManager) StartTimeout

func (m *HookTimeoutManager) StartTimeout(parentCtx context.Context, hookID string, timeout time.Duration) (context.Context, context.CancelFunc)

StartTimeout starts tracking a timeout for a hook. parentCtx is used as the parent context so the hook is cancelled when the calling request is cancelled in addition to its own deadline.

func (*HookTimeoutManager) StopTimeout

func (m *HookTimeoutManager) StopTimeout(hookID string)

StopTimeout stops tracking a timeout for a hook

type Registry

type Registry struct {
	// contains filtered or unexported fields
}

Registry is a unified hook registry for all lifecycle hooks. It supports registration, retrieval, and execution of hooks by event type.

func NewRegistry

func NewRegistry() *Registry

NewRegistry creates a new hook registry.

func (*Registry) Add

func (r *Registry) Add(hook HookRegistration)

Add registers a new hook for the specified event.

func (*Registry) CleanupDeadHooks

func (r *Registry) CleanupDeadHooks() int

CleanupDeadHooks removes hooks that are in dead state

func (*Registry) CleanupExpiredHooks

func (r *Registry) CleanupExpiredHooks() int

CleanupExpiredHooks removes hooks that have exceeded their lifetime

func (*Registry) Clear

func (r *Registry) Clear(event types.HookEvent)

Clear removes all hooks for a specific event, or all events if event is empty.

func (*Registry) Count

func (r *Registry) Count(event types.HookEvent) int

Count returns the number of hooks registered for a specific event.

func (*Registry) Get

func (r *Registry) Get(event types.HookEvent) []HookRegistration

Get retrieves all hooks registered for a specific event.

func (*Registry) GetActiveHooks

func (r *Registry) GetActiveHooks(event types.HookEvent) []HookRegistration

GetActiveHooks returns only active hooks for a specific event

func (*Registry) GetHook

func (r *Registry) GetHook(id string) (*HookRegistration, error)

GetHook retrieves a specific hook by ID

func (*Registry) GetHookStats

func (r *Registry) GetHookStats() map[string]interface{}

GetHookStats returns statistics for all hooks

func (*Registry) GetHooksByState

func (r *Registry) GetHooksByState(state HookState) []HookRegistration

GetHooksByState returns all hooks in a specific state

func (*Registry) HasHooks

func (r *Registry) HasHooks(event types.HookEvent) bool

HasHooks checks if there are any hooks registered for a specific event.

func (*Registry) List

func (r *Registry) List() map[types.HookEvent][]HookRegistration

List returns all registered hooks across all events.

func (*Registry) PauseHook

func (r *Registry) PauseHook(id string) error

PauseHook temporarily pauses a hook by ID

func (*Registry) RecordHookExecution

func (r *Registry) RecordHookExecution(id string, err error) error

RecordHookExecution records that a hook was executed

func (*Registry) Remove

func (r *Registry) Remove(id string)

Remove removes a hook registration by ID.

func (*Registry) ResumeHook

func (r *Registry) ResumeHook(id string) error

ResumeHook resumes a paused hook by ID

func (*Registry) SetHookState

func (r *Registry) SetHookState(id string, state HookState) error

SetHookState changes the state of a hook by ID

type Runner

type Runner struct {
	// contains filtered or unexported fields
}

Runner executes PreToolUse hooks before each tool call.

func NewRunner

func NewRunner(cfgs []HookConfig, cwd, projectDir string) *Runner

NewRunner builds a Runner from configs. Hooks with invalid matchers are skipped.

func (*Runner) Run

func (r *Runner) Run(ctx context.Context, sessionID, toolName, toolInputJSON string) (AggregateResult, error)

Run executes all matching hooks for the given tool call.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL