cli

package module
v0.0.0-...-3e500c5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 11, 2026 License: MIT Imports: 19 Imported by: 0

README

cli

Go Reference Go Report Card CI codecov

A composable CLI framework for Go built on small interfaces.

Commands are Go types that implement Runner. The framework discovers capabilities through type assertions on optional interfaces — there is no base struct to embed and no configuration DSL. Think io.Reader for CLIs.

Install

go get github.com/bjaus/cli

Quick Start

package main

import (
    "context"
    "fmt"
    "os"

    "github.com/bjaus/cli"
)

type GreetCmd struct {
    Name string `flag:"name" short:"n" default:"World" help:"Who to greet"`
}

func (g *GreetCmd) Run(_ context.Context) error {
    fmt.Printf("Hello, %s!\n", g.Name)
    return nil
}

func main() {
    cli.ExecuteAndExit(context.Background(), &GreetCmd{}, os.Args)
}
$ greet --name Alice
Hello, Alice!

Design Philosophy

  • Small interfaces — each interface has one method. Implement only what you need.
  • Composition over configuration — no YAML, no builder chains, no struct embedding. Wire commands with plain Go constructors.
  • Type assertion discovery — the framework discovers capabilities at runtime. A command that implements Namer gets a custom name. One that doesn't gets its struct type name.
  • Everything is replaceable — flag parsing, help rendering, suggestions. The framework provides defaults but imposes nothing.

Core Interface

Every command must implement Runner:

type Runner interface {
    Run(ctx context.Context) error
}

Positional arguments are available via the Args field (see Positional Arguments).

For simple cases, RunFunc adapts a plain function:

cmd := cli.RunFunc(func(ctx context.Context) error {
    fmt.Println("Hello!")
    return nil
})

Flags

The default flag parser reads struct tags. Fields tagged with flag become CLI flags.

type ServeCmd struct {
    Port    int           `flag:"port" short:"p" default:"8080" help:"Port to listen on" env:"PORT"`
    Host    string        `flag:"host" default:"localhost" help:"Host to bind to"`
    Tags    []string      `flag:"tag" short:"t" help:"Tags to apply"`
    Env     map[string]string `flag:"env" help:"Environment variables as key=value"`
    Format  string        `flag:"format" enum:"text,json,yaml" default:"text" help:"Output format"`
    Verbose int           `flag:"verbose" short:"v" counter:"true" help:"Increase verbosity"`
    Color   bool          `flag:"color" default:"true" negate:"true" help:"Colorize output"`
}
Supported types

string, int, int64, float64, bool, time.Duration, slices of any scalar type, map[string]string, and any type implementing FlagUnmarshaler.

Struct tag reference
Tag Description
flag Flag name (required to register the field)
short Single-character short form
default Default value if not provided
help Description shown in help output
env Environment variable fallback
enum Comma-separated allowed values
required "true" to require the flag
counter "true" to increment an int per occurrence
negate "true" to add --no- prefix for bool flags
alt Comma-separated additional long flag names
sep Separator for splitting values into slice elements
mask Displayed instead of default in help (e.g. "****")
placeholder Value label shown in help (e.g. "PORT" in --port PORT)
prefix Flag name prefix for named struct fields (e.g. "db-")
Priority

explicit flag > environment variable > default > zero value

Flags anywhere

Flags can appear before or after subcommand names. Both of these work:

myapp --verbose serve --port 8080
myapp serve --verbose --port 8080
Slice flags

Repeat a flag to accumulate values:

Tags []string `flag:"tag" short:"t"`
$ app --tag v1 --tag latest
Map flags

Pass key=value pairs:

Env map[string]string `flag:"env"`
$ app --env DB_HOST=localhost --env DB_PORT=5432
Enum validation

Restrict a flag to allowed values. Invalid values produce a clear error automatically.

Format string `flag:"format" enum:"text,json,yaml" default:"text"`
$ app --format xml
Error: invalid flag value: --format must be one of [text,json,yaml]
Counter flags

An int that increments per occurrence. Classic verbosity pattern:

Verbose int `flag:"verbose" short:"v" counter:"true"`
$ app -v -v -v    # Verbose = 3
Negatable bools

Add --no- prefix to explicitly disable a default-on flag:

Color bool `flag:"color" default:"true" negate:"true"`
$ app --no-color  # Color = false
Embedded structs

Anonymous embedded structs have their flags promoted:

type OutputFlags struct {
    Format string `flag:"format" enum:"json,table" default:"table"`
}

type ListCmd struct {
    OutputFlags
    Limit int `flag:"limit" default:"50"`
}
Prefix

Named struct fields with prefix namespace their flags:

type DBFlags struct {
    Host string `flag:"host" default:"localhost"`
    Port int    `flag:"port" default:"5432"`
}

type ServeCmd struct {
    DB   DBFlags `prefix:"db-"`  // --db-host, --db-port
    Port int     `flag:"port" default:"8080"`
}
Custom flag types

Implement FlagUnmarshaler for custom parsing:

type LogLevel int

func (l *LogLevel) UnmarshalFlag(value string) error {
    switch value {
    case "debug":
        *l = 0
    case "info":
        *l = 1
    case "error":
        *l = 2
    default:
        return fmt.Errorf("unknown log level: %s", value)
    }
    return nil
}

Subcommands

Implement Parent to declare subcommands:

type App struct{}

func (a *App) Run(_ context.Context) error { return nil }
func (a *App) Name() string                { return "myapp" }
func (a *App) Subcommands() []cli.Runner {
    return []cli.Runner{&ServeCmd{}, &MigrateCmd{}}
}
$ myapp serve --port 9090
$ myapp migrate up
Prefix matching

Enable unique prefix resolution so users can abbreviate subcommand names:

cli.Execute(ctx, root, args, cli.WithPrefixMatching(true))
$ myapp ser    # matches "serve" if unambiguous
Fallback command

Implement Fallbacker to provide a default subcommand when none is specified:

func (a *App) Fallback() cli.Runner { return &ServeCmd{} }
$ myapp          # runs ServeCmd automatically
$ myapp serve    # also runs ServeCmd

Discovery Interfaces

All optional. Implement any combination:

Interface Method Purpose
Namer Name() string Override command name (default: lowercase struct type)
Describer Description() string One-line description for help
Aliaser Aliases() []string Alternate names
Parent Subcommands() []Runner Declare subcommands
Hider Hidden() bool Hide from help output
Exampler Examples() []Example Usage examples
Versioner Version() string Version string for --version
Deprecator Deprecated() string Deprecation warning to stderr
Categorizer Category() string Group subcommands in help
Fallbacker Fallback() Runner Default subcommand
Version

Implement Versioner on your root command. --version or -V prints it and exits.

func (a *App) Version() string { return "2.1.0" }
$ myapp --version
2.1.0
Deprecation

Implement Deprecator to warn users. The command still runs, but a warning is printed to stderr.

func (o *OldCmd) Deprecated() string { return "use new-cmd instead" }
$ myapp old-cmd
Warning: "old-cmd" is deprecated: use new-cmd instead
Categories

Implement Categorizer to group subcommands under headings in help output:

func (a *AdminCmd) Category() string { return "Admin Commands" }
Commands:
  run    Run the app

Admin Commands:
  users  Manage users

Lifecycle Hooks

Interface Method When
Beforer Before(ctx) (ctx, error) Before Run, parent-first. Returns modified context.
Afterer After(ctx) error After Run, child-first. Always runs.
Validator Validate(provided map[string]bool) error After flag parsing, before Run.
func (a *App) Before(ctx context.Context) (context.Context, error) {
    db, err := sql.Open("postgres", a.DSN)
    if err != nil {
        return ctx, err
    }
    return context.WithValue(ctx, dbKey, db), nil
}

func (a *App) After(_ context.Context) error {
    return a.db.Close()
}

Middleware

Implement Middlewarer to wrap the run function:

func (c *Cmd) Middleware() []func(next cli.RunFunc) cli.RunFunc {
    return []func(next cli.RunFunc) cli.RunFunc{
        func(next cli.RunFunc) cli.RunFunc {
            return func(ctx context.Context) error {
                start := time.Now()
                err := next(ctx)
                log.Printf("took %s", time.Since(start))
                return err
            }
        },
    }
}

Short Option Combining

Enable POSIX-style short option combining globally:

cli.Execute(ctx, root, args, cli.WithShortOptionHandling(true))
$ app -alr    # expands to -a -l -r
$ app -vvv    # works with counters too

All flags except the last must be bool or counter (no value). The last flag may take a value from the next argument.

Positional Arguments

Commands that need positional arguments declare an Args field:

type CopyCmd struct {
    Args    cli.Args
    Verbose bool `flag:"verbose" short:"v"`
}

func (c *CopyCmd) Run(_ context.Context) error {
    for _, file := range c.Args {
        fmt.Println("copying", file)
    }
    return nil
}
$ copy -v file1.txt file2.txt
copying file1.txt
copying file2.txt

Dependency Injection with Bind

Use cli.Bind to inject dependencies into commands by type:

func main() {
    db := openDB()
    cache := redis.New()

    cli.ExecuteAndExit(ctx, &App{}, os.Args,
        cli.Bind(db),                        // inject *sql.DB
        cli.BindTo(cache, (*Cache)(nil)),    // inject as interface
    )
}

type ServeCmd struct {
    DB    *sql.DB  // injected by type match
    Cache Cache    // injected as interface
    Port  int      `flag:"port" default:"8080"`
}

