Notifications automatiques avec les Hooks : soyez prévenu dès que Claude a terminé

Configurez un Stop Hook pour que Claude vous prévienne automatiquement par notification bureau, Slack ou Telegram


Les tâches Claude Code prennent souvent plusieurs minutes, voire plus. Vous lui demandez de refactoriser un module, de lancer la suite de tests ou de traiter un lot de fichiers — puis vous passez à autre chose. Le problème : impossible de savoir quand il a fini.

Quand vous revenez, peut-être qu'il a terminé depuis dix minutes. Ou peut-être qu'il attend votre confirmation sur une boîte de dialogue.

Avec le Hook Stop, vous recevez automatiquement une notification dès que Claude termine une tâche ou a besoin de votre intervention. Peu importe la fenêtre ou l'appareil sur lequel vous êtes : la notification arrive directement.


Le mécanisme

Claude Code dispose d'un événement Stop qui se déclenche à chaque fois que Claude s'arrête, que ce soit parce qu'il a terminé la tâche, rencontré une erreur nécessitant une confirmation, ou qu'il attend une saisie utilisateur.

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

Le hook reçoit sur stdin un JSON contenant stop_reason (la raison de l'arrêt) et stop_message (le dernier message de Claude). Ces informations permettent de personnaliser le contenu de la notification.

Structure du JSON en entrée :

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

Option 1 : Notification système (la plus simple)

Sous Linux, on utilise notify-send ; sous macOS, osascript. Aucun service externe nécessaire :

#!/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

Avantage : aucune configuration, ça marche tout de suite.
Inconvénient : la notification n'arrive que sur la machine locale. Si vous changez d'appareil, vous ne la verrez pas.


Option 2 : Webhook Slack (recommandé en équipe)

Utilisez un Incoming Webhook Slack pour envoyer les notifications dans un canal ou en message privé.

Prérequis :
1. Créer une App sur la page de gestion Slack
2. Activer les Incoming Webhooks et récupérer l'URL du webhook
3. Stocker l'URL dans la variable d'environnement SLACK_WEBHOOK_URL

#!/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 &

Le & à la fin est essentiel : il lance curl en arrière-plan pour ne pas bloquer Claude.


Option 3 : Bot Telegram

Un Bot Telegram est idéal pour un usage personnel : gratuit, instantané, et les notifications arrivent directement sur votre téléphone.

Prérequis :
1. Créer un Bot via @BotFather et récupérer le token
2. Obtenir votre Chat ID (envoyez un message au Bot puis appelez l'API getUpdates)
3. Définir les variables d'environnement TELEGRAM_BOT_TOKEN et TELEGRAM_CHAT_ID

#!/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 &

Option 4 : Alerte sonore (pratique aussi en SSH)

Si vous êtes sur la même machine mais avez changé de fenêtre, un signal sonore est souvent plus efficace qu'un 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

Vous pouvez combiner cette option avec les autres : son local + notification à distance.


Configuration complète : combiner plusieurs méthodes

En pratique, le mieux est de regrouper plusieurs méthodes dans un seul script :

.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"
          }
        ]
      }
    ]
  }
}

Points d'attention

Toutes les requêtes réseau doivent tourner en arrière-plan

Les curl dans le script de notification doivent impérativement être suivis de &. Si le réseau est lent ou qu'un timeout se produit, toute la session Claude sera bloquée. Ajouter > /dev/null 2>&1 & est la pratique standard.

Stop ne veut pas dire « terminé avec succès »

Un stop_reason à end_turn indique généralement que la tâche est terminée, mais il peut aussi signifier que Claude ne sait pas quoi faire ensuite. Si vous ne voulez être notifié que lorsque la tâche est réellement terminée, filtrez dans le script :

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

Tronquer le contenu de la notification

Le dernier message de Claude peut être très long (blocs de code, sorties volumineuses). Avant de l'envoyer sur Slack ou Telegram, utilisez head -c 300 pour le tronquer et éviter les messages à rallonge.

Les variables d'environnement doivent rester dans la configuration globale

Les URLs de webhooks et les tokens de bots n'ont rien à faire dans le dépôt du projet. Placez-les dans ~/.zshrc (ou ~/.bashrc), ou gérez-les avec direnv.


Le résultat

Une fois la configuration en place, votre façon de travailler change :

  1. Vous confiez une tâche à Claude
  2. Vous passez à une autre fenêtre ou un autre appareil
  3. Claude termine → la notification arrive
  4. Vous revenez consulter le résultat et passez à la suite

Plus besoin de surveiller le terminal ni de jongler entre les fenêtres pour vérifier l'avancement. Claude bosse de son côté, vous du vôtre, et il vous prévient quand c'est fait.