codetether_agent/tui/app/
text.rs1pub fn truncate_preview(text: &str, max_chars: usize) -> String {
2 let mut chars = text.chars();
3 let preview: String = chars.by_ref().take(max_chars).collect();
4 if chars.next().is_some() {
5 format!("{preview}…")
6 } else {
7 preview
8 }
9}
10
11pub fn normalize_slash_command(input: &str) -> String {
12 let trimmed = input.trim();
13 let mut parts = trimmed.splitn(2, char::is_whitespace);
14 let command = parts.next().unwrap_or("");
15 let args = parts.next().unwrap_or("").trim();
16
17 let normalized = match command {
18 "/h" | "/?" => "/help".to_string(),
19 "/s" => "/sessions".to_string(),
20 "/w" => "/swarm".to_string(),
21 "/r" => "/ralph".to_string(),
22 "/b" | "/buslog" | "/logs" => "/bus".to_string(),
23 "/p" => "/protocol".to_string(),
24 "/m" => "/model".to_string(),
25 "/set" => "/settings".to_string(),
26 "/sym" => "/symbols".to_string(),
27 other => other.to_string(),
28 };
29
30 if args.is_empty() {
31 normalized
32 } else {
33 format!("{normalized} {args}")
34 }
35}
36
37pub fn normalize_easy_command(input: &str) -> String {
40 let trimmed = input.trim();
41 let mut parts = trimmed.splitn(2, char::is_whitespace);
42 let command = parts.next().unwrap_or("");
43 let args = parts.next().unwrap_or("").trim();
44
45 let normalized = match command {
46 "/add" => {
47 if args.is_empty() {
48 "/spawn".to_string()
49 } else {
50 format!("/spawn {args}")
51 }
52 }
53 "/talk" | "/say" => {
54 if args.is_empty() {
55 "/agent".to_string()
56 } else {
57 format!("/agent {args}")
58 }
59 }
60 "/list" | "/ls" => "/agents".to_string(),
61 "/remove" | "/rm" => {
62 if args.is_empty() {
63 "/kill".to_string()
64 } else {
65 format!("/kill {args}")
66 }
67 }
68 "/focus" => {
69 if args.is_empty() {
70 "/agent".to_string()
71 } else {
72 format!("/agent {}", args.trim_start_matches('@'))
73 }
74 }
75 "/home" | "/main" => "/chat".to_string(),
76 _ => return input.to_string(),
77 };
78
79 normalized
80}
81
82pub fn command_with_optional_args<'a>(input: &'a str, command: &str) -> Option<&'a str> {
83 let trimmed = input.trim();
84 let rest = trimmed.strip_prefix(command)?;
85
86 if rest.is_empty() {
87 return Some("");
88 }
89
90 let first = rest.chars().next()?;
91 if first.is_whitespace() {
92 Some(rest.trim())
93 } else {
94 None
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use super::{command_with_optional_args, normalize_easy_command, normalize_slash_command};
101
102 #[test]
103 fn normalize_slash_command_preserves_arguments() {
104 assert_eq!(normalize_slash_command("/p events"), "/protocol events");
105 }
106
107 #[test]
108 fn command_with_optional_args_avoids_prefix_collisions() {
109 assert_eq!(command_with_optional_args("/filed", "/file"), None);
110 }
111
112 #[test]
113 fn normalize_easy_command_add_alias() {
114 assert_eq!(normalize_easy_command("/add coder"), "/spawn coder");
115 assert_eq!(normalize_easy_command("/add"), "/spawn");
116 }
117
118 #[test]
119 fn normalize_easy_command_talk_alias() {
120 assert_eq!(
121 normalize_easy_command("/talk coder hello"),
122 "/agent coder hello"
123 );
124 assert_eq!(
125 normalize_easy_command("/say coder hello"),
126 "/agent coder hello"
127 );
128 assert_eq!(normalize_easy_command("/talk"), "/agent");
129 }
130
131 #[test]
132 fn normalize_easy_command_list_alias() {
133 assert_eq!(normalize_easy_command("/list"), "/agents");
134 assert_eq!(normalize_easy_command("/ls"), "/agents");
135 }
136
137 #[test]
138 fn normalize_easy_command_remove_alias() {
139 assert_eq!(normalize_easy_command("/remove coder"), "/kill coder");
140 assert_eq!(normalize_easy_command("/rm coder"), "/kill coder");
141 assert_eq!(normalize_easy_command("/remove"), "/kill");
142 }
143
144 #[test]
145 fn normalize_easy_command_focus_alias() {
146 assert_eq!(normalize_easy_command("/focus coder"), "/agent coder");
147 assert_eq!(normalize_easy_command("/focus @coder"), "/agent coder");
148 assert_eq!(normalize_easy_command("/focus"), "/agent");
149 }
150
151 #[test]
152 fn normalize_easy_command_home_alias() {
153 assert_eq!(normalize_easy_command("/home"), "/chat");
154 assert_eq!(normalize_easy_command("/main"), "/chat");
155 }
156
157 #[test]
158 fn normalize_easy_command_passthrough() {
159 assert_eq!(normalize_easy_command("/help"), "/help");
160 assert_eq!(normalize_easy_command("/spawn coder"), "/spawn coder");
161 assert_eq!(normalize_easy_command("hello world"), "hello world");
162 }
163}