Skip to content

Commit b9100eb

Browse files
fix: slash command handling for file paths (#356)
1 parent 7ce50bc commit b9100eb

File tree

1 file changed

+42
-21
lines changed
  • crates/chat-cli/src/cli/chat

1 file changed

+42
-21
lines changed

crates/chat-cli/src/cli/chat/mod.rs

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1248,19 +1248,8 @@ impl ChatSession {
12481248
let input = user_input.trim();
12491249

12501250
// handle image path
1251-
if input.starts_with('/') {
1252-
if let Some(after_slash) = input.strip_prefix('/') {
1253-
let looks_like_path = after_slash.contains(MAIN_SEPARATOR)
1254-
|| after_slash.contains('/')
1255-
|| after_slash.contains('\\')
1256-
|| after_slash.contains('.');
1257-
1258-
if looks_like_path {
1259-
return Ok(ChatState::HandleInput {
1260-
input: after_slash.to_string(),
1261-
});
1262-
}
1263-
}
1251+
if let Some(chat_state) = does_input_reference_file(input) {
1252+
return Ok(chat_state);
12641253
}
12651254
if let Some(mut args) = input.strip_prefix("/").and_then(shlex::split) {
12661255
// Required for printing errors correctly.
@@ -1648,15 +1637,11 @@ impl ChatSession {
16481637
tool_name_being_recvd = Some(name);
16491638
},
16501639
parser::ResponseEvent::AssistantText(text) => {
1651-
// Add Q response prefix before the first assistant text
1640+
// Add Q response prefix before the first assistant text.
1641+
// This must be markdown - using a code tick, which is printed
1642+
// as green.
16521643
if !response_prefix_printed && !text.trim().is_empty() {
1653-
// Print the Q response prefix with cyan color
1654-
execute!(
1655-
self.stdout,
1656-
style::SetForegroundColor(Color::Cyan),
1657-
style::Print("> "),
1658-
style::SetForegroundColor(Color::Reset)
1659-
)?;
1644+
buf.push_str("`>` ");
16601645
response_prefix_printed = true;
16611646
}
16621647
buf.push_str(&text);
@@ -2241,6 +2226,25 @@ where
22412226
result
22422227
}
22432228

2229+
/// Checks if an input may be referencing a file and should not be handled as a typical slash
2230+
/// command. If true, then return [Option::Some<ChatState>], otherwise [Option::None].
2231+
fn does_input_reference_file(input: &str) -> Option<ChatState> {
2232+
let after_slash = input.strip_prefix("/")?;
2233+
2234+
if let Some(first) = shlex::split(after_slash).unwrap_or_default().first() {
2235+
let looks_like_path =
2236+
first.contains(MAIN_SEPARATOR) || first.contains('/') || first.contains('\\') || first.contains('.');
2237+
2238+
if looks_like_path {
2239+
return Some(ChatState::HandleInput {
2240+
input: after_slash.to_string(),
2241+
});
2242+
}
2243+
}
2244+
2245+
None
2246+
}
2247+
22442248
#[cfg(test)]
22452249
mod tests {
22462250
use super::*;
@@ -2653,4 +2657,21 @@ mod tests {
26532657
.await
26542658
.unwrap();
26552659
}
2660+
2661+
#[test]
2662+
fn test_does_input_reference_file() {
2663+
let tests = &[
2664+
(
2665+
r"/Users/user/Desktop/Screenshot\ 2025-06-30\ at\ 2.13.34 PM.png read this image for me",
2666+
true,
2667+
),
2668+
("/path/to/file.json", true),
2669+
("/save output.json", false),
2670+
("~/does/not/start/with/slash", false),
2671+
];
2672+
for (input, expected) in tests {
2673+
let actual = does_input_reference_file(input).is_some();
2674+
assert_eq!(actual, *expected, "expected {} for input {}", expected, input);
2675+
}
2676+
}
26562677
}

0 commit comments

Comments
 (0)