func (s *ServeCmd) Run(_ context.Context) error {
    // s.DB and s.Cache are ready to use
    return nil
}

Fields with flag:, arg:, or env: tags are not eligible for injection.

Error Handling

Execute returns errors. ExecuteAndExit wraps it with os.Exit:

// In main — pass os.Args directly, the framework strips the program name:
cli.ExecuteAndExit(ctx, root, os.Args)

// Or handle errors yourself:
if err := cli.Execute(ctx, root, os.Args); err != nil {
    log.Fatal(err)
}

Return cli.Exit for custom exit codes:

return cli.Exit("port already in use", 2)

Extensibility

Every major subsystem is replaceable per-command or globally:

Interface Method Scope
Helper Help() string Per-command help override
FlagParser ParseFlags(cmd, args) (remaining, error) Custom flag parsing
HelpRenderer RenderHelp(cmd, chain, flags, args, globalFlags) string Custom help rendering
Suggester Suggest(name) string Custom "did you mean?"

Global overrides via options:

cli.Execute(ctx, root, args,
    cli.WithFlagParser(myParser),
    cli.WithHelpRenderer(myRenderer),
    cli.WithSuggest(false),
)

Manual Dependency Injection

Beyond cli.Bind, two manual patterns work well:

Constructor wiring — parent builds children with dependencies:

func (a *App) Subcommands() []cli.Runner {
    return []cli.Runner{&ServeCmd{DB: a.db, Logger: a.logger}}
}

Context enrichment — cross-cutting concerns flow through context via Beforer:

func (a *App) Before(ctx context.Context) (context.Context, error) {
    return context.WithValue(ctx, loggerKey, a.logger), nil
}

Execution Pipeline

1. Resolve command tree (flags-anywhere aware)
2. Check --version
3. Check --help
4. Parse flags for each command in chain
5. Validate (Validator interface on leaf)
6. Print deprecation warnings
7. Before hooks (parent-first, context flows forward)
8. Run with middleware
9. After hooks (child-first, always runs)

Options

Option Default Description
WithStdout(w) os.Stdout Standard output writer
WithStderr(w) os.Stderr Standard error writer
WithFlagParser(p) struct tags Global flag parser
WithHelpRenderer(r) built-in Global help renderer
WithSuggest(bool) true "Did you mean?" suggestions
WithShortOptionHandling(bool) false POSIX short option combining
WithPrefixMatching(bool) false Unique prefix subcommand matching
Bind(v) Inject dependency by concrete type
BindTo(v, iface) Inject dependency as interface type

Contributing

See CONTRIBUTING.md for guidelines.

License

MIT

Documentation

Overview

Package cli is a composable CLI framework built on small interfaces.

Commands are Go types that implement Commander. The framework discovers capabilities through type assertions on optional interfaces — there is no base struct to embed and no configuration DSL.

Core Interface

Every command must implement Commander:

type Commander interface {
    Run(ctx context.Context, args []string) error
}

RunFunc adapts a plain function into a Commander for simple cases.

Discovery Interfaces

Implement any combination of these optional interfaces to declare metadata, subcommands, aliases, examples, and hidden status:

  • Namer — override the command name (default: lowercase struct type name)
  • Descriptor — provide a one-line description
  • LongDescriptor — provide extended description for help output
  • Aliaser — declare alternate names
  • Subcommander — return subcommands
  • Hider — hide the command from help output
  • Exampler — provide usage examples
  • Versioner — report a version string via --version / -V
  • Deprecator — mark a command as deprecated with a warning message
  • Categorizer — group subcommands under headings in help output
  • Fallbacker — provide a fallback subcommand when no name matches
  • Exiter — control error printing and process exit in ExecuteAndExit
  • Discoverer — provide runtime-discovered commands (plugins)

Lifecycle Interfaces

  • Beforer — run setup logic before Run (parent-first), returns modified context
  • Afterer — run teardown logic after Run (child-first, always runs)
  • Validator — validate state after flag parsing, before Run

Flags

The default flag parser reads struct tags:

type ServeCmd struct {
    Port    int           `flag:"port" short:"p" default:"8080" help:"Port to listen on" env:"PORT"`
    Host    string        `flag:"host" default:"localhost" help:"Host to bind to"`
    Tags    []string      `flag:"tag" short:"t" help:"Tags to apply (repeatable)"`
    Env     map[string]string `flag:"env" help:"Environment variables as key=value"`
    Format  string        `flag:"format" enum:"text,json,yaml" default:"text" help:"Output format"`
    Verbose int           `flag:"verbose" short:"v" counter:"true" help:"Increase verbosity"`
    Color   bool          `flag:"color" default:"true" negate:"true" help:"Colorize output"`
}

Supported types: string, int, int64, float64, bool, time.Duration, slices of any scalar type, map[string]string, and any type implementing FlagUnmarshaler.

Struct tag keys:

  • flag — the flag name (if empty, derived from field name: OutputFormat → output-format)
  • short — single-character short form
  • default — default value if not provided
  • help — description shown in help output
  • env — environment variable name; standalone (without flag/arg) for env/config/default-only fields
  • enum — comma-separated list of allowed values
  • required — "true" to require the flag
  • counter — "true" to increment an int on each occurrence (-vvv)
  • negate — "true" to add a --no- prefix that sets a bool to false
  • alt — comma-separated additional long flag names (e.g. "output,out")
  • sep — separator for splitting a single value into slice elements (e.g. ",")
  • hidden — "true" to hide the flag from help output (flag still works)
  • deprecated — message shown when the flag is used; prints a warning to stderr
  • category — group heading for the flag in help output
  • mask — displayed instead of default in help (e.g. "****" for secrets)
  • placeholder — value name shown in help (e.g. "PORT" in --port PORT)
  • prefix — flag name prefix for named struct fields (e.g. "db-" yields --db-host)

Priority: explicit flag > env var > config > default > zero value.

Use WithEnvVarPrefix to scope all env var lookups under a common prefix. For example, WithEnvVarPrefix("APP_") causes `env:"PORT"` to look up APP_PORT.

Flags can appear anywhere — before or after subcommand names.

The framework performs strict tag validation at parse time. Invalid combinations (flag + arg, required + default, flag-only tags without flag, etc.) return ErrInvalidTag.

Env-Only Fields

A field with an env tag but no flag or arg tag is env/config/default-only. It does not appear in help output and cannot be passed via command-line arguments. This is useful for secrets that should never appear in shell history:

type DeployCmd struct {
    Token string `env:"DEPLOY_TOKEN" required:"true"`
    Env   string `flag:"env" enum:"prod,staging,dev" help:"Target environment"`
}

The logical name is derived from the field name (Token → token) and is used for config resolver lookups and context storage via Set/Get.

Priority for env-only fields: env var > config > default > zero value.

Embedded Structs and Prefix

Anonymous embedded structs have their flags promoted, just like Go's own field promotion:

type OutputFlags struct {
    Format string `flag:"format" enum:"json,table" default:"table" help:"Output format"`
}
type ListCmd struct {
    OutputFlags // promoted: --format works as if declared on ListCmd
    Limit int   `flag:"limit" default:"50"`
}

Named struct fields with the prefix tag namespace their flags:

type DBFlags struct {
    Host string `flag:"host" default:"localhost" help:"Database host"`
    Port int    `flag:"port" default:"5432" help:"Database port"`
}
type ServeCmd struct {
    DB   DBFlags `prefix:"db-"`  // --db-host, --db-port
    Port int     `flag:"port" default:"8080" help:"Listen port"`
}

