1
0
Files
AnchorCli/Tools/CommandTool.cs

121 lines
3.9 KiB
C#

using System.ComponentModel;
using System.Diagnostics;
using Spectre.Console;
namespace AnchorCli.Tools;
/// <summary>
/// Command execution tool with user approval.
/// </summary>
internal static class CommandTool
{
public static Action<string> Log { get; set; } = Console.WriteLine;
public static Action? PauseSpinner { get; set; }
public static Action? ResumeSpinner { get; set; }
#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($" ● execute_command: {command}");
// Prompt for user approval
PauseSpinner?.Invoke();
try
{
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
try
{
while (Console.KeyAvailable)
Console.ReadKey(intercept: true);
}
catch { }
if (!AnsiConsole.Confirm("Execute?", defaultValue: true))
{
AnsiConsole.MarkupLine("[dim grey] ✗ Cancelled by user[/]");
return "ERROR: Command execution cancelled by user";
}
}
finally
{
ResumeSpinner?.Invoke();
}
// 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}";
}
}
}