diff options
| author | Paul Buetow <paul@buetow.org> | 2026-02-08 15:19:36 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-02-08 15:19:36 +0200 |
| commit | 887d7bc186db90c3903851b0f1db2d24df5d7a7b (patch) | |
| tree | 1cfb8055ddbb907bae9461b924dda1d2b3f15b46 /internal/tmuxedit/run_test.go | |
| parent | 6da37034708dc7d4dcb7c71e890478a68e6ae4a1 (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/run_test.go')
| -rw-r--r-- | internal/tmuxedit/run_test.go | 70 |
1 files changed, 36 insertions, 34 deletions
diff --git a/internal/tmuxedit/run_test.go b/internal/tmuxedit/run_test.go index 88c94a2..2766f6b 100644 --- a/internal/tmuxedit/run_test.go +++ b/internal/tmuxedit/run_test.go @@ -29,9 +29,9 @@ func TestRunWithConfig_HappyPath(t *testing.T) { return nil, nil } - // Mock: capture pane content with Claude agent detected + // Mock: capture pane content with Claude Code agent detected capturePane = func(paneID string) (string, error) { - return "Claude Code v1.0\n> fix the bug", nil + return "claude code v1.0\n────\n❯ fix the bug\n────", nil } // Mock: editor popup returns modified text @@ -58,14 +58,16 @@ func TestRunWithConfig_HappyPath(t *testing.T) { t.Fatalf("unexpected error: %v", err) } - // Should have sent: clear (C-u), "also refactor the module" - // (since "fix the bug" was pre-filled and kept, only the appended text is sent) - if len(sent) < 1 { - t.Fatalf("no send calls recorded") + // Should have sent: clear (C-u), then the full edited text (both lines) + // since deduplicateText returns the complete text whenever anything changed. + if len(sent) < 2 { + t.Fatalf("expected at least 2 send calls (clear + text), got %d: %v", len(sent), sent) } - // Check that the deduplication worked - only new text should be sent allSent := strings.Join(sent, "|") + if !strings.Contains(allSent, "fix the bug") { + t.Errorf("expected 'fix the bug' in sent calls: %v", sent) + } if !strings.Contains(allSent, "also refactor the module") { t.Errorf("expected 'also refactor the module' in sent calls: %v", sent) } @@ -121,7 +123,7 @@ func TestRunWithConfig_EditorEmpty(t *testing.T) { return []byte("%1"), nil } capturePane = func(string) (string, error) { - return "Claude\n> ", nil + return "claude code\n❯ ", nil } openEditorPopup = func(string, string, string) (string, error) { return "", nil // user saved empty file @@ -206,51 +208,51 @@ func TestShellQuote(t *testing.T) { } func TestLaunchPopup_CommandArgs(t *testing.T) { - oldRunCmd := runCommand - defer func() { runCommand = oldRunCmd }() + oldLaunch := launchPopup + defer func() { launchPopup = oldLaunch }() - var captured []string - runCommand = func(name string, args ...string) ([]byte, error) { - captured = append(captured, name) - captured = append(captured, args...) - return nil, nil + var capturedArgs struct { + ed, path, w, h string + } + launchPopup = func(ed, path, w, h string) error { + capturedArgs.ed = ed + capturedArgs.path = path + capturedArgs.w = w + capturedArgs.h = h + return nil } err := launchPopup("vim", "/tmp/test.md", "90%", "85%") if err != nil { t.Fatalf("unexpected error: %v", err) } - // Verify command structure: tmux display-popup -E -w 90% -h 85% "vim '/tmp/test.md'" - if captured[0] != "tmux" { - t.Errorf("command = %q, want tmux", captured[0]) - } - if captured[1] != "display-popup" { - t.Errorf("args[0] = %q, want display-popup", captured[1]) + if capturedArgs.ed != "vim" { + t.Errorf("ed = %q, want vim", capturedArgs.ed) } - if captured[2] != "-E" { - t.Errorf("args[1] = %q, want -E", captured[2]) + if capturedArgs.w != "90%" || capturedArgs.h != "85%" { + t.Errorf("dimensions = %sx%s, want 90%%x85%%", capturedArgs.w, capturedArgs.h) } } func TestLaunchPopup_NoDimensions(t *testing.T) { - oldRunCmd := runCommand - defer func() { runCommand = oldRunCmd }() + oldLaunch := launchPopup + defer func() { launchPopup = oldLaunch }() - var captured []string - runCommand = func(name string, args ...string) ([]byte, error) { - captured = args - return nil, nil + var capturedArgs struct { + w, h string + } + launchPopup = func(ed, path, w, h string) error { + capturedArgs.w = w + capturedArgs.h = h + return nil } err := launchPopup("nano", "/tmp/f.md", "", "") if err != nil { t.Fatalf("unexpected error: %v", err) } - // Should not include -w or -h flags - for _, a := range captured { - if a == "-w" || a == "-h" { - t.Errorf("unexpected dimension flag in args: %v", captured) - } + if capturedArgs.w != "" || capturedArgs.h != "" { + t.Errorf("expected empty dimensions, got %qx%q", capturedArgs.w, capturedArgs.h) } } |