Prefixes nest: a prefix:"a-" containing prefix:"b-" yields --a-b-name. When an outer and embedded field share a flag name, the outer field wins (matching Go's own promotion semantics).

Inheritance

Flags flow from parent commands to child subcommands via automatic flag inheritance: when a parent and child both declare a flag with the same name and type, the child inherits the parent's parsed value if the child's flag was not explicitly provided via CLI args or env var. The child's flag still appears in help output and accepts CLI args normally.

type App struct {
    Env string `flag:"env" required:"true" enum:"dev,qa,prod" help:"Target environment"`
}
type ServeCmd struct {
    Env  string `flag:"env" help:"Target environment"`
    Port int    `flag:"port" default:"8080" help:"Listen port"`
}

To inherit a parent flag without exposing it as a child flag, use a hidden flag with the same name:

type ServeCmd struct {
    Env  string `flag:"env" hidden:"true"`
    Port int    `flag:"port" default:"8080" help:"Listen port"`
}

Priority for automatic flag inheritance: explicit child flag > child env var > inherited from parent > child default > zero value.

Context Values

The framework automatically stores every parsed flag value in the context before Beforer hooks run. Subcommands can retrieve any ancestor's flag value without declaring struct fields:

func (s *ServeCmd) Run(ctx context.Context, args []string) error {
    env := cli.Get[string](ctx, "env") // from parent's --env flag
    // ...
}

Three functions are provided:

  • Set — store a named value in the context (returns new context)
  • Get — retrieve a value by name; returns zero value if missing or type mismatch
  • Lookup — retrieve a value by name; returns (value, ok) for safe checking

User code can also call Set in a [Beforer.Before] hook to share arbitrary values (database connections, loggers, etc.) with downstream commands:

func (a *App) Before(ctx context.Context) (context.Context, error) {
    return cli.Set(ctx, "db", a.db), nil
}

This complements struct-based inheritance: use context values when you want loose coupling or need to share non-flag data.

Config

Flag values can be loaded from external configuration sources via a ConfigResolver. A resolver is a single function:

type ConfigResolver func(key ConfigKey) (value string, found bool)

Given a ConfigKey, it returns the string value and whether it was found. The key provides both the full flag name ([ConfigKey.Name]) and decomposed parts ([ConfigKey.Parts]) for resolvers backed by nested formats. The framework handles all type conversion, validation, required checks, and enum enforcement — the resolver only needs to return strings.

Priority chain: explicit CLI flag > env var > config > default > zero value.

Set a global resolver via WithConfigResolver:

f, _ := os.Open("config.json")
resolver, _ := config.FromJSON(f)
cli.Execute(ctx, root, os.Args[1:],
    cli.WithConfigResolver(resolver),
)

Or implement ConfigProvider on a command for per-command resolvers:

func (c *ServeCmd) ConfigResolver() cli.ConfigResolver {
    return config.FromMap(map[string]string{"port": "9090"})
}

Command-level resolvers take priority over the global resolver. Use [config.Chain] to try multiple sources in order:

resolver := config.Chain(
    config.FromMap(overrides),
    jsonResolver,
)

The [config] subpackage ships [config.FromMap], [config.FromJSON], and [config.Chain]. Because ConfigResolver is a plain function and [config.FromMap] accepts any map[string]string, adding support for any configuration format — YAML, TOML, HCL, .env files, remote stores — is a matter of decoding into a map and calling [config.FromMap]. See the [config] package documentation for copy-paste adapter examples.

Flag Groups

Flags can be constrained to enforce relationships using FlagGrouper:

func (c *Cmd) FlagGroups() []cli.FlagGroup {
    return []cli.FlagGroup{
        cli.MutuallyExclusive("json", "yaml", "text"),
        cli.RequiredTogether("username", "password"),
        cli.OneRequired("file", "stdin"),
    }
}

Three constraint kinds are supported:

Validation runs after flag parsing and inheritance, before Validator.

Plugins

Commands can be extended at runtime with external executables (plugins). A command that implements Discoverer provides additional subcommands discovered at runtime, merged with any static subcommands from Subcommander. Built-in commands always take priority on name collisions.

The Discover function scans directories and optionally the system PATH for plugin executables:

func (a *App) Discover() ([]cli.Commander, error) {
    return cli.Discover("myapp",
        cli.WithDirs(cli.DefaultDirs("myapp")...),
        cli.WithPATH(),
    )
}

DefaultDirs returns conventional plugin directories in priority order:

  1. ./<name>/plugins — project-level (highest priority)
  2. $HOME/.config/<name>/plugins — user-level
  3. /etc/<name>/plugins — system-level (Unix only)

These paths are configurable. Use WithDir and WithDirs to specify custom directories in any order, and WithPATH to optionally scan PATH for executables matching "<prefix>-<command>".

Plugin Metadata Protocol

When a plugin is discovered, the framework executes it with the --cli-info flag (customizable via WithInfoFlag) and expects optional JSON:

{"name":"deploy","description":"Deploy to cloud","aliases":["d"]}

If the plugin does not support the flag or returns invalid JSON, it still works — it just has no description or aliases in help output. The only requirement for a plugin is that it be an executable file.

This means plugins can be written in any language:

#!/bin/bash
if [ "$1" = "--cli-info" ]; then
    echo '{"name":"deploy","description":"Deploy to cloud environments"}'
    exit 0
fi
echo "deploying to $1..."

Plugin Discovery Modes

Directory-based discovery (primary): all executable files in the scanned directories become plugins. The filename is the command name.

PATH-based discovery (via WithPATH): executables matching "<prefix>-*" on the system PATH are discovered. The prefix and hyphen are stripped to derive the command name (e.g., "myapp-deploy" → "deploy").

Priority: directories are scanned first in the order added. PATH results have lower priority than any directory result. First match wins on name collision.

Enumerating All Subcommands

AllSubcommands returns the merged set of static and discovered subcommands for a command. This is useful for custom HelpRenderer implementations, documentation generators, and shell completion scripts:

subs, err := cli.AllSubcommands(cmd)

Extensibility

Every major subsystem is replaceable:

  • FlagParser — replace the flag parsing engine per-command or globally
  • HelpRenderer — replace help rendering per-command or globally
  • Helper — override help text for a single command
  • HelpAppender / HelpPrepender — add custom sections to the default help output
  • Middlewarer — wrap the run function with middleware
  • Suggester — custom "did you mean?" per-command

Global overrides are set via Option functions passed to Execute:

cli.Execute(ctx, root, os.Args[1:],
    cli.WithStdout(os.Stdout),
    cli.WithFlagParser(myParser),
    cli.WithShortOptionHandling(true),
    cli.WithPrefixMatching(true),
)

Documentation Generation

The doc subpackage generates documentation from the command tree:

doc.GenMarkdown(root)              // markdown for a single command
doc.GenMarkdownTree(root, "docs/") // markdown files for all commands
doc.GenManPage(root, header)       // man page for a single command
doc.GenManTree(root, "man/", header) // man pages for all commands

Hidden commands and flags are excluded from generated documentation.

Shell Completion

The completion subpackage generates shell completion scripts:

completion.Bash(root, "myapp")       // bash completion script
completion.Zsh(root, "myapp")        // zsh completion script
completion.Fish(root, "myapp")       // fish completion script
completion.PowerShell(root, "myapp") // PowerShell completion script

Generated scripts call the binary at runtime via the __complete protocol: when the binary receives "__complete" as the first argument, it runs RuntimeComplete instead of normal execution. This avoids side effects (no lifecycle hooks, flag parsing, or validation) and provides dynamic completions based on the current command tree.

Commands implementing Completer can provide custom completion candidates. When a command implements Completer, its Complete method is called during tab-completion and the returned strings are offered as candidates. The returned ShellCompDirective controls shell behavior (e.g. suppressing space or file completion). If Complete returns nil completions, the framework falls back to static completion of subcommands and flags.

Example
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/bjaus/cli"
)

// A complete CLI with a root command, subcommand, and flags.
// This is the typical starting point for building a CLI with this package.
type exRoot struct{}

func (r *exRoot) Name() string        { return "mytool" }
func (r *exRoot) Description() string { return "A CLI built with the cli package" }
func (r *exRoot) Version() string     { return "1.0.0" }
func (r *exRoot) Subcommands() []cli.Commander {
	return []cli.Commander{&exHello{}}
}

func (r *exRoot) Run(_ context.Context) error {
	return cli.ErrShowHelp
}

type exHello struct {
	Recipient string `flag:"name" short:"n" default:"World" help:"Who to greet"`
}

func (h *exHello) Name() string        { return "hello" }
func (h *exHello) Description() string { return "Say hello" }
func (h *exHello) Run(_ context.Context) error {
	fmt.Fprintf(os.Stdout, "Hello, %s!\n", h.Recipient)
	return nil
}

func main() {
	app := &exRoot{}
	_ = cli.Execute(context.Background(), app, []string{"hello", "--name", "Alice"}, cli.WithStdout(os.Stdout)) //nolint:errcheck // example
}
Output:

Hello, Alice!
Example (Help)
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/bjaus/cli"
)

// A complete CLI with a root command, subcommand, and flags.
// This is the typical starting point for building a CLI with this package.
type exRoot struct{}

func (r *exRoot) Name() string        { return "mytool" }
func (r *exRoot) Description() string { return "A CLI built with the cli package" }
func (r *exRoot) Version() string     { return "1.0.0" }
func (r *exRoot) Subcommands() []cli.Commander {
	return []cli.Commander{&exHello{}}
}

func (r *exRoot) Run(_ context.Context) error {
	return cli.ErrShowHelp
}

type exHello struct {
	Recipient string `flag:"name" short:"n" default:"World" help:"Who to greet"`
}

func (h *exHello) Name() string        { return "hello" }
func (h *exHello) Description() string { return "Say hello" }
func (h *exHello) Run(_ context.Context) error {
	fmt.Fprintf(os.Stdout, "Hello, %s!\n", h.Recipient)
	return nil
}

func main() {
	app := &exRoot{}
	_ = cli.Execute(context.Background(), app, []string{"--help"}, cli.WithStdout(os.Stdout)) //nolint:errcheck // example
}
Output:

A CLI built with the cli package

Usage:
  mytool [command]
  mytool [args...]

Commands:
  hello  Say hello

Use "mytool [command] --help" for more information about a command.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrUnknownFlag      = errors.New("unknown flag")
	ErrFlagRequiresVal  = errors.New("flag requires a value")
	ErrRequiredFlag     = errors.New("required flag not provided")
	ErrUnsupportedType  = errors.New("unsupported flag type")
	ErrInvalidFlagValue = errors.New("invalid flag value")
	ErrInvalidTag       = errors.New("invalid struct tag")
)

Sentinel errors for flag parsing.

View Source
var ErrShowHelp = errors.New("show help")

ErrShowHelp can be returned from [Commander.Run] to make the framework render help for the current command. The error is not propagated to the caller.

Functions

func DefaultDirs

func DefaultDirs(name string) []string

DefaultDirs returns the conventional plugin directories for the given application name, in priority order:

  1. ./<name>/plugins — project-level (highest priority)
  2. $HOME/.config/<name>/plugins — user-level
  3. /etc/<name>/plugins — system-level (lowest priority, Unix only)

Missing directories are silently skipped by Discover. The returned paths are suitable for passing directly to WithDirs:

