Notifications visuelles pour Claude Code dans Windows Terminal
Tags : #claude-code #terminal
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.