mod common;
mod config;
mod llm;
mod session;
mod ui;
use crate::{llm::Llm, session::Session, ui::Ui};
use anyhow::Result;
use crossterm::{
event::{KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags},
execute,
};
use std::io::stdout;
use tracing_appender::non_blocking::WorkerGuard;
use tracing_appender::rolling::{RollingFileAppender, Rotation};
use tracing_subscriber::{EnvFilter, layer::SubscriberExt, util::SubscriberInitExt};
#[tokio::main]
async fn main() -> Result<()> {
if std::env::args().any(|arg| arg == "--version" || arg == "-V") {
println!("{} {}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"));
return Ok(());
}
let _tracing_guard = init_tracing();
tracing::info!("monothink starting");
let base_dir = std::env::current_dir().expect("Cannot get current directory");
let config = config::load_config()?;
let terminal = ratatui::init();
let mut stdout = stdout();
execute!(
stdout,
PushKeyboardEnhancementFlags(KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES)
)?;
let (session_tx, session_rx) = tokio::sync::mpsc::channel(128);
let (ui_tx, ui_rx) = tokio::sync::mpsc::channel(128);
let (llm_tx, llm_rx) = tokio::sync::mpsc::channel(32);
let mut ui = Ui::new(session_tx.clone(), ui_rx, config.symbols.clone());
let mut session = Session::new(ui_tx, llm_tx, session_rx, config.clone(), base_dir.clone());
let mut llm = Llm::new(session_tx, llm_rx, config.general, config.tools, base_dir).await;
tokio::spawn(async move {
session.run().await;
});
tokio::spawn(async move {
llm.run().await;
});
let app_result = ui.run(terminal).await;
execute!(stdout, PopKeyboardEnhancementFlags)?;
ratatui::restore();
app_result
}
fn init_tracing() -> WorkerGuard {
let project_dirs = directories::ProjectDirs::from("org", "codemonk", "monothink")
.expect("Cannot locate project directories");
let log_dir = project_dirs
.state_dir()
.unwrap_or_else(|| project_dirs.data_dir())
.join("logs");
std::fs::create_dir_all(&log_dir).expect("Cannot create log directory");
let file_appender = RollingFileAppender::builder()
.rotation(Rotation::DAILY)
.filename_prefix("monothink.log")
.max_log_files(7)
.build(&log_dir)
.expect("failed to initialize rolling file appender");
let (writer, guard) = tracing_appender::non_blocking(file_appender);
let filter =
EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("monothink=info"));
tracing_subscriber::registry()
.with(
tracing_subscriber::fmt::layer()
.with_writer(writer)
.with_ansi(false),
)
.with(filter)
.init();
guard
}