use clap::Parser;
use crossterm::{
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
ExecutableCommand,
};
use hematite::runtime::{
build_runtime_bundle, run_agent_loop, spawn_runtime_profile_sync, AgentLoopConfig,
AgentLoopRuntime, RuntimeBundle,
};
use hematite::{ui, CliCockpit};
use ratatui::{backend::CrosstermBackend, Terminal};
use std::sync::Arc;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let cwd_ok = std::env::current_dir()
.map(|p| std::fs::read_dir(&p).is_ok())
.unwrap_or(false);
if !cwd_ok {
let home = std::env::var_os("USERPROFILE")
.or_else(|| std::env::var_os("HOME"))
.map(std::path::PathBuf::from);
if let Some(home) = home {
let _ = std::env::set_current_dir(home);
}
}
let cockpit = CliCockpit::parse();
if let Some(path) = cockpit.pdf_extract_helper.as_deref() {
let code = hematite::memory::vein::run_pdf_extract_helper(std::path::Path::new(path));
std::process::exit(code);
}
let local_soul = ui::hatch::generate_soul(cockpit.reroll.clone());
if cockpit.stats {
println!(
"Species: {} | Wisdom: {} | Chaos: {}",
local_soul.species, local_soul.wisdom, local_soul.chaos
);
return Ok(());
}
let RuntimeBundle {
services,
channels,
watcher_guard: _watcher_guard,
} = build_runtime_bundle(
&cockpit,
&local_soul.species,
local_soul.snark,
!cockpit.rusty,
)
.await?;
let hematite::runtime::RuntimeServices {
engine,
gpu_state,
git_state,
voice_manager,
swarm_coordinator,
cancel_token,
} = services;
let hematite::runtime::RuntimeChannels {
specular_rx,
agent_tx,
agent_rx,
swarm_tx,
swarm_rx,
user_input_tx,
user_input_rx,
} = channels;
let tui_cancel_token = cancel_token.clone();
tokio::spawn(run_agent_loop(
AgentLoopRuntime {
user_input_rx,
agent_tx: agent_tx.clone(),
services: hematite::runtime::RuntimeServices {
engine: engine.clone(),
gpu_state: gpu_state.clone(),
git_state: git_state.clone(),
voice_manager: voice_manager.clone(),
swarm_coordinator: swarm_coordinator.clone(),
cancel_token,
},
},
AgentLoopConfig {
yolo: cockpit.yolo,
professional: !cockpit.rusty,
brief: cockpit.brief,
snark: local_soul.snark,
chaos: local_soul.chaos,
soul_personality: local_soul.personality.clone(),
fast_model: cockpit.fast_model.clone(),
think_model: cockpit.think_model.clone(),
},
));
let _runtime_profile_poller = spawn_runtime_profile_sync(engine.clone(), agent_tx.clone());
tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
std::io::stdout().execute(EnterAlternateScreen)?;
std::io::stdout().execute(crossterm::event::EnableMouseCapture)?;
enable_raw_mode()?;
let mut terminal = Terminal::new(CrosstermBackend::new(std::io::stdout()))?;
let _app_result = ui::tui::run_app(
&mut terminal,
specular_rx,
agent_rx,
user_input_tx,
swarm_rx,
swarm_tx,
swarm_coordinator,
Arc::new(std::sync::Mutex::new(std::time::Instant::now())),
cockpit.clone(),
local_soul,
!cockpit.rusty,
gpu_state,
git_state,
tui_cancel_token,
voice_manager,
)
.await;
disable_raw_mode()?;
std::io::stdout().execute(crossterm::event::DisableMouseCapture)?;
std::io::stdout().execute(LeaveAlternateScreen)?;
Ok(())
}