Skip to main content

steer_tui/tui/widgets/formatters/
mod.rs

1use crate::tui::theme::Theme;
2use ratatui::text::Line;
3use serde_json::Value;
4use std::collections::HashMap;
5use std::sync::LazyLock;
6use steer_grpc::client_api::ToolResult;
7
8pub mod astgrep;
9pub mod bash;
10pub mod default;
11pub mod dispatch_agent;
12pub mod edit;
13pub mod external;
14pub mod fetch;
15pub mod glob;
16pub mod grep;
17pub mod helpers;
18pub mod ls;
19pub mod replace;
20pub mod todo;
21pub mod view;
22
23// Import the formatters
24use self::astgrep::AstGrepFormatter;
25use self::bash::BashFormatter;
26use self::default::DefaultFormatter;
27use self::dispatch_agent::DispatchAgentFormatter;
28use self::edit::EditFormatter;
29use self::external::ExternalFormatter;
30use self::fetch::FetchFormatter;
31use self::glob::GlobFormatter;
32use self::grep::GrepFormatter;
33use self::ls::LsFormatter;
34use self::replace::ReplaceFormatter;
35
36use self::todo::{TodoReadFormatter, TodoWriteFormatter};
37use self::view::ViewFormatter;
38
39/// Trait for formatting tool calls and results
40pub trait ToolFormatter: Send + Sync {
41    /// Format tool call and result in compact mode (single line summary)
42    fn compact(
43        &self,
44        params: &Value,
45        result: &Option<ToolResult>,
46        wrap_width: usize,
47        theme: &Theme,
48    ) -> Vec<Line<'static>>;
49
50    /// Format tool call and result in detailed mode (full parameters and output)
51    fn detailed(
52        &self,
53        params: &Value,
54        result: &Option<ToolResult>,
55        wrap_width: usize,
56        theme: &Theme,
57    ) -> Vec<Line<'static>>;
58
59    /// Format tool call for approval request (shows what the tool will do)
60    fn approval(&self, params: &Value, wrap_width: usize, theme: &Theme) -> Vec<Line<'static>> {
61        // Default implementation just wraps compact() without result
62        self.compact(params, &None, wrap_width, theme)
63    }
64}
65
66static FORMATTERS: LazyLock<HashMap<&'static str, Box<dyn ToolFormatter>>> = LazyLock::new(|| {
67    use steer_tools::tools::{
68        AST_GREP_TOOL_NAME, BASH_TOOL_NAME, DISPATCH_AGENT_TOOL_NAME, EDIT_TOOL_NAME,
69        FETCH_TOOL_NAME, GLOB_TOOL_NAME, GREP_TOOL_NAME, LS_TOOL_NAME, REPLACE_TOOL_NAME,
70        TODO_READ_TOOL_NAME, TODO_WRITE_TOOL_NAME, VIEW_TOOL_NAME, edit,
71    };
72
73    let mut map: HashMap<&'static str, Box<dyn ToolFormatter>> = HashMap::new();
74
75    map.insert(BASH_TOOL_NAME, Box::new(BashFormatter));
76    map.insert(GREP_TOOL_NAME, Box::new(GrepFormatter));
77    map.insert(LS_TOOL_NAME, Box::new(LsFormatter));
78    map.insert(GLOB_TOOL_NAME, Box::new(GlobFormatter));
79    map.insert(VIEW_TOOL_NAME, Box::new(ViewFormatter));
80    map.insert(EDIT_TOOL_NAME, Box::new(EditFormatter));
81    map.insert(
82        edit::multi_edit::MULTI_EDIT_TOOL_NAME,
83        Box::new(EditFormatter),
84    );
85    map.insert(REPLACE_TOOL_NAME, Box::new(ReplaceFormatter));
86    map.insert(TODO_READ_TOOL_NAME, Box::new(TodoReadFormatter));
87    map.insert(TODO_WRITE_TOOL_NAME, Box::new(TodoWriteFormatter));
88    map.insert(AST_GREP_TOOL_NAME, Box::new(AstGrepFormatter));
89    map.insert(FETCH_TOOL_NAME, Box::new(FetchFormatter));
90    map.insert(DISPATCH_AGENT_TOOL_NAME, Box::new(DispatchAgentFormatter));
91
92    map
93});
94
95static DEFAULT_FORMATTER: LazyLock<Box<dyn ToolFormatter>> =
96    LazyLock::new(|| Box::new(DefaultFormatter));
97static EXTERNAL_FORMATTER: LazyLock<Box<dyn ToolFormatter>> =
98    LazyLock::new(|| Box::new(ExternalFormatter));
99
100pub fn get_formatter(tool_name: &str) -> &'static dyn ToolFormatter {
101    if let Some(fmt) = FORMATTERS.get(tool_name) {
102        fmt.as_ref()
103    } else if tool_name.starts_with("mcp__") {
104        EXTERNAL_FORMATTER.as_ref()
105    } else {
106        DEFAULT_FORMATTER.as_ref()
107    }
108}