cli.Discover("myapp", cli.WithDirs(cli.DefaultDirs("myapp")...))

func ExactArgs

func ExactArgs(n int) func([]string) error

ExactArgs returns an arg validator that requires exactly n arguments.

func Execute

func Execute(ctx context.Context, root Commander, args []string, opts ...Option) error

Execute runs the command tree rooted at root with the given args and options. It resolves subcommands, parses flags, runs lifecycle hooks, and executes the target command.

Example (Categories)
package main

import (
	"context"
	"os"

	"github.com/bjaus/cli"
)

// Categorizer interface groups subcommands in help output.
// Use to organize large CLIs with many subcommands into logical groups.
type AdminCmd struct{}

func (a *AdminCmd) Run(_ context.Context) error { return nil }
func (a *AdminCmd) Name() string                { return "users" }
func (a *AdminCmd) Description() string         { return "Manage users" }
func (a *AdminCmd) Category() string            { return "Admin Commands" }

type CoreCmd struct{}

func (c *CoreCmd) Run(_ context.Context) error { return nil }
func (c *CoreCmd) Name() string                { return "run" }
func (c *CoreCmd) Description() string         { return "Run the app" }

type CategorizedApp struct{}

func (a *CategorizedApp) Run(_ context.Context) error { return nil }
func (a *CategorizedApp) Name() string                { return "myapp" }
func (a *CategorizedApp) Description() string         { return "Categorized example" }
func (a *CategorizedApp) Subcommands() []cli.Commander {
	return []cli.Commander{&CoreCmd{}, &AdminCmd{}}
}

func main() {
	app := &CategorizedApp{}
	_ = cli.Execute(context.Background(), app, []string{"--help"}, cli.WithStdout(os.Stdout)) //nolint:errcheck // example
}
Output:

Categorized example

Usage:
  myapp [command]
  myapp [args...]

Commands:
  run    Run the app

Admin Commands:
  users  Manage users

Use "myapp [command] --help" for more information about a command.
Example (ConfigProvider)
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/bjaus/cli"
)

// ConfigProvider lets a command supply its own resolver.
// The command-level resolver takes priority over the global one.
type ConfigProviderCmd struct {
	Port int `flag:"port" default:"8080" help:"Listen port"`
}

func (c *ConfigProviderCmd) Run(_ context.Context) error {
	fmt.Fprintf(os.Stdout, "port=%d\n", c.Port)
	return nil
}

func (c *ConfigProviderCmd) ConfigResolver() cli.ConfigResolver {
	return func(key cli.ConfigKey) (string, bool) {
		if key.Name == "port" {
			return "3000", true
		}
		return "", false
	}
}

func main() {
	cmd := &ConfigProviderCmd{}
	_ = cli.Execute(context.Background(), cmd, nil, cli.WithStdout(os.Stdout)) //nolint:errcheck // example
}
Output:

port=3000
Example (ConfigResolver)
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/bjaus/cli"
)

// ConfigResolver loads flag values from an external source.
// Config values sit between defaults and env vars in priority:
// explicit flag > env > config > default > zero.
type ConfigServeCmd struct {
	Port int    `flag:"port" default:"8080" help:"Listen port"`
	Host string `flag:"host" default:"localhost" help:"Host to bind to"`
}

func (c *ConfigServeCmd) Run(_ context.Context) error {
	fmt.Fprintf(os.Stdout, "host=%s port=%d\n", c.Host, c.Port)
	return nil
}

func main() {
	resolver := cli.ConfigResolver(func(key cli.ConfigKey) (string, bool) {
		m := map[string]string{"port": "9090", "host": "0.0.0.0"}
		v, ok := m[key.Name]
		return v, ok
	})

	cmd := &ConfigServeCmd{}
	_ = cli.Execute(context.Background(), cmd, nil, //nolint:errcheck // example
		cli.WithStdout(os.Stdout),
		cli.WithConfigResolver(resolver),
	)
}
Output:

host=0.0.0.0 port=9090
Example (Counter)
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/bjaus/cli"
)

// Counter flags increment an int each time the flag appears.
// Classic use case: -v for info, -vv for debug, -vvv for trace.
type VerboseCmd struct {
	Verbosity int `flag:"verbose" short:"v" counter:"true" help:"Increase verbosity"`
}

func (c *VerboseCmd) Run(_ context.Context) error {
	fmt.Fprintf(os.Stdout, "verbosity: %d\n", c.Verbosity)
	return nil
}

func main() {
	cmd := &VerboseCmd{}
	_ = cli.Execute(context.Background(), cmd, []string{"-v", "-v", "-v"}, cli.WithStdout(os.Stdout)) //nolint:errcheck // example
}
Output:

verbosity: 3
Example (DefaultCommand)
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/bjaus/cli"
)

// Fallbacker interface provides a fallback subcommand when none is specified.
// Use for CLIs where the "main" action shouldn't require a subcommand name.
type ServeCmd struct{}

func (s *ServeCmd) Run(_ context.Context) error {
	fmt.Fprintln(os.Stdout, "serving on :8080")
	return nil
}

func (s *ServeCmd) Name() string { return "serve" }

type DefaultApp struct{}

func (a *DefaultApp) Run(_ context.Context) error  { return nil }
func (a *DefaultApp) Name() string                 { return "app" }
func (a *DefaultApp) Subcommands() []cli.Commander { return []cli.Commander{&ServeCmd{}} }
func (a *DefaultApp) Fallback() cli.Commander      { return &ServeCmd{} }

func main() {
	app := &DefaultApp{}
	// No subcommand specified — Fallback() is invoked automatically.
	_ = cli.Execute(context.Background(), app, nil, cli.WithStdout(os.Stdout)) //nolint:errcheck // example
}
Output:

serving on :8080
Example (Deprecated)
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/bjaus/cli"
)

// Deprecator interface prints a warning to stderr when a command runs.
// Use when retiring a command — keep it functional but warn users to migrate.
type OldCmd struct{}

func (o *OldCmd) Run(_ context.Context) error {
	fmt.Fprintln(os.Stdout, "still works")
	return nil
}

func (o *OldCmd) Name() string       { return "old-cmd" }
func (o *OldCmd) Deprecated() string { return "use new-cmd instead" }

func main() {
	cmd := &OldCmd{}
	// Redirect stderr to stdout so the example can capture it.
	_ = cli.Execute(context.Background(), cmd, nil, cli.WithStdout(os.Stdout), cli.WithStderr(os.Stdout)) //nolint:errcheck // example
}
Output:

Warning: "old-cmd" is deprecated: use new-cmd instead
still works
Example (Enum)
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/bjaus/cli"
)

// Enum validation restricts a flag to a fixed set of values.
// The framework validates automatically — no need to check in Run.
type OutputCmd struct {
	Format string `flag:"format" short:"f" default:"text" enum:"text,json,yaml" help:"Output format"`
}

func (o *OutputCmd) Run(_ context.Context) error {
	fmt.Fprintf(os.Stdout, "format: %s\n", o.Format)
	return nil
}

func main() {
	cmd := &OutputCmd{}
	_ = cli.Execute(context.Background(), cmd, []string{"--format", "json"}, cli.WithStdout(os.Stdout)) //nolint:errcheck // example

	// Invalid values produce a clear error.
	err := cli.Execute(context.Background(), cmd, []string{"--format", "xml"})
	fmt.Fprintln(os.Stdout, err) //nolint:errcheck // example output
}
Output:

format: json
invalid flag value: --format must be one of [text,json,yaml]
Example (FlagInheritance)
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/bjaus/cli"
)

// Automatic flag inheritance: parent and child both declare --env.
// The child inherits the parent's parsed value when its flag is not set.
type InheritApp struct {
	Env string `flag:"env" help:"Target environment"`
}

func (a *InheritApp) Run(_ context.Context) error  { return nil }
func (a *InheritApp) Name() string                 { return "app" }
func (a *InheritApp) Subcommands() []cli.Commander { return []cli.Commander{&InheritServe{}} }

type InheritServe struct {
	Env  string `flag:"env" help:"Target environment"`
	Port int    `flag:"port" default:"8080" help:"Listen port"`
}

func (s *InheritServe) Name() string { return "serve" }
func (s *InheritServe) Run(_ context.Context) error {
	fmt.Fprintf(os.Stdout, "env=%s port=%d\n", s.Env, s.Port)
	return nil
}

func main() {
	app := &InheritApp{}
	// --env is set on the parent; child inherits it automatically.
	_ = cli.Execute(context.Background(), app, []string{"--env", "prod", "serve"}, cli.WithStdout(os.Stdout)) //nolint:errcheck // example
}
Output:

env=prod port=8080
Example (Flags)
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/bjaus/cli"
)

// A command struct with flags parsed from struct tags.
type GreetCmd struct {
	Name    string `flag:"name" short:"n" default:"World" help:"Who to greet"`
	Excited bool   `flag:"excited" short:"e" help:"Add exclamation mark"`
}

func (g *GreetCmd) Run(_ context.Context) error {
	punct := "."
	if g.Excited {
		punct = "!"
	}
	fmt.Fprintf(os.Stdout, "Hello, %s%s\n", g.Name, punct)
	return nil
}

func main() {
	cmd := &GreetCmd{}
	_ = cli.Execute(context.Background(), cmd, []string{"--name", "Alice", "-e"}, cli.WithStdout(os.Stdout)) //nolint:errcheck // example
}
Output:

