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:
2026-05-26 11:19:34 +08:00
parent f13dcd6cae
commit 5fecb6d215
22 changed files with 2729 additions and 11 deletions
+46
View File
@@ -0,0 +1,46 @@
package tmux
import (
"os"
"os/exec"
"strings"
)
func InTmux() bool {
return os.Getenv("TMUX") != ""
}
func ClientTTY() (string, error) {
if !InTmux() {
return "", nil
}
out, err := exec.Command("tmux", "display-message", "-p", "#{client_tty}").Output()
if err != nil {
return "", err
}
return strings.TrimSpace(string(out)), nil
}
func WrapPassthrough(seq string) string {
return "\033Ptmux;\033" + seq + "\033\\"
}
func WrapPassthroughLayers(seq string, layers int) string {
out := seq
for i := 0; i < layers; i++ {
out = WrapPassthrough(out)
}
return out
}
func AllowPassthroughEnabled() (bool, string, error) {
if !InTmux() {
return true, "", nil
}
out, err := exec.Command("tmux", "show-option", "-gv", "allow-passthrough").Output()
if err != nil {
return false, "", err
}
val := strings.TrimSpace(string(out))
return val == "on" || val == "all", val, nil
}
+29
View File
@@ -0,0 +1,29 @@
package tmux
import "testing"
func TestInTmux(t *testing.T) {
t.Setenv("TMUX", "/tmp/tmux-123,1,0")
if !InTmux() {
t.Fatal("expected InTmux true")
}
}
func TestWrapPassthroughSingle(t *testing.T) {
inner := "\033]777;notify;t;b\007"
got := WrapPassthrough(inner)
want := "\033Ptmux;\033" + inner + "\033\\"
if got != want {
t.Fatalf("got %q want %q", got, want)
}
}
func TestWrapPassthroughNested(t *testing.T) {
inner := "\033]777;notify;t;b\007"
got := WrapPassthroughLayers(inner, 2)
once := WrapPassthrough(inner)
twice := WrapPassthrough(once)
if got != twice {
t.Fatalf("nested wrap mismatch")
}
}