Documentation
¶
Overview ¶
ABOUTME: terminal-bell delivery on Unix-likes — write BEL to /dev/tty, with a ABOUTME: tmux pane-tty fallback for hook subprocesses that lack a controlling tty.
ABOUTME: Linux-specific notification handling with click-to-focus support. ABOUTME: Uses background daemon for persistent D-Bus connection when click-to-focus is enabled.
ABOUTME: Fills the zellij half of the focus hints the Linux daemon acts on. ABOUTME: Linux-only because FocusHints is the daemon's IPC payload, and the daemon is Linux-only.
Index ¶
- func EnsureClaudeNotificationsApp() error
- func FocusAppWindow(bundleID, cwd string) error
- func FocusAppWindowWithOptions(bundleID, cwd string, opts FocusWindowOptions) error
- func GetKittyWindowTarget() (windowID, listenOn string, err error)
- func GetTerminalBundleID(configOverride string) string
- func GetTerminalNotifierPath() (string, error)
- func GetTmuxPaneTarget() (string, error)
- func GetWezTermPaneTarget() (paneID, socketPath string, err error)
- func GetZellijTabTarget() (tabName, sessionName string, err error)
- func IsDaemonAvailable() bool
- func IsDisplayAsleep() bool
- func IsDoNotDisturb() bool
- func IsKitty() bool
- func IsTerminalFocused(sessionID, cwd string) bool
- func IsTerminalNotifierAvailable() bool
- func IsTmux() bool
- func IsTmuxControlMode() bool
- func IsWezTerm() bool
- func IsZellij() bool
- func MaybeCaptureGhosttyTerminalID(configOverride, sessionID, cwd string)
- func SendQuickNotification(title, message, executeCmd string) error
- func SetupPermission(ctx context.Context, root string, expected installruntime.InstalledSnapshot, ...) (string, error)
- func StartDaemon() bool
- func StopDaemon() error
- func ZellijSupportsPaneFocus() bool
- type BootClock
- type FocusWindowOptions
- type FreedesktopDelivery
- type ManagedInstallation
- type ManagedNativeProcess
- func (ManagedNativeProcess) Launch(ctx context.Context, bundle, request, receipt string) (bool, error)
- func (ManagedNativeProcess) Probe(ctx context.Context, executable string) ([]byte, error)
- func (ManagedNativeProcess) ProbePermission(ctx context.Context, executable, correlation, nonce string) ([]byte, error)
- func (ManagedNativeProcess) ProbeSetup(ctx context.Context, executable string) ([]byte, error)
- func (ManagedNativeProcess) RequestPermission(ctx context.Context, executable, correlation, nonce string) ([]byte, error)
- type NativeAttempt
- type NativeInstallation
- type NativeLease
- type NativeProcess
- type NativeSpool
- type NotificationPermissionDeniedError
- type Notifier
- type PermissionProcess
- type PrivateNativeSpool
- func (s *PrivateNativeSpool) Cleanup(ctx context.Context) error
- func (s *PrivateNativeSpool) Expire(a NativeAttempt) error
- func (s *PrivateNativeSpool) Prepare(ctx context.Context, r notification.Request, ...) (NativeAttempt, error)
- func (s *PrivateNativeSpool) Receipt(a NativeAttempt) ([]byte, error)
- type SendOption
- type StructuredDelivery
- type SystemBootClock
- type WindowsToastDelivery
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func EnsureClaudeNotificationsApp ¶
func EnsureClaudeNotificationsApp() error
EnsureClaudeNotificationsApp is a no-op on Linux.
func FocusAppWindow ¶
FocusAppWindow is not supported on non-darwin platforms.
func FocusAppWindowWithOptions ¶
func FocusAppWindowWithOptions(bundleID, cwd string, opts FocusWindowOptions) error
FocusAppWindowWithOptions is not supported on non-darwin platforms.
func GetKittyWindowTarget ¶
GetKittyWindowTarget returns the window ID and listen socket path from environment variables.
func GetTerminalBundleID ¶
GetTerminalBundleID returns empty string on Linux as terminal bundle IDs are a macOS-specific concept.
func GetTerminalNotifierPath ¶
GetTerminalNotifierPath returns an error on Linux as terminal-notifier is macOS-only.
func GetTmuxPaneTarget ¶
GetTmuxPaneTarget returns the tmux pane ID (e.g. "%42") of the pane where Claude Code is running, for use with tmux select-pane / select-window commands.
Prefers $TMUX_PANE (set by tmux per-pane at creation, always points to the process's own pane) over "tmux display-message" (which returns the currently active pane and may be wrong if the user switched tabs).
func GetWezTermPaneTarget ¶
GetWezTermPaneTarget returns the pane ID and unix socket path from environment variables. No external commands needed — the pane ID is already available in $WEZTERM_PANE.
func GetZellijTabTarget ¶
GetZellijTabTarget returns the active tab name and session name for the current zellij session.
dump-layout is answered by the running zellij server, so it can hang in a way that argument parsing cannot. It runs in the hook process, ahead of the notification the user is waiting for, so it is bounded: a session that stops answering costs the deadline and then reports a failed target, which every caller already treats as "no zellij focus" rather than "no notification".
func IsDaemonAvailable ¶
func IsDaemonAvailable() bool
IsDaemonAvailable checks if the notification daemon is available and running. Exported for testing and status checks.
func IsDisplayAsleep ¶ added in v1.45.17
func IsDisplayAsleep() bool
IsDisplayAsleep reports whether the desktop's display(s) are currently asleep, so callers can mute a sound nobody is present to hear.
Like IsDoNotDisturb, it is deliberately conservative: it returns true ONLY when it can positively confirm every active display is asleep. Any uncertainty - an unsupported platform, a failed system query, or no displays found - returns false, so the notification's sound is delivered. A wrong "asleep" result would silently swallow a cue the user is waiting for; a wrong "awake" result merely plays one sound nobody heard.
Detection is implemented on macOS (see sleep_darwin.go). Every other platform reports "not asleep".
func IsDoNotDisturb ¶ added in v1.45.17
func IsDoNotDisturb() bool
IsDoNotDisturb reports whether the desktop session is currently in a Do Not Disturb / notification-inhibited state.
Like IsTerminalFocused, it is deliberately conservative: it returns true ONLY when it can positively confirm that notifications are inhibited. Any uncertainty - an unsupported platform or desktop, a notification daemon that exposes no DND state, a D-Bus error, a timeout, or an unparseable value - returns false, so the notification and its sound are delivered.
This bias matters: a wrong "in DND" result silently swallows a cue the user is waiting for, whereas a wrong "not in DND" result merely plays one sound the user did not want. Probe failures therefore keep the existing delivery behavior. A confirmed positive can intentionally drop desktop delivery in "suppress" mode.
Detection is implemented on Linux (see dnd_linux.go). Every other platform reports "not in DND"; see docs/DO_NOT_DISTURB.md for the per-platform status.
func IsKitty ¶
func IsKitty() bool
IsKitty returns true if the current process is running inside Kitty with remote control enabled. Checks both $KITTY_WINDOW_ID (always set in Kitty) and $KITTY_LISTEN_ON (only set when remote control is configured).
func IsTerminalFocused ¶
IsTerminalFocused reports whether the terminal window running Claude Code currently has operating-system focus.
It is deliberately conservative: it returns true ONLY when it can positively confirm that the focused terminal belongs to this session. sessionID and cwd let platform implementations match exact tabs/windows when available. On any uncertainty - an OS API error, an unsupported platform or session (e.g. a Wayland compositor with no generic active-window query), or a terminal it cannot identify - it returns false so the caller still delivers the notification.
This bias matters: a wrong "focused" result silently swallows a notification the user is waiting for, whereas a wrong "unfocused" result merely shows one extra banner. The failure mode is therefore always an extra notification, never a missing one.
func IsTerminalNotifierAvailable ¶
func IsTerminalNotifierAvailable() bool
IsTerminalNotifierAvailable returns false on Linux.
func IsTmux ¶
func IsTmux() bool
IsTmux returns true if the current process is running inside a tmux session.
func IsTmuxControlMode ¶
func IsTmuxControlMode() bool
IsTmuxControlMode returns true if any tmux client attached to the current server is running in control mode (-CC), typically used by iTerm2. In control mode, standard tmux select-window doesn't cause iTerm2 to switch tabs; the iTerm2 Python API must be used instead.
Uses list-clients (not display-message) because display-message evaluates #{client_control_mode} for the temporary command-line client we spawn, which is never in control mode. list-clients enumerates all persistent (attached) clients, so we can check if ANY is in control mode.
func IsWezTerm ¶
func IsWezTerm() bool
IsWezTerm returns true if the current process is running inside WezTerm.
func IsZellij ¶
func IsZellij() bool
IsZellij returns true if the current process is running inside a zellij session.
func MaybeCaptureGhosttyTerminalID ¶
func MaybeCaptureGhosttyTerminalID(configOverride, sessionID, cwd string)
func SendQuickNotification ¶
SendQuickNotification sends a one-off notification without requiring a Notifier instance. executeCmd is the shell command run when the user clicks the notification (may be empty).
func SetupPermission ¶ added in v1.44.0
func SetupPermission(ctx context.Context, root string, expected installruntime.InstalledSnapshot, request bool) (string, error)
SetupPermission is an explicit installer action. It never changes enablement, sends a notification, repairs an installation, or retries authorization.
func StartDaemon ¶
func StartDaemon() bool
StartDaemon starts the notification daemon on-demand. Returns true if daemon started successfully or was already running.
func ZellijSupportsPaneFocus ¶ added in v1.43.1
func ZellijSupportsPaneFocus() bool
ZellijSupportsPaneFocus reports whether the installed zellij accepts focus-pane-id, which arrived in 0.44.1.
It asks zellij rather than comparing version numbers: `action <name> --help` exits zero for a subcommand that exists and non-zero for one that does not, needs no running session, and leaves the running one alone. Any non-zero status counts as unsupported, which is what carries this across zellij's clap upgrade — the exit code for an unknown subcommand changed from 1 to 2 — and what makes an absent zellij degrade to the target that has always existed rather than to no focus at all.
The probe is bounded: it runs in the hook process, ahead of the notification the user is waiting for, so a zellij that never answers must not hold that up. A timeout reads as unsupported, which lands on the tab path.
Types ¶
type BootClock ¶ added in v1.44.0
BootClock uses the same boot epoch and continuous seconds as native. Callers use Now at transport admission; Deliver never starts a new request budget.
type FocusWindowOptions ¶
type FocusWindowOptions struct {
GhosttyTerminalID string
}
type FreedesktopDelivery ¶ added in v1.44.0
type FreedesktopDelivery struct {
Clock BootClock
Open func(context.Context) (sessionNotifications, error)
}
FreedesktopDelivery is the Linux explicit-notify adapter. It makes one session-bus Notify call and never falls back after a possible D-Bus effect.
func NewFreedesktopDelivery ¶ added in v1.44.0
func NewFreedesktopDelivery(clock BootClock) *FreedesktopDelivery
func (*FreedesktopDelivery) CheckReadiness ¶ added in v1.44.0
func (d *FreedesktopDelivery) CheckReadiness(ctx context.Context, r notification.Request) notification.Readiness
func (*FreedesktopDelivery) Deliver ¶ added in v1.44.0
func (d *FreedesktopDelivery) Deliver(ctx context.Context, r notification.Request) notification.Receipt
type ManagedInstallation ¶ added in v1.44.0
type ManagedInstallation struct {
ControlRoot string
Expected installruntime.InstalledSnapshot
}
ManagedInstallation belongs to one request. Expected is the unchanged, immutable Installation from that request's ReadPolicySnapshot. Use the same adapter for readiness and delivery, never a startup-global snapshot. Acquire must run without a journal lock and never refreshes a stale snapshot. The kernel owns root, permanent lock, generation, owner and artifact checks.
func (ManagedInstallation) Acquire ¶ added in v1.44.0
func (m ManagedInstallation) Acquire(ctx context.Context) (NativeLease, error)
type ManagedNativeProcess ¶ added in v1.44.0
type ManagedNativeProcess struct{}
ManagedNativeProcess executes only the paths returned by a verified lease. It never uses PATH lookup, bundle-ID lookup, or the legacy notifier finder.
func (ManagedNativeProcess) ProbePermission ¶ added in v1.44.0
func (ManagedNativeProcess) ProbeSetup ¶ added in v1.44.0
ProbeSetup is used only after the installer has verified the managed artifact and its base capabilities. It never requests OS authorization.
func (ManagedNativeProcess) RequestPermission ¶ added in v1.44.0
func (ManagedNativeProcess) RequestPermission(ctx context.Context, executable, correlation, nonce string) ([]byte, error)
RequestPermission is an explicit installer action, never a notify fallback. The caller must hold a setup lease and first validate setup capabilities. A timeout cannot retract an OS prompt already dispatched; never auto-retry.
type NativeAttempt ¶ added in v1.44.0
type NativeAttempt struct{ Directory, RequestPath, ReceiptPath, Nonce string }
type NativeInstallation ¶ added in v1.44.0
type NativeInstallation interface {
Acquire(context.Context) (NativeLease, error)
}
NativeInstallation is a lease from the managed component owner. Verify must check its trusted offline manifest/fingerprint before returning a path; unknown helpers must never be executed to discover support. The lease pins the stable bundle against update through handoff. PR2 composition owns that lock/ledger.
type NativeLease ¶ added in v1.44.0
type NativeProcess ¶ added in v1.44.0
type NativeProcess interface {
Probe(context.Context, string) ([]byte, error)
// Launch returns whether a process might have handed work to LaunchServices.
// A started launcher is an unknown outcome until a correlated native receipt.
Launch(context.Context, string, string, string) (mayHaveHandedOff bool, err error)
}
type NativeSpool ¶ added in v1.44.0
type NativeSpool interface {
Prepare(context.Context, notification.Request, func(string) ([]byte, error)) (NativeAttempt, error)
Receipt(NativeAttempt) ([]byte, error)
// Expire removes only this owned attempt after the original boot deadline.
// It must not be called merely because the open launcher exited.
Expire(NativeAttempt) error
}
type NotificationPermissionDeniedError ¶
type NotificationPermissionDeniedError struct {
Details string
}
NotificationPermissionDeniedError indicates macOS rejected the native ClaudeNotifier path because notification permission is denied for the app.
func (*NotificationPermissionDeniedError) Error ¶
func (e *NotificationPermissionDeniedError) Error() string
type Notifier ¶
type Notifier struct {
// contains filtered or unexported fields
}
Notifier sends desktop notifications
func (*Notifier) SendDesktop ¶
func (n *Notifier) SendDesktop(status analyzer.Status, message, sessionID, cwd string, opts ...SendOption) error
SendDesktop sends a desktop notification. On macOS, it always prefers ClaudeNotifier/terminal-notifier to avoid Script Editor attribution and optionally enables click-to-focus. On Linux with clickToFocus enabled, it uses the background daemon. cwd is the working directory of the project; used for window-specific focus. May be empty.
Passing WithoutSound() delivers the banner without the plugin's own audio cue (used when the desktop is in Do Not Disturb).
type PermissionProcess ¶ added in v1.44.0
type PermissionProcess interface {
ProbePermission(context.Context, string, string, string) ([]byte, error)
}
PermissionProcess extends only the known capability mode; unsupported implementations fail closed. Acquire must verify offline before any execution.
type PrivateNativeSpool ¶ added in v1.44.0
PrivateNativeSpool is used only by notify/setup, never read-only status. Root must already be a setup-owned private directory on a local filesystem. All physical paths are derived from EvalSymlinks before creating files.
func (*PrivateNativeSpool) Cleanup ¶ added in v1.44.0
func (s *PrivateNativeSpool) Cleanup(ctx context.Context) error
Cleanup is an explicit bounded notify/setup operation. It is not a status API.
func (*PrivateNativeSpool) Expire ¶ added in v1.44.0
func (s *PrivateNativeSpool) Expire(a NativeAttempt) error
func (*PrivateNativeSpool) Prepare ¶ added in v1.44.0
func (s *PrivateNativeSpool) Prepare(ctx context.Context, r notification.Request, encode func(string) ([]byte, error)) (NativeAttempt, error)
func (*PrivateNativeSpool) Receipt ¶ added in v1.44.0
func (s *PrivateNativeSpool) Receipt(a NativeAttempt) ([]byte, error)
type SendOption ¶ added in v1.45.17
type SendOption func(*sendOptions)
SendOption customises a single desktop notification delivery.
func WithoutSound ¶ added in v1.45.17
func WithoutSound() SendOption
WithoutSound delivers the notification without playing the plugin's own audio cue. The banner is still sent, so it reaches the desktop's notification centre and is visible once the user is available again.
type StructuredDelivery ¶ added in v1.44.0
type StructuredDelivery struct {
Installation NativeInstallation
Clock BootClock
Process NativeProcess
Spool NativeSpool
}
StructuredDelivery is independent of Notifier's legacy best-effort policy. Dependencies are immutable and safe for concurrent requests. No globals, hook state, analyzer, bell, webhook or fallback are consulted by Deliver.
func NewStructuredDelivery ¶ added in v1.44.0
func NewStructuredDelivery(installation NativeInstallation, spoolRoot string) *StructuredDelivery
NewStructuredDelivery wires the real managed launcher, private spool and boot-continuous clock. Construction is read-only: setup must already have prepared spoolRoot and the trusted installation. PR4 owns policy/origin and durable admission before calling this port.
func (*StructuredDelivery) CheckReadiness ¶ added in v1.44.0
func (d *StructuredDelivery) CheckReadiness(ctx context.Context, r notification.Request) notification.Readiness
CheckReadiness releases its lease before returning; callers may then enter their journal transaction. Ready is a snapshot, never a delivery guarantee.
func (*StructuredDelivery) Deliver ¶ added in v1.44.0
func (d *StructuredDelivery) Deliver(ctx context.Context, r notification.Request) notification.Receipt
type SystemBootClock ¶ added in v1.44.0
type SystemBootClock struct{}
SystemBootClock uses the same trusted procfs boot identity and CLOCK_BOOTTIME epoch as the production journal clock, with nanosecond precision for deadlines.
type WindowsToastDelivery ¶ added in v1.44.0
type WindowsToastDelivery struct {
Clock BootClock
Open func(context.Context) (windowsToastSession, error)
}
WindowsToastDelivery is the Windows explicit-notify adapter. It shows one informational toast and never starts the click-to-focus handler or falls back to beeep after a possible toast effect.
func NewWindowsToastDelivery ¶ added in v1.44.0
func NewWindowsToastDelivery(clock BootClock) *WindowsToastDelivery
func (*WindowsToastDelivery) CheckReadiness ¶ added in v1.44.0
func (d *WindowsToastDelivery) CheckReadiness(ctx context.Context, r notification.Request) notification.Readiness
func (*WindowsToastDelivery) Deliver ¶ added in v1.44.0
func (d *WindowsToastDelivery) Deliver(ctx context.Context, r notification.Request) notification.Receipt
Source Files
¶
- ax_focus_stub.go
- bell_other.go
- delivery.go
- delivery_clock_linux.go
- delivery_files_unix.go
- delivery_installation.go
- delivery_linux.go
- delivery_process.go
- delivery_spool.go
- delivery_toast.go
- delivery_toast_other.go
- dnd.go
- dnd_linux.go
- focus.go
- focus_linux.go
- ghostty_session_stub.go
- iterm2_focus.go
- kitty.go
- multiplexer.go
- notifier.go
- options.go
- presentation.go
- readiness.go
- setup_permission.go
- setup_process.go
- sleep.go
- sleep_other.go
- terminal_linux.go
- tmux.go
- tmux_iterm2.go
- warp_focus.go
- wezterm.go
- windows_toast_xml.go
- zellij.go
- zellij_linux.go
Directories
¶
| Path | Synopsis |
|---|---|
|
Package nativeprotocol validates the versioned native helper replies.
|
Package nativeprotocol validates the versioned native helper replies. |