Wednesday, 16 September 2026

Cursor Hooks Setup: Perimeter Log

[TECHNICAL GUIDE]

This is a purely technical guide on how to set up some monitoring in Cursor IDE 

Purpose: Set up two Cursor hooks that record file access and shell commands reaching outside the currently open project. The log remains local to the machine and applies across workspaces.


1. Confirm jq is installed

Both scripts parse JSON sent by Cursor, so jq must be available on your PATH.

which jq || brew install jq

If which jq prints a path, jq is already installed and the installation command will not run.

2. Create the file-watcher script

Create the hooks directory:

mkdir -p ~/.cursor/hooks

Create ~/.cursor/hooks/log-external.sh with the following contents:

vim ~/.cursor/hooks/log-external.sh

#!/bin/bash input=$(cat) file_path=$(echo "$input" | jq -r '.file_path // .path // empty') project_dir="${CURSOR_PROJECT_DIR:-}" log_file="$HOME/.cursor/external-activity.log" if [ -n "$file_path" ]; then abs=$(realpath "$file_path" 2>/dev/null || echo "$file_path") case "$abs" in "$project_dir"*) ;; *) printf '[%s] %s -> file: %s\n' \ "$(date '+%F %T')" "$project_dir" "$abs" >> "$log_file" ;; esac fi echo '{"permission":"allow"}'

This hook runs when Cursor reads or edits a file. It resolves the path and logs it only when it falls outside the active project directory.

  • Press Esc to leave insert mode.

  • Type :wq and hit Enter to save and quit.

3. Create the command-watcher script


Create ~/.cursor/hooks/log-external-cmd.sh:

vim ~/.cursor/hooks/log-external-cmd.sh
#!/bin/bash input=$(cat) cmd=$(echo "$input" | jq -r '.command // empty') project_dir="${CURSOR_PROJECT_DIR:-}" log_file="$HOME/.cursor/external-activity.log" if [ -n "$cmd" ]; then if echo "$cmd" | grep -qE '(~|/Users/|/home/)' \ && ! echo "$cmd" | grep -qF "$project_dir"; then printf '[%s] %s -> command: %s\n' \ "$(date '+%F %T')" "$project_dir" "$cmd" >> "$log_file" fi fi echo '{"permission":"allow"}'
  • Press Esc to leave insert mode.

  • Type :wq and hit Enter to save and quit.


This hook uses a heuristic. It records commands that reference common home-directory paths, such as ~, /Users/, or /home/, unless the current project path also appears in the command.

4. Make both scripts executable

chmod +x ~/.cursor/hooks/log-external.sh chmod +x ~/.cursor/hooks/log-external-cmd.sh

5. Configure Cursor hooks

Create or edit the user-level file ~/.cursor/hooks.json. User-level configuration ensures the hooks apply regardless of the repository or worktree currently open.

vim ~/.cursor/hooks.json
{ "version": 1, "hooks": { "afterFileEdit": [ { "command": "$HOME/.cursor/hooks/log-external.sh" } ], "beforeReadFile": [ { "command": "$HOME/.cursor/hooks/log-external.sh" } ], "beforeShellExecution": [ { "command": "$HOME/.cursor/hooks/log-external-cmd.sh" } ] } }
  • Press Esc to leave insert mode.

  • Type :wq and hit Enter to save and quit.

If hooks.json already contains other configuration, merge these entries into the existing "hooks" object. Do not replace unrelated hooks.

6. Restart and verify

Cursor reads hooks during startup. Reload the window with Cmd+Shift+P → Developer: Reload Window, or fully restart Cursor.

Ask the agent to perform an operation outside the open project, such as listing the Desktop directory:

ls ~/Desktop

Monitor the activity log:

tail -f ~/.cursor/external-activity.log

A matching entry resembles:

[2026-09-16 10:42:07] /Users/example/project -> command: ls ~/Desktop

What the hooks capture

Hook

Event

Detection method

Output

afterFileEdit

Agent edits a file

Resolved file path is compared with the project directory

File activity entry

beforeReadFile

Agent reads a file

Resolved file path is compared with the project directory

File activity entry

beforeShellExecution

Agent runs a shell command

Heuristic search for home-directory paths outside the project

Command activity entry

Known limitations

  • Command detection is heuristic. It may miss indirect access, including git push, uploads with curl, or Docker volume mounts.

  • File detection is the reliable half. It compares a resolved path with the project root for each file event.

  • The log is local. There is no synchronization between devices. Each machine maintains its own ~/.cursor/external-activity.log.

  • Only Cursor is covered. Other coding agents use separate approval or hook systems.

  • The log does not rotate automatically. Archive or clear it periodically to prevent unbounded growth.

Log maintenance

To clear the log after reviewing it:

echo -n > ~/.cursor/external-activity.log

For audit retention, archive the file first, preferably with a date in the filename.

Optional next step: blocking instead of logging

After running in log-only mode and reviewing false positives, the command watcher can be adapted to block matched commands by returning {"permission":"deny"} on the matched branch. Start with logging because the command pattern is intentionally broad and may identify legitimate operations.

No comments: