1// Package hooks provides a Git-like hooks system for Crush.
2//
3// Hooks are executable scripts that run at specific points in the application
4// lifecycle. They can modify behavior, add context, control permissions, and
5// audit activity.
6package hooks
7
8import "context"
9
10// HookType represents the type of hook.
11type HookType string
12
13const (
14 // HookUserPromptSubmit executes after user submits prompt, before sending to LLM.
15 HookUserPromptSubmit HookType = "user-prompt-submit"
16
17 // HookPreToolUse executes after LLM requests tool use, before permission check & execution.
18 HookPreToolUse HookType = "pre-tool-use"
19
20 // HookPostToolUse executes after tool executes, before result sent to LLM.
21 HookPostToolUse HookType = "post-tool-use"
22
23 // HookStop executes when agent conversation loop stops or is cancelled.
24 HookStop HookType = "stop"
25)
26
27// HookContext contains the data passed to hooks.
28type HookContext struct {
29 // HookType is the type of hook being executed.
30 HookType HookType
31
32 // SessionID is the current session ID.
33 SessionID string
34
35 // WorkingDir is the working directory.
36 WorkingDir string
37
38 // Data is hook-specific data marshaled to JSON and passed via stdin.
39 // For UserPromptSubmit: prompt, attachments, model, is_first_message
40 // For PreToolUse: tool_name, tool_call_id, tool_input
41 // For PostToolUse: tool_name, tool_call_id, tool_input, tool_output, execution_time_ms
42 // For Stop: reason
43 Data any
44
45 // ToolName is the tool name (for tool hooks only).
46 ToolName string
47
48 // ToolCallID is the tool call ID (for tool hooks only).
49 ToolCallID string
50
51 // Environment contains additional environment variables to pass to the hook.
52 Environment map[string]string
53}
54
55// HookResult contains the result of hook execution.
56type HookResult struct {
57 // HookType the hook type
58 HookType HookType `json:"hook_type"`
59 // Name the name of the hook (usually the file name)
60 Name string `json:"name"`
61 // Path hook path
62 Path string `json:"path"`
63 // AllResults stores all results for this event
64 AllResults []HookResult `json:"all_results,omitempty"`
65 // Continue indicates whether to continue execution.
66 // If false, execution stops.
67 Continue bool `json:"continue"`
68
69 // Permission decision (for PreToolUse hooks only).
70 // Values: "ask" (default), "approve", "deny"
71 Permission string `json:"permission"`
72
73 // ModifiedPrompt is the modified user prompt (for UserPromptSubmit).
74 ModifiedPrompt *string `json:"modified_prompt"`
75
76 // ModifiedInput is the modified tool input parameters (for PreToolUse).
77 // This is a map that can be merged with the original tool input.
78 ModifiedInput map[string]any `json:"modified_input"`
79
80 // ModifiedOutput is the modified tool output (for PostToolUse).
81 ModifiedOutput map[string]any `json:"modified_output"`
82
83 // ContextContent is raw text content to add to LLM context.
84 ContextContent string `json:"context_content"`
85
86 // ContextFiles is a list of file paths to load and add to LLM context.
87 ContextFiles []string `json:"context_files"`
88
89 // Message is a user-facing message (logged and potentially displayed).
90 Message string `json:"message"`
91}
92
93// Manager coordinates hook discovery and execution.
94type Manager interface {
95 // ListHooks returns all discovered hooks for a given type.
96 ListHooks(hookType HookType) []string
97
98 // ExecuteUserPromptSubmit executes the UserPromptSubmit event
99 ExecuteUserPromptSubmit(ctx context.Context, sessionID, workingDir string, data UserPromptSubmitData) (HookResult, error)
100
101 // ExecutePreToolUse executes the PreToolUse event
102 ExecutePreToolUse(ctx context.Context, sessionID, workingDir string, data PreToolUseData) (HookResult, error)
103
104 // ExecutePostToolUse executes the PostToolUse event
105 ExecutePostToolUse(ctx context.Context, sessionID, workingDir string, data PostToolUseData) (HookResult, error)
106
107 // ExecuteStop executes the Stop event
108 ExecuteStop(ctx context.Context, sessionID, workingDir string, data StopData) (HookResult, error)
109}
110
111type UserPromptSubmitData struct {
112 Prompt string `json:"prompt"`
113 Attachments []string `json:"attachments"`
114 Model string `json:"model"`
115 Provider string `json:"provider"`
116 IsFirstMessage bool `json:"is_first_message"`
117}
118
119type PreToolUseData struct {
120 ToolName string `json:"tool_name"`
121 ToolCallID string `json:"tool_call_id"`
122 ToolInput map[string]any `json:"tool_input"`
123}
124
125type PostToolUseData struct {
126 ToolName string `json:"tool_name"`
127 ToolCallID string `json:"tool_call_id"`
128 ToolInput map[string]any `json:"tool_input"`
129 ToolOutput map[string]any `json:"tool_output"`
130 ExecutionTimeMs int64 `json:"execution_time_ms"`
131}
132
133type StopData struct {
134 Reason string `json:"reason"`
135}