cli

package module
v0.5.0 Latest Latest
Warning

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

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

README

cli

GoDoc CI

An intentionally minimal Go package for building CLI applications. Extends the standard library's flag package to support flags anywhere in command arguments, adds nested subcommands, and gets out of the way.

Installation

go get github.com/pressly/cli@latest

Requires Go 1.21 or higher.

Quick Start

root := &cli.Command{
	Name:      "greet",
	ShortHelp: "Print a greeting",
	Exec: func(ctx context.Context, s *cli.State) error {
		fmt.Fprintln(s.Stdout, "hello, world!")
		return nil
	},
}
if err := cli.ParseAndRun(ctx, root, os.Args[1:], nil); err != nil {
	fmt.Fprintf(os.Stderr, "error: %v\n", err)
	os.Exit(1)
}

ParseAndRun parses the command hierarchy, handles --help automatically, and executes the resolved command. For applications that need work between parsing and execution, use Parse and Run separately. See the examples directory for more complete applications.

Flags

FlagsFunc is a convenience for defining flags inline. Use FlagOptions to extend the standard flag package with features like required flag enforcement and short aliases:

Flags: cli.FlagsFunc(func(f *flag.FlagSet) {
	f.Bool("verbose", false, "enable verbose output")
	f.String("output", "", "output file")
}),
FlagOptions: []cli.FlagOption{
	{Name: "verbose", Short: "v"},
	{Name: "output", Short: "o", Required: true},
},

Short aliases register -v as an alias for --verbose, -o as an alias for --output, and so on. Both forms are shown in help output automatically.

Access flags inside Exec with the type-safe GetFlag function:

verbose := cli.GetFlag[bool](s, "verbose")
output := cli.GetFlag[string](s, "output")

Child commands automatically inherit flags from parent commands, so a --verbose flag on the root is accessible from any subcommand via GetFlag.

Subcommands

Commands can have nested subcommands, each with their own flags and Exec function:

root := &cli.Command{
	Name:      "todo",
	Usage:     "todo <command> [flags]",
	ShortHelp: "A simple CLI for managing your tasks",
	SubCommands: []*cli.Command{
		{
			Name:      "list",
			ShortHelp: "List all tasks",
			Exec: func(ctx context.Context, s *cli.State) error {
				// ...
				return nil
			},
		},
	},
}

For a more complete example with deeply nested subcommands, see the todo example.

Help

Help text is generated automatically and displayed when --help is passed. To customize it, set the UsageFunc field on a command.

Usage Syntax

See docs/usage-syntax.md for conventions used in usage strings.

Status

This project is in active development and undergoing changes as the API gets refined. Please open an issue if you encounter any problems or have suggestions for improvement.

Acknowledgements

There are many great CLI libraries out there, but I always felt they were too heavy for my needs.

Inspired by Peter Bourgon's ff library, specifically the v3 branch, which was so close to what I wanted. The v4 branch took a different direction, and I wanted to keep the simplicity of v3. This library carries that idea forward.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Documentation

Overview

Package cli provides a lightweight library for building command-line applications using Go's standard library flag package. It extends flag functionality to support flags anywhere in command arguments.

Key features:

  • Nested subcommands for organizing complex CLIs
  • Flexible flag parsing, allowing flags anywhere in arguments
  • Parent-to-child flag inheritance
  • Type-safe flag access
  • Automatic help text generation
  • Command suggestions for misspelled inputs

Quick example:

root := &cli.Command{
    Name:      "echo",
    Usage:     "echo [flags] <text>...",
    ShortHelp: "prints the provided text",
    Flags: cli.FlagsFunc(func(f *flag.FlagSet) {
        f.Bool("c", false, "capitalize the input")
    }),
    Exec: func(ctx context.Context, s *cli.State) error {
        output := strings.Join(s.Args, " ")
        if cli.GetFlag[bool](s, "c") {
            output = strings.ToUpper(output)
        }
        fmt.Fprintln(s.Stdout, output)
        return nil
    },
}

The package intentionally maintains a minimal API surface to serve as a building block for CLI applications while leveraging the standard library's flag package. This approach enables developers to build maintainable command-line tools quickly while focusing on application logic rather than framework complexity.

Index

Constants

This section is empty.

Variables

View Source
var ErrHelp = flag.ErrHelp

ErrHelp is returned by Parse when the -help or -h flag is invoked. It is identical to flag.ErrHelp but re-exported here so callers using Parse and Run separately do not need to import the flag package solely for error checking.

Note: ParseAndRun handles this automatically and never surfaces ErrHelp to the caller.

Functions

func DefaultUsage

func DefaultUsage(root *Command) string

DefaultUsage returns the default usage string for the command hierarchy. It is used when the command does not provide a custom usage function. The usage string includes the command's short help, usage pattern, available subcommands, and flags.

func FlagsFunc

func FlagsFunc(fn func(f *flag.FlagSet)) *flag.FlagSet

FlagsFunc is a helper function that creates a new flag.FlagSet and applies the given function to it. Intended for use in command definitions to simplify flag setup. Example usage:

