feat(ui): render assistant tool_use blocks with per-tool args
This commit is contained in:
@@ -95,7 +95,8 @@ public class StreamLineFormatter
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case "tool_use":
|
case "tool_use":
|
||||||
// Filled in by a later task.
|
sb.Append(FormatToolUse(block));
|
||||||
|
sb.Append('\n');
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "thinking":
|
case "thinking":
|
||||||
@@ -128,6 +129,96 @@ public class StreamLineFormatter
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string FormatToolUse(JsonElement block)
|
||||||
|
{
|
||||||
|
var name = block.TryGetProperty("name", out var nameProp)
|
||||||
|
? nameProp.GetString() ?? "?"
|
||||||
|
: "?";
|
||||||
|
|
||||||
|
JsonElement input = default;
|
||||||
|
var hasInput = block.TryGetProperty("input", out input)
|
||||||
|
&& input.ValueKind == JsonValueKind.Object;
|
||||||
|
|
||||||
|
var label = name;
|
||||||
|
if (hasInput && (name == "Task" || name == "Agent"))
|
||||||
|
{
|
||||||
|
var sub = GetStr(input, "subagent_type");
|
||||||
|
if (!string.IsNullOrEmpty(sub))
|
||||||
|
label = $"{name}: {sub}";
|
||||||
|
}
|
||||||
|
|
||||||
|
string? arg = hasInput ? BuildToolArg(name, input) : null;
|
||||||
|
|
||||||
|
return string.IsNullOrEmpty(arg)
|
||||||
|
? $"[{label}]"
|
||||||
|
: $"[{label}] {arg}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string? BuildToolArg(string toolName, JsonElement input)
|
||||||
|
{
|
||||||
|
switch (toolName)
|
||||||
|
{
|
||||||
|
case "Read":
|
||||||
|
case "Write":
|
||||||
|
case "Edit":
|
||||||
|
case "NotebookEdit":
|
||||||
|
return Basename(GetStr(input, "file_path"));
|
||||||
|
|
||||||
|
case "Bash":
|
||||||
|
case "PowerShell":
|
||||||
|
{
|
||||||
|
var cmd = GetStr(input, "command");
|
||||||
|
return string.IsNullOrEmpty(cmd) ? null : "$ " + Truncate(cmd, MaxArgChars);
|
||||||
|
}
|
||||||
|
|
||||||
|
case "Grep":
|
||||||
|
{
|
||||||
|
var p = GetStr(input, "pattern");
|
||||||
|
return string.IsNullOrEmpty(p) ? null : $"\"{Truncate(p, MaxArgChars)}\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
case "Glob":
|
||||||
|
return Truncate(GetStr(input, "pattern"), MaxArgChars);
|
||||||
|
|
||||||
|
case "Task":
|
||||||
|
case "Agent":
|
||||||
|
return Truncate(GetStr(input, "description"), MaxArgChars);
|
||||||
|
|
||||||
|
case "WebFetch":
|
||||||
|
return GetStr(input, "url");
|
||||||
|
|
||||||
|
case "WebSearch":
|
||||||
|
{
|
||||||
|
var q = GetStr(input, "query");
|
||||||
|
return string.IsNullOrEmpty(q) ? null : $"\"{Truncate(q, MaxArgChars)}\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
case "TodoWrite":
|
||||||
|
return null;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string? GetStr(JsonElement obj, string name)
|
||||||
|
=> obj.TryGetProperty(name, out var p) && p.ValueKind == JsonValueKind.String
|
||||||
|
? p.GetString()
|
||||||
|
: null;
|
||||||
|
|
||||||
|
private static string Basename(string? path)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(path)) return "";
|
||||||
|
var i = path.LastIndexOfAny(new[] { '/', '\\' });
|
||||||
|
return i < 0 ? path : path[(i + 1)..];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string Truncate(string? s, int max)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(s)) return "";
|
||||||
|
return s.Length <= max ? s : s[..max] + "…";
|
||||||
|
}
|
||||||
|
|
||||||
public string FormatFile(string filePath)
|
public string FormatFile(string filePath)
|
||||||
{
|
{
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
|
|||||||
Reference in New Issue
Block a user