Automatische Benachrichtigungen mit Hooks: Claude meldet sich, wenn er fertig ist

Richte einen Stop Hook ein, damit Claude dich automatisch per Desktop-Benachrichtigung, Slack oder Telegram informiert


Aufgaben in Claude Code dauern oft mehrere Minuten oder länger. Du lässt ihn ein Modul refactoren, die Test-Suite durchlaufen oder einen Stapel Dateien verarbeiten — und wechselst in der Zwischenzeit zu etwas anderem. Das Problem: Du weißt nicht, wann er fertig ist.

Wenn du zurückschaust, ist er vielleicht schon seit zehn Minuten fertig. Oder er hängt an einem Bestätigungsdialog und wartet auf dich.

Mit dem Stop-Hook bekommst du automatisch eine Benachrichtigung, sobald Claude eine Aufgabe abgeschlossen hat oder dein Eingreifen braucht. Egal in welchem Fenster oder auf welchem Gerät du gerade bist — die Nachricht kommt direkt durch.


So funktioniert's

Claude Code hat ein Stop-Event, das jedes Mal ausgelöst wird, wenn Claude anhält — sei es, weil die Aufgabe erledigt ist, ein Fehler aufgetreten ist, der bestätigt werden muss, oder weil er auf eine Eingabe wartet.

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/notify.sh"
          }
        ]
      }
    ]
  }
}

Der Hook erhält über stdin ein JSON mit stop_reason (Grund des Stopps) und stop_message (Claudes letzte Nachricht). Damit lässt sich der Inhalt der Benachrichtigung anpassen.

Struktur des JSON auf stdin:

{
  "session_id": "xxx",
  "stop_reason": "end_turn",
  "stop_message": "重构完成,所有测试通过。"
}

Variante 1: Desktop-Benachrichtigung (am einfachsten)

Unter Linux mit notify-send, unter macOS mit osascript. Kein externer Dienst nötig:

#!/bin/bash
input=$(cat)
reason=$(echo "$input" | jq -r '.stop_reason // "unknown"')
message=$(echo "$input" | jq -r '.stop_message // "Claude 已停止"' | head -c 200)

case "$(uname)" in
  Linux)
    notify-send "Claude Code" "$message" --urgency=normal
    ;;
  Darwin)
    osascript -e "display notification \"$message\" with title \"Claude Code\""
    ;;
esac

Vorteil: Null Konfiguration, funktioniert sofort.
Nachteil: Die Benachrichtigung kommt nur auf dem lokalen Rechner an. Auf einem anderen Gerät bekommst du nichts mit.


Variante 2: Slack-Webhook (empfohlen für Teams)

Mit einem Slack Incoming Webhook lassen sich Benachrichtigungen in einen Kanal oder als Direktnachricht senden.

Voraussetzungen:
1. Eine App auf der Slack-Verwaltungsseite erstellen
2. Incoming Webhooks aktivieren und die Webhook-URL kopieren
3. Die URL in der Umgebungsvariable SLACK_WEBHOOK_URL speichern

#!/bin/bash
input=$(cat)
reason=$(echo "$input" | jq -r '.stop_reason // "unknown"')
message=$(echo "$input" | jq -r '.stop_message // "Claude 已停止"' | head -c 300)

webhook_url="${SLACK_WEBHOOK_URL}"
[[ -z "$webhook_url" ]] && exit 0

# 根据停止原因选 emoji
case "$reason" in
  end_turn)    emoji="✅" ;;
  user_input)  emoji="⏳" ;;
  *)           emoji="🔔" ;;
esac

