From 685c94c1be38fd3b4c65f1daff73e624bda83f0b Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Thu, 25 Sep 2025 13:50:55 +0000 Subject: [PATCH 1/2] feat: Use external editor for command editing instead of inline editor - Replaces readline inline editor with external editor (Git-like approach) - Uses $EDITOR or $VISUAL environment variables - Falls back to common editors (vim, vi, nano, emacs) if env vars not set - Creates temporary file for editing with command pre-filled - Properly handles stdin/stdout/stderr for terminal editor support Fixes #76 Co-authored-by: Alvin --- internal/confirm.go | 69 ++++++++++++++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 19 deletions(-) diff --git a/internal/confirm.go b/internal/confirm.go index 6c94e52..6b14040 100644 --- a/internal/confirm.go +++ b/internal/confirm.go @@ -2,6 +2,8 @@ package internal import ( "fmt" + "os" + "os/exec" "regexp" "strings" @@ -59,33 +61,62 @@ func (m *Manager) confirmedToExecFn(command string, prompt string, edit bool) (b case "y", "yes", "ok", "sure": return true, command case "e", "edit": - // Allow user to edit the command using readline for better editing experience - editConfig := &readline.Config{ - Prompt: "Edit command: ", - InterruptPrompt: "^C", - EOFPrompt: "exit", + // Use external editor (Git-like approach) + editor := os.Getenv("EDITOR") + if editor == "" { + editor = os.Getenv("VISUAL") + } + if editor == "" { + // Fall back to common editors + editors := []string{"vim", "vi", "nano", "emacs"} + for _, e := range editors { + if _, err := exec.LookPath(e); err == nil { + editor = e + break + } + } } - editRl, editErr := readline.NewEx(editConfig) - if editErr != nil { - fmt.Printf("Error initializing readline for edit: %v\n", editErr) + if editor == "" { + fmt.Println("Error: No editor found. Please set the EDITOR environment variable.") return false, "" } - defer func() { _ = editRl.Close() }() - - // Use ReadlineWithDefault to prefill the command - editedCommand, editErr := editRl.ReadlineWithDefault(command) - if editErr != nil { - if editErr == readline.ErrInterrupt { - m.Status = "" - return false, "" - } - fmt.Printf("Error reading edited command: %v\n", editErr) + // Create a temporary file for editing + tmpFile, err := os.CreateTemp("", "tmuxai-edit-*.sh") + if err != nil { + fmt.Printf("Error creating temporary file: %v\n", err) + return false, "" + } + defer os.Remove(tmpFile.Name()) + + // Write the command to the temporary file + if _, err := tmpFile.WriteString(command); err != nil { + fmt.Printf("Error writing to temporary file: %v\n", err) + tmpFile.Close() + return false, "" + } + tmpFile.Close() + + // Open the editor + cmd := exec.Command(editor, tmpFile.Name()) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + if err := cmd.Run(); err != nil { + fmt.Printf("Error running editor: %v\n", err) + return false, "" + } + + // Read the edited command + editedBytes, err := os.ReadFile(tmpFile.Name()) + if err != nil { + fmt.Printf("Error reading edited command: %v\n", err) return false, "" } - editedCommand = strings.TrimSpace(editedCommand) + editedCommand := strings.TrimSpace(string(editedBytes)) if editedCommand != "" { return true, editedCommand } else { From dd0eb37d8a6d1a0ed161d97b8ecf9f2407232ae9 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Thu, 25 Sep 2025 13:55:08 +0000 Subject: [PATCH 2/2] fix: Address golangci-lint errcheck issues - Properly handle os.Remove error in defer statement - Check error returns for tmpFile.Close() calls - Fixes CI linting failures Co-authored-by: Alvin --- internal/confirm.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/internal/confirm.go b/internal/confirm.go index 6b14040..be5ca8a 100644 --- a/internal/confirm.go +++ b/internal/confirm.go @@ -88,15 +88,18 @@ func (m *Manager) confirmedToExecFn(command string, prompt string, edit bool) (b fmt.Printf("Error creating temporary file: %v\n", err) return false, "" } - defer os.Remove(tmpFile.Name()) + defer func() { _ = os.Remove(tmpFile.Name()) }() // Write the command to the temporary file if _, err := tmpFile.WriteString(command); err != nil { fmt.Printf("Error writing to temporary file: %v\n", err) - tmpFile.Close() + _ = tmpFile.Close() + return false, "" + } + if err := tmpFile.Close(); err != nil { + fmt.Printf("Error closing temporary file: %v\n", err) return false, "" } - tmpFile.Close() // Open the editor cmd := exec.Command(editor, tmpFile.Name())