[FEATURE] Per-tab focus-aware notifications — hook payload lacks terminal/tab identifier

Resolved 💬 1 comment Opened May 9, 2026 by g-i-o-r-g-i-o Closed Jun 7, 2026

Problem

When Claude Code runs in a terminal with multiple tabs (drop-down terminals like ddterm, gnome-terminal, kitty tabs, Windows Terminal, tmux, etc.), the Notification and Stop hooks have no way to determine whether the current Claude session is in the foreground tab.

Use case

I want a desktop popup (notify-send, osascript, BurntToast) to fire only when the specific tab running this Claude session is not in foreground. If I'm staring at it, the popup is noise; if I'm on another tab (even of the same terminal app), I want the popup.

This is what terminals supporting OSC 9 / OSC 99 (Ghostty, kitty, iTerm2) provide automatically — the terminal itself delivers the notification and is focus-aware. Everything VTE-based (gnome-terminal, ddterm, Tilix) and Windows Terminal is stuck with the terminal_bell ASCII beep.

Why building it via the Notification hook is currently impossible

  1. Hook payload contains session_id but no information that ties the hook to a specific OS-level window/tab.
  2. Hooks run with PPID=1 and no controlling TTY, so ps -o tty= doesn't help (see #30910).
  3. The session title that Claude Code already writes to the terminal tab via OSC 0/2 is not in the payload (see #15461, #16917, #19083).
  4. Querying the OS only tells you "is the terminal app focused", not "is this PTY the active tab". On a drop-down terminal with N tabs, the window is focused but you can't tell which tab is shown.

Workaround: a 3-part hack involving CLAUDE_TTY_MARKER env vars, ps -Eww scans, and writing escape sequences to /dev/$TTY (described in #30910). It works but is fragile and per-user.

And even without multi-tab, notifications are a mess

Set aside the multi-tab case. Even with a single Claude Code session in a single terminal window, getting a usable focus-aware desktop notification requires:

  • A terminal that implements OSC 9 / OSC 99 (a small subset: kitty, iTerm2, Ghostty, Windows Terminal partial). Otherwise:
  • A custom Notification hook that calls notify-send / osascript / BurntToast, plus a way to detect "is the terminal currently focused?" — which is entirely platform-specific:
  • Linux X11: xdotool getactivewindow works.
  • Linux Wayland: nothing native works. You need to install a GNOME Shell extension (focused-window-dbus@flexagoon.com, window-calls@domandoman.xyz, …) just to query the active window via D-Bus, because org.gnome.Shell.Eval is gated and the X11 tools fail under Wayland without XWayland window listing. wmctrl -l returns empty.
  • macOS: osascript -e 'tell application "System Events" to ...'.
  • Windows: PowerShell + WMI, or AutoHotkey, or BurntToast with custom Window-ID matching.

So the user has to write a per-OS, per-terminal, per-window-manager focus-detection routine just to get the equivalent of what kitty / Ghostty give for free via OSC 99.

The setting preferredNotifChannel only covers a small set of terminals (iterm2, iterm2_with_bell, terminal_bell, kitty, ghostty, notifications_disabled). Anything else falls back to terminal_bell (a beep, not a desktop popup) and there is no built-in cross-terminal "send desktop notification" path. A preferredNotifChannel: desktop (cross-platform, focus-aware using what the WM/OS exposes) would close most of this gap.

Proposed Solution

Two things would go a long way:

  1. Include in the hook JSON payload any one of:
  • session_title — the auto-generated topic Claude Code already sets as the terminal tab title (cheap; matches _NET_WM_NAME / D-Bus active-window queries).
  • parent_tty / terminal_pid — the TTY or PID of the originating foreground process before the orphaning, so hooks can correlate to OS-level "which PTY is the foreground job of the focused terminal".
  • A stable terminal_window_id recorded at session attach.
  1. A built-in cross-terminal desktop notification channel that handles the OS-level focus detection internally, instead of leaving every user to reimplement it.

Related closed issues (all autoclosed for inactivity, never implemented)

  • #15461 — Expose Session Name to All Extension Points
  • #16917 — Include session title in hook input JSON (closed as duplicate of #15461)
  • #19083 — Add terminal_title to hook JSON payload for session identification (closed as duplicate)
  • #30910 — Expose parent TTY path to hooks via environment variable
  • #15029, #15267 — related (session name in statusLine)
  • #7460 — Add inactivity-based notification delay
  • #104 — More configurable preferredNotifChannel (run an arbitrary script)

This has been requested repeatedly for almost a year. Tracking it under one open issue would help.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