Complex tasks in Claude Code can take a while. Run a few in parallel across different repos and it gets hard to track which ones are done and which ones need input.
Claude Code supports hooks - shell commands that run in response to events. Hook into the Stop and Notification events to send a Slack message when a task completes or when Claude needs attention.
The notification script
Using PEP 723 inline metadata to keep everything in one file:
# /// script
# requires-python = ">=3.10"
# dependencies = [
# "httpx",
# ]
# ///
import json
import os
import sys
SLACK_WEBHOOK_URL = os.environ.get("SLACK_WEBHOOK_URL")
def send_slack_message(text: str) -> None:
if not SLACK_WEBHOOK_URL:
print("SLACK_WEBHOOK_URL not set", file=sys.stderr)
return
import httpx
httpx.post(SLACK_WEBHOOK_URL, json={"text": text})
def main():
hook_data = json.load(sys.stdin)
event = hook_data.get("event")
cwd = hook_data.get("cwd", "unknown")
project = cwd.split("/")[-1]
if event == "stop":
send_slack_message(f":white_check_mark: Claude Code finished in `{project}`")
elif event == "notification":
message = hook_data.get("notification", {}).get("message", "")
send_slack_message(f":bell: Claude Code needs attention in `{project}`: {message}")
if __name__ == "__main__":
main()
The script reads hook data from stdin (JSON with event type, working directory, etc.), formats a message with the project name, and posts to Slack.
Setting up the webhook
- Go to Slack API Apps and create a new app
- Enable “Incoming Webhooks”
- Add a webhook to your workspace and choose a channel
- Copy the webhook URL
Set it as an environment variable:
export SLACK_WEBHOOK_URL="https://hooks.slack.com/services/..."
Configuring Claude Code hooks
Add this to ~/.claude/settings.json:
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
"uv run /path/to/claude-code-slack-notify.py"
]
}
],
"Stop": [
{
"matcher": "",
"hooks": [
"uv run /path/to/claude-code-slack-notify.py"
]
}
]
}
}
Replace /path/to/claude-code-slack-notify.py with the actual path.
Now when Claude Code finishes a task or needs input, you get a Slack message with the project name. Useful when running tasks across multiple repos or when you’re away from your terminal.
The full gist is here.