feat(cli): migrate to cobra and add completion
CI / test (push) Successful in 4s

This commit is contained in:
gary committed 2026-07-12 23:12:55 +08:00
1 parent bbc62e4dcf
commit ce34fce16b
7 files changed
+693 -12

No files matched your search

+27 -3
View File
@@ -145,14 +145,38 @@ ktui [flags]
ktui status [flags] ktui status [flags]
ktui export <markdown|csv|json> [flags] ktui export <markdown|csv|json> [flags]
ktui config <init|path|show|set|help> ktui config <init|path|show|set|help>
ktui profile <list|current|use|add|rename|remove> ktui keys
ktui update <check|install> ktui profile <list|current|use|add|rename|remove|help>
ktui update <check|install|help>
ktui completion <bash|zsh|fish|powershell>
ktui version ktui version
ktui help [status|config|profile|keys|update|export] ktui help [command]
``` ```
连接到多个 Komari 站点时使用 `ktui profile ...` 管理,并可用 `--profile name` 临时选择。TUI 专属显示参数使用 `--mode sheet|line`。`--realtime-window 1m|5m|10m` 控制 realtime 图表横轴时间范围。一次性拉取摘要使用 `ktui status`,更新检查和安装分别使用 `ktui update check`、`ktui update install`。 连接到多个 Komari 站点时使用 `ktui profile ...` 管理,并可用 `--profile name` 临时选择。TUI 专属显示参数使用 `--mode sheet|line`。`--realtime-window 1m|5m|10m` 控制 realtime 图表横轴时间范围。一次性拉取摘要使用 `ktui status`,更新检查和安装分别使用 `ktui update check`、`ktui update install`。
## Shell 补全
`ktui completion` 可以生成 Tab 补全脚本。加载后可以补全子命令、常用 flags、枚举值、配置键和已保存的 profile 名称。
当前终端临时启用:
```sh
source <(ktui completion bash)
# zsh:
source <(ktui completion zsh)
# fish:
ktui completion fish | source
```
PowerShell:
```powershell
ktui completion powershell | Out-String | Invoke-Expression
```
持久启用时,把对应命令放进 `~/.bashrc`、`~/.zshrc`、fish 配置或 PowerShell profile;也可以把 `ktui completion zsh` 输出保存为 `$fpath` 目录里的 `_ktui` 文件。
## 配置文件 ## 配置文件
初始化配置: 初始化配置:
+541
View File
@@ -0,0 +1,541 @@
package main
import (
"fmt"
"os"
"strings"
"time"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"ktui/internal/config"
"ktui/internal/update"
)
var (
completionShells = []string{"bash", "zsh", "fish", "powershell"}
cobraExportFormats = []string{"markdown", "csv", "json"}
cobraConfigKeys = []string{
"profile",
"url",
"api-key",
"interval",
"timeout",
"realtime-window",
"chart-y-axis",
"warn-cpu",
"warn-ram",
"warn-disk",
"warn-expiry-days",
"mode",
"ascii",
"no-color",
}
)
func execute() error {
return newRootCommand().Execute()
}
func newRootCommand() *cobra.Command {
root := &cobra.Command{
Use: "ktui",
Short: "Komari terminal UI",
SilenceErrors: true,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
return handleTUI(changedFlagArgs(cmd))
},
}
root.CompletionOptions.DisableDefaultCmd = true
root.PersistentFlags().String("config", defaultConfigPath(), "config file path")
must(root.MarkPersistentFlagFilename("config"))
addTUIFlags(root)
addConnectionFlagCompletions(root)
addTUIFlagCompletions(root)
root.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
return setConfigEnvFromCommand(cmd)
}
root.AddCommand(
newStatusCommand(),
newExportCommand(),
newConfigCommand(),
newProfileCommand(),
newUpdateCommand(),
newVersionCommand(),
newKeysCommand(),
newCompletionCommand(),
)
return root
}
func newStatusCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "status",
Short: "Fetch once and print a node summary",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
return handleStatus(changedFlagArgs(cmd))
},
}
addStatusFlags(cmd)
addConnectionFlagCompletions(cmd)
return cmd
}
func newExportCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "export <markdown|csv|json>",
Short: "Export current node status",
ValidArgs: cobraExportFormats,
Args: cobra.RangeArgs(0, 1),
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return handleExport(nil)
}
next := append([]string{args[0]}, changedFlagArgs(cmd)...)
return handleExport(next)
},
}
addStatusFlags(cmd)
cmd.Flags().StringP("output", "o", "", "write export to a file instead of stdout")
must(cmd.MarkFlagFilename("output"))
addConnectionFlagCompletions(cmd)
return cmd
}
func newConfigCommand() *cobra.Command {
initCmd := &cobra.Command{
Use: "init",
Short: "Create the config file",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
return handleConfig(append([]string{"init"}, changedFlagArgs(cmd)...))
},
}
initCmd.Flags().Bool("force", false, "overwrite existing config")
must(initCmd.RegisterFlagCompletionFunc("force", cobra.NoFileCompletions))
cmd := &cobra.Command{
Use: "config",
Short: "Manage persistent settings",
RunE: func(cmd *cobra.Command, args []string) error {
return handleConfig(nil)
},
}
cmd.AddCommand(
initCmd,
&cobra.Command{
Use: "path",
Short: "Print the config path",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
return handleConfig([]string{"path"})
},
},
&cobra.Command{
Use: "show",
Short: "Print the config file with secrets redacted",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
return handleConfig([]string{"show"})
},
},
&cobra.Command{
Use: "set <key> <value>",
Short: "Set a config value",
Args: cobra.ExactArgs(2),
ValidArgsFunction: configSetCompletions,
RunE: func(cmd *cobra.Command, args []string) error {
return handleConfig([]string{"set", args[0], args[1]})
},
},
&cobra.Command{
Use: "help",
Short: "Show config help",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
return handleConfig([]string{"help"})
},
},
)
return cmd
}
func newProfileCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "profile",
Short: "Manage Komari connection profiles",
RunE: func(cmd *cobra.Command, args []string) error {
return handleProfile(nil)
},
}
cmd.AddCommand(
&cobra.Command{
Use: "list",
Short: "List configured profiles",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
return handleProfile([]string{"list"})
},
},
&cobra.Command{
Use: "current",
Short: "Print the active profile",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
return handleProfile([]string{"current"})
},
},
&cobra.Command{
Use: "use <name>",
Short: "Switch active profile",
Args: cobra.ExactArgs(1),
ValidArgsFunction: profileNameArgCompletions,
RunE: func(cmd *cobra.Command, args []string) error {
return handleProfile([]string{"use", args[0]})
},
},
newProfileAddCommand(),
&cobra.Command{
Use: "rename <old> <new>",
Short: "Rename a profile",
Args: cobra.ExactArgs(2),
ValidArgsFunction: profileRenameArgCompletions,
RunE: func(cmd *cobra.Command, args []string) error {
return handleProfile([]string{"rename", args[0], args[1]})
},
},
&cobra.Command{
Use: "remove <name>",
Short: "Remove a profile",
Args: cobra.ExactArgs(1),
ValidArgsFunction: profileNameArgCompletions,
RunE: func(cmd *cobra.Command, args []string) error {
return handleProfile([]string{"remove", args[0]})
},
},
&cobra.Command{
Use: "help",
Short: "Show profile help",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
return handleProfile([]string{"help"})
},
},
)
return cmd
}
func newProfileAddCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "add <name>",
Short: "Add a profile",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
next := append([]string{"add", args[0]}, changedFlagArgs(cmd)...)
return handleProfile(next)
},
}
cmd.Flags().String("url", "", "Komari base URL")
cmd.Flags().String("api-key", "", "Komari API key")
cmd.Flags().Bool("use", false, "make this profile active")
must(cmd.RegisterFlagCompletionFunc("url", cobra.NoFileCompletions))
must(cmd.RegisterFlagCompletionFunc("api-key", cobra.NoFileCompletions))
must(cmd.RegisterFlagCompletionFunc("use", cobra.NoFileCompletions))
cmd.ValidArgsFunction = cobra.NoFileCompletions
return cmd
}
func newUpdateCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "update",
Short: "Check or install ktui updates",
RunE: func(cmd *cobra.Command, args []string) error {
return handleUpdate(nil)
},
}
cmd.AddCommand(
newUpdateCheckCommand(),
newUpdateInstallCommand(),
&cobra.Command{
Use: "help",
Short: "Show update help",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
return handleUpdate([]string{"help"})
},
},
)
return cmd
}
func newUpdateCheckCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "check",
Short: "Check for a ktui update",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
return handleUpdate(append([]string{"check"}, changedFlagArgs(cmd)...))
},
}
addUpdateFlags(cmd, false)
return cmd
}
func newUpdateInstallCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "install",
Short: "Install a ktui update",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
return handleUpdate(append([]string{"install"}, changedFlagArgs(cmd)...))
},
}
addUpdateFlags(cmd, true)
return cmd
}
func newVersionCommand() *cobra.Command {
return &cobra.Command{
Use: "version",
Short: "Print version information",
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) {
printVersion()
},
}
}
func newKeysCommand() *cobra.Command {
return &cobra.Command{
Use: "keys",
Short: "Show TUI key bindings",
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) {
printKeysHelp()
},
}
}
func newCompletionCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "completion <bash|zsh|fish|powershell>",
Short: "Generate shell completion scripts",
ValidArgs: completionShells,
Args: cobra.RangeArgs(0, 1),
DisableFlagsInUseLine: true,
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return cmd.Help()
}
switch args[0] {
case "bash":
return cmd.Root().GenBashCompletion(os.Stdout)
case "zsh":
return cmd.Root().GenZshCompletion(os.Stdout)
case "fish":
return cmd.Root().GenFishCompletion(os.Stdout, true)
case "powershell":
return cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout)
default:
return fmt.Errorf("unknown completion shell %q: use bash, zsh, fish, or powershell", args[0])
}
},
}
return cmd
}
func addTUIFlags(cmd *cobra.Command) {
flags := cmd.Flags()
addConnectionFlags(flags, 10*time.Second)
flags.Duration("interval", 5*time.Second, "refresh interval")
flags.Duration("realtime-window", time.Minute, "realtime chart time window: 1m, 5m, or 10m")
flags.String("chart-y-axis", "absolute", "percent chart Y axis mode: absolute or relative")
flags.String("mode", "sheet", "view mode: sheet or line")
flags.Bool("ascii", false, "use ASCII-only rendering for terminals/fonts with Unicode issues")
flags.Bool("no-color", false, "disable ANSI color and inverse video")
}
func addStatusFlags(cmd *cobra.Command) {
addConnectionFlags(cmd.Flags(), 10*time.Second)
}
func addConnectionFlags(flags *pflag.FlagSet, timeoutDefault time.Duration) {
flags.String("profile", config.DefaultProfile, "profile name")
flags.String("url", "", "Komari base URL")
flags.String("api-key", "", "Komari API key (sent as Bearer token)")
flags.Duration("timeout", timeoutDefault, "HTTP timeout")
}
func addUpdateFlags(cmd *cobra.Command, withTag bool) {
cmd.Flags().String("api-url", update.DefaultAPIBaseURL, "Gitea repository API URL")
cmd.Flags().Duration("timeout", 60*time.Second, "update HTTP timeout")
if withTag {
cmd.Flags().String("tag", "", "install a specific release tag instead of the latest release")
must(cmd.RegisterFlagCompletionFunc("tag", cobra.NoFileCompletions))
}
must(cmd.RegisterFlagCompletionFunc("api-url", cobra.NoFileCompletions))
must(cmd.RegisterFlagCompletionFunc("timeout", cobra.NoFileCompletions))
}
func addConnectionFlagCompletions(cmd *cobra.Command) {
must(cmd.RegisterFlagCompletionFunc("profile", profileFlagCompletions))
must(cmd.RegisterFlagCompletionFunc("url", cobra.NoFileCompletions))
must(cmd.RegisterFlagCompletionFunc("api-key", cobra.NoFileCompletions))
must(cmd.RegisterFlagCompletionFunc("timeout", cobra.NoFileCompletions))
}
func addTUIFlagCompletions(cmd *cobra.Command) {
must(cmd.RegisterFlagCompletionFunc("interval", cobra.NoFileCompletions))
must(cmd.RegisterFlagCompletionFunc("realtime-window", cobra.FixedCompletions([]cobra.Completion{"1m", "5m", "10m"}, cobra.ShellCompDirectiveNoFileComp)))
must(cmd.RegisterFlagCompletionFunc("chart-y-axis", cobra.FixedCompletions([]cobra.Completion{"absolute", "relative"}, cobra.ShellCompDirectiveNoFileComp)))
must(cmd.RegisterFlagCompletionFunc("mode", cobra.FixedCompletions([]cobra.Completion{"sheet", "line"}, cobra.ShellCompDirectiveNoFileComp)))
must(cmd.RegisterFlagCompletionFunc("ascii", cobra.NoFileCompletions))
must(cmd.RegisterFlagCompletionFunc("no-color", cobra.NoFileCompletions))
}
func changedFlagArgs(cmd *cobra.Command) []string {
args := []string{}
cmd.Flags().Visit(func(flag *pflag.Flag) {
if flag.Name == "config" {
return
}
args = append(args, "--"+flag.Name+"="+flag.Value.String())
})
return args
}
func setConfigEnvFromCommand(cmd *cobra.Command) error {
value, err := cmd.Flags().GetString("config")
if err != nil {
return err
}
value = strings.TrimSpace(value)
if value == "" {
return nil
}
return os.Setenv("KTUI_CONFIG", value)
}
func defaultConfigPath() string {
path, err := config.Path()
if err != nil {
return ""
}
return path
}
func profileFlagCompletions(cmd *cobra.Command, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
return profileCompletions(cmd, toComplete), cobra.ShellCompDirectiveNoFileComp
}
func profileNameArgCompletions(cmd *cobra.Command, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
if len(args) > 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
}
return profileCompletions(cmd, toComplete), cobra.ShellCompDirectiveNoFileComp
}
func profileRenameArgCompletions(cmd *cobra.Command, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
if len(args) == 0 {
return profileCompletions(cmd, toComplete), cobra.ShellCompDirectiveNoFileComp
}
if len(args) == 1 {
return profileCompletionsExcept(cmd, toComplete, args[0]), cobra.ShellCompDirectiveNoFileComp
}
return nil, cobra.ShellCompDirectiveNoFileComp
}
func configSetCompletions(cmd *cobra.Command, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
if len(args) == 0 {
return filterCompletions(cobraConfigKeys, toComplete), cobra.ShellCompDirectiveNoFileComp
}
if len(args) == 1 {
return filterCompletions(configValueCompletionsForCobra(cmd, args[0]), toComplete), cobra.ShellCompDirectiveNoFileComp
}
return nil, cobra.ShellCompDirectiveNoFileComp
}
func configValueCompletionsForCobra(cmd *cobra.Command, key string) []string {
switch strings.TrimSpace(strings.ToLower(key)) {
case "profile":
return completionProfileNamesForCommand(cmd)
case "mode":
return []string{"sheet", "line"}
case "realtime-window", "realtime_window":
return []string{"1m", "5m", "10m"}
case "chart-y-axis", "chart_y_axis":
return []string{"absolute", "relative"}
case "ascii", "no-color", "no_color":
return []string{"true", "false"}
default:
return nil
}
}
func profileCompletions(cmd *cobra.Command, toComplete string) []cobra.Completion {
return filterCompletions(completionProfileNamesForCommand(cmd), toComplete)
}
func profileCompletionsExcept(cmd *cobra.Command, toComplete string, exclude string) []cobra.Completion {
names := completionProfileNamesForCommand(cmd)
out := make([]string, 0, len(names))
for _, name := range names {
if name != exclude {
out = append(out, name)
}
}
return filterCompletions(out, toComplete)
}
func completionProfileNamesForCommand(cmd *cobra.Command) []string {
cfg, err := loadCompletionConfig(cmd)
if err != nil {
return []string{config.DefaultProfile}
}
return cfg.ProfileNames()
}
func loadCompletionConfig(cmd *cobra.Command) (config.Config, error) {
path, _ := cmd.Flags().GetString("config")
path = strings.TrimSpace(path)
if path == "" {
cfg, _, err := config.Load()
return cfg, err
}
old, hadOld := os.LookupEnv("KTUI_CONFIG")
if err := os.Setenv("KTUI_CONFIG", path); err != nil {
return config.Config{}, err
}
defer func() {
if hadOld {
_ = os.Setenv("KTUI_CONFIG", old)
} else {
_ = os.Unsetenv("KTUI_CONFIG")
}
}()
cfg, _, err := config.Load()
return cfg, err
}
func filterCompletions(values []string, prefix string) []cobra.Completion {
out := make([]cobra.Completion, 0, len(values))
for _, value := range values {
if strings.HasPrefix(value, prefix) {
out = append(out, value)
}
}
return out
}
func must(err error) {
if err != nil {
panic(err)
}
}
+101
View File
@@ -0,0 +1,101 @@
package main
import (
"bytes"
"path/filepath"
"strings"
"testing"
"ktui/internal/config"
)
func TestCobraRootCommandTree(t *testing.T) {
root := newRootCommand()
for _, name := range []string{"status", "export", "config", "profile", "update", "version", "keys", "completion"} {
cmd, _, err := root.Find([]string{name})
if err != nil {
t.Fatalf("find %q error = %v", name, err)
}
if cmd == nil || cmd.Name() != name {
t.Fatalf("find %q = %v", name, cmd)
}
}
}
func TestCobraChangedFlagArgsSkipsConfig(t *testing.T) {
root := newRootCommand()
if err := root.ParseFlags([]string{"--mode", "line", "--config", "custom.json", "--ascii"}); err != nil {
t.Fatal(err)
}
args := changedFlagArgs(root)
if !hasString(args, "--mode=line") || !hasString(args, "--ascii=true") {
t.Fatalf("changedFlagArgs = %#v, want changed root flags", args)
}
for _, arg := range args {
if strings.HasPrefix(arg, "--config=") {
t.Fatalf("changedFlagArgs should skip config: %#v", args)
}
}
}
func TestCobraCompletionUsesConfiguredProfiles(t *testing.T) {
path := filepath.Join(t.TempDir(), "config.json")
cfg, err := config.AddProfile(config.Default(), "prod", "https://prod.example.com", "")
if err != nil {
t.Fatal(err)
}
t.Setenv("KTUI_CONFIG", path)
if _, err := config.Save(cfg); err != nil {
t.Fatal(err)
}
root := newRootCommand()
status, _, err := root.Find([]string{"status"})
if err != nil {
t.Fatal(err)
}
if err := status.ParseFlags([]string{"--config", path}); err != nil {
t.Fatal(err)
}
got := profileCompletions(status, "pr")
if !hasCompletion(got, "prod") {
t.Fatalf("profile completions = %#v, want prod", got)
}
}
func TestCobraHiddenCompletionCommand(t *testing.T) {
root := newRootCommand()
var out bytes.Buffer
root.SetOut(&out)
root.SetErr(&out)
root.SetArgs([]string{"__complete", "completion", ""})
if err := root.Execute(); err != nil {
t.Fatal(err)
}
text := out.String()
for _, want := range []string{"bash", "zsh", "fish", "powershell"} {
if !strings.Contains(text, want) {
t.Fatalf("hidden completion output missing %q:\n%s", want, text)
}
}
}
func hasString(values []string, want string) bool {
for _, value := range values {
if value == want {
return true
}
}
return false
}
func hasCompletion(values []string, want string) bool {
for _, value := range values {
if value == want {
return true
}
}
return false
}
+5 -9
View File
@@ -30,13 +30,7 @@ const (
) )
func main() { func main() {
args, configPath := splitConfigArg(os.Args[1:]) if err := execute(); err != nil {
if configPath != "" {
if err := os.Setenv("KTUI_CONFIG", configPath); err != nil {
fatal(err)
}
}
if err := handleCommand(args); err != nil {
fatal(err) fatal(err)
} }
} }
@@ -802,7 +796,7 @@ func handleProfile(args []string) error {
func handleHelp(args []string) error { func handleHelp(args []string) error {
if len(args) > 1 { if len(args) > 1 {
return fmt.Errorf("usage: ktui help [status|config|profile|keys|update|export]") return fmt.Errorf("usage: ktui help [status|config|profile|keys|update|export|completion]")
} }
if len(args) == 0 { if len(args) == 0 {
printHelp() printHelp()
@@ -880,8 +874,9 @@ Usage:
ktui config <init|path|show|set|help> ktui config <init|path|show|set|help>
ktui profile <list|current|use|add|rename|remove> ktui profile <list|current|use|add|rename|remove>
ktui update <check|install> ktui update <check|install>
ktui completion <bash|zsh|fish|powershell>
ktui version ktui version
ktui help [status|config|profile|keys|update|export] ktui help [status|config|profile|keys|update|export|completion]
Connection flags: Connection flags:
--profile NAME profile name --profile NAME profile name
@@ -908,6 +903,7 @@ Examples:
ktui version ktui version
ktui update check ktui update check
ktui update install ktui update install
ktui completion bash
ktui export markdown ktui export markdown
ktui export csv --output nodes.csv ktui export csv --output nodes.csv
ktui config init ktui config init
+2
View File
@@ -62,11 +62,13 @@ func TestHelpTextUsesRefactoredCommands(t *testing.T) {
"ktui export <markdown|csv|json> [flags]", "ktui export <markdown|csv|json> [flags]",
"ktui profile <list|current|use|add|rename|remove>", "ktui profile <list|current|use|add|rename|remove>",
"ktui update <check|install>", "ktui update <check|install>",
"ktui completion <bash|zsh|fish|powershell>",
"--profile NAME", "--profile NAME",
"--mode MODE", "--mode MODE",
"--realtime-window DURATION", "--realtime-window DURATION",
"ktui update check", "ktui update check",
"ktui update install", "ktui update install",
"ktui completion bash",
} { } {
if !strings.Contains(helpText, want) { if !strings.Contains(helpText, want) {
t.Fatalf("help text missing %q:\n%s", want, helpText) t.Fatalf("help text missing %q:\n%s", want, helpText)
+7
View File
@@ -1,3 +1,10 @@
module ktui module ktui
go 1.26 go 1.26
require (
github.com/spf13/cobra v1.10.2
github.com/spf13/pflag v1.0.9
)
require github.com/inconshreveable/mousetrap v1.1.0 // indirect
+10
View File
@@ -0,0 +1,10 @@
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=