Hello, Alice!
Example (HiddenInherit)
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/bjaus/cli"
)

// Hidden flag inheritance: the child gets the parent's flag value via a hidden
// flag with the same name. It does not appear in help but still participates
// in automatic flag inheritance.
type HiddenInheritApp struct {
	Env string `flag:"env" help:"Target environment"`
}

func (a *HiddenInheritApp) Run(_ context.Context) error { return nil }
func (a *HiddenInheritApp) Name() string                { return "app" }
func (a *HiddenInheritApp) Subcommands() []cli.Commander {
	return []cli.Commander{&HiddenInheritServe{}}
}

type HiddenInheritServe struct {
	Env  string `flag:"env" hidden:"true"`
	Port int    `flag:"port" default:"8080" help:"Listen port"`
}

func (s *HiddenInheritServe) Name() string { return "serve" }
func (s *HiddenInheritServe) Run(_ context.Context) error {
	fmt.Fprintf(os.Stdout, "env=%s port=%d\n", s.Env, s.Port)
	return nil
}

func main() {
	app := &HiddenInheritApp{}
	// --env is set on the parent; child's hidden --env receives it via auto-inheritance.
	_ = cli.Execute(context.Background(), app, []string{"--env", "staging", "serve"}, cli.WithStdout(os.Stdout)) //nolint:errcheck // example
}
Output:

env=staging port=8080
Example (Lifecycle)
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/bjaus/cli"
)

// Demonstrating lifecycle hooks with Before and After.
type SetupApp struct {
	child *WorkerCmd
}

func (s *SetupApp) Run(_ context.Context) error  { return nil }
func (s *SetupApp) Name() string                 { return "app" }
func (s *SetupApp) Subcommands() []cli.Commander { return []cli.Commander{s.child} }

func (s *SetupApp) Before(ctx context.Context) (context.Context, error) {
	fmt.Fprintln(os.Stdout, "setup: before")
	return ctx, nil
}

func (s *SetupApp) After(_ context.Context) error {
	fmt.Fprintln(os.Stdout, "setup: after")
	return nil
}

type WorkerCmd struct{}

func (w *WorkerCmd) Run(_ context.Context) error {
	fmt.Fprintln(os.Stdout, "worker: run")
	return nil
}

func (w *WorkerCmd) Name() string { return "work" }

func main() {
	app := &SetupApp{child: &WorkerCmd{}}
	_ = cli.Execute(context.Background(), app, []string{"work"}, cli.WithStdout(os.Stdout)) //nolint:errcheck // example
}
Output:

setup: before
worker: run
setup: after
Example (MapFlags)
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/bjaus/cli"
)

// Map flags accept key=value pairs. Repeated flags add entries.
// Use for label-like data: --env DB_HOST=localhost --env DB_PORT=5432.
type DeployCmd struct {
	Env map[string]string `flag:"env" short:"e" help:"Environment variables"`
}

func (d *DeployCmd) Run(_ context.Context) error {
	for k, v := range d.Env {
		fmt.Fprintf(os.Stdout, "%s=%s\n", k, v)
	}
	return nil
}

func main() {
	cmd := &DeployCmd{}
	_ = cli.Execute(context.Background(), cmd, []string{"--env", "REGION=us-east-1"}, cli.WithStdout(os.Stdout)) //nolint:errcheck // example
}
Output:

REGION=us-east-1
Example (Middleware)
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/bjaus/cli"
)

// Demonstrating middleware wrapping around Run.
type LoggedCmd struct{}

func (l *LoggedCmd) Run(_ context.Context) error {
	fmt.Fprintln(os.Stdout, "executing")
	return nil
}

func (l *LoggedCmd) Middleware() []func(next cli.RunFunc) cli.RunFunc {
	return []func(next cli.RunFunc) cli.RunFunc{
		func(next cli.RunFunc) cli.RunFunc {
			return func(ctx context.Context) error {
				fmt.Fprintln(os.Stdout, "middleware: before")
				err := next(ctx)
				fmt.Fprintln(os.Stdout, "middleware: after")
				return err
			}
		},
	}
}

func main() {
	cmd := &LoggedCmd{}
	_ = cli.Execute(context.Background(), cmd, nil, cli.WithStdout(os.Stdout)) //nolint:errcheck // example
}
Output:

middleware: before
executing
middleware: after
Example (NegatableBool)
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/bjaus/cli"
)

// Negatable bools support --flag and --no-flag patterns.
// Use for features with sensible defaults that users may want to explicitly disable:
// --color is on by default, --no-color turns it off.
type ColorCmd struct {
	Color bool `flag:"color" default:"true" negate:"true" help:"Colorize output"`
}

func (c *ColorCmd) Run(_ context.Context) error {
	fmt.Fprintf(os.Stdout, "color: %v\n", c.Color)
	return nil
}

func main() {
	cmd := &ColorCmd{}
	_ = cli.Execute(context.Background(), cmd, []string{"--no-color"}, cli.WithStdout(os.Stdout)) //nolint:errcheck // example
}
Output:

color: false
Example (PrefixMatching)
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/bjaus/cli"
)

// Prefix matching resolves unique prefixes to full subcommand names.
// "sta" matches "status" if no other subcommand starts with "sta".
// Ambiguous prefixes (matching multiple commands) produce an error.
type StatusCmd struct{}

func (s *StatusCmd) Run(_ context.Context) error {
	fmt.Fprintln(os.Stdout, "all systems go")
	return nil
}

func (s *StatusCmd) Name() string { return "status" }

type PrefixApp struct{}

func (a *PrefixApp) Run(_ context.Context) error  { return nil }
func (a *PrefixApp) Name() string                 { return "app" }
func (a *PrefixApp) Subcommands() []cli.Commander { return []cli.Commander{&StatusCmd{}} }

func main() {
	app := &PrefixApp{}
	_ = cli.Execute(context.Background(), app, []string{"sta"}, //nolint:errcheck // example
		cli.WithStdout(os.Stdout),
		cli.WithPrefixMatching(true),
	)
}
Output:

all systems go
Example (ShortOptionHandling)
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/bjaus/cli"
)

// Short option combining lets users merge single-character flags.
// Enable with WithShortOptionHandling. -abc expands to -a -b -c.
// Combine with counters: -vvv is equivalent to -v -v -v.
type CompactCmd struct {
	All     bool `flag:"all" short:"a" help:"Show all"`
	Long    bool `flag:"long" short:"l" help:"Long format"`
	Reverse bool `flag:"reverse" short:"r" help:"Reverse order"`
}

func (c *CompactCmd) Run(_ context.Context) error {
	fmt.Fprintf(os.Stdout, "all=%v long=%v reverse=%v\n", c.All, c.Long, c.Reverse)
	return nil
}

func main() {
	cmd := &CompactCmd{}
	_ = cli.Execute(context.Background(), cmd, []string{"-alr"}, //nolint:errcheck // example
		cli.WithStdout(os.Stdout),
		cli.WithShortOptionHandling(true),
	)
}
Output:

all=true long=true reverse=true
Example (SliceFlags)
package main

import (
	"context"
	"fmt"
	"os"
	"strings"

	"github.com/bjaus/cli"
)

// Slice flags accumulate values from repeated flags.
// Use when a flag can be specified multiple times, like --tag staging --tag prod.
type BuildCmd struct {
	Tags []string `flag:"tag" short:"t" help:"Tags to apply"`
}

func (b *BuildCmd) Run(_ context.Context) error {
	fmt.Fprintf(os.Stdout, "tags: %s\n", strings.Join(b.Tags, ", "))
	return nil
}

func main() {
	cmd := &BuildCmd{}
	_ = cli.Execute(context.Background(), cmd, []string{"--tag", "v1", "--tag", "latest"}, cli.WithStdout(os.Stdout)) //nolint:errcheck // example
}
Output:

tags: v1, latest
Example (Subcommands)
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/bjaus/cli"
)

// A command struct with flags parsed from struct tags.
type GreetCmd struct {
	Name    string `flag:"name" short:"n" default:"World" help:"Who to greet"`
	Excited bool   `flag:"excited" short:"e" help:"Add exclamation mark"`
}

func (g *GreetCmd) Run(_ context.Context) error {
	punct := "."
	if g.Excited {
		punct = "!"
	}
	fmt.Fprintf(os.Stdout, "Hello, %s%s\n", g.Name, punct)
	return nil
}

// A parent command with subcommands demonstrating the tree structure.
type App struct{}

func (a *App) Run(_ context.Context) error {
	fmt.Fprintln(os.Stdout, "Use a subcommand. Try --help.")
	return nil
}

func (a *App) Name() string        { return "myapp" }
func (a *App) Description() string { return "My example application" }
func (a *App) Subcommands() []cli.Commander {
	return []cli.Commander{&GreetCmd{}, &VersionCmd{}}
}

type VersionCmd struct{}

func (v *VersionCmd) Run(_ context.Context) error {
	fmt.Fprintln(os.Stdout, "v1.0.0")
	return nil
}

func (v *VersionCmd) Name() string        { return "version" }
func (v *VersionCmd) Description() string { return "Print version" }

func main() {
	app := &App{}
	_ = cli.Execute(context.Background(), app, []string{"version"}, cli.WithStdout(os.Stdout)) //nolint:errcheck // example
}
Output:

v1.0.0
Example (Versioner)
package main

import (
	"context"
	"os"

	"github.com/bjaus/cli"
)

