Help us shape the future of Go! Take the 2026 Go Developer Survey

cli

package module
v0.1.4 Latest Latest
Warning

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

Go to latest
Published: Jul 13, 2025 License: MIT Imports: 17 Imported by: 1

README

cli

Go Reference Go Report Card codecov gosec

A Go package for building CLIs.

Installation

go get github.com/broothie/cli@latest

Documentation

Detailed documentation can be found at pkg.go.dev.

Usage

Using cli is as simple as:

// Create and run a command called "fileserver".
// `Run` automatically passes down a `context.Background()` and parses `os.Args[1:]`.
// If an error is returned, and it is either a `cli.ExitError` or an `*exec.ExitError`, the error's exit code will be used.
// For any other errors returned, it exits with code 1.
cli.Run("fileserver", "An HTTP server.",

	// Add an optional positional argument called "root" which will default to ".".
	cli.AddArg("root", "Directory to serve from", cli.SetArgDefault(".")),

	// Add an optional flag called "port" which will default to 3000.
	cli.AddFlag("port", "Port to run server.", cli.SetFlagDefault(3000)),

	// Register a handler for this command.
	// If no handler is registered, it will simply print help and exit.
	cli.SetHandler(func(ctx context.Context) error {
		// Extract the value of the "root" argument.
		root, _ := cli.ArgValue[string](ctx, "root")

		// Extract the value of the "port" flag.
		port, _ := cli.FlagValue[int](ctx, "port")

		addr := fmt.Sprintf(":%d", port)
		return http.ListenAndServe(addr, http.FileServer(http.Dir(root)))
	}),
)

Here's an example using the complete set of options:

cmd, err := cli.NewCommand("git", "Modern version control.",
	// Set command version
	cli.SetVersion("2.37.0"),

	// Add a "--version" flag with a short flag "-V" for printing the command version
	cli.AddVersionFlag(cli.AddFlagShort('V')),

	// Add a "--help" flag
	cli.AddHelpFlag(

		// Add a short flag "-h" to help
		cli.AddFlagShort('h'),

		// Make this flag inherited by sub-commands
		cli.SetFlagIsInherited(true),
	),

	// Add a hidden "--debug" flag
	cli.AddFlag("debug", "Enable debugging",
		cli.SetFlagDefault(false), // Default parser for flags is cli.StringParser

		// Make it hidden
		cli.SetFlagIsHidden(true),
	),

	// Add a sub-command "clone"
	cli.AddSubCmd("clone", "Clone a repository.",

		// Add a required argument "<url>"
		cli.AddArg("url", "Repository to clone.",

			// Parse it into a *url.URL
			cli.SetArgParser(cli.URLParser),
		),

		// Add optional argument "<dir?>"
		cli.AddArg("dir", "Directory to clone repo into.",

			// Set its default value to "."
			cli.SetArgDefault("."),
		),

		// Add a flag "--verbose"
		cli.AddFlag("verbose", "Be more verbose.",

			// Add a short "-v"
			cli.AddFlagShort('v'),

			// Make it a boolean that defaults to false
			cli.SetFlagDefault(false),
		),
	),
)
if err != nil {
	cli.ExitWithError(err)
}

// Pass in your `context.Context` and args
if err := cmd.Run(context.TODO(), os.Args[1:]); err != nil {
	cli.ExitWithError(err)
}

Roadmap

  • Audit bare err returns
    • Two types of errors: config and parse
  • Tab completion
  • Allow variadic arguments
  • Allow slice and map based flags?

Documentation

Overview

Example (Basic_usage)
package main

import (
	"context"
	"fmt"
	"net/http"

	"github.com/broothie/cli"
)

