Skip to main content

vtcode_core/core/agent/
display.rs

1use crate::config::constants::tools;
2use crate::tools::tool_intent;
3use serde_json::Value;
4
5/// Format tool result for display in the TUI.
6/// Limits verbose output from web_fetch to avoid overwhelming the terminal.
7#[inline]
8pub fn format_tool_result_for_display(tool_name: &str, result: &Value) -> String {
9    let display_tool_name =
10        tool_intent::canonical_unified_exec_tool_name(tool_name).unwrap_or(tool_name);
11    let is_command_session_tool = display_tool_name == tools::UNIFIED_EXEC;
12
13    if display_tool_name == tools::UNIFIED_SEARCH {
14        return format_unified_search_result_for_display(display_tool_name, result);
15    }
16
17    if is_command_session_tool {
18        // Extract errors + 2 context lines for build output
19        if let Some(obj) = result.as_object()
20            && let Some(stdout) = obj
21                .get("stdout")
22                .or_else(|| obj.get("output"))
23                .and_then(|v| v.as_str())
24            && stdout.len() > 2000
25            && (stdout.contains("error") || stdout.contains("Error"))
26        {
27            let lines: Vec<&str> = stdout.lines().collect();
28            let mut extracted = Vec::new();
29            for (i, line) in lines.iter().enumerate() {
30                if line.to_lowercase().contains("error") {
31                    let start = i.saturating_sub(2);
32                    let end = (i + 3).min(lines.len());
33                    extracted.extend_from_slice(&lines[start..end]);
34                    extracted.push("...");
35                }
36            }
37            if !extracted.is_empty() {
38                let compact = serde_json::json!({
39                    "exit_code": obj.get("exit_code"),
40                    "errors": extracted.join("\n"),
41                    "note": "Showing error lines + context only"
42                });
43                return format!("Tool {} result: {}", display_tool_name, compact);
44            }
45        }
46        return format!("Tool {} result: {}", display_tool_name, result);
47    }
48
49    format!("Tool {} result: {}", display_tool_name, result)
50}
51
52fn format_unified_search_result_for_display(tool_name: &str, result: &Value) -> String {
53    if let Some(obj) = result.as_object()
54        && obj.get("url").is_some()
55        && obj.get("content").is_some()
56    {
57        if obj.contains_key("error") {
58            return format!(
59                "Tool {} result: {{\"error\": {}}}",
60                tool_name,
61                obj.get("error")
62                    .map(|v| v.to_string())
63                    .unwrap_or_else(|| "unknown error".into())
64            );
65        }
66
67        let status = serde_json::json!({
68            "status": "fetched",
69            "content_length": obj.get("content_length"),
70            "truncated": obj.get("truncated"),
71            "url": obj.get("url")
72        });
73        return format!("Tool {} result: {}", tool_name, status);
74    }
75
76    if let Some(obj) = result.as_object()
77        && let Some(matches) = obj.get("matches").and_then(|v| v.as_array())
78        && matches.len() > 5
79    {
80        let truncated: Vec<_> = matches.iter().take(5).cloned().collect();
81        let overflow = matches.len().saturating_sub(5);
82        let summary = serde_json::json!({
83            "matches": truncated,
84            "overflow": format!("[+{} more matches]", overflow),
85            "total": matches.len()
86        });
87        return format!("Tool {} result: {}", tool_name, summary);
88    }
89
90    if let Some(obj) = result.as_object()
91        && let Some(files) = obj.get("files").and_then(|v| v.as_array())
92        && files.len() > 50
93    {
94        let sample: Vec<_> = files.iter().take(5).cloned().collect();
95        let summary = serde_json::json!({
96            "total_files": files.len(),
97            "sample": sample,
98            "note": format!("Showing 5 of {} files", files.len())
99        });
100        return format!("Tool {} result: {}", tool_name, summary);
101    }
102
103    format!("Tool {} result: {}", tool_name, result)
104}