// Versioner interface adds --version / -V support.
// Implement on your root command to report the application version.
type MyApp struct{}

func (a *MyApp) Run(_ context.Context) error { return nil }
func (a *MyApp) Name() string                { return "myapp" }
func (a *MyApp) Version() string             { return "2.1.0" }

func main() {
	app := &MyApp{}
	_ = cli.Execute(context.Background(), app, []string{"--version"}, cli.WithStdout(os.Stdout)) //nolint:errcheck // example
}
Output:

2.1.0

func ExecuteAndExit

func ExecuteAndExit(ctx context.Context, root Commander, args []string, opts ...Option)

ExecuteAndExit calls Execute and exits the process. If root implements Exiter, its Exit method is called with the error and controls the exit entirely. Otherwise, the error is printed to stderr. For flag and command errors (unknown flag, missing required flag, etc.) a usage hint is appended. If the error implements ExitCoder, its exit code is used; non-nil errors default to exit code 1.

func Exit

func Exit(message string, code int) error

Exit returns an error that implements ExitCoder with the given message and exit code.

Example

Demonstrating error handling with Exit.

package main

import (
	"context"
	"errors"
	"fmt"
	"os"

	"github.com/bjaus/cli"
)

func main() {
	cmd := cli.RunFunc(func(_ context.Context) error {
		return cli.Exit("port already in use", 2)
	})

	err := cli.Execute(context.Background(), cmd, nil)
	if err != nil {
		fmt.Fprintln(os.Stdout, err) //nolint:errcheck // example output
		var ec cli.ExitCoder
		if ok := errors.As(err, &ec); ok {
			fmt.Fprintf(os.Stdout, "exit code: %d\n", ec.ExitCode()) //nolint:errcheck // example output
		}
	}
}
Output:

port already in use
exit code: 2

func Exitf

func Exitf(code int, format string, args ...any) error

Exitf returns an error that implements ExitCoder with a formatted message and exit code.

func Get

func Get[T any](ctx context.Context, name string) T

Get retrieves a named value from the context. It returns the zero value of T if the name is not found or if the stored value's type does not match T.

Use Lookup when you need to distinguish between a missing value and a stored zero value.

env := cli.Get[string](ctx, "env")

func Lookup

func Lookup[T any](ctx context.Context, name string) (T, bool)

Lookup retrieves a named value from the context. It returns the zero value and false if the name is not found or if the stored value's type does not match T.

env, ok := cli.Lookup[string](ctx, "env")

func MaxArgs

func MaxArgs(n int) func([]string) error

MaxArgs returns an arg validator that requires at most n arguments.

func MinArgs

func MinArgs(n int) func([]string) error

MinArgs returns an arg validator that requires at least n arguments.

func NoArgs

func NoArgs(args []string) error

NoArgs is an arg validator that rejects any arguments.

func RangeArgs

func RangeArgs(lo, hi int) func([]string) error

RangeArgs returns an arg validator that requires between lo and hi arguments inclusive.

func RuntimeComplete

func RuntimeComplete(ctx context.Context, root Commander, args []string, w io.Writer)

RuntimeComplete generates completion candidates for the given args and writes them to w. Each candidate is one line, optionally followed by a tab and description. The last line is a directive in the format ":<int>".

func Set

func Set[T any](ctx context.Context, name string, val T) context.Context

Set stores a named value in the context. The returned context contains the value and should be used for subsequent operations.

The framework automatically calls Set for every parsed flag value before Beforer hooks run, so flag values are available to all commands in the chain via Get or Lookup. User code can also call Set in a [Beforer.Before] hook to share arbitrary values with downstream commands:

func (a *App) Before(ctx context.Context) (context.Context, error) {
    db, err := openDB(a.DSN)
    if err != nil {
        return ctx, err
    }
    return cli.Set(ctx, "db", db), nil
}

func (s *ServeCmd) Run(ctx context.Context, args []string) error {
    db := cli.Get[*sql.DB](ctx, "db")
    // ...
}

func ValidateFlags

func ValidateFlags(cmd Commander, provided map[string]bool) error

ValidateFlags runs required and enum checks on a command using the given provided set. This is called after flag inheritance so that inherited values satisfy required constraints and are checked against enum lists.

Types

type Afterer

type Afterer interface {
	After(ctx context.Context) error
}

Afterer runs teardown logic after Run. Called child-first through the command chain. After hooks always run, even if Run returned an error.

type Aliaser

type Aliaser interface {
	Aliases() []string
}

Aliaser declares alternate names for the command.

type ArgDef

type ArgDef struct {
	Name     string
	Help     string
	Default  string
	Mask     string
	Env      string
	Enum     string
	Required bool
	TypeName string
	IsSlice  bool
}

ArgDef describes a positional argument for use by custom HelpRenderer implementations.

func ScanArgs

func ScanArgs(cmd Commander) []ArgDef

ScanArgs inspects a command's struct tags and returns positional arg definitions. This is exported so custom HelpRenderer implementations can inspect a command's args.

type Args

type Args []string

Args is a slice of positional arguments. Commands can declare an Args field to receive positional arguments via dependency injection:

type ServeCmd struct {
    Args cli.Args
    Port int `flag:"port"`
}

func (s *ServeCmd) Run(ctx context.Context) error {
    for _, file := range s.Args {
        // process file
    }
}

type ArgsValidator

type ArgsValidator interface {
	ValidateArgs(args []string) error
}

ArgsValidator validates positional arguments after flag parsing. Checked on the leaf command before Validator.

type Beforer

type Beforer interface {
	Before(ctx context.Context) (context.Context, error)
}

Beforer runs setup logic before Run. Called parent-first through the command chain. The returned context flows forward to subsequent hooks and Run.

type Categorizer

type Categorizer interface {
	Category() string
}

Categorizer groups the command under a heading in help output.

type Commander

type Commander interface {
	Run(ctx context.Context) error
}

Commander is the core interface every command must implement.

func AllSubcommands

func AllSubcommands(cmd Commander) ([]Commander, error)

AllSubcommands returns all subcommands for a command by merging static subcommands from Subcommander with runtime-discovered subcommands from Discoverer. Built-in commands from Subcommander take priority — discovered commands whose name or alias collides with a built-in are silently dropped.

This is useful for custom HelpRenderer implementations, documentation generators, and shell completion scripts that need to enumerate all available subcommands including plugins.

func Discover

func Discover(prefix string, opts ...DiscoverOption) ([]Commander, error)

Discover scans directories and optionally PATH for plugin executables and returns them as Commander values. Each discovered executable is wrapped in an ExternalCommand.

In directories, every executable file becomes a plugin. The command name is derived from the filename (e.g., a file named "deploy" in the directory becomes the "deploy" command).

On PATH (enabled with WithPATH), executables matching "<prefix>-*" are discovered. The prefix and hyphen are stripped to derive the command name (e.g., prefix "mytool" + executable "mytool-deploy" → command "deploy").

For each discovered executable, Discover runs it with the info flag (default "--cli-info") to retrieve optional PluginInfo JSON. If the executable does not support the flag, the plugin still works — it just has no description or aliases in help output.

Priority rules:

  • Directories are scanned in the order added via WithDir/WithDirs.
  • Within each directory, all executables are found.
  • First match wins on command name collision across directories.
  • PATH results (from WithPATH) have lower priority than any directory result.

Example:

func (a *App) Discover() ([]cli.Commander, error) {
    return cli.Discover("myapp",
        cli.WithDirs(cli.DefaultDirs("myapp")...),
        cli.WithPATH(),
    )
}

func HelpCommand

func HelpCommand(root Commander, w io.Writer) Commander

HelpCommand returns a Commander that displays help for a named command. It accepts command names as positional arguments and prints the help output for that command. Typically added as a hidden "help" subcommand:

func (a *App) Subcommands() []cli.Commander {
    return []cli.Commander{&serveCmd{}, cli.HelpCommand(a, os.Stdout)}
}

Usage:

myapp help serve        # shows help for "serve"
myapp help cluster list # shows help for "cluster list"

func Leaf

func Leaf(ctx context.Context) Commander

Leaf returns the leaf (target) command from the context. The framework stores the leaf before Beforer hooks run, so parent commands can inspect the leaf to make decisions based on consumer-defined interfaces. Returns nil if called on a context that did not originate from Execute.

A common use case is centralized auth: define marker interfaces in your application and check them in a root Beforer:

type Authenticated interface{ Authenticate() }
type Authorized interface{ Permissions() []string }

func (r *Root) Before(ctx context.Context) (context.Context, error) {
    leaf := cli.Leaf(ctx)
    if _, ok := leaf.(Authenticated); !ok {
        return ctx, nil // no auth required
    }
    token, err := auth.Login(ctx)
    if err != nil {
        return ctx, err
    }
    ctx = auth.WithToken(ctx, token)
    if az, ok := leaf.(Authorized); ok {
        if err := auth.Check(token, az.Permissions()); err != nil {
            return ctx, err
        }
    }
    return ctx, nil
}

type Completer

type Completer interface {
	Complete(ctx context.Context, args []string) ([]string, ShellCompDirective)
}

Completer provides shell completion candidates. The returned ShellCompDirective controls shell behavior after completing (e.g. suppressing space or file completion). Return nil completions to fall through to static completion of subcommands and flags.

type ConfigKey

type ConfigKey struct {
	Name  string
	Parts []string
}

