Create agent hooks. Use when you want to create a hook, write hooks.json, add hook scripts, or automate behavior around agent events.
Create hooks when you want the agent to run custom logic before or after agent events. Hooks are scripts or prompt-based checks that exchange JSON over stdin/stdout and can observe, block, modify, or follow up on behavior.
When the user asks for a hook, don't stop at describing the format. Gather the missing requirements, then create or update the hook files directly.
Before you write anything, determine:
Infer these from the conversation when possible. Only ask for the missing pieces.
.cursor/hooks.json and .cursor/hooks/*~/.cursor/hooks.json and ~/.cursor/hooks/*Path behavior matters:
.cursor/hooks/my-hook.sh~/.cursor/, so use paths like ./hooks/my-hook.sh or hooks/my-hook.shPrefer project hooks when the behavior should be shared with the repository and checked into version control.
Use the narrowest event that matches the user's goal.
sessionStart, sessionEnd: set up or audit a sessionpreToolUse, postToolUse, postToolUseFailure: work across all toolssubagentStart, subagentStop: control or continue Task/subagent workflowsbeforeShellExecution, afterShellExecution: gate or audit terminal commandsbeforeMCPExecution, afterMCPExecution: gate or audit MCP tool callsbeforeReadFile, afterFileEdit: control file reads or post-process editsbeforeSubmitPrompt: validate prompts before they are sentpreCompact: observe context compactionstop: handle agent completionafterAgentResponse, afterAgentThought: track agent output or reasoningbeforeTabFileRead: control file access for inline completionsafterTabFileEdit: post-process edits made by TabbeforeShellExecutionafterShellExecutionafterFileEditpreToolUsepostToolUsesubagentStartsubagentStopbeforeSubmitPromptbeforeMCPExecutionCreate a hooks.json file with schema version 1:
{
"version": 1,
"hooks": {
"afterFileEdit": [
{
"command": ".cursor/hooks/format.sh"
}
]
}
}
Each hook definition can include:
command: shell command or script pathtype: "command" or "prompt" (defaults to "command")timeout: timeout in secondsmatcher: filter for when the hook runsfailClosed: block the action when the hook crashes, times out, or returns invalid JSONloop_limit: mainly for stop and subagentStop follow-up loopsUse matchers to avoid running the hook on every event.
preToolUse / postToolUse / postToolUseFailure: match on tool type such as Shell, Read, Write, Task, or MCP tools in MCP: ... formsubagentStart / subagentStop: match on subagent type such as generalPurpose, explore, or shellbeforeShellExecution / afterShellExecution: match on the full shell command stringbeforeReadFile: match on tool type such as Read or TabReadafterFileEdit: match on tool type such as Write or TabWritebeforeSubmitPrompt: matches the value UserPromptSubmitImportant matcher warning:
[[:space:]]; use JavaScript equivalents like \sIf the user wants a hook for only one risky command family, prefer script-side filtering for the first working version and add a matcher afterward only if it is simple and clearly correct.
Command hooks are the default. They receive JSON on stdin and can return JSON on stdout.
Before using a command hook, verify that every executable it depends on will actually run in the hook environment:
$PATHjq, python3, node, or repo-local CLIs, verify that explicitly before finishingDo not assume a binary exists just because it is common on your machine.
{
"version": 1,
"hooks": {
"beforeShellExecution": [
{
"command": ".cursor/hooks/approve-network.sh",
"matcher": "curl|wget|nc ",
"failClosed": true
}
]
}
}
#!/bin/bash
input=$(cat)
command=$(echo "$input" | jq -r '.command // empty')
if [[ "$command" =~ curl|wget|nc ]]; then
echo '{
"permission": "ask",
"user_message": "This command may make a network request. Please review it before continuing.",
"agent_message": "A hook flagged this shell command as a possible network call."
}'
exit 0
fi
echo '{ "permission": "allow" }'
exit 0
Important behavior:
0: success2: block the action, same as returning denyfailClosed: trueAlways make hook scripts executable after creating them.
Prompt hooks are useful when the policy is easier to describe than to script.
{
"version": 1,
"hooks": {
"beforeShellExecution": [
{
"type": "prompt",
"prompt": "Does this command look safe to execute? Only allow read-only operations. Here is the hook input: $ARGUMENTS",
"timeout": 10
}
]
}
}
Use prompt hooks for lightweight policy decisions. Prefer command hooks when the logic must be deterministic or when the user needs exact, auditable behavior.
Use the event's supported output fields only.
preToolUse: can return permission, user_message, agent_message, and updated_inputpostToolUse: can return additional_context; for MCP tools it can also return updated_mcp_tool_outputsubagentStart: can return permission and user_messagesubagentStop: can return followup_messagebeforeShellExecution / beforeMCPExecution: can return permission, user_message, and agent_messageWhen the user wants to rewrite a tool call, prefer preToolUse. When they want to gate only shell commands, prefer beforeShellExecution.
hooks.json file$PATHIf you are editing an existing hooks setup, preserve unrelated hooks and only change the minimum necessary entries.
hooks.json and reloads on save~/.cursor/command -v or equivalentfailClosed: true2 is valid