cmd.Flags = cli.FlagsFunc(func(f *flag.FlagSet) {
    f.Bool("verbose", false, "enable verbose output")
    f.String("output", "", "output file")
    f.Int("count", 0, "number of items")
})

func GetFlag

func GetFlag[T any](s *State, name string) T

GetFlag retrieves a flag value by name from the command hierarchy. It first checks the current command's flags, then walks up through parent commands.

If the flag doesn't exist or if the type doesn't match the requested type T an error will be raised in the Run function. This is an internal error and should never happen in normal usage. This ensures flag-related programming errors are caught early during development.

verbose := GetFlag[bool](state, "verbose")
count := GetFlag[int](state, "count")
path := GetFlag[string](state, "path")

func Parse

func Parse(root *Command, args []string) error

Parse traverses the command hierarchy and parses arguments. It returns an error if parsing fails at any point.

This function is the main entry point for parsing command-line arguments and should be called with the root command and the arguments to parse, typically os.Args[1:]. Once parsing is complete, the root command is ready to be executed with the Run function.

func ParseAndRun

func ParseAndRun(ctx context.Context, root *Command, args []string, options *RunOptions) error

ParseAndRun is a convenience function that combines Parse and Run into a single call. It parses the command hierarchy, handles help flags automatically (printing usage to stdout and returning nil), and then executes the resolved command.

This is the recommended entry point for most CLI applications:

if err := cli.ParseAndRun(ctx, root, os.Args[1:], nil); err != nil {
    fmt.Fprintf(os.Stderr, "error: %v\n", err)
    os.Exit(1)
}

The options parameter may be nil, in which case default values are used. See RunOptions for more details.

For applications that need to perform work between parsing and execution (e.g., initializing resources based on parsed flags), use Parse and Run separately.

func Run

func Run(ctx context.Context, root *Command, options *RunOptions) error

Run executes the current command. It returns an error if the command has not been parsed or if the command has no execution function.

The options parameter may be nil, in which case default values are used. See RunOptions for more details.

Types

type Command

type Command struct {
	// Name is always a single word representing the command's name. It is used to identify the
	// command in the command hierarchy and in help text.
	Name string

	// Usage provides the command's full usage pattern.
	//
	// Example: "cli todo list [flags]"
	Usage string

	// ShortHelp is a brief description of the command's purpose. It is displayed in the help text
	// when the command is shown.
	ShortHelp string

	// UsageFunc is an optional function that can be used to generate a custom usage string for the
	// command. It receives the current command and should return a string with the full usage
	// pattern.
	UsageFunc func(*Command) string

	// Flags holds the command-specific flag definitions. Each command maintains its own flag set
	// for parsing arguments.
	Flags *flag.FlagSet
	// FlagOptions is an optional list of flag options to extend the FlagSet with additional
	// behavior. This is useful for tracking required flags, short aliases, and local flags.
	FlagOptions []FlagOption

	// SubCommands is a list of nested commands that exist under this command.
	SubCommands []*Command

	// Exec defines the command's execution logic. It receives the current application [State] and
	// returns an error if execution fails. This function is called when [Run] is invoked on the
	// command.
	Exec func(ctx context.Context, s *State) error
	// contains filtered or unexported fields
}

Command represents a CLI command or subcommand within the application's command hierarchy.

func (*Command) Path

func (c *Command) Path() []*Command

Path returns the command chain from root to current command. It can only be called after the root command has been parsed and the command hierarchy has been established.

type FlagOption added in v0.5.0

type FlagOption struct {
	// Name is the flag's name. Must match the flag name in the flag set.
	Name string

	// Short is an optional single-character alias for the flag. When set, users can use either -v
	// or -verbose (if Short is "v" and Name is "verbose"). Must be a single ASCII letter.
	Short string

	// Required indicates whether the flag is required.
	Required bool

	// Local indicates that the flag should not be inherited by child commands. When true, the flag
	// is only available on the command that defines it.
	Local bool
}

FlagOption holds additional options for a flag, such as whether it is required or has a short alias.

type RunOptions

type RunOptions struct {
	// Stdin, Stdout, and Stderr are the standard input, output, and error streams for the command.
	// If any of these are nil, the command will use the default streams ([os.Stdin], [os.Stdout],
	// and [os.Stderr], respectively).
	Stdin          io.Reader
	Stdout, Stderr io.Writer
}

RunOptions specifies options for running a command.

type State

type State struct {
	// Args contains the remaining arguments after flag parsing.
	Args []string

	// Standard I/O streams.
	Stdin          io.Reader
	Stdout, Stderr io.Writer
	// contains filtered or unexported fields
}

State holds command information during Exec function execution, allowing child commands to access parent flags. Use GetFlag to get flag values across the command hierarchy.

Directories

Path Synopsis
examples
cmd/echo command
cmd/task command
Package graceful provides utilities for running long-lived processes with predictable, well-behaved shutdown semantics.
Package graceful provides utilities for running long-lived processes with predictable, well-behaved shutdown semantics.
pkg
Package xflag extends the standard library's flag package to support parsing flags interleaved with positional arguments.
Package xflag extends the standard library's flag package to support parsing flags interleaved with positional arguments.

Jump to

Keyboard shortcuts

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