| Field | Value |
|---|---|
| Link | https://docs.cursor.com/agent/hooks |
| Tags | cursor, hooks, logging, mongodb, agent-tracking, observability |
| Date | 2026-02-25 |
Cursor hooks are shell scripts triggered at key points in the agent lifecycle. They're configured per-project in .cursor/hooks.json. There's no global hooks.json — it's per-project only. To get "everywhere automatically" logging, the best approach is a zsh chpwd hook that auto-installs the config whenever you cd into a git repo. The hooks receive JSON on stdin with context (tool name, command, etc.) and can log to Mongo via our existing logger.js.
.cursor/hooks.json at the project rootpreToolUse hooks can block actions via {"decision": "block", "reason": "..."}stop hooks can auto-continue via {"followup_message": "..."}| Hook | When | stdin Data | Can Control? |
|---|---|---|---|
sessionStart | New agent session | Session metadata | Set env vars, inject context |
sessionEnd | Session ends | Session metadata | Informational |
beforeSubmitPrompt | Before user prompt sent | Prompt text | Can modify/block |
preToolUse | Before tool runs | Tool name, input args | Block with {"decision":"block"} |
postToolUse | After tool completes | Tool name, output, exit code | Informational |
afterFileEdit | After agent edits file | File path, changes | Informational |
afterTabFileEdit | After tab-complete edit | File path | Informational |
beforeShellExecution | Before shell command | Command string | Can block |
stop | Agent about to stop | Session context | Auto-continue with {"followup_message":"..."} |
preCompact | Before context compaction | Context metadata | Informational |
subagentStart | Subagent spawns | Agent name, task | Informational |
subagentStop | Subagent finishes | Agent name, result | Informational |
``json
{
"version": 1,
"hooks": {
"sessionStart": [
{
"command": ".cursor/hooks/session-start.sh",
"timeout": 5
}
],
"preToolUse": [
{
"command": ".cursor/hooks/pre-tool-use.sh",
"matcher": "Shell|Write|Edit",
"timeout": 5
}
],
"stop": [
{
"command": ".cursor/hooks/stop.sh",
"timeout": 10,
"loop_limit": 3
}
]
}
}
`
Hook Entry Fields
Field Required Description command Yes Shell command or script path matcher No Regex to filter by tool name (e.g. "Shell\ Write") timeout No Seconds before hook is killed loop_limit No Max iterations for stop hooks (prevents infinite loops)
Full Summary
How Hooks Work
Cursor reads .cursor/hooks.json from the project root when an agent session starts
At each lifecycle event, matching hooks fire in order
Each hook runs as a subprocess — it receives JSON on stdin and returns JSON on stdout
Hooks that produce no stdout or invalid JSON are treated as no-ops
preToolUse hooks MUST return {"decision": "allow"} or {"decision": "block", "reason": "..."} — no output = allow
stop hooks can return {"followup_message": "..."} to auto-continue the agent
Multiple hooks can be registered for the same event — they run sequentially
The "Everywhere Automatically" Problem
Cursor hooks are strictly per-project. The
.cursor/hooks.json must exist in each project's root. There is no ~/.cursor/hooks.json global config. Three approaches:
Approach 1: zsh chpwd hook (recommended)
Add a function to
~/.zshrc that checks for a .git directory and auto-creates .cursor/hooks.json if missing. Zero friction — it just works when you cd anywhere.
Approach 2: Git template directory
Set
git config --global init.templateDir ~/.git-template and put .cursor/hooks.json in the template. Only works for new repos (git init/clone).
Approach 3: Cursor Plugin (future)
Cursor 2.5 introduced the plugin marketplace. A plugin could theoretically install hooks globally. Not available for custom use yet.
What To Track
For full agent observability:
What Hook Gives You All user prompts beforeSubmitPrompt Every question asked across all projects All shell commands preToolUse (matcher: Shell) Terminal command audit trail All file changes afterFileEdit Which files agents modify Session lifecycle sessionStart + sessionEnd Session duration, project context Subagent activity subagentStart + subagentStop Background agent tracking Agent completions stop When/why agents finish
Real-World Examples Found
- reactive/data-client —
afterFileEdit hook runs ESLint auto-fixOthmanAdi/planning-with-files — Full hook suite: preToolUse reads task plan for context, postToolUse reminds to update plan, stop checks if all phases are complete and auto-continues if notbetter-stack-ai/better-stack — sessionStart sets Node.js env vars, afterFileEdit runs formatterduckduckgo/privacy-configuration — afterFileEdit runs code formatterentireio/cli — Most complete: sessionStart, sessionEnd, beforeSubmitPrompt, stop, preCompact, subagentStart, subagentStop`