{"id":18494856,"url":"https://github.com/gbrlsnchs/cli","last_synced_at":"2025-10-12T22:39:34.182Z","repository":{"id":57523809,"uuid":"254245359","full_name":"gbrlsnchs/cli","owner":"gbrlsnchs","description":"Library for CLI functionality with Go-style flags","archived":false,"fork":false,"pushed_at":"2020-05-01T01:10:08.000Z","size":39,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-04T04:48:17.426Z","etag":null,"topics":["cli","go","golang","library","positional-arguments","unix"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gbrlsnchs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-04-09T01:53:59.000Z","updated_at":"2024-09-27T11:36:31.000Z","dependencies_parsed_at":"2022-09-26T18:10:37.565Z","dependency_job_id":null,"html_url":"https://github.com/gbrlsnchs/cli","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/gbrlsnchs/cli","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gbrlsnchs%2Fcli","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gbrlsnchs%2Fcli/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gbrlsnchs%2Fcli/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gbrlsnchs%2Fcli/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gbrlsnchs","download_url":"https://codeload.github.com/gbrlsnchs/cli/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gbrlsnchs%2Fcli/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279013279,"owners_count":26085250,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-12T02:00:06.719Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","go","golang","library","positional-arguments","unix"],"created_at":"2024-11-06T13:22:27.566Z","updated_at":"2025-10-12T22:39:34.131Z","avatar_url":"https://github.com/gbrlsnchs.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# cli\n![Linux, macOS and Windows](https://github.com/gbrlsnchs/cli/workflows/Linux,%20macOS%20and%20Windows/badge.svg)\n[![GoDoc](https://godoc.org/github.com/gbrlsnchs/cli?status.svg)](https://pkg.go.dev/github.com/gbrlsnchs/cli)\n\n## About\nThis is a library that adds some CLI functionalities on top of [Go's flag package] while preserving the single dash Go-style flags. Some of those functionalities are:\n- Subcommands\n- Positional arguments\n  - Both required and optional arguments\n  - Repeating arguments\n- More robust help message\n- Correct handling of help flags\n  - Print help to stdout when help is explicitly requested (via `-h` or `-help` options)\n  - Print help to stderr when the CLI is misused (by requesting a bad command or argument)\n- Prefix errors with the program's name\n- Easy to set up (the whole CLI can be configured all at once)\n\n### Principles\nThis library enforces some principles that might not suit everybody's taste:\n- Go-style flags (single dash for both long and short options)\n- No flags with short name only\n- No conditional flags (internal parser is still [Go's flag package])\n- No required flags (if it's required, make it an argument)\n- Flags must come right after its command, before args\n- Do not expose types from external packages\n- Boring (it's basically configuration plus your logic)\n\nReally, if you're looking for GNU-style flags with the whole parsing kung fu, this library might not be for you. But don't worry, Go is full of awesome libraries out there for such purposes.\n\n## Example\n### Multiple subcommands with top-level configuration\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/gbrlsnchs/cli\"\n)\n\n// appConfig is the program's configuration.\n// It can be used by any command.\ntype appConfig struct {\n\tquiet bool\n}\n\n// copy returns a copy of cfg, thus not allowing the original configuration\n// to be modified by registering functions.\n// This is one way to simulate a const reference, which, although modifiable,\n// won't interfere with the original instance, so modifying it is harmless.\nfunc (cfg *appConfig) copy() appConfig { return *cfg }\n\n// helloCmd says hello to someone. Toggle upper to scream.\ntype helloCmd struct {\n\tname  string\n\tupper bool\n}\n\nfunc (cmd *helloCmd) register(getcfg func() appConfig) cli.ExecFunc {\n\treturn func(prg cli.Program) error {\n\t\tappcfg := getcfg()\n\t\ts := cmd.name\n\t\tif cmd.upper {\n\t\t\ts = strings.ToUpper(s)\n\t\t}\n\t\tif !appcfg.quiet {\n\t\t\tfmt.Fprintf(prg.Stdout(), \"Hello, %s!\\n\", s)\n\t\t}\n\t\treturn nil\n\t}\n}\n\n// concatCmd prints the concatenation of two words.\ntype concatCmd struct {\n\tfirst  string\n\tsecond string\n}\n\nfunc (cmd *concatCmd) register(getcfg func() appConfig) cli.ExecFunc {\n\treturn func(prg cli.Program) error {\n\t\tappcfg := getcfg()\n\t\tif !appcfg.quiet {\n\t\t\tfmt.Fprintf(prg.Stdout(), \"%s %s\\n\", cmd.first, cmd.second)\n\t\t}\n\t\treturn nil\n\t}\n}\n\n// joinCmd prints a list of words joined by a separator.\ntype joinCmd struct {\n\twords []string\n\tsep   string\n}\n\nfunc (cmd *joinCmd) register(getcfg func() appConfig) cli.ExecFunc {\n\treturn func(prg cli.Program) error {\n\t\tappcfg := getcfg()\n\t\tif !appcfg.quiet {\n\t\t\ts := strings.Join(cmd.words, cmd.sep)\n\t\t\tfmt.Fprintln(prg.Stdout(), s)\n\t\t}\n\t\treturn nil\n\t}\n}\n\n// rootCmd is simply a store for commands, and also a way to\n// initialize all of them at once by using rootCmd's zero value.\ntype rootCmd struct {\n\thello  helloCmd\n\tconcat concatCmd\n\tjoin   joinCmd\n}\n\nfunc main() {\n\tvar (\n\t\troot   rootCmd\n\t\tappcfg appConfig\n\t)\n\tcmdl := cli.New(\u0026cli.Command{\n\t\tDescription: `This is a simple program that serves as an example for how to use package cli.\n\nIts commands should not be taken seriously, since they do nothing really great, but serve well for demonstration.`,\n\t\tOptions: map[string]cli.Option{\n\t\t\t\"quiet\": cli.BoolOption{\n\t\t\t\tOptionDetails: cli.OptionDetails{\n\t\t\t\t\tDescription: \"Turn output off.\",\n\t\t\t\t\tShort:       'q',\n\t\t\t\t},\n\t\t\t\tRecipient: \u0026appcfg.quiet,\n\t\t\t},\n\t\t},\n\t\tSubcommands: map[string]*cli.Command{\n\t\t\t\"hello\": {\n\t\t\t\tDescription: \"Say hello to someone.\",\n\t\t\t\tArg: cli.StringArg{\n\t\t\t\t\tLabel:     \"NAME\",\n\t\t\t\t\tRequired:  true,\n\t\t\t\t\tRecipient: \u0026root.hello.name,\n\t\t\t\t},\n\t\t\t\tOptions: map[string]cli.Option{\n\t\t\t\t\t\"upper\": cli.BoolOption{\n\t\t\t\t\t\tOptionDetails: cli.OptionDetails{\n\t\t\t\t\t\t\tDescription: \"Convert name to uppercase.\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\tRecipient: \u0026root.hello.upper,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tExec: root.hello.register(appcfg.copy),\n\t\t\t},\n\t\t\t\"concat\": {\n\t\t\t\tDescription: \"Concatenate two words.\",\n\t\t\t\tArg: cli.StringArg{\n\t\t\t\t\tLabel:     \"FIRST WORD\",\n\t\t\t\t\tRequired:  true,\n\t\t\t\t\tRecipient: \u0026root.concat.first,\n\t\t\t\t\tNext: cli.StringArg{\n\t\t\t\t\t\tLabel:     \"LAST WORD\",\n\t\t\t\t\t\tRequired:  true,\n\t\t\t\t\t\tRecipient: \u0026root.concat.second,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tExec: root.concat.register(appcfg.copy),\n\t\t\t},\n\t\t\t\"join\": {\n\t\t\t\tDescription: \"Join strings together.\",\n\t\t\t\tArg: cli.RepeatingArg{\n\t\t\t\t\tLabel:     \"WORD\",\n\t\t\t\t\tRequired:  true,\n\t\t\t\t\tRecipient: \u0026root.join.words,\n\t\t\t\t},\n\t\t\t\tOptions: map[string]cli.Option{\n\t\t\t\t\t\"separator\": cli.StringOption{\n\t\t\t\t\t\tOptionDetails: cli.OptionDetails{\n\t\t\t\t\t\t\tDescription: \"Set a custom separator.\",\n\t\t\t\t\t\t\tShort:       's',\n\t\t\t\t\t\t\tArgLabel:    \"SEPARATOR\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\tRecipient: \u0026root.join.sep,\n\t\t\t\t\t\tDefValue:  \",\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tExec: root.join.register(appcfg.copy),\n\t\t\t},\n\t\t},\n\t}, cli.Name(\"my-cmd\"))\n\tcode := cmdl.ParseAndRun(os.Args)\n\tos.Exit(code)\n}\n```\n\n#### Outputs\n\u003cdetails\u003e\u003csummary\u003eRunning the main program (prints help)\u003c/summary\u003e\n\u003cp\u003e\n\n```shell\n$ go run .examples/full_featured/main.go -h\nThis is a simple program that serves as an example for how to use\npackage cli.\n\nIts commands should not be taken seriously, since they do nothing really\ngreat, still they serve well for demonstration.\n\nUSAGE:\n    my-cmd [OPTIONS] \u003cCOMMAND\u003e\n\nOPTIONS:\n    -h, -help     Print this help message.\n    -q, -quiet    Turn output off.\n\nCOMMANDS:\n    concat    Concatenate two words.\n    hello     Say hello to someone.\n    join      Join strings together.\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eHelp for the \"hello\" program\u003c/summary\u003e\n\u003cp\u003e\n\n```shell\n$ go run .examples/full_featured/main.go hello -h\nSay hello to someone.\n\nUSAGE:\n    hello [OPTIONS] \u003cNAME\u003e\n\nOPTIONS:\n    -h, -help     Print this help message.\n        -upper    Convert name to uppercase.\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eHelp for the \"concat\" program\u003c/summary\u003e\n\u003cp\u003e\n\n```shell\n$ go run .examples/full_featured/main.go concat -h\nConcatenate two words.\n\nUSAGE:\n    concat [OPTIONS] \u003cFIRST WORD\u003e \u003cLAST WORD\u003e\n\nOPTIONS:\n    -h, -help    Print this help message.\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eHelp for the \"join\" program\u003c/summary\u003e\n\u003cp\u003e\n\n```shell\n$ go run .examples/full_featured/main.go join -h\nJoin strings together.\n\nUSAGE:\n    join [OPTIONS] \u003cWORD\u003e [...]\n\nOPTIONS:\n    -h, -help                     Print this help message.\n    -s, -separator \u003cSEPARATOR\u003e    Set a custom separator. (default: \",\")\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n[Go's flag package]: https://golang.org/pkg/flag/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgbrlsnchs%2Fcli","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgbrlsnchs%2Fcli","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgbrlsnchs%2Fcli/lists"}