Overview
Build command-line apps where a command is a struct and its flags, args, and validation are declared once as tags.
cli is a small, generics-based framework for building command-line apps where a command is a struct.
Declare a command's flags, positional arguments, environment bindings, defaults, and validation rules once as struct tags, and the framework does the parsing, merging, validating, help, and dispatch.
It never reads config files or the environment on its own beyond the merge, so you stay in control of where values come from.
Install
go get github.com/toaweme/cliDefine a command and run the app
A command is a struct that embeds BaseCommand[T], where T is the config struct whose tags define the flags and args. Implement Run, register the command on an App, and dispatch os.Args.
package main
import (
"fmt"
"os"
"strings"
"github.com/toaweme/cli"
)
type GreetConfig struct {
Name string `arg:"0" env:"GREET_NAME" help:"Name to greet" rules:"required"`
Shout bool `arg:"shout" short:"s" help:"Uppercase the greeting"`
}
type GreetCommand struct {
cli.BaseCommand[GreetConfig]
}
func (c *GreetCommand) Run(_ cli.GlobalFlags, _ cli.Unknowns) error {
msg := fmt.Sprintf("hello, %s!", c.Inputs.Name)
if c.Inputs.Shout {
msg = strings.ToUpper(msg)
}
fmt.Println(msg)
return nil
}
func (c *GreetCommand) Help() string { return "Greet someone by name" }
func main() {
app := cli.NewApp(
cli.Config{Name: "greet", Version: "1.0.0"},
cli.GlobalFlags{},
)
app.Add("hello", &GreetCommand{BaseCommand: cli.NewBaseCommand[GreetConfig]()})
if err := app.Run(os.Args[1:]); cli.IsRealError(err) {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
}The parsed config lands in c.Inputs. The tags drive the rest, so greet hello Ada prints hello, Ada!, greet hello Ada --shout uppercases it, and GREET_NAME=Ada greet hello binds the name from the environment. Help (--help, -h) and version (--version, -V) are built in.
greet hello Ada # hello, Ada!
greet hello Ada --shout # HELLO, ADA!
GREET_NAME=Ada greet hello # hello, Ada!
greet hello --help # generated help for the command
greet --version # greet 1.0.0Note
IsRealError filters the ErrShowingHelp and ErrShowingVersion sentinels, which the framework returns once it has already printed help or the version. That is why the call site checks cli.IsRealError(err) rather than err != nil.
Where to go next
Start with the guides to learn how each piece behaves, then reach for the reference when you need the exact signature or tag.
BaseCommand[T], implement Run, build the App, and dispatch.Args and flagsPositional args, named flags, shorts, defaults, slices, repeats, and validation, all through field tags.SubcommandsBuild command trees with Add chaining, a default command, and parent placeholders.Global flagsThe built-in globals every command receives, and the clean-exit sentinels.Help outputEnrich help with descriptions and examples, and render it in several formats.Config and environmentThe merge precedence, environment binding, resolver chains, and .env loading.ReferenceThe app, commands, the tag vocabulary, global flags, and the config seams.