1use anyhow::Result;
4use std::env;
5
6pub async fn handle_shell_init(shell: Option<String>) -> Result<()> {
7 let shell_type = shell.unwrap_or_else(detect_shell);
8
9 match shell_type.as_str() {
10 "bash" => print_bash_init(),
11 "zsh" => print_zsh_init(),
12 "fish" => print_fish_init(),
13 "powershell" | "pwsh" => print_powershell_init(),
14 "cmd" => print_cmd_init(),
15 _ => {
16 return Err(anyhow::anyhow!("Unsupported shell: {}", shell_type));
17 }
18 }
19
20 Ok(())
21}
22
23pub async fn handle_completion(shell: String) -> Result<()> {
24 match shell.as_str() {
25 "bash" => print_bash_completion(),
26 "zsh" => print_zsh_completion(),
27 "fish" => print_fish_completion(),
28 "powershell" | "pwsh" => print_powershell_completion(),
29 _ => {
30 return Err(anyhow::anyhow!("Unsupported shell: {}", shell));
31 }
32 }
33
34 Ok(())
35}
36
37fn detect_shell() -> String {
38 if let Ok(shell) = env::var("SHELL") {
40 if shell.contains("bash") {
41 return "bash".to_string();
42 } else if shell.contains("zsh") {
43 return "zsh".to_string();
44 } else if shell.contains("fish") {
45 return "fish".to_string();
46 }
47 }
48
49 if env::var("PSModulePath").is_ok() {
51 return "powershell".to_string();
52 }
53
54 if cfg!(windows) {
56 "cmd".to_string()
57 } else {
58 "bash".to_string()
59 }
60}
61
62fn print_bash_init() {
63 let vx_home = dirs::home_dir()
64 .map(|p| p.join(".vx").display().to_string())
65 .unwrap_or_else(|| "$HOME/.vx".to_string());
66
67 println!(
68 r#"# VX Shell Integration for Bash
69# Add this to your ~/.bashrc or source it directly
70
71# Set VX environment variables
72export VX_HOME="{vx_home}"
73export VX_SHELL="bash"
74
75# Add VX bin directory to PATH if not already present
76if [[ ":$PATH:" != *":$VX_HOME/bin:"* ]]; then
77 export PATH="$VX_HOME/bin:$PATH"
78fi
79
80# VX project detection function
81__vx_detect_project() {{
82 local dir="$PWD"
83 while [[ "$dir" != "/" ]]; do
84 if [[ -f "$dir/.vx.toml" ]]; then
85 export VX_PROJECT_ROOT="$dir"
86 return 0
87 fi
88 dir="$(dirname "$dir")"
89 done
90 unset VX_PROJECT_ROOT
91 return 1
92}}
93
94# Auto-sync on directory change
95__vx_auto_sync() {{
96 if __vx_detect_project && [[ -f "$VX_PROJECT_ROOT/.vx.toml" ]]; then
97 if command -v vx >/dev/null 2>&1; then
98 vx sync --check --quiet 2>/dev/null || true
99 fi
100 fi
101}}
102
103# Hook into cd command
104__vx_original_cd=$(declare -f cd)
105cd() {{
106 builtin cd "$@"
107 __vx_auto_sync
108}}
109
110# Initialize on shell startup
111__vx_auto_sync
112
113# VX prompt integration (optional)
114__vx_prompt() {{
115 if [[ -n "$VX_PROJECT_ROOT" ]]; then
116 echo "[vx]"
117 fi
118}}
119
120# Uncomment to add VX info to prompt
121# PS1="$(__vx_prompt)$PS1"
122"#,
123 vx_home = vx_home
124 );
125}
126
127fn print_zsh_init() {
128 let vx_home = dirs::home_dir()
129 .map(|p| p.join(".vx").display().to_string())
130 .unwrap_or_else(|| "$HOME/.vx".to_string());
131
132 println!(
133 r#"# VX Shell Integration for Zsh
134# Add this to your ~/.zshrc or source it directly
135
136# Set VX environment variables
137export VX_HOME="{vx_home}"
138export VX_SHELL="zsh"
139
140# Add VX bin directory to PATH if not already present
141if [[ ":$PATH:" != *":$VX_HOME/bin:"* ]]; then
142 export PATH="$VX_HOME/bin:$PATH"
143fi
144
145# VX project detection function
146__vx_detect_project() {{
147 local dir="$PWD"
148 while [[ "$dir" != "/" ]]; do
149 if [[ -f "$dir/.vx.toml" ]]; then
150 export VX_PROJECT_ROOT="$dir"
151 return 0
152 fi
153 dir="${{dir:h}}"
154 done
155 unset VX_PROJECT_ROOT
156 return 1
157}}
158
159# Auto-sync on directory change
160__vx_auto_sync() {{
161 if __vx_detect_project && [[ -f "$VX_PROJECT_ROOT/.vx.toml" ]]; then
162 if command -v vx >/dev/null 2>&1; then
163 vx sync --check --quiet 2>/dev/null || true
164 fi
165 fi
166}}
167
168# Hook into chpwd
169autoload -U add-zsh-hook
170add-zsh-hook chpwd __vx_auto_sync
171
172# Initialize on shell startup
173__vx_auto_sync
174
175# VX prompt integration (optional)
176__vx_prompt() {{
177 if [[ -n "$VX_PROJECT_ROOT" ]]; then
178 echo "[vx]"
179 fi
180}}
181
182# Uncomment to add VX info to prompt
183# PROMPT="$(__vx_prompt)$PROMPT"
184"#,
185 vx_home = vx_home
186 );
187}
188
189fn print_fish_init() {
190 let vx_home = dirs::home_dir()
191 .map(|p| p.join(".vx").display().to_string())
192 .unwrap_or_else(|| "$HOME/.vx".to_string());
193
194 println!(
195 r#"# VX Shell Integration for Fish
196# Add this to your ~/.config/fish/config.fish or source it directly
197
198# Set VX environment variables
199set -gx VX_HOME "{vx_home}"
200set -gx VX_SHELL "fish"
201
202# Add VX bin directory to PATH if not already present
203if not contains "$VX_HOME/bin" $PATH
204 set -gx PATH "$VX_HOME/bin" $PATH
205end
206
207# VX project detection function
208function __vx_detect_project
209 set dir (pwd)
210 while test "$dir" != "/"
211 if test -f "$dir/.vx.toml"
212 set -gx VX_PROJECT_ROOT "$dir"
213 return 0
214 end
215 set dir (dirname "$dir")
216 end
217 set -e VX_PROJECT_ROOT
218 return 1
219end
220
221# Auto-sync on directory change
222function __vx_auto_sync
223 if __vx_detect_project; and test -f "$VX_PROJECT_ROOT/.vx.toml"
224 if command -v vx >/dev/null 2>&1
225 vx sync --check --quiet 2>/dev/null; or true
226 end
227 end
228end
229
230# Hook into directory change
231function __vx_pwd_handler --on-variable PWD
232 __vx_auto_sync
233end
234
235# Initialize on shell startup
236__vx_auto_sync
237
238# VX prompt integration (optional)
239function __vx_prompt
240 if set -q VX_PROJECT_ROOT
241 echo "[vx]"
242 end
243end
244
245# Uncomment to add VX info to prompt
246# function fish_prompt
247# echo (__vx_prompt)(fish_prompt)
248# end
249"#,
250 vx_home = vx_home
251 );
252}
253
254fn print_powershell_init() {
255 let vx_home = dirs::home_dir()
256 .map(|p| p.join(".vx").display().to_string())
257 .unwrap_or_else(|| "$env:USERPROFILE\\.vx".to_string());
258
259 println!(
260 r#"# VX Shell Integration for PowerShell
261# Add this to your $PROFILE or dot-source it directly
262
263# Set VX environment variables
264$env:VX_HOME = "{vx_home}"
265$env:VX_SHELL = "powershell"
266
267# Add VX bin directory to PATH if not already present
268$vxBinPath = Join-Path $env:VX_HOME "bin"
269if ($env:PATH -notlike "*$vxBinPath*") {{
270 $env:PATH = "$vxBinPath;$env:PATH"
271}}
272
273# VX project detection function
274function Find-VxProject {{
275 $dir = Get-Location
276 while ($dir.Path -ne $dir.Root.Name) {{
277 $vxConfig = Join-Path $dir.Path ".vx.toml"
278 if (Test-Path $vxConfig) {{
279 $env:VX_PROJECT_ROOT = $dir.Path
280 return $true
281 }}
282 $dir = $dir.Parent
283 }}
284 Remove-Item Env:VX_PROJECT_ROOT -ErrorAction SilentlyContinue
285 return $false
286}}
287
288# Auto-sync on directory change
289function Invoke-VxAutoSync {{
290 if (Find-VxProject -and (Test-Path "$env:VX_PROJECT_ROOT\.vx.toml")) {{
291 if (Get-Command vx -ErrorAction SilentlyContinue) {{
292 try {{
293 vx sync --check --quiet 2>$null
294 }} catch {{
295 # Ignore errors
296 }}
297 }}
298 }}
299}}
300
301# Hook into location change
302$ExecutionContext.SessionState.InvokeCommand.LocationChangedAction = {{
303 Invoke-VxAutoSync
304}}
305
306# Initialize on shell startup
307Invoke-VxAutoSync
308
309# VX prompt integration (optional)
310function Get-VxPrompt {{
311 if ($env:VX_PROJECT_ROOT) {{
312 return "[vx]"
313 }}
314 return ""
315}}
316
317# Uncomment to add VX info to prompt
318# function prompt {{
319# "$(Get-VxPrompt)PS $($executionContext.SessionState.Path.CurrentLocation)$('>' * ($nestedPromptLevel + 1)) "
320# }}
321"#,
322 vx_home = vx_home
323 );
324}
325
326fn print_cmd_init() {
327 let vx_home = "%USERPROFILE%\\.vx".to_string();
328
329 println!(
330 r#"@echo off
331REM VX Shell Integration for CMD
332REM Add this to your startup script or run it manually
333
334REM Set VX environment variables
335set VX_HOME={vx_home}
336set VX_SHELL=cmd
337
338REM Add VX bin directory to PATH if not already present
339echo %PATH% | find /i "%VX_HOME%\bin" >nul
340if errorlevel 1 (
341 set PATH=%VX_HOME%\bin;%PATH%
342)
343
344echo VX Shell integration initialized for CMD
345echo Use 'vx --help' to get started
346"#,
347 vx_home = vx_home
348 );
349}
350
351fn print_bash_completion() {
352 println!(
353 r#"# VX Bash Completion
354# Source this file or add it to /etc/bash_completion.d/
355
356_vx_completion() {{
357 local cur prev words cword
358 _init_completion || return
359
360 case $prev in
361 install|remove|switch|fetch)
362 # Complete with available tools
363 COMPREPLY=($(compgen -W "node npm npx go cargo uv uvx python" -- "$cur"))
364 return
365 ;;
366 --template)
367 # Complete with available templates
368 COMPREPLY=($(compgen -W "node python rust go fullstack minimal" -- "$cur"))
369 return
370 ;;
371 --format)
372 # Complete with output formats
373 COMPREPLY=($(compgen -W "table json yaml" -- "$cur"))
374 return
375 ;;
376 --category)
377 # Complete with categories
378 COMPREPLY=($(compgen -W "javascript python rust go utility" -- "$cur"))
379 return
380 ;;
381 esac
382
383 case ${{words[1]}} in
384 venv)
385 case ${{words[2]}} in
386 activate|remove|use)
387 # Complete with venv names
388 local venvs=$(vx venv list --names-only 2>/dev/null || echo "")
389 COMPREPLY=($(compgen -W "$venvs" -- "$cur"))
390 return
391 ;;
392 *)
393 COMPREPLY=($(compgen -W "create list activate remove current" -- "$cur"))
394 return
395 ;;
396 esac
397 ;;
398 config)
399 COMPREPLY=($(compgen -W "show set get reset edit" -- "$cur"))
400 return
401 ;;
402 *)
403 # Complete with main commands
404 COMPREPLY=($(compgen -W "install remove list update search sync init cleanup stats venv config global plugin shell-init completion version help" -- "$cur"))
405 return
406 ;;
407 esac
408}}
409
410complete -F _vx_completion vx
411"#
412 );
413}
414
415fn print_zsh_completion() {
416 println!(
417 r#"#compdef vx
418
419# VX Zsh Completion
420
421_vx() {{
422 local context state line
423 typeset -A opt_args
424
425 _arguments -C \
426 '1: :_vx_commands' \
427 '*::arg:->args'
428
429 case $state in
430 args)
431 case $words[1] in
432 install|remove|switch|fetch)
433 _arguments \
434 '*:tools:(node npm npx go cargo uv uvx python)'
435 ;;
436 venv)
437 case $words[2] in
438 activate|remove|use)
439 _arguments \
440 '*:venv:_vx_venvs'
441 ;;
442 *)
443 _arguments \
444 '1:subcommand:(create list activate remove current)'
445 ;;
446 esac
447 ;;
448 config)
449 _arguments \
450 '1:subcommand:(show set get reset edit)'
451 ;;
452 esac
453 ;;
454 esac
455}}
456
457_vx_commands() {{
458 local commands
459 commands=(
460 'install:Install a tool'
461 'remove:Remove a tool'
462 'list:List installed tools'
463 'update:Update tools'
464 'search:Search available tools'
465 'sync:Sync project tools'
466 'init:Initialize project'
467 'cleanup:Clean up cache and orphaned files'
468 'stats:Show statistics'
469 'venv:Virtual environment management'
470 'config:Configuration management'
471 'global:Global tool management'
472 'plugin:Plugin management'
473 'shell-init:Generate shell initialization script'
474 'completion:Generate shell completion script'
475 'version:Show version information'
476 'help:Show help'
477 )
478 _describe 'commands' commands
479}}
480
481_vx_venvs() {{
482 local venvs
483 venvs=($(vx venv list --names-only 2>/dev/null || echo ""))
484 _describe 'virtual environments' venvs
485}}
486
487_vx "$@"
488"#
489 );
490}
491
492fn print_fish_completion() {
493 println!(
494 r#"# VX Fish Completion
495
496# Main commands
497complete -c vx -f -n '__fish_use_subcommand' -a 'install' -d 'Install a tool'
498complete -c vx -f -n '__fish_use_subcommand' -a 'remove' -d 'Remove a tool'
499complete -c vx -f -n '__fish_use_subcommand' -a 'list' -d 'List installed tools'
500complete -c vx -f -n '__fish_use_subcommand' -a 'update' -d 'Update tools'
501complete -c vx -f -n '__fish_use_subcommand' -a 'search' -d 'Search available tools'
502complete -c vx -f -n '__fish_use_subcommand' -a 'sync' -d 'Sync project tools'
503complete -c vx -f -n '__fish_use_subcommand' -a 'init' -d 'Initialize project'
504complete -c vx -f -n '__fish_use_subcommand' -a 'cleanup' -d 'Clean up cache and orphaned files'
505complete -c vx -f -n '__fish_use_subcommand' -a 'stats' -d 'Show statistics'
506complete -c vx -f -n '__fish_use_subcommand' -a 'venv' -d 'Virtual environment management'
507complete -c vx -f -n '__fish_use_subcommand' -a 'config' -d 'Configuration management'
508complete -c vx -f -n '__fish_use_subcommand' -a 'global' -d 'Global tool management'
509complete -c vx -f -n '__fish_use_subcommand' -a 'plugin' -d 'Plugin management'
510complete -c vx -f -n '__fish_use_subcommand' -a 'shell-init' -d 'Generate shell initialization script'
511complete -c vx -f -n '__fish_use_subcommand' -a 'completion' -d 'Generate shell completion script'
512complete -c vx -f -n '__fish_use_subcommand' -a 'version' -d 'Show version information'
513complete -c vx -f -n '__fish_use_subcommand' -a 'help' -d 'Show help'
514
515# Tool names for install/remove/switch/fetch
516complete -c vx -f -n '__fish_seen_subcommand_from install remove switch fetch' -a 'node npm npx go cargo uv uvx python'
517
518# Venv subcommands
519complete -c vx -f -n '__fish_seen_subcommand_from venv; and not __fish_seen_subcommand_from create list activate remove current' -a 'create' -d 'Create virtual environment'
520complete -c vx -f -n '__fish_seen_subcommand_from venv; and not __fish_seen_subcommand_from create list activate remove current' -a 'list' -d 'List virtual environments'
521complete -c vx -f -n '__fish_seen_subcommand_from venv; and not __fish_seen_subcommand_from create list activate remove current' -a 'activate' -d 'Activate virtual environment'
522complete -c vx -f -n '__fish_seen_subcommand_from venv; and not __fish_seen_subcommand_from create list activate remove current' -a 'remove' -d 'Remove virtual environment'
523complete -c vx -f -n '__fish_seen_subcommand_from venv; and not __fish_seen_subcommand_from create list activate remove current' -a 'current' -d 'Show current virtual environment'
524
525# Config subcommands
526complete -c vx -f -n '__fish_seen_subcommand_from config; and not __fish_seen_subcommand_from show set get reset edit' -a 'show' -d 'Show configuration'
527complete -c vx -f -n '__fish_seen_subcommand_from config; and not __fish_seen_subcommand_from show set get reset edit' -a 'set' -d 'Set configuration value'
528complete -c vx -f -n '__fish_seen_subcommand_from config; and not __fish_seen_subcommand_from show set get reset edit' -a 'get' -d 'Get configuration value'
529complete -c vx -f -n '__fish_seen_subcommand_from config; and not __fish_seen_subcommand_from show set get reset edit' -a 'reset' -d 'Reset configuration'
530complete -c vx -f -n '__fish_seen_subcommand_from config; and not __fish_seen_subcommand_from show set get reset edit' -a 'edit' -d 'Edit configuration'
531
532# Shell types for completion and shell-init
533complete -c vx -f -n '__fish_seen_subcommand_from completion shell-init' -a 'bash zsh fish powershell'
534
535# Common options
536complete -c vx -l help -d 'Show help'
537complete -c vx -l version -d 'Show version'
538complete -c vx -l verbose -d 'Verbose output'
539complete -c vx -l dry-run -d 'Preview operations'
540complete -c vx -l force -d 'Force operation'
541"#
542 );
543}
544
545fn print_powershell_completion() {
546 println!(
547 r#"# VX PowerShell Completion
548
549Register-ArgumentCompleter -Native -CommandName vx -ScriptBlock {{
550 param($wordToComplete, $commandAst, $cursorPosition)
551
552 $commands = @(
553 'install', 'remove', 'list', 'update', 'search', 'sync', 'init', 'cleanup', 'stats',
554 'venv', 'config', 'global', 'plugin', 'shell-init', 'completion', 'version', 'help'
555 )
556
557 $tools = @('node', 'npm', 'npx', 'go', 'cargo', 'uv', 'uvx', 'python')
558 $shells = @('bash', 'zsh', 'fish', 'powershell')
559 $formats = @('table', 'json', 'yaml')
560 $templates = @('node', 'python', 'rust', 'go', 'fullstack', 'minimal')
561
562 $tokens = $commandAst.CommandElements
563 $command = $tokens[1].Value
564
565 switch ($command) {{
566 {{ $_ -in @('install', 'remove', 'switch', 'fetch') }} {{
567 $tools | Where-Object {{ $_ -like "$wordToComplete*" }}
568 }}
569 'venv' {{
570 if ($tokens.Count -eq 2) {{
571 @('create', 'list', 'activate', 'remove', 'current') | Where-Object {{ $_ -like "$wordToComplete*" }}
572 }}
573 }}
574 'config' {{
575 if ($tokens.Count -eq 2) {{
576 @('show', 'set', 'get', 'reset', 'edit') | Where-Object {{ $_ -like "$wordToComplete*" }}
577 }}
578 }}
579 {{ $_ -in @('completion', 'shell-init') }} {{
580 $shells | Where-Object {{ $_ -like "$wordToComplete*" }}
581 }}
582 default {{
583 if ($tokens.Count -eq 1) {{
584 $commands | Where-Object {{ $_ -like "$wordToComplete*" }}
585 }}
586 }}
587 }}
588}}
589"#
590 );
591}