on main help sign in
chore(lint): gofmt
commitaf72204ab19104137d9af34c57fd8b479931fcf7 authorChristian Rocha <christian@rocha.is> dateWed 25 Jun 2025, 17:12 (1 year ago) parentfb0f172
5 files changed+51−51
internal/config/config.go+3−3 internal/llm/tools/bash.go+2−2 internal/llm/tools/bash_test.go+9−9 internal/llm/tools/shell/shell.go+19−19 internal/llm/tools/shell/shell_test.go+18−18
internal/config/config.go+3−3
@@ -203,17 +203,17 @@ func Load(workingDir string, debug bool) (*Config, error) {
203203func configureViper() {
204204 viper.SetConfigName(fmt.Sprintf(".%s", appName))
205205 viper.SetConfigType("json")
206−
206+
207207 // Unix-style paths
208208 viper.AddConfigPath("$HOME")
209209 viper.AddConfigPath(fmt.Sprintf("$XDG_CONFIG_HOME/%s", appName))
210210 viper.AddConfigPath(fmt.Sprintf("$HOME/.config/%s", appName))
211−
211+
212212 // Windows-style paths
213213 viper.AddConfigPath(fmt.Sprintf("$USERPROFILE"))
214214 viper.AddConfigPath(fmt.Sprintf("$APPDATA/%s", appName))
215215 viper.AddConfigPath(fmt.Sprintf("$LOCALAPPDATA/%s", appName))
216−
216+
217217 viper.SetEnvPrefix(strings.ToUpper(appName))
218218 viper.AutomaticEnv()
219219}
internal/llm/tools/bash.go+2−2
@@ -52,7 +52,7 @@ func getSafeReadOnlyCommands() []string {
5252 baseCommands := []string{
5353 // Cross-platform commands
5454 "echo", "hostname", "whoami",
55−
55+
5656 // Git commands (cross-platform)
5757 "git status", "git log", "git diff", "git show", "git branch", "git tag", "git remote", "git ls-files", "git ls-remote",
5858 "git rev-parse", "git config --get", "git config --list", "git describe", "git blame", "git grep", "git shortlog",
@@ -395,4 +395,4 @@ func countLines(s string) int {
395395 return 0
396396 }
397397 return len(strings.Split(s, "\n"))
398−}
398+}
internal/llm/tools/bash_test.go+9−9
@@ -7,12 +7,12 @@ import (
77
88func TestGetSafeReadOnlyCommands(t *testing.T) {
99 commands := getSafeReadOnlyCommands()
10−
10+
1111 // Check that we have some commands
1212 if len(commands) == 0 {
1313 t.Fatal("Expected some safe commands, got none")
1414 }
15−
15+
1616 // Check for cross-platform commands that should always be present
1717 crossPlatformCommands := []string{"echo", "hostname", "whoami", "git status", "go version"}
1818 for _, cmd := range crossPlatformCommands {
@@ -27,7 +27,7 @@ func TestGetSafeReadOnlyCommands(t *testing.T) {
2727 t.Errorf("Expected cross-platform command %q to be in safe commands", cmd)
2828 }
2929 }
30−
30+
3131 if runtime.GOOS == "windows" {
3232 // Check for Windows-specific commands
3333 windowsCommands := []string{"dir", "type", "Get-Process"}
@@ -43,7 +43,7 @@ func TestGetSafeReadOnlyCommands(t *testing.T) {
4343 t.Errorf("Expected Windows command %q to be in safe commands on Windows", cmd)
4444 }
4545 }
46−
46+
4747 // Check that Unix commands are NOT present on Windows
4848 unixCommands := []string{"ls", "pwd", "ps"}
4949 for _, cmd := range unixCommands {
@@ -73,7 +73,7 @@ func TestGetSafeReadOnlyCommands(t *testing.T) {
7373 t.Errorf("Expected Unix command %q to be in safe commands on Unix", cmd)
7474 }
7575 }
76−
76+
7777 // Check that Windows-specific commands are NOT present on Unix
7878 windowsOnlyCommands := []string{"dir", "Get-Process", "systeminfo"}
7979 for _, cmd := range windowsOnlyCommands {
@@ -94,10 +94,10 @@ func TestGetSafeReadOnlyCommands(t *testing.T) {
9494func TestPlatformSpecificSafeCommands(t *testing.T) {
9595 // Test that the function returns different results on different platforms
9696 commands := getSafeReadOnlyCommands()
97−
97+
9898 hasWindowsCommands := false
9999 hasUnixCommands := false
100−
100+
101101 for _, cmd := range commands {
102102 if cmd == "dir" || cmd == "Get-Process" || cmd == "systeminfo" {
103103 hasWindowsCommands = true
@@ -106,7 +106,7 @@ func TestPlatformSpecificSafeCommands(t *testing.T) {
106106 hasUnixCommands = true
107107 }
108108 }
109−
109+
110110 if runtime.GOOS == "windows" {
111111 if !hasWindowsCommands {
112112 t.Error("Expected Windows commands on Windows platform")
@@ -122,4 +122,4 @@ func TestPlatformSpecificSafeCommands(t *testing.T) {
122122 t.Error("Expected Unix commands on Unix platform")
123123 }
124124 }
125−}
125+}
internal/llm/tools/shell/shell.go+19−19
@@ -1,7 +1,7 @@
11// Package shell provides cross-platform shell execution capabilities.
2−//
2+//
33// WINDOWS COMPATIBILITY:
4−// This implementation provides both POSIX shell emulation (mvdan.cc/sh/v3) and
4+// This implementation provides both POSIX shell emulation (mvdan.cc/sh/v3) and
55// native Windows shell support (cmd.exe/PowerShell) for optimal compatibility:
66// - On Windows: Uses native cmd.exe or PowerShell for Windows-specific commands
77// - Cross-platform: Falls back to POSIX emulation for Unix-style commands
@@ -86,7 +86,7 @@ func (s *PersistentShell) Exec(ctx context.Context, command string) (string, str
8686
8787 // Determine which shell to use based on platform and command
8888 shellType := s.determineShellType(command)
89−
89+
9090 switch shellType {
9191 case ShellTypeCmd:
9292 return s.execWindows(ctx, command, "cmd")
@@ -110,19 +110,19 @@ func (s *PersistentShell) determineShellType(command string) ShellType {
110110 }
111111
112112 firstCmd := strings.ToLower(parts[0])
113−
113+
114114 // Check if it's a Windows-specific command
115115 if windowsNativeCommands[firstCmd] {
116116 return ShellTypeCmd
117117 }
118−
118+
119119 // Check for PowerShell-specific syntax
120− if strings.Contains(command, "Get-") || strings.Contains(command, "Set-") ||
121− strings.Contains(command, "New-") || strings.Contains(command, "$_") ||
122− strings.Contains(command, "| Where-Object") || strings.Contains(command, "| ForEach-Object") {
120+ if strings.Contains(command, "Get-") || strings.Contains(command, "Set-") ||
121+ strings.Contains(command, "New-") || strings.Contains(command, "$_") ||
122+ strings.Contains(command, "| Where-Object") || strings.Contains(command, "| ForEach-Object") {
123123 return ShellTypePowerShell
124124 }
125−
125+
126126 // Default to POSIX emulation for cross-platform compatibility
127127 return ShellTypePOSIX
128128}
@@ -130,12 +130,12 @@ func (s *PersistentShell) determineShellType(command string) ShellType {
130130// execWindows executes commands using native Windows shells (cmd.exe or PowerShell)
131131func (s *PersistentShell) execWindows(ctx context.Context, command string, shell string) (string, string, error) {
132132 var cmd *exec.Cmd
133−
133+
134134 // Handle directory changes specially to maintain persistent shell behavior
135135 if strings.HasPrefix(strings.TrimSpace(command), "cd ") {
136136 return s.handleWindowsCD(command)
137137 }
138−
138+
139139 switch shell {
140140 case "cmd":
141141 // Use cmd.exe for Windows commands
@@ -150,16 +150,16 @@ func (s *PersistentShell) execWindows(ctx context.Context, command string, shell
150150 default:
151151 return "", "", fmt.Errorf("unsupported Windows shell: %s", shell)
152152 }
153−
153+
154154 // Set environment variables
155155 cmd.Env = s.env
156−
156+
157157 var stdout, stderr bytes.Buffer
158158 cmd.Stdout = &stdout
159159 cmd.Stderr = &stderr
160−
160+
161161 err := cmd.Run()
162−
162+
163163 logging.InfoPersist("Windows command finished", "shell", shell, "command", command, "err", err)
164164 return stdout.String(), stderr.String(), err
165165}
@@ -171,9 +171,9 @@ func (s *PersistentShell) handleWindowsCD(command string) (string, string, error
171171 if len(parts) < 2 {
172172 return "", "cd: missing directory argument", fmt.Errorf("missing directory argument")
173173 }
174−
174+
175175 targetDir := parts[1]
176−
176+
177177 // Handle relative paths
178178 if !strings.Contains(targetDir, ":") && !strings.HasPrefix(targetDir, "\\") {
179179 // Relative path - resolve against current directory
@@ -193,12 +193,12 @@ func (s *PersistentShell) handleWindowsCD(command string) (string, string, error
193193 // Absolute path
194194 s.cwd = targetDir
195195 }
196−
196+
197197 // Verify the directory exists
198198 if _, err := os.Stat(s.cwd); err != nil {
199199 return "", fmt.Sprintf("cd: %s: No such file or directory", targetDir), err
200200 }
201−
201+
202202 return "", "", nil
203203}
204204
internal/llm/tools/shell/shell_test.go+18−18
@@ -87,10 +87,10 @@ func TestRunContinuity(t *testing.T) {
8787
8888func TestShellTypeDetection(t *testing.T) {
8989 shell := &PersistentShell{}
90−
90+
9191 tests := []struct {
92− command string
93− expected ShellType
92+ command string
93+ expected ShellType
9494 windowsOnly bool
9595 }{
9696 // Windows-specific commands
@@ -100,14 +100,14 @@ func TestShellTypeDetection(t *testing.T) {
100100 {"del file.txt", ShellTypeCmd, true},
101101 {"md newdir", ShellTypeCmd, true},
102102 {"tasklist", ShellTypeCmd, true},
103−
103+
104104 // PowerShell commands
105105 {"Get-Process", ShellTypePowerShell, true},
106106 {"Get-ChildItem", ShellTypePowerShell, true},
107107 {"Set-Location C:\\", ShellTypePowerShell, true},
108108 {"Get-Content file.txt | Where-Object {$_ -match 'pattern'}", ShellTypePowerShell, true},
109109 {"$files = Get-ChildItem", ShellTypePowerShell, true},
110−
110+
111111 // Unix/cross-platform commands
112112 {"ls -la", ShellTypePOSIX, false},
113113 {"cat file.txt", ShellTypePOSIX, false},
@@ -116,11 +116,11 @@ func TestShellTypeDetection(t *testing.T) {
116116 {"git status", ShellTypePOSIX, false},
117117 {"go build", ShellTypePOSIX, false},
118118 }
119−
119+
120120 for _, test := range tests {
121121 t.Run(test.command, func(t *testing.T) {
122122 result := shell.determineShellType(test.command)
123−
123+
124124 if test.windowsOnly && runtime.GOOS != "windows" {
125125 // On non-Windows systems, everything should use POSIX
126126 if result != ShellTypePOSIX {
@@ -140,12 +140,12 @@ func TestWindowsCDHandling(t *testing.T) {
140140 if runtime.GOOS != "windows" {
141141 t.Skip("Windows CD handling test only runs on Windows")
142142 }
143−
143+
144144 shell := &PersistentShell{
145145 cwd: "C:\\Users",
146146 env: []string{},
147147 }
148−
148+
149149 tests := []struct {
150150 command string
151151 expectedCwd string
@@ -156,12 +156,12 @@ func TestWindowsCDHandling(t *testing.T) {
156156 {"cd C:\\Windows", "C:\\Windows", false},
157157 {"cd", "", true}, // Missing argument
158158 }
159−
159+
160160 for _, test := range tests {
161161 t.Run(test.command, func(t *testing.T) {
162162 originalCwd := shell.cwd
163163 stdout, stderr, err := shell.handleWindowsCD(test.command)
164−
164+
165165 if test.shouldError {
166166 if err == nil {
167167 t.Errorf("Command %q should have failed", test.command)
@@ -174,7 +174,7 @@ func TestWindowsCDHandling(t *testing.T) {
174174 t.Errorf("Command %q: expected cwd %q, got %q", test.command, test.expectedCwd, shell.cwd)
175175 }
176176 }
177−
177+
178178 // Reset for next test
179179 shell.cwd = originalCwd
180180 _ = stdout
@@ -187,17 +187,17 @@ func TestCrossPlatformExecution(t *testing.T) {
187187 shell := newPersistentShell(".")
188188 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
189189 defer cancel()
190−
190+
191191 // Test a simple command that should work on all platforms
192192 stdout, stderr, err := shell.Exec(ctx, "echo hello")
193193 if err != nil {
194194 t.Fatalf("Echo command failed: %v, stderr: %s", err, stderr)
195195 }
196−
196+
197197 if stdout == "" {
198198 t.Error("Echo command produced no output")
199199 }
200−
200+
201201 // The output should contain "hello" regardless of platform
202202 if !strings.Contains(strings.ToLower(stdout), "hello") {
203203 t.Errorf("Echo output should contain 'hello', got: %q", stdout)
@@ -208,17 +208,17 @@ func TestWindowsNativeCommands(t *testing.T) {
208208 if runtime.GOOS != "windows" {
209209 t.Skip("Windows native command test only runs on Windows")
210210 }
211−
211+
212212 shell := newPersistentShell(".")
213213 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
214214 defer cancel()
215−
215+
216216 // Test Windows dir command
217217 stdout, stderr, err := shell.Exec(ctx, "dir")
218218 if err != nil {
219219 t.Fatalf("Dir command failed: %v, stderr: %s", err, stderr)
220220 }
221−
221+
222222 if stdout == "" {
223223 t.Error("Dir command produced no output")
224224 }