mod app;
mod chat;
mod command;
mod extract;
mod factcheck;
mod facts_tree;
mod focus;
mod insert;
mod llm;
mod picker;
mod rag;
mod render;
mod thread;
mod verify;
pub(crate) use focus::Focus;
use std::io;
use std::path::Path;
use anyhow::Result;
use crossterm::execute;
use crossterm::terminal::{
EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode,
};
use ratatui::Terminal;
use ratatui::backend::CrosstermBackend;
use crate::config::Config;
use crate::project::ProjectLayout;
use crate::store::Store;
use crate::store::hierarchy::Hierarchy;
use app::ResearchApp;
pub(crate) struct ResearchInvocation {
pub thread: Option<String>,
pub list_threads: bool,
pub export_thread: Option<String>,
pub format: Option<String>,
pub out: Option<String>,
}
pub(crate) fn run(project: &Path, inv: ResearchInvocation) -> Result<()> {
let layout = ProjectLayout::new(project);
layout.require_initialized().map_err(anyhow::Error::from)?;
let cfg = Config::load_layered(&layout.config_path()).map_err(anyhow::Error::from)?;
if inv.list_threads {
return app::list_threads_cli(&layout, inv.format.as_deref());
}
if let Some(name) = inv.export_thread.as_deref() {
return app::export_thread_cli(&layout, name, inv.format.as_deref(), inv.out.as_deref());
}
let store = Store::open(layout.clone(), &cfg).map_err(anyhow::Error::from)?;
let hierarchy = Hierarchy::load(&store).map_err(anyhow::Error::from)?;
launch_tui(layout, cfg, store, hierarchy, inv.thread)
}
fn launch_tui(
layout: ProjectLayout,
cfg: Config,
store: Store,
hierarchy: Hierarchy,
thread: Option<String>,
) -> Result<()> {
crate::crash::set_terminal_restore(Some(Box::new(|| {
let _ = disable_raw_mode();
let _ = execute!(io::stdout(), LeaveAlternateScreen);
})));
enable_raw_mode()?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen)?;
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
let result = match picker::resolve_thread(&mut terminal, &layout, thread)? {
Some(name) => {
let mut app = ResearchApp::new(layout, cfg, store, hierarchy, Some(name))?;
app.run(&mut terminal)
}
None => Ok(()),
};
disable_raw_mode()?;
execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
terminal.show_cursor()?;
crate::crash::set_terminal_restore(None);
result
}