diff --git a/src/cortex-cli/src/agent_cmd/tests.rs b/src/cortex-cli/src/agent_cmd/tests.rs index e2ff07f9f..18f7ba753 100644 --- a/src/cortex-cli/src/agent_cmd/tests.rs +++ b/src/cortex-cli/src/agent_cmd/tests.rs @@ -3,10 +3,9 @@ #[cfg(test)] mod tests { use crate::agent_cmd::cli::{CopyArgs, ExportArgs}; - use crate::agent_cmd::loader::{ - load_builtin_agents, parse_frontmatter, read_file_with_encoding, - }; + use crate::agent_cmd::loader::{load_builtin_agents, parse_frontmatter}; use crate::agent_cmd::types::AgentMode; + use crate::utils::file::read_file_with_encoding; #[test] fn test_read_file_with_utf8() { diff --git a/src/cortex-cli/src/cli/handlers.rs b/src/cortex-cli/src/cli/handlers.rs index e92fa8632..7a23bb8df 100644 --- a/src/cortex-cli/src/cli/handlers.rs +++ b/src/cortex-cli/src/cli/handlers.rs @@ -19,6 +19,8 @@ use crate::styled_output::{print_info, print_success, print_warning}; /// /// This is the main command router for the CLI. pub async fn dispatch_command(cli: Cli) -> Result<()> { + let verbose = cli.verbose; + match cli.command { None => run_tui(cli.interactive).await, Some(Commands::Init(init_cli)) => run_init(init_cli).await, @@ -58,7 +60,7 @@ pub async fn dispatch_command(cli: Cli) -> Result<()> { Some(Commands::Debug(debug_cli)) => debug_cli.run().await, Some(Commands::Servers(servers_cli)) => run_servers(servers_cli).await, Some(Commands::History(history_cli)) => run_history(history_cli).await, - Some(Commands::Plugin(plugin_cli)) => plugin_cli.run().await, + Some(Commands::Plugin(plugin_cli)) => plugin_cli.run(verbose).await, Some(Commands::Feedback(feedback_cli)) => feedback_cli.run().await, Some(Commands::Lock(lock_cli)) => lock_cli.run().await, Some(Commands::Alias(alias_cli)) => alias_cli.run().await, diff --git a/src/cortex-cli/src/plugin_cmd.rs b/src/cortex-cli/src/plugin_cmd.rs index 3ce99f238..f5103c40a 100644 --- a/src/cortex-cli/src/plugin_cmd.rs +++ b/src/cortex-cli/src/plugin_cmd.rs @@ -939,11 +939,11 @@ cp plugin.toml ~/.cortex/plugins/{}/ impl PluginCli { /// Run the plugin command. - pub async fn run(self) -> Result<()> { + pub async fn run(self, verbose: bool) -> Result<()> { match self.subcommand { PluginSubcommand::List(args) => run_list(args).await, PluginSubcommand::Install(args) => run_install(args).await, - PluginSubcommand::Remove(args) => run_remove(args).await, + PluginSubcommand::Remove(args) => run_remove(args, verbose).await, PluginSubcommand::Enable(args) => run_enable(args).await, PluginSubcommand::Disable(args) => run_disable(args).await, PluginSubcommand::Show(args) => run_show(args).await, @@ -1143,7 +1143,7 @@ fn copy_dir_recursive(src: &std::path::Path, dst: &std::path::Path) -> Result<() Ok(()) } -async fn run_remove(args: PluginRemoveArgs) -> Result<()> { +async fn run_remove(args: PluginRemoveArgs, verbose: bool) -> Result<()> { let plugins_dir = get_plugins_dir(); let plugin_path = plugins_dir.join(&args.name); @@ -1164,11 +1164,49 @@ async fn run_remove(args: PluginRemoveArgs) -> Result<()> { } } + if verbose { + for line in plugin_remove_verbose_preflight_lines(&plugin_path, args.yes) { + println!("{line}"); + } + } + std::fs::remove_dir_all(&plugin_path)?; println!("Plugin '{}' removed successfully.", args.name); + + if verbose { + for line in plugin_remove_verbose_completion_lines(&plugin_path, &plugins_dir) { + println!("{line}"); + } + } + Ok(()) } +fn plugin_remove_verbose_preflight_lines(plugin_path: &Path, yes: bool) -> Vec { + vec![ + format!("Plugin directory: {}", plugin_path.display()), + format!( + "Confirmation: {}", + if yes { + "skipped (--yes)" + } else { + "confirmed by prompt" + } + ), + ] +} + +fn plugin_remove_verbose_completion_lines(plugin_path: &Path, plugins_dir: &Path) -> Vec { + vec![ + format!("Removed path: {}", plugin_path.display()), + format!( + "Plugin exists after removal: {}", + if plugin_path.exists() { "yes" } else { "no" } + ), + format!("Plugins directory: {}", plugins_dir.display()), + ] +} + async fn run_enable(args: PluginEnableArgs) -> Result<()> { let plugins_dir = get_plugins_dir(); let plugin_path = plugins_dir.join(&args.name); @@ -2458,6 +2496,35 @@ mod tests { assert!(args.yes, "yes should be true when set"); } + #[test] + fn test_plugin_remove_verbose_lines_include_extra_context() { + let plugins_dir = PathBuf::from("cortex-plugins"); + let plugin_path = plugins_dir.join("remove-me"); + + let preflight = plugin_remove_verbose_preflight_lines(&plugin_path, true); + assert_eq!( + preflight, + vec![ + format!("Plugin directory: {}", plugin_path.display()), + "Confirmation: skipped (--yes)".to_string(), + ] + ); + + let completion = plugin_remove_verbose_completion_lines(&plugin_path, &plugins_dir); + assert!( + completion + .iter() + .any(|line| line == &format!("Removed path: {}", plugin_path.display())), + "completion output should include the removed plugin path" + ); + assert!( + completion + .iter() + .any(|line| line == &format!("Plugins directory: {}", plugins_dir.display())), + "completion output should include the parent plugin directory" + ); + } + // ========================================================================== // CLI argument parsing tests - PluginEnableArgs / PluginDisableArgs // ==========================================================================