Skip to content

Commit bbca934

Browse files
committed
fix: polish tool call rendering at tui
1 parent 2c6d0bb commit bbca934

5 files changed

Lines changed: 79 additions & 36 deletions

File tree

crates/server/src/persistence.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1089,7 +1089,7 @@ mod tests {
10891089
);
10901090

10911091
assert_eq!(history_items.len(), 2);
1092-
assert_eq!(history_items[0].title, "Ran read");
1092+
assert_eq!(history_items[0].title, "read /tmp/test.txt");
10931093
assert_eq!(history_items[1].title, "read output");
10941094
}
10951095

crates/server/src/projection.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -222,14 +222,8 @@ impl TurnProjector for DefaultProjection {
222222
}
223223

224224
fn summarize_tool_call(tool_name: &str, input: &serde_json::Value) -> String {
225-
match tool_name {
226-
"bash" => input
227-
.get("command")
228-
.and_then(serde_json::Value::as_str)
229-
.map(|command| format!("Ran {command}"))
230-
.unwrap_or_else(|| "Ran shell command".to_string()),
231-
other => format!("Ran {other}"),
232-
}
225+
let cwd = std::env::current_dir().unwrap_or_default();
226+
devo_tools::tool_summary::tool_summary(tool_name, input, &cwd).replacen(": ", " ", 1)
233227
}
234228

235229
fn summarize_tool_result(tool_name: Option<&str>, is_error: bool) -> String {

crates/tui/src/chatwidget.rs

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -276,11 +276,17 @@ impl ChatWidget {
276276
}
277277

278278
fn completed_dot_prefix() -> Line<'static> {
279-
Line::from("• ".cyan())
279+
Line::from(vec![
280+
Span::styled("▌", Style::default().fg(Color::Rgb(120, 220, 160))),
281+
" ".into(),
282+
])
280283
}
281284

282285
fn pending_dot_prefix() -> Line<'static> {
283-
Line::from("• ".cyan())
286+
Line::from(vec![
287+
Span::styled("▌", Style::default().fg(Color::Rgb(110, 200, 255))),
288+
" ".into(),
289+
])
284290
}
285291

