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
|
package tmuxedit
import (
"regexp"
)
// cursorAgent handles Cursor's distinctive box-drawing │ → prompt │ UI.
// Cursor uses a text field (not vim), so clearing is done with End + bulk
// backspace. Multi-line prompts are entered with Shift-Enter within the box.
type cursorAgent struct{ baseAgent }
// newCursorAgent returns a cursorAgent with the default configuration.
// Detect by the box structure or "/ commands" footer. Checked first because
// cursor panes often show model names like "Claude 4.5 Sonnet".
func newCursorAgent() *cursorAgent {
return &cursorAgent{baseAgent{
name: "cursor",
displayName: "Cursor",
detectPattern: `(│\s*→|/ commands · @ files)`,
promptPat: `(?m)│\s*→?\s*(.+?)\s*│\s*$`,
stripPatterns: []string{"INSERT", "Add a follow-up", "ctrl+c to stop"},
clearFirst: true,
clearKeys: "End BSpace*200",
newlineKeys: "S-Enter",
submitKeys: "Enter",
}}
}
// ExtractPrompt extracts the prompt text from the last contiguous │...│ block
// in the pane. This avoids picking up earlier command-review or dialog boxes
// that also use box-drawing characters.
func (c *cursorAgent) ExtractPrompt(paneContent string) string {
if c.promptPat == "" {
return ""
}
re, err := regexp.Compile(c.promptPat)
if err != nil {
return ""
}
allMatches := matchPromptLines(re, paneContent)
if len(allMatches) == 0 {
return ""
}
return joinLastContiguousBlock(allMatches, c.stripPatterns)
}
// ClearInput sends End + 200 backspaces to clear Cursor's text field.
// Cursor's input is a standard text field, not vim.
func (c *cursorAgent) ClearInput(paneID string) error {
if !c.clearFirst || c.clearKeys == "" {
return nil
}
if err := sendClearSequence(paneID, c.clearKeys); err != nil {
return err
}
sleepAfterClear()
return nil
}
|