func main() {
	// Create and run a command called "fileserver".
	// `Run` automatically passes down a `context.Background()` and parses `os.Args[1:]`.
	// If an error is returned, and it is either a `cli.ExitError` or an `*exec.ExitError`, the error's exit code will be used.
	// For any other errors returned, it exits with code 1.
	cli.Run("fileserver", "An HTTP server.",

		// Add an optional positional argument called "root" which will default to ".".
		cli.AddArg("root", "Directory to serve from", cli.SetArgDefault(".")),

		// Add an optional flag called "port" (usage: --port) which will default to 3000.
		cli.AddFlag("port", "Port to run server.", cli.SetFlagDefault(3000)),

		// Register a handler for this command.
		// If no handler is registered, it will simply print help and exit.
		cli.SetHandler(func(ctx context.Context) error {
			// Extract the value of the "root" argument.
			root, _ := cli.ArgValue[string](ctx, "root")

			// Extract the value of the "port" flag.
			port, _ := cli.FlagValue[int](ctx, "port")

			addr := fmt.Sprintf(":%d", port)
			return http.ListenAndServe(addr, http.FileServer(http.Dir(root)))
		}),
	)
}
Example (Kitchen_sink)
package main

import (
	"context"
	"os"

	"github.com/broothie/cli"
)

func main() {
	// Create a new command
	cmd, err := cli.NewCommand("git", "Modern version control.",
		// Set command version
		cli.SetVersion("2.37.0"),

		// Add a "--version" flag with a short flag "-V" for printing the command version
		cli.AddVersionFlag(cli.AddFlagShort('V')),

		// Add a "--help" flag
		cli.AddHelpFlag(

			// Add a short flag "-h" to help
			cli.AddFlagShort('h'),

			// Make this flag inherited by sub-commands
			cli.SetFlagIsInherited(true),
		),

		// Add a hidden "--debug" flag
		cli.AddFlag("debug", "Enable debugging",
			cli.SetFlagDefault(false), // Default parser for flags is cli.StringParser

			// Make it hidden
			cli.SetFlagIsHidden(true),
		),

		// Add a sub-command "clone"
		cli.AddSubCmd("clone", "Clone a repository.",

			// Add a required argument "<url>"
			cli.AddArg("url", "Repository to clone.",

				// Parse it into a *url.URL
				cli.SetArgParser(cli.URLParser),
			),

			// Add optional argument "<dir?>"
			cli.AddArg("dir", "Directory to clone repo into.",

				// Set its default value to "."
				cli.SetArgDefault("."),
			),

			// Add a flag "--verbose"
			cli.AddFlag("verbose", "Be more verbose.",

				// Add a short "-v"
				cli.AddFlagShort('v'),

				// Make it a boolean that defaults to false
				cli.SetFlagDefault(false),
			),
		),
	)
	if err != nil {
		cli.ExitWithError(err)
	}

	// Pass in your `context.Context` and args
	if err := cmd.Run(context.TODO(), os.Args[1:]); err != nil {
		cli.ExitWithError(err)
	}
}

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	NotACommandContextError = errors.New("not a command context")
	FlagNotFoundError       = errors.New("flag not found")
	ArgumentNotFoundError   = errors.New("argument not found")
)
View Source
var (
	InvalidFlagError        = errors.New("invalid flag")
	MissingFlagValueError   = errors.New("missing flag value")
	TooManyArgumentsError   = errors.New("too many arguments")
	FlagGroupWithEqualError = errors.New("short flags with equal signs cannot be grouped")
)
View Source
var ArgumentMissingValueError = errors.New("argument missing value")
View Source
var NotParseableError = errors.New("type not parseable")

Functions

func AddAlias

func AddAlias(alias string) option.Func[*Command]

AddAlias adds an alias to the command.

func AddArg

func AddArg(name, description string, options ...option.Option[*Argument]) option.Func[*Command]

AddArg adds an argument to the command.

Example
command, _ := NewCommand("server", "An http server.",
	AddArg("port", "Port to run server on", SetArgParser(IntParser)),
)

command.renderHelp(os.Stdout)
Output:
server: An http server.

Usage:
  server <port>

Arguments:
  <port>  Port to run server on  (type: int)

func AddFlag

func AddFlag(name, description string, options ...option.Option[*Flag]) option.Func[*Command]

AddFlag adds a flag to the command.

Example
command, _ := NewCommand("server", "An http server.",
	AddFlag("port", "Port to run server on",
		AddFlagShort('p'),
		SetFlagDefault(3000),
	),
)

command.renderHelp(os.Stdout)
Output:
server: An http server.

Usage:
  server [flags]

Flags:
  --port  -p  Port to run server on  (type: int, default: "3000")
