Notifications visuelles pour Claude Code dans Windows Terminal



Tags : #claude-code #terminal

Créé le 28 avril 2026.

Plusieurs sessions Claude Code en parallèle dans le terminal Windows, une par projet. Laquelle vient de terminer ? Laquelle attend une saisie ?

Trois signaux visuels rendent l’identification immédiate :
- une icône de cloche sur le titre de l’onglet inactif,
- un flash de la fenêtre et de la barre des tâches, et
- une bannière éphémère qui nomme le projet concerné.

Le dispositif:

Les hooks Claude Code. Deux événements — Notification (Claude attend une saisie) et Stop (Claude a fini son tour) — déclenchent chacun deux commandes. La première écrit le caractère BEL sur le terminal contrôleur ; la seconde invoque un petit script Python qui produit une toast notification Windows. Dans ~/.claude/settings.json :

{
  "hooks": {
    "Notification": [{ "matcher": "", "hooks": [
      { "type": "command",
        "command": "printf '\\007' > /dev/tty" },
      { "type": "command", "async": true,
        "command": "python3 ~/.claude/notify.py 'Waiting for input'" }
    ]}],
    "Stop": [{ "matcher": "", "hooks": [
      { "type": "command",
        "command": "printf '\\007' > /dev/tty" },
      { "type": "command", "async": true,
        "command": "python3 ~/.claude/notify.py 'Finished'" }
    ]}]
  }
}

Windows Terminal reçoit le caractère BEL et affiche les signaux visuels — à condition que son style de cloche le permette. Dans les paramètres du profil :

"bellStyle": ["visual", "window", "taskbar"]

La valeur audible est volontairement absente. La cloche devient silencieuse, ses trois compagnons visuels demeurent.

Le script Python. ~/.claude/notify.py lit le JSON transmis sur l’entrée standard, extrait le nom du dossier projet depuis cwd, et demande à PowerShell d’afficher une toast Windows. Le XML utilise scenario="urgent" avec Priority=High :

#!/usr/bin/env python3
import json
import sys
import subprocess

status = sys.argv[1] if len(sys.argv) > 1 else "Notification"

try:
    data = json.load(sys.stdin) if not sys.stdin.isatty() else {}
except (json.JSONDecodeError, ValueError):
    data = {}

cwd = (data.get("cwd") or "").rstrip("/")
project = cwd.rsplit("/", 1)[-1] if cwd else ""
title = f"Claude Code: {project}" if project else "Claude Code"


def ps_str(s: str) -> str:
    return s.replace("'", "''")


ps = (
    "& { "
    "[Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType=WindowsRuntime] > $null; "
    "$t = [Windows.UI.Notifications.ToastNotificationManager]::GetTemplateContent("
    "[Windows.UI.Notifications.ToastTemplateType]::ToastText02); "
    "$r = $t.GetElementsByTagName('toast').Item(0); "
    "$r.SetAttribute('scenario','urgent'); "
    "$audio = $t.CreateElement('audio'); "
    "$audio.SetAttribute('silent','true'); "
    "$r.AppendChild($audio) > $null; "
    "$n = $t.GetElementsByTagName('text'); "
    f"$n.Item(0).AppendChild($t.CreateTextNode('{ps_str(title)}')) > $null; "
    f"$n.Item(1).AppendChild($t.CreateTextNode('{ps_str(status)}')) > $null; "
    "$x = [Windows.UI.Notifications.ToastNotification]::new($t); "
    "$x.Priority = [Windows.UI.Notifications.ToastNotificationPriority]::High; "
    "[Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier('ClaudeCode').Show($x) "
    "}"
)

subprocess.run(
    ["powershell.exe", "-NoProfile", "-Command", ps],
    stdout=subprocess.DEVNULL,
    stderr=subprocess.DEVNULL,
)

La clé de registre. Un AppUserModelID enregistré est également requis, sans quoi Windows refuse d’associer la toast à une entrée par application dans Paramètres > Notifications. Deux lignes reg add depuis une invite cmd suffisent :

reg add "HKCU\Software\Classes\AppUserModelId\ClaudeCode" /v DisplayName    /t REG_SZ    /d "Claude Code" /f
reg add "HKCU\Software\Classes\AppUserModelId\ClaudeCode" /v ShowInSettings /t REG_DWORD /d 1             /f

Le nom de la clé (ClaudeCode) doit correspondre à la chaîne passée à CreateToastNotifier dans le script.