Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions plugins/warp/scripts/legacy/warp-notify.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@
# Warp notification utility using OSC escape sequences
# Usage: warp-notify.sh <title> <body>

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/../resolve-tty.sh"

TITLE="${1:-Notification}"
BODY="${2:-}"

# OSC 777 format: \033]777;notify;<title>;<body>\007
# Write directly to /dev/tty to ensure it reaches the terminal
printf '\033]777;notify;%s;%s\007' "$TITLE" "$BODY" > /dev/tty 2>/dev/null || true
# Hook processes have no controlling terminal, so resolve the real tty of an
# ancestor process rather than relying on /dev/tty (which fails in that case).
printf '\033]777;notify;%s;%s\007' "$TITLE" "$BODY" > "$(resolve_tty_device)" 2>/dev/null || true
36 changes: 36 additions & 0 deletions plugins/warp/scripts/resolve-tty.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/bin/bash
# Resolve the terminal device to write OSC escape sequences to.
#
# Claude Code spawns hook processes WITHOUT a controlling terminal, so the
# usual `/dev/tty` is unavailable ("Device not configured" / ENXIO) and any
# notification written there is silently dropped. Walk up the process tree
# to find an ancestor (the `claude` process or its parent shell) that still
# has a controlling tty, and return that device node instead.
#
# Falls back to `/dev/tty` when no ancestor with a tty is found, so callers
# behave exactly as before in environments where `/dev/tty` already works.
#
# Usage:
# source "$SCRIPT_DIR/resolve-tty.sh"
# printf '...' > "$(resolve_tty_device)"

resolve_tty_device() {
local pid=$PPID depth=0 tty_name
while [ -n "$pid" ] && [ "$pid" -gt 1 ] && [ "$depth" -lt 25 ]; do
# `ps -o tty=` prints e.g. `ttys003` (macOS) or `pts/3` (Linux),
# and `?` / `??` for a process with no controlling terminal.
tty_name=$(ps -o tty= -p "$pid" 2>/dev/null | tr -d '[:space:]')
case "$tty_name" in
'' | '?' | '??')
: # this ancestor has no controlling tty — keep walking up
;;
*)
printf '/dev/%s\n' "$tty_name"
return 0
;;
esac
pid=$(ps -o ppid= -p "$pid" 2>/dev/null | tr -d '[:space:]')
depth=$((depth + 1))
done
printf '/dev/tty\n'
}
6 changes: 4 additions & 2 deletions plugins/warp/scripts/warp-notify.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/should-use-structured.sh"
source "$SCRIPT_DIR/resolve-tty.sh"

# Only emit notifications when we've confirmed the Warp build can render them.
if ! should_use_structured; then
Expand All @@ -17,5 +18,6 @@ TITLE="${1:-Notification}"
BODY="${2:-}"

# OSC 777 format: \033]777;notify;<title>;<body>\007
# Write directly to /dev/tty to ensure it reaches the terminal
printf '\033]777;notify;%s;%s\007' "$TITLE" "$BODY" > /dev/tty 2>/dev/null || true
# Hook processes have no controlling terminal, so resolve the real tty of an
# ancestor process rather than relying on /dev/tty (which fails in that case).
printf '\033]777;notify;%s;%s\007' "$TITLE" "$BODY" > "$(resolve_tty_device)" 2>/dev/null || true