feat: fix Cursor hook delivery and improve notify workflow

Route hook notifications through /dev/tty and client_tty so OSC reaches
Ghostty when Cursor captures stdout. Add afterAgentResponse hook, hook
logging, debounce, split test modes, and use directory name for titles.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-05-26 12:46:50 +08:00
parent aa9c2b8ea5
commit 021fc3a828
20 changed files with 757 additions and 76 deletions
+5 -21
View File
@@ -2,7 +2,6 @@ package context
import (
"os"
"os/exec"
"path/filepath"
"strings"
)
@@ -15,16 +14,13 @@ type Meta struct {
}
func Render(tmpl string, m Meta) string {
ctx := m.ResolveContext(m.Context)
ctx := m.ResolveContext()
out := strings.ReplaceAll(tmpl, "{agent}", m.Agent)
out = strings.ReplaceAll(out, "{context}", ctx)
return out
}
func (m Meta) ResolveContext(window string) string {
if window != "" {
return window
}
func (m Meta) ResolveContext() string {
if m.Context != "" {
return m.Context
}
@@ -38,17 +34,6 @@ func (m Meta) ResolveContext(window string) string {
return filepath.Base(cwd)
}
func TmuxWindowName() string {
if os.Getenv("TMUX") == "" {
return ""
}
out, err := exec.Command("tmux", "display-message", "-p", "#{window_name}").Output()
if err != nil {
return ""
}
return strings.TrimSpace(string(out))
}
func MetaFromEnv(agent, event string) Meta {
cwd := os.Getenv("AGENT_NOTIFY_CWD")
if cwd == "" {
@@ -61,9 +46,8 @@ func MetaFromEnv(agent, event string) Meta {
event = e
}
return Meta{
Agent: agent,
CWD: cwd,
Context: TmuxWindowName(),
Event: event,
Agent: agent,
CWD: cwd,
Event: event,
}
}
+7 -7
View File
@@ -3,7 +3,7 @@ package context
import "testing"
func TestRenderTitle(t *testing.T) {
meta := Meta{Agent: "Cursor", Context: "myapp"}
meta := Meta{Agent: "Cursor", CWD: "/home/user/code/myapp"}
got := Render("{agent} — {context}", meta)
want := "Cursor — myapp"
if got != want {
@@ -13,14 +13,14 @@ func TestRenderTitle(t *testing.T) {
func TestContextFromCWD(t *testing.T) {
meta := Meta{Agent: "Claude", CWD: "/home/user/code/myapp"}
if meta.ResolveContext("") != "myapp" {
t.Fatalf("got %q", meta.ResolveContext(""))
if meta.ResolveContext() != "myapp" {
t.Fatalf("got %q", meta.ResolveContext())
}
}
func TestContextPrefersWindow(t *testing.T) {
meta := Meta{Agent: "Claude", CWD: "/home/user/code/myapp"}
if meta.ResolveContext("tmux-win") != "tmux-win" {
t.Fatalf("expected window name")
func TestContextExplicitOverride(t *testing.T) {
meta := Meta{Agent: "Claude", CWD: "/home/user/code/myapp", Context: "custom"}
if meta.ResolveContext() != "custom" {
t.Fatalf("expected explicit context override")
}
}