{"id":23543045,"url":"https://github.com/mfridman/cli","last_synced_at":"2025-11-01T07:30:30.419Z","repository":{"id":269359089,"uuid":"907162038","full_name":"mfridman/cli","owner":"mfridman","description":"Simple Go library for creating CLI apps with subcommands and flexible flags","archived":false,"fork":false,"pushed_at":"2025-02-01T15:57:31.000Z","size":144,"stargazers_count":12,"open_issues_count":4,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-14T14:53:54.230Z","etag":null,"topics":["cli","framework","golang"],"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/mfridman.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-12-23T01:11:35.000Z","updated_at":"2025-02-01T15:57:05.000Z","dependencies_parsed_at":"2024-12-23T02:27:04.837Z","dependency_job_id":"0b2e9dc9-a125-4d36-8a54-fa6429134c3e","html_url":"https://github.com/mfridman/cli","commit_stats":null,"previous_names":["mfridman/cli"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mfridman%2Fcli","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mfridman%2Fcli/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mfridman%2Fcli/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mfridman%2Fcli/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mfridman","download_url":"https://codeload.github.com/mfridman/cli/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239263026,"owners_count":19609675,"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","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","framework","golang"],"created_at":"2024-12-26T06:17:45.927Z","updated_at":"2025-02-17T09:23:00.572Z","avatar_url":"https://github.com/mfridman.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# cli\n\n[![GoDoc](https://godoc.org/github.com/mfridman/cli?status.svg)](https://pkg.go.dev/github.com/mfridman/cli#pkg-index)\n[![CI](https://github.com/mfridman/cli/actions/workflows/ci.yaml/badge.svg)](https://github.com/mfridman/cli/actions/workflows/ci.yaml)\n\nA Go package for building CLI applications. Extends the standard library's `flag` package to support\n[flags anywhere](https://mfridman.com/blog/2024/allowing-flags-anywhere-on-the-cli/) in command\narguments.\n\n## Features\n\nThe **bare minimum** to build a CLI application while leveraging the standard library's `flag`\npackage.\n\n- Nested subcommands for organizing complex CLIs\n- Flexible flag parsing, allowing flags anywhere\n- Subcommands inherit flags from parent commands\n- Type-safe flag access\n- Automatic generation of help text and usage information\n- Suggestions for misspelled or incomplete commands\n\n### But why?\n\nThis package is intentionally minimal. It aims to be a building block for CLI applications that want\nto leverage the standard library's `flag` package while providing a bit more structure and\nflexibility.\n\n- Build maintainable command-line tools quickly\n- Focus on application logic rather than framework complexity\n- Extend functionality **only when needed**\n\nSometimes less is more. While other frameworks offer extensive features, this package focuses on\ncore functionality.\n\n## Installation\n\n```bash\ngo get github.com/mfridman/cli@latest\n```\n\nRequired go version: 1.21 or higher\n\n## Quick Start\n\nHere's a simple example of a CLI application that echoes back the input with a required `-c` flag to\ncapitalize the output:\n\n```go\nroot := \u0026cli.Command{\n\tName:      \"echo\",\n\tUsage:     \"echo [flags] \u003ctext\u003e...\",\n\tShortHelp: \"echo is a simple command that prints the provided text\",\n\tFlags: cli.FlagsFunc(func(f *flag.FlagSet) {\n\t\t// Add a flag to capitalize the input\n\t\tf.Bool(\"c\", false, \"capitalize the input\")\n\t}),\n\tFlagsMetadata: []cli.FlagMetadata{\n\t\t{Name: \"c\", Required: true},\n\t},\n\tExec: func(ctx context.Context, s *cli.State) error {\n\t\tif len(s.Args) == 0 {\n\t\t\treturn errors.New(\"must provide text to echo, see --help\")\n\t\t}\n\t\toutput := strings.Join(s.Args, \" \")\n\t\t// If -c flag is set, capitalize the output\n\t\tif cli.GetFlag[bool](s, \"c\") {\n\t\t\toutput = strings.ToUpper(output)\n\t\t}\n\t\tfmt.Fprintln(s.Stdout, output)\n\t\treturn nil\n\t},\n}\nif err := cli.Parse(root, os.Args[1:]); err != nil {\n\tif errors.Is(err, flag.ErrHelp) {\n\t\tfmt.Fprintf(os.Stdout, \"%s\\n\", cli.DefaultUsage(root))\n\t\treturn\n\t}\n\tfmt.Fprintf(os.Stderr, \"error: %v\\n\", err)\n\tos.Exit(1)\n}\nif err := cli.Run(context.Background(), root, nil); err != nil {\n\tfmt.Fprintf(os.Stderr, \"error: %v\\n\", err)\n\tos.Exit(1)\n}\n```\n\n## Command Structure\n\nEach command is represented by a `Command` struct:\n\n```go\ntype Command struct {\n\tName          string // Required\n\tUsage         string\n\tShortHelp     string\n\tUsageFunc     func(*Command) string\n\tFlags         *flag.FlagSet\n\tFlagsMetadata []FlagMetadata\n\tSubCommands   []*Command\n\tExec          func(ctx context.Context, s *State) error\n}\n```\n\nThe `Name` field is the command's name and is **required**.\n\nThe `Usage` and `ShortHelp` fields are used to generate help text. Nice-to-have but not required.\n\nThe `Flags` field is a `*flag.FlagSet` that defines the command's flags.\n\n\u003e [!TIP]\n\u003e\n\u003e There's a convenience function `FlagsFunc` that allows you to define flags inline:\n\n```go\nroot := \u0026cli.Command{\n\tFlags: cli.FlagsFunc(func(f *flag.FlagSet) {\n\t\tfs.Bool(\"verbose\", false, \"enable verbose output\")\n\t\tfs.String(\"output\", \"\", \"output file\")\n\t\tfs.Int(\"count\", 0, \"number of items\")\n\t}),\n\tFlagsMetadata: []cli.FlagMetadata{\n\t\t{Name: \"c\", Required: true},\n\t},\n}\n```\n\nThe optional `FlagsMetadata` field is a way to extend defined flags. The `flag` package alone is a\nbit limiting, so we add this to provide the most common features, such as handling of required\nflags.\n\nThe `SubCommands` field is a list of `*Command` structs that represent subcommands. This allows you\nto organize CLI applications into a hierarchy of commands. Each subcommand can have its own flags\nand business logic.\n\nThe `Exec` field is a function that is called when the command is executed. This is where you put\nbusiness logic.\n\n## Flag Access\n\nFlags can be accessed using the type-safe `GetFlag` function, called inside the `Exec` function:\n\n```go\n// Access boolean flag\nverbose := cli.GetFlag[bool](state, \"verbose\")\n// Access string flag\noutput := cli.GetFlag[string](state, \"output\")\n// Access integer flag\ncount := cli.GetFlag[int](state, \"count\")\n```\n\n### State Inheritance\n\nChild commands automatically inherit their parent command's flags:\n\n```go\n// Parent command with a verbose flag\nroot := cli.Command{\n\tName: \"root\",\n\tFlags: cli.FlagsFunc(func(f *flag.FlagSet) {\n\t\tf.Bool(\"verbose\", false, \"enable verbose mode\")\n\t}),\n}\n\n// Child command that can access parent's verbose flag\nsub := cli.Command{\n\tName: \"sub\",\n\tExec: func(ctx context.Context, s *cli.State) error {\n\t\tverbose := cli.GetFlag[bool](s, \"verbose\")\n\t\tif verbose {\n\t\t\tfmt.Println(\"Verbose mode enabled\")\n\t\t}\n\t\treturn nil\n\t},\n}\n```\n\n## Help System\n\nHelp text is automatically generated, but you can customize it by setting the `UsageFunc` field.\n\nThere is a `DefaultUsage` function that generates a default help text for a command, which is useful\nto display when `flag.ErrHelp` is returned from `Parse`:\n\n```go\nif err := cli.Parse(root, os.Args[1:]); err != nil {\n\tif errors.Is(err, flag.ErrHelp) {\n\t\tfmt.Fprintf(os.Stdout, \"%s\\n\", cli.DefaultUsage(root)) // Display help text and exit\n\t\treturn\n\t}\n\tfmt.Fprintf(os.Stderr, \"error: %v\\n\", err)\n\tos.Exit(1)\n}\n```\n\n## Usage Syntax Conventions\n\nWhen reading command usage strings, the following syntax is used:\n\n| Syntax        | Description                |\n| ------------- | -------------------------- |\n| `\u003crequired\u003e`  | Required argument          |\n| `[optional]`  | Optional argument          |\n| `\u003carg\u003e...`    | One or more arguments      |\n| `[arg]...`    | Zero or more arguments     |\n| `(a\\|b)`      | Must choose one of a or b  |\n| `[-f \u003cfile\u003e]` | Flag with value (optional) |\n| `-f \u003cfile\u003e`   | Flag with value (required) |\n\nExamples:\n\n```bash\n# Multiple source files, one destination\nmv \u003csource\u003e... \u003cdest\u003e\n\n# Required flag with value, optional config\nbuild -t \u003ctag\u003e [config]...\n\n# Subcommands with own flags\ndocker (run|build) [--file \u003cdockerfile\u003e] \u003cimage\u003e\n\n# Multiple flag values\nfind [--exclude \u003cpattern\u003e]... \u003cpath\u003e\n\n# Choice between options, required path\nchmod (u+x|a+r) \u003cfile\u003e...\n\n# Flag groups with value\nkubectl [-n \u003cnamespace\u003e] (get|delete) (pod|service) \u003cname\u003e\n```\n\n## Status\n\nThis project is in active development and undergoing changes as the API gets refined. Please open an\nissue if you encounter any problems or have suggestions for improvement.\n\n## Acknowledgements\n\nThere are many great CLI libraries out there, but I always felt [they were too heavy for my\nneeds](https://mfridman.com/blog/2021/a-simpler-building-block-for-go-clis/).\n\nI was inspired by Peter Bourgon's [ff](https://github.com/peterbourgon/ff) library, specifically the\n`v3` branch, which was soooo close to what I wanted. But the `v4` branch took a different direction\nand I wanted to keep the simplicity of `v3`. This library aims to pick up where `v3` left off.\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmfridman%2Fcli","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmfridman%2Fcli","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmfridman%2Fcli/lists"}