777genius / 777genius/agent-notifications
[Bug]: Click-to-focus cannot switch to the correct Ghostty tab (window-level only)
- 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
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