Skip to content

gechr/clib

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

47 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

clib

A reusable Go library that plugs into existing CLI frameworks to add helpers, shell completions, and more polished help output.

Packages

Package Description
ansi Terminal-aware ANSI output
cli/cobra Cobra framework adapters
cli/kong Kong framework adapters
cli/urfave urfave/cli framework adapters
complete Shell completion generation (bash, zsh, fish)
help Structured help rendering with themed output
human Human-friendly formatting
shell Shell detection
terminal Terminal detection
theme Configurable theme (via lipgloss)

Installation

go get github.com/gechr/clib

Usage

ANSI Output

Auto-detect terminal support, or force/disable ANSI output:

w := ansi.Auto()                          // detect from os.Stdout
w := ansi.Auto(os.Stdout, os.Stderr)      // all must be terminals
w := ansi.Force()                         // always emit ANSI
w := ansi.Never()                         // plain text only
w := ansi.New(ansi.WithTerminal(true))    // manual configuration

w.Hyperlink("https://example.com", "click here")  // OSC 8 hyperlink

// Control how hyperlinks render in non-terminal output:
w = ansi.New(ansi.WithHyperlinkFallback(ansi.HyperlinkFallbackMarkdown))
// HyperlinkFallbackExpanded (default) β†’ "text (url)"
// HyperlinkFallbackMarkdown           β†’ "[text](url)"
// HyperlinkFallbackText               β†’ "text"
// HyperlinkFallbackURL                β†’ "url"

Terminal Detection

if terminal.Is(os.Stdout) {
    // stdout is a terminal
}

width := terminal.Width(os.Stdout)  // column count, 0 if not a terminal

Theme

Create a theme with defaults or customize individual styles:

// Use defaults.
th := theme.Default()

// Or customize with options.
th := theme.Default().With(
    theme.WithRed(lipgloss.NewStyle().Foreground(lipgloss.Color("9"))),
    theme.WithEntityColors([]lipgloss.Color{"208", "51", "226"}),
)

Help Rendering

Build structured help output. Content blocks implement the help.Content interface: FlagGroup, Args, Usage, Text, Examples, CommandGroup, and nested *Section.

hr := help.NewRenderer(th)

sections := []help.Section{
    {Title: "Flags", Content: []help.Content{
        help.FlagGroup{
            {Short: "q", Long: "query", Placeholder: "text", Desc: "Filter results by query"},
            {
                Short:        "f",
                Long:         "format",
                Placeholder:  "format",
                Desc:         "Output format",
                Enum:         []string{"table", "json", "yaml"},
                EnumDefault:  "table",
            },
        },
    }},
    {Title: "Examples", Content: []help.Content{
        help.Examples{
            {Comment: "List items in JSON format", Command: "catalog list --format json"},
        },
    }},
}

if err := hr.Render(os.Stdout, sections); err != nil {
    return err
}

Flag fields use bare names - the renderer adds dashes (-/--) and angle brackets (</>) automatically. Sections can be nested by including a *help.Section as content.

Framework Adapters

Each adapter provides the same core capabilities for its framework: Extend (or struct tags for Kong), FlagMeta, Sections/HelpFunc, NewCompletion, and CSVFlag.

Annotate your CLI struct with Kong-style tags and a clib:"..." tag for clib-specific metadata (grouping, descriptions, completions, placeholders):

type CLI struct {
    clib.CompletionFlags

    Query  string        `name:"query"  short:"q" help:"Filter results by query" clib:"group='Filters',terse='Query',complete='predictor=query',order=keep"`
    Fields clib.CSVFlag  `name:"fields" help:"Fields to show"                    clib:"complete='predictor=field,comma'"`
    Format string        `name:"format" short:"f" help:"Output format" default:"table" enum:"table,json,yaml"`
}

flags := clib.Reflect(&CLI{})
gen := complete.NewGenerator("catalog").FromFlags(flags)
gen.Install("fish", false)

Integrate with Kong's help system using HelpPrinter or HelpPrinterFunc:

r := help.NewRenderer(th)
k := konglib.Must(&cli,
  konglib.Help(clib.HelpPrinterFunc(r, clib.NodeSectionsFunc())),
)

Use Extend to attach clib metadata to pflag flags:

f := root.Flags()
f.StringP("query", "q", "", "Filter results by query")
f.StringP("format", "f", "table", "Output format")
cobraFields := &cobracli.CSVFlag{}
f.Var(cobraFields, "fields", "Fields to show")

