package cli import ( "fmt" "os" "runtime/debug" "forge.lthn.ai/core/go/pkg/crypt/openpgp" "forge.lthn.ai/core/go/pkg/framework" "forge.lthn.ai/core/go/pkg/log" "forge.lthn.ai/core/go/pkg/workspace" "github.com/spf13/cobra" ) const ( // AppName is the CLI application name. AppName = "core" ) // Build-time variables set via ldflags (SemVer 2.0.0): // // go build -ldflags="-X forge.lthn.ai/core/go/pkg/cli.AppVersion=1.2.0 \ // -X forge.lthn.ai/core/go/pkg/cli.BuildCommit=df94c24 \ // -X forge.lthn.ai/core/go/pkg/cli.BuildDate=2026-02-06 \ // -X forge.lthn.ai/core/go/pkg/cli.BuildPreRelease=dev.8" var ( AppVersion = "0.0.0" BuildCommit = "unknown" BuildDate = "unknown" BuildPreRelease = "" ) // SemVer returns the full SemVer 2.0.0 version string. // - Release: 1.2.0 // - Pre-release: 1.2.0-dev.8 // - Full: 1.2.0-dev.8+df94c24.20260206 func SemVer() string { v := AppVersion if BuildPreRelease != "" { v += "-" + BuildPreRelease } if BuildCommit != "unknown" { v += "+" + BuildCommit if BuildDate != "unknown" { v += "." + BuildDate } } return v } // Main initialises and runs the CLI application. // This is the main entry point for the CLI. // Exits with code 1 on error or panic. func Main() { // Recovery from panics defer func() { if r := recover(); r != nil { log.Error("recovered from panic", "error", r, "stack", string(debug.Stack())) Shutdown() Fatal(fmt.Errorf("panic: %v", r)) } }() // Initialise CLI runtime with services if err := Init(Options{ AppName: AppName, Version: SemVer(), Services: []framework.Option{ framework.WithName("i18n", NewI18nService(I18nOptions{})), framework.WithName("log", NewLogService(log.Options{ Level: log.LevelInfo, })), framework.WithName("crypt", openpgp.New), framework.WithName("workspace", workspace.New), }, }); err != nil { Error(err.Error()) os.Exit(1) } defer Shutdown() // Add completion command to the CLI's root RootCmd().AddCommand(completionCmd) if err := Execute(); err != nil { code := 1 var exitErr *ExitError if As(err, &exitErr) { code = exitErr.Code } Error(err.Error()) os.Exit(code) } } // completionCmd generates shell completion scripts. var completionCmd = &cobra.Command{ Use: "completion [bash|zsh|fish|powershell]", Short: "Generate shell completion script", Long: `Generate shell completion script for the specified shell. To load completions: Bash: $ source <(core completion bash) # To load completions for each session, execute once: # Linux: $ core completion bash > /etc/bash_completion.d/core # macOS: $ core completion bash > $(brew --prefix)/etc/bash_completion.d/core 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: $ core completion zsh > "${fpath[1]}/_core" # You will need to start a new shell for this setup to take effect. Fish: $ core completion fish | source # To load completions for each session, execute once: $ core completion fish > ~/.config/fish/completions/core.fish PowerShell: PS> core completion powershell | Out-String | Invoke-Expression # To load completions for every new session, run: PS> core completion powershell > core.ps1 # and source this file from your PowerShell profile. `, DisableFlagsInUseLine: true, ValidArgs: []string{"bash", "zsh", "fish", "powershell"}, Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs), 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) } }, }