cli is a minimalist, ergonomic, scalable library for building Go
commands with no dependencies outside Go stdlib. Built for ease of
use and ease of growth of Go commands.
Simple example demonstrates subcommands, flag struct tags, error handling, and a simple CLI code management pattern in a single small Go file.
Commands document themselves in markdown, from the same synopses, descriptions
and options that drive -h:
// a directory of one .md per command, root in README.md
err := root.MarkdownDocs("docs")
// a single .md, for a command with no sub-commands
err := cmd.MarkdownDocs("docs/cmd.md")
// or write a page anywhere
err := cmd.MarkdownDoc(os.Stdout)MarkdownDocs picks the directory or the single file according to whether the
command has sub-commands. Each page carries the command's own options and
those it inherits from its parents, minus anything suppressed along the way.
A synopsis written as prose, as in WithSynopsis("o read and write tony"),
reads as the line under the title with the command name it repeats taken off.
One written as an invocation, as in WithSynopsis("o [opts] command [opts]")
or WithSynopsis("cp SRC DST"), is the usage line of the page instead, spelled
out from the root command.
A Doc adds links to the generated markdown without touching what the command
prints for -h, which stays plain text:
cli.NewDoc(root).
WithSite("https://signadot.github.io/tony-format/").
WithTerms(cli.Terms{
"tony format": "/", // relative to the site
"tony": "/",
"schema": "/schema/",
"go-tony": "https://pkg.go.dev/github.com/signadot/tony-format/go-tony",
}).
Write("docs")A desc='validate against a schema' then reads the same at the terminal and
links to https://signadot.github.io/tony-format/schema/ in markdown. Terms
match whole words, longest first, case sensitively, once per description, and
never inside backticks or a link already written. With a Site set, each page
also links to the page for its command there; WithPage says where those live
and WithLinkify is the escape hatch for links the term map cannot express.
cli is young but it works. The general design and organisation of the data types
has mostly settled but may evolve slightly. May still have some bugs in the corners.
There are lots of cli libraries out there, I've worked with many and I don't like any of them. AFAIK, only cli and kong support struct tags for command line options, which is much less cumbersome. In contrast to Kong, cli is tiny and unobtrusive.
- Go's stdlib
flagonly works reasonably for the most rudimentary things and is quite difficult to get basic things working like being able to append a flag to a command line that has arguments already. - Cobra/Viper is huge and extremely verbose and seems to constantlly yell "you should ..." when it's just a distraction. It is also much harder to customize.
- github.com/mitchellh/cli does too much
- Kong does even more
- Other attempts at finding a balance between minimalism and expressivity don't seem to hit the mark for expressivity.