summaryrefslogtreecommitdiff
path: root/internal/tmuxedit/send_test.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-02-08 15:19:36 +0200
committerPaul Buetow <paul@buetow.org>2026-02-08 15:19:36 +0200
commit887d7bc186db90c3903851b0f1db2d24df5d7a7b (patch)
tree1cfb8055ddbb907bae9461b924dda1d2b3f15b46 /internal/tmuxedit/send_test.go
parent6da37034708dc7d4dcb7c71e890478a68e6ae4a1 (diff)
fix hexai-tmux-edit agent detection, multi-line extraction, and clearing
- Reorder agents: cursor first to avoid false positive from "Claude 4.5 Sonnet" model name appearing in cursor panes - Change cursor detect pattern to box-drawing UI elements instead of name-based matching - Change claude detect pattern to ❯ prompt instead of generic "claude" - Support multi-line prompt extraction using last-contiguous-block algorithm to ignore command-review and dialog boxes - Fix deduplicateText to always return full edited text (clear + resend) instead of stripping original prefix which caused double-removal - Replace vim-style clear (Escape gg dG i) with universal End+BSpace*200 since cursor's prompt is not actually vim - Add Key*N repeat syntax in ClearKeys (parsed by parseKeyRepeat, sent via tmux send-keys -N) - Add 300ms sleep after clear to let TUI drain queued backspaces before new text arrives - Add debug logging to /tmp/hexai-tmux-edit.log - Add strip pattern for "ctrl+c to stop" noise in cursor prompt Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'internal/tmuxedit/send_test.go')
-rw-r--r--internal/tmuxedit/send_test.go81
1 files changed, 78 insertions, 3 deletions
diff --git a/internal/tmuxedit/send_test.go b/internal/tmuxedit/send_test.go
index eeced35..e458282 100644
--- a/internal/tmuxedit/send_test.go
+++ b/internal/tmuxedit/send_test.go
@@ -17,10 +17,10 @@ func TestDeduplicateText(t *testing.T) {
{"empty original", "", "new text", "new text"},
{"empty edited", "original", "", ""},
{"unchanged", "hello world", "hello world", ""},
- {"appended", "hello", "hello world", "world"},
+ {"appended", "hello", "hello world", "hello world"},
{"rewritten", "hello world", "goodbye world", "goodbye world"},
- {"whitespace handling", " hello ", " hello world ", "world"},
- {"prefix match with newlines", "line1\nline2", "line1\nline2\nline3", "line3"},
+ {"whitespace handling", " hello ", " hello world ", "hello world"},
+ {"appended with newlines", "line1\nline2", "line1\nline2\nline3", "line1\nline2\nline3"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@@ -33,7 +33,16 @@ func TestDeduplicateText(t *testing.T) {
}
}
+// noSleep disables the post-clear sleep in tests and restores it on cleanup.
+func noSleep(t *testing.T) {
+ t.Helper()
+ old := sleepAfterClear
+ sleepAfterClear = func() {}
+ t.Cleanup(func() { sleepAfterClear = old })
+}
+
func TestSendTextToPane_SingleLine(t *testing.T) {
+ noSleep(t)
var calls []string
oldSend := sendKeys
defer func() { sendKeys = oldSend }()
@@ -116,6 +125,7 @@ func TestSendTextToPane_Empty(t *testing.T) {
}
func TestSendTextToPane_ClearError(t *testing.T) {
+ noSleep(t)
oldSend := sendKeys
defer func() { sendKeys = oldSend }()
sendKeys = func(paneID string, keys ...string) error {
@@ -129,6 +139,7 @@ func TestSendTextToPane_ClearError(t *testing.T) {
}
func TestSendTextToPane_SendError(t *testing.T) {
+ noSleep(t)
callCount := 0
oldSend := sendKeys
defer func() { sendKeys = oldSend }()
@@ -146,6 +157,70 @@ func TestSendTextToPane_SendError(t *testing.T) {
}
}
+func TestSendTextToPane_BulkClear(t *testing.T) {
+ noSleep(t)
+ var calls []string
+ oldSend := sendKeys
+ oldRepeat := sendRepeatedKey
+ defer func() {
+ sendKeys = oldSend
+ sendRepeatedKey = oldRepeat
+ }()
+ sendKeys = func(paneID string, keys ...string) error {
+ calls = append(calls, fmt.Sprintf("send:%s:%s", paneID, strings.Join(keys, ",")))
+ return nil
+ }
+ sendRepeatedKey = func(paneID, key string, count int) error {
+ calls = append(calls, fmt.Sprintf("repeat:%s:%s*%d", paneID, key, count))
+ return nil
+ }
+ // "End BSpace*200" should send End normally, then BSpace 200 times via -N
+ agent := AgentConfig{ClearFirst: true, ClearKeys: "End BSpace*200", NewlineKeys: "S-Enter"}
+ err := sendTextToPane("%5", "new text", agent)
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ want := []string{
+ "send:%5:End",
+ "repeat:%5:BSpace*200",
+ "send:%5:new text",
+ }
+ if len(calls) != len(want) {
+ t.Fatalf("got %d calls, want %d: %v", len(calls), len(want), calls)
+ }
+ for i, w := range want {
+ if calls[i] != w {
+ t.Errorf("call[%d] = %q, want %q", i, calls[i], w)
+ }
+ }
+}
+
+func TestParseKeyRepeat(t *testing.T) {
+ tests := []struct {
+ token string
+ wantKey string
+ wantCount int
+ }{
+ {"BSpace*200", "BSpace", 200},
+ {"End", "End", 1},
+ {"C-u", "C-u", 1},
+ {"BSpace*1", "BSpace", 1},
+ {"BSpace*0", "BSpace*0", 1}, // invalid count
+ {"BSpace*abc", "BSpace*abc", 1}, // non-numeric
+ {"*200", "*200", 1}, // no key name
+ {"x*3", "x", 3},
+ }
+ for _, tt := range tests {
+ t.Run(tt.token, func(t *testing.T) {
+ key, count := parseKeyRepeat(tt.token)
+ if key != tt.wantKey || count != tt.wantCount {
+ t.Errorf("parseKeyRepeat(%q) = (%q, %d), want (%q, %d)",
+ tt.token, key, count, tt.wantKey, tt.wantCount)
+ }
+ })
+ }
+}
+
func TestSendTextToPane_FallbackNewline(t *testing.T) {
var calls []string
oldSend := sendKeys