feat: implement agent-notify CLI with hooks and tmux passthrough
Add OSC 777 notification sender, Cursor/Claude hook adapters, config/install commands, and README for Ghostty + tmux setup. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Meta struct {
|
||||
Agent string
|
||||
CWD string
|
||||
Context string
|
||||
Event string
|
||||
}
|
||||
|
||||
func Render(tmpl string, m Meta) string {
|
||||
ctx := m.ResolveContext(m.Context)
|
||||
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
|
||||
}
|
||||
if m.Context != "" {
|
||||
return m.Context
|
||||
}
|
||||
cwd := m.CWD
|
||||
if cwd == "" {
|
||||
cwd, _ = os.Getwd()
|
||||
}
|
||||
if cwd == "" {
|
||||
return "unknown"
|
||||
}
|
||||
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 == "" {
|
||||
cwd, _ = os.Getwd()
|
||||
}
|
||||
if a := os.Getenv("AGENT_NOTIFY_AGENT"); a != "" {
|
||||
agent = a
|
||||
}
|
||||
if e := os.Getenv("AGENT_NOTIFY_EVENT"); e != "" {
|
||||
event = e
|
||||
}
|
||||
return Meta{
|
||||
Agent: agent,
|
||||
CWD: cwd,
|
||||
Context: TmuxWindowName(),
|
||||
Event: event,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package context
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestRenderTitle(t *testing.T) {
|
||||
meta := Meta{Agent: "Cursor", Context: "myapp"}
|
||||
got := Render("{agent} — {context}", meta)
|
||||
want := "Cursor — myapp"
|
||||
if got != want {
|
||||
t.Fatalf("got %q want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContextFromCWD(t *testing.T) {
|
||||
meta := Meta{Agent: "Claude", CWD: "/home/user/code/myapp"}
|
||||
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")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user