Added a 10-minute timeout to the GNOME notification loop, so the script exits even when no closed signal arrives. The previous version leaked memory: every unanswered notification left a Python process running until the next reboot. Sorry about that!
Changes
- It's quite frustrating to come back to a coding agent after a while only to see that it needed user input only 10 seconds into the task.
- You can set up [notification hooks](https://code.claude.com/docs/en/hooks#notification) to trigger a Desktop notification (or similar) whenever the Claude Code agent halts. Clicking the notification will open your configured IDE in the correct project directory.
- 
- This is my configuration:
- ```sh
- # ~/.claude/notify.sh
- # Note: You need to `chmod +x` this
- #!/bin/bash
- # Test usage:
- # echo '{ "title": "foo", "message": "bar" }' | bash ~/.claude/notify.sh
- EDITOR_COMMAND="/snap/bin/rubymine"
- INPUT=$(cat)
- INPUT=${INPUT:-"{}"}
- TITLE=$(echo "$INPUT" | jq -r '.title // "Claude Code"')
- MESSAGE=$(echo "$INPUT" | jq -r '.message // "Needs your input"')
- ICON=$(echo "$INPUT" | jq -r '.icon // "dialog-question"')
- # We use Python here because GNOME Shell's notification daemon does not emit
- # D-Bus ActionInvoked signals for the "default" click action. Only libnotify
- # with a GLib main loop can reliably handle click-to-open-editor on GNOME.
- python3 - "$TITLE" "$MESSAGE" "$PWD" "$ICON" "$EDITOR_COMMAND" << 'PYTHON' > /dev/null 2>&1 & disown
- import sys
- import subprocess
- import gi
- gi.require_version('Notify', '0.7')
- from gi.repository import Notify, GLib
- title = sys.argv[1]
- message = sys.argv[2]
- working_directory = sys.argv[3]
- icon = sys.argv[4]
- editor_command = sys.argv[5]
- def on_notification_click(notification, action, user_data):
- subprocess.Popen([editor_command, working_directory], start_new_session=True)
- loop.quit()
- def on_close(notification):
- loop.quit()
- Notify.init("Claude Code")
- notification = Notify.Notification.new(title, message, icon)
- notification.add_action("default", "Open Editor", on_notification_click, None)
- notification.connect("closed", on_close)
- notification.show()
-# Keep the script alive waiting for the user's click or dismissal- +# Keep the script alive waiting for the user's click or dismissal.
- +# GNOME doesn't always emit "closed" (e.g. for notifications left in the message tray),
- +# so give up after 10 minutes instead of lingering forever.
- loop = GLib.MainLoop()
- +GLib.timeout_add_seconds(600, loop.quit)
- loop.run()
- PYTHON
- ```
- ```sh
- # ~/.claude/settings.json
- {
- "hooks": {
- "Notification": [
- {
- "matcher": "permission_prompt|elicitation_dialog",
- "hooks": [
- {
- "type": "command",
- "command": "bash ~/.claude/notify.sh"- + "command": "bash ~/.claude/notify.sh"
- }
- ]
- }
- ],
- "Stop": [
- {
- "hooks": [
- {
- "type": "command",
- "command": "echo '{\"title\": \"Claude Code\", \"message\": \"Finished its current task\", \"icon\": \"dialog-information\"}' | bash ~/.claude/notify.sh"
- }
- ]
- }
- ]
- },- + }
- }
- ```
Posted by Michael Leimstädtner to makandra dev (2026-11-04 11:50)