1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
|
package tmuxedit
import (
"fmt"
"strings"
"testing"
)
func TestCursorAgent_ExtractPrompt(t *testing.T) {
agent := newCursorAgent()
tests := []struct {
name string
content string
want string
}{
{
name: "box with arrow",
content: "Cursor Agent\n │ → fix the bug INSERT │",
want: "fix the bug",
},
{
name: "box without arrow",
content: "Cursor Agent\n │ fix the bug │",
want: "fix the bug",
},
{
name: "strips follow-up placeholder",
content: "Cursor\n │ → Add a follow-up │",
want: "",
},
{
name: "multi-line prompt",
content: " │ → first line of prompt │\n │ second line here │\n │ third line end │",
want: "first line of prompt\nsecond line here\nthird line end",
},
{
name: "multi-line with noise",
content: " │ → fix the bug INSERT │\n │ also refactor tests │",
want: "fix the bug\nalso refactor tests",
},
{
name: "multi-box takes last box only",
content: " ┌──────────────┐\n" +
" │ $ git push │\n" +
" └──────────────┘\n" +
" ┌──────────────┐\n" +
" │ Run command? │\n" +
" │ → Yes (enter) │\n" +
" │ No (esc) │\n" +
" └──────────────┘\n" +
" ┌──────────────┐\n" +
" │ → hello world │\n" +
" └──────────────┘\n",
want: "hello world",
},
{
name: "multi-box multi-line prompt",
content: " ┌──────────────┐\n" +
" │ $ git push │\n" +
" └──────────────┘\n" +
" ┌──────────────┐\n" +
" │ → first line │\n" +
" │ second line │\n" +
" │ third line │\n" +
" └──────────────┘\n",
want: "first line\nsecond line\nthird line",
},
{
name: "no match",
content: "no prompt here",
want: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := agent.ExtractPrompt(tt.content)
if got != tt.want {
t.Errorf("ExtractPrompt() = %q, want %q", got, tt.want)
}
})
}
}
func TestCursorAgent_ClearInput(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
}
agent := newCursorAgent()
err := agent.ClearInput("%5")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
// "End BSpace*200" should send End normally, then BSpace 200 times via -N
want := []string{
"send:%5:End",
"repeat:%5:BSpace*200",
}
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 TestCursorAgent_ExtractPrompt_EmptyPattern(t *testing.T) {
// A cursorAgent with empty promptPat returns empty string
agent := &cursorAgent{baseAgent{promptPat: ""}}
got := agent.ExtractPrompt("│ → hello │")
if got != "" {
t.Errorf("expected empty for empty pattern, got %q", got)
}
}
func TestCursorAgent_ExtractPrompt_InvalidRegex(t *testing.T) {
// A cursorAgent with invalid regex returns empty string
agent := &cursorAgent{baseAgent{promptPat: "[invalid"}}
got := agent.ExtractPrompt("│ → hello │")
if got != "" {
t.Errorf("expected empty for invalid regex, got %q", got)
}
}
func TestCursorAgent_ClearInput_Disabled(t *testing.T) {
agent := &cursorAgent{baseAgent{clearFirst: false, clearKeys: "End BSpace*200"}}
err := agent.ClearInput("%1")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
}
func TestCursorAgent_ClearInput_EmptyKeys(t *testing.T) {
agent := &cursorAgent{baseAgent{clearFirst: true, clearKeys: ""}}
err := agent.ClearInput("%1")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
}
func TestCursorAgent_ClearInput_Error(t *testing.T) {
noSleep(t)
oldSend := sendKeys
defer func() { sendKeys = oldSend }()
sendKeys = func(string, ...string) error {
return fmt.Errorf("send failed")
}
agent := newCursorAgent()
err := agent.ClearInput("%1")
if err == nil {
t.Fatal("expected error from sendClearSequence failure")
}
}
func TestCursorAgent_Detect(t *testing.T) {
agent := newCursorAgent()
tests := []struct {
name string
content string
want bool
}{
{"box with arrow", "│ → type here │", true},
{"commands footer", "/ commands · @ files", true},
{"no match", "some text", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := agent.Detect(tt.content); got != tt.want {
t.Errorf("Detect() = %v, want %v", got, tt.want)
}
})
}
}
|