Platform-specific types and helpers for Windsurf Cascade hooks. Use these when you need features not available in the unified API.
See Windsurf Cascade Hooks Documentation for official docs.
| Event | Input | Output | Description |
|---|---|---|---|
| pre-run-command | PreRunCommandInput |
PreRunCommandOutput |
Before shell command |
| post-run-command | PostRunCommandInput |
PostRunCommandOutput |
After shell command |
| pre-write-code | PreWriteCodeInput |
PreWriteCodeOutput |
Before file write |
| post-write-code | PostWriteCodeInput |
PostWriteCodeOutput |
After file write |
| pre-read-code | PreReadCodeInput |
PreReadCodeOutput |
Before file read |
| post-read-code | PostReadCodeInput |
PostReadCodeOutput |
After file read |
| pre-mcp-tool-use | PreMCPToolUseInput |
PreMCPToolUseOutput |
Before MCP tool |
| post-mcp-tool-use | PostMCPToolUseInput |
PostMCPToolUseOutput |
After MCP tool |
| pre-user-prompt | PreUserPromptInput |
PreUserPromptOutput |
Before prompt processing |
| post-cascade-response | PostCascadeResponseInput |
PostCascadeResponseOutput |
After Cascade response |
| post-setup-worktree | PostSetupWorktreeInput |
PostSetupWorktreeOutput |
After worktree setup |
All Cascade hook inputs include these fields:
type BaseInput struct {
AgentActionName string `json:"agent_action_name"` // The hook event name
TrajectoryID string `json:"trajectory_id"` // Session identifier
ExecutionID string `json:"execution_id"` // Unique execution identifier
Timestamp string `json:"timestamp"` // ISO 8601 timestamp
}Pre-hooks that support decisions use this structure:
type BaseOutput struct {
Decision string `json:"decision,omitempty"` // "allow", "deny", "ask"
Message string `json:"message,omitempty"` // Shown when denied
}func Allow() BaseOutput // Permit the action
func Deny(message string) BaseOutput // Block the action
func Ask(message string) BaseOutput // Prompt user to confirmCalled before a shell command executes.
type PreRunCommandToolInfo struct {
CommandLine string `json:"command_line"`
Cwd string `json:"cwd"`
}
type PreRunCommandInput struct {
BaseInput
ToolInfo PreRunCommandToolInfo `json:"tool_info"`
}type PreRunCommandOutput struct {
BaseOutput
}func AllowCommand() PreRunCommandOutput
func DenyCommand(message string) PreRunCommandOutput
func AskCommand(message string) PreRunCommandOutputCascade uses exit code 2 to block actions, so pre-hooks should use RunE. When the handler returns an error, the process exits with code 2 and Cascade blocks the action.
hookshot.Register("cascade-pre-run-command", func() {
hookshot.RunE(func(input cascade.PreRunCommandInput) (cascade.PreRunCommandOutput, error) {
if strings.Contains(input.ToolInfo.CommandLine, "rm -rf /") {
return cascade.PreRunCommandOutput{}, fmt.Errorf("Dangerous command blocked")
}
return cascade.AllowCommand(), nil
})
})Note: The unified handler
OnBeforeExecutionalready handles this correctly — it usesRunEfor Cascade pre-hooks automatically. You only needRegisterfor Cascade-specific hooks not covered by the unified API.
Called after a shell command executes.
type PostRunCommandToolInfo struct {
CommandLine string `json:"command_line"`
Cwd string `json:"cwd"`
ExitCode int `json:"exit_code"`
Output string `json:"output,omitempty"`
}
type PostRunCommandInput struct {
BaseInput
ToolInfo PostRunCommandToolInfo `json:"tool_info"`
}type PostRunCommandOutput struct{} // Fire-and-forgetfunc PostRunCommandOK() PostRunCommandOutputCalled before writing a file.
type CascadeEdit struct {
OldString string `json:"old_string"`
NewString string `json:"new_string"`
}
type PreWriteCodeToolInfo struct {
FilePath string `json:"file_path"`
Edits []CascadeEdit `json:"edits"`
}
type PreWriteCodeInput struct {
BaseInput
ToolInfo PreWriteCodeToolInfo `json:"tool_info"`
}type PreWriteCodeOutput struct {
BaseOutput
}func AllowWrite() PreWriteCodeOutput
func DenyWrite(message string) PreWriteCodeOutput
func AskWrite(message string) PreWriteCodeOutputhookshot.Register("cascade-pre-write-code", func() {
hookshot.RunE(func(input cascade.PreWriteCodeInput) (cascade.PreWriteCodeOutput, error) {
if strings.HasSuffix(input.ToolInfo.FilePath, ".env") {
return cascade.PreWriteCodeOutput{}, fmt.Errorf("Cannot write to .env files")
}
return cascade.AllowWrite(), nil
})
})Called after writing a file.
type PostWriteCodeToolInfo struct {
FilePath string `json:"file_path"`
Edits []CascadeEdit `json:"edits"`
}
type PostWriteCodeInput struct {
BaseInput
ToolInfo PostWriteCodeToolInfo `json:"tool_info"`
}type PostWriteCodeOutput struct{} // Fire-and-forgetfunc PostWriteCodeOK() PostWriteCodeOutputCalled before reading a file.
type PreReadCodeToolInfo struct {
FilePath string `json:"file_path"`
}
type PreReadCodeInput struct {
BaseInput
ToolInfo PreReadCodeToolInfo `json:"tool_info"`
}type PreReadCodeOutput struct {
BaseOutput
}func AllowRead() PreReadCodeOutput
func DenyRead(message string) PreReadCodeOutput
func AskRead(message string) PreReadCodeOutputhookshot.Register("cascade-pre-read-code", func() {
hookshot.RunE(func(input cascade.PreReadCodeInput) (cascade.PreReadCodeOutput, error) {
if strings.Contains(input.ToolInfo.FilePath, "secrets") {
return cascade.PreReadCodeOutput{}, fmt.Errorf("Cannot read secret files")
}
return cascade.AllowRead(), nil
})
})Called after reading a file.
type PostReadCodeToolInfo struct {
FilePath string `json:"file_path"`
Content string `json:"content,omitempty"`
}
type PostReadCodeInput struct {
BaseInput
ToolInfo PostReadCodeToolInfo `json:"tool_info"`
}type PostReadCodeOutput struct{} // Fire-and-forgetfunc PostReadCodeOK() PostReadCodeOutputCalled before an MCP tool executes.
type PreMCPToolUseToolInfo struct {
MCPServerName string `json:"mcp_server_name"`
MCPToolName string `json:"mcp_tool_name"`
MCPToolArguments json.RawMessage `json:"mcp_tool_arguments"`
}
type PreMCPToolUseInput struct {
BaseInput
ToolInfo PreMCPToolUseToolInfo `json:"tool_info"`
}type PreMCPToolUseOutput struct {
BaseOutput
}func AllowMCP() PreMCPToolUseOutput
func DenyMCP(message string) PreMCPToolUseOutput
func AskMCP(message string) PreMCPToolUseOutputhookshot.Register("cascade-pre-mcp-tool-use", func() {
hookshot.RunE(func(input cascade.PreMCPToolUseInput) (cascade.PreMCPToolUseOutput, error) {
if input.ToolInfo.MCPServerName == "blocked-server" {
return cascade.PreMCPToolUseOutput{}, fmt.Errorf("MCP server not allowed")
}
return cascade.AllowMCP(), nil
})
})Called after an MCP tool executes.
type PostMCPToolUseToolInfo struct {
MCPServerName string `json:"mcp_server_name"`
MCPToolName string `json:"mcp_tool_name"`
MCPToolArguments json.RawMessage `json:"mcp_tool_arguments"`
MCPResult string `json:"mcp_result,omitempty"`
}
type PostMCPToolUseInput struct {
BaseInput
ToolInfo PostMCPToolUseToolInfo `json:"tool_info"`
}type PostMCPToolUseOutput struct{} // Fire-and-forgetfunc PostMCPToolUseOK() PostMCPToolUseOutputCalled when the user submits a prompt.
type PreUserPromptToolInfo struct {
UserPrompt string `json:"user_prompt"`
}
type PreUserPromptInput struct {
BaseInput
ToolInfo PreUserPromptToolInfo `json:"tool_info"`
}type PreUserPromptOutput struct {
BaseOutput
}func AllowPrompt() PreUserPromptOutput
func BlockPrompt(message string) PreUserPromptOutputhookshot.Register("cascade-pre-user-prompt", func() {
hookshot.RunE(func(input cascade.PreUserPromptInput) (cascade.PreUserPromptOutput, error) {
if strings.Contains(input.ToolInfo.UserPrompt, "api_key=") {
return cascade.PreUserPromptOutput{}, fmt.Errorf("Don't include API keys in prompts")
}
return cascade.AllowPrompt(), nil
})
})Called after Cascade completes a response.
type PostCascadeResponseToolInfo struct {
Response string `json:"response"`
}
type PostCascadeResponseInput struct {
BaseInput
ToolInfo PostCascadeResponseToolInfo `json:"tool_info"`
}type PostCascadeResponseOutput struct{} // Fire-and-forgetfunc PostCascadeResponseOK() PostCascadeResponseOutputCalled after worktree setup completes.
type PostSetupWorktreeToolInfo struct {
WorktreePath string `json:"worktree_path"`
}
type PostSetupWorktreeInput struct {
BaseInput
ToolInfo PostSetupWorktreeToolInfo `json:"tool_info"`
}type PostSetupWorktreeOutput struct{} // Fire-and-forgetfunc PostSetupWorktreeOK() PostSetupWorktreeOutput