Files
cursor-flasher/hooks/notify.py
cottongin 392183692e Disable shell execution hooks to fix broken notifications
beforeShellExecution in hooks.json required a JSON response we never
provided, likely causing Cursor to silently break the entire hook
pipeline. Commenting out those entries (and afterShellExecution) from
HOOKS_CONFIG restores reliable preToolUse/postToolUse delivery. All
Python handler code is retained as dead code for reference.

Also reverts the is_cursor_frontmost() gate in _check_pending — pulses
should fire unconditionally when the approval delay expires.

Made-with: Cursor
2026-03-11 15:24:43 -04:00

56 lines
1.7 KiB
Python
Executable File

#!/usr/bin/env python3
"""Cursor hook script that notifies the cursor-flasher daemon via Unix socket.
Installed as a Cursor hook to trigger a window flash when the agent needs
user attention. Reads hook JSON from stdin, extracts workspace and event
info, and sends it to the daemon's socket.
Shell-specific hook mapping (beforeShellExecution -> shellApproved,
afterShellExecution -> shellCompleted) is retained but currently dead
code — those hooks are disabled in HOOKS_CONFIG / hooks.json because
beforeShellExecution fires pre-approval and Cursor expects a JSON
response we don't provide.
"""
import json
import os
import socket
import sys
SOCKET_PATH = os.path.expanduser("~/.cursor-flasher/flasher.sock")
_SHELL_EVENT_MAP = {
"beforeShellExecution": "shellApproved",
"afterShellExecution": "shellCompleted",
}
def main() -> None:
try:
data = json.load(sys.stdin)
except (json.JSONDecodeError, ValueError):
return
workspace_roots = data.get("workspace_roots") or []
workspace = workspace_roots[0] if workspace_roots else ""
event = data.get("hook_event_name", "")
mapped_event = _SHELL_EVENT_MAP.get(event)
if mapped_event:
msg = json.dumps({"workspace": workspace, "event": mapped_event, "tool": "Shell"})
else:
tool = data.get("tool_name", "")
msg = json.dumps({"workspace": workspace, "event": event, "tool": tool})
try:
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
s.settimeout(1)
s.connect(SOCKET_PATH)
s.sendall(msg.encode())
s.close()
except (ConnectionRefusedError, FileNotFoundError, OSError):
pass
if __name__ == "__main__":
main()