Help us shape the future of Go! Take the 2026 Go Developer Survey

hooks

package
v1.11.3 Latest Latest
Warning

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

Go to latest
Published: Oct 21, 2026 License: Apache-2.0 Imports: 14 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 config.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]any
}

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]any

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