Example (With_env)
os.Setenv("PORT", "8080")
defer os.Unsetenv("PORT")

command, _ := NewCommand("server", "An http server.",
	AddFlag("port", "Port to run server on",
		AddFlagShort('p'),
		SetFlagDefault(3000),
		SetFlagDefaultEnv("PORT"),
	),
)

command.renderHelp(os.Stdout)
Output:
server: An http server.

Usage:
  server [flags]

Flags:
  --port  -p  Port to run server on  (type: int, default: $PORT, "3000")

func AddFlagAlias

func AddFlagAlias(alias string) option.Func[*Flag]

AddFlagAlias adds an alias to the flag.

func AddFlagShort

func AddFlagShort(short rune) option.Func[*Flag]

AddFlagShort adds a short flag to the flag.

func AddHelpFlag

func AddHelpFlag(options ...option.Option[*Flag]) option.Func[*Command]

AddHelpFlag adds a help flag to the command.

Example
command, _ := NewCommand("server", "An http server.",
	AddHelpFlag(AddFlagShort('h')),
)

command.renderHelp(os.Stdout)
Output:
server: An http server.

Usage:
  server [flags]

Flags:
  --help  -h  Print help.  (type: bool, default: "false")

func AddSubCmd

func AddSubCmd(name, description string, options ...option.Option[*Command]) option.Func[*Command]

AddSubCmd adds a subcommand to the command.

Example
command, _ := NewCommand("server", "An http server.",
	AddSubCmd("start", "Start the server"),
)

command.renderHelp(os.Stdout)
Output:
server: An http server.

Usage:
  server [sub-command]

Sub-commands:
  start: Start the server

func AddVersionFlag

func AddVersionFlag(options ...option.Option[*Flag]) option.Func[*Command]
Example
command, _ := NewCommand("server", "An http server.",
	SetVersion("v0.1.0"),
	AddVersionFlag(AddFlagShort('V')),
)

command.renderHelp(os.Stdout)
Output:
server v0.1.0: An http server.

Usage:
  server [flags]

Flags:
  --version  -V  Print version.  (type: bool, default: "false")

func ArgValue

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

func BoolParser

func BoolParser(s string) (bool, error)

BoolParser parses a string into a bool.

func DurationParser

func DurationParser(s string) (time.Duration, error)

DurationParser parses a string into a time.Duration.

func ExitWithError added in v0.1.3

func ExitWithError(err error)

ExitWithError exits the program with an error.

func FlagValue

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

func Float64Parser

func Float64Parser(s string) (float64, error)

Float64Parser parses a string into a float64.

func IntParser

func IntParser(s string) (int, error)

IntParser parses a string into an int.

func MountSubCmd added in v0.1.3

func MountSubCmd(subCommand *Command) option.Func[*Command]

MountSubCmd mounds an existing *Command as a sub-command.

func Run

func Run(name, description string, options ...option.Option[*Command])

Run creates and runs a command using os.Args as the arguments and context.Background as the context.

Example
oldArgs := os.Args
os.Args = []string{"echo", "hello"}
defer func() { os.Args = oldArgs }()

Run("echo", "Echo the arguments.",
	AddArg("arg", "The argument to echo."),
	SetHandler(func(ctx context.Context) error {
		arg, _ := ArgValue[string](ctx, "arg")

		fmt.Println(arg)
		return nil
	}),
)
Output:
hello

func SetArgDefault added in v0.1.2

func SetArgDefault[T Parseable](defaultValue T) option.Func[*Argument]

SetArgDefault sets the default value of the argument.

func SetArgParser

func SetArgParser[T any](parser ArgParser[T]) option.Func[*Argument]

SetArgParser sets the parser of the argument.

func SetFlagDefault

func SetFlagDefault[T Parseable](defaultValue T) option.Func[*Flag]

SetFlagDefault sets the default value of the flag.

func SetFlagDefaultAndParser

func SetFlagDefaultAndParser[T any](defaultValue T, argParser ArgParser[T]) option.Func[*Flag]

SetFlagDefaultAndParser sets the default value and parser of the flag.

func SetFlagDefaultEnv added in v0.1.3

func SetFlagDefaultEnv(name string) option.Func[*Flag]

