Cursor Hooks Validator (Free): Fix Cursor Hooks Not Working

Find out in one command why your Cursor hook isn't firing.

Cursor Hooks Validator (Free): Fix Cursor Hooks Not Working product illustration

What it does

Cursor hooks not working? hookcheck tells you why, in one command, without restarting Cursor five times. It checks .cursor/hooks.json (and ~/.cursor/hooks.json) against the Cursor hooks reference as of Nov 2026 and runs your hooks on realistic sample events to check what they print.

Python 3.9+, standard library only. No install, no network, nothing leaves your machine.

What you get

The screenshot is real output from the kit’s own commands. Tested from a fresh download of this exact zip: ./run_tests.sh ran 9 tests OK on Python 3.13 and 3.10.

Real terminal output from Cursor Hooks Validator: Fix Cursor Hooks Not Working: demo and passing tests

Use it

python3 tools/hookcheck.py check                       # .cursor/hooks.json in the current project
python3 tools/hookcheck.py check --user                # also ~/.cursor/hooks.json
python3 tools/hookcheck.py check path/to/hooks.json -v # one file, with info notes
python3 tools/hookcheck.py simulate beforeShellExecution --command "git push --force"
python3 tools/hookcheck.py simulate beforeReadFile --file .env
python3 tools/hookcheck.py simulate stop --status completed
python3 tools/hookcheck.py simulate beforeMCPExecution --input samples/beforeMCPExecution.json
python3 tools/hookcheck.py events                      # all 21 events, matcher values and output fields

Exit code 0 = no errors, 1 = errors (or a simulated hook misbehaved), 2 = file not found. Add --json for CI.

What check catches

  • invalid JSON (with a hint for trailing commas and comments): Cursor silently ignores the whole file
  • missing "version": 1, "hooks" not an object, unknown top-level keys
  • unknown event names with a suggestion (afterFileEdited -> afterFileEdit) and Claude Code names (PreToolUse -> preToolUse)
  • misspelled options (comand, timout), wrong types for timeout, failClosed, loop_limit, and type
  • script paths that don't resolve from where Cursor runs the hook: project hooks run from the project root (./hooks/x.sh must be .cursor/hooks/x.sh), user hooks from ~/.cursor
  • scripts that aren't executable, have no #! line, or have Windows line endings; interpreters and jq that aren't installed
  • matcher regexes that don't compile, and matchers that can never match (afterFileEdit is matched against Write, beforeSubmitPrompt against UserPromptSubmit...), so the hook silently never runs
  • timeout written in milliseconds (it is seconds), loop_limit on events other than stop/subagentStop
  • beforeMCPExecution security hooks without "failClosed": true (a crash lets the call through)
  • with -v: events cloud agents don't run (sessionStart, beforeMCPExecution...) and prompt hooks in cloud agents

What simulate checks

It runs every hook registered for one event exactly the way Cursor does: same working directory, the event JSON on stdin, CURSOR_PROJECT_DIR, matcher filtering and timeout. Then it checks the answer: exactly one JSON object on stdout (log lines on stdout break it), a valid permission for that event (ask is not enforced for preToolUse and means deny for subagentStart), exit code 2 = deny, crashes and timeouts fail open unless failClosed. It prints what Cursor would do after merging all answers (deny beats ask beats allow). samples/ has a realistic input for each of the 21 events.

Try it on the broken example

./tools/demo.sh checks examples/.cursor/hooks.json, which has six real-world mistakes, then simulates a shell command against it. examples/fixed-hooks.json is the corrected file.

How it was tested

./run_tests.sh (9 tests, Python 3.13 and 3.10): the broken example (6 errors found), the fixed example (0 errors), every error class above on generated configs, user vs project path rules, CLI exit codes, and the simulator on hooks that print logs to stdout, return an invalid permission, or exit 2.

Honest limits

  • Follows the Cursor hooks docs as of Nov 2026; Cursor may add events or fields. Unknown keys are warnings, not errors.
  • simulate uses sample events; real events can carry more fields. Prompt-type hooks are not simulated (they call a model).
  • Team and enterprise hooks from the dashboard are not visible on disk, so they are not checked.

Format and license

Zip, 23 KB, v1.0, 31 files. Free to use, modify and share for any purpose, including commercial work, as long as this notice stays in copies. Not affiliated with or endorsed by Cursor.

FAQ

Does it change my files?
No. It only reads hooks.json and runs your hook scripts with sample input.
Does it need Cursor installed?
No. It reads the files and runs the scripts itself.
Where do I get ready hooks?
The Cursor Hooks Pack has 8 tested hooks and an installer.
How do I get the files?
Click Download free. The zip downloads straight away: no checkout, no account.
Do I have to sign up for the newsletter?
No. The form is optional and the download works without it.

Get Cursor Hooks Validator (Free): Fix Cursor Hooks Not Working

Free tool that finds why Cursor hooks aren't running: checks hooks.json events, matchers, script paths and permissions, then simulates hook events. Offline.

Pairs well with

Download$19

Claude Code Safety Hooks

Tested Claude Code hooks as a plugin: block rm -rf and force-push to main, protect .env and lockfiles, catch API keys in files, audit log. $19 once.

View Claude Code Safety Hooks
DownloadFree

Claude Code Hooks Starter (Free)

Free Claude Code hooks examples: SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, Notification and Stop, plus a local tester that checks your hook's output.

View Claude Code Hooks Starter (Free)

Related

1 email a week: new x402 + MCP tools and free kits.

Short weekly digest of new x402 + MCP tools and free kits, including the MCP OAuth Checker. The download above works without signing up. No spam, unsubscribe any time.