ConfigKey identifies a flag for config resolution. Name is the full prefixed flag name (e.g. "db-host"). Parts decomposes the name into prefix segments and base name (e.g. ["db", "host"]), useful for resolvers backed by nested configuration formats (YAML, TOML). For unprefixed flags, Parts contains a single element equal to Name.

type ConfigProvider

type ConfigProvider interface {
	ConfigResolver() ConfigResolver
}

ConfigProvider is implemented by commands that supply their own resolver. Checked before the global resolver set via WithConfigResolver.

type ConfigResolver

type ConfigResolver func(key ConfigKey) (value string, found bool)

ConfigResolver resolves flag values from an external source such as a config file. Given a ConfigKey, it returns the string value and whether the flag was found. The framework handles type conversion. Use [ConfigKey.Name] for flat lookups or [ConfigKey.Parts] for nested lookups.

type Deprecator

type Deprecator interface {
	Deprecated() string
}

Deprecator marks a command as deprecated. A non-empty return value is printed as a warning to stderr before the command runs.

type Descriptor

type Descriptor interface {
	Description() string
}

Descriptor provides a one-line description shown in help output.

type DiscoverOption

type DiscoverOption func(*discoverConfig)

DiscoverOption configures how Discover finds plugin executables.

func WithDir

func WithDir(dir string) DiscoverOption

WithDir adds a directory to scan for plugin executables. Directories are scanned in the order they are added. When multiple directories contain a plugin with the same command name, the first one found wins.

Missing directories are silently skipped — this is not an error. Only directories that exist but cannot be read produce an error.

func WithDirs

func WithDirs(dirs ...string) DiscoverOption

WithDirs adds multiple directories to scan for plugin executables. Equivalent to calling WithDir for each directory in order.

func WithInfoFlag

func WithInfoFlag(flag string) DiscoverOption

WithInfoFlag sets the flag name used to query plugin metadata. The default is "--cli-info". When discovering plugins, the framework executes each plugin with this flag to retrieve optional PluginInfo JSON metadata. If the plugin does not support the flag, returns non-zero, or returns invalid JSON, the plugin is still registered — it just has no description or aliases.

func WithPATH

func WithPATH() DiscoverOption

WithPATH enables scanning the system PATH for executables matching "<prefix>-<command>". For example, if the prefix is "mytool", an executable named "mytool-deploy" on PATH becomes the "deploy" command.

PATH-discovered plugins have lower priority than directory-discovered plugins. Unreadable PATH entries are silently skipped.

type Discoverer

type Discoverer interface {
	Discover() ([]Commander, error)
}

Discoverer provides runtime-discovered commands (plugins). Discovered commands are merged with [Subcommander.Subcommands] — built-in commands take priority on name collisions. A command may implement both Subcommander and Discoverer; it may also implement Discoverer alone (without Subcommander) to have only dynamically discovered subcommands.

Use Discover to scan directories and PATH for plugin executables:

func (a *App) Discover() ([]Commander, error) {
    return cli.Discover("myapp",
        cli.WithDirs(cli.DefaultDirs("myapp")...),
        cli.WithPATH(),
    )
}

type Example

type Example struct {
	Description string
	Command     string
}

Example represents a single usage example.

type Exampler

type Exampler interface {
	Examples() []Example
}

Exampler provides usage examples shown in help output.

type ExitCoder

type ExitCoder interface {
	ExitCode() int
}

ExitCoder is implemented by errors that carry a process exit code.

type Exiter

type Exiter interface {
	Exit(err error)
}

Exiter controls process exit behavior. When implemented on the root command, ExecuteAndExit delegates to Exit instead of calling os.Exit directly. The implementation is responsible for printing the error and exiting the process.

type ExternalCommand

type ExternalCommand struct {
	// Path is the absolute path to the plugin executable.
	Path string

	// Cmd is the command name used for subcommand matching and help output.
	Cmd string

	// Desc is the one-line description shown in help output.
	Desc string

	// CommandAliases are alternate names for the command.
	CommandAliases []string

	// Args receives positional arguments via injection.
	Args Args
}

ExternalCommand wraps an external executable as a Commander. When Run is called, it executes the binary, wiring stdin, stdout, and stderr to the parent process.

ExternalCommand implements Namer, Descriptor, and Aliaser.

func (*ExternalCommand) Aliases

func (e *ExternalCommand) Aliases() []string

Aliases implements Aliaser.

func (*ExternalCommand) Description

func (e *ExternalCommand) Description() string

Description implements Descriptor.

func (*ExternalCommand) Name

func (e *ExternalCommand) Name() string

Name implements Namer.

func (*ExternalCommand) Run

func (e *ExternalCommand) Run(ctx context.Context) error

Run implements Commander.

type Fallbacker

type Fallbacker interface {
	Fallback() Commander
}

Fallbacker provides a fallback subcommand to run when no subcommand name matches.

type FlagCompleter

type FlagCompleter interface {
	CompleteFlag(ctx context.Context, flag string, value string) ([]string, ShellCompDirective)
}

FlagCompleter provides dynamic completion for flag values. When a flag requires a value and the command implements this interface, the framework calls CompleteFlag with the flag name and partial value. Return nil to fall through to enum-based completion.

func (c *DeployCmd) CompleteFlag(ctx context.Context, flag, value string) ([]string, ShellCompDirective) {
    if flag == "region" {
        return []string{"us-east-1", "us-west-2", "eu-west-1"}, cli.ShellCompDirectiveNoFileComp
    }
    return nil, cli.ShellCompDirectiveDefault
}

type FlagDef

type FlagDef struct {
	Name        string
	Short       string
	Alt         []string // additional long flag names
	Help        string
	Default     string
	Mask        string // displayed instead of Default in help (e.g. "****" for secrets)
	Env         string
	Enum        string
	Sep         string // separator for splitting values into slice elements (e.g. ",")
	Category    string
	Deprecated  string
	Placeholder string // shown in help as value name (e.g. "PORT" in --port PORT)
	Required    bool
	Hidden      bool
	TypeName    string
	IsBool      bool
	IsCounter   bool
	Negate      bool
}

FlagDef describes a single flag for use by custom HelpRenderer implementations.

func ScanFlags

func ScanFlags(cmd Commander) []FlagDef

ScanFlags inspects a command's struct tags and returns flag definitions. This is exported so custom HelpRenderer and FlagParser implementations can inspect a command's flags.

type FlagGroup

type FlagGroup struct {
	Kind  FlagGroupKind
	Flags []string
}

FlagGroup defines a relationship constraint between flags.

func MutuallyExclusive

func MutuallyExclusive(flags ...string) FlagGroup

MutuallyExclusive creates a flag group where at most one flag may be set.

func OneRequired

func OneRequired(flags ...string) FlagGroup

OneRequired creates a flag group where exactly one flag must be set.

func RequiredTogether

func RequiredTogether(flags ...string) FlagGroup

RequiredTogether creates a flag group where if any flag is set, all must be set.

type FlagGroupKind

type FlagGroupKind int

FlagGroupKind describes the type of constraint a flag group enforces.

const (
	// GroupMutuallyExclusive means at most one flag in the group may be set.
	GroupMutuallyExclusive FlagGroupKind = iota
	// GroupRequiredTogether means if any flag in the group is set, all must be set.
	GroupRequiredTogether
	// GroupOneRequired means exactly one flag in the group must be set.
	GroupOneRequired
)

type FlagGrouper

type FlagGrouper interface {
	FlagGroups() []FlagGroup
}

FlagGrouper declares flag group constraints on a command. Validation runs after flag parsing and inheritance.

type FlagParser

type FlagParser interface {
	ParseFlags(cmd Commander, args []string) (remaining []string, err error)
}

FlagParser replaces the flag parsing engine. Checked on the command first, then falls back to the global parser set via WithFlagParser, then to the default struct-tag parser.

type FlagUnmarshaler

type FlagUnmarshaler interface {
	UnmarshalFlag(value string) error
}

FlagUnmarshaler allows custom types to be used as flag values.

type HelpAppender

type HelpAppender interface {
	AppendHelp() []HelpSection
}

HelpAppender declares sections appended after the main help content (after Arguments, before Global Flags). Sections are rendered in order.

func (j *JiraCmd) AppendHelp() []cli.HelpSection {
    return []cli.HelpSection{{
        Header: "Required Tokens",
        Body:   "  JIRA_TOKEN    Jira API token (env: JIRA_TOKEN)",
    }}
}

type HelpPrepender

type HelpPrepender interface {
	PrependHelp() []HelpSection
}

HelpPrepender declares sections prepended before the main help content (before Usage). Sections are rendered in order.

func (j *JiraCmd) PrependHelp() []cli.HelpSection {
    return []cli.HelpSection{{
        Header: "Notice",
        Body:   "  This command requires VPN access.",
    }}
}

type HelpRenderer

type HelpRenderer interface {
	RenderHelp(cmd Commander, chain []Commander, flags []FlagDef, args []ArgDef, globalFlags []FlagDef) string
}

HelpRenderer replaces the help rendering engine. Checked on the command first, then falls back to the global renderer set via WithHelpRenderer, then to the default renderer.

Parameters:

  • cmd: the leaf command being rendered
  • chain: the full command chain from root to leaf
  • flags: the leaf command's flags
  • args: the leaf command's positional arg definitions
  • globalFlags: visible flags from parent commands

type HelpSection

type HelpSection struct {
	Header string
	Body   string
}

