Claude Code supports hooks, a powerful feature that lets you run commands before or after certain events. In this post, we’ll set up a hook to display a desktop notification every time Claude finishes executing a tool or completes a task. This is especially handy if you often multitask and want to know exactly when Claude is ready for more action.

If you’re new to Claude Code hooks, check out the official hooks documentation first.

What We’ll Build

We’ll configure a hook for the Stop event — which triggers whenever Claude finishes running a tool. The hook will execute a command that shows a native notification:

  • On macOS, we’ll use osascript (AppleScript CLI).
  • On Linux, we’ll use notify-send.

Prerequisites

  • Claude Code installed and configured.
  • macOS: osascript comes built-in. Important: ensure the app that executes the hook (Script Editor) has permission in System Settings → Notifications; otherwise notifications won’t appear.
  • Linux: make sure you have notify-send installed (often included with libnotify-bin on Debian/Ubuntu systems).

The Hook Configuration

Claude Code reads JSON settings from different places depending on the scope. Use the file that matches where you want the configuration to apply:

  • ~/.claude/settings.jsonUser/global settings that apply to all projects (personal defaults).
  • .claude/settings.jsonProject settings checked into source control and shared with the team.
  • .claude/settings.local.jsonProject-local / per-machine overrides (not committed to git; ideal for personal experiments, local hooks or OS-specific commands).

Configure hooks under the hooks key in the appropriate JSON file (e.g. add a Stop hook to .claude/settings.local.json for a per-machine notifier). Settings are merged with a clear precedence, so enterprise-managed policies override project and user files — keep that in mind if your environment is managed.

Option A — macOS

Use osascript (AppleScript from the CLI) to show a native notification:

{
  "hooks": {
    "Stop": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude is ready for more action!\" with title \"Claude Code\"'"
          }
        ]
      }
    ]
  }
}

Option B — Linux

Use notify-send (make sure it’s installed, e.g., libnotify-bin on Debian/Ubuntu):

{
  "hooks": {
    "Stop": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "command",
            "command": "notify-send 'Claude Code' 'Claude is ready for more action!'"
          }
        ]
      }
    ]
  }
}

Let’s break down what’s happening here:

  • Stop — the event we’re listening for. It fires whenever Claude finishes a run or tool execution.
  • matcher: "*" — applies the hook to all tools.
  • type: "command" — tells Claude to execute a shell command.
  • The command — executes a single OS-specific notifier (no runtime environment checks needed).

Testing It

  1. Open Claude Code and run any action that uses a tool — for example, ask it to run a script or modify a file.
  2. Once Claude finishes, you should see a system notification pop up.
  3. If nothing appears:
    • On macOS, check System Settings → Notifications and ensure the app that runs the hook (Script Editor, Terminal, or VS Code) has permission to show notifications.
    • On Linux, verify that notify-send is installed and your desktop environment supports notifications.

Conclusion

With just a few lines of configuration, you can make Claude Code a more responsive assistant. Desktop notifications help you stay focused without constantly checking whether Claude has finished working.

This small tweak is a great example of how hooks can make your AI tools fit seamlessly into your workflow — quiet when you need them to be, and visible when it matters most.


Credits

Concept and inspiration provided by Nicolás Pozas.


Useful References:


Thanks for reading me 😊