Notifikasi Otomatis Saat Claude Selesai Bekerja dengan Hooks

Konfigurasi Stop Hook agar Claude otomatis mengirim notifikasi via desktop, Slack, atau Telegram saat selesai bekerja


Claude Code sering butuh waktu beberapa menit atau bahkan lebih lama untuk menyelesaikan tugas. Kamu minta dia refactor sebuah modul, menjalankan test suite, atau memproses sekumpulan file — lalu pindah ke jendela lain untuk mengerjakan hal lain. Masalahnya: kamu tidak tahu kapan dia selesai.

Begitu kamu cek lagi, mungkin sudah selesai sepuluh menit yang lalu, atau malah sedang menunggu konfirmasi darimu.

Dengan Stop Hook, kamu bisa dapat notifikasi otomatis setiap kali Claude selesai mengerjakan tugas (atau butuh intervensimu). Entah kamu ada di jendela mana atau perangkat mana, notifikasi langsung sampai.


Mekanisme Dasar

Claude Code punya event Stop — terpicu setiap kali Claude berhenti, entah karena tugas selesai, ada error yang perlu konfirmasi, atau menunggu input dari pengguna.

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

Hook menerima JSON melalui stdin yang berisi stop_reason (alasan berhenti) dan stop_message (pesan terakhir Claude). Berdasarkan informasi ini, kamu bisa menyesuaikan isi notifikasi.

Struktur data stdin:

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

Opsi 1: Notifikasi Desktop Sistem (Paling Simpel)

Linux pakai notify-send, macOS pakai osascript, tanpa layanan eksternal apa pun:

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

Kelebihan: Tanpa konfigurasi, langsung jalan.
Kekurangan: Hanya bisa diterima di mesin yang sama — kalau pindah perangkat, tidak akan terlihat.


Opsi 2: Slack Webhook (Direkomendasikan untuk Tim)

Gunakan Slack Incoming Webhook untuk mengirim notifikasi ke channel atau DM tertentu.

Persiapan:
1. Buat App di halaman manajemen App Slack
2. Aktifkan Incoming Webhooks dan dapatkan Webhook URL
3. Simpan URL tersebut di environment variable 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

# Pilih emoji berdasarkan alasan berhenti
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 &

Tanda & di akhir itu penting — agar curl berjalan di background dan tidak memblokir Claude.


Opsi 3: Telegram Bot

Telegram Bot cocok untuk penggunaan pribadi — gratis, instan, dan notifikasinya langsung masuk ke HP.

Persiapan:
1. Buat Bot melalui @BotFather dan dapatkan Token
2. Dapatkan Chat ID-mu (kirim pesan ke Bot, lalu panggil API getUpdates)
3. Set environment variable TELEGRAM_BOT_TOKEN dan 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 &

Opsi 4: Notifikasi Suara (Berguna Juga untuk SSH)

Kalau kamu masih di mesin yang sama tapi pindah jendela, suara notifikasi bisa lebih efektif dari 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

Bisa dikombinasikan dengan opsi lain — putar suara di lokal + kirim notifikasi ke perangkat lain.


Konfigurasi Lengkap: Kombinasi Beberapa Cara Notifikasi

Untuk penggunaan sehari-hari, disarankan menggabungkan beberapa cara dalam satu 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)

# Pilih emoji berdasarkan alasan
case "$reason" in
  end_turn)    emoji="✅"; title="任务完成" ;;
  user_input)  emoji="⏳"; title="等待输入" ;;
  *)           emoji="🔔"; title="Claude 已停止" ;;
esac

# 1. Notifikasi desktop (lokal)
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. Notifikasi suara
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 (jika Webhook sudah dikonfigurasi)
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 (jika Bot sudah dikonfigurasi)
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"
          }
        ]
      }
    ]
  }
}

Beberapa Hal yang Perlu Diperhatikan

Semua request jaringan harus dijalankan di background

Setiap curl di script notifikasi wajib ditambahkan & agar berjalan di background. Kalau jaringan lambat atau timeout, bisa memblokir seluruh sesi Claude. Menambahkan > /dev/null 2>&1 & adalah praktik standar.

Stop bukan berarti "berhasil selesai"

stop_reason bernilai end_turn biasanya berarti tugas selesai, tapi bisa juga Claude sedang tidak yakin apa langkah selanjutnya. Kalau kamu hanya ingin dapat notifikasi saat benar-benar selesai, tambahkan filter di script:

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

Potong isi notifikasi

Pesan terakhir Claude bisa sangat panjang (berisi blok kode, output panjang). Sebelum dikirim ke Slack atau Telegram, potong dengan head -c 300 agar notifikasi tidak meledak.

Simpan environment variable di konfigurasi global

Webhook URL dan Bot Token jangan disimpan di repository proyek. Lebih baik taruh di ~/.zshrc (atau ~/.bashrc), atau kelola dengan direnv.


Hasilnya

Setelah dikonfigurasi, cara kerjamu berubah:

  1. Beri Claude sebuah tugas
  2. Pindah ke jendela/perangkat lain
  3. Claude selesai → notifikasi langsung masuk
  4. Kembali, lihat hasilnya, lanjut ke langkah berikutnya

Tidak perlu lagi memelototi terminal, tidak perlu bolak-balik cek progress. Claude bekerja, kamu juga bekerja — selesai, dia kasih tahu.