use anyhow::{Context, Result}; use grokboy_core::{ChatMessage, Config, stream_chat}; use std::io::{self, Write}; use std::process::ExitCode; const SYSTEM: &str = "You are GrokBoy, a concise local coding assistant. Prefer clear, short answers. Traditional Chinese is welcome when the user writes in Chinese."; #[tokio::main] async fn main() -> ExitCode { if let Err(err) = run().await { eprintln!("error: {err:#}"); return ExitCode::FAILURE; } ExitCode::SUCCESS } async fn run() -> Result<()> { let mut args = std::env::args().skip(1); let cmd = args.next().unwrap_or_else(|| "help".into()); match cmd.as_str() { "chat" => cmd_chat().await, "version" | "-V" | "--version" => { println!("grokboy {}", env!("CARGO_PKG_VERSION")); Ok(()) } "help" | "-h" | "--help" | _ => { print_help(); Ok(()) } } } fn print_help() { println!( "\ GrokBoy — minimal local CLI agent (P0: chat) USAGE: grokboy chat Interactive streaming chat grokboy version grokboy help ENV: GROKBOY_API_KEY API key (or XAI_API_KEY / OPENAI_API_KEY) GROKBOY_BASE_URL default https://api.x.ai/v1 GROKBOY_MODEL default grok-4 P0 has no tools yet. P1 will add shell/files + ReAct. " ); } async fn cmd_chat() -> Result<()> { let config = Config::from_env().map_err(anyhow::Error::msg)?; println!( "GrokBoy chat model={} base={}", config.model, config.base_url ); println!("Type a message. Empty line or /exit to quit.\n"); let mut history = vec![ChatMessage::system(SYSTEM)]; let stdin = io::stdin(); let mut stdout = io::stdout(); loop { print!("you> "); stdout.flush().ok(); let mut line = String::new(); if stdin.read_line(&mut line).context("stdin")? == 0 { println!(); break; } let input = line.trim(); if input.is_empty() || input == "/exit" || input == "/quit" { break; } history.push(ChatMessage::user(input)); print!("assistant> "); stdout.flush().ok(); let reply = stream_chat(&config, &history, |delta| { print!("{delta}"); let _ = io::stdout().flush(); }) .await?; println!("\n"); history.push(ChatMessage::assistant(reply)); } Ok(()) }