Documentation
¶
Overview ¶
Package agenthooks provides a framework for building hooks for AI coding agents.
It supports Claude Code, Cursor, Windsurf Cascade, Factory Droid, Gemini CLI, and VS Code Copilot (Preview) through a single unified API plus platform-specific packages for advanced use. (Copilot hooks are project-scoped, so `agenthooks install` configures the other five; see the README for Copilot setup.)
Quick start with unified handlers (one handler works across all agents):
package main
import "github.com/Checkmarx/ast-cx-hooks"
func main() {
agenthooks.WhenAgentIdle(func(e agenthooks.AgentIdleEvent) agenthooks.IdleVerdict {
if e.IsLooping() {
return agenthooks.Resume()
}
return agenthooks.Interrupt("Please review changes before finishing.")
})
agenthooks.Dispatch()
}
Platform-specific handlers for advanced use:
agenthooks.AddRoute("claude-pre-tool-use", func() {
agenthooks.Process(func(e claude.PreToolUseEvent) claude.PreToolUseResult {
return claude.ApproveToolUse()
})
})
Index ¶
- Constants
- Variables
- func AddRoute(name string, fn RouteFunc)
- func AfterFileWrite(fn FileWriteFunc)
- func AfterFileWriteScenario(name string, fn FileWriteFunc)
- func AfterToolFailure(fn ToolFailureFunc)
- func AfterToolFailureScenario(name string, fn ToolFailureFunc)
- func BeforeFileEdit(fn FileEditFunc)
- func BeforeFileEditScenario(name string, fn FileEditFunc)
- func BeforeFileRead(fn FileReadFunc)
- func BeforeFileReadScenario(name string, fn FileReadFunc)
- func BeforePrompt(fn PromptFunc)
- func BeforePromptScenario(name string, fn PromptFunc)
- func BeforeToolCall(fn ToolCallFunc)
- func BeforeToolCallScenario(name string, fn ToolCallFunc)
- func ClearRoutes()
- func Dispatch()
- func Process[I any, O any](handler func(I) O)
- func ProcessE[I any, O any](handler func(I) (O, error))
- func RouteNames() []string
- func UseScenarioSelector(sel ScenarioSelector)
- func WhenAgentIdle(fn AgentIdleFunc)
- func WhenAgentIdleScenario(name string, fn AgentIdleFunc)
- func WhenSubagentIdle(fn AgentIdleFunc)
- func WhenSubagentIdleScenario(name string, fn AgentIdleFunc)
- type AgentID
- type AgentIdleEvent
- type AgentIdleFunc
- type CatalogEntry
- type FileDiff
- type FileEditEvent
- type FileEditFunc
- type FileEditVerdict
- type FileReadEvent
- type FileReadFunc
- type FileReadVerdict
- type FileWriteEvent
- type FileWriteFunc
- type FileWriteVerdict
- type HookStyle
- type IdleVerdict
- type PromptEvent
- type PromptFunc
- type PromptVerdict
- type RouteFunc
- type ScenarioMeta
- type ScenarioSelector
- type ToolCallEvent
- type ToolCallFunc
- type ToolFailureEvent
- type ToolFailureFunc
- type ToolFailureVerdict
- type ToolKind
- type ToolVerdict
Constants ¶
const ( AgentClaude = hookcore.AgentClaude AgentCursor = hookcore.AgentCursor AgentWindsurf = hookcore.AgentWindsurf AgentDroid = hookcore.AgentDroid AgentGemini = hookcore.AgentGemini AgentCopilot = hookcore.AgentCopilot AgentCopilotCLI = hookcore.AgentCopilotCLI ToolKindShell = hookcore.ToolKindShell ToolKindMCP = hookcore.ToolKindMCP ToolKindBuiltin = hookcore.ToolKindBuiltin )
Variables ¶
var ( Resume = hookcore.Resume Interrupt = hookcore.Interrupt Allow = hookcore.Allow AllowWithNote = hookcore.AllowWithNote Deny = hookcore.Deny AskUser = hookcore.AskUser AllowWithInput = hookcore.AllowWithInput AllowWithContext = hookcore.AllowWithContext DenyWithContext = hookcore.DenyWithContext AcceptWrite = hookcore.AcceptWrite RejectWrite = hookcore.RejectWrite RejectWriteWithContext = hookcore.RejectWriteWithContext AnnotateWrite = hookcore.AnnotateWrite AcceptEdit = hookcore.AcceptEdit AcceptEditWithInput = hookcore.AcceptEditWithInput RejectEdit = hookcore.RejectEdit RejectEditWithContext = hookcore.RejectEditWithContext AskBeforeEdit = hookcore.AskBeforeEdit AcceptPrompt = hookcore.AcceptPrompt RejectPrompt = hookcore.RejectPrompt EnrichPrompt = hookcore.EnrichPrompt AcknowledgeFailure = hookcore.AcknowledgeFailure AnnotateFailure = hookcore.AnnotateFailure RejectAfterFailure = hookcore.RejectAfterFailure AllowRead = hookcore.AllowRead DenyRead = hookcore.DenyRead // Built-in scenario selectors. ScenarioFromArg = hookcore.ScenarioFromArg ScenarioFromEnv = hookcore.ScenarioFromEnv )
Verdict constructors, re-exported from hookcore.
var Catalog = []CatalogEntry{ {AgentClaude, "claude-stop", ".claude/settings.json", "Stop", StyleClaudeNested}, {AgentClaude, "claude-pre-tool-use", ".claude/settings.json", "PreToolUse", StyleClaudeNested}, {AgentClaude, "claude-pre-file-write", ".claude/settings.json", "PreToolUse", StyleClaudeNested}, {AgentClaude, "claude-after-file-write", ".claude/settings.json", "PostToolUse", StyleClaudeNested}, {AgentClaude, "claude-user-prompt-submit", ".claude/settings.json", "UserPromptSubmit", StyleClaudeNested}, {AgentClaude, "claude-subagent-stop", ".claude/settings.json", "SubagentStop", StyleClaudeNested}, {AgentClaude, "claude-post-tool-use-failure", ".claude/settings.json", "PostToolUseFailure", StyleClaudeNested}, {AgentDroid, "droid-stop", ".factory/settings.json", "Stop", StyleClaudeNested}, {AgentDroid, "droid-pre-tool-use", ".factory/settings.json", "PreToolUse", StyleClaudeNested}, {AgentDroid, "droid-pre-file-write", ".factory/settings.json", "PreToolUse", StyleClaudeNested}, {AgentDroid, "droid-after-file-write", ".factory/settings.json", "PostToolUse", StyleClaudeNested}, {AgentDroid, "droid-user-prompt-submit", ".factory/settings.json", "UserPromptSubmit", StyleClaudeNested}, {AgentDroid, "droid-subagent-stop", ".factory/settings.json", "SubagentStop", StyleClaudeNested}, {AgentCursor, "cursor-stop", ".cursor/hooks.json", "stop", StyleFlatCommand}, {AgentCursor, "cursor-before-shell", ".cursor/hooks.json", "beforeShellExecution", StyleFlatCommand}, {AgentCursor, "cursor-before-mcp", ".cursor/hooks.json", "beforeMCPExecution", StyleFlatCommand}, {AgentCursor, "cursor-before-file-write", ".cursor/hooks.json", "preToolUse", StyleFlatCommand}, {AgentCursor, "cursor-after-file-edit", ".cursor/hooks.json", "postToolUse", StyleFlatCommand}, {AgentCursor, "cursor-before-submit-prompt", ".cursor/hooks.json", "beforeSubmitPrompt", StyleFlatCommand}, {AgentCursor, "cursor-before-file-read", ".cursor/hooks.json", "beforeReadFile", StyleFlatCommand}, {AgentCursor, "cursor-before-read-file", ".cursor/hooks.json", "beforeReadFile", StyleFlatCommand}, {AgentCursor, "cursor-subagent-stop", ".cursor/hooks.json", "subagentStop", StyleFlatCommand}, {AgentCursor, "cursor-post-tool-use-failure", ".cursor/hooks.json", "postToolUseFailure", StyleFlatCommand}, {AgentWindsurf, "windsurf-pre-run-command", ".codeium/windsurf/hooks.json", "pre_run_command", StyleFlatCommand}, {AgentWindsurf, "windsurf-pre-mcp-tool-use", ".codeium/windsurf/hooks.json", "pre_mcp_tool_use", StyleFlatCommand}, {AgentWindsurf, "windsurf-pre-user-prompt", ".codeium/windsurf/hooks.json", "pre_user_prompt", StyleFlatCommand}, {AgentWindsurf, "windsurf-pre-read-code", ".codeium/windsurf/hooks.json", "pre_read_code", StyleFlatCommand}, {AgentWindsurf, "windsurf-pre-write-code", ".codeium/windsurf/hooks.json", "pre_write_code", StyleFlatCommand}, {AgentWindsurf, "windsurf-post-write-code", ".codeium/windsurf/hooks.json", "post_write_code", StyleFlatCommand}, {AgentWindsurf, "windsurf-post-cascade-response", ".codeium/windsurf/hooks.json", "post_cascade_response", StyleFlatCommand}, {AgentGemini, "gemini-before-tool", ".gemini/settings.json", "BeforeTool", StyleGeminiNested}, {AgentGemini, "gemini-before-file-tool", ".gemini/settings.json", "BeforeTool", StyleGeminiNested}, {AgentGemini, "gemini-after-agent", ".gemini/settings.json", "AfterAgent", StyleGeminiNested}, {AgentGemini, "gemini-before-agent", ".gemini/settings.json", "BeforeAgent", StyleGeminiNested}, {AgentGemini, "gemini-after-file-tool", ".gemini/settings.json", "AfterTool", StyleGeminiNested}, {AgentCopilotCLI, "copilot-cli-stop", ".copilot/hooks/agenthooks.json", "Stop", StyleCopilotCLINested}, {AgentCopilotCLI, "copilot-cli-pre-tool-use", ".copilot/hooks/agenthooks.json", "PreToolUse", StyleCopilotCLINested}, {AgentCopilotCLI, "copilot-cli-pre-file-write", ".copilot/hooks/agenthooks.json", "PreToolUse", StyleCopilotCLINested}, {AgentCopilotCLI, "copilot-cli-after-file-write", ".copilot/hooks/agenthooks.json", "PostToolUse", StyleCopilotCLINested}, {AgentCopilotCLI, "copilot-cli-post-tool-use-failure", ".copilot/hooks/agenthooks.json", "PostToolUseFailure", StyleCopilotCLINested}, {AgentCopilotCLI, "copilot-cli-subagent-stop", ".copilot/hooks/agenthooks.json", "SubagentStop", StyleCopilotCLINested}, {AgentCopilotCLI, "copilot-cli-user-prompt-submit", ".copilot/hooks/agenthooks.json", "UserPromptSubmit", StyleCopilotCLINested}, }
Catalog lists every unified route and where each platform expects it installed.
The VS Code Copilot extension is intentionally absent: its hooks are registered per-project in .github/hooks/*.json (not a home-directory settings file), so `agenthooks install` does not write them automatically — see the README for manual setup. The GitHub Copilot CLI (a distinct product) IS installable via its user-level ~/.copilot/hooks/ directory and is listed below.
Functions ¶
func AddRoute ¶
AddRoute registers fn under the given command name. When Dispatch is called and os.Args[1] matches name, fn is invoked.
func AfterFileWrite ¶
func AfterFileWrite(fn FileWriteFunc)
AfterFileWrite registers the default handler for post-file-edit events:
- Claude Code → "claude-after-file-write"
- Cursor → "cursor-after-file-write" (postToolUse; delivers additional_context, cannot block)
- Windsurf → "windsurf-post-write-code" (fire-and-forget)
- Factory Droid → "droid-after-file-write"
- Gemini CLI → "gemini-after-file-tool"
- VS Code Copilot → "copilot-after-file-write"
func AfterFileWriteScenario ¶
func AfterFileWriteScenario(name string, fn FileWriteFunc)
AfterFileWriteScenario registers a named scenario handler for the post-file-write hook.
func AfterToolFailure ¶
func AfterToolFailure(fn ToolFailureFunc)
AfterToolFailure registers the default handler for failed tool calls:
- Claude Code → "claude-post-tool-use-failure" (can block / annotate)
- Cursor → "cursor-post-tool-use-failure" (observational; verdict ignored)
func AfterToolFailureScenario ¶
func AfterToolFailureScenario(name string, fn ToolFailureFunc)
AfterToolFailureScenario registers a named scenario handler for the tool-failure hook.
func BeforeFileEdit ¶
func BeforeFileEdit(fn FileEditFunc)
BeforeFileEdit registers the default handler for "agent about to write/edit a file" events — a pre-write GATE that can BLOCK the change before it lands (unlike AfterFileWrite, which fires post-write and can only annotate). The handler sees the proposed FilePath/Changes and returns a PreToolUse-style verdict (AcceptEdit / RejectEdit / RejectEditWithContext / AskBeforeEdit):
- Claude Code → "claude-pre-file-write" (PreToolUse, Write/Edit/MultiEdit; deny + additionalContext)
- Factory Droid → "droid-pre-file-write" (PreToolUse, Write/Edit; deny only — no context channel)
func BeforeFileEditScenario ¶
func BeforeFileEditScenario(name string, fn FileEditFunc)
BeforeFileEditScenario registers a named scenario handler for the pre-file-write hook.
func BeforeFileRead ¶
func BeforeFileRead(fn FileReadFunc)
BeforeFileRead registers the default handler for "agent about to read a file" events:
- Cursor → "cursor-before-read-file" (allow/deny)
- Windsurf → "windsurf-pre-read-code" (blocking via exit 2)
func BeforeFileReadScenario ¶
func BeforeFileReadScenario(name string, fn FileReadFunc)
BeforeFileReadScenario registers a named scenario handler for the pre-file-read hook.
func BeforePrompt ¶
func BeforePrompt(fn PromptFunc)
BeforePrompt registers the default handler for prompt-submission events:
- Claude Code → "claude-user-prompt-submit"
- Cursor → "cursor-before-submit-prompt"
- Windsurf → "windsurf-pre-user-prompt" (blocking via exit 2)
- Factory Droid → "droid-user-prompt-submit"
- Gemini CLI → "gemini-before-agent"
- VS Code Copilot → "copilot-user-prompt-submit"
func BeforePromptScenario ¶
func BeforePromptScenario(name string, fn PromptFunc)
BeforePromptScenario registers a named scenario handler for the prompt-submit hook.
func BeforeToolCall ¶
func BeforeToolCall(fn ToolCallFunc)
BeforeToolCall registers the default handler for pre-execution events:
- Claude Code → "claude-pre-tool-use"
- Cursor → "cursor-before-shell", "cursor-before-mcp"
- Windsurf → "windsurf-pre-run-command", "windsurf-pre-mcp-tool-use" (blocking via exit 2)
- Factory Droid → "droid-pre-tool-use" (blocking via exit 2)
- Gemini CLI → "gemini-before-tool"
- VS Code Copilot → "copilot-pre-tool-use"
func BeforeToolCallScenario ¶
func BeforeToolCallScenario(name string, fn ToolCallFunc)
BeforeToolCallScenario registers a named scenario handler for the pre-tool-call hook.
func ClearRoutes ¶
func ClearRoutes()
ClearRoutes removes all registered handlers and scenario state. Intended for use in tests.
func Dispatch ¶
func Dispatch()
Dispatch selects and runs the handler whose name matches os.Args[1]. If os.Args[1] is absent, the binary name is used instead. Exits with code 1 if no matching handler is found.
func Process ¶
Process reads JSON from stdin, passes it to handler, and writes the result to stdout. Any stdin parse error causes a graceful exit (code 0) so a bad payload never blocks an agent. The implementation lives in internal/hookcore so platform adapters share it.
func ProcessE ¶
ProcessE is like Process but allows the handler to signal a blocking error. When handler returns a non-nil error, agenthooks writes the message to stderr and exits with code 2, which causes supporting agents to surface the message and block the pending action.
func RouteNames ¶
func RouteNames() []string
RouteNames returns the names of all currently registered routes, unsorted. Intended for inspection and tests (e.g. verifying the route Catalog stays in sync with what the unified handlers register).
func UseScenarioSelector ¶
func UseScenarioSelector(sel ScenarioSelector)
UseScenarioSelector overrides how the active scenario key is chosen for all hooks. A nil selector is ignored. The default is ScenarioFromArg.
func WhenAgentIdle ¶
func WhenAgentIdle(fn AgentIdleFunc)
WhenAgentIdle registers the default handler for "agent finished responding" events:
- Claude Code → "claude-stop"
- Cursor → "cursor-stop"
- Windsurf → "windsurf-post-cascade-response" (fire-and-forget; Interrupt logged, ignored)
- Factory Droid → "droid-stop"
- Gemini CLI → "gemini-after-agent"
- VS Code Copilot → "copilot-stop"
func WhenAgentIdleScenario ¶
func WhenAgentIdleScenario(name string, fn AgentIdleFunc)
WhenAgentIdleScenario registers a named scenario handler for the agent-idle hook.
func WhenSubagentIdle ¶
func WhenSubagentIdle(fn AgentIdleFunc)
WhenSubagentIdle registers the default handler for "subagent finished" events. It reuses AgentIdleEvent/IdleVerdict; Interrupt blocks the subagent from stopping (on Cursor it auto-submits a follow-up):
- Claude Code → "claude-subagent-stop"
- Factory Droid → "droid-subagent-stop"
- VS Code Copilot → "copilot-subagent-stop"
- Cursor → "cursor-subagent-stop"
func WhenSubagentIdleScenario ¶
func WhenSubagentIdleScenario(name string, fn AgentIdleFunc)
WhenSubagentIdleScenario registers a named scenario handler for the subagent-idle hook.
Types ¶
type AgentIdleEvent ¶
type AgentIdleEvent = hookcore.AgentIdleEvent
type AgentIdleFunc ¶
type AgentIdleFunc = hookcore.AgentIdleFunc
type CatalogEntry ¶
type CatalogEntry struct {
Agent AgentID // which platform this route belongs to
Route string // the route name passed as the hook binary's first argument
SettingsRel string // settings-file path relative to the user's home directory (slash-separated)
EventKey string // key under which the hook is registered in the settings file
Style HookStyle // how the entry is encoded in that file
}
CatalogEntry maps one unified hook route to the settings-file slot a platform expects it in. It is the single source of truth shared by route registration (the WhenAgentIdle / BeforeToolCall / ... functions) and `agenthooks install`.
type FileEditEvent ¶
type FileEditEvent = hookcore.FileEditEvent
type FileEditFunc ¶
type FileEditFunc = hookcore.FileEditFunc
type FileEditVerdict ¶
type FileEditVerdict = hookcore.FileEditVerdict
type FileReadEvent ¶
type FileReadEvent = hookcore.FileReadEvent
type FileReadFunc ¶
type FileReadFunc = hookcore.FileReadFunc
type FileReadVerdict ¶
type FileReadVerdict = hookcore.FileReadVerdict
type FileWriteEvent ¶
type FileWriteEvent = hookcore.FileWriteEvent
type FileWriteFunc ¶
type FileWriteFunc = hookcore.FileWriteFunc
type FileWriteVerdict ¶
type FileWriteVerdict = hookcore.FileWriteVerdict
type HookStyle ¶
type HookStyle string
HookStyle describes how a platform's settings file encodes a hook entry.
const ( // StyleClaudeNested writes {"hooks": {EventKey: [{"type":"command","command":...}]}}. // Used by Claude Code and Factory Droid. StyleClaudeNested HookStyle = "claude-nested" // StyleFlatCommand writes {EventKey: {"command": ...}} at the top level. // Used by Cursor and Windsurf Cascade. StyleFlatCommand HookStyle = "flat-command" // StyleGeminiNested writes {"hooks": {EventKey: [{"matcher":"","hooks":[{"type":"command","command":...}]}]}}. // Used by Gemini CLI. StyleGeminiNested HookStyle = "gemini-nested" // StyleCopilotCLINested writes {"version":1,"hooks":{EventKey:[{"type":"command","command":...}]}}. // Used by GitHub Copilot CLI (a dedicated hooks file under ~/.copilot/hooks/). StyleCopilotCLINested HookStyle = "copilot-cli-nested" )
type IdleVerdict ¶
type IdleVerdict = hookcore.IdleVerdict
type PromptEvent ¶
type PromptEvent = hookcore.PromptEvent
type PromptFunc ¶
type PromptFunc = hookcore.PromptFunc
type PromptVerdict ¶
type PromptVerdict = hookcore.PromptVerdict
type ScenarioMeta ¶
type ScenarioMeta = hookcore.ScenarioMeta
type ScenarioSelector ¶
type ScenarioSelector = hookcore.ScenarioSelector
type ToolCallEvent ¶
type ToolCallEvent = hookcore.ToolCallEvent
type ToolCallFunc ¶
type ToolCallFunc = hookcore.ToolCallFunc
type ToolFailureEvent ¶
type ToolFailureEvent = hookcore.ToolFailureEvent
type ToolFailureFunc ¶
type ToolFailureFunc = hookcore.ToolFailureFunc
type ToolFailureVerdict ¶
type ToolFailureVerdict = hookcore.ToolFailureVerdict
type ToolVerdict ¶
type ToolVerdict = hookcore.ToolVerdict
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package claude provides types and response helpers for Claude Code hooks.
|
Package claude provides types and response helpers for Claude Code hooks. |
|
cmd
|
|
|
agenthooks
command
Command agenthooks provides install, build, and init utilities for agent hooks.
|
Command agenthooks provides install, build, and init utilities for agent hooks. |
|
Package copilot provides types and response helpers for GitHub Copilot agent hooks in Visual Studio Code.
|
Package copilot provides types and response helpers for GitHub Copilot agent hooks in Visual Studio Code. |
|
Package copilotcli provides types and response helpers for GitHub Copilot CLI (and Copilot cloud agent) hooks.
|
Package copilotcli provides types and response helpers for GitHub Copilot CLI (and Copilot cloud agent) hooks. |
|
Package cursor provides types and response helpers for Cursor IDE hooks.
|
Package cursor provides types and response helpers for Cursor IDE hooks. |
|
Package droid provides types and response helpers for Factory Droid hooks.
|
Package droid provides types and response helpers for Factory Droid hooks. |
|
examples
|
|
|
copilot-demo
command
copilot-demo is a runnable hook binary for the VS Code Copilot demo.
|
copilot-demo is a runnable hook binary for the VS Code Copilot demo. |
|
Package gemini provides types and response helpers for Gemini CLI hooks.
|
Package gemini provides types and response helpers for Gemini CLI hooks. |
|
Package install writes per-agent hook configuration into each agent's settings file.
|
Package install writes per-agent hook configuration into each agent's settings file. |
|
internal
|
|
|
codec
Package codec handles JSON serialization between hook processes and AI agents.
|
Package codec handles JSON serialization between hook processes and AI agents. |
|
hookcore
Package hookcore defines the platform-agnostic vocabulary of the unified hook API: the event views, verdicts, and constructors shared across every agent.
|
Package hookcore defines the platform-agnostic vocabulary of the unified hook API: the event views, verdicts, and constructors shared across every agent. |
|
scaffold
Package scaffold generates a starter hooks project from built-in templates.
|
Package scaffold generates a starter hooks project from built-in templates. |
|
Package windsurf provides types and response helpers for Windsurf Cascade hooks.
|
Package windsurf provides types and response helpers for Windsurf Cascade hooks. |