Skip to main content

wisp/
lib.rs

1// Items reachable only through the `testing` harness look dead and
2// unreachable in default builds; the all-features lint gate still checks both.
3#![cfg_attr(not(feature = "testing"), allow(dead_code, unused_imports, unreachable_pub))]
4
5pub mod cli;
6pub mod error;
7pub mod settings;
8
9// The crate's public API is the entry points above plus [`Session`]. The
10// internal module graph opens up only under the `testing` feature, for the
11// integration suite's cohesive harness in [`testing`].
12#[cfg(all(test, not(feature = "testing")))]
13mod testing;
14#[cfg(feature = "testing")]
15pub mod testing;
16
17macro_rules! internal_modules {
18    ($($name:ident),* $(,)?) => {
19        $(
20            #[cfg(feature = "testing")]
21            pub mod $name;
22            #[cfg(not(feature = "testing"))]
23            mod $name;
24        )*
25    };
26}
27
28internal_modules!(
29    app,
30    attachment,
31    conversation,
32    command,
33    file_index,
34    git_review,
35    renderer,
36    request,
37    runtime,
38    screens,
39    session,
40    surfaces,
41    theme,
42    view,
43);
44
45use agent_client_protocol::{Client, ConnectTo, schema::v2::SessionId};
46pub use session::Session;
47
48use app::App;
49use error::AppError;
50use renderer::Renderer;
51use settings::UiSettings;
52use std::fs::create_dir_all;
53use std::path::Path;
54use tracing_appender::rolling::daily;
55use tracing_subscriber::EnvFilter;
56
57/// Launch the Wisp TUI with the given agent subprocess command.
58pub async fn run_tui(agent_command: &str, settings: UiSettings, log_dir: Option<&str>) -> Result<(), AppError> {
59    setup_logging(log_dir.map(Path::new));
60    let session = Session::connect(agent_command).await?;
61    run_with_session(session, settings).await
62}
63
64/// Launch the TUI attached to an Aether remote host over an established transport.
65pub async fn run_remote_tui(
66    transport: impl ConnectTo<Client> + 'static,
67    requested_session: Option<SessionId>,
68    settings: UiSettings,
69    log_dir: Option<&Path>,
70) -> Result<(), AppError> {
71    setup_logging(log_dir);
72    let session = Session::connect_remote_to(transport, requested_session).await?;
73    run_with_session(session, settings).await
74}
75
76/// Run the TUI from an already-initialized ACP session.
77pub async fn run_with_session(session: Session, settings: UiSettings) -> Result<(), AppError> {
78    let (app, event_rx, client_handle) = App::from_session(session, settings);
79    runtime::run(app, Renderer::new(), event_rx, client_handle).await
80}
81
82pub fn init_file_logging(dir: &Path, file_name: &str, default_filter: &str) {
83    create_dir_all(dir).ok();
84    let _ = tracing_subscriber::fmt()
85        .with_writer(daily(dir, file_name))
86        .with_ansi(false)
87        .with_env_filter(EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(default_filter)))
88        .try_init();
89}
90
91fn setup_logging(log_dir: Option<&Path>) {
92    init_file_logging(log_dir.unwrap_or(Path::new(DEFAULT_LOG_DIR)), "wisp.log", "info");
93}
94
95pub const DEFAULT_LOG_DIR: &str = "/tmp/wisp-logs";