cobracli.Extend(f.Lookup("query"), cobracli.FlagExtra{
  Group: "Filters", Placeholder: "text", Complete: "predictor=query", Order: complete.OrderKeep,
})
cobracli.Extend(f.Lookup("format"), cobracli.FlagExtra{
  Group: "Output", Placeholder: "format", Enum: []string{"table", "json", "yaml"}, EnumDefault: "table",
})
cobracli.Extend(f.Lookup("fields"), cobracli.FlagExtra{
  Complete: "predictor=field,comma",
})

// Auto-grouped help using extras.
root.SetHelpFunc(cobracli.HelpFunc(r, cobracli.Sections))

// Completion flags (hidden).
comp := cobracli.NewCompletion(root)
gen := complete.NewGenerator("catalog").FromFlags(cobracli.FlagMeta(root))
handled, err := comp.Handle(gen, nil)

Use Extend to attach clib metadata to urfave/cli flags:

queryFlag := &clilib.StringFlag{Name: "query", Aliases: []string{"q"}, Usage: "Filter results by query"}
formatFlag := &clilib.StringFlag{Name: "format", Aliases: []string{"f"}, Usage: "Output format", Value: "table"}
fieldsFlag := &clilib.GenericFlag{Name: "fields", Usage: "Fields to show", Value: &cliurfave.CSVFlag{}}

cliurfave.Extend(queryFlag, cliurfave.FlagExtra{
  Group: "Filters", Placeholder: "text", Complete: "predictor=query", Order: complete.OrderKeep,
})
cliurfave.Extend(formatFlag, cliurfave.FlagExtra{
  Group: "Output", Placeholder: "format", Enum: []string{"table", "json", "yaml"}, EnumDefault: "table",
})
cliurfave.Extend(fieldsFlag, cliurfave.FlagExtra{
  Complete: "predictor=field,comma",
})

// Custom help using clib themed renderer.
clilib.HelpPrinter = cliurfave.HelpPrinter(r, cliurfave.Sections)

// Completion flags (hidden).
comp := cliurfave.NewCompletion(root)
gen := complete.NewGenerator("catalog").FromFlags(cliurfave.FlagMeta(cmd))
handled, err := comp.Handle(gen, nil)

Completions

The complete package generates shell completion scripts from flag metadata. The complete tag (Kong) or FlagExtra.Complete field (Cobra/urfave) controls completion behavior:

  • predictor=<name> - dynamic completion via <app> --@complete=<name>
  • comma - comma-separated multi-value mode
  • values=<space-separated> - static completion values

The terse key provides a very short description for completions (falls back to help). Use order=keep (Kong) or Order: complete.OrderKeep (Cobra/urfave) to preserve fish completion order for a flag by emitting complete -k. Use order=shell or Order: complete.OrderShell to force the shell's normal ordering for a flag. Use complete.WithOrder(complete.OrderKeep) to make keep-order the generator default.

Time-Ago

styled  := th.RenderTimeAgo(someTime, true)     // colored based on thresholds
plain   := human.FormatTimeAgo(someTime)        // "3 hours ago"
compact := human.FormatTimeAgoCompact(someTime) // "3h ago"

Path Formatting

human.ContractHome("/Users/alice/Documents")  // "~/Documents"

Enum Formatting

// Styled enum with shortcut letters: [text, json, yaml]
th.FmtEnum([]theme.EnumValue{
  {Name: "text", Bold: "t"},
  {Name: "json", Bold: "j"},
  {Name: "yaml", Bold: "y"},
})

// With default annotation: [text, json, yaml] (default: text)
th.FmtEnumDefault("text", []theme.EnumValue{
  {Name: "text", Bold: "t"},
  {Name: "json", Bold: "j"},
  {Name: "yaml", Bold: "y"},
})

// Dim default/note annotations for flag descriptions.
th.DimDefault("30")    // (default: 30)
th.DimNote("required") // (required)

Markdown Rendering

Render short markdown strings for inline display with themed code spans:

th.RenderMarkdown("Use `--verbose` for debug output")

Shell Detection

shell := shell.Detect() // COMPLETE_SHELL env -> parent process -> SHELL env

Examples

Working examples for each framework adapter are in the examples/ directory:

About

πŸ“š Rich shell completions, styled help and utilities for Go command-line tools

Resources

Stars

Watchers

Forks

Contributors