From 06dea5e86349182cf7e195d4eae754c59ce8cab7 Mon Sep 17 00:00:00 2001 From: David Chen Date: Thu, 25 Sep 2025 15:42:20 -0700 Subject: [PATCH] update agent notifier --- .gitignore | 1 + agent-tracker/scripts/focus_last_origin.sh | 18 ++++++ .../scripts/focus_latest_notified.sh | 35 +++++++++++ codex/notify.py | 60 +++++++++++++++++++ 4 files changed, 114 insertions(+) create mode 100644 agent-tracker/scripts/focus_last_origin.sh create mode 100644 agent-tracker/scripts/focus_latest_notified.sh diff --git a/.gitignore b/.gitignore index d8e9f5e..f771e4e 100755 --- a/.gitignore +++ b/.gitignore @@ -87,3 +87,4 @@ agent-tracker/bin/tracker-server agent-tracker/run/server.pid tmux/cache agent-tracker/.DS_Store +agent-tracker/run diff --git a/agent-tracker/scripts/focus_last_origin.sh b/agent-tracker/scripts/focus_last_origin.sh new file mode 100644 index 0000000..bd959a8 --- /dev/null +++ b/agent-tracker/scripts/focus_last_origin.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +set -euo pipefail + +F="$HOME/.config/agent-tracker/run/jump_back.txt" +if [[ ! -f "$F" ]]; then + exit 0 +fi + +sid=$(awk -F ':::' 'NR==1{print $1}' "$F" | tr -d '\r\n') +wid=$(awk -F ':::' 'NR==1{print $2}' "$F" | tr -d '\r\n') +pid=$(awk -F ':::' 'NR==1{print $3}' "$F" | tr -d '\r\n') + +if [[ -z "${sid:-}" || -z "${wid:-}" || -z "${pid:-}" ]]; then + exit 0 +fi + +tmux switch-client -t "$sid" \; select-window -t "$wid" \; select-pane -t "$pid" + diff --git a/agent-tracker/scripts/focus_latest_notified.sh b/agent-tracker/scripts/focus_latest_notified.sh new file mode 100644 index 0000000..14a7ad5 --- /dev/null +++ b/agent-tracker/scripts/focus_latest_notified.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +set -euo pipefail + +F="$HOME/.config/agent-tracker/run/latest_notified.txt" +if [[ ! -f "$F" ]]; then + exit -1 +fi + +# Read line and split by literal ':::' into sid, wid, pid robustly +# Extract fields robustly using awk with a literal ':::' separator +sid=$(awk -F ':::' 'NR==1{print $1}' "$F" | tr -d '\r\n') +wid=$(awk -F ':::' 'NR==1{print $2}' "$F" | tr -d '\r\n') +pid=$(awk -F ':::' 'NR==1{print $3}' "$F" | tr -d '\r\n') + +if [[ -z "${sid:-}" || -z "${wid:-}" || -z "${pid:-}" ]]; then + exit 0 +fi + +RUN_DIR="$HOME/.config/agent-tracker/run" +mkdir -p "$RUN_DIR" + +# Record current location for jump-back +current=$(tmux display-message -p "#{session_id}:::#{window_id}:::#{pane_id}" | tr -d '\r\n') +if [[ -n "$current" ]]; then + printf '%s\n' "$current" > "$RUN_DIR/jump_back.txt" +fi + +# Mark as viewed (acknowledged) in tracker +CLIENT_BIN="$HOME/.config/agent-tracker/bin/tracker-client" +if [[ -x "$CLIENT_BIN" ]]; then + "$CLIENT_BIN" command -session-id "$sid" -window-id "$wid" -pane "$pid" acknowledge >/dev/null 2>&1 || true +fi + +# Focus the tmux target +tmux switch-client -t "$sid" \; select-window -t "$wid" \; select-pane -t "$pid" diff --git a/codex/notify.py b/codex/notify.py index a57bdd9..1f14005 100755 --- a/codex/notify.py +++ b/codex/notify.py @@ -2,6 +2,7 @@ import json import os +import re import shlex import shutil import subprocess @@ -35,6 +36,7 @@ def main() -> int: # Prefer TMUX_PANE from env; fall back to parent process TTY. tmux_path = shutil.which("tmux") or "/opt/homebrew/bin/tmux" tmux_ids = None + title_names = None # (session_name, window_name) if os.path.exists(tmux_path): pane = os.environ.get("TMUX_PANE", "").strip() try: @@ -53,6 +55,24 @@ def main() -> int: parts = out.split(":::") if len(parts) == 3: tmux_ids = tuple(parts) + # Also fetch human-readable names for the banner title + try: + name_out = subprocess.check_output( + [ + tmux_path, + "display-message", + "-p", + "-t", + pane, + "#{session_name}:::#{window_name}", + ], + text=True, + ).strip() + name_parts = name_out.split(":::") + if len(name_parts) == 2: + title_names = (name_parts[0].strip(), name_parts[1].strip()) + except Exception: + pass except Exception: tmux_ids = None @@ -81,9 +101,47 @@ def main() -> int: parts = out.split(":::") if len(parts) == 3: tmux_ids = tuple(parts) + try: + name_out = subprocess.check_output( + [ + tmux_path, + "display-message", + "-p", + "-c", + f"/dev/{tty}", + "#{session_name}:::#{window_name}", + ], + text=True, + ).strip() + name_parts = name_out.split(":::") + if len(name_parts) == 2: + title_names = (name_parts[0].strip(), name_parts[1].strip()) + except Exception: + pass except Exception: tmux_ids = None + # Prefer title = " - " if available + if title_names is not None: + sn, wn = title_names + sn = re.sub(r'^\d+\s*-\s*', '', sn).strip() + combined = " - ".join([p for p in (sn, wn) if p]) + if combined: + title = combined + + # Persist latest target so tmux hotkey can jump to it later + if tmux_ids is not None: + try: + run_dir = os.path.join(os.path.expanduser("~"), ".config", "agent-tracker", "run") + os.makedirs(run_dir, exist_ok=True) + tmp_path = os.path.join(run_dir, ".latest_notified.tmp") + final_path = os.path.join(run_dir, "latest_notified.txt") + with open(tmp_path, "w", encoding="utf-8") as f: + f.write("%s:::%s:::%s\n" % tmux_ids) + os.replace(tmp_path, final_path) + except Exception: + pass + args = [ "terminal-notifier", "-title", @@ -92,6 +150,8 @@ def main() -> int: subtitle, "-message", message, + "-sound", + "Blow", "-group", "codex", "-ignoreDnD",