286292
fn truncate_display_text(value: &str, max_chars: usize) -> String {
@@ -304,17 +310,14 @@ impl ChatWidget {
304310
}
305311

306312
fn tool_text_style() -> Style {
307-
Style::default().fg(Color::Rgb(176, 176, 176))
313+
Style::default().fg(Color::Rgb(160, 163, 168))
308314
}
309315

310-
fn running_tool_prefix_style() -> Style {
311-
Style::default()
312-
.fg(Color::Rgb(110, 200, 255))
313-
.bold()
314-
.italic()
316+
fn tool_status_running_style() -> Style {
317+
Style::default().fg(Color::Rgb(106, 200, 255)).bold()
315318
}
316319

317-
fn ran_tool_prefix_style() -> Style {
320+
fn tool_status_done_style() -> Style {
318321
Style::default().fg(Color::Rgb(120, 220, 160)).bold()
319322
}
320323

@@ -324,7 +327,7 @@ impl ChatWidget {
324327
.or_else(|| title.strip_prefix("Ran "))
325328
.unwrap_or(title);
326329
Line::from(vec![
327-
Span::styled("Running ", Self::running_tool_prefix_style()),
330+
Span::styled("Running ", Self::tool_status_running_style()),
328331
Span::styled(normalized.to_string(), Self::tool_text_style()),
329332
])
330333
}
@@ -335,17 +338,23 @@ impl ChatWidget {
335338
.or_else(|| title.strip_prefix("Ran "))
336339
.unwrap_or(title);
337340
Line::from(vec![
338-
Span::styled("Ran ", Self::ran_tool_prefix_style()),
341+
Span::styled("Ran ", Self::tool_status_done_style()),
339342
Span::styled(normalized.to_string(), Self::tool_text_style()),
340343
])
341344
}
342345

343346
fn tool_dot_prefix() -> Line<'static> {
344-
Line::from("• ".green())
347+
Line::from(vec![
348+
Span::styled("▌", Style::default().fg(Color::Rgb(120, 220, 160))),
349+
" ".into(),
350+
])
345351
}
346352

347353
fn failed_dot_prefix() -> Line<'static> {
348-
Line::from("• ").red()
354+
Line::from(vec![
355+
Span::styled("▌", Style::default().fg(Color::Rgb(255, 110, 110))),
356+
" ".into(),
357+
])
349358
}
350359

351360
fn dot_prefix(status: DotStatus) -> Line<'static> {
@@ -778,17 +787,10 @@ impl ChatWidget {
778787
WorkerEvent::ToolCall {
779788
tool_use_id,
780789
summary,
781-
detail,
782790
} => {
783791
// Do not commit active streams here — pending tool calls share the
784792
// active viewport alongside reasoning/assistant text.
785-
// If summary already has a key detail (e.g. "read: src/main.rs"),
786-
// skip the redundant JSON preview.
787-
let title = if summary.contains(": ") {
788-
summary
789-
} else {
790-
detail.unwrap_or_else(|| summary.clone())
791-
};
793+
let title = summary;
792794
let tool_call = ActiveToolCall {
793795
tool_use_id: tool_use_id.clone(),
794796
title: title.clone(),

crates/tui/src/events.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,6 @@ pub(crate) enum WorkerEvent {
6969
tool_use_id: String,
7070
/// Human-readable summary line for the tool execution.
7171
summary: String,
72-
/// Optional structured input preview for the tool call.
73-
detail: Option<String>,
7472
},
7573
/// Incremental output delta from a running tool.
7674
ToolOutputDelta {

crates/tui/src/worker.rs

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -815,12 +815,9 @@ async fn run_worker_inner(
815815
&& matches!(payload.item.item_kind, ItemKind::ToolCall)
816816
&& let Ok(payload) = serde_json::from_value::<ToolCallPayload>(payload.item.payload) {
817817
let summary = summarize_tool_call(&payload);
818-
let detail = Some(render_json_preview(&payload.parameters))
819-
.filter(|detail: &String| !detail.is_empty());
820818
let _ = event_tx.send(WorkerEvent::ToolCall {
821819
tool_use_id: payload.tool_call_id,
822820
summary,
823-
detail,
824821
});
825822
}
826823
}
@@ -1258,7 +1255,7 @@ fn fmt_offset_limit(input: &serde_json::Value) -> String {
12581255

12591256
fn summarize_tool_input(tool_name: &str, input: &serde_json::Value) -> String {
12601257
let candidate = match tool_name {
1261-
"bash" => input
1258+
"bash" | "shell_command" | "exec_command" => input
12621259
.get("command")
12631260
.and_then(serde_json::Value::as_str)
12641261
.or_else(|| input.get("cmd").and_then(serde_json::Value::as_str))
@@ -1277,6 +1274,34 @@ fn summarize_tool_input(tool_name: &str, input: &serde_json::Value) -> String {
12771274
.and_then(serde_json::Value::as_str)
12781275
.or_else(|| input.get("filePath").and_then(serde_json::Value::as_str))
12791276
.map(make_path_relative),
1277+
"grep" => {
1278+
let pattern = input
1279+
.get("pattern")
1280+
.and_then(serde_json::Value::as_str)
1281+
.unwrap_or("");
1282+
let path = input
1283+
.get("path")
1284+
.and_then(serde_json::Value::as_str)
1285+
.map(make_path_relative);
1286+
match path {
1287+
Some(p) => Some(format!("'{pattern}' in {p}")),
1288+
None => Some(format!("'{pattern}'")),
1289+
}
1290+
}
1291+
"glob" => {
1292+
let pattern = input
1293+
.get("pattern")
1294+
.and_then(serde_json::Value::as_str)
1295+
.unwrap_or("");
1296+
let path = input
1297+
.get("path")
1298+
.and_then(serde_json::Value::as_str)
1299+
.map(make_path_relative);
1300+
match path {
1301+
Some(p) => Some(format!("{pattern} in {p}")),
1302+
None => Some(pattern.to_string()),
1303+
}
1304+
}
12801305
"webfetch" | "websearch" => input
12811306
.get("url")
12821307
.and_then(serde_json::Value::as_str)
@@ -1287,6 +1312,30 @@ fn summarize_tool_input(tool_name: &str, input: &serde_json::Value) -> String {
12871312
.and_then(serde_json::Value::as_str)
12881313
.map(|s| s.to_string())
12891314
}),
1315+
"lsp" => {
1316+
let path = input
1317+
.get("filePath")
1318+
.and_then(serde_json::Value::as_str)
1319+
.map(make_path_relative);
1320+
let line = input.get("line").and_then(|v| v.as_i64());
1321+
let col = input.get("character").and_then(|v| v.as_i64());
1322+
match (path, line, col) {
1323+
(Some(p), Some(l), Some(c)) => Some(format!("{p}:{l}:{c}")),
1324+
(Some(p), Some(l), None) => Some(format!("{p}:{l}")),
1325+
(Some(p), None, _) => Some(p),
1326+
_ => None,
1327+
}
1328+
}
1329+
"task" => input
1330+
.get("description")
1331+
.and_then(serde_json::Value::as_str)
1332+
.map(|s| s.to_string()),
1333+
"question" => None,
1334+
"todowrite" => None,
1335+
"skill" => input
1336+
.get("name")
1337+
.and_then(serde_json::Value::as_str)
1338+
.map(|s| s.to_string()),
12901339
_ => None,
12911340
};
12921341

0 commit comments

Comments
 (0)