diff --git a/cmd/hostkeeper/completion.go b/cmd/hostkeeper/completion.go new file mode 100644 index 0000000..c1d30a4 --- /dev/null +++ b/cmd/hostkeeper/completion.go @@ -0,0 +1,67 @@ +package main + +import ( + "os" + + "github.com/spf13/cobra" +) + +var completionCmd = &cobra.Command{ + Use: "completion [bash|zsh|fish|powershell]", + Short: "Generate shell completion script", + Long: `Generate shell completion script for hostkeeper. + +To load completions: + +Bash: + $ source <(hostkeeper completion bash) + + # To load completions for each session, execute once: + # Linux: + $ hostkeeper completion bash > /etc/bash_completion.d/hostkeeper + # macOS: + $ hostkeeper completion bash > /usr/local/etc/bash_completion.d/hostkeeper + +Zsh: + # If shell completion is not already enabled in your environment, + # you will need to enable it. You can execute the following once: + $ echo "autoload -U compinit; compinit" >> ~/.zshrc + + # To load completions for each session, execute once: + $ hostkeeper completion zsh > "${fpath[1]}/_hostkeeper" + + # You will need to start a new shell for this setup to take effect. + +fish: + $ hostkeeper completion fish | source + + # To load completions for each session, execute once: + $ hostkeeper completion fish > ~/.config/fish/completions/hostkeeper.fish + +PowerShell: + PS> hostkeeper completion powershell | Out-String | Invoke-Expression + + # To load completions for every new session, run: + PS> hostkeeper completion powershell > hostkeeper.ps1 + # and source this file from your PowerShell profile. +`, + DisableFlagsInUseLine: true, + ValidArgs: []string{"bash", "zsh", "fish", "powershell"}, + Args: cobra.ExactValidArgs(1), + Run: func(cmd *cobra.Command, args []string) { + switch args[0] { + case "bash": + _ = cmd.Root().GenBashCompletion(os.Stdout) + case "zsh": + _ = cmd.Root().GenZshCompletion(os.Stdout) + case "fish": + _ = cmd.Root().GenFishCompletion(os.Stdout, true) + case "powershell": + _ = cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout) + } + }, +} + +func init() { + rootCmd.AddCommand(completionCmd) +} \ No newline at end of file diff --git a/cmd/hostkeeper/main.go b/cmd/hostkeeper/main.go index 04ed203..cd9e68e 100644 --- a/cmd/hostkeeper/main.go +++ b/cmd/hostkeeper/main.go @@ -11,11 +11,8 @@ var ( ) func main() { - if len(os.Args) > 1 && os.Args[1] == "--version" { - fmt.Printf("hostkeeper %s (built: %s)\n", version, buildTime) - return + if err := Execute(); err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) } - - fmt.Println("HostKeeper - SSH/SFTP Management Tool") - fmt.Println("Run 'hostkeeper --help' for usage.") } \ No newline at end of file diff --git a/cmd/hostkeeper/root.go b/cmd/hostkeeper/root.go new file mode 100644 index 0000000..bfa2e4e --- /dev/null +++ b/cmd/hostkeeper/root.go @@ -0,0 +1,87 @@ +package main + +import ( + "fmt" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + + "git.tukangketik.id/swanadiva/hostkeeper/pkg/config" +) + +var ( + cfgFile string + appCfg *config.Config + verbose int + debug bool +) + +var rootCmd = &cobra.Command{ + Use: "hostkeeper", + Short: "Cross-platform SSH/SFTP management tool", + Long: `Hostkeeper - Cross-platform SSH/SFTP Management Tool + +A comprehensive SSH/SFTP management tool with secure credential storage, +host management, and cross-device sync capabilities. + +Quick Start: + hostkeeper add myserver --host 192.168.1.10 --user admin + hostkeeper list + hostkeeper connect myserver + +For more information, visit the project repository.`, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + // Initialize configuration + cfg, err := config.New() + if err != nil { + return fmt.Errorf("failed to initialize config: %w", err) + } + appCfg = cfg + return nil + }, +} + +func init() { + cobra.OnInitialize(initConfig) + + rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is platform-specific app dir)") + rootCmd.PersistentFlags().CountVarP(&verbose, "verbose", "v", "verbose output (-v for info, -vv for debug)") + rootCmd.PersistentFlags().BoolVar(&debug, "debug", false, "enable debug mode") + + rootCmd.AddCommand(versionCmd) +} + +// initConfig reads in config file and ENV variables if set +func initConfig() { + if cfgFile != "" { + viper.SetConfigFile(cfgFile) + } else { + // Use platform-specific config directory + cfg, err := config.New() + if err != nil { + return + } + viper.AddConfigPath(cfg.GetConfigDir()) + viper.SetConfigType("json") + viper.SetConfigName("config") + } + + viper.AutomaticEnv() + + // Read config file (ignore if not found for first run) + _ = viper.ReadInConfig() +} + +var versionCmd = &cobra.Command{ + Use: "version", + Short: "Print the version number", + Long: `Print the version and build information for HostKeeper.`, + Run: func(cmd *cobra.Command, args []string) { + fmt.Printf("hostkeeper %s (built: %s)\n", version, buildTime) + }, +} + +// Execute runs the root command +func Execute() error { + return rootCmd.Execute() +} \ No newline at end of file