payload=$(jq -n \
  --arg text "$emoji *Claude Code* | \`$reason\`
$message" \
  '{text: $text}')

curl -s -X POST "$webhook_url" \
  -H 'Content-Type: application/json' \
  -d "$payload" > /dev/null 2>&1 &

Das & am Ende ist wichtig — es lässt curl im Hintergrund laufen, damit Claude nicht blockiert wird.


Variante 3: Telegram-Bot

Ein Telegram-Bot eignet sich perfekt für den persönlichen Gebrauch: kostenlos, sofort, und die Nachricht kommt direkt aufs Handy.

Voraussetzungen:
1. Einen Bot über @BotFather erstellen und den Token kopieren
2. Die eigene Chat-ID ermitteln (dem Bot eine Nachricht schicken und die getUpdates-API aufrufen)
3. Die Umgebungsvariablen TELEGRAM_BOT_TOKEN und TELEGRAM_CHAT_ID setzen

#!/bin/bash
input=$(cat)
reason=$(echo "$input" | jq -r '.stop_reason // "unknown"')
message=$(echo "$input" | jq -r '.stop_message // "Claude 已停止"' | head -c 300)

token="${TELEGRAM_BOT_TOKEN}"
chat_id="${TELEGRAM_CHAT_ID}"
[[ -z "$token" || -z "$chat_id" ]] && exit 0

text="🤖 *Claude Code*
状态:\`$reason\`
$message"

curl -s "https://api.telegram.org/bot${token}/sendMessage" \
  -d chat_id="$chat_id" \
  -d text="$text" \
  -d parse_mode="Markdown" > /dev/null 2>&1 &

Variante 4: Akustisches Signal (auch über SSH praktisch)

Wenn du auf demselben Rechner bist, aber das Fenster gewechselt hast, ist ein Ton oft wirkungsvoller als ein Popup:

#!/bin/bash
input=$(cat)
reason=$(echo "$input" | jq -r '.stop_reason // "unknown"')

case "$(uname)" in
  Linux)
    paplay /usr/share/sounds/freedesktop/stereo/complete.oga 2>/dev/null &
    ;;
  Darwin)
    afplay /System/Library/Sounds/Glass.aiff &
    ;;
esac

Lässt sich gut mit den anderen Varianten kombinieren — lokal ein Ton, remote eine Push-Benachrichtigung.


Komplette Konfiguration: mehrere Benachrichtigungswege kombinieren

In der Praxis empfiehlt es sich, mehrere Methoden in einem einzigen Skript zu bündeln:

.claude/hooks/notify.sh:

#!/bin/bash
input=$(cat)
reason=$(echo "$input" | jq -r '.stop_reason // "unknown"')
message=$(echo "$input" | jq -r '.stop_message // "Claude 已停止"' | head -c 300)

# 根据原因选 emoji
case "$reason" in
  end_turn)    emoji="✅"; title="任务完成" ;;
  user_input)  emoji="⏳"; title="等待输入" ;;
  *)           emoji="🔔"; title="Claude 已停止" ;;
esac

# 1. 桌面通知(本地)
case "$(uname)" in
  Linux)  notify-send "Claude Code: $title" "$message" --urgency=normal ;;
  Darwin) osascript -e "display notification \"$message\" with title \"Claude Code: $title\"" ;;
esac

# 2. 声音提示
case "$(uname)" in
  Linux)  paplay /usr/share/sounds/freedesktop/stereo/complete.oga 2>/dev/null & ;;
  Darwin) afplay /System/Library/Sounds/Glass.aiff & ;;
esac

# 3. Slack(如果配了 Webhook)
if [[ -n "$SLACK_WEBHOOK_URL" ]]; then
  payload=$(jq -n --arg text "$emoji *$title*
$message" '{text: $text}')
  curl -s -X POST "$SLACK_WEBHOOK_URL" \
    -H 'Content-Type: application/json' \
    -d "$payload" > /dev/null 2>&1 &
fi

# 4. Telegram(如果配了 Bot)
if [[ -n "$TELEGRAM_BOT_TOKEN" && -n "$TELEGRAM_CHAT_ID" ]]; then
  curl -s "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
    -d chat_id="$TELEGRAM_CHAT_ID" \
    -d text="$emoji *$title*
$message" \
    -d parse_mode="Markdown" > /dev/null 2>&1 &
fi

.claude/settings.json:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/notify.sh"
          }
        ]
      }
    ]
  }
}

Worauf du achten solltest

Alle Netzwerkanfragen im Hintergrund ausführen

Die curl-Aufrufe im Benachrichtigungsskript müssen immer mit & in den Hintergrund geschickt werden. Wenn das Netzwerk langsam ist oder ein Timeout auftritt, blockiert das die gesamte Claude-Session. > /dev/null 2>&1 & ist die Standardpraxis.

Stop heißt nicht „erfolgreich abgeschlossen"

Ein stop_reason von end_turn bedeutet in der Regel, dass die Aufgabe erledigt ist — kann aber auch heißen, dass Claude nicht weiß, was als Nächstes zu tun ist. Wenn du nur bei tatsächlichem Abschluss benachrichtigt werden willst, filtere im Skript:

[[ "$reason" != "end_turn" ]] && exit 0

Benachrichtigungsinhalt kürzen

Claudes letzte Nachricht kann sehr lang sein (Codeblöcke, umfangreiche Ausgaben). Bevor du sie an Slack oder Telegram schickst, kürze sie mit head -c 300, um riesige Nachrichten zu vermeiden.

Umgebungsvariablen gehören in die globale Konfiguration

Webhook-URLs und Bot-Tokens haben im Projekt-Repository nichts verloren. Am besten in ~/.zshrc (oder ~/.bashrc) ablegen oder mit direnv verwalten.


Das Ergebnis

Nach der Einrichtung ändert sich dein Arbeitsablauf:

  1. Du gibst Claude eine Aufgabe
  2. Du wechselst zu einem anderen Fenster oder Gerät
  3. Claude wird fertig → die Benachrichtigung kommt
  4. Du schaust dir das Ergebnis an und machst weiter

Kein Starren aufs Terminal, kein ständiges Hin- und Herwechseln, um den Fortschritt zu prüfen. Claude arbeitet, du arbeitest — und wenn er fertig ist, sagt er Bescheid.