use std::io::{self, Write};
use anyhow::Result;
use clap::Parser;
use crossterm::{
event::{self, Event, KeyCode, KeyModifiers},
terminal::{disable_raw_mode, enable_raw_mode},
};
#[derive(Parser)]
pub struct ChatCommand {
#[arg(long)]
pub model: Option<String>,
}
impl ChatCommand {
pub async fn execute(self) -> Result<()> {
let ai_info = crate::utils::preflight::check_ai_credentials(self.model.as_deref())?;
eprintln!(
"Connected to {} (model: {})",
ai_info.provider, ai_info.model
);
eprintln!("Enter to send, Shift+Enter for newline, Ctrl+D to exit.\n");
let client = crate::claude::create_default_claude_client(self.model, None)?;
chat_loop(&client).await
}
}
async fn chat_loop(client: &crate::claude::client::ClaudeClient) -> Result<()> {
let system_prompt = "You are a helpful assistant.";
loop {
let input = match read_user_input() {
Ok(Some(text)) => text,
Ok(None) => {
eprintln!("\nGoodbye!");
break;
}
Err(e) => {
eprintln!("\nInput error: {e}");
break;
}
};
let trimmed = input.trim();
if trimmed.is_empty() {
continue;
}
let response = client.send_message(system_prompt, trimmed).await?;
println!("{response}\n");
}
Ok(())
}
struct RawModeGuard;
impl Drop for RawModeGuard {
fn drop(&mut self) {
let _ = disable_raw_mode();
}
}
fn read_user_input() -> Result<Option<String>> {
eprint!("> ");
io::stderr().flush()?;
enable_raw_mode()?;
let _guard = RawModeGuard;
let mut buffer = String::new();
loop {
if let Event::Key(key_event) = event::read()? {
match key_event.code {
KeyCode::Enter => {
if key_event.modifiers.contains(KeyModifiers::SHIFT) {
buffer.push('\n');
eprint!("\r\n... ");
io::stderr().flush()?;
} else {
eprint!("\r\n");
io::stderr().flush()?;
return Ok(Some(buffer));
}
}
KeyCode::Char('d') if key_event.modifiers.contains(KeyModifiers::CONTROL) => {
if buffer.is_empty() {
return Ok(None);
}
eprint!("\r\n");
io::stderr().flush()?;
return Ok(Some(buffer));
}
KeyCode::Char('c') if key_event.modifiers.contains(KeyModifiers::CONTROL) => {
return Ok(None);
}
KeyCode::Char(c) => {
buffer.push(c);
eprint!("{c}");
io::stderr().flush()?;
}
KeyCode::Backspace if buffer.pop().is_some() => {
eprint!("\x08 \x08");
io::stderr().flush()?;
}
_ => {}
}
}
}
}