777genius / 777genius/agent-notifications

[Bug]: Click-to-focus cannot switch to the correct Ghostty tab (window-level only)

Open
#72 3 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
Go
Stars
813
Forks
108
Avg merge
17h 58m
Merged PRs (30d)
36

Description

### Summary

Clicking a desktop notification only brings the correct Ghostty **window** to the foreground, but does **not** switch to the specific **tab** containing the Claude Code session. When multiple Claude instances run in different tabs of the same Ghostty window, the notification click is unable to activate the correct tab.

### Environment

| Field | Value |
|---|---|
| Plugin version | 1.36.7 |
| Operating system | macOS (Darwin 25.3.0, arm64) |
| Terminal | Ghostty (latest) |
| Terminal multiplexer | None |
| Notification type affected | All (task_complete, question, plan_ready, review_complete) |
| Issue category | click-to-focus |

### Root Cause Analysis

The current Ghostty click-to-focus path uses macOS Accessibility API with `AXDocument` matching (`raiseWindowByAXDocument` in `ax_focus_darwin.go:321-339`):

```go
if isGhosttyBundleID(bundleID) {
C.activateByPID(C.int(pid))
fileURL := cwdToFileURL(cwd)
result := retryWindowFocus(func() C.int {
return C.raiseWindowByAXDocument(C.int(pid), cFileURL)
})
}
```

`AXDocument` is a **window-level** attribute. It can identify which Ghostty window matches the CWD (via OSC 7 `file://` URL), but **cannot distinguish between tabs within the same window**. This is explicitly documented in the code comment:

> `AXDocument is window-level only; tabs and split panes within a window are not individually addressable.`

Additionally, Ghostty is **not registered as a multiplexer** in `multiplexer.go` (only tmux, zellij, wezterm, and kitty are), so there is no tab-level switching logic for Ghostty at all.

### Steps to Reproduce

1. Open Ghostty with multiple tabs (e.g., Tab 1: `/project-a`, Tab 2: `/project-b`)
2. Run `claude` in each tab (different projects)
3. Wait for a notification (task complete, question, etc.) in a background tab
4. Click the notification
5. **Expected:** Ghostty activates and switches to the tab containing the Claude session that triggered the notification
6. **Actual:** Ghostty window is brought to the foreground, but the **active tab does not change** — it stays on whichever tab was last focused

### Proposed Fix

Ghostty provides native **AppleScript support** that can locate and focus a specific terminal by working directory, with full tab-level precision:

```applescript
tell application "Ghostty"
set matches to every terminal whose working directory contains "/path/to/project"
if (count of matches) > 0 then
focus item 1 of matches
end if
end tell
```

This searches **all terminals across all windows and tabs**, and `focus` activates the correct tab. See: https://ghostty.org/docs/features/applescript

**Note:** The codebase previously used AppleScript for focus (`osascript -e ...`) but removed it because macOS Tahoe (26.x) broke Automation permission prompts for notification click handlers (`osascript fails silently`). See `notifier.go:357-361`. Any new AppleScript-based solution would need to handle this permission model change — perhaps by:
- Using the `focus-window` binary subcommand as the entry point (which already has Accessibility + Screen Recording permissions), and calling AppleScript from within that binary instead of from `terminal-notifier`'s `-execute`
- Or requesting Automation permissions proactively on first use

### Workaround

No current workaround exists within the plugin. Users must manually switch tabs after clicking the notification.

Contributor guide

Open the contributing guide

Research direction

Start with raiseWindowByAXDocument in ax_focus_darwin.go:321-339, then inspect the focus-window entry point, multiplexer.go, and notifier.go:357-361. Investigate how Ghostty’s AppleScript focus path can work within the current macOS permission model; done means clicking a notification selects the correct Ghostty tab, not only its window.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, macos
Domain
desktop, devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.