diff --git a/cmd/netssh/main.go b/cmd/netssh/main.go index a222273..47adbc6 100644 --- a/cmd/netssh/main.go +++ b/cmd/netssh/main.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "os" + "path/filepath" "sort" "strings" "text/tabwriter" @@ -207,11 +208,84 @@ func rootCmd() *cobra.Command { }, } - // cobra automatically adds a "completion" subcommand + // cobra automatically adds a "completion" subcommand; we extend it with "install". root.AddCommand(configureCmd(), searchCmd(), cacheCmd()) + + for _, cmd := range root.Commands() { + if cmd.Name() == "completion" { + cmd.AddCommand(completionInstallCmd(root)) + break + } + } + return root } +func completionInstallCmd(root *cobra.Command) *cobra.Command { + var shell string + cmd := &cobra.Command{ + Use: "install", + Short: "Install shell completion for the current user (no sudo required)", + RunE: func(cmd *cobra.Command, args []string) error { + if shell == "" { + shell = filepath.Base(os.Getenv("SHELL")) + } + + var ( + dir string + file string + gen func() ([]byte, error) + note string + ) + + switch shell { + case "bash": + dir = filepath.Join(os.Getenv("HOME"), ".local", "share", "bash-completion", "completions") + file = filepath.Join(dir, "netssh") + gen = func() ([]byte, error) { + var buf strings.Builder + return []byte(buf.String()), root.GenBashCompletionV2(&buf, true) + } + note = "Reload your shell or run: source " + file + case "zsh": + dir = filepath.Join(os.Getenv("HOME"), ".zfunc") + file = filepath.Join(dir, "_netssh") + gen = func() ([]byte, error) { + var buf strings.Builder + return []byte(buf.String()), root.GenZshCompletion(&buf) + } + note = "Make sure ~/.zfunc is in your fpath:\n fpath=(~/.zfunc $fpath)\n autoload -Uz compinit && compinit" + case "fish": + configDir, _ := os.UserConfigDir() + dir = filepath.Join(configDir, "fish", "completions") + file = filepath.Join(dir, "netssh.fish") + gen = func() ([]byte, error) { + var buf strings.Builder + return []byte(buf.String()), root.GenFishCompletion(&buf, true) + } + note = "Reload your shell or start a new fish session." + default: + return fmt.Errorf("unsupported shell %q — use --shell bash|zsh|fish", shell) + } + + script, err := gen() + if err != nil { + return fmt.Errorf("generating completion: %w", err) + } + if err := os.MkdirAll(dir, 0o755); err != nil { + return fmt.Errorf("creating %s: %w", dir, err) + } + if err := os.WriteFile(file, script, 0o644); err != nil { + return fmt.Errorf("writing %s: %w", file, err) + } + fmt.Printf("Completion installed → %s\n%s\n", file, note) + return nil + }, + } + cmd.Flags().StringVar(&shell, "shell", "", "Shell to install for (default: $SHELL). Supported: bash, zsh, fish") + return cmd +} + func configureCmd() *cobra.Command { return &cobra.Command{ Use: "configure",