Files
jarvis/crates/jarvis-cli/src/main.rs
2026-01-05 02:23:32 +05:00

194 lines
6.0 KiB
Rust

use std::{io::{self, Write}, sync::Arc};
use parking_lot::RwLock;
use jarvis_core::{COMMANDS_LIST, DB, JCommandsList, commands, config, db, intent};
fn print_help() {
println!("
--## Jarvis CLI - Testing Tool ##--
Commands:
classify <text> - Test intent classification
execute <text> - Simulate voice input and execute command
list - List all loaded commands
phrases - List all training phrases
hash - Show commands hash
reload - Reload commands from disk
help - Show this help
exit - Exit the CLI
");
}
fn list_commands(commands: &Vec<JCommandsList>) {
println!("\n[ Loaded Commands ]");
for cmd_list in commands {
println!(" 📁 {}", cmd_list.path.display());
for cmd in &cmd_list.commands {
println!(" ├─ id: {}", cmd.id);
println!(" ├─ action: {}", cmd.action);
println!(" └─ phrases: {} total", cmd.phrases.len());
}
}
println!();
}
fn list_phrases(commands: &Vec<JCommandsList>) {
println!("\n[ Training Phrases ]");
for cmd_list in commands {
for cmd in &cmd_list.commands {
println!(" [{}]", cmd.id);
for phrase in &cmd.phrases {
println!(" - {}", phrase);
}
}
}
println!();
}
async fn classify_text(text: &str) {
match intent::classify(text).await {
Some((intent_id, confidence)) => {
println!(" ✓ Intent: {} (confidence: {:.2}%)", intent_id, confidence * 100.0);
}
None => {
println!(" ✗ No intent matched (below threshold)");
}
}
}
async fn execute_text(commands: &'static Vec<JCommandsList>, text: &str) {
// try intent classification first
if let Some((intent_id, confidence)) = intent::classify(text).await {
println!(" Intent: {} (confidence: {:.2}%)", intent_id, confidence * 100.0);
if let Some((cmd_path, cmd)) = intent::get_command_by_intent(commands, &intent_id) {
println!(" Command: {:?}", cmd_path);
println!(" Action: {}", cmd.action);
println!(" Executing...");
match commands::execute_command(cmd_path, cmd) {
Ok(chain) => println!(" ✓ Success (chain: {})", chain),
Err(e) => println!(" ✗ Error: {}", e),
}
return;
}
}
// fallback to levenshtein
println!(" Intent not matched, trying levenshtein fallback...");
if let Some((cmd_path, cmd)) = commands::fetch_command(text, commands) {
println!(" Command: {:?}", cmd_path);
println!(" Action: {}", cmd.action);
println!(" Executing...");
match commands::execute_command(cmd_path, cmd) {
Ok(chain) => println!(" ✓ Success (chain: {})", chain),
Err(e) => println!(" ✗ Error: {}", e),
}
} else {
println!(" ✗ No command matched");
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// init logging
env_logger::Builder::from_env(
env_logger::Env::default().default_filter_or("info")
).init();
println!("Jarvis CLI v{}", config::APP_VERSION.unwrap_or("unknown"));
// init dirs
config::init_dirs()?;
// parse commands
println!("\n[*] Loading commands...");
let cmds = match commands::parse_commands() {
Ok(c) => {
println!(" Loaded {} command groups", c.len());
c
}
Err(e) => {
println!(" Warning: {}", e);
Vec::new()
}
};
COMMANDS_LIST.set(cmds).expect("Failed to set commands list");
// init intent classifier
println!("[*] Initializing intent classifier...");
match intent::init(COMMANDS_LIST.get().unwrap()).await {
Ok(_) => println!(" Intent classifier ready"),
Err(e) => println!(" Warning: {}", e),
}
print_help();
// init db
DB.set(Arc::new(RwLock::new(db::init_settings())))
.expect("DB already initialized");
// init sound
println!("[*] Initializing audio...");
if let Err(e) = jarvis_core::audio::init() {
println!(" Warning: Audio init failed: {:?}", e);
}
// REPL loop
let mut input = String::new();
loop {
print!("jarvis> ");
io::stdout().flush()?;
input.clear();
io::stdin().read_line(&mut input)?;
let input = input.trim();
if input.is_empty() {
continue;
}
let parts: Vec<&str> = input.splitn(2, ' ').collect();
let cmd = parts[0];
let arg = parts.get(1).map(|s| *s).unwrap_or("");
match cmd {
"exit" | "quit" | "q" => {
println!("Bye!");
break;
}
"help" | "h" | "?" => print_help(),
"list" | "ls" => list_commands(COMMANDS_LIST.get().unwrap()),
"phrases" => list_phrases(COMMANDS_LIST.get().unwrap()),
"hash" => {
let hash = commands::commands_hash(COMMANDS_LIST.get().unwrap());
println!(" Commands hash: {}", hash);
}
"classify" | "c" => {
if arg.is_empty() {
println!(" Usage: classify <text>");
} else {
classify_text(arg).await;
}
}
"execute" | "exec" | "e" => {
if arg.is_empty() {
println!(" Usage: execute <text>");
} else {
execute_text(COMMANDS_LIST.get().unwrap(), arg).await;
}
}
"reload" => {
println!(" Note: Reload requires app restart (statics can't be reset)");
}
_ => {
// treat unknown commands as text to classify
classify_text(input).await;
}
}
}
Ok(())
}