Talk to Whiskers from a terminal, through the same engine the tablet uses.
whiskers-cli <data-dir> <service-url> [model]
<service-url> is whiskersd (the model, the guard and the memory search all go through it).
Type a line, read the answer. /pic <file.jpg> words shows Whiskers a picture. The log,
memory, chat and pictures go in <data-dir>.
9use std::io::{BufRead, Write};
11use whiskers_core::Image; 12use log::{debug, error, info, warn}; 13use whiskers_engine::{Engine, EngineConfig}; 14 15fn main() { 16 whiskers_core::logging::init("whiskers-cli"); 17 let args: Vec<String> = std::env::args().skip(1).collect(); 18 let [dir, service, rest @ ..] = args.as_slice() else { 19 eprintln!("usage: whiskers-cli <data-dir> <service-url> [model]"); 20 std::process::exit(2); 21 }; 22 info!("whiskers-cli starting, data dir {dir}"); 23 let engine = Engine::new(EngineConfig { 24 data_dir: dir.into(), 25 gateway_url: service.clone(), 26 guard_url: service.clone(), 27 model: rest.first().cloned().unwrap_or_else(|| "claude-haiku-4-5-20251001".to_owned()), 28 system_prompt: None, 29 }) 30 .unwrap_or_else(|e| { 31 error!("cannot start: {e}"); 32 std::process::exit(1); 33 }); 34 if engine.greeting_due() { 35 debug!("greeting"); 36 println!("Whiskers: {}", engine.greet().text); 37 } 38 let stdin = std::io::stdin(); 39 loop { 40 print!("You: "); 41 std::io::stdout().flush().ok(); 42 let mut line = String::new(); 43 if stdin.lock().read_line(&mut line).unwrap_or(0) == 0 { 44 info!("end of input; leaving"); 45 break; 46 } 47 let (words, pictures) = match line.trim().strip_prefix("/pic ") { 48 Some(rest) => { 49 let (path, words) = rest.split_once(' ').unwrap_or((rest, "")); 50 match std::fs::read(path) { 51 Ok(bytes) => (words.to_owned(), vec![Image { media_type: "image/jpeg".into(), bytes }]), 52 Err(e) => { 53 warn!("cannot read the picture {path}: {e}"); 54 println!("(cannot read {path}: {e})"); 55 continue; 56 } 57 } 58 } 59 None => (line.trim().to_owned(), vec![]), 60 }; 61 debug!("heard {} chars, {} picture(s)", words.len(), pictures.len()); 62 let said = engine.hear(&words, pictures); 63 println!("Whiskers [{:?}]: {}", said.outcome, said.text); 64 for fact in engine.reflect() { 65 println!(" (remembers: {} [{}])", fact.text, fact.kind.word()); 66 } 67 } 68}