vx_cli/commands/
mod.rs

1//! CLI command implementations
2
3use crate::cli::{Cli, Commands};
4use crate::ui::UI;
5use vx_core::PluginRegistry;
6
7pub mod cleanup;
8pub mod config;
9pub mod execute;
10pub mod fetch;
11pub mod global;
12pub mod init;
13pub mod install;
14pub mod list;
15pub mod plugin;
16pub mod remove;
17pub mod search;
18pub mod shell;
19pub mod stats;
20pub mod switch;
21pub mod sync;
22
23pub mod update;
24pub mod venv_cmd;
25pub mod version;
26pub mod where_cmd;
27
28// Tests moved to tests/ directory
29
30pub struct CommandHandler;
31
32impl CommandHandler {
33    pub async fn handle(cli: Cli, registry: &PluginRegistry) -> anyhow::Result<()> {
34        // Set verbose mode
35        UI::set_verbose(cli.verbose);
36
37        match cli.command {
38            Some(Commands::Version) => version::handle().await.map_err(Into::into),
39
40            Some(Commands::List {
41                tool,
42                status,
43                installed: _,
44                available: _,
45            }) => list::handle(registry, tool.as_deref(), status)
46                .await
47                .map_err(Into::into),
48
49            Some(Commands::Install {
50                tool,
51                version,
52                force,
53            }) => install::handle(registry, &tool, version.as_deref(), force)
54                .await
55                .map_err(Into::into),
56
57            Some(Commands::Update { tool, apply: _ }) => {
58                update::handle(registry, tool.as_deref(), false)
59                    .await
60                    .map_err(Into::into)
61            }
62
63            Some(Commands::Uninstall {
64                tool,
65                version,
66                force,
67            }) => remove::handle(registry, &tool, version.as_deref(), force)
68                .await
69                .map_err(Into::into),
70
71            Some(Commands::Which { tool, all }) => where_cmd::handle(registry, &tool, all)
72                .await
73                .map_err(Into::into),
74
75            Some(Commands::Versions {
76                tool,
77                latest,
78                prerelease,
79                detailed,
80                interactive,
81            }) => fetch::handle(registry, &tool, latest, detailed, interactive, prerelease)
82                .await
83                .map_err(Into::into),
84
85            Some(Commands::Switch {
86                tool_version,
87                global,
88            }) => switch::handle(registry, &tool_version, global)
89                .await
90                .map_err(Into::into),
91
92            Some(Commands::Config { command }) => match command {
93                Some(crate::cli::ConfigCommand::Show) | None => {
94                    config::handle().await.map_err(Into::into)
95                }
96                Some(crate::cli::ConfigCommand::Set { key, value }) => {
97                    config::handle_set(&key, &value).await.map_err(Into::into)
98                }
99                Some(crate::cli::ConfigCommand::Get { key }) => {
100                    config::handle_get(&key).await.map_err(Into::into)
101                }
102                Some(crate::cli::ConfigCommand::Reset { key }) => {
103                    config::handle_reset(key.clone()).await.map_err(Into::into)
104                }
105                Some(crate::cli::ConfigCommand::Edit) => {
106                    config::handle_edit().await.map_err(Into::into)
107                }
108            },
109
110            Some(Commands::Search {
111                query,
112                category,
113                installed_only,
114                available_only,
115                format,
116                verbose,
117            }) => {
118                // TODO: Get registry from context
119                // For now, create a minimal registry
120                let registry = vx_core::PluginRegistry::new();
121                search::handle(
122                    &registry,
123                    query.clone(),
124                    category.clone(),
125                    installed_only,
126                    available_only,
127                    format.clone(),
128                    verbose,
129                )
130                .await
131                .map_err(Into::into)
132            }
133
134            Some(Commands::Sync {
135                check,
136                force,
137                dry_run,
138                verbose,
139                no_parallel,
140                no_auto_install,
141            }) => {
142                // TODO: Get registry from context
143                let registry = vx_core::PluginRegistry::new();
144                sync::handle(
145                    &registry,
146                    check,
147                    force,
148                    dry_run,
149                    verbose,
150                    no_parallel,
151                    no_auto_install,
152                )
153                .await
154                .map_err(Into::into)
155            }
156
157            Some(Commands::Init {
158                interactive,
159                template,
160                tools,
161                force,
162                dry_run,
163                list_templates,
164            }) => init::handle(
165                interactive,
166                template.clone(),
167                tools.clone(),
168                force,
169                dry_run,
170                list_templates,
171            )
172            .await
173            .map_err(Into::into),
174
175            Some(Commands::Clean {
176                dry_run,
177                cache,
178                orphaned,
179                all,
180                force,
181                older_than,
182                verbose,
183            }) => {
184                // Map new clean options to cleanup options
185                let cache_only = cache && !all;
186                let orphaned_only = orphaned && !all;
187                cleanup::handle(
188                    dry_run,
189                    cache_only,
190                    orphaned_only,
191                    force,
192                    older_than,
193                    verbose,
194                )
195                .await
196                .map_err(Into::into)
197            }
198
199            Some(Commands::Stats) => stats::handle(registry).await.map_err(Into::into),
200
201            Some(Commands::Plugin { command }) => {
202                plugin::handle(registry, command).await.map_err(Into::into)
203            }
204
205            Some(Commands::Venv { command }) => venv_cmd::handle(command).await.map_err(Into::into),
206
207            Some(Commands::Global { command }) => global::handle(command).await.map_err(Into::into),
208
209            None => {
210                // Handle tool execution
211                if cli.args.is_empty() {
212                    UI::error("No tool specified");
213                    UI::hint("Usage: vx <tool> [args...]");
214                    UI::hint("Example: vx uv pip install requests");
215                    UI::hint("Run 'vx list --all' to see supported tools");
216                    std::process::exit(1);
217                }
218
219                let tool_name = &cli.args[0];
220                let tool_args = &cli.args[1..];
221
222                // Use the executor to run the tool
223                let exit_code =
224                    execute::execute_tool(registry, tool_name, tool_args, cli.use_system_path)
225                        .await
226                        .map_err(anyhow::Error::from)?;
227                if exit_code != 0 {
228                    std::process::exit(exit_code);
229                }
230                Ok(())
231            }
232
233            Some(Commands::Shell { command }) => {
234                use crate::cli::ShellCommand;
235                match command {
236                    ShellCommand::Init { shell } => shell::handle_shell_init(shell.clone())
237                        .await
238                        .map_err(Into::into),
239                    ShellCommand::Completions { shell } => shell::handle_completion(shell.clone())
240                        .await
241                        .map_err(Into::into),
242                }
243            }
244        }
245    }
246}