HelpSection is a custom section rendered in the default help output. The Header is rendered as a section title (like "Flags:" or "Commands:"), and Body is rendered as-is beneath it. Use this with HelpAppender or HelpPrepender to add context-specific information (required tokens, environment setup, etc.) without replacing the entire help renderer.

type Helper

type Helper interface {
	Help() string
}

Helper overrides help text for a single command.

type Hider

type Hider interface {
	Hidden() bool
}

Hider hides the command from help output.

type LongDescriptor

type LongDescriptor interface {
	LongDescription() string
}

LongDescriptor provides extended description text shown in the command's own help output. When implemented, this replaces Descriptor in the help body while Descriptor remains used in subcommand listings.

type Middlewarer

type Middlewarer interface {
	Middleware() []func(next RunFunc) RunFunc
}

Middlewarer provides middleware that wraps the command's Run function.

type Namer

type Namer interface {
	Name() string
}

Namer overrides the command name. The default is the lowercase struct type name.

type Option

type Option func(*options)

Option configures the execution environment.

func Bind

func Bind(v any) Option

Bind registers a dependency for injection into command structs. The value is matched by its concrete type against struct fields.

db := openDB()
cli.Execute(ctx, root, args, cli.Bind(db))

Commands declare the dependency as a struct field (no tag needed):

type ServeCmd struct {
    DB   *sql.DB
    Port int `flag:"port"`
}

func (s *ServeCmd) Run(ctx context.Context) error {
    s.DB.Query("SELECT ...") // ready to use
}

Fields with flag:, arg:, or env: tags are not eligible for injection.

func BindProvider

func BindProvider[T any](fn func() (T, error)) Option

BindProvider registers a lazy dependency that is resolved by calling fn each time bindings are injected. The provider is called once per Execute call. If fn returns an error, execution is aborted.

cli.Execute(ctx, root, args,
    cli.BindProvider(func() (*sql.DB, error) {
        return sql.Open("postgres", dsn)
    }),
)

func BindSingleton

func BindSingleton[T any](fn func() (T, error)) Option

BindSingleton registers a lazy dependency that is resolved at most once. The first call to fn caches the result; subsequent injections reuse the cached value. If fn returns an error, the error is cached and execution is aborted.

cli.Execute(ctx, root, args,
    cli.BindSingleton(func() (*sql.DB, error) {
        return sql.Open("postgres", dsn)
    }),
)

func BindTo

func BindTo(v any, iface any) Option

BindTo registers a dependency as a specific interface type. Use this when you want commands to depend on an interface rather than a concrete type.

cli.Execute(ctx, root, args,
    cli.BindTo(redisCache, (*Cache)(nil)),
)

Commands declare the interface type:

type ServeCmd struct {
    Store Cache
}

func WithCaseInsensitive

func WithCaseInsensitive(enabled bool) Option

WithCaseInsensitive enables case-insensitive subcommand matching. When enabled, "Serve" matches "serve".

func WithConfigResolver

func WithConfigResolver(r ConfigResolver) Option

WithConfigResolver sets a global config resolver for flag values. Config values have lower priority than env vars and explicit CLI flags, but higher priority than defaults: explicit flag > env > config > default > zero.

func WithEnvVarPrefix

func WithEnvVarPrefix(prefix string) Option

WithEnvVarPrefix sets a prefix prepended to all env var names declared via the env struct tag. For example, WithEnvVarPrefix("APP_") causes a flag tagged with `env:"PORT"` to look up the APP_PORT environment variable.

func WithFlagNormalization

func WithFlagNormalization(fn func(string) string) Option

WithFlagNormalization sets a function that normalizes flag names before lookup. For example, to treat underscores as dashes:

cli.WithFlagNormalization(func(s string) string {
    return strings.ReplaceAll(s, "_", "-")
})

func WithFlagParser

func WithFlagParser(p FlagParser) Option

WithFlagParser sets a global flag parser, overriding the default struct-tag parser.

func WithHelpRenderer

func WithHelpRenderer(r HelpRenderer) Option

WithHelpRenderer sets a global help renderer, overriding the default renderer.

func WithIgnoreUnknown

func WithIgnoreUnknown(enabled bool) Option

WithIgnoreUnknown causes unknown flags to be treated as positional args instead of returning an error. Useful for wrapper tools that forward flags to child processes.

func WithInteractive

func WithInteractive(enabled bool) Option

WithInteractive enables interactive prompting for missing required flags when stdin is a terminal. Commands can implement Prompter to customize the prompting behavior.

func WithPrefixMatching

func WithPrefixMatching(enabled bool) Option

WithPrefixMatching enables unique prefix matching for subcommand names. When enabled, "ser" matches "serve" if no other subcommand starts with "ser".

func WithShortOptionHandling

func WithShortOptionHandling(enabled bool) Option

WithShortOptionHandling enables POSIX-style short option combining. When enabled, -abc is expanded to -a -b -c (all but last must be bool/counter).

func WithSignalHandling

func WithSignalHandling(enabled bool) Option

WithSignalHandling enables automatic signal handling. When enabled, Execute wraps the context with signal.NotifyContext for SIGINT and SIGTERM, causing the context to be canceled when either signal is received.

func WithSortedHelp

func WithSortedHelp(enabled bool) Option

WithSortedHelp sorts subcommands and flags alphabetically in help output.

func WithStderr

func WithStderr(w io.Writer) Option

WithStderr sets the writer used for standard error output.

func WithStdin

func WithStdin(r io.Reader) Option

WithStdin sets the reader used for standard input (e.g., interactive prompts).

func WithStdout

func WithStdout(w io.Writer) Option

WithStdout sets the writer used for standard output (e.g., help text).

func WithSuggest

func WithSuggest(enabled bool) Option

WithSuggest enables or disables "did you mean?" suggestions for unknown commands and flags. Enabled by default.

type Passthrougher

type Passthrougher interface {
	Passthrough() bool
}

Passthrougher enables passthrough mode for a command. When enabled, recognized flags declared via struct tags are parsed normally, but unknown flags are passed through as positional arguments instead of producing errors. This is useful for wrapper commands like "exec" that have their own flags but also forward args to child processes.

type PluginInfo

type PluginInfo struct {
	Name        string   `json:"name,omitempty"`
	Description string   `json:"description,omitempty"`
	Aliases     []string `json:"aliases,omitempty"`
}

PluginInfo is the optional JSON metadata a plugin can return via its info flag (default "--cli-info"). All fields are optional — a plugin that does not support the flag or returns invalid JSON still works; it simply has no description or aliases in help output.

type Prompter

type Prompter interface {
	Prompt(flag FlagDef) (string, error)
}

Prompter customizes how a command prompts for missing required flags in interactive mode. When WithInteractive is enabled and stdin is a terminal, the framework calls Prompt for each missing required flag before validation. Returning an empty string causes the flag to remain unset (validation will catch it). Return an error to abort execution.

type RunFunc

type RunFunc func(ctx context.Context) error

RunFunc adapts a plain function into a Commander.

Example

A minimal command using RunFunc.

package main

import (
	"context"
	"fmt"
	"os"

	"github.com/bjaus/cli"
)

func main() {
	hello := cli.RunFunc(func(_ context.Context) error {
		fmt.Fprintln(os.Stdout, "Hello, world!") //nolint:errcheck // example output
		return nil
	})

	_ = cli.Execute(context.Background(), hello, nil, cli.WithStdout(os.Stdout)) //nolint:errcheck // example
}
Output:

Hello, world!

func (RunFunc) Run

func (f RunFunc) Run(ctx context.Context) error

Run implements Commander.

type ShellCompDirective

type ShellCompDirective int

ShellCompDirective controls shell behavior after completing.

const (
	// ShellCompDirectiveDefault indicates default completion behavior.
	ShellCompDirectiveDefault ShellCompDirective = 0
	// ShellCompDirectiveNoSpace prevents adding a space after the completion.
	ShellCompDirectiveNoSpace ShellCompDirective = 1 << iota
	// ShellCompDirectiveNoFileComp disables file completion when no candidates match.
	ShellCompDirectiveNoFileComp
	// ShellCompDirectiveError indicates an error occurred during completion.
	ShellCompDirectiveError
	// ShellCompDirectiveFilterFileExt indicates completions are file extensions
	// to filter by (e.g. ".yaml", ".json"). Shells will only show files matching
	// these extensions.
	ShellCompDirectiveFilterFileExt
	// ShellCompDirectiveFilterDirs indicates only directories should be completed.
	ShellCompDirectiveFilterDirs
)

type Subcommander

type Subcommander interface {
	Subcommands() []Commander
}

Subcommander declares subcommands.

type Suggester

type Suggester interface {
	Suggest(name string) string
}

Suggester provides a custom suggestion algorithm for a command. Given an unknown name, it returns a suggestion or empty string.

type Validator

type Validator interface {
	Validate(provided map[string]bool) error
}

Validator validates command state after flag parsing and before Run. The provided map contains the names of flags that were explicitly set (via CLI args, env vars, config, or inheritance) — flags with only a default value are not included.

type Versioner

type Versioner interface {
	Version() string
}

Versioner provides a version string, displayed when --version is passed.

Directories

Path Synopsis
Package completion generates shell completion scripts from a cli.Commander command tree.
Package completion generates shell completion scripts from a cli.Commander command tree.
Package config provides cli.ConfigResolver implementations for common configuration sources.
Package config provides cli.ConfigResolver implementations for common configuration sources.
Package doc generates documentation from a cli.Commander command tree.
Package doc generates documentation from a cli.Commander command tree.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL