@@ -49,6 +49,34 @@ func Run(ctx context.Context, root *Command, options *RunOptions) error {
4949 return run (ctx , cmd , root .state )
5050}
5151
52+ // ParseAndRun is a convenience function that combines [Parse] and [Run] into a single call. It
53+ // parses the command hierarchy, handles help flags automatically (printing usage to stdout and
54+ // returning nil), and then executes the resolved command.
55+ //
56+ // This is the recommended entry point for most CLI applications:
57+ //
58+ // if err := cli.ParseAndRun(ctx, root, os.Args[1:], nil); err != nil {
59+ // fmt.Fprintf(os.Stderr, "error: %v\n", err)
60+ // os.Exit(1)
61+ // }
62+ //
63+ // The options parameter may be nil, in which case default values are used. See [RunOptions] for
64+ // more details.
65+ //
66+ // For applications that need to perform work between parsing and execution (e.g., initializing
67+ // resources based on parsed flags), use [Parse] and [Run] separately.
68+ func ParseAndRun (ctx context.Context , root * Command , args []string , options * RunOptions ) error {
69+ if err := Parse (root , args ); err != nil {
70+ if errors .Is (err , ErrHelp ) {
71+ options = checkAndSetRunOptions (options )
72+ _ , _ = fmt .Fprintln (options .Stdout , DefaultUsage (root ))
73+ return nil
74+ }
75+ return err
76+ }
77+ return Run (ctx , root , options )
78+ }
79+
5280func run (ctx context.Context , cmd * Command , state * State ) (retErr error ) {
5381 defer func () {
5482 if r := recover (); r != nil {
@@ -121,12 +149,12 @@ func location(skip int) string {
121149
122150 frame , _ := runtime .CallersFrames (pcs [:n ]).Next ()
123151
124- // Trim the module name from function and file paths for cleaner output.
125- // Function names use the module path directly (e.g., "github.com/pressly/cli.Run").
152+ // Trim the module name from function and file paths for cleaner output. Function names use the
153+ // module path directly (e.g., "github.com/pressly/cli.Run").
126154 fn := strings .TrimPrefix (frame .Function , getGoModuleName ()+ "/" )
127- // File paths from runtime are absolute (e.g., "/Users/.../cli/run.go"). We want a relative
128- // path for cleaner output. Try to find the module's import path in the filesystem path
129- // (works with GOPATH-style layouts), otherwise fall back to just the base filename.
155+ // File paths from runtime are absolute (e.g., "/Users/.../cli/run.go"). We want a relative path
156+ // for cleaner output. Try to find the module's import path in the filesystem path (works with
157+ // GOPATH-style layouts), otherwise fall back to just the base filename.
130158 file := frame .File
131159 mod := getGoModuleName ()
132160 if mod != "" {
0 commit comments