feat: Add CommandTool for user-approved shell command execution, integrating it with a custom spinner that can be paused during interaction.
This commit is contained in:
71
Program.cs
71
Program.cs
@@ -239,23 +239,70 @@ while (true)
|
||||
}
|
||||
|
||||
// Phase 1: Show BouncingBar spinner while agent thinks & invokes tools
|
||||
await AnsiConsole.Status()
|
||||
.Spinner(Spinner.Known.BouncingBar)
|
||||
.SpinnerStyle(Style.Parse("cornflowerblue"))
|
||||
.StartAsync("Thinking...", async ctx =>
|
||||
using var spinnerCts = CancellationTokenSource.CreateLinkedTokenSource(responseCts.Token);
|
||||
bool showSpinner = true;
|
||||
|
||||
CommandTool.PauseSpinner = () =>
|
||||
{
|
||||
showSpinner = false;
|
||||
Console.Write("\r" + new string(' ', 40) + "\r");
|
||||
};
|
||||
CommandTool.ResumeSpinner = () =>
|
||||
{
|
||||
showSpinner = true;
|
||||
};
|
||||
|
||||
var spinnerTask = Task.Run(async () =>
|
||||
{
|
||||
var frames = Spinner.Known.BouncingBar.Frames;
|
||||
var interval = Spinner.Known.BouncingBar.Interval;
|
||||
int i = 0;
|
||||
|
||||
// Hide cursor
|
||||
Console.Write("\x1b[?25l");
|
||||
try
|
||||
{
|
||||
while (await stream.MoveNextAsync())
|
||||
while (!spinnerCts.Token.IsCancellationRequested)
|
||||
{
|
||||
responseCts.Token.ThrowIfCancellationRequested();
|
||||
CaptureUsage(stream.Current);
|
||||
if (!string.IsNullOrEmpty(stream.Current.Text))
|
||||
if (showSpinner)
|
||||
{
|
||||
firstChunk = stream.Current.Text;
|
||||
fullResponse = firstChunk;
|
||||
return; // Break out → stops the spinner
|
||||
var frame = frames[i % frames.Count];
|
||||
Console.Write($"\r\x1b[38;5;69m{frame}\x1b[0m Thinking...");
|
||||
i++;
|
||||
}
|
||||
try { await Task.Delay(interval, spinnerCts.Token); } catch { }
|
||||
}
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Clear the spinner line and show cursor
|
||||
if (showSpinner)
|
||||
Console.Write("\r" + new string(' ', 40) + "\r");
|
||||
Console.Write("\x1b[?25h");
|
||||
}
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
while (await stream.MoveNextAsync())
|
||||
{
|
||||
responseCts.Token.ThrowIfCancellationRequested();
|
||||
CaptureUsage(stream.Current);
|
||||
if (!string.IsNullOrEmpty(stream.Current.Text))
|
||||
{
|
||||
firstChunk = stream.Current.Text;
|
||||
fullResponse = firstChunk;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
spinnerCts.Cancel();
|
||||
await Task.WhenAny(spinnerTask);
|
||||
CommandTool.PauseSpinner = null;
|
||||
CommandTool.ResumeSpinner = null;
|
||||
}
|
||||
|
||||
// Phase 2: Stream text tokens directly to the console
|
||||
if (firstChunk != null)
|
||||
|
||||
Reference in New Issue
Block a user