Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 47 additions & 1 deletion crates/chat-cli/src/cli/chat/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2031,6 +2031,8 @@ impl ChatSession {
Ok(ChatState::PromptUser {
skip_printing_tools: false,
})
} else if matches!(input.to_lowercase().as_str(), "exit" | "quit") {
return Ok(ChatState::Exit);
Copy link
Contributor

@kensave kensave Oct 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have second thoughts about this one as user could legitimately want to use this as a prompt.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a possibility. It feels like "exit" though would be a stronger indicator than "quit" that the user wanted to end the session. I can update the PR to only look for "exit" if you agree.

For "quit", could we look at data to verify? For example, what's the percentage of chats that have "quit" followed by "/quit" or "/exit"?

} else if let Some(command) = input.strip_prefix("@") {
let input_parts =
shlex::split(command).ok_or(ChatError::Custom("Error splitting prompt command".into()))?;
Expand Down Expand Up @@ -3003,7 +3005,7 @@ impl ChatSession {
return Ok(ChatState::PromptUser {
skip_printing_tools: false,
});
},
},
Err(err) => return Err(err),
}

Expand Down Expand Up @@ -4170,6 +4172,50 @@ mod tests {
assert_eq!(actual, *expected, "expected {} for input {}", expected, input);
}
}

#[tokio::test]
async fn test_exit_quit_commands() {
let mut os = Os::new().await.unwrap();
let agents = get_test_agents(&os).await;
let tool_manager = ToolManager::default();
let tool_config = serde_json::from_str::<HashMap<String, ToolSpec>>(include_str!("tools/tool_index.json"))
.expect("Tools failed to load");

let mut chat_session = ChatSession::new(
&mut os,
std::io::stdout(),
std::io::stderr(),
"fake_conv_id",
agents,
None,
InputSource::new_mock(vec![]),
false,
|| Some(80),
tool_manager,
None,
tool_config,
true,
false,
None,
)
.await
.unwrap();

let test_cases = vec!["exit", "quit", "EXIT", "QUIT", "Exit", "Quit"];

for input in test_cases {
let result = chat_session.handle_input(&mut os, input.to_string()).await;
assert!(result.is_ok(), "handle_input should succeed for input: {}", input);

let chat_state = result.unwrap();
assert!(
matches!(chat_state, ChatState::Exit),
"Expected ChatState::Exit for input '{}', got {:?}",
input,
chat_state
);
}
}
}

// Helper method to save the agent config to file
Expand Down