SetFlagDefaultEnv sets the default value to that of the corresponding environment variable, and parser of the flag.

func SetFlagIsHidden

func SetFlagIsHidden(isHidden bool) option.Func[*Flag]

SetFlagIsHidden controls whether the flag is hidden from the help message.

func SetFlagIsInherited

func SetFlagIsInherited(isInherited bool) option.Func[*Flag]

SetFlagIsInherited controls whether the flag is inherited by child commands.

func SetHandler

func SetHandler(handler Handler) option.Func[*Command]

SetHandler sets the handler of the command.

Example
command, _ := NewCommand("server", "An http server.",
	SetHandler(func(ctx context.Context) error {
		fmt.Println("running server")
		return nil
	}),
)

command.Run(context.Background(), nil)
Output:
running server

func SetVersion

func SetVersion(version string) option.Func[*Command]

SetVersion sets the version of the command.

Example
command, _ := NewCommand("server", "An http server.",
	SetVersion("v0.1.0"),
)

command.renderHelp(os.Stdout)
Output:
server v0.1.0: An http server.

Usage:
  server

func StringParser

func StringParser(s string) (string, error)

StringParser parses a string into a string.

func TimeParser

func TimeParser(s string) (time.Time, error)

TimeParser parses a string into a time.Time.

func URLParser

func URLParser(s string) (*url.URL, error)

URLParser parses a string into a *url.URL.

Types

type ArgParser

type ArgParser[T any] func(string) (T, error)

ArgParser is a function that parses a string into a value of type T.

func NewArgParser

func NewArgParser[T any](f ArgParser[T]) ArgParser[T]

NewArgParser creates a new ArgParser.

func TimeLayoutParser

func TimeLayoutParser(timeLayout string) ArgParser[time.Time]

TimeLayoutParser parses a string into a time.Time using a specific time layout.

func (ArgParser[T]) Parse

func (p ArgParser[T]) Parse(s string) (any, error)

func (ArgParser[T]) Type

func (ArgParser[T]) Type() any

type Argument

type Argument struct {
	// contains filtered or unexported fields
}

type Command

type Command struct {
	// contains filtered or unexported fields
}

func NewCommand

func NewCommand(name, description string, options ...option.Option[*Command]) (*Command, error)

NewCommand creates a new command.

Example
command, _ := NewCommand("server", "An http server.",
	SetVersion("v0.1.0"),
	AddVersionFlag(AddFlagShort('V')),
	AddHelpFlag(AddFlagShort('h')),
	AddFlag("port", "Port to run server on.",
		AddFlagShort('p'),
		SetFlagDefault(3000),
		SetFlagDefaultEnv("PORT"),
	),
	AddFlag("auth-required", "Whether to require authentication.",
		SetFlagDefault(true),
	),
	AddSubCmd("proxy", "Proxy requests to another server.",
		AddArg("target", "Target server to proxy requests to.",
			SetArgParser(URLParser),
		),
	),
)

command.renderHelp(os.Stdout)
Output:
server v0.1.0: An http server.

Usage:
  server [flags] [sub-command]

Sub-commands:
  proxy: Proxy requests to another server.

Flags:
  --version        -V  Print version.                      (type: bool, default: "false")
  --help           -h  Print help.                         (type: bool, default: "false")
  --port           -p  Port to run server on.              (type: int, default: $PORT, "3000")
  --auth-required      Whether to require authentication.  (type: bool, default: "true")

func (*Command) Run

func (c *Command) Run(ctx context.Context, rawArgs []string) error

Run runs the command.

type ExitError added in v0.1.3

type ExitError struct {
	Code int
}

ExitError is an error that causes the program to exit with a given status code.

func ExitCode added in v0.1.3

func ExitCode(code int) *ExitError

ExitCode returns an ExitError with the given code.

func (ExitError) Error added in v0.1.3

func (e ExitError) Error() string

Error implements the error interface.

type Flag

type Flag struct {
	// contains filtered or unexported fields
}

type Handler

type Handler func(ctx context.Context) error

type Parseable

type Parseable interface {
	string | bool | int | float64 | time.Time | time.Duration | *url.URL
}

Parseable is a type that can be parsed from a string.

Jump to

Keyboard shortcuts

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