using System.ComponentModel; using System.Diagnostics; using Spectre.Console; namespace AnchorCli.Tools; /// /// Command execution tool with user approval. /// internal static class CommandTool { public static Action Log { get; set; } = Console.WriteLine; #if WINDOWS [Description("Execute a PowerShell command after user approval. Prompts with [Y/n] before running. Note: For file editing and operations, use the built-in file tools (ReadFile, ReplaceLines, InsertAfter, DeleteRange, CreateFile, etc.) instead of shell commands.")] #else [Description("Execute a Bash shell command after user approval. Prompts with [Y/n] before running. Note: For file editing and operations, use the built-in file tools (ReadFile, ReplaceLines, InsertAfter, DeleteRange, CreateFile, etc.) instead of shell commands.")] #endif public static string ExecuteCommand( [Description("The shell command to execute.")] string command) { Log($"Command request: {command}"); // Prompt for user approval AnsiConsole.WriteLine(); AnsiConsole.Write( new Panel($"[yellow]{Markup.Escape(command)}[/]") .Header("[bold yellow] Run command? [/]") .BorderColor(Color.Yellow) .RoundedBorder() .Padding(1, 0)); // Drain any buffered keystrokes so stale input doesn't auto-answer while (Console.KeyAvailable) Console.ReadKey(intercept: true); if (!AnsiConsole.Confirm("Execute?", defaultValue: true)) { AnsiConsole.MarkupLine("[dim grey] ✗ Cancelled by user[/]"); return "ERROR: Command execution cancelled by user"; } // Execute the command try { var startInfo = new ProcessStartInfo { #if WINDOWS FileName = "powershell.exe", Arguments = $"-NoProfile -Command \"{command}\"", #else FileName = "/bin/bash", Arguments = $"-c \"{command}\"", #endif UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true, }; using var process = Process.Start(startInfo); if (process == null) { return "ERROR: Failed to start process"; } string output = process.StandardOutput.ReadToEnd(); string error = process.StandardError.ReadToEnd(); process.WaitForExit(); // Log the output and error if (!string.IsNullOrWhiteSpace(output)) { Log("Output:"); Log(output); } if (!string.IsNullOrWhiteSpace(error)) { Log("Error:"); Log(error); } var sb = new System.Text.StringBuilder(); sb.AppendLine($"Command: {command}"); sb.AppendLine($"Exit code: {process.ExitCode}"); if (!string.IsNullOrWhiteSpace(output)) { sb.AppendLine("Output:"); sb.AppendLine(output); } if (!string.IsNullOrWhiteSpace(error)) { sb.AppendLine("Error:"); sb.AppendLine(error); } return sb.ToString(); } catch (Exception ex) { return $"ERROR executing command: {ex.Message}"; } } }