feat(#2196): stamp _body on mcp__bash__run, drop tool-specific JS branch

Backend now stamps `_body` (full `$ <cmd>`) alongside `_summary` (first
line) and `_category: "rich"` for mcp__bash__run tool_use entries. The
frontend drops the mcp__bash__run-specific branch in renderRichToolUse
and uses a generic `if (c._body)` path instead — api.details() with the
backend-computed summary and body, no JS knowledge of the tool name.
This commit is contained in:
iris 2026-07-19 18:16:46 +02:00
commit b62652c01b
2 changed files with 32 additions and 8 deletions

View file

@ -230,6 +230,7 @@ fn enrich_tool_use_entry(entry: &mut Value) {
let icon = tool_icon(&name);
let summary = fmt_tool_use(&name, &input);
let rich = is_rich_tool(&name);
let body = rich_tool_body(&name, &input);
let Some(obj) = entry.as_object_mut() else {
return;
};
@ -238,6 +239,9 @@ fn enrich_tool_use_entry(entry: &mut Value) {
if rich {
obj.insert("_category".to_owned(), json!("rich"));
}
if let Some(b) = body {
obj.insert("_body".to_owned(), json!(b));
}
}
// ---------------------------------------------------------------------------
@ -260,6 +264,29 @@ fn is_rich_tool(name: &str) -> bool {
)
}
/// Pre-compute the expandable body text for tools that have one.
///
/// Returns `Some(body)` when the tool has a meaningful multi-line body that
/// the frontend can display in a `<details>` block without tool-specific JS.
/// `None` for tools whose body is computed client-side (diffs, markdown) or
/// that have no body at all.
fn rich_tool_body(name: &str, input: &Value) -> Option<String> {
match name {
// Full bash command — first line is already in `_summary`; the full
// `cmd` (prefixed with `$ `) is the body so multi-line scripts are
// readable on expand.
"mcp__bash__run" => {
let cmd = input.get("cmd").and_then(Value::as_str).unwrap_or("");
if cmd.is_empty() {
None
} else {
Some(format!("$ {cmd}"))
}
}
_ => None,
}
}
fn tool_icon(name: &str) -> &'static str {
// Exact-match table first, then prefix/contains fallbacks.
match name {