agent_core/tui/
commands.rs1use super::widgets::SlashCommandTrait;
7
8#[derive(Debug, Clone)]
10pub struct SlashCommand {
11 pub name: &'static str,
13 pub description: &'static str,
15}
16
17impl SlashCommandTrait for SlashCommand {
18 fn name(&self) -> &str {
19 self.name
20 }
21
22 fn description(&self) -> &str {
23 self.description
24 }
25}
26
27impl SlashCommandTrait for &SlashCommand {
28 fn name(&self) -> &str {
29 self.name
30 }
31
32 fn description(&self) -> &str {
33 self.description
34 }
35}
36
37pub static DEFAULT_COMMANDS: &[SlashCommand] = &[
39 SlashCommand {
40 name: "help",
41 description: "Show available commands and usage",
42 },
43 SlashCommand {
44 name: "clear",
45 description: "Clear the conversation history",
46 },
47 SlashCommand {
48 name: "status",
49 description: "Show current session status",
50 },
51 SlashCommand {
52 name: "new-session",
53 description: "Create a new LLM session",
54 },
55 SlashCommand {
56 name: "quit",
57 description: "Exit the application",
58 },
59 SlashCommand {
60 name: "version",
61 description: "Show application version",
62 },
63 SlashCommand {
64 name: "themes",
65 description: "Open theme picker to change colors",
66 },
67 SlashCommand {
68 name: "compact",
69 description: "Compact the conversation history",
70 },
71 SlashCommand {
72 name: "sessions",
73 description: "View and switch between sessions",
74 },
75];
76
77pub fn get_default_commands() -> &'static [SlashCommand] {
79 DEFAULT_COMMANDS
80}
81
82pub fn filter_commands<'a>(
85 commands: &'a [SlashCommand],
86 input: &str,
87) -> Vec<&'a SlashCommand> {
88 let search_term = input.trim_start_matches('/');
89
90 commands
91 .iter()
92 .filter(|cmd| cmd.name.starts_with(search_term))
93 .collect()
94}
95
96pub fn get_command_by_name<'a>(
98 commands: &'a [SlashCommand],
99 name: &str,
100) -> Option<&'a SlashCommand> {
101 let name = name.trim_start_matches('/');
102 commands.iter().find(|cmd| cmd.name == name)
103}
104
105pub fn is_slash_command(input: &str) -> bool {
107 input.starts_with('/')
108}
109
110pub fn parse_command(input: &str) -> Option<(&str, &str)> {
112 if !is_slash_command(input) {
113 return None;
114 }
115
116 let trimmed = input.trim_start_matches('/');
117 let mut parts = trimmed.splitn(2, ' ');
118 let name = parts.next()?;
119 let args = parts.next().unwrap_or("");
120
121 Some((name, args))
122}
123
124pub fn generate_help_message(commands: &[SlashCommand]) -> String {
126 let mut help = String::from("Available commands:\n\n");
127
128 for cmd in commands {
129 help.push_str(&format!("/{} - {}\n", cmd.name, cmd.description));
130 }
131
132 help
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138
139 #[test]
140 fn test_filter_commands() {
141 let matches = filter_commands(DEFAULT_COMMANDS, "/he");
142 assert_eq!(matches.len(), 1);
143 assert_eq!(matches[0].name, "help");
144
145 let matches = filter_commands(DEFAULT_COMMANDS, "/");
146 assert_eq!(matches.len(), DEFAULT_COMMANDS.len());
147 }
148
149 #[test]
150 fn test_get_command_by_name() {
151 assert!(get_command_by_name(DEFAULT_COMMANDS, "help").is_some());
152 assert!(get_command_by_name(DEFAULT_COMMANDS, "/help").is_some());
153 assert!(get_command_by_name(DEFAULT_COMMANDS, "unknown").is_none());
154 }
155
156 #[test]
157 fn test_parse_command() {
158 assert_eq!(parse_command("/help"), Some(("help", "")));
159 assert_eq!(
160 parse_command("/new-session arg"),
161 Some(("new-session", "arg"))
162 );
163 assert_eq!(parse_command("not a command"), None);
164 }
165
166 #[test]
167 fn test_is_slash_command() {
168 assert!(is_slash_command("/help"));
169 assert!(!is_slash_command("hello"));
170 }
171}