diff --git a/crates/goose-cli/Cargo.toml b/crates/goose-cli/Cargo.toml index cc9c5f1c4..fc863ef5e 100644 --- a/crates/goose-cli/Cargo.toml +++ b/crates/goose-cli/Cargo.toml @@ -90,7 +90,6 @@ libc = { version = "0.2.182", default-features = false, features = ["std"] } default = [ "code-mode", "local-inference", - "tui", "aws-providers", "telemetry", "nostr", @@ -109,9 +108,8 @@ telemetry = ["goose/telemetry"] nostr = ["goose/nostr"] otel = ["goose/otel"] system-keyring = ["goose/system-keyring"] -tui = [] update = ["dep:sigstore-verify", "dep:snap"] -portable-default = ["rustls-tls", "aws-providers", "telemetry", "otel", "tui"] +portable-default = ["rustls-tls", "aws-providers", "telemetry", "otel"] # disables the update command disable-update = [] rustls-tls = [ diff --git a/crates/goose-cli/src/cli.rs b/crates/goose-cli/src/cli.rs index d03657c9c..5252d7f7f 100644 --- a/crates/goose-cli/src/cli.rs +++ b/crates/goose-cli/src/cli.rs @@ -1045,27 +1045,6 @@ enum Command { command: TermCommand, }, - /// Launch the goose terminal UI (TUI) - #[cfg(feature = "tui")] - #[command( - about = "Launch the goose terminal UI", - long_about = "Launch the goose terminal UI (the @aaif/goose npm package).\n\ - \n\ - Resolution order:\n \ - 1. GOOSE_TUI_SCRIPT, if set to an existing dist/tui.js\n \ - 2. A local checkout's ui/text/dist/tui.js (dev workflow)\n \ - 3. `npx --yes --package -- goose-tui` (deployed installs)\n\ - \n\ - Override the npm spec via GOOSE_TUI_NPM_SPEC (default: @aaif/goose@latest).\n\ - Local script mode requires `node` on PATH; npx mode requires `npx` on PATH.\n\ - Any extra arguments are passed through to the TUI." - )] - Tui { - /// Arguments forwarded to the TUI - #[arg(trailing_var_arg = true, allow_hyphen_values = true)] - args: Vec, - }, - /// Manage local inference models #[cfg(feature = "local-inference")] #[command(about = "Manage local inference models", visible_alias = "lm")] @@ -1388,8 +1367,6 @@ fn get_command_name(command: &Option) -> &'static str { Some(Command::Skills { .. }) => "skills", Some(Command::Plugin { .. }) => "plugin", Some(Command::Term { .. }) => "term", - #[cfg(feature = "tui")] - Some(Command::Tui { .. }) => "tui", #[cfg(feature = "local-inference")] Some(Command::LocalModels { .. }) => "local-models", Some(Command::Completion { .. }) => "completion", @@ -2761,8 +2738,6 @@ pub async fn cli() -> anyhow::Result<()> { Some(Command::Skills { command }) => handle_skills_subcommand(command).await, Some(Command::Plugin { command }) => handle_plugin_subcommand(command), Some(Command::Term { command }) => handle_term_subcommand(command).await, - #[cfg(feature = "tui")] - Some(Command::Tui { args }) => crate::commands::tui::handle_tui(args), #[cfg(feature = "local-inference")] Some(Command::LocalModels { command }) => handle_local_models_command(command).await, Some(Command::Review { @@ -3055,18 +3030,6 @@ mod tests { } } - #[cfg(feature = "tui")] - #[test] - fn tui_command_accepts_trailing_args() { - let cli = - Cli::try_parse_from(["goose", "tui", "--", "--theme", "dark"]).expect("parse failed"); - - match cli.command { - Some(Command::Tui { args }) => assert_eq!(args, vec!["--theme", "dark"]), - _ => panic!("expected tui command"), - } - } - #[cfg(feature = "local-inference")] mod local_search { use super::super::{ diff --git a/crates/goose-cli/src/commands/mod.rs b/crates/goose-cli/src/commands/mod.rs index f43e90579..e677c078b 100644 --- a/crates/goose-cli/src/commands/mod.rs +++ b/crates/goose-cli/src/commands/mod.rs @@ -9,7 +9,5 @@ pub mod schedule; pub mod session; pub mod skills; pub mod term; -#[cfg(feature = "tui")] -pub mod tui; #[cfg(feature = "update")] pub mod update; diff --git a/crates/goose-cli/src/commands/tui.rs b/crates/goose-cli/src/commands/tui.rs deleted file mode 100644 index 3b49a633f..000000000 --- a/crates/goose-cli/src/commands/tui.rs +++ /dev/null @@ -1,127 +0,0 @@ -use anyhow::{anyhow, Context, Result}; -use std::path::{Path, PathBuf}; -use std::process::Command; - -const TUI_NPM_SPEC_ENV: &str = "GOOSE_TUI_NPM_SPEC"; -const TUI_REL_PATH: &str = "ui/text/dist/tui.js"; -const DEFAULT_NPM_SPEC: &str = "@aaif/goose@latest"; -const NPM_BIN_NAME: &str = "goose-tui"; - -enum TuiSource { - LocalScript(PathBuf), - Npx(String), -} - -fn find_local_script() -> Option { - let exe = std::env::current_exe().ok()?; - find_local_script_from(&exe) -} - -fn find_local_script_from(exe: &Path) -> Option { - let exe_dir = exe.parent().unwrap_or_else(|| Path::new(".")); - - let mut dir = Some(exe_dir.to_path_buf()); - for _ in 0..6 { - if let Some(d) = dir.clone() { - let candidate = d.join(TUI_REL_PATH); - if candidate.is_file() { - return Some(candidate); - } - dir = d.parent().map(Path::to_path_buf); - } - } - - None -} - -fn resolve_source() -> TuiSource { - if let Some(script) = find_local_script() { - return TuiSource::LocalScript(script); - } - let spec = std::env::var(TUI_NPM_SPEC_ENV).unwrap_or_else(|_| DEFAULT_NPM_SPEC.to_string()); - TuiSource::Npx(spec) -} - -fn build_command(source: &TuiSource, args: &[String]) -> Result { - match source { - TuiSource::LocalScript(script) => { - let mut cmd = Command::new("node"); - cmd.arg(script).args(args); - Ok(cmd) - } - TuiSource::Npx(spec) => { - let mut cmd = Command::new("npx"); - cmd.arg("--yes") - .arg("--package") - .arg(spec) - .arg("--") - .arg(NPM_BIN_NAME) - .args(args); - Ok(cmd) - } - } -} - -pub fn handle_tui(args: Vec) -> Result<()> { - let source = resolve_source(); - - let goose_binary = std::env::current_exe() - .context("could not determine current goose executable to expose as GOOSE_BINARY")?; - - let mut cmd = build_command(&source, &args)?; - cmd.env("GOOSE_BINARY", &goose_binary); - - let descriptor = match &source { - TuiSource::LocalScript(p) => format!("node {}", p.display()), - TuiSource::Npx(spec) => format!("npx --package {} -- {}", spec, NPM_BIN_NAME), - }; - - #[cfg(unix)] - { - use std::os::unix::process::CommandExt; - let err = cmd.exec(); - Err(anyhow!("failed to exec TUI ({descriptor}): {err}")) - } - - #[cfg(not(unix))] - { - let status = cmd - .status() - .with_context(|| format!("failed to run `{descriptor}`"))?; - if !status.success() { - std::process::exit(status.code().unwrap_or(1)); - } - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use std::fs; - - #[test] - fn find_local_script_ignores_unrelated_directories() { - let temp_dir = tempfile::tempdir().expect("create temp dir"); - let executable = temp_dir.path().join("install/bin/goose"); - let planted_script = temp_dir.path().join("checkout").join(TUI_REL_PATH); - fs::create_dir_all(planted_script.parent().unwrap()).expect("create script directory"); - fs::write(&planted_script, "process.exit(0)\n").expect("write planted script"); - - assert_eq!(find_local_script_from(&executable), None); - } - - #[test] - fn find_local_script_accepts_executable_ancestor() { - let temp_dir = tempfile::tempdir().expect("create temp dir"); - let executable = temp_dir.path().join("target/debug/goose"); - let bundled_script = temp_dir.path().join(TUI_REL_PATH); - fs::create_dir_all(bundled_script.parent().unwrap()).expect("create script directory"); - fs::write(&bundled_script, "process.exit(0)\n").expect("write bundled script"); - - assert_eq!( - find_local_script_from(&executable).as_deref(), - Some(bundled_script.as_path()) - ); - } -}