Repository: urfave/cli
Pull request #1446 (2022-08-02 19:14:41)

Add App.InvalidFlagAccessHandler
Follow up from #1434

julian7: I couldn't test it myself, but I like this PR.
icholy: @julian7 I don't like the name. It sounds like it will get called when someone passes an unknown flag, ex `mycli --unknown-flag`
julian7: > @julian7 I don't like the name. It sounds like it will get called when someone passes an unknown flag, ex `mycli --unknown-flag`

Hmm, reasonable. What about UndefinedFlagHandler?
icholy: > > @julian7 I don't like the name. It sounds like it will get called when someone passes an unknown flag, ex `mycli --unknown-flag`
> 
> Hmm, reasonable. What about UndefinedFlagHandler?

That name has the same problem.

Patch:
diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml
index 3b31afc..43ba540 100644
--- a/.github/workflows/cli.yml
+++ b/.github/workflows/cli.yml
@@ -15,7 +15,7 @@ jobs:
     strategy:
       matrix:
         os: [ubuntu-latest, macos-latest, windows-latest]
-        go: [1.16.x, 1.17.x, 1.18.x]
+        go: [1.17.x, 1.18.x, 1.19.x]
     name: ${{ matrix.os }} @ Go ${{ matrix.go }}
     runs-on: ${{ matrix.os }}
     steps:
@@ -27,12 +27,16 @@ jobs:
       - name: Set PATH
         run: echo "${GITHUB_WORKSPACE}/.local/bin" >>"${GITHUB_PATH}"
 
+      - name: install goimports
+        if: matrix.go == '1.19.x' && matrix.os == 'ubuntu-latest'
+        run: GOBIN=${PWD}/.local/bin go install golang.org/x/tools/cmd/goimports@latest
+
       - name: Checkout Code
         uses: actions/checkout@v3
 
-      - name: GOFMT Check
-        if: matrix.go == '1.18.x' && matrix.os == 'ubuntu-latest'
-        run: test -z $(gofmt -l .)
+      - name: goimports check
+        if: matrix.go == '1.19.x' && matrix.os == 'ubuntu-latest'
+        run: test -z $(goimports -l .)
 
       - name: vet
         run: go run internal/build/build.go vet
@@ -43,6 +47,9 @@ jobs:
       - name: test
         run: go run internal/build/build.go test
 
+      - name: test urfave-cli-genflags
+        run: make -C cmd/urfave-cli-genflags
+
       - name: check-binary-size
         run: go run internal/build/build.go check-binary-size
 
@@ -50,7 +57,7 @@ jobs:
         run: go run internal/build/build.go -tags urfave_cli_no_docs check-binary-size
 
       - name: Upload coverage to Codecov
-        if: success() && matrix.go == '1.18.x' && matrix.os == 'ubuntu-latest'
+        if: success() && matrix.go == '1.19.x' && matrix.os == 'ubuntu-latest'
         uses: codecov/codecov-action@v2
         with:
           fail_ci_if_error: true
@@ -62,7 +69,7 @@ jobs:
       - name: Set up Go
         uses: actions/setup-go@v3
         with:
-          go-version: 1.18.x
+          go-version: 1.19.x
 
       - name: Use Node.js 16
         uses: actions/setup-node@v3
@@ -82,7 +89,7 @@ jobs:
           chmod +x "${GITHUB_WORKSPACE}/.local/bin/gfmrun"
 
       - name: gfmrun
-        run: go run internal/build/build.go gfmrun docs/v2/manual.md
+        run: go run internal/build/build.go gfmrun --walk docs/v2/
 
       - name: diff check
         run: |
diff --git a/.gitignore b/.gitignore
index c04fcc5..d7d26b1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,10 +1,13 @@
 *.coverprofile
+*.exe
 *.orig
-vendor
+.*envrc
+.envrc
 .idea
-internal/*/built-example
-coverage.txt
 /.local/
 /site/
-
+coverage.txt
+internal/*/built-example
+vendor
+/cmd/urfave-cli-genflags/urfave-cli-genflags
 *.exe
diff --git a/altsrc/flag.go b/altsrc/flag.go
index db95949..1132ded 100644
--- a/altsrc/flag.go
+++ b/altsrc/flag.go
@@ -64,15 +64,22 @@ func InitInputSourceWithContext(flags []cli.Flag, createInputSource func(cCtx *c
 
 // ApplyInputSourceValue applies a generic value to the flagSet if required
 func (f *GenericFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error {
-	if f.set != nil && !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) && isc.isSet(f.GenericFlag.Name) {
-		value, err := isc.Generic(f.GenericFlag.Name)
+	if f.set == nil || cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars) {
+		return nil
+	}
+	for _, name := range f.GenericFlag.Names() {
+		if !isc.isSet(name) {
+			continue
+		}
+		value, err := isc.Generic(name)
 		if err != nil {
 			return err
 		}
-		if value != nil {
-			for _, name := range f.Names() {
-				_ = f.set.Set(name, value.String())
-			}
+		if value == nil {
+			continue
+		}
+		for _, n := range f.Names() {
+			_ = f.set.Set(n, value.String())
 		}
 	}
 
@@ -81,19 +88,27 @@ func (f *GenericFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceCo
 
 // ApplyInputSourceValue applies a StringSlice value to the flagSet if required
 func (f *StringSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error {
-	if f.set != nil && !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) && isc.isSet(f.StringSliceFlag.Name) {
-		value, err := isc.StringSlice(f.StringSliceFlag.Name)
+	if f.set == nil || cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars) {
+		return nil
+	}
+	for _, name := range f.StringSliceFlag.Names() {
+		if !isc.isSet(name) {
+			continue
+		}
+		value, err := isc.StringSlice(name)
 		if err != nil {
 			return err
 		}
-		if value != nil {
-			var sliceValue cli.StringSlice = *(cli.NewStringSlice(value...))
-			for _, name := range f.Names() {
-				underlyingFlag := f.set.Lookup(name)
-				if underlyingFlag != nil {
-					underlyingFlag.Value = &sliceValue
-				}
+		if value == nil {
+			continue
+		}
+		var sliceValue = *(cli.NewStringSlice(value...))
+		for _, n := range f.Names() {
+			underlyingFlag := f.set.Lookup(n)
+			if underlyingFlag == nil {
+				continue
 			}
+			underlyingFlag.Value = &sliceValue
 		}
 	}
 	return nil
@@ -101,19 +116,27 @@ func (f *StringSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSour
 
 // ApplyInputSourceValue applies a IntSlice value if required
 func (f *IntSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error {
-	if f.set != nil && !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) && isc.isSet(f.IntSliceFlag.Name) {
-		value, err := isc.IntSlice(f.IntSliceFlag.Name)
+	if f.set == nil || cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars) {
+		return nil
+	}
+	for _, name := range f.IntSliceFlag.Names() {
+		if !isc.isSet(name) {
+			continue
+		}
+		value, err := isc.IntSlice(name)
 		if err != nil {
 			return err
 		}
-		if value != nil {
-			var sliceValue cli.IntSlice = *(cli.NewIntSlice(value...))
-			for _, name := range f.Names() {
-				underlyingFlag := f.set.Lookup(name)
-				if underlyingFlag != nil {
-					underlyingFlag.Value = &sliceValue
-				}
+		if value == nil {
+			continue
+		}
+		var sliceValue = *(cli.NewIntSlice(value...))
+		for _, n := range f.Names() {
+			underlyingFlag := f.set.Lookup(n)
+			if underlyingFlag == nil {
+				continue
 			}
+			underlyingFlag.Value = &sliceValue
 		}
 	}
 	return nil
@@ -121,15 +144,19 @@ func (f *IntSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceC
 
 // ApplyInputSourceValue applies a Bool value to the flagSet if required
 func (f *BoolFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error {
-	if f.set != nil && !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) && isc.isSet(f.BoolFlag.Name) {
-		value, err := isc.Bool(f.BoolFlag.Name)
+	if f.set == nil || cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars) {
+		return nil
+	}
+	for _, name := range f.BoolFlag.Names() {
+		if !isc.isSet(name) {
+			continue
+		}
+		value, err := isc.Bool(name)
 		if err != nil {
 			return err
 		}
-		if value {
-			for _, name := range f.Names() {
-				_ = f.set.Set(name, strconv.FormatBool(value))
-			}
+		for _, n := range f.Names() {
+			_ = f.set.Set(n, strconv.FormatBool(value))
 		}
 	}
 	return nil
@@ -137,15 +164,19 @@ func (f *BoolFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceConte
 
 // ApplyInputSourceValue applies a String value to the flagSet if required
 func (f *StringFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error {
-	if f.set != nil && !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) && isc.isSet(f.StringFlag.Name) {
-		value, err := isc.String(f.StringFlag.Name)
+	if f.set == nil || cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars) {
+		return nil
+	}
+	for _, name := range f.StringFlag.Names() {
+		if !isc.isSet(name) {
+			continue
+		}
+		value, err := isc.String(name)
 		if err != nil {
 			return err
 		}
-		if value != "" {
-			for _, name := range f.Names() {
-				_ = f.set.Set(name, value)
-			}
+		for _, n := range f.Names() {
+			_ = f.set.Set(n, value)
 		}
 	}
 	return nil
@@ -153,25 +184,29 @@ func (f *StringFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceCon
 
 // ApplyInputSourceValue applies a Path value to the flagSet if required
 func (f *PathFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error {
-	if f.set != nil && !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) && isc.isSet(f.PathFlag.Name) {
-		value, err := isc.String(f.PathFlag.Name)
+	if f.set == nil || cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars) {
+		return nil
+	}
+	for _, name := range f.PathFlag.Names() {
+		if !isc.isSet(name) {
+			continue
+		}
+		value, err := isc.String(name)
 		if err != nil {
 			return err
 		}
-		if value != "" {
-			for _, name := range f.Names() {
-
-				if !filepath.IsAbs(value) && isc.Source() != "" {
-					basePathAbs, err := filepath.Abs(isc.Source())
-					if err != nil {
-						return err
-					}
-
-					value = filepath.Join(filepath.Dir(basePathAbs), value)
+		if value == "" {
+			continue
+		}
+		for _, n := range f.Names() {
+			if !filepath.IsAbs(value) && isc.Source() != "" {
+				basePathAbs, err := filepath.Abs(isc.Source())
+				if err != nil {
+					return err
 				}
-
-				_ = f.set.Set(name, value)
+				value = filepath.Join(filepath.Dir(basePathAbs), value)
 			}
+			_ = f.set.Set(n, value)
 		}
 	}
 	return nil
@@ -179,13 +214,19 @@ func (f *PathFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceConte
 
 // ApplyInputSourceValue applies a int value to the flagSet if required
 func (f *IntFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error {
-	if f.set != nil && !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) && isc.isSet(f.IntFlag.Name) {
-		value, err := isc.Int(f.IntFlag.Name)
+	if f.set == nil || cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars) {
+		return nil
+	}
+	for _, name := range f.IntFlag.Names() {
+		if !isc.isSet(name) {
+			continue
+		}
+		value, err := isc.Int(name)
 		if err != nil {
 			return err
 		}
-		for _, name := range f.Names() {
-			_ = f.set.Set(name, strconv.FormatInt(int64(value), 10))
+		for _, n := range f.Names() {
+			_ = f.set.Set(n, strconv.FormatInt(int64(value), 10))
 		}
 	}
 	return nil
@@ -193,13 +234,19 @@ func (f *IntFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContex
 
 // ApplyInputSourceValue applies a Duration value to the flagSet if required
 func (f *DurationFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error {
-	if f.set != nil && !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) && isc.isSet(f.DurationFlag.Name) {
-		value, err := isc.Duration(f.DurationFlag.Name)
+	if f.set == nil || cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars) {
+		return nil
+	}
+	for _, name := range f.DurationFlag.Names() {
+		if !isc.isSet(name) {
+			continue
+		}
+		value, err := isc.Duration(name)
 		if err != nil {
 			return err
 		}
-		for _, name := range f.Names() {
-			_ = f.set.Set(name, value.String())
+		for _, n := range f.Names() {
+			_ = f.set.Set(n, value.String())
 		}
 	}
 	return nil
@@ -207,14 +254,20 @@ func (f *DurationFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceC
 
 // ApplyInputSourceValue applies a Float64 value to the flagSet if required
 func (f *Float64Flag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error {
-	if f.set != nil && !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) && isc.isSet(f.Float64Flag.Name) {
-		value, err := isc.Float64(f.Float64Flag.Name)
+	if f.set == nil || cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars) {
+		return nil
+	}
+	for _, name := range f.Float64Flag.Names() {
+		if !isc.isSet(name) {
+			continue
+		}
+		value, err := isc.Float64(name)
 		if err != nil {
 			return err
 		}
 		floatStr := float64ToString(value)
-		for _, name := range f.Names() {
-			_ = f.set.Set(name, floatStr)
+		for _, n := range f.Names() {
+			_ = f.set.Set(n, floatStr)
 		}
 	}
 	return nil
diff --git a/app.go b/app.go
index 0b3f45c..1d2f6e8 100644
--- a/app.go
+++ b/app.go
@@ -78,6 +78,8 @@ type App struct {
 	CommandNotFound CommandNotFoundFunc
 	// Execute this function if a usage error occurs
 	OnUsageError OnUsageErrorFunc
+	// Execute this function when an invalid flag is accessed from the context
+	InvalidFlagAccessHandler InvalidFlagAccessFunc
 	// Compilation date
 	Compiled time.Time
 	// List of all authors who contributed
@@ -304,7 +306,7 @@ func (a *App) RunContext(ctx context.Context, arguments []string) (err error) {
 		return err
 	}
 
-	if a.After != nil {
+	if a.After != nil && !cCtx.shellComplete {
 		defer func() {
 			if afterErr := a.After(cCtx); afterErr != nil {
 				if err != nil {
@@ -332,7 +334,7 @@ func (a *App) RunContext(ctx context.Context, arguments []string) (err error) {
 		return cerr
 	}
 
-	if a.Before != nil {
+	if a.Before != nil && !cCtx.shellComplete {
 		beforeErr := a.Before(cCtx)
 		if beforeErr != nil {
 			a.handleExitCoder(cCtx, beforeErr)
@@ -499,7 +501,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
 		return cerr
 	}
 
-	if a.After != nil {
+	if a.After != nil && !cCtx.shellComplete {
 		defer func() {
 			afterErr := a.After(cCtx)
 			if afterErr != nil {
@@ -513,7 +515,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
 		}()
 	}
 
-	if a.Before != nil {
+	if a.Before != nil && !cCtx.shellComplete {
 		beforeErr := a.Before(cCtx)
 		if beforeErr != nil {
 			a.handleExitCoder(cCtx, beforeErr)
diff --git a/cli.go b/cli.go
index 2a11c5a..b3b864c 100644
--- a/cli.go
+++ b/cli.go
@@ -1,23 +1,25 @@
 // Package cli provides a minimal framework for creating and organizing command line
 // Go applications. cli is designed to be easy to understand and write, the most simple
 // cli application can be written as follows:
-//   func main() {
-//     (&cli.App{}).Run(os.Args)
-//   }
+//
+//	func main() {
+//		(&cli.App{}).Run(os.Args)
+//	}
 //
 // Of course this application does not do much, so let's make this an actual application:
-//   func main() {
-//     app := &cli.App{
-//			 Name: "greet",
-//			 Usage: "say a greeting",
-//			 Action: func(c *cli.Context) error {
-//				 fmt.Println("Greetings")
-//				 return nil
-//			 },
-//		 }
 //
-//     app.Run(os.Args)
-//   }
+//	func main() {
+//		app := &cli.App{
+//	  		Name: "greet",
+//	  		Usage: "say a greeting",
+//	  		Action: func(c *cli.Context) error {
+//	  			fmt.Println("Greetings")
+//	  			return nil
+//	  		},
+//		}
+//
+//		app.Run(os.Args)
+//	}
 package cli
 
-//go:generate go run internal/genflags/cmd/genflags/main.go
+//go:generate go run cmd/urfave-cli-genflags/main.go
diff --git a/cmd/urfave-cli-genflags/Makefile b/cmd/urfave-cli-genflags/Makefile
new file mode 100644
index 0000000..3b11415
--- /dev/null
+++ b/cmd/urfave-cli-genflags/Makefile
@@ -0,0 +1,21 @@
+GOTEST_FLAGS ?= -v --coverprofile main.coverprofile --covermode count --cover github.com/urfave/cli/v2/cmd/urfave-cli-genflags
+GOBUILD_FLAGS ?= -x
+
+.PHONY: all
+all: test build smoke-test
+
+.PHONY: test
+test:
+	go test $(GOTEST_FLAGS) ./...
+
+.PHONY: build
+build:
+	go build $(GOBUILD_FLAGS) ./...
+
+.PHONY: smoke-test
+smoke-test: build
+	./urfave-cli-genflags --help
+
+.PHONY: show-cover
+show-cover:
+	go tool cover -func main.coverprofile
diff --git a/cmd/urfave-cli-genflags/README.md b/cmd/urfave-cli-genflags/README.md
new file mode 100644
index 0000000..9424234
--- /dev/null
+++ b/cmd/urfave-cli-genflags/README.md
@@ -0,0 +1,15 @@
+# urfave-cli-genflags
+
+This is a tool that is used internally by [urfave/cli] to generate
+flag types and methods from a YAML input. It intentionally pins
+usage of `github.com/urfave/cli/v2` to a *release* rather than
+using the adjacent code so that changes don't result in *this* tool
+refusing to compile. It's almost like dogfooding?
+
+## support warning
+
+This tool is maintained as a sub-project and is not covered by the
+API and backward compatibility guaranteed by releases of
+[urfave/cli].
+
+[urfave/cli]: https://github.com/urfave/cli
diff --git a/internal/genflags/generated.gotmpl b/cmd/urfave-cli-genflags/generated.gotmpl
similarity index 100%
rename from internal/genflags/generated.gotmpl
rename to cmd/urfave-cli-genflags/generated.gotmpl
diff --git a/internal/genflags/generated_test.gotmpl b/cmd/urfave-cli-genflags/generated_test.gotmpl
similarity index 100%
rename from internal/genflags/generated_test.gotmpl
rename to cmd/urfave-cli-genflags/generated_test.gotmpl
diff --git a/cmd/urfave-cli-genflags/go.mod b/cmd/urfave-cli-genflags/go.mod
new file mode 100644
index 0000000..af40aaa
--- /dev/null
+++ b/cmd/urfave-cli-genflags/go.mod
@@ -0,0 +1,15 @@
+module github.com/urfave/cli/v2/cmd/urfave-cli-genflags
+
+go 1.18
+
+require (
+	github.com/urfave/cli/v2 v2.11.2
+	golang.org/x/text v0.3.7
+	gopkg.in/yaml.v3 v3.0.1
+)
+
+require (
+	github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
+	github.com/russross/blackfriday/v2 v2.1.0 // indirect
+	github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
+)
diff --git a/cmd/urfave-cli-genflags/go.sum b/cmd/urfave-cli-genflags/go.sum
new file mode 100644
index 0000000..9821127
--- /dev/null
+++ b/cmd/urfave-cli-genflags/go.sum
@@ -0,0 +1,17 @@
+github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
+github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
+github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
+github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
+github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
+github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/urfave/cli/v2 v2.11.2 h1:FVfNg4m3vbjbBpLYxW//WjxUoHvJ9TlppXcqY9Q9ZfA=
+github.com/urfave/cli/v2 v2.11.2/go.mod h1:f8iq5LtQ/bLxafbdBSLPPNsgaW0l/2fYYEHhAyPlwvo=
+github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
+github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
+golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/internal/genflags/cmd/genflags/main.go b/cmd/urfave-cli-genflags/main.go
similarity index 53%
rename from internal/genflags/cmd/genflags/main.go
rename to cmd/urfave-cli-genflags/main.go
index 4212e60..835216a 100644
--- a/internal/genflags/cmd/genflags/main.go
+++ b/cmd/urfave-cli-genflags/main.go
@@ -9,12 +9,14 @@ import (
 	"os/exec"
 	"os/signal"
 	"path/filepath"
+	"sort"
 	"strings"
 	"syscall"
 	"text/template"
 
 	"github.com/urfave/cli/v2"
-	"github.com/urfave/cli/v2/internal/genflags"
+	"golang.org/x/text/cases"
+	"golang.org/x/text/language"
 	"gopkg.in/yaml.v3"
 )
 
@@ -22,6 +24,16 @@ const (
 	defaultPackageName = "cli"
 )
 
+var (
+	//go:embed generated.gotmpl
+	TemplateString string
+
+	//go:embed generated_test.gotmpl
+	TestTemplateString string
+
+	titler = cases.Title(language.Und, cases.NoLower)
+)
+
 func sh(ctx context.Context, exe string, args ...string) (string, error) {
 	cmd := exec.CommandContext(ctx, exe, args...)
 	cmd.Stderr = os.Stderr
@@ -92,7 +104,7 @@ func runGenFlags(cCtx *cli.Context) error {
 		return err
 	}
 
-	spec := &genflags.Spec{}
+	spec := &Spec{}
 	if err := yaml.Unmarshal(specBytes, spec); err != nil {
 		return err
 	}
@@ -123,12 +135,12 @@ func runGenFlags(cCtx *cli.Context) error {
 		spec.UrfaveCLITestNamespace = "cli."
 	}
 
-	genTmpl, err := template.New("gen").Parse(genflags.TemplateString)
+	genTmpl, err := template.New("gen").Parse(TemplateString)
 	if err != nil {
 		return err
 	}
 
-	genTestTmpl, err := template.New("gen_test").Parse(genflags.TestTemplateString)
+	genTestTmpl, err := template.New("gen_test").Parse(TestTemplateString)
 	if err != nil {
 		return err
 	}
@@ -161,3 +173,120 @@ func runGenFlags(cCtx *cli.Context) error {
 
 	return nil
 }
+
+func TypeName(goType string, fc *FlagTypeConfig) string {
+	if fc != nil && strings.TrimSpace(fc.TypeName) != "" {
+		return strings.TrimSpace(fc.TypeName)
+	}
+
+	dotSplit := strings.Split(goType, ".")
+	goType = dotSplit[len(dotSplit)-1]
+
+	if strings.HasPrefix(goType, "[]") {
+		return titler.String(strings.TrimPrefix(goType, "[]")) + "SliceFlag"
+	}
+
+	return titler.String(goType) + "Flag"
+}
+
+type Spec struct {
+	FlagTypes              map[string]*FlagTypeConfig `yaml:"flag_types"`
+	PackageName            string                     `yaml:"package_name"`
+	TestPackageName        string                     `yaml:"test_package_name"`
+	UrfaveCLINamespace     string                     `yaml:"urfave_cli_namespace"`
+	UrfaveCLITestNamespace string                     `yaml:"urfave_cli_test_namespace"`
+}
+
+func (gfs *Spec) SortedFlagTypes() []*FlagType {
+	typeNames := []string{}
+
+	for name := range gfs.FlagTypes {
+		if strings.HasPrefix(name, "[]") {
+			name = strings.TrimPrefix(name, "[]") + "Slice"
+		}
+
+		typeNames = append(typeNames, name)
+	}
+
+	sort.Strings(typeNames)
+
+	ret := make([]*FlagType, len(typeNames))
+
+	for i, typeName := range typeNames {
+		ret[i] = &FlagType{
+			GoType: typeName,
+			Config: gfs.FlagTypes[typeName],
+		}
+	}
+
+	return ret
+}
+
+type FlagTypeConfig struct {
+	SkipInterfaces []string           `yaml:"skip_interfaces"`
+	StructFields   []*FlagStructField `yaml:"struct_fields"`
+	TypeName       string             `yaml:"type_name"`
+	ValuePointer   bool               `yaml:"value_pointer"`
+}
+
+type FlagStructField struct {
+	Name string
+	Type string
+}
+
+type FlagType struct {
+	GoType string
+	Config *FlagTypeConfig
+}
+
+func (ft *FlagType) StructFields() []*FlagStructField {
+	if ft.Config == nil || ft.Config.StructFields == nil {
+		return []*FlagStructField{}
+	}
+
+	return ft.Config.StructFields
+}
+
+func (ft *FlagType) ValuePointer() bool {
+	if ft.Config == nil {
+		return false
+	}
+
+	return ft.Config.ValuePointer
+}
+
+func (ft *FlagType) TypeName() string {
+	return TypeName(ft.GoType, ft.Config)
+}
+
+func (ft *FlagType) GenerateFmtStringerInterface() bool {
+	return ft.skipInterfaceNamed("fmt.Stringer")
+}
+
+func (ft *FlagType) GenerateFlagInterface() bool {
+	return ft.skipInterfaceNamed("Flag")
+}
+
+func (ft *FlagType) GenerateRequiredFlagInterface() bool {
+	return ft.skipInterfaceNamed("RequiredFlag")
+}
+
+func (ft *FlagType) GenerateVisibleFlagInterface() bool {
+	return ft.skipInterfaceNamed("VisibleFlag")
+}
+
+func (ft *FlagType) skipInterfaceNamed(name string) bool {
+	if ft.Config == nil {
+		return true
+	}
+
+	lowName := strings.ToLower(name)
+
+	for _, interfaceName := range ft.Config.SkipInterfaces {
+		if strings.ToLower(interfaceName) == lowName {
+			return false
+		}
+	}
+
+	return true
+}
diff --git a/context.go b/context.go
index 6b497ed..dc0d1ef 100644
--- a/context.go
+++ b/context.go
@@ -46,6 +46,9 @@ func (cCtx *Context) NumFlags() int {
 
 // Set sets a context flag to a value.
 func (cCtx *Context) Set(name, value string) error {
+	if cCtx.flagSet.Lookup(name) == nil {
+		cCtx.onInvalidFlag(name)
+	}
 	return cCtx.flagSet.Set(name, value)
 }
 
@@ -158,7 +161,7 @@ func (cCtx *Context) lookupFlagSet(name string) *flag.FlagSet {
 			return c.flagSet
 		}
 	}
-
+	cCtx.onInvalidFlag(name)
 	return nil
 }
 
@@ -170,9 +173,7 @@ func (cCtx *Context) checkRequiredFlags(flags []Flag) requiredFlagsErr {
 			var flagName string
 
 			for _, key := range f.Names() {
-				if len(key) > 1 {
-					flagName = key
-				}
+				flagName = key
 
 				if cCtx.IsSet(strings.TrimSpace(key)) {
 					flagPresent = true
@@ -192,6 +193,16 @@ func (cCtx *Context) checkRequiredFlags(flags []Flag) requiredFlagsErr {
 	return nil
 }
 
+func (cCtx *Context) onInvalidFlag(name string) {
+	for cCtx != nil {
+		if cCtx.App != nil && cCtx.App.InvalidFlagAccessHandler != nil {
+			cCtx.App.InvalidFlagAccessHandler(cCtx, name)
+			break
+		}
+		cCtx = cCtx.parentContext
+	}
+}
+
 func makeFlagNameVisitor(names *[]string) func(*flag.Flag) {
 	return func(f *flag.Flag) {
 		nameParts := strings.Split(f.Name, ",")
diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md
index b0d9bfc..4462899 100644
--- a/docs/CONTRIBUTING.md
+++ b/docs/CONTRIBUTING.md
@@ -95,7 +95,7 @@ The built-in `go generate` command is used to run the commands specified in
 line help system which may be consulted for further information, e.g.:
 
 ```sh
-go run internal/genflags/cmd/genflags/main.go --help
+go run cmd/urfave-cli-genflags/main.go --help
 ```
 
 #### docs output
diff --git a/docs/index.md b/docs/index.md
index 5f02fed..60a1102 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -11,8 +11,8 @@ an expressive way.
 
 These are the guides for each major supported version:
 
-- [`v2`](./v2/)
-- [`v1`](./v1/)
+- [`v2`](./v2/getting-started)
+- [`v1`](./v1/getting-started)
 
 In addition to the version-specific guides, these other documents are available:
 
diff --git a/docs/v1/examples/arguments.md b/docs/v1/examples/arguments.md
new file mode 100644
index 0000000..4416d32
--- /dev/null
+++ b/docs/v1/examples/arguments.md
@@ -0,0 +1,35 @@
+---
+tags:
+  - v1
+---
+
+You can lookup arguments by calling the `Args` function on `cli.Context`, e.g.:
+
+<!-- {
+  "output": "Hello \""
+} -->
+``` go
+package main
+
+import (
+  "fmt"
+  "log"
+  "os"
+
+  "github.com/urfave/cli"
+)
+
+func main() {
+  app := cli.NewApp()
+
+  app.Action = func(c *cli.Context) error {
+    fmt.Printf("Hello %q", c.Args().Get(0))
+    return nil
+  }
+
+  err := app.Run(os.Args)
+  if err != nil {
+    log.Fatal(err)
+  }
+}
+```
diff --git a/docs/v1/examples/bash-completions.md b/docs/v1/examples/bash-completions.md
new file mode 100644
index 0000000..73af176
--- /dev/null
+++ b/docs/v1/examples/bash-completions.md
@@ -0,0 +1,119 @@
+---
+tags:
+  - v1
+---
+
+You can enable completion commands by setting the `EnableBashCompletion`
+flag on the `App` object.  By default, this setting will only auto-complete to
+show an app's subcommands, but you can write your own completion methods for
+the App or its subcommands.
+
+<!-- {
+  "args": ["complete", "&#45;&#45;generate&#45;bash&#45;completion"],
+  "output": "laundry"
+} -->
+``` go
+package main
+
+import (
+  "fmt"
+  "log"
+  "os"
+
+  "github.com/urfave/cli"
+)
+
+func main() {
+  tasks := []string{"cook", "clean", "laundry", "eat", "sleep", "code"}
+
+  app := cli.NewApp()
+  app.EnableBashCompletion = true
+  app.Commands = []cli.Command{
+    {
+      Name:  "complete",
+      Aliases: []string{"c"},
+      Usage: "complete a task on the list",
+      Action: func(c *cli.Context) error {
+         fmt.Println("completed task: ", c.Args().First())
+         return nil
+      },
+      BashComplete: func(c *cli.Context) {
+        // This will complete if no args are passed
+        if c.NArg() > 0 {
+          return
+        }
+        for _, t := range tasks {
+          fmt.Println(t)
+        }
+      },
+    },
+  }
+
+  err := app.Run(os.Args)
+  if err != nil {
+    log.Fatal(err)
+  }
+}
+```
+
+#### Enabling
+
+Source the `autocomplete/bash_autocomplete` file in your `.bashrc` file while
+setting the `PROG` variable to the name of your program:
+
+`PROG=myprogram source /.../cli/autocomplete/bash_autocomplete`
+
+#### Distribution
+
+Copy `autocomplete/bash_autocomplete` into `/etc/bash_completion.d/` and rename
+it to the name of the program you wish to add autocomplete support for (or
+automatically install it there if you are distributing a package). Don't forget
+to source the file to make it active in the current shell.
+
+```
+sudo cp src/bash_autocomplete /etc/bash_completion.d/<myprogram>
+source /etc/bash_completion.d/<myprogram>
+```
+
+Alternatively, you can just document that users should source the generic
+`autocomplete/bash_autocomplete` in their bash configuration with `$PROG` set
+to the name of their program (as above).
+
+#### Customization
+
+The default bash completion flag (`--generate-bash-completion`) is defined as
+`cli.BashCompletionFlag`, and may be redefined if desired, e.g.:
+
+<!-- {
+  "args": ["&#45;&#45;compgen"],
+  "output": "wat\nhelp\nh"
+} -->
+``` go
+package main
+
+import (
+  "log"
+  "os"
+
+  "github.com/urfave/cli"
+)
+
+func main() {
+  cli.BashCompletionFlag = cli.BoolFlag{
+    Name:   "compgen",
+    Hidden: true,
+  }
+
+  app := cli.NewApp()
+  app.EnableBashCompletion = true
+  app.Commands = []cli.Command{
+    {
+      Name: "wat",
+    },
+  }
+  err := app.Run(os.Args)
+  if err != nil {
+    log.Fatal(err)
+  }
+}
+```
diff --git a/docs/v1/examples/combining-short-options.md b/docs/v1/examples/combining-short-options.md
new file mode 100644
index 0000000..0496300
--- /dev/null
+++ b/docs/v1/examples/combining-short-options.md
@@ -0,0 +1,72 @@
+---
+tags:
+  - v1
+---
+
+Traditional use of options using their shortnames look like this:
+
+```
+$ cmd -s -o -m "Some message"
+```
+
+Suppose you want users to be able to combine options with their shortnames. This
+can be done using the `UseShortOptionHandling` bool in your app configuration,
+or for individual commands by attaching it to the command configuration. For
+example:
+
+<!-- {
+  "args": ["short", "&#45;som", "Some message"],
+  "output": "serve: true\noption: true\nmessage: Some message\n"
+} -->
+``` go
+package main
+
+import (
+  "fmt"
+  "log"
+  "os"
+
+  "github.com/urfave/cli"
+)
+
+func main() {
+  app := cli.NewApp()
+  app.UseShortOptionHandling = true
+  app.Commands = []cli.Command{
+    {
+      Name:  "short",
+      Usage: "complete a task on the list",
+      Flags: []cli.Flag{
+        cli.BoolFlag{Name: "serve, s"},
+        cli.BoolFlag{Name: "option, o"},
+        cli.StringFlag{Name: "message, m"},
+      },
+      Action: func(c *cli.Context) error {
+        fmt.Println("serve:", c.Bool("serve"))
+        fmt.Println("option:", c.Bool("option"))
+        fmt.Println("message:", c.String("message"))
+        return nil
+      },
+    },
+  }
+
+  err := app.Run(os.Args)
+  if err != nil {
+    log.Fatal(err)
+  }
+}
+```
+
+If your program has any number of bool flags such as `serve` and `option`, and
+optionally one non-bool flag `message`, with the short options of `-s`, `-o`,
+and `-m` respectively, setting `UseShortOptionHandling` will also support the
+following syntax:
+
+```
+$ cmd -som "Some message"
+```
+
+If you enable `UseShortOptionHandling`, then you must not use any flags that
+have a single leading `-` or this will result in failures. For example,
+`-option` can no longer be used. Flags with two leading dashes (such as
+`--options`) are still valid.
diff --git a/docs/v1/examples/exit-codes.md b/docs/v1/examples/exit-codes.md
new file mode 100644
index 0000000..1d6627d
--- /dev/null
+++ b/docs/v1/examples/exit-codes.md
@@ -0,0 +1,43 @@
+---
+tags:
+  - v1
+---
+
+Calling `App.Run` will not automatically call `os.Exit`, which means that by
+default the exit code will "fall through" to being `0`.  An explicit exit code
+may be set by returning a non-nil error that fulfills `cli.ExitCoder`, *or* a
+`cli.MultiError` that includes an error that fulfills `cli.ExitCoder`, e.g.:
+<!-- {
+  "error": "Ginger croutons are not in the soup"
+} -->
+``` go
+package main
+
+import (
+  "log"
+  "os"
+
+  "github.com/urfave/cli"
+)
+
+func main() {
+  app := cli.NewApp()
+  app.Flags = []cli.Flag{
+    cli.BoolFlag{
+      Name:  "ginger-crouton",
+      Usage: "Add ginger croutons to the soup",
+    },
+  }
+  app.Action = func(ctx *cli.Context) error {
+    if !ctx.Bool("ginger-crouton") {
+      return cli.NewExitError("Ginger croutons are not in the soup", 86)
+    }
+    return nil
+  }
+
+  err := app.Run(os.Args)
+  if err != nil {
+    log.Fatal(err)
+  }
+}
+```
diff --git a/docs/v1/examples/flags.md b/docs/v1/examples/flags.md
new file mode 100644
index 0000000..2f9300b
--- /dev/null
+++ b/docs/v1/examples/flags.md
@@ -0,0 +1,462 @@
+---
+tags:
+  - v1
+---
+
+Setting and querying flags is simple.
+
+<!-- {
+  "output": "Hello Nefertiti"
+} -->
+``` go
+package main
+
+import (
+  "fmt"
+  "log"
+  "os"
+
+  "github.com/urfave/cli"
+)
+
+func main() {
+  app := cli.NewApp()
+
+  app.Flags = []cli.Flag {
+    cli.StringFlag{
+      Name: "lang",
+      Value: "english",
+      Usage: "language for the greeting",
+    },
+  }
+
+  app.Action = func(c *cli.Context) error {
+    name := "Nefertiti"
+    if c.NArg() > 0 {
+      name = c.Args().Get(0)
+    }
+    if c.String("lang") == "spanish" {
+      fmt.Println("Hola", name)
+    } else {
+      fmt.Println("Hello", name)
+    }
+    return nil
+  }
+
+  err := app.Run(os.Args)
+  if err != nil {
+    log.Fatal(err)
+  }
+}
+```
+
+You can also set a destination variable for a flag, to which the content will be
+scanned.
+
+<!-- {
+  "output": "Hello someone"
+} -->
+``` go
+package main
+
+import (
+  "log"
+  "os"
+  "fmt"
+
+  "github.com/urfave/cli"
+)
+
+func main() {
+  var language string
+
+  app := cli.NewApp()
+
+  app.Flags = []cli.Flag {
+    cli.StringFlag{
+      Name:        "lang",
+      Value:       "english",
+      Usage:       "language for the greeting",
+      Destination: &language,
+    },
+  }
+
+  app.Action = func(c *cli.Context) error {
+    name := "someone"
+    if c.NArg() > 0 {
+      name = c.Args()[0]
+    }
+    if language == "spanish" {
+      fmt.Println("Hola", name)
+    } else {
+      fmt.Println("Hello", name)
+    }
+    return nil
+  }
+
+  err := app.Run(os.Args)
+  if err != nil {
+    log.Fatal(err)
+  }
+}
+```
+
+See full list of flags at http://godoc.org/github.com/urfave/cli
+
+#### Placeholder Values
+
+Sometimes it's useful to specify a flag's value within the usage string itself.
+Such placeholders are indicated with back quotes.
+
+For example this:
+
+<!-- {
+  "args": ["&#45;&#45;help"],
+  "output": "&#45;&#45;config FILE, &#45;c FILE"
+} -->
+```go
+package main
+
+import (
+  "log"
+  "os"
+
+  "github.com/urfave/cli"
+)
+
+func main() {
+  app := cli.NewApp()
+
+  app.Flags = []cli.Flag{
+    cli.StringFlag{
+      Name:  "config, c",
+      Usage: "Load configuration from `FILE`",
+    },
+  }
+
+  err := app.Run(os.Args)
+  if err != nil {
+    log.Fatal(err)
+  }
+}
+```
+
+Will result in help output like:
+
+```
+--config FILE, -c FILE   Load configuration from FILE
+```
+
+Note that only the first placeholder is used. Subsequent back-quoted words will
+be left as-is.
+
+#### Alternate Names
+
+You can set alternate (or short) names for flags by providing a comma-delimited
+list for the `Name`. e.g.
+
+<!-- {
+  "args": ["&#45;&#45;help"],
+  "output": "&#45;&#45;lang value, &#45;l value.*language for the greeting.*default: \"english\""
+} -->
+``` go
+package main
+
+import (
+  "log"
+  "os"
+
+  "github.com/urfave/cli"
+)
+
+func main() {
+  app := cli.NewApp()
+
+  app.Flags = []cli.Flag {
+    cli.StringFlag{
+      Name: "lang, l",
+      Value: "english",
+      Usage: "language for the greeting",
+    },
+  }
+
+  err := app.Run(os.Args)
+  if err != nil {
+    log.Fatal(err)
+  }
+}
+```
+
+That flag can then be set with `--lang spanish` or `-l spanish`. Note that
+giving two different forms of the same flag in the same command invocation is an
+error.
+
+#### Ordering
+
+Flags for the application and commands are shown in the order they are defined.
+However, it's possible to sort them from outside this library by using `FlagsByName`
+or `CommandsByName` with `sort`.
+
+For example this:
+
+<!-- {
+  "args": ["&#45;&#45;help"],
+  "output": "add a task to the list\n.*complete a task on the list\n.*\n\n.*\n.*Load configuration from FILE\n.*Language for the greeting.*"
+} -->
+``` go
+package main
+
+import (
+  "log"
+  "os"
+  "sort"
+
+  "github.com/urfave/cli"
+)
+
+func main() {
+  app := cli.NewApp()
+
+  app.Flags = []cli.Flag {
+    cli.StringFlag{
+      Name: "lang, l",
+      Value: "english",
+      Usage: "Language for the greeting",
+    },
+    cli.StringFlag{
+      Name: "config, c",
+      Usage: "Load configuration from `FILE`",
+    },
+  }
+
+  app.Commands = []cli.Command{
+    {
+      Name:    "complete",
+      Aliases: []string{"c"},
+      Usage:   "complete a task on the list",
+      Action:  func(c *cli.Context) error {
+        return nil
+      },
+    },
+    {
+      Name:    "add",
+      Aliases: []string{"a"},
+      Usage:   "add a task to the list",
+      Action:  func(c *cli.Context) error {
+        return nil
+      },
+    },
+  }
+
+  sort.Sort(cli.FlagsByName(app.Flags))
+  sort.Sort(cli.CommandsByName(app.Commands))
+
+  err := app.Run(os.Args)
+  if err != nil {
+    log.Fatal(err)
+  }
+}
+```
+
+Will result in help output like:
+
+```
+--config FILE, -c FILE  Load configuration from FILE
+--lang value, -l value  Language for the greeting (default: "english")
+```
+
+#### Values from the Environment
+
+You can also have the default value set from the environment via `EnvVar`.  e.g.
+
+<!-- {
+  "args": ["&#45;&#45;help"],
+  "output": "language for the greeting.*APP_LANG"
+} -->
+``` go
+package main
+
+import (
+  "log"
+  "os"
+
+  "github.com/urfave/cli"
+)
+
+func main() {
+  app := cli.NewApp()
+
+  app.Flags = []cli.Flag {
+    cli.StringFlag{
+      Name: "lang, l",
+      Value: "english",
+      Usage: "language for the greeting",
+      EnvVar: "APP_LANG",
+    },
+  }
+
+  err := app.Run(os.Args)
+  if err != nil {
+    log.Fatal(err)
+  }
+}
+```
+
+The `EnvVar` may also be given as a comma-delimited "cascade", where the first
+environment variable that resolves is used as the default.
+
+<!-- {
+  "args": ["&#45;&#45;help"],
+  "output": "language for the greeting.*LEGACY_COMPAT_LANG.*APP_LANG.*LANG"
+} -->
+``` go
+package main
+
+import (
+  "log"
+  "os"
+
+  "github.com/urfave/cli"
+)
+
+func main() {
+  app := cli.NewApp()
+
+  app.Flags = []cli.Flag {
+    cli.StringFlag{
+      Name: "lang, l",
+      Value: "english",
+      Usage: "language for the greeting",
+      EnvVar: "LEGACY_COMPAT_LANG,APP_LANG,LANG",
+    },
+  }
+
+  err := app.Run(os.Args)
+  if err != nil {
+    log.Fatal(err)
+  }
+}
+```
+
+#### Values from files
+
+You can also have the default value set from file via `FilePath`.  e.g.
+
+<!-- {
+  "args": ["&#45;&#45;help"],
+  "output": "password for the mysql database"
+} -->
+``` go
+package main
+
+import (
+  "log"
+  "os"
+
+  "github.com/urfave/cli"
+)
+
+func main() {
+  app := cli.NewApp()
+
+  app.Flags = []cli.Flag {
+    cli.StringFlag{
+      Name: "password, p",
+      Usage: "password for the mysql database",
+      FilePath: "/etc/mysql/password",
+    },
+  }
+
+  err := app.Run(os.Args)
+  if err != nil {
+    log.Fatal(err)
+  }
+}
+```
+
+Note that default values set from file (e.g. `FilePath`) take precedence over
+default values set from the environment (e.g. `EnvVar`).
+
+#### Values from alternate input sources (YAML, TOML, and others)
+
+There is a separate package altsrc that adds support for getting flag values
+from other file input sources.
+
+Currently supported input source formats:
+* YAML
+* JSON
+* TOML
+
+In order to get values for a flag from an alternate input source the following
+code would be added to wrap an existing cli.Flag like below:
+
+``` go
+  altsrc.NewIntFlag(cli.IntFlag{Name: "test"})
+```
+
+Initialization must also occur for these flags. Below is an example initializing
+getting data from a yaml file below.
+
+``` go
+  command.Before = altsrc.InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
+```
+
+The code above will use the "load" string as a flag name to get the file name of
+a yaml file from the cli.Context.  It will then use that file name to initialize
+the yaml input source for any flags that are defined on that command.  As a note
+the "load" flag used would also have to be defined on the command flags in order
+for this code snippet to work.
+
+Currently only YAML, JSON, and TOML files are supported but developers can add support
+for other input sources by implementing the altsrc.InputSourceContext for their
+given sources.
+
+Here is a more complete sample of a command using YAML support:
+
+<!-- {
+  "args": ["&#45;&#45;help"],
+  "output": "&#45&#45;test value.*default: 0"
+} -->
+``` go
+package notmain
+
+import (
+  "fmt"
+  "log"
+  "os"
+
+  "github.com/urfave/cli"
+  "github.com/urfave/cli/altsrc"
+)
+
+func main() {
+  app := cli.NewApp()
+
+  flags := []cli.Flag{
+    altsrc.NewIntFlag(cli.IntFlag{Name: "test"}),
+    cli.StringFlag{Name: "load"},
+  }
+
+  app.Action = func(c *cli.Context) error {
+    fmt.Println("yaml ist rad")
+    return nil
+  }
+
+  app.Before = altsrc.InitInputSourceWithContext(flags, altsrc.NewYamlSourceFromFlagFunc("load"))
+  app.Flags = flags
+
+  err := app.Run(os.Args)
+  if err != nil {
+    log.Fatal(err)
+  }
+}
+```
+
+#### Precedence
+
+The precedence for flag value sources is as follows (highest to lowest):
+
+0. Command line flag value from user
+0. Environment variable (if specified)
+0. Configuration file (if specified)
+0. Default defined on the flag
diff --git a/docs/v1/examples/generated-help-text.md b/docs/v1/examples/generated-help-text.md
new file mode 100644
index 0000000..84ec272
--- /dev/null
+++ b/docs/v1/examples/generated-help-text.md
@@ -0,0 +1,106 @@
+---
+tags:
+  - v1
+---
+
+The default help flag (`-h/--help`) is defined as `cli.HelpFlag` and is checked
+by the cli internals in order to print generated help text for the app, command,
+or subcommand, and break execution.
+
+#### Customization
+
+All of the help text generation may be customized, and at multiple levels.  The
+templates are exposed as variables `AppHelpTemplate`, `CommandHelpTemplate`, and
+`SubcommandHelpTemplate` which may be reassigned or augmented, and full override
+is possible by assigning a compatible func to the `cli.HelpPrinter` variable,
+e.g.:
+
+<!-- {
+  "output": "Ha HA.  I pwnd the help!!1"
+} -->
+``` go
+package main
+
+import (
+  "fmt"
+  "log"
+  "io"
+  "os"
+
+  "github.com/urfave/cli"
+)
+
+func main() {
+  // EXAMPLE: Append to an existing template
+  cli.AppHelpTemplate = fmt.Sprintf(`%s
+
+WEBSITE: http://awesometown.example.com
+
+SUPPORT: support@awesometown.example.com
+
+`, cli.AppHelpTemplate)
+
+  // EXAMPLE: Override a template
+  cli.AppHelpTemplate = `NAME:
+   {{.Name}} - {{.Usage}}
+USAGE:
+   {{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}
+   {{if len .Authors}}
+AUTHOR:
+   {{range .Authors}}{{ . }}{{end}}
+   {{end}}{{if .Commands}}
+COMMANDS:
+{{range .Commands}}{{if not .HideHelp}}   {{join .Names ", "}}{{ "\t"}}{{.Usage}}{{ "\n" }}{{end}}{{end}}{{end}}{{if .VisibleFlags}}
+GLOBAL OPTIONS:
+   {{range .VisibleFlags}}{{.}}
+   {{end}}{{end}}{{if .Copyright }}
+COPYRIGHT:
+   {{.Copyright}}
+   {{end}}{{if .Version}}
+VERSION:
+   {{.Version}}
+   {{end}}
+`
+
+  // EXAMPLE: Replace the `HelpPrinter` func
+  cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) {
+    fmt.Println("Ha HA.  I pwnd the help!!1")
+  }
+
+  err := cli.NewApp().Run(os.Args)
+  if err != nil {
+    log.Fatal(err)
+  }
+}
+```
+
+The default flag may be customized to something other than `-h/--help` by
+setting `cli.HelpFlag`, e.g.:
+
+<!-- {
+  "args": ["&#45;&#45halp"],
+  "output": "haaaaalp.*HALP"
+} -->
+``` go
+package main
+
+import (
+  "log"
+  "os"
+
+  "github.com/urfave/cli"
+)
+
+func main() {
+  cli.HelpFlag = cli.BoolFlag{
+    Name: "halp, haaaaalp",
+    Usage: "HALP",
+    EnvVar: "SHOW_HALP,HALPPLZ",
+  }
+
+  err := cli.NewApp().Run(os.Args)
+  if err != nil {
+    log.Fatal(err)
+  }
+}
+```
diff --git a/docs/v1/examples/greet.md b/docs/v1/examples/greet.md
new file mode 100644
index 0000000..7314893
--- /dev/null
+++ b/docs/v1/examples/greet.md
@@ -0,0 +1,74 @@
+---
+tags:
+  - v1
+---
+
+Being a programmer can be a lonely job. Thankfully by the power of automation
+that is not the case! Let's create a greeter app to fend off our demons of
+loneliness!
+
+Start by creating a directory named `greet`, and within it, add a file,
+`greet.go` with the following code in it:
+
+<!-- {
+  "output": "Hello friend!"
+} -->
+``` go
+package main
+
+import (
+  "fmt"
+  "log"
+  "os"
+
+  "github.com/urfave/cli"
+)
+
+func main() {
+  app := cli.NewApp()
+  app.Name = "greet"
+  app.Usage = "fight the loneliness!"
+  app.Action = func(c *cli.Context) error {
+    fmt.Println("Hello friend!")
+    return nil
+  }
+
+  err := app.Run(os.Args)
+  if err != nil {
+    log.Fatal(err)
+  }
+}
+```
+
+Install our command to the `$GOPATH/bin` directory:
+
+```
+$ go install
+```
+
+Finally run our new command:
+
+```
+$ greet
+Hello friend!
+```
+
+cli also generates neat help text:
+
+```
+$ greet help
+NAME:
+    greet - fight the loneliness!
+
+USAGE:
+    greet [global options] command [command options] [arguments...]
+
+VERSION:
+    0.0.0
+
+COMMANDS:
+    help, h  Shows a list of commands or help for one command
+
+GLOBAL OPTIONS
+    --version Shows version information
+```
diff --git a/docs/v1/examples/subcommands-categories.md b/docs/v1/examples/subcommands-categories.md
new file mode 100644
index 0000000..27c2bd1
--- /dev/null
+++ b/docs/v1/examples/subcommands-categories.md
@@ -0,0 +1,55 @@
+---
+tags:
+  - v1
+---
+
+For additional organization in apps that have many subcommands, you can
+associate a category for each command to group them together in the help
+output.
+
+E.g.
+
+```go
+package main
+
+import (
+  "log"
+  "os"
+
+  "github.com/urfave/cli"
+)
+
+func main() {
+  app := cli.NewApp()
+
+  app.Commands = []cli.Command{
+    {
+      Name: "noop",
+    },
+    {
+      Name:     "add",
+      Category: "Template actions",
+    },
+    {
+      Name:     "remove",
+      Category: "Template actions",
+    },
+  }
+
+  err := app.Run(os.Args)
+  if err != nil {
+    log.Fatal(err)
+  }
+}
+```
+
+Will include:
+
+```
+COMMANDS:
+  noop
+
+  Template actions:
+    add
+    remove
+```
diff --git a/docs/v1/examples/subcommands.md b/docs/v1/examples/subcommands.md
new file mode 100644
index 0000000..e1be306
--- /dev/null
+++ b/docs/v1/examples/subcommands.md
@@ -0,0 +1,75 @@
+---
+tags:
+  - v1
+---
+
+Subcommands can be defined for a more git-like command line app.
+
+<!-- {
+  "args": ["template", "add"],
+  "output": "new task template: .+"
+} -->
+```go
+package main
+
+import (
+  "fmt"
+  "log"
+  "os"
+
+  "github.com/urfave/cli"
+)
+
+func main() {
+  app := cli.NewApp()
+
+  app.Commands = []cli.Command{
+    {
+      Name:    "add",
+      Aliases: []string{"a"},
+      Usage:   "add a task to the list",
+      Action:  func(c *cli.Context) error {
+        fmt.Println("added task: ", c.Args().First())
+        return nil
+      },
+    },
+    {
+      Name:    "complete",
+      Aliases: []string{"c"},
+      Usage:   "complete a task on the list",
+      Action:  func(c *cli.Context) error {
+        fmt.Println("completed task: ", c.Args().First())
+        return nil
+      },
+    },
+    {
+      Name:        "template",
+      Aliases:     []string{"t"},
+      Usage:       "options for task templates",
+      Subcommands: []cli.Command{
+        {
+          Name:  "add",
+          Usage: "add a new template",
+          Action: func(c *cli.Context) error {
+            fmt.Println("new task template: ", c.Args().First())
+            return nil
+          },
+        },
+        {
+          Name:  "remove",
+          Usage: "remove an existing template",
+          Action: func(c *cli.Context) error {
+            fmt.Println("removed task template: ", c.Args().First())
+            return nil
+          },
+        },
+      },
+    },
+  }
+
+  err := app.Run(os.Args)
+  if err != nil {
+    log.Fatal(err)
+  }
+}
+```
diff --git a/docs/v1/examples/version-flag.md b/docs/v1/examples/version-flag.md
new file mode 100644
index 0000000..dc089a1
--- /dev/null
+++ b/docs/v1/examples/version-flag.md
@@ -0,0 +1,363 @@
+---
+tags:
+  - v1
+---
+
+The default version flag (`-v/--version`) is defined as `cli.VersionFlag`, which
+is checked by the cli internals in order to print the `App.Version` via
+`cli.VersionPrinter` and break execution.
+
+#### Customization
+
+The default flag may be customized to something other than `-v/--version` by
+setting `cli.VersionFlag`, e.g.:
+
+<!-- {
+  "args": ["&#45;&#45print-version"],
+  "output": "partay version 19\\.99\\.0"
+} -->
+``` go
+package main
+
+import (
+  "log"
+  "os"
+
+  "github.com/urfave/cli"
+)
+
+func main() {
+  cli.VersionFlag = cli.BoolFlag{
+    Name: "print-version, V",
+    Usage: "print only the version",
+  }
+
+  app := cli.NewApp()
+  app.Name = "partay"
+  app.Version = "19.99.0"
+  err := app.Run(os.Args)
+  if err != nil {
+    log.Fatal(err)
+  }
+}
+```
+
+Alternatively, the version printer at `cli.VersionPrinter` may be overridden, e.g.:
+
+<!-- {
+  "args": ["&#45;&#45version"],
+  "output": "version=19\\.99\\.0 revision=fafafaf"
+} -->
+``` go
+package main
+
+import (
+  "fmt"
+  "log"
+  "os"
+
+  "github.com/urfave/cli"
+)
+
+var (
+  Revision = "fafafaf"
+)
+
+func main() {
+  cli.VersionPrinter = func(c *cli.Context) {
+    fmt.Printf("version=%s revision=%s\n", c.App.Version, Revision)
+  }
+
+  app := cli.NewApp()
+  app.Name = "partay"
+  app.Version = "19.99.0"
+  err := app.Run(os.Args)
+  if err != nil {
+    log.Fatal(err)
+  }
+}
+```
+
+#### Full API Example
+
+**Notice**: This is a contrived (functioning) example meant strictly for API
+demonstration purposes.  Use of one's imagination is encouraged.
+
+<!-- {
+  "output": "made it!\nPhew!"
+} -->
+``` go
+package main
+
+import (
+  "errors"
+  "flag"
+  "fmt"
+  "io"
+  "io/ioutil"
+  "os"
+  "time"
+
+  "github.com/urfave/cli"
+)
+
+func init() {
+  cli.AppHelpTemplate += "\nCUSTOMIZED: you bet ur muffins\n"
+  cli.CommandHelpTemplate += "\nYMMV\n"
+  cli.SubcommandHelpTemplate += "\nor something\n"
+
+  cli.HelpFlag = cli.BoolFlag{Name: "halp"}
+  cli.BashCompletionFlag = cli.BoolFlag{Name: "compgen", Hidden: true}
+  cli.VersionFlag = cli.BoolFlag{Name: "print-version, V"}
+
+  cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) {
+    fmt.Fprintf(w, "best of luck to you\n")
+  }
+  cli.VersionPrinter = func(c *cli.Context) {
+    fmt.Fprintf(c.App.Writer, "version=%s\n", c.App.Version)
+  }
+  cli.OsExiter = func(c int) {
+    fmt.Fprintf(cli.ErrWriter, "refusing to exit %d\n", c)
+  }
+  cli.ErrWriter = ioutil.Discard
+  cli.FlagStringer = func(fl cli.Flag) string {
+    return fmt.Sprintf("\t\t%s", fl.GetName())
+  }
+}
+
+type hexWriter struct{}
+
+func (w *hexWriter) Write(p []byte) (int, error) {
+  for _, b := range p {
+    fmt.Printf("%x", b)
+  }
+  fmt.Printf("\n")
+
+  return len(p), nil
+}
+
+type genericType struct{
+  s string
+}
+
+func (g *genericType) Set(value string) error {
+  g.s = value
+  return nil
+}
+
+func (g *genericType) String() string {
+  return g.s
+}
+
+func main() {
+  app := cli.NewApp()
+  app.Name = "kənˈtrīv"
+  app.Version = "19.99.0"
+  app.Compiled = time.Now()
+  app.Authors = []cli.Author{
+    cli.Author{
+      Name:  "Example Human",
+      Email: "human@example.com",
+    },
+  }
+  app.Copyright = "(c) 1999 Serious Enterprise"
+  app.HelpName = "contrive"
+  app.Usage = "demonstrate available API"
+  app.UsageText = "contrive - demonstrating the available API"
+  app.ArgsUsage = "[args and such]"
+  app.Commands = []cli.Command{
+    cli.Command{
+      Name:        "doo",
+      Aliases:     []string{"do"},
+      Category:    "motion",
+      Usage:       "do the doo",
+      UsageText:   "doo - does the dooing",
+      Description: "no really, there is a lot of dooing to be done",
+      ArgsUsage:   "[arrgh]",
+      Flags: []cli.Flag{
+        cli.BoolFlag{Name: "forever, forevvarr"},
+      },
+      Subcommands: cli.Commands{
+        cli.Command{
+          Name:   "wop",
+          Action: wopAction,
+        },
+      },
+      SkipFlagParsing: false,
+      HideHelp:        false,
+      Hidden:          false,
+      HelpName:        "doo!",
+      BashComplete: func(c *cli.Context) {
+        fmt.Fprintf(c.App.Writer, "--better\n")
+      },
+      Before: func(c *cli.Context) error {
+        fmt.Fprintf(c.App.Writer, "brace for impact\n")
+        return nil
+      },
+      After: func(c *cli.Context) error {
+        fmt.Fprintf(c.App.Writer, "did we lose anyone?\n")
+        return nil
+      },
+      Action: func(c *cli.Context) error {
+        c.Command.FullName()
+        c.Command.HasName("wop")
+        c.Command.Names()
+        c.Command.VisibleFlags()
+        fmt.Fprintf(c.App.Writer, "dodododododoodododddooooododododooo\n")
+        if c.Bool("forever") {
+          c.Command.Run(c)
+        }
+        return nil
+      },
+      OnUsageError: func(c *cli.Context, err error, isSubcommand bool) error {
+        fmt.Fprintf(c.App.Writer, "for shame\n")
+        return err
+      },
+    },
+  }
+  app.Flags = []cli.Flag{
+    cli.BoolFlag{Name: "fancy"},
+    cli.BoolTFlag{Name: "fancier"},
+    cli.DurationFlag{Name: "howlong, H", Value: time.Second * 3},
+    cli.Float64Flag{Name: "howmuch"},
+    cli.GenericFlag{Name: "wat", Value: &genericType{}},
+    cli.Int64Flag{Name: "longdistance"},
+    cli.Int64SliceFlag{Name: "intervals"},
+    cli.IntFlag{Name: "distance"},
+    cli.IntSliceFlag{Name: "times"},
+    cli.StringFlag{Name: "dance-move, d"},
+    cli.StringSliceFlag{Name: "names, N"},
+    cli.UintFlag{Name: "age"},
+    cli.Uint64Flag{Name: "bigage"},
+  }
+  app.EnableBashCompletion = true
+  app.UseShortOptionHandling = true
+  app.HideHelp = false
+  app.HideVersion = false
+  app.BashComplete = func(c *cli.Context) {
+    fmt.Fprintf(c.App.Writer, "lipstick\nkiss\nme\nlipstick\nringo\n")
+  }
+  app.Before = func(c *cli.Context) error {
+    fmt.Fprintf(c.App.Writer, "HEEEERE GOES\n")
+    return nil
+  }
+  app.After = func(c *cli.Context) error {
+    fmt.Fprintf(c.App.Writer, "Phew!\n")
+    return nil
+  }
+  app.CommandNotFound = func(c *cli.Context, command string) {
+    fmt.Fprintf(c.App.Writer, "Thar be no %q here.\n", command)
+  }
+  app.OnUsageError = func(c *cli.Context, err error, isSubcommand bool) error {
+    if isSubcommand {
+      return err
+    }
+
+    fmt.Fprintf(c.App.Writer, "WRONG: %#v\n", err)
+    return nil
+  }
+  app.Action = func(c *cli.Context) error {
+    cli.DefaultAppComplete(c)
+    cli.HandleExitCoder(errors.New("not an exit coder, though"))
+    cli.ShowAppHelp(c)
+    cli.ShowCommandCompletions(c, "nope")
+    cli.ShowCommandHelp(c, "also-nope")
+    cli.ShowCompletions(c)
+    cli.ShowSubcommandHelp(c)
+    cli.ShowVersion(c)
+
+    categories := c.App.Categories()
+    categories.AddCommand("sounds", cli.Command{
+      Name: "bloop",
+    })
+
+    for _, category := range c.App.Categories() {
+      fmt.Fprintf(c.App.Writer, "%s\n", category.Name)
+      fmt.Fprintf(c.App.Writer, "%#v\n", category.Commands)
+      fmt.Fprintf(c.App.Writer, "%#v\n", category.VisibleCommands())
+    }
+
+    fmt.Printf("%#v\n", c.App.Command("doo"))
+    if c.Bool("infinite") {
+      c.App.Run([]string{"app", "doo", "wop"})
+    }
+
+    if c.Bool("forevar") {
+      c.App.RunAsSubcommand(c)
+    }
+    c.App.Setup()
+    fmt.Printf("%#v\n", c.App.VisibleCategories())
+    fmt.Printf("%#v\n", c.App.VisibleCommands())
+    fmt.Printf("%#v\n", c.App.VisibleFlags())
+
+    fmt.Printf("%#v\n", c.Args().First())
+    if len(c.Args()) > 0 {
+      fmt.Printf("%#v\n", c.Args()[1])
+    }
+    fmt.Printf("%#v\n", c.Args().Present())
+    fmt.Printf("%#v\n", c.Args().Tail())
+
+    set := flag.NewFlagSet("contrive", 0)
+    nc := cli.NewContext(c.App, set, c)
+
+    fmt.Printf("%#v\n", nc.Args())
+    fmt.Printf("%#v\n", nc.Bool("nope"))
+    fmt.Printf("%#v\n", nc.BoolT("nerp"))
+    fmt.Printf("%#v\n", nc.Duration("howlong"))
+    fmt.Printf("%#v\n", nc.Float64("hay"))
+    fmt.Printf("%#v\n", nc.Generic("bloop"))
+    fmt.Printf("%#v\n", nc.Int64("bonk"))
+    fmt.Printf("%#v\n", nc.Int64Slice("burnks"))
+    fmt.Printf("%#v\n", nc.Int("bips"))
+    fmt.Printf("%#v\n", nc.IntSlice("blups"))
+    fmt.Printf("%#v\n", nc.String("snurt"))
+    fmt.Printf("%#v\n", nc.StringSlice("snurkles"))
+    fmt.Printf("%#v\n", nc.Uint("flub"))
+    fmt.Printf("%#v\n", nc.Uint64("florb"))
+    fmt.Printf("%#v\n", nc.GlobalBool("global-nope"))
+    fmt.Printf("%#v\n", nc.GlobalBoolT("global-nerp"))
+    fmt.Printf("%#v\n", nc.GlobalDuration("global-howlong"))
+    fmt.Printf("%#v\n", nc.GlobalFloat64("global-hay"))
+    fmt.Printf("%#v\n", nc.GlobalGeneric("global-bloop"))
+    fmt.Printf("%#v\n", nc.GlobalInt("global-bips"))
+    fmt.Printf("%#v\n", nc.GlobalIntSlice("global-blups"))
+    fmt.Printf("%#v\n", nc.GlobalString("global-snurt"))
+    fmt.Printf("%#v\n", nc.GlobalStringSlice("global-snurkles"))
+
+    fmt.Printf("%#v\n", nc.FlagNames())
+    fmt.Printf("%#v\n", nc.GlobalFlagNames())
+    fmt.Printf("%#v\n", nc.GlobalIsSet("wat"))
+    fmt.Printf("%#v\n", nc.GlobalSet("wat", "nope"))
+    fmt.Printf("%#v\n", nc.NArg())
+    fmt.Printf("%#v\n", nc.NumFlags())
+    fmt.Printf("%#v\n", nc.Parent())
+
+    nc.Set("wat", "also-nope")
+
+    ec := cli.NewExitError("ohwell", 86)
+    fmt.Fprintf(c.App.Writer, "%d", ec.ExitCode())
+    fmt.Printf("made it!\n")
+    return nil
+  }
+
+  if os.Getenv("HEXY") != "" {
+    app.Writer = &hexWriter{}
+    app.ErrWriter = &hexWriter{}
+  }
+
+  app.Metadata = map[string]interface{}{
+    "layers":     "many",
+    "explicable": false,
+    "whatever-values": 19.99,
+  }
+
+
+  // ignore error so we don't exit non-zero and break gfmrun README example tests
+  _ = app.Run(os.Args)
+}
+
+func wopAction(c *cli.Context) error {
+  fmt.Fprintf(c.App.Writer, ":wave: over here, eh\n")
+  return nil
+}
+```
diff --git a/docs/v1/getting-started.md b/docs/v1/getting-started.md
new file mode 100644
index 0000000..b98f011
--- /dev/null
+++ b/docs/v1/getting-started.md
@@ -0,0 +1,65 @@
+---
+tags:
+  - v1
+---
+
+One of the philosophies behind cli is that an API should be playful and full of
+discovery. So a cli app can be as little as one line of code in `main()`.
+
+<!-- {
+  "args": ["&#45;&#45;help"],
+  "output": "A new cli application"
+} -->
+``` go
+package main
+
+import (
+  "log"
+  "os"
+
+  "github.com/urfave/cli"
+)
+
+func main() {
+  err := cli.NewApp().Run(os.Args)
+  if err != nil {
+    log.Fatal(err)
+  }
+}
+```
+
+This app will run and show help text, but is not very useful. Let's give an
+action to execute and some help documentation:
+
+<!-- {
+  "output": "boom! I say!"
+} -->
+``` go
+package main
+
+import (
+  "fmt"
+  "log"
+  "os"
+
+  "github.com/urfave/cli"
+)
+
+func main() {
+  app := cli.NewApp()
+  app.Name = "boom"
+  app.Usage = "make an explosive entrance"
+  app.Action = func(c *cli.Context) error {
+    fmt.Println("boom! I say!")
+    return nil
+  }
+
+  err := app.Run(os.Args)
+  if err != nil {
+    log.Fatal(err)
+  }
+}
+```
+
+Running this already gives you a ton of functionality, plus support for things
+like subcommands and flags, which are covered below.
diff --git a/docs/v1/index.md b/docs/v1/index.md
deleted file mode 120000
index 9d0493a..0000000
--- a/docs/v1/index.md
+++ /dev/null
@@ -1,1 +0,0 @@
-manual.md
\ No newline at end of file
diff --git a/docs/v1/manual.md b/docs/v1/manual.md
deleted file mode 100644
index 6f568b7..0000000
--- a/docs/v1/manual.md
+++ /dev/null
@@ -1,1455 +0,0 @@
-# v1 guide
-
-## Getting Started
-
-One of the philosophies behind cli is that an API should be playful and full of
-discovery. So a cli app can be as little as one line of code in `main()`.
-
-<!-- {
-  "args": ["&#45;&#45;help"],
-  "output": "A new cli application"
-} -->
-``` go
-package main
-
-import (
-  "log"
-  "os"
-
-  "github.com/urfave/cli"
-)
-
-func main() {
-  err := cli.NewApp().Run(os.Args)
-  if err != nil {
-    log.Fatal(err)
-  }
-}
-```
-
-This app will run and show help text, but is not very useful. Let's give an
-action to execute and some help documentation:
-
-<!-- {
-  "output": "boom! I say!"
-} -->
-``` go
-package main
-
-import (
-  "fmt"
-  "log"
-  "os"
-
-  "github.com/urfave/cli"
-)
-
-func main() {
-  app := cli.NewApp()
-  app.Name = "boom"
-  app.Usage = "make an explosive entrance"
-  app.Action = func(c *cli.Context) error {
-    fmt.Println("boom! I say!")
-    return nil
-  }
-
-  err := app.Run(os.Args)
-  if err != nil {
-    log.Fatal(err)
-  }
-}
-```
-
-Running this already gives you a ton of functionality, plus support for things
-like subcommands and flags, which are covered below.
-
-## Examples
-
-Being a programmer can be a lonely job. Thankfully by the power of automation
-that is not the case! Let's create a greeter app to fend off our demons of
-loneliness!
-
-Start by creating a directory named `greet`, and within it, add a file,
-`greet.go` with the following code in it:
-
-<!-- {
-  "output": "Hello friend!"
-} -->
-``` go
-package main
-
-import (
-  "fmt"
-  "log"
-  "os"
-
-  "github.com/urfave/cli"
-)
-
-func main() {
-  app := cli.NewApp()
-  app.Name = "greet"
-  app.Usage = "fight the loneliness!"
-  app.Action = func(c *cli.Context) error {
-    fmt.Println("Hello friend!")
-    return nil
-  }
-
-  err := app.Run(os.Args)
-  if err != nil {
-    log.Fatal(err)
-  }
-}
-```
-
-Install our command to the `$GOPATH/bin` directory:
-
-```
-$ go install
-```
-
-Finally run our new command:
-
-```
-$ greet
-Hello friend!
-```
-
-cli also generates neat help text:
-
-```
-$ greet help
-NAME:
-    greet - fight the loneliness!
-
-USAGE:
-    greet [global options] command [command options] [arguments...]
-
-VERSION:
-    0.0.0
-
-COMMANDS:
-    help, h  Shows a list of commands or help for one command
-
-GLOBAL OPTIONS
-    --version Shows version information
-```
-
-### Arguments
-
-You can lookup arguments by calling the `Args` function on `cli.Context`, e.g.:
-
-<!-- {
-  "output": "Hello \""
-} -->
-``` go
-package main
-
-import (
-  "fmt"
-  "log"
-  "os"
-
-  "github.com/urfave/cli"
-)
-
-func main() {
-  app := cli.NewApp()
-
-  app.Action = func(c *cli.Context) error {
-    fmt.Printf("Hello %q", c.Args().Get(0))
-    return nil
-  }
-
-  err := app.Run(os.Args)
-  if err != nil {
-    log.Fatal(err)
-  }
-}
-```
-
-### Flags
-
-Setting and querying flags is simple.
-
-<!-- {
-  "output": "Hello Nefertiti"
-} -->
-``` go
-package main
-
-import (
-  "fmt"
-  "log"
-  "os"
-
-  "github.com/urfave/cli"
-)
-
-func main() {
-  app := cli.NewApp()
-
-  app.Flags = []cli.Flag {
-    cli.StringFlag{
-      Name: "lang",
-      Value: "english",
-      Usage: "language for the greeting",
-    },
-  }
-
-  app.Action = func(c *cli.Context) error {
-    name := "Nefertiti"
-    if c.NArg() > 0 {
-      name = c.Args().Get(0)
-    }
-    if c.String("lang") == "spanish" {
-      fmt.Println("Hola", name)
-    } else {
-      fmt.Println("Hello", name)
-    }
-    return nil
-  }
-
-  err := app.Run(os.Args)
-  if err != nil {
-    log.Fatal(err)
-  }
-}
-```
-
-You can also set a destination variable for a flag, to which the content will be
-scanned.
-
-<!-- {
-  "output": "Hello someone"
-} -->
-``` go
-package main
-
-import (
-  "log"
-  "os"
-  "fmt"
-
-  "github.com/urfave/cli"
-)
-
-func main() {
-  var language string
-
-  app := cli.NewApp()
-
-  app.Flags = []cli.Flag {
-    cli.StringFlag{
-      Name:        "lang",
-      Value:       "english",
-      Usage:       "language for the greeting",
-      Destination: &language,
-    },
-  }
-
-  app.Action = func(c *cli.Context) error {
-    name := "someone"
-    if c.NArg() > 0 {
-      name = c.Args()[0]
-    }
-    if language == "spanish" {
-      fmt.Println("Hola", name)
-    } else {
-      fmt.Println("Hello", name)
-    }
-    return nil
-  }
-
-  err := app.Run(os.Args)
-  if err != nil {
-    log.Fatal(err)
-  }
-}
-```
-
-See full list of flags at http://godoc.org/github.com/urfave/cli
-
-#### Placeholder Values
-
-Sometimes it's useful to specify a flag's value within the usage string itself.
-Such placeholders are indicated with back quotes.
-
-For example this:
-
-<!-- {
-  "args": ["&#45;&#45;help"],
-  "output": "&#45;&#45;config FILE, &#45;c FILE"
-} -->
-```go
-package main
-
-import (
-  "log"
-  "os"
-
-  "github.com/urfave/cli"
-)
-
-func main() {
-  app := cli.NewApp()
-
-  app.Flags = []cli.Flag{
-    cli.StringFlag{
-      Name:  "config, c",
-      Usage: "Load configuration from `FILE`",
-    },
-  }
-
-  err := app.Run(os.Args)
-  if err != nil {
-    log.Fatal(err)
-  }
-}
-```
-
-Will result in help output like:
-
-```
---config FILE, -c FILE   Load configuration from FILE
-```
-
-Note that only the first placeholder is used. Subsequent back-quoted words will
-be left as-is.
-
-#### Alternate Names
-
-You can set alternate (or short) names for flags by providing a comma-delimited
-list for the `Name`. e.g.
-
-<!-- {
-  "args": ["&#45;&#45;help"],
-  "output": "&#45;&#45;lang value, &#45;l value.*language for the greeting.*default: \"english\""
-} -->
-``` go
-package main
-
-import (
-  "log"
-  "os"
-
-  "github.com/urfave/cli"
-)
-
-func main() {
-  app := cli.NewApp()
-
-  app.Flags = []cli.Flag {
-    cli.StringFlag{
-      Name: "lang, l",
-      Value: "english",
-      Usage: "language for the greeting",
-    },
-  }
-
-  err := app.Run(os.Args)
-  if err != nil {
-    log.Fatal(err)
-  }
-}
-```
-
-That flag can then be set with `--lang spanish` or `-l spanish`. Note that
-giving two different forms of the same flag in the same command invocation is an
-error.
-
-#### Ordering
-
-Flags for the application and commands are shown in the order they are defined.
-However, it's possible to sort them from outside this library by using `FlagsByName`
-or `CommandsByName` with `sort`.
-
-For example this:
-
-<!-- {
-  "args": ["&#45;&#45;help"],
-  "output": "add a task to the list\n.*complete a task on the list\n.*\n\n.*\n.*Load configuration from FILE\n.*Language for the greeting.*"
-} -->
-``` go
-package main
-
-import (
-  "log"
-  "os"
-  "sort"
-
-  "github.com/urfave/cli"
-)
-
-func main() {
-  app := cli.NewApp()
-
-  app.Flags = []cli.Flag {
-    cli.StringFlag{
-      Name: "lang, l",
-      Value: "english",
-      Usage: "Language for the greeting",
-    },
-    cli.StringFlag{
-      Name: "config, c",
-      Usage: "Load configuration from `FILE`",
-    },
-  }
-
-  app.Commands = []cli.Command{
-    {
-      Name:    "complete",
-      Aliases: []string{"c"},
-      Usage:   "complete a task on the list",
-      Action:  func(c *cli.Context) error {
-        return nil
-      },
-    },
-    {
-      Name:    "add",
-      Aliases: []string{"a"},
-      Usage:   "add a task to the list",
-      Action:  func(c *cli.Context) error {
-        return nil
-      },
-    },
-  }
-
-  sort.Sort(cli.FlagsByName(app.Flags))
-  sort.Sort(cli.CommandsByName(app.Commands))
-
-  err := app.Run(os.Args)
-  if err != nil {
-    log.Fatal(err)
-  }
-}
-```
-
-Will result in help output like:
-
-```
---config FILE, -c FILE  Load configuration from FILE
---lang value, -l value  Language for the greeting (default: "english")
-```
-
-#### Values from the Environment
-
-You can also have the default value set from the environment via `EnvVar`.  e.g.
-
-<!-- {
-  "args": ["&#45;&#45;help"],
-  "output": "language for the greeting.*APP_LANG"
-} -->
-``` go
-package main
-
-import (
-  "log"
-  "os"
-
-  "github.com/urfave/cli"
-)
-
-func main() {
-  app := cli.NewApp()
-
-  app.Flags = []cli.Flag {
-    cli.StringFlag{
-      Name: "lang, l",
-      Value: "english",
-      Usage: "language for the greeting",
-      EnvVar: "APP_LANG",
-    },
-  }
-
-  err := app.Run(os.Args)
-  if err != nil {
-    log.Fatal(err)
-  }
-}
-```
-
-The `EnvVar` may also be given as a comma-delimited "cascade", where the first
-environment variable that resolves is used as the default.
-
-<!-- {
-  "args": ["&#45;&#45;help"],
-  "output": "language for the greeting.*LEGACY_COMPAT_LANG.*APP_LANG.*LANG"
-} -->
-``` go
-package main
-
-import (
-  "log"
-  "os"
-
-  "github.com/urfave/cli"
-)
-
-func main() {
-  app := cli.NewApp()
-
-  app.Flags = []cli.Flag {
-    cli.StringFlag{
-      Name: "lang, l",
-      Value: "english",
-      Usage: "language for the greeting",
-      EnvVar: "LEGACY_COMPAT_LANG,APP_LANG,LANG",
-    },
-  }
-
-  err := app.Run(os.Args)
-  if err != nil {
-    log.Fatal(err)
-  }
-}
-```
-
-#### Values from files
-
-You can also have the default value set from file via `FilePath`.  e.g.
-
-<!-- {
-  "args": ["&#45;&#45;help"],
-  "output": "password for the mysql database"
-} -->
-``` go
-package main
-
-import (
-  "log"
-  "os"
-
-  "github.com/urfave/cli"
-)
-
-func main() {
-  app := cli.NewApp()
-
-  app.Flags = []cli.Flag {
-    cli.StringFlag{
-      Name: "password, p",
-      Usage: "password for the mysql database",
-      FilePath: "/etc/mysql/password",
-    },
-  }
-
-  err := app.Run(os.Args)
-  if err != nil {
-    log.Fatal(err)
-  }
-}
-```
-
-Note that default values set from file (e.g. `FilePath`) take precedence over
-default values set from the environment (e.g. `EnvVar`).
-
-#### Values from alternate input sources (YAML, TOML, and others)
-
-There is a separate package altsrc that adds support for getting flag values
-from other file input sources.
-
-Currently supported input source formats:
-* YAML
-* JSON
-* TOML
-
-In order to get values for a flag from an alternate input source the following
-code would be added to wrap an existing cli.Flag like below:
-
-``` go
-  altsrc.NewIntFlag(cli.IntFlag{Name: "test"})
-```
-
-Initialization must also occur for these flags. Below is an example initializing
-getting data from a yaml file below.
-
-``` go
-  command.Before = altsrc.InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
-```
-
-The code above will use the "load" string as a flag name to get the file name of
-a yaml file from the cli.Context.  It will then use that file name to initialize
-the yaml input source for any flags that are defined on that command.  As a note
-the "load" flag used would also have to be defined on the command flags in order
-for this code snippet to work.
-
-Currently only YAML, JSON, and TOML files are supported but developers can add support
-for other input sources by implementing the altsrc.InputSourceContext for their
-given sources.
-
-Here is a more complete sample of a command using YAML support:
-
-<!-- {
-  "args": ["&#45;&#45;help"],
-  "output": "&#45&#45;test value.*default: 0"
-} -->
-``` go
-package notmain
-
-import (
-  "fmt"
-  "log"
-  "os"
-
-  "github.com/urfave/cli"
-  "github.com/urfave/cli/altsrc"
-)
-
-func main() {
-  app := cli.NewApp()
-
-  flags := []cli.Flag{
-    altsrc.NewIntFlag(cli.IntFlag{Name: "test"}),
-    cli.StringFlag{Name: "load"},
-  }
-
-  app.Action = func(c *cli.Context) error {
-    fmt.Println("yaml ist rad")
-    return nil
-  }
-
-  app.Before = altsrc.InitInputSourceWithContext(flags, altsrc.NewYamlSourceFromFlagFunc("load"))
-  app.Flags = flags
-
-  err := app.Run(os.Args)
-  if err != nil {
-    log.Fatal(err)
-  }
-}
-```
-
-#### Precedence
-
-The precedence for flag value sources is as follows (highest to lowest):
-
-0. Command line flag value from user
-0. Environment variable (if specified)
-0. Configuration file (if specified)
-0. Default defined on the flag
-
-### Subcommands
-
-Subcommands can be defined for a more git-like command line app.
-
-<!-- {
-  "args": ["template", "add"],
-  "output": "new task template: .+"
-} -->
-```go
-package main
-
-import (
-  "fmt"
-  "log"
-  "os"
-
-  "github.com/urfave/cli"
-)
-
-func main() {
-  app := cli.NewApp()
-
-  app.Commands = []cli.Command{
-    {
-      Name:    "add",
-      Aliases: []string{"a"},
-      Usage:   "add a task to the list",
-      Action:  func(c *cli.Context) error {
-        fmt.Println("added task: ", c.Args().First())
-        return nil
-      },
-    },
-    {
-      Name:    "complete",
-      Aliases: []string{"c"},
-      Usage:   "complete a task on the list",
-      Action:  func(c *cli.Context) error {
-        fmt.Println("completed task: ", c.Args().First())
-        return nil
-      },
-    },
-    {
-      Name:        "template",
-      Aliases:     []string{"t"},
-      Usage:       "options for task templates",
-      Subcommands: []cli.Command{
-        {
-          Name:  "add",
-          Usage: "add a new template",
-          Action: func(c *cli.Context) error {
-            fmt.Println("new task template: ", c.Args().First())
-            return nil
-          },
-        },
-        {
-          Name:  "remove",
-          Usage: "remove an existing template",
-          Action: func(c *cli.Context) error {
-            fmt.Println("removed task template: ", c.Args().First())
-            return nil
-          },
-        },
-      },
-    },
-  }
-
-  err := app.Run(os.Args)
-  if err != nil {
-    log.Fatal(err)
-  }
-}
-```
-
-### Subcommands categories
-
-For additional organization in apps that have many subcommands, you can
-associate a category for each command to group them together in the help
-output.
-
-E.g.
-
-```go
-package main
-
-import (
-  "log"
-  "os"
-
-  "github.com/urfave/cli"
-)
-
-func main() {
-  app := cli.NewApp()
-
-  app.Commands = []cli.Command{
-    {
-      Name: "noop",
-    },
-    {
-      Name:     "add",
-      Category: "Template actions",
-    },
-    {
-      Name:     "remove",
-      Category: "Template actions",
-    },
-  }
-
-  err := app.Run(os.Args)
-  if err != nil {
-    log.Fatal(err)
-  }
-}
-```
-
-Will include:
-
-```
-COMMANDS:
-  noop
-
-  Template actions:
-    add
-    remove
-```
-
-### Exit code
-
-Calling `App.Run` will not automatically call `os.Exit`, which means that by
-default the exit code will "fall through" to being `0`.  An explicit exit code
-may be set by returning a non-nil error that fulfills `cli.ExitCoder`, *or* a
-`cli.MultiError` that includes an error that fulfills `cli.ExitCoder`, e.g.:
-<!-- {
-  "error": "Ginger croutons are not in the soup"
-} -->
-``` go
-package main
-
-import (
-  "log"
-  "os"
-
-  "github.com/urfave/cli"
-)
-
-func main() {
-  app := cli.NewApp()
-  app.Flags = []cli.Flag{
-    cli.BoolFlag{
-      Name:  "ginger-crouton",
-      Usage: "Add ginger croutons to the soup",
-    },
-  }
-  app.Action = func(ctx *cli.Context) error {
-    if !ctx.Bool("ginger-crouton") {
-      return cli.NewExitError("Ginger croutons are not in the soup", 86)
-    }
-    return nil
-  }
-
-  err := app.Run(os.Args)
-  if err != nil {
-    log.Fatal(err)
-  }
-}
-```
-
-### Combining short options
-
-Traditional use of options using their shortnames look like this:
-
-```
-$ cmd -s -o -m "Some message"
-```
-
-Suppose you want users to be able to combine options with their shortnames. This
-can be done using the `UseShortOptionHandling` bool in your app configuration,
-or for individual commands by attaching it to the command configuration. For
-example:
-
-<!-- {
-  "args": ["short", "&#45;som", "Some message"],
-  "output": "serve: true\noption: true\nmessage: Some message\n"
-} -->
-``` go
-package main
-
-import (
-  "fmt"
-  "log"
-  "os"
-
-  "github.com/urfave/cli"
-)
-
-func main() {
-  app := cli.NewApp()
-  app.UseShortOptionHandling = true
-  app.Commands = []cli.Command{
-    {
-      Name:  "short",
-      Usage: "complete a task on the list",
-      Flags: []cli.Flag{
-        cli.BoolFlag{Name: "serve, s"},
-        cli.BoolFlag{Name: "option, o"},
-        cli.StringFlag{Name: "message, m"},
-      },
-      Action: func(c *cli.Context) error {
-        fmt.Println("serve:", c.Bool("serve"))
-        fmt.Println("option:", c.Bool("option"))
-        fmt.Println("message:", c.String("message"))
-        return nil
-      },
-    },
-  }
-
-  err := app.Run(os.Args)
-  if err != nil {
-    log.Fatal(err)
-  }
-}
-```
-
-If your program has any number of bool flags such as `serve` and `option`, and
-optionally one non-bool flag `message`, with the short options of `-s`, `-o`,
-and `-m` respectively, setting `UseShortOptionHandling` will also support the
-following syntax:
-
-```
-$ cmd -som "Some message"
-```
-
-If you enable `UseShortOptionHandling`, then you must not use any flags that
-have a single leading `-` or this will result in failures. For example,
-`-option` can no longer be used. Flags with two leading dashes (such as
-`--options`) are still valid.
-
-### Bash Completion
-
-You can enable completion commands by setting the `EnableBashCompletion`
-flag on the `App` object.  By default, this setting will only auto-complete to
-show an app's subcommands, but you can write your own completion methods for
-the App or its subcommands.
-
-<!-- {
-  "args": ["complete", "&#45;&#45;generate&#45;bash&#45;completion"],
-  "output": "laundry"
-} -->
-``` go
-package main
-
-import (
-  "fmt"
-  "log"
-  "os"
-
-  "github.com/urfave/cli"
-)
-
-func main() {
-  tasks := []string{"cook", "clean", "laundry", "eat", "sleep", "code"}
-
-  app := cli.NewApp()
-  app.EnableBashCompletion = true
-  app.Commands = []cli.Command{
-    {
-      Name:  "complete",
-      Aliases: []string{"c"},
-      Usage: "complete a task on the list",
-      Action: func(c *cli.Context) error {
-         fmt.Println("completed task: ", c.Args().First())
-         return nil
-      },
-      BashComplete: func(c *cli.Context) {
-        // This will complete if no args are passed
-        if c.NArg() > 0 {
-          return
-        }
-        for _, t := range tasks {
-          fmt.Println(t)
-        }
-      },
-    },
-  }
-
-  err := app.Run(os.Args)
-  if err != nil {
-    log.Fatal(err)
-  }
-}
-```
-
-#### Enabling
-
-Source the `autocomplete/bash_autocomplete` file in your `.bashrc` file while
-setting the `PROG` variable to the name of your program:
-
-`PROG=myprogram source /.../cli/autocomplete/bash_autocomplete`
-
-#### Distribution
-
-Copy `autocomplete/bash_autocomplete` into `/etc/bash_completion.d/` and rename
-it to the name of the program you wish to add autocomplete support for (or
-automatically install it there if you are distributing a package). Don't forget
-to source the file to make it active in the current shell.
-
-```
-sudo cp src/bash_autocomplete /etc/bash_completion.d/<myprogram>
-source /etc/bash_completion.d/<myprogram>
-```
-
-Alternatively, you can just document that users should source the generic
-`autocomplete/bash_autocomplete` in their bash configuration with `$PROG` set
-to the name of their program (as above).
-
-#### Customization
-
-The default bash completion flag (`--generate-bash-completion`) is defined as
-`cli.BashCompletionFlag`, and may be redefined if desired, e.g.:
-
-<!-- {
-  "args": ["&#45;&#45;compgen"],
-  "output": "wat\nhelp\nh"
-} -->
-``` go
-package main
-
-import (
-  "log"
-  "os"
-
-  "github.com/urfave/cli"
-)
-
-func main() {
-  cli.BashCompletionFlag = cli.BoolFlag{
-    Name:   "compgen",
-    Hidden: true,
-  }
-
-  app := cli.NewApp()
-  app.EnableBashCompletion = true
-  app.Commands = []cli.Command{
-    {
-      Name: "wat",
-    },
-  }
-  err := app.Run(os.Args)
-  if err != nil {
-    log.Fatal(err)
-  }
-}
-```
-
-### Generated Help Text
-
-The default help flag (`-h/--help`) is defined as `cli.HelpFlag` and is checked
-by the cli internals in order to print generated help text for the app, command,
-or subcommand, and break execution.
-
-#### Customization
-
-All of the help text generation may be customized, and at multiple levels.  The
-templates are exposed as variables `AppHelpTemplate`, `CommandHelpTemplate`, and
-`SubcommandHelpTemplate` which may be reassigned or augmented, and full override
-is possible by assigning a compatible func to the `cli.HelpPrinter` variable,
-e.g.:
-
-<!-- {
-  "output": "Ha HA.  I pwnd the help!!1"
-} -->
-``` go
-package main
-
-import (
-  "fmt"
-  "log"
-  "io"
-  "os"
-
-  "github.com/urfave/cli"
-)
-
-func main() {
-  // EXAMPLE: Append to an existing template
-  cli.AppHelpTemplate = fmt.Sprintf(`%s
-
-WEBSITE: http://awesometown.example.com
-
-SUPPORT: support@awesometown.example.com
-
-`, cli.AppHelpTemplate)
-
-  // EXAMPLE: Override a template
-  cli.AppHelpTemplate = `NAME:
-   {{.Name}} - {{.Usage}}
-USAGE:
-   {{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}
-   {{if len .Authors}}
-AUTHOR:
-   {{range .Authors}}{{ . }}{{end}}
-   {{end}}{{if .Commands}}
-COMMANDS:
-{{range .Commands}}{{if not .HideHelp}}   {{join .Names ", "}}{{ "\t"}}{{.Usage}}{{ "\n" }}{{end}}{{end}}{{end}}{{if .VisibleFlags}}
-GLOBAL OPTIONS:
-   {{range .VisibleFlags}}{{.}}
-   {{end}}{{end}}{{if .Copyright }}
-COPYRIGHT:
-   {{.Copyright}}
-   {{end}}{{if .Version}}
-VERSION:
-   {{.Version}}
-   {{end}}
-`
-
-  // EXAMPLE: Replace the `HelpPrinter` func
-  cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) {
-    fmt.Println("Ha HA.  I pwnd the help!!1")
-  }
-
-  err := cli.NewApp().Run(os.Args)
-  if err != nil {
-    log.Fatal(err)
-  }
-}
-```
-
-The default flag may be customized to something other than `-h/--help` by
-setting `cli.HelpFlag`, e.g.:
-
-<!-- {
-  "args": ["&#45;&#45halp"],
-  "output": "haaaaalp.*HALP"
-} -->
-``` go
-package main
-
-import (
-  "log"
-  "os"
-
-  "github.com/urfave/cli"
-)
-
-func main() {
-  cli.HelpFlag = cli.BoolFlag{
-    Name: "halp, haaaaalp",
-    Usage: "HALP",
-    EnvVar: "SHOW_HALP,HALPPLZ",
-  }
-
-  err := cli.NewApp().Run(os.Args)
-  if err != nil {
-    log.Fatal(err)
-  }
-}
-```
-
-### Version Flag
-
-The default version flag (`-v/--version`) is defined as `cli.VersionFlag`, which
-is checked by the cli internals in order to print the `App.Version` via
-`cli.VersionPrinter` and break execution.
-
-#### Customization
-
-The default flag may be customized to something other than `-v/--version` by
-setting `cli.VersionFlag`, e.g.:
-
-<!-- {
-  "args": ["&#45;&#45print-version"],
-  "output": "partay version 19\\.99\\.0"
-} -->
-``` go
-package main
-
-import (
-  "log"
-  "os"
-
-  "github.com/urfave/cli"
-)
-
-func main() {
-  cli.VersionFlag = cli.BoolFlag{
-    Name: "print-version, V",
-    Usage: "print only the version",
-  }
-
-  app := cli.NewApp()
-  app.Name = "partay"
-  app.Version = "19.99.0"
-  err := app.Run(os.Args)
-  if err != nil {
-    log.Fatal(err)
-  }
-}
-```
-
-Alternatively, the version printer at `cli.VersionPrinter` may be overridden, e.g.:
-
-<!-- {
-  "args": ["&#45;&#45version"],
-  "output": "version=19\\.99\\.0 revision=fafafaf"
-} -->
-``` go
-package main
-
-import (
-  "fmt"
-  "log"
-  "os"
-
-  "github.com/urfave/cli"
-)
-
-var (
-  Revision = "fafafaf"
-)
-
-func main() {
-  cli.VersionPrinter = func(c *cli.Context) {
-    fmt.Printf("version=%s revision=%s\n", c.App.Version, Revision)
-  }
-
-  app := cli.NewApp()
-  app.Name = "partay"
-  app.Version = "19.99.0"
-  err := app.Run(os.Args)
-  if err != nil {
-    log.Fatal(err)
-  }
-}
-```
-
-#### Full API Example
-
-**Notice**: This is a contrived (functioning) example meant strictly for API
-demonstration purposes.  Use of one's imagination is encouraged.
-
-<!-- {
-  "output": "made it!\nPhew!"
-} -->
-``` go
-package main
-
-import (
-  "errors"
-  "flag"
-  "fmt"
-  "io"
-  "io/ioutil"
-  "os"
-  "time"
-
-  "github.com/urfave/cli"
-)
-
-func init() {
-  cli.AppHelpTemplate += "\nCUSTOMIZED: you bet ur muffins\n"
-  cli.CommandHelpTemplate += "\nYMMV\n"
-  cli.SubcommandHelpTemplate += "\nor something\n"
-
-  cli.HelpFlag = cli.BoolFlag{Name: "halp"}
-  cli.BashCompletionFlag = cli.BoolFlag{Name: "compgen", Hidden: true}
-  cli.VersionFlag = cli.BoolFlag{Name: "print-version, V"}
-
-  cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) {
-    fmt.Fprintf(w, "best of luck to you\n")
-  }
-  cli.VersionPrinter = func(c *cli.Context) {
-    fmt.Fprintf(c.App.Writer, "version=%s\n", c.App.Version)
-  }
-  cli.OsExiter = func(c int) {
-    fmt.Fprintf(cli.ErrWriter, "refusing to exit %d\n", c)
-  }
-  cli.ErrWriter = ioutil.Discard
-  cli.FlagStringer = func(fl cli.Flag) string {
-    return fmt.Sprintf("\t\t%s", fl.GetName())
-  }
-}
-
-type hexWriter struct{}
-
-func (w *hexWriter) Write(p []byte) (int, error) {
-  for _, b := range p {
-    fmt.Printf("%x", b)
-  }
-  fmt.Printf("\n")
-
-  return len(p), nil
-}
-
-type genericType struct{
-  s string
-}
-
-func (g *genericType) Set(value string) error {
-  g.s = value
-  return nil
-}
-
-func (g *genericType) String() string {
-  return g.s
-}
-
-func main() {
-  app := cli.NewApp()
-  app.Name = "kənˈtrīv"
-  app.Version = "19.99.0"
-  app.Compiled = time.Now()
-  app.Authors = []cli.Author{
-    cli.Author{
-      Name:  "Example Human",
-      Email: "human@example.com",
-    },
-  }
-  app.Copyright = "(c) 1999 Serious Enterprise"
-  app.HelpName = "contrive"
-  app.Usage = "demonstrate available API"
-  app.UsageText = "contrive - demonstrating the available API"
-  app.ArgsUsage = "[args and such]"
-  app.Commands = []cli.Command{
-    cli.Command{
-      Name:        "doo",
-      Aliases:     []string{"do"},
-      Category:    "motion",
-      Usage:       "do the doo",
-      UsageText:   "doo - does the dooing",
-      Description: "no really, there is a lot of dooing to be done",
-      ArgsUsage:   "[arrgh]",
-      Flags: []cli.Flag{
-        cli.BoolFlag{Name: "forever, forevvarr"},
-      },
-      Subcommands: cli.Commands{
-        cli.Command{
-          Name:   "wop",
-          Action: wopAction,
-        },
-      },
-      SkipFlagParsing: false,
-      HideHelp:        false,
-      Hidden:          false,
-      HelpName:        "doo!",
-      BashComplete: func(c *cli.Context) {
-        fmt.Fprintf(c.App.Writer, "--better\n")
-      },
-      Before: func(c *cli.Context) error {
-        fmt.Fprintf(c.App.Writer, "brace for impact\n")
-        return nil
-      },
-      After: func(c *cli.Context) error {
-        fmt.Fprintf(c.App.Writer, "did we lose anyone?\n")
-        return nil
-      },
-      Action: func(c *cli.Context) error {
-        c.Command.FullName()
-        c.Command.HasName("wop")
-        c.Command.Names()
-        c.Command.VisibleFlags()
-        fmt.Fprintf(c.App.Writer, "dodododododoodododddooooododododooo\n")
-        if c.Bool("forever") {
-          c.Command.Run(c)
-        }
-        return nil
-      },
-      OnUsageError: func(c *cli.Context, err error, isSubcommand bool) error {
-        fmt.Fprintf(c.App.Writer, "for shame\n")
-        return err
-      },
-    },
-  }
-  app.Flags = []cli.Flag{
-    cli.BoolFlag{Name: "fancy"},
-    cli.BoolTFlag{Name: "fancier"},
-    cli.DurationFlag{Name: "howlong, H", Value: time.Second * 3},
-    cli.Float64Flag{Name: "howmuch"},
-    cli.GenericFlag{Name: "wat", Value: &genericType{}},
-    cli.Int64Flag{Name: "longdistance"},
-    cli.Int64SliceFlag{Name: "intervals"},
-    cli.IntFlag{Name: "distance"},
-    cli.IntSliceFlag{Name: "times"},
-    cli.StringFlag{Name: "dance-move, d"},
-    cli.StringSliceFlag{Name: "names, N"},
-    cli.UintFlag{Name: "age"},
-    cli.Uint64Flag{Name: "bigage"},
-  }
-  app.EnableBashCompletion = true
-  app.UseShortOptionHandling = true
-  app.HideHelp = false
-  app.HideVersion = false
-  app.BashComplete = func(c *cli.Context) {
-    fmt.Fprintf(c.App.Writer, "lipstick\nkiss\nme\nlipstick\nringo\n")
-  }
-  app.Before = func(c *cli.Context) error {
-    fmt.Fprintf(c.App.Writer, "HEEEERE GOES\n")
-    return nil
-  }
-  app.After = func(c *cli.Context) error {
-    fmt.Fprintf(c.App.Writer, "Phew!\n")
-    return nil
-  }
-  app.CommandNotFound = func(c *cli.Context, command string) {
-    fmt.Fprintf(c.App.Writer, "Thar be no %q here.\n", command)
-  }
-  app.OnUsageError = func(c *cli.Context, err error, isSubcommand bool) error {
-    if isSubcommand {
-      return err
-    }
-
-    fmt.Fprintf(c.App.Writer, "WRONG: %#v\n", err)
-    return nil
-  }
-  app.Action = func(c *cli.Context) error {
-    cli.DefaultAppComplete(c)
-    cli.HandleExitCoder(errors.New("not an exit coder, though"))
-    cli.ShowAppHelp(c)
-    cli.ShowCommandCompletions(c, "nope")
-    cli.ShowCommandHelp(c, "also-nope")
-    cli.ShowCompletions(c)
-    cli.ShowSubcommandHelp(c)
-    cli.ShowVersion(c)
-
-    categories := c.App.Categories()
-    categories.AddCommand("sounds", cli.Command{
-      Name: "bloop",
-    })
-
-    for _, category := range c.App.Categories() {
-      fmt.Fprintf(c.App.Writer, "%s\n", category.Name)
-      fmt.Fprintf(c.App.Writer, "%#v\n", category.Commands)
-      fmt.Fprintf(c.App.Writer, "%#v\n", category.VisibleCommands())
-    }
-
-    fmt.Printf("%#v\n", c.App.Command("doo"))
-    if c.Bool("infinite") {
-      c.App.Run([]string{"app", "doo", "wop"})
-    }
-
-    if c.Bool("forevar") {
-      c.App.RunAsSubcommand(c)
-    }
-    c.App.Setup()
-    fmt.Printf("%#v\n", c.App.VisibleCategories())
-    fmt.Printf("%#v\n", c.App.VisibleCommands())
-    fmt.Printf("%#v\n", c.App.VisibleFlags())
-
-    fmt.Printf("%#v\n", c.Args().First())
-    if len(c.Args()) > 0 {
-      fmt.Printf("%#v\n", c.Args()[1])
-    }
-    fmt.Printf("%#v\n", c.Args().Present())
-    fmt.Printf("%#v\n", c.Args().Tail())
-
-    set := flag.NewFlagSet("contrive", 0)
-    nc := cli.NewContext(c.App, set, c)
-
-    fmt.Printf("%#v\n", nc.Args())
-    fmt.Printf("%#v\n", nc.Bool("nope"))
-    fmt.Printf("%#v\n", nc.BoolT("nerp"))
-    fmt.Printf("%#v\n", nc.Duration("howlong"))
-    fmt.Printf("%#v\n", nc.Float64("hay"))
-    fmt.Printf("%#v\n", nc.Generic("bloop"))
-    fmt.Printf("%#v\n", nc.Int64("bonk"))
-    fmt.Printf("%#v\n", nc.Int64Slice("burnks"))
-    fmt.Printf("%#v\n", nc.Int("bips"))
-    fmt.Printf("%#v\n", nc.IntSlice("blups"))
-    fmt.Printf("%#v\n", nc.String("snurt"))
-    fmt.Printf("%#v\n", nc.StringSlice("snurkles"))
-    fmt.Printf("%#v\n", nc.Uint("flub"))
-    fmt.Printf("%#v\n", nc.Uint64("florb"))
-    fmt.Printf("%#v\n", nc.GlobalBool("global-nope"))
-    fmt.Printf("%#v\n", nc.GlobalBoolT("global-nerp"))
-    fmt.Printf("%#v\n", nc.GlobalDuration("global-howlong"))
-    fmt.Printf("%#v\n", nc.GlobalFloat64("global-hay"))
-    fmt.Printf("%#v\n", nc.GlobalGeneric("global-bloop"))
-    fmt.Printf("%#v\n", nc.GlobalInt("global-bips"))
-    fmt.Printf("%#v\n", nc.GlobalIntSlice("global-blups"))
-    fmt.Printf("%#v\n", nc.GlobalString("global-snurt"))
-    fmt.Printf("%#v\n", nc.GlobalStringSlice("global-snurkles"))
-
-    fmt.Printf("%#v\n", nc.FlagNames())
-    fmt.Printf("%#v\n", nc.GlobalFlagNames())
-    fmt.Printf("%#v\n", nc.GlobalIsSet("wat"))
-    fmt.Printf("%#v\n", nc.GlobalSet("wat", "nope"))
-    fmt.Printf("%#v\n", nc.NArg())
-    fmt.Printf("%#v\n", nc.NumFlags())
-    fmt.Printf("%#v\n", nc.Parent())
-
-    nc.Set("wat", "also-nope")
-
-    ec := cli.NewExitError("ohwell", 86)
-    fmt.Fprintf(c.App.Writer, "%d", ec.ExitCode())
-    fmt.Printf("made it!\n")
-    return nil
-  }
-
-  if os.Getenv("HEXY") != "" {
-    app.Writer = &hexWriter{}
-    app.ErrWriter = &hexWriter{}
-  }
-
-  app.Metadata = map[string]interface{}{
-    "layers":     "many",
-    "explicable": false,
-    "whatever-values": 19.99,
-  }
-
-
-  // ignore error so we don't exit non-zero and break gfmrun README example tests
-  _ = app.Run(os.Args)
-}
-
-func wopAction(c *cli.Context) error {
-  fmt.Fprintf(c.App.Writer, ":wave: over here, eh\n")
-  return nil
-}
-```
-
-## Migrating to V2
-
-There are a small set of breaking changes between v1 and v2.
-Converting is relatively straightforward and typically takes less than
-an hour. Specific steps are included in
-[Migration Guide: v1 to v2](../migrate-v1-to-v2.md).
diff --git a/docs/v1/migrating-to-v2.md b/docs/v1/migrating-to-v2.md
new file mode 100644
index 0000000..059b7db
--- /dev/null
+++ b/docs/v1/migrating-to-v2.md
@@ -0,0 +1,9 @@
+---
+tags:
+  - v1
+---
+
+There are a small set of breaking changes between v1 and v2.
+Converting is relatively straightforward and typically takes less than
+an hour. Specific steps are included in
+[Migration Guide: v1 to v2](../migrate-v1-to-v2.md).
diff --git a/docs/v2/examples/arguments.md b/docs/v2/examples/arguments.md
new file mode 100644
index 0000000..88dd460
--- /dev/null
+++ b/docs/v2/examples/arguments.md
@@ -0,0 +1,36 @@
+---
+tags:
+  - v2
+search:
+  boost: 2
+---
+
+You can lookup arguments by calling the `Args` function on `cli.Context`, e.g.:
+
+<!-- {
+  "output": "Hello \""
+} -->
+```go
+package main
+
+import (
+	"fmt"
+	"log"
+	"os"
+
+	"github.com/urfave/cli/v2"
+)
+
+func main() {
+	app := &cli.App{
+		Action: func(cCtx *cli.Context) error {
+			fmt.Printf("Hello %q", cCtx.Args().Get(0))
+			return nil
+		},
+	}
+
+	if err := app.Run(os.Args); err != nil {
+		log.Fatal(err)
+	}
+}
+```
diff --git a/docs/v2/examples/bash-completions.md b/docs/v2/examples/bash-completions.md
new file mode 100644
index 0000000..e9265f4
--- /dev/null
+++ b/docs/v2/examples/bash-completions.md
@@ -0,0 +1,257 @@
+---
+tags:
+  - v2
+search:
+  boost: 2
+---
+
+You can enable completion commands by setting the `EnableBashCompletion` flag on
+the `App` object to `true`.  By default, this setting will allow auto-completion
+for an app's subcommands, but you can write your own completion methods for the
+App or its subcommands as well.
+
+#### Default auto-completion
+
+```go
+package main
+
+import (
+	"fmt"
+	"log"
+	"os"
+
+	"github.com/urfave/cli/v2"
+)
+
+func main() {
+	app := &cli.App{
+		EnableBashCompletion: true,
+		Commands: []*cli.Command{
+			{
+				Name:    "add",
+				Aliases: []string{"a"},
+				Usage:   "add a task to the list",
+				Action: func(cCtx *cli.Context) error {
+					fmt.Println("added task: ", cCtx.Args().First())
+					return nil
+				},
+			},
+			{
+				Name:    "complete",
+				Aliases: []string{"c"},
+				Usage:   "complete a task on the list",
+				Action: func(cCtx *cli.Context) error {
+					fmt.Println("completed task: ", cCtx.Args().First())
+					return nil
+				},
+			},
+			{
+				Name:    "template",
+				Aliases: []string{"t"},
+				Usage:   "options for task templates",
+				Subcommands: []*cli.Command{
+					{
+						Name:  "add",
+						Usage: "add a new template",
+						Action: func(cCtx *cli.Context) error {
+							fmt.Println("new task template: ", cCtx.Args().First())
+							return nil
+						},
+					},
+					{
+						Name:  "remove",
+						Usage: "remove an existing template",
+						Action: func(cCtx *cli.Context) error {
+							fmt.Println("removed task template: ", cCtx.Args().First())
+							return nil
+						},
+					},
+				},
+			},
+		},
+	}
+
+	if err := app.Run(os.Args); err != nil {
+		log.Fatal(err)
+	}
+}
+```
+![](/docs/v2/images/default-bash-autocomplete.gif)
+
+#### Custom auto-completion
+<!-- {
+  "args": ["complete", "&#45;&#45;generate&#45;bash&#45;completion"],
+  "output": "laundry"
+} -->
+```go
+package main
+
+import (
+	"fmt"
+	"log"
+	"os"
+
+	"github.com/urfave/cli/v2"
+)
+
+func main() {
+	tasks := []string{"cook", "clean", "laundry", "eat", "sleep", "code"}
+
+	app := &cli.App{
+		EnableBashCompletion: true,
+		Commands: []*cli.Command{
+			{
+				Name:    "complete",
+				Aliases: []string{"c"},
+				Usage:   "complete a task on the list",
+				Action: func(cCtx *cli.Context) error {
+					fmt.Println("completed task: ", cCtx.Args().First())
+					return nil
+				},
+				BashComplete: func(cCtx *cli.Context) {
+					// This will complete if no args are passed
+					if cCtx.NArg() > 0 {
+						return
+					}
+					for _, t := range tasks {
+						fmt.Println(t)
+					}
+				},
+			},
+		},
+	}
+
+	if err := app.Run(os.Args); err != nil {
+		log.Fatal(err)
+	}
+}
+```
+![](/docs/v2/images/custom-bash-autocomplete.gif)
+
+#### Enabling
+
+To enable auto-completion for the current shell session, a bash script,
+`autocomplete/bash_autocomplete` is included in this repo.
+
+To use `autocomplete/bash_autocomplete` set an environment variable named `PROG`
+to the name of your program and then `source` the
+`autocomplete/bash_autocomplete` file.
+
+For example, if your cli program is called `myprogram`:
+
+```sh-session
+$ PROG=myprogram source path/to/cli/autocomplete/bash_autocomplete
+```
+
+Auto-completion is now enabled for the current shell, but will not persist into
+a new shell.
+
+#### Distribution and Persistent Autocompletion
+
+Copy `autocomplete/bash_autocomplete` into `/etc/bash_completion.d/` and rename
+it to the name of the program you wish to add autocomplete support for (or
+automatically install it there if you are distributing a package). Don't forget
+to source the file or restart your shell to activate the auto-completion.
+
+```sh-session
+$ sudo cp path/to/autocomplete/bash_autocomplete /etc/bash_completion.d/<myprogram>
+$ source /etc/bash_completion.d/<myprogram>
+```
+
+Alternatively, you can just document that users should `source` the generic
+`autocomplete/bash_autocomplete` and set `$PROG` within their bash configuration
+file, adding these lines:
+
+```sh-session
+$ PROG=<myprogram>
+$ source path/to/cli/autocomplete/bash_autocomplete
+```
+
+Keep in mind that if they are enabling auto-completion for more than one
+program, they will need to set `PROG` and source
+`autocomplete/bash_autocomplete` for each program, like so:
+
+```sh-session
+$ PROG=<program1>
+$ source path/to/cli/autocomplete/bash_autocomplete
+
+$ PROG=<program2>
+$ source path/to/cli/autocomplete/bash_autocomplete
+```
+
+#### Customization
+
+The default shell completion flag (`--generate-bash-completion`) is defined as
+`cli.EnableBashCompletion`, and may be redefined if desired, e.g.:
+
+<!-- {
+  "args": ["&#45;&#45;generate&#45;bash&#45;completion"],
+  "output": "wat\nhelp\nh"
+} -->
+```go
+package main
+
+import (
+	"log"
+	"os"
+
+	"github.com/urfave/cli/v2"
+)
+
+func main() {
+	app := &cli.App{
+		EnableBashCompletion: true,
+		Commands: []*cli.Command{
+			{
+				Name: "wat",
+			},
+		},
+	}
+
+	if err := app.Run(os.Args); err != nil {
+		log.Fatal(err)
+	}
+}
+```
+
+#### ZSH Support
+
+Auto-completion for ZSH is also supported using the
+`autocomplete/zsh_autocomplete` file included in this repo. One environment
+variable is used, `PROG`.  Set `PROG` to the program name as before, and then
+`source path/to/autocomplete/zsh_autocomplete`.  Adding the following lines to
+your ZSH configuration file (usually `.zshrc`) will allow the auto-completion to
+persist across new shells:
+
+```sh-session
+$ PROG=<myprogram>
+$ source path/to/autocomplete/zsh_autocomplete
+```
+
+#### ZSH default auto-complete example
+![](/docs/v2/images/default-zsh-autocomplete.gif)
+
+#### ZSH custom auto-complete example
+![](/docs/v2/images/custom-zsh-autocomplete.gif)
+
+#### PowerShell Support
+
+Auto-completion for PowerShell is also supported using the
+`autocomplete/powershell_autocomplete.ps1` file included in this repo.
+
+Rename the script to `<my program>.ps1` and move it anywhere in your file
+system.  The location of script does not matter, only the file name of the
+script has to match the your program's binary name.
+
+To activate it, enter:
+
+```powershell
+& path/to/autocomplete/<my program>.ps1
+```
+
+To persist across new shells, open the PowerShell profile (with `code $profile`
+or `notepad $profile`) and add the line:
+
+```powershell
+& path/to/autocomplete/<my program>.ps1
+```
diff --git a/docs/v2/examples/combining-short-options.md b/docs/v2/examples/combining-short-options.md
new file mode 100644
index 0000000..f502877
--- /dev/null
+++ b/docs/v2/examples/combining-short-options.md
@@ -0,0 +1,74 @@
+---
+tags:
+  - v2
+search:
+  boost: 2
+---
+
+Traditional use of options using their shortnames look like this:
+
+```sh-session
+$ cmd -s -o -m "Some message"
+```
+
+Suppose you want users to be able to combine options with their shortnames. This
+can be done using the `UseShortOptionHandling` bool in your app configuration,
+or for individual commands by attaching it to the command configuration. For
+example:
+
+<!-- {
+  "args": ["short", "&#45;som", "Some message"],
+  "output": "serve: true\noption: true\nmessage: Some message\n"
+} -->
+```go
+package main
+
+import (
+	"fmt"
+	"log"
+	"os"
+
+	"github.com/urfave/cli/v2"
+)
+
+func main() {
+	app := &cli.App{
+		UseShortOptionHandling: true,
+		Commands: []*cli.Command{
+			{
+				Name:  "short",
+				Usage: "complete a task on the list",
+				Flags: []cli.Flag{
+					&cli.BoolFlag{Name: "serve", Aliases: []string{"s"}},
+					&cli.BoolFlag{Name: "option", Aliases: []string{"o"}},
+					&cli.StringFlag{Name: "message", Aliases: []string{"m"}},
+				},
+				Action: func(cCtx *cli.Context) error {
+					fmt.Println("serve:", cCtx.Bool("serve"))
+					fmt.Println("option:", cCtx.Bool("option"))
+					fmt.Println("message:", cCtx.String("message"))
+					return nil
+				},
+			},
+		},
+	}
+
+	if err := app.Run(os.Args); err != nil {
+		log.Fatal(err)
+	}
+}
+```
+
+If your program has any number of bool flags such as `serve` and `option`, and
+optionally one non-bool flag `message`, with the short options of `-s`, `-o`,
+and `-m` respectively, setting `UseShortOptionHandling` will also support the
+following syntax:
+
+```sh-session
+$ cmd -som "Some message"
+```
+
+If you enable `UseShortOptionHandling`, then you must not use any flags that
+have a single leading `-` or this will result in failures. For example,
+`-option` can no longer be used. Flags with two leading dashes (such as
+`--options`) are still valid.
diff --git a/docs/v2/examples/exit-codes.md b/docs/v2/examples/exit-codes.md
new file mode 100644
index 0000000..75db781
--- /dev/null
+++ b/docs/v2/examples/exit-codes.md
@@ -0,0 +1,45 @@
+---
+tags:
+  - v2
+search:
+  boost: 2
+---
+
+Calling `App.Run` will not automatically call `os.Exit`, which means that by
+default the exit code will "fall through" to being `0`.  An explicit exit code
+may be set by returning a non-nil error that fulfills `cli.ExitCoder`, *or* a
+`cli.MultiError` that includes an error that fulfills `cli.ExitCoder`, e.g.:
+<!-- {
+  "error": "Ginger croutons are not in the soup"
+} -->
+```go
+package main
+
+import (
+	"log"
+	"os"
+
+	"github.com/urfave/cli/v2"
+)
+
+func main() {
+	app := &cli.App{
+		Flags: []cli.Flag{
+			&cli.BoolFlag{
+				Name:  "ginger-crouton",
+				Usage: "is it in the soup?",
+			},
+		},
+		Action: func(ctx *cli.Context) error {
+			if !ctx.Bool("ginger-crouton") {
+				return cli.Exit("Ginger croutons are not in the soup", 86)
+			}
+			return nil
+		},
+	}
+
+	if err := app.Run(os.Args); err != nil {
+		log.Fatal(err)
+	}
+}
+```
diff --git a/docs/v2/examples/flags.md b/docs/v2/examples/flags.md
new file mode 100644
index 0000000..2a629f7
--- /dev/null
+++ b/docs/v2/examples/flags.md
@@ -0,0 +1,557 @@
+---
+tags:
+  - v2
+search:
+  boost: 2
+---
+
+Setting and querying flags is simple.
+
+<!-- {
+  "output": "Hello Nefertiti"
+} -->
+```go
+package main
+
+import (
+	"fmt"
+	"log"
+	"os"
+
+	"github.com/urfave/cli/v2"
+)
+
+func main() {
+	app := &cli.App{
+		Flags: []cli.Flag{
+			&cli.StringFlag{
+				Name:  "lang",
+				Value: "english",
+				Usage: "language for the greeting",
+			},
+		},
+		Action: func(cCtx *cli.Context) error {
+			name := "Nefertiti"
+			if cCtx.NArg() > 0 {
+				name = cCtx.Args().Get(0)
+			}
+			if cCtx.String("lang") == "spanish" {
+				fmt.Println("Hola", name)
+			} else {
+				fmt.Println("Hello", name)
+			}
+			return nil
+		},
+	}
+
+	if err := app.Run(os.Args); err != nil {
+		log.Fatal(err)
+	}
+}
+```
+
+You can also set a destination variable for a flag, to which the content will be
+scanned.
+
+<!-- {
+  "output": "Hello someone"
+} -->
+```go
+package main
+
+import (
+	"fmt"
+	"log"
+	"os"
+
+	"github.com/urfave/cli/v2"
+)
+
+func main() {
+	var language string
+
+	app := &cli.App{
+		Flags: []cli.Flag{
+			&cli.StringFlag{
+				Name:        "lang",
+				Value:       "english",
+				Usage:       "language for the greeting",
+				Destination: &language,
+			},
+		},
+		Action: func(cCtx *cli.Context) error {
+			name := "someone"
+			if cCtx.NArg() > 0 {
+				name = cCtx.Args().Get(0)
+			}
+			if language == "spanish" {
+				fmt.Println("Hola", name)
+			} else {
+				fmt.Println("Hello", name)
+			}
+			return nil
+		},
+	}
+
+	if err := app.Run(os.Args); err != nil {
+		log.Fatal(err)
+	}
+}
+```
+
+See full list of flags at https://pkg.go.dev/github.com/urfave/cli/v2
+
+#### Placeholder Values
+
+Sometimes it's useful to specify a flag's value within the usage string itself.
+Such placeholders are indicated with back quotes.
+
+For example this:
+
+<!-- {
+  "args": ["&#45;&#45;help"],
+  "output": "&#45;&#45;config FILE, &#45;c FILE"
+} -->
+```go
+package main
+
+import (
+	"log"
+	"os"
+
+	"github.com/urfave/cli/v2"
+)
+
+func main() {
+	app := &cli.App{
+		Flags: []cli.Flag{
+			&cli.StringFlag{
+				Name:    "config",
+				Aliases: []string{"c"},
+				Usage:   "Load configuration from `FILE`",
+			},
+		},
+	}
+
+	if err := app.Run(os.Args); err != nil {
+		log.Fatal(err)
+	}
+}
+```
+
+Will result in help output like:
+
+```
+--config FILE, -c FILE   Load configuration from FILE
+```
+
+Note that only the first placeholder is used. Subsequent back-quoted words will
+be left as-is.
+
+#### Alternate Names
+
+You can set alternate (or short) names for flags by providing a comma-delimited
+list for the `Name`. e.g.
+
+<!-- {
+  "args": ["&#45;&#45;help"],
+  "output": "&#45;&#45;lang value, &#45;l value.*language for the greeting.*default: \"english\""
+} -->
+```go
+package main
+
+import (
+	"log"
+	"os"
+
+	"github.com/urfave/cli/v2"
+)
+
+func main() {
+	app := &cli.App{
+		Flags: []cli.Flag{
+			&cli.StringFlag{
+				Name:    "lang",
+				Aliases: []string{"l"},
+				Value:   "english",
+				Usage:   "language for the greeting",
+			},
+		},
+	}
+
+	if err := app.Run(os.Args); err != nil {
+		log.Fatal(err)
+	}
+}
+```
+
+That flag can then be set with `--lang spanish` or `-l spanish`. Note that
+giving two different forms of the same flag in the same command invocation is an
+error.
+
+#### Ordering
+
+Flags for the application and commands are shown in the order they are defined.
+However, it's possible to sort them from outside this library by using `FlagsByName`
+or `CommandsByName` with `sort`.
+
+For example this:
+
+<!-- {
+  "args": ["&#45;&#45;help"],
+  "output": ".*Load configuration from FILE\n.*\n.*Language for the greeting.*"
+} -->
+```go
+package main
+
+import (
+	"log"
+	"os"
+	"sort"
+
+	"github.com/urfave/cli/v2"
+)
+
+func main() {
+	app := &cli.App{
+		Flags: []cli.Flag{
+			&cli.StringFlag{
+				Name:    "lang",
+				Aliases: []string{"l"},
+				Value:   "english",
+				Usage:   "Language for the greeting",
+			},
+			&cli.StringFlag{
+				Name:    "config",
+				Aliases: []string{"c"},
+				Usage:   "Load configuration from `FILE`",
+			},
+		},
+		Commands: []*cli.Command{
+			{
+				Name:    "complete",
+				Aliases: []string{"c"},
+				Usage:   "complete a task on the list",
+				Action: func(*cli.Context) error {
+					return nil
+				},
+			},
+			{
+				Name:    "add",
+				Aliases: []string{"a"},
+				Usage:   "add a task to the list",
+				Action: func(*cli.Context) error {
+					return nil
+				},
+			},
+		},
+	}
+
+	sort.Sort(cli.FlagsByName(app.Flags))
+	sort.Sort(cli.CommandsByName(app.Commands))
+
+	if err := app.Run(os.Args); err != nil {
+		log.Fatal(err)
+	}
+}
+```
+
+Will result in help output like:
+
+```
+--config FILE, -c FILE  Load configuration from FILE
+--lang value, -l value  Language for the greeting (default: "english")
+```
+
+#### Values from the Environment
+
+You can also have the default value set from the environment via `EnvVars`.  e.g.
+
+<!-- {
+  "args": ["&#45;&#45;help"],
+  "output": "language for the greeting.*APP_LANG"
+} -->
+```go
+package main
+
+import (
+	"log"
+	"os"
+
+	"github.com/urfave/cli/v2"
+)
+
+func main() {
+	app := &cli.App{
+		Flags: []cli.Flag{
+			&cli.StringFlag{
+				Name:    "lang",
+				Aliases: []string{"l"},
+				Value:   "english",
+				Usage:   "language for the greeting",
+				EnvVars: []string{"APP_LANG"},
+			},
+		},
+	}
+
+	if err := app.Run(os.Args); err != nil {
+		log.Fatal(err)
+	}
+}
+```
+
+If `EnvVars` contains more than one string, the first environment variable that
+resolves is used.
+
+<!-- {
+  "args": ["&#45;&#45;help"],
+  "output": "language for the greeting.*LEGACY_COMPAT_LANG.*APP_LANG.*LANG"
+} -->
+```go
+package main
+
+import (
+	"log"
+	"os"
+
+	"github.com/urfave/cli/v2"
+)
+
+func main() {
+	app := &cli.App{
+		Flags: []cli.Flag{
+			&cli.StringFlag{
+				Name:    "lang",
+				Aliases: []string{"l"},
+				Value:   "english",
+				Usage:   "language for the greeting",
+				EnvVars: []string{"LEGACY_COMPAT_LANG", "APP_LANG", "LANG"},
+			},
+		},
+	}
+
+	if err := app.Run(os.Args); err != nil {
+		log.Fatal(err)
+	}
+}
+```
+
+#### Values from files
+
+You can also have the default value set from file via `FilePath`.  e.g.
+
+<!-- {
+  "args": ["&#45;&#45;help"],
+  "output": "password for the mysql database"
+} -->
+```go
+package main
+
+import (
+	"log"
+	"os"
+
+	"github.com/urfave/cli/v2"
+)
+
+func main() {
+	app := &cli.App{
+		Flags: []cli.Flag{
+			&cli.StringFlag{
+				Name:     "password",
+				Aliases:  []string{"p"},
+				Usage:    "password for the mysql database",
+				FilePath: "/etc/mysql/password",
+			},
+		},
+	}
+
+	if err := app.Run(os.Args); err != nil {
+		log.Fatal(err)
+	}
+}
+```
+
+Note that default values set from file (e.g. `FilePath`) take precedence over
+default values set from the environment (e.g. `EnvVar`).
+
+#### Values from alternate input sources (YAML, TOML, and others)
+
+There is a separate package altsrc that adds support for getting flag values
+from other file input sources.
+
+Currently supported input source formats:
+
+- YAML
+- JSON
+- TOML
+
+In order to get values for a flag from an alternate input source the following
+code would be added to wrap an existing cli.Flag like below:
+
+```go
+  // --- >8 ---
+  altsrc.NewIntFlag(&cli.IntFlag{Name: "test"})
+```
+
+Initialization must also occur for these flags. Below is an example initializing
+getting data from a yaml file below.
+
+```go
+  // --- >8 ---
+  command.Before = altsrc.InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
+```
+
+The code above will use the "load" string as a flag name to get the file name of
+a yaml file from the cli.Context.  It will then use that file name to initialize
+the yaml input source for any flags that are defined on that command.  As a note
+the "load" flag used would also have to be defined on the command flags in order
+for this code snippet to work.
+
+Currently only YAML, JSON, and TOML files are supported but developers can add
+support for other input sources by implementing the altsrc.InputSourceContext
+for their given sources.
+
+Here is a more complete sample of a command using YAML support:
+
+<!-- {
+  "args": ["&#45;&#45;help"],
+  "output": "&#45&#45;test value.*default: 0"
+} -->
+```go
+package main
+
+import (
+	"fmt"
+	"os"
+
+	"github.com/urfave/cli/v2"
+	"github.com/urfave/cli/v2/altsrc"
+)
+
+func main() {
+	flags := []cli.Flag{
+		altsrc.NewIntFlag(&cli.IntFlag{Name: "test"}),
+		&cli.StringFlag{Name: "load"},
+	}
+
+	app := &cli.App{
+		Action: func(*cli.Context) error {
+			fmt.Println("--test value.*default: 0")
+			return nil
+		},
+		Before: altsrc.InitInputSourceWithContext(flags, altsrc.NewYamlSourceFromFlagFunc("load")),
+		Flags:  flags,
+	}
+
+	app.Run(os.Args)
+}
+```
+
+#### Required Flags
+
+You can make a flag required by setting the `Required` field to `true`. If a user
+does not provide a required flag, they will be shown an error message.
+
+Take for example this app that requires the `lang` flag:
+
+<!-- {
+  "error": "Required flag \"lang\" not set"
+} -->
+```go
+package main
+
+import (
+	"fmt"
+	"log"
+	"os"
+
+	"github.com/urfave/cli/v2"
+)
+
+func main() {
+	app := &cli.App{
+		Flags: []cli.Flag{
+			&cli.StringFlag{
+				Name:     "lang",
+				Value:    "english",
+				Usage:    "language for the greeting",
+				Required: true,
+			},
+		},
+		Action: func(cCtx *cli.Context) error {
+			output := "Hello"
+			if cCtx.String("lang") == "spanish" {
+				output = "Hola"
+			}
+			fmt.Println(output)
+			return nil
+		},
+	}
+
+	if err := app.Run(os.Args); err != nil {
+		log.Fatal(err)
+	}
+}
+```
+
+If the app is run without the `lang` flag, the user will see the following message
+
+```
+Required flag "lang" not set
+```
+
+#### Default Values for help output
+
+Sometimes it's useful to specify a flag's default help-text value within the
+flag declaration. This can be useful if the default value for a flag is a
+computed value. The default value can be set via the `DefaultText` struct field.
+
+For example this:
+
+<!-- {
+  "args": ["&#45;&#45;help"],
+  "output": "&#45;&#45;port value"
+} -->
+```go
+package main
+
+import (
+	"log"
+	"os"
+
+	"github.com/urfave/cli/v2"
+)
+
+func main() {
+	app := &cli.App{
+		Flags: []cli.Flag{
+			&cli.IntFlag{
+				Name:        "port",
+				Usage:       "Use a randomized port",
+				Value:       0,
+				DefaultText: "random",
+			},
+		},
+	}
+
+	if err := app.Run(os.Args); err != nil {
+		log.Fatal(err)
+	}
+}
+```
+
+Will result in help output like:
+
+```
+--port value  Use a randomized port (default: random)
+```
+
+#### Precedence
+
+The precedence for flag value sources is as follows (highest to lowest):
+
+0. Command line flag value from user
+0. Environment variable (if specified)
+0. Configuration file (if specified)
+0. Default defined on the flag
diff --git a/docs/v2/examples/full-api-example.md b/docs/v2/examples/full-api-example.md
new file mode 100644
index 0000000..289cffe
--- /dev/null
+++ b/docs/v2/examples/full-api-example.md
@@ -0,0 +1,262 @@
+---
+tags:
+  - v2
+search:
+  boost: 2
+---
+
+**Notice**: This is a contrived (functioning) example meant strictly for API
+demonstration purposes. Use of one's imagination is encouraged.
+
+<!-- {
+  "output": "made it!\nPhew!"
+} -->
+```go
+package main
+
+import (
+	"errors"
+	"flag"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"time"
+
+	"github.com/urfave/cli/v2"
+)
+
+func init() {
+	cli.AppHelpTemplate += "\nCUSTOMIZED: you bet ur muffins\n"
+	cli.CommandHelpTemplate += "\nYMMV\n"
+	cli.SubcommandHelpTemplate += "\nor something\n"
+
+	cli.HelpFlag = &cli.BoolFlag{Name: "halp"}
+	cli.VersionFlag = &cli.BoolFlag{Name: "print-version", Aliases: []string{"V"}}
+
+	cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) {
+		fmt.Fprintf(w, "best of luck to you\n")
+	}
+	cli.VersionPrinter = func(cCtx *cli.Context) {
+		fmt.Fprintf(cCtx.App.Writer, "version=%s\n", cCtx.App.Version)
+	}
+	cli.OsExiter = func(cCtx int) {
+		fmt.Fprintf(cli.ErrWriter, "refusing to exit %d\n", cCtx)
+	}
+	cli.ErrWriter = ioutil.Discard
+	cli.FlagStringer = func(fl cli.Flag) string {
+		return fmt.Sprintf("\t\t%s", fl.Names()[0])
+	}
+}
+
+type hexWriter struct{}
+
+func (w *hexWriter) Write(p []byte) (int, error) {
+	for _, b := range p {
+		fmt.Printf("%x", b)
+	}
+	fmt.Printf("\n")
+
+	return len(p), nil
+}
+
+type genericType struct {
+	s string
+}
+
+func (g *genericType) Set(value string) error {
+	g.s = value
+	return nil
+}
+
+func (g *genericType) String() string {
+	return g.s
+}
+
+func main() {
+	app := &cli.App{
+		Name:     "kənˈtrīv",
+		Version:  "v19.99.0",
+		Compiled: time.Now(),
+		Authors: []*cli.Author{
+			&cli.Author{
+				Name:  "Example Human",
+				Email: "human@example.com",
+			},
+		},
+		Copyright: "(c) 1999 Serious Enterprise",
+		HelpName:  "contrive",
+		Usage:     "demonstrate available API",
+		UsageText: "contrive - demonstrating the available API",
+		ArgsUsage: "[args and such]",
+		Commands: []*cli.Command{
+			&cli.Command{
+				Name:        "doo",
+				Aliases:     []string{"do"},
+				Category:    "motion",
+				Usage:       "do the doo",
+				UsageText:   "doo - does the dooing",
+				Description: "no really, there is a lot of dooing to be done",
+				ArgsUsage:   "[arrgh]",
+				Flags: []cli.Flag{
+					&cli.BoolFlag{Name: "forever", Aliases: []string{"forevvarr"}},
+				},
+				Subcommands: []*cli.Command{
+					&cli.Command{
+						Name:   "wop",
+						Action: wopAction,
+					},
+				},
+				SkipFlagParsing: false,
+				HideHelp:        false,
+				Hidden:          false,
+				HelpName:        "doo!",
+				BashComplete: func(cCtx *cli.Context) {
+					fmt.Fprintf(cCtx.App.Writer, "--better\n")
+				},
+				Before: func(cCtx *cli.Context) error {
+					fmt.Fprintf(cCtx.App.Writer, "brace for impact\n")
+					return nil
+				},
+				After: func(cCtx *cli.Context) error {
+					fmt.Fprintf(cCtx.App.Writer, "did we lose anyone?\n")
+					return nil
+				},
+				Action: func(cCtx *cli.Context) error {
+					cCtx.Command.FullName()
+					cCtx.Command.HasName("wop")
+					cCtx.Command.Names()
+					cCtx.Command.VisibleFlags()
+					fmt.Fprintf(cCtx.App.Writer, "dodododododoodododddooooododododooo\n")
+					if cCtx.Bool("forever") {
+						cCtx.Command.Run(cCtx)
+					}
+					return nil
+				},
+				OnUsageError: func(cCtx *cli.Context, err error, isSubcommand bool) error {
+					fmt.Fprintf(cCtx.App.Writer, "for shame\n")
+					return err
+				},
+			},
+		},
+		Flags: []cli.Flag{
+			&cli.BoolFlag{Name: "fancy"},
+			&cli.BoolFlag{Value: true, Name: "fancier"},
+			&cli.DurationFlag{Name: "howlong", Aliases: []string{"H"}, Value: time.Second * 3},
+			&cli.Float64Flag{Name: "howmuch"},
+			&cli.GenericFlag{Name: "wat", Value: &genericType{}},
+			&cli.Int64Flag{Name: "longdistance"},
+			&cli.Int64SliceFlag{Name: "intervals"},
+			&cli.IntFlag{Name: "distance"},
+			&cli.IntSliceFlag{Name: "times"},
+			&cli.StringFlag{Name: "dance-move", Aliases: []string{"d"}},
+			&cli.StringSliceFlag{Name: "names", Aliases: []string{"N"}},
+			&cli.UintFlag{Name: "age"},
+			&cli.Uint64Flag{Name: "bigage"},
+		},
+		EnableBashCompletion: true,
+		HideHelp:             false,
+		HideVersion:          false,
+		BashComplete: func(cCtx *cli.Context) {
+			fmt.Fprintf(cCtx.App.Writer, "lipstick\nkiss\nme\nlipstick\nringo\n")
+		},
+		Before: func(cCtx *cli.Context) error {
+			fmt.Fprintf(cCtx.App.Writer, "HEEEERE GOES\n")
+			return nil
+		},
+		After: func(cCtx *cli.Context) error {
+			fmt.Fprintf(cCtx.App.Writer, "Phew!\n")
+			return nil
+		},
+		CommandNotFound: func(cCtx *cli.Context, command string) {
+			fmt.Fprintf(cCtx.App.Writer, "Thar be no %q here.\n", command)
+		},
+		OnUsageError: func(cCtx *cli.Context, err error, isSubcommand bool) error {
+			if isSubcommand {
+				return err
+			}
+
+			fmt.Fprintf(cCtx.App.Writer, "WRONG: %#v\n", err)
+			return nil
+		},
+		Action: func(cCtx *cli.Context) error {
+			cli.DefaultAppComplete(cCtx)
+			cli.HandleExitCoder(errors.New("not an exit coder, though"))
+			cli.ShowAppHelp(cCtx)
+			cli.ShowCommandCompletions(cCtx, "nope")
+			cli.ShowCommandHelp(cCtx, "also-nope")
+			cli.ShowCompletions(cCtx)
+			cli.ShowSubcommandHelp(cCtx)
+			cli.ShowVersion(cCtx)
+
+			fmt.Printf("%#v\n", cCtx.App.Command("doo"))
+			if cCtx.Bool("infinite") {
+				cCtx.App.Run([]string{"app", "doo", "wop"})
+			}
+
+			if cCtx.Bool("forevar") {
+				cCtx.App.RunAsSubcommand(cCtx)
+			}
+			cCtx.App.Setup()
+			fmt.Printf("%#v\n", cCtx.App.VisibleCategories())
+			fmt.Printf("%#v\n", cCtx.App.VisibleCommands())
+			fmt.Printf("%#v\n", cCtx.App.VisibleFlags())
+
+			fmt.Printf("%#v\n", cCtx.Args().First())
+			if cCtx.Args().Len() > 0 {
+				fmt.Printf("%#v\n", cCtx.Args().Get(1))
+			}
+			fmt.Printf("%#v\n", cCtx.Args().Present())
+			fmt.Printf("%#v\n", cCtx.Args().Tail())
+
+			set := flag.NewFlagSet("contrive", 0)
+			nc := cli.NewContext(cCtx.App, set, cCtx)
+
+			fmt.Printf("%#v\n", nc.Args())
+			fmt.Printf("%#v\n", nc.Bool("nope"))
+			fmt.Printf("%#v\n", !nc.Bool("nerp"))
+			fmt.Printf("%#v\n", nc.Duration("howlong"))
+			fmt.Printf("%#v\n", nc.Float64("hay"))
+			fmt.Printf("%#v\n", nc.Generic("bloop"))
+			fmt.Printf("%#v\n", nc.Int64("bonk"))
+			fmt.Printf("%#v\n", nc.Int64Slice("burnks"))
+			fmt.Printf("%#v\n", nc.Int("bips"))
+			fmt.Printf("%#v\n", nc.IntSlice("blups"))
+			fmt.Printf("%#v\n", nc.String("snurt"))
+			fmt.Printf("%#v\n", nc.StringSlice("snurkles"))
+			fmt.Printf("%#v\n", nc.Uint("flub"))
+			fmt.Printf("%#v\n", nc.Uint64("florb"))
+
+			fmt.Printf("%#v\n", nc.FlagNames())
+			fmt.Printf("%#v\n", nc.IsSet("wat"))
+			fmt.Printf("%#v\n", nc.Set("wat", "nope"))
+			fmt.Printf("%#v\n", nc.NArg())
+			fmt.Printf("%#v\n", nc.NumFlags())
+			fmt.Printf("%#v\n", nc.Lineage()[1])
+			nc.Set("wat", "also-nope")
+
+			ec := cli.Exit("ohwell", 86)
+			fmt.Fprintf(cCtx.App.Writer, "%d", ec.ExitCode())
+			fmt.Printf("made it!\n")
+			return ec
+		},
+		Metadata: map[string]interface{}{
+			"layers":          "many",
+			"explicable":      false,
+			"whatever-values": 19.99,
+		},
+	}
+
+	if os.Getenv("HEXY") != "" {
+		app.Writer = &hexWriter{}
+		app.ErrWriter = &hexWriter{}
+	}
+
+	app.Run(os.Args)
+}
+
+func wopAction(cCtx *cli.Context) error {
+	fmt.Fprintf(cCtx.App.Writer, ":wave: over here, eh\n")
+	return nil
+}
+```
diff --git a/docs/v2/examples/generated-help-text.md b/docs/v2/examples/generated-help-text.md
new file mode 100644
index 0000000..f733b96
--- /dev/null
+++ b/docs/v2/examples/generated-help-text.md
@@ -0,0 +1,101 @@
+---
+tags:
+  - v2
+search:
+  boost: 2
+---
+
+The default help flag (`-h/--help`) is defined as `cli.HelpFlag` and is checked
+by the cli internals in order to print generated help text for the app, command,
+or subcommand, and break execution.
+
+#### Customization
+
+All of the help text generation may be customized, and at multiple levels.  The
+templates are exposed as variables `AppHelpTemplate`, `CommandHelpTemplate`, and
+`SubcommandHelpTemplate` which may be reassigned or augmented, and full override
+is possible by assigning a compatible func to the `cli.HelpPrinter` variable,
+e.g.:
+
+<!-- {
+  "output": "Ha HA.  I pwnd the help!!1"
+} -->
+```go
+package main
+
+import (
+	"fmt"
+	"io"
+	"os"
+
+	"github.com/urfave/cli/v2"
+)
+
+func main() {
+	// EXAMPLE: Append to an existing template
+	cli.AppHelpTemplate = fmt.Sprintf(`%s
+
+WEBSITE: http://awesometown.example.com
+
+SUPPORT: support@awesometown.example.com
+
+`, cli.AppHelpTemplate)
+
+	// EXAMPLE: Override a template
+	cli.AppHelpTemplate = `NAME:
+   {{.Name}} - {{.Usage}}
+USAGE:
+   {{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}
+   {{if len .Authors}}
+AUTHOR:
+   {{range .Authors}}{{ . }}{{end}}
+   {{end}}{{if .Commands}}
+COMMANDS:
+{{range .Commands}}{{if not .HideHelp}}   {{join .Names ", "}}{{ "\t"}}{{.Usage}}{{ "\n" }}{{end}}{{end}}{{end}}{{if .VisibleFlags}}
+GLOBAL OPTIONS:
+   {{range .VisibleFlags}}{{.}}
+   {{end}}{{end}}{{if .Copyright }}
+COPYRIGHT:
+   {{.Copyright}}
+   {{end}}{{if .Version}}
+VERSION:
+   {{.Version}}
+   {{end}}
+`
+
+	// EXAMPLE: Replace the `HelpPrinter` func
+	cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) {
+		fmt.Println("Ha HA.  I pwnd the help!!1")
+	}
+
+	(&cli.App{}).Run(os.Args)
+}
+```
+
+The default flag may be customized to something other than `-h/--help` by
+setting `cli.HelpFlag`, e.g.:
+
+<!-- {
+  "args": ["&#45;&#45halp"],
+  "output": "haaaaalp.*HALP"
+} -->
+```go
+package main
+
+import (
+	"os"
+
+	"github.com/urfave/cli/v2"
+)
+
+func main() {
+	cli.HelpFlag = &cli.BoolFlag{
+		Name:    "haaaaalp",
+		Aliases: []string{"halp"},
+		Usage:   "HALP",
+		EnvVars: []string{"SHOW_HALP", "HALPPLZ"},
+	}
+
+	(&cli.App{}).Run(os.Args)
+}
+```
diff --git a/docs/v2/examples/greet.md b/docs/v2/examples/greet.md
new file mode 100644
index 0000000..aee4b0c
--- /dev/null
+++ b/docs/v2/examples/greet.md
@@ -0,0 +1,73 @@
+---
+tags:
+  - v2
+search:
+  boost: 2
+---
+
+Being a programmer can be a lonely job. Thankfully by the power of automation
+that is not the case! Let's create a greeter app to fend off our demons of
+loneliness!
+
+Start by creating a directory named `greet`, and within it, add a file,
+`greet.go` with the following code in it:
+
+<!-- {
+  "output": "Hello friend!"
+} -->
+```go
+package main
+
+import (
+	"fmt"
+	"log"
+	"os"
+
+	"github.com/urfave/cli/v2"
+)
+
+func main() {
+	app := &cli.App{
+		Name:  "greet",
+		Usage: "fight the loneliness!",
+		Action: func(*cli.Context) error {
+			fmt.Println("Hello friend!")
+			return nil
+		},
+	}
+
+	if err := app.Run(os.Args); err != nil {
+		log.Fatal(err)
+	}
+}
+```
+
+Install our command to the `$GOPATH/bin` directory:
+
+```sh-session
+$ go install
+```
+
+Finally run our new command:
+
+```sh-session
+$ greet
+Hello friend!
+```
+
+cli also generates neat help text:
+
+```sh-session
+$ greet help
+NAME:
+    greet - fight the loneliness!
+
+USAGE:
+    greet [global options] command [command options] [arguments...]
+
+COMMANDS:
+    help, h  Shows a list of commands or help for one command
+
+GLOBAL OPTIONS
+    --help, -h  show help (default: false)
+```
diff --git a/docs/v2/examples/subcommands-categories.md b/docs/v2/examples/subcommands-categories.md
new file mode 100644
index 0000000..5f0b8d2
--- /dev/null
+++ b/docs/v2/examples/subcommands-categories.md
@@ -0,0 +1,54 @@
+---
+tags:
+  - v2
+search:
+  boost: 2
+---
+
+For additional organization in apps that have many subcommands, you can
+associate a category for each command to group them together in the help
+output, e.g.:
+
+```go
+package main
+
+import (
+	"log"
+	"os"
+
+	"github.com/urfave/cli/v2"
+)
+
+func main() {
+	app := &cli.App{
+		Commands: []*cli.Command{
+			{
+				Name: "noop",
+			},
+			{
+				Name:     "add",
+				Category: "template",
+			},
+			{
+				Name:     "remove",
+				Category: "template",
+			},
+		},
+	}
+
+	if err := app.Run(os.Args); err != nil {
+		log.Fatal(err)
+	}
+}
+```
+
+Will include:
+
+```
+COMMANDS:
+  noop
+
+  Template actions:
+    add
+    remove
+```
diff --git a/docs/v2/examples/subcommands.md b/docs/v2/examples/subcommands.md
new file mode 100644
index 0000000..b054622
--- /dev/null
+++ b/docs/v2/examples/subcommands.md
@@ -0,0 +1,76 @@
+---
+tags:
+  - v2
+search:
+  boost: 2
+---
+
+Subcommands can be defined for a more git-like command line app.
+
+<!-- {
+  "args": ["template", "add"],
+  "output": "new task template: .+"
+} -->
+```go
+package main
+
+import (
+	"fmt"
+	"log"
+	"os"
+
+	"github.com/urfave/cli/v2"
+)
+
+func main() {
+	app := &cli.App{
+		Commands: []*cli.Command{
+			{
+				Name:    "add",
+				Aliases: []string{"a"},
+				Usage:   "add a task to the list",
+				Action: func(cCtx *cli.Context) error {
+					fmt.Println("added task: ", cCtx.Args().First())
+					return nil
+				},
+			},
+			{
+				Name:    "complete",
+				Aliases: []string{"c"},
+				Usage:   "complete a task on the list",
+				Action: func(cCtx *cli.Context) error {
+					fmt.Println("completed task: ", cCtx.Args().First())
+					return nil
+				},
+			},
+			{
+				Name:    "template",
+				Aliases: []string{"t"},
+				Usage:   "options for task templates",
+				Subcommands: []*cli.Command{
+					{
+						Name:  "add",
+						Usage: "add a new template",
+						Action: func(cCtx *cli.Context) error {
+							fmt.Println("new task template: ", cCtx.Args().First())
+							return nil
+						},
+					},
+					{
+						Name:  "remove",
+						Usage: "remove an existing template",
+						Action: func(cCtx *cli.Context) error {
+							fmt.Println("removed task template: ", cCtx.Args().First())
+							return nil
+						},
+					},
+				},
+			},
+		},
+	}
+
+	if err := app.Run(os.Args); err != nil {
+		log.Fatal(err)
+	}
+}
+```
diff --git a/docs/v2/examples/suggestions.md b/docs/v2/examples/suggestions.md
new file mode 100644
index 0000000..9a0cd3f
--- /dev/null
+++ b/docs/v2/examples/suggestions.md
@@ -0,0 +1,11 @@
+---
+tags:
+  - v2
+search:
+  boost: 2
+---
+
+To enable flag and command suggestions, set `app.Suggest = true`. If the suggest
+feature is enabled, then the help output of the corresponding command will
+provide an appropriate suggestion for the provided flag or subcommand if
+available.
diff --git a/docs/v2/examples/timestamp-flag.md b/docs/v2/examples/timestamp-flag.md
new file mode 100644
index 0000000..4e19adf
--- /dev/null
+++ b/docs/v2/examples/timestamp-flag.md
@@ -0,0 +1,64 @@
+---
+tags:
+  - v2
+search:
+  boost: 2
+---
+
+Using the timestamp flag is simple. Please refer to
+[`time.Parse`](https://golang.org/pkg/time/#example_Parse) to get possible
+formats.
+
+<!-- {
+  "args": ["&#45;&#45;meeting", "2019-08-12T15:04:05"],
+  "output": "2019\\-08\\-12 15\\:04\\:05 \\+0000 UTC"
+} -->
+```go
+package main
+
+import (
+	"fmt"
+	"log"
+	"os"
+
+	"github.com/urfave/cli/v2"
+)
+
+func main() {
+	app := &cli.App{
+		Flags: []cli.Flag{
+			&cli.TimestampFlag{Name: "meeting", Layout: "2006-01-02T15:04:05"},
+		},
+		Action: func(cCtx *cli.Context) error {
+			fmt.Printf("%s", cCtx.Timestamp("meeting").String())
+			return nil
+		},
+	}
+
+	if err := app.Run(os.Args); err != nil {
+		log.Fatal(err)
+	}
+}
+```
+
+In this example the flag could be used like this:
+
+```sh-session
+$ myapp --meeting 2019-08-12T15:04:05
+```
+
+When the layout doesn't contain timezones, timestamp will render with UTC. To
+change behavior, a default timezone can be provided with flag definition:
+
+```go
+app := &cli.App{
+	Flags: []cli.Flag{
+		&cli.TimestampFlag{Name: "meeting", Layout: "2006-01-02T15:04:05", Timezone: time.Local},
+	},
+}
+```
+
+(time.Local contains the system's local time zone.)
+
+Side note: quotes may be necessary around the date depending on your layout (if
+you have spaces for instance)
diff --git a/docs/v2/examples/version-flag.md b/docs/v2/examples/version-flag.md
new file mode 100644
index 0000000..af0508c
--- /dev/null
+++ b/docs/v2/examples/version-flag.md
@@ -0,0 +1,77 @@
+---
+tags:
+  - v2
+search:
+  boost: 2
+---
+
+The default version flag (`-v/--version`) is defined as `cli.VersionFlag`, which
+is checked by the cli internals in order to print the `App.Version` via
+`cli.VersionPrinter` and break execution.
+
+#### Customization
+
+The default flag may be customized to something other than `-v/--version` by
+setting `cli.VersionFlag`, e.g.:
+
+<!-- {
+  "args": ["&#45;&#45print-version"],
+  "output": "partay version v19\\.99\\.0"
+} -->
+```go
+package main
+
+import (
+	"os"
+
+	"github.com/urfave/cli/v2"
+)
+
+func main() {
+	cli.VersionFlag = &cli.BoolFlag{
+		Name:    "print-version",
+		Aliases: []string{"V"},
+		Usage:   "print only the version",
+	}
+
+	app := &cli.App{
+		Name:    "partay",
+		Version: "v19.99.0",
+	}
+	app.Run(os.Args)
+}
+```
+
+Alternatively, the version printer at `cli.VersionPrinter` may be overridden,
+e.g.:
+
+<!-- {
+  "args": ["&#45;&#45version"],
+  "output": "version=v19\\.99\\.0 revision=fafafaf"
+} -->
+```go
+package main
+
+import (
+	"fmt"
+	"os"
+
+	"github.com/urfave/cli/v2"
+)
+
+var (
+	Revision = "fafafaf"
+)
+
+func main() {
+	cli.VersionPrinter = func(cCtx *cli.Context) {
+		fmt.Printf("version=%s revision=%s\n", cCtx.App.Version, Revision)
+	}
+
+	app := &cli.App{
+		Name:    "partay",
+		Version: "v19.99.0",
+	}
+	app.Run(os.Args)
+}
+```
diff --git a/docs/v2/getting-started.md b/docs/v2/getting-started.md
new file mode 100644
index 0000000..1d631cf
--- /dev/null
+++ b/docs/v2/getting-started.md
@@ -0,0 +1,63 @@
+---
+tags:
+  - v2
+search:
+  boost: 2
+---
+
+One of the philosophies behind cli is that an API should be playful and full of
+discovery. So a cli app can be as little as one line of code in `main()`.
+
+<!-- {
+  "args": ["&#45;&#45;help"],
+  "output": "A new cli application"
+} -->
+```go
+package main
+
+import (
+	"os"
+
+	"github.com/urfave/cli/v2"
+)
+
+func main() {
+	(&cli.App{}).Run(os.Args)
+}
+```
+
+This app will run and show help text, but is not very useful. Let's give an
+action to execute and some help documentation:
+
+<!-- {
+  "output": "boom! I say!"
+} -->
+```go
+package main
+
+import (
+	"fmt"
+	"log"
+	"os"
+
+	"github.com/urfave/cli/v2"
+)
+
+func main() {
+	app := &cli.App{
+		Name:  "boom",
+		Usage: "make an explosive entrance",
+		Action: func(*cli.Context) error {
+			fmt.Println("boom! I say!")
+			return nil
+		},
+	}
+
+	if err := app.Run(os.Args); err != nil {
+		log.Fatal(err)
+	}
+}
+```
+
+Running this already gives you a ton of functionality, plus support for things
+like subcommands and flags, which are covered below.
diff --git a/docs/v2/index.md b/docs/v2/index.md
deleted file mode 120000
index 9d0493a..0000000
--- a/docs/v2/index.md
+++ /dev/null
@@ -1,1 +0,0 @@
-manual.md
\ No newline at end of file
diff --git a/docs/v2/manual.md b/docs/v2/manual.md
deleted file mode 100644
index 475e047..0000000
--- a/docs/v2/manual.md
+++ /dev/null
@@ -1,1703 +0,0 @@
-# v2 guide
-
-## Getting Started
-
-One of the philosophies behind cli is that an API should be playful and full of
-discovery. So a cli app can be as little as one line of code in `main()`.
-
-<!-- {
-  "args": ["&#45;&#45;help"],
-  "output": "A new cli application"
-} -->
-```go
-package main
-
-import (
-	"os"
-
-	"github.com/urfave/cli/v2"
-)
-
-func main() {
-	(&cli.App{}).Run(os.Args)
-}
-```
-
-This app will run and show help text, but is not very useful. Let's give an
-action to execute and some help documentation:
-
-<!-- {
-  "output": "boom! I say!"
-} -->
-```go
-package main
-
-import (
-	"fmt"
-	"log"
-	"os"
-
-	"github.com/urfave/cli/v2"
-)
-
-func main() {
-	app := &cli.App{
-		Name:  "boom",
-		Usage: "make an explosive entrance",
-		Action: func(*cli.Context) error {
-			fmt.Println("boom! I say!")
-			return nil
-		},
-	}
-
-	if err := app.Run(os.Args); err != nil {
-		log.Fatal(err)
-	}
-}
-```
-
-Running this already gives you a ton of functionality, plus support for things
-like subcommands and flags, which are covered below.
-
-## Examples
-
-Being a programmer can be a lonely job. Thankfully by the power of automation
-that is not the case! Let's create a greeter app to fend off our demons of
-loneliness!
-
-Start by creating a directory named `greet`, and within it, add a file,
-`greet.go` with the following code in it:
-
-<!-- {
-  "output": "Hello friend!"
-} -->
-```go
-package main
-
-import (
-	"fmt"
-	"log"
-	"os"
-
-	"github.com/urfave/cli/v2"
-)
-
-func main() {
-	app := &cli.App{
-		Name:  "greet",
-		Usage: "fight the loneliness!",
-		Action: func(*cli.Context) error {
-			fmt.Println("Hello friend!")
-			return nil
-		},
-	}
-
-	if err := app.Run(os.Args); err != nil {
-		log.Fatal(err)
-	}
-}
-```
-
-Install our command to the `$GOPATH/bin` directory:
-
-```sh-session
-$ go install
-```
-
-Finally run our new command:
-
-```sh-session
-$ greet
-Hello friend!
-```
-
-cli also generates neat help text:
-
-```sh-session
-$ greet help
-NAME:
-    greet - fight the loneliness!
-
-USAGE:
-    greet [global options] command [command options] [arguments...]
-
-COMMANDS:
-    help, h  Shows a list of commands or help for one command
-
-GLOBAL OPTIONS
-    --help, -h  show help (default: false)
-```
-
-### Arguments
-
-You can lookup arguments by calling the `Args` function on `cli.Context`, e.g.:
-
-<!-- {
-  "output": "Hello \""
-} -->
-```go
-package main
-
-import (
-	"fmt"
-	"log"
-	"os"
-
-	"github.com/urfave/cli/v2"
-)
-
-func main() {
-	app := &cli.App{
-		Action: func(cCtx *cli.Context) error {
-			fmt.Printf("Hello %q", cCtx.Args().Get(0))
-			return nil
-		},
-	}
-
-	if err := app.Run(os.Args); err != nil {
-		log.Fatal(err)
-	}
-}
-```
-
-### Flags
-
-Setting and querying flags is simple.
-
-<!-- {
-  "output": "Hello Nefertiti"
-} -->
-```go
-package main
-
-import (
-	"fmt"
-	"log"
-	"os"
-
-	"github.com/urfave/cli/v2"
-)
-
-func main() {
-	app := &cli.App{
-		Flags: []cli.Flag{
-			&cli.StringFlag{
-				Name:  "lang",
-				Value: "english",
-				Usage: "language for the greeting",
-			},
-		},
-		Action: func(cCtx *cli.Context) error {
-			name := "Nefertiti"
-			if cCtx.NArg() > 0 {
-				name = cCtx.Args().Get(0)
-			}
-			if cCtx.String("lang") == "spanish" {
-				fmt.Println("Hola", name)
-			} else {
-				fmt.Println("Hello", name)
-			}
-			return nil
-		},
-	}
-
-	if err := app.Run(os.Args); err != nil {
-		log.Fatal(err)
-	}
-}
-```
-
-You can also set a destination variable for a flag, to which the content will be
-scanned.
-
-<!-- {
-  "output": "Hello someone"
-} -->
-```go
-package main
-
-import (
-	"fmt"
-	"log"
-	"os"
-
-	"github.com/urfave/cli/v2"
-)
-
-func main() {
-	var language string
-
-	app := &cli.App{
-		Flags: []cli.Flag{
-			&cli.StringFlag{
-				Name:        "lang",
-				Value:       "english",
-				Usage:       "language for the greeting",
-				Destination: &language,
-			},
-		},
-		Action: func(cCtx *cli.Context) error {
-			name := "someone"
-			if cCtx.NArg() > 0 {
-				name = cCtx.Args().Get(0)
-			}
-			if language == "spanish" {
-				fmt.Println("Hola", name)
-			} else {
-				fmt.Println("Hello", name)
-			}
-			return nil
-		},
-	}
-
-	if err := app.Run(os.Args); err != nil {
-		log.Fatal(err)
-	}
-}
-```
-
-See full list of flags at https://pkg.go.dev/github.com/urfave/cli/v2
-
-#### Placeholder Values
-
-Sometimes it's useful to specify a flag's value within the usage string itself.
-Such placeholders are indicated with back quotes.
-
-For example this:
-
-<!-- {
-  "args": ["&#45;&#45;help"],
-  "output": "&#45;&#45;config FILE, &#45;c FILE"
-} -->
-```go
-package main
-
-import (
-	"log"
-	"os"
-
-	"github.com/urfave/cli/v2"
-)
-
-func main() {
-	app := &cli.App{
-		Flags: []cli.Flag{
-			&cli.StringFlag{
-				Name:    "config",
-				Aliases: []string{"c"},
-				Usage:   "Load configuration from `FILE`",
-			},
-		},
-	}
-
-	if err := app.Run(os.Args); err != nil {
-		log.Fatal(err)
-	}
-}
-```
-
-Will result in help output like:
-
-```
---config FILE, -c FILE   Load configuration from FILE
-```
-
-Note that only the first placeholder is used. Subsequent back-quoted words will
-be left as-is.
-
-#### Alternate Names
-
-You can set alternate (or short) names for flags by providing a comma-delimited
-list for the `Name`. e.g.
-
-<!-- {
-  "args": ["&#45;&#45;help"],
-  "output": "&#45;&#45;lang value, &#45;l value.*language for the greeting.*default: \"english\""
-} -->
-```go
-package main
-
-import (
-	"log"
-	"os"
-
-	"github.com/urfave/cli/v2"
-)
-
-func main() {
-	app := &cli.App{
-		Flags: []cli.Flag{
-			&cli.StringFlag{
-				Name:    "lang",
-				Aliases: []string{"l"},
-				Value:   "english",
-				Usage:   "language for the greeting",
-			},
-		},
-	}
-
-	if err := app.Run(os.Args); err != nil {
-		log.Fatal(err)
-	}
-}
-```
-
-That flag can then be set with `--lang spanish` or `-l spanish`. Note that
-giving two different forms of the same flag in the same command invocation is an
-error.
-
-#### Ordering
-
-Flags for the application and commands are shown in the order they are defined.
-However, it's possible to sort them from outside this library by using `FlagsByName`
-or `CommandsByName` with `sort`.
-
-For example this:
-
-<!-- {
-  "args": ["&#45;&#45;help"],
-  "output": ".*Load configuration from FILE\n.*\n.*Language for the greeting.*"
-} -->
-```go
-package main
-
-import (
-	"log"
-	"os"
-	"sort"
-
-	"github.com/urfave/cli/v2"
-)
-
-func main() {
-	app := &cli.App{
-		Flags: []cli.Flag{
-			&cli.StringFlag{
-				Name:    "lang",
-				Aliases: []string{"l"},
-				Value:   "english",
-				Usage:   "Language for the greeting",
-			},
-			&cli.StringFlag{
-				Name:    "config",
-				Aliases: []string{"c"},
-				Usage:   "Load configuration from `FILE`",
-			},
-		},
-		Commands: []*cli.Command{
-			{
-				Name:    "complete",
-				Aliases: []string{"c"},
-				Usage:   "complete a task on the list",
-				Action: func(*cli.Context) error {
-					return nil
-				},
-			},
-			{
-				Name:    "add",
-				Aliases: []string{"a"},
-				Usage:   "add a task to the list",
-				Action: func(*cli.Context) error {
-					return nil
-				},
-			},
-		},
-	}
-
-	sort.Sort(cli.FlagsByName(app.Flags))
-	sort.Sort(cli.CommandsByName(app.Commands))
-
-	if err := app.Run(os.Args); err != nil {
-		log.Fatal(err)
-	}
-}
-```
-
-Will result in help output like:
-
-```
---config FILE, -c FILE  Load configuration from FILE
---lang value, -l value  Language for the greeting (default: "english")
-```
-
-#### Values from the Environment
-
-You can also have the default value set from the environment via `EnvVars`.  e.g.
-
-<!-- {
-  "args": ["&#45;&#45;help"],
-  "output": "language for the greeting.*APP_LANG"
-} -->
-```go
-package main
-
-import (
-	"log"
-	"os"
-
-	"github.com/urfave/cli/v2"
-)
-
-func main() {
-	app := &cli.App{
-		Flags: []cli.Flag{
-			&cli.StringFlag{
-				Name:    "lang",
-				Aliases: []string{"l"},
-				Value:   "english",
-				Usage:   "language for the greeting",
-				EnvVars: []string{"APP_LANG"},
-			},
-		},
-	}
-
-	if err := app.Run(os.Args); err != nil {
-		log.Fatal(err)
-	}
-}
-```
-
-If `EnvVars` contains more than one string, the first environment variable that
-resolves is used.
-
-<!-- {
-  "args": ["&#45;&#45;help"],
-  "output": "language for the greeting.*LEGACY_COMPAT_LANG.*APP_LANG.*LANG"
-} -->
-```go
-package main
-
-import (
-	"log"
-	"os"
-
-	"github.com/urfave/cli/v2"
-)
-
-func main() {
-	app := &cli.App{
-		Flags: []cli.Flag{
-			&cli.StringFlag{
-				Name:    "lang",
-				Aliases: []string{"l"},
-				Value:   "english",
-				Usage:   "language for the greeting",
-				EnvVars: []string{"LEGACY_COMPAT_LANG", "APP_LANG", "LANG"},
-			},
-		},
-	}
-
-	if err := app.Run(os.Args); err != nil {
-		log.Fatal(err)
-	}
-}
-```
-
-#### Values from files
-
-You can also have the default value set from file via `FilePath`.  e.g.
-
-<!-- {
-  "args": ["&#45;&#45;help"],
-  "output": "password for the mysql database"
-} -->
-```go
-package main
-
-import (
-	"log"
-	"os"
-
-	"github.com/urfave/cli/v2"
-)
-
-func main() {
-	app := &cli.App{
-		Flags: []cli.Flag{
-			&cli.StringFlag{
-				Name:     "password",
-				Aliases:  []string{"p"},
-				Usage:    "password for the mysql database",
-				FilePath: "/etc/mysql/password",
-			},
-		},
-	}
-
-	if err := app.Run(os.Args); err != nil {
-		log.Fatal(err)
-	}
-}
-```
-
-Note that default values set from file (e.g. `FilePath`) take precedence over
-default values set from the environment (e.g. `EnvVar`).
-
-#### Values from alternate input sources (YAML, TOML, and others)
-
-There is a separate package altsrc that adds support for getting flag values
-from other file input sources.
-
-Currently supported input source formats:
-
-- YAML
-- JSON
-- TOML
-
-In order to get values for a flag from an alternate input source the following
-code would be added to wrap an existing cli.Flag like below:
-
-```go
-  // --- >8 ---
-  altsrc.NewIntFlag(&cli.IntFlag{Name: "test"})
-```
-
-Initialization must also occur for these flags. Below is an example initializing
-getting data from a yaml file below.
-
-```go
-  // --- >8 ---
-  command.Before = altsrc.InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
-```
-
-The code above will use the "load" string as a flag name to get the file name of
-a yaml file from the cli.Context.  It will then use that file name to initialize
-the yaml input source for any flags that are defined on that command.  As a note
-the "load" flag used would also have to be defined on the command flags in order
-for this code snippet to work.
-
-Currently only YAML, JSON, and TOML files are supported but developers can add
-support for other input sources by implementing the altsrc.InputSourceContext
-for their given sources.
-
-Here is a more complete sample of a command using YAML support:
-
-<!-- {
-  "args": ["&#45;&#45;help"],
-  "output": "&#45&#45;test value.*default: 0"
-} -->
-```go
-package main
-
-import (
-	"fmt"
-	"os"
-
-	"github.com/urfave/cli/v2"
-	"github.com/urfave/cli/v2/altsrc"
-)
-
-func main() {
-	flags := []cli.Flag{
-		altsrc.NewIntFlag(&cli.IntFlag{Name: "test"}),
-		&cli.StringFlag{Name: "load"},
-	}
-
-	app := &cli.App{
-		Action: func(*cli.Context) error {
-			fmt.Println("--test value.*default: 0")
-			return nil
-		},
-		Before: altsrc.InitInputSourceWithContext(flags, altsrc.NewYamlSourceFromFlagFunc("load")),
-		Flags:  flags,
-	}
-
-	app.Run(os.Args)
-}
-```
-
-#### Required Flags
-
-You can make a flag required by setting the `Required` field to `true`. If a user
-does not provide a required flag, they will be shown an error message.
-
-Take for example this app that requires the `lang` flag:
-
-<!-- {
-  "error": "Required flag \"lang\" not set"
-} -->
-```go
-package main
-
-import (
-	"fmt"
-	"log"
-	"os"
-
-	"github.com/urfave/cli/v2"
-)
-
-func main() {
-	app := &cli.App{
-		Flags: []cli.Flag{
-			&cli.StringFlag{
-				Name:     "lang",
-				Value:    "english",
-				Usage:    "language for the greeting",
-				Required: true,
-			},
-		},
-		Action: func(cCtx *cli.Context) error {
-			output := "Hello"
-			if cCtx.String("lang") == "spanish" {
-				output = "Hola"
-			}
-			fmt.Println(output)
-			return nil
-		},
-	}
-
-	if err := app.Run(os.Args); err != nil {
-		log.Fatal(err)
-	}
-}
-```
-
-If the app is run without the `lang` flag, the user will see the following message
-
-```
-Required flag "lang" not set
-```
-
-#### Default Values for help output
-
-Sometimes it's useful to specify a flag's default help-text value within the
-flag declaration. This can be useful if the default value for a flag is a
-computed value. The default value can be set via the `DefaultText` struct field.
-
-For example this:
-
-<!-- {
-  "args": ["&#45;&#45;help"],
-  "output": "&#45;&#45;port value"
-} -->
-```go
-package main
-
-import (
-	"log"
-	"os"
-
-	"github.com/urfave/cli/v2"
-)
-
-func main() {
-	app := &cli.App{
-		Flags: []cli.Flag{
-			&cli.IntFlag{
-				Name:        "port",
-				Usage:       "Use a randomized port",
-				Value:       0,
-				DefaultText: "random",
-			},
-		},
-	}
-
-	if err := app.Run(os.Args); err != nil {
-		log.Fatal(err)
-	}
-}
-```
-
-Will result in help output like:
-
-```
---port value  Use a randomized port (default: random)
-```
-
-#### Precedence
-
-The precedence for flag value sources is as follows (highest to lowest):
-
-0. Command line flag value from user
-0. Environment variable (if specified)
-0. Configuration file (if specified)
-0. Default defined on the flag
-
-### Subcommands
-
-Subcommands can be defined for a more git-like command line app.
-
-<!-- {
-  "args": ["template", "add"],
-  "output": "new task template: .+"
-} -->
-```go
-package main
-
-import (
-	"fmt"
-	"log"
-	"os"
-
-	"github.com/urfave/cli/v2"
-)
-
-func main() {
-	app := &cli.App{
-		Commands: []*cli.Command{
-			{
-				Name:    "add",
-				Aliases: []string{"a"},
-				Usage:   "add a task to the list",
-				Action: func(cCtx *cli.Context) error {
-					fmt.Println("added task: ", cCtx.Args().First())
-					return nil
-				},
-			},
-			{
-				Name:    "complete",
-				Aliases: []string{"c"},
-				Usage:   "complete a task on the list",
-				Action: func(cCtx *cli.Context) error {
-					fmt.Println("completed task: ", cCtx.Args().First())
-					return nil
-				},
-			},
-			{
-				Name:    "template",
-				Aliases: []string{"t"},
-				Usage:   "options for task templates",
-				Subcommands: []*cli.Command{
-					{
-						Name:  "add",
-						Usage: "add a new template",
-						Action: func(cCtx *cli.Context) error {
-							fmt.Println("new task template: ", cCtx.Args().First())
-							return nil
-						},
-					},
-					{
-						Name:  "remove",
-						Usage: "remove an existing template",
-						Action: func(cCtx *cli.Context) error {
-							fmt.Println("removed task template: ", cCtx.Args().First())
-							return nil
-						},
-					},
-				},
-			},
-		},
-	}
-
-	if err := app.Run(os.Args); err != nil {
-		log.Fatal(err)
-	}
-}
-```
-
-### Subcommands categories
-
-For additional organization in apps that have many subcommands, you can
-associate a category for each command to group them together in the help
-output, e.g.:
-
-```go
-package main
-
-import (
-	"log"
-	"os"
-
-	"github.com/urfave/cli/v2"
-)
-
-func main() {
-	app := &cli.App{
-		Commands: []*cli.Command{
-			{
-				Name: "noop",
-			},
-			{
-				Name:     "add",
-				Category: "template",
-			},
-			{
-				Name:     "remove",
-				Category: "template",
-			},
-		},
-	}
-
-	if err := app.Run(os.Args); err != nil {
-		log.Fatal(err)
-	}
-}
-```
-
-Will include:
-
-```
-COMMANDS:
-  noop
-
-  Template actions:
-    add
-    remove
-```
-
-### Exit code
-
-Calling `App.Run` will not automatically call `os.Exit`, which means that by
-default the exit code will "fall through" to being `0`.  An explicit exit code
-may be set by returning a non-nil error that fulfills `cli.ExitCoder`, *or* a
-`cli.MultiError` that includes an error that fulfills `cli.ExitCoder`, e.g.:
-<!-- {
-  "error": "Ginger croutons are not in the soup"
-} -->
-```go
-package main
-
-import (
-	"log"
-	"os"
-
-	"github.com/urfave/cli/v2"
-)
-
-func main() {
-	app := &cli.App{
-		Flags: []cli.Flag{
-			&cli.BoolFlag{
-				Name:  "ginger-crouton",
-				Usage: "is it in the soup?",
-			},
-		},
-		Action: func(ctx *cli.Context) error {
-			if !ctx.Bool("ginger-crouton") {
-				return cli.Exit("Ginger croutons are not in the soup", 86)
-			}
-			return nil
-		},
-	}
-
-	if err := app.Run(os.Args); err != nil {
-		log.Fatal(err)
-	}
-}
-```
-
-### Combining short options
-
-Traditional use of options using their shortnames look like this:
-
-```sh-session
-$ cmd -s -o -m "Some message"
-```
-
-Suppose you want users to be able to combine options with their shortnames. This
-can be done using the `UseShortOptionHandling` bool in your app configuration,
-or for individual commands by attaching it to the command configuration. For
-example:
-
-<!-- {
-  "args": ["short", "&#45;som", "Some message"],
-  "output": "serve: true\noption: true\nmessage: Some message\n"
-} -->
-```go
-package main
-
-import (
-	"fmt"
-	"log"
-	"os"
-
-	"github.com/urfave/cli/v2"
-)
-
-func main() {
-	app := &cli.App{
-		UseShortOptionHandling: true,
-		Commands: []*cli.Command{
-			{
-				Name:  "short",
-				Usage: "complete a task on the list",
-				Flags: []cli.Flag{
-					&cli.BoolFlag{Name: "serve", Aliases: []string{"s"}},
-					&cli.BoolFlag{Name: "option", Aliases: []string{"o"}},
-					&cli.StringFlag{Name: "message", Aliases: []string{"m"}},
-				},
-				Action: func(cCtx *cli.Context) error {
-					fmt.Println("serve:", cCtx.Bool("serve"))
-					fmt.Println("option:", cCtx.Bool("option"))
-					fmt.Println("message:", cCtx.String("message"))
-					return nil
-				},
-			},
-		},
-	}
-
-	if err := app.Run(os.Args); err != nil {
-		log.Fatal(err)
-	}
-}
-```
-
-If your program has any number of bool flags such as `serve` and `option`, and
-optionally one non-bool flag `message`, with the short options of `-s`, `-o`,
-and `-m` respectively, setting `UseShortOptionHandling` will also support the
-following syntax:
-
-```sh-session
-$ cmd -som "Some message"
-```
-
-If you enable `UseShortOptionHandling`, then you must not use any flags that
-have a single leading `-` or this will result in failures. For example,
-`-option` can no longer be used. Flags with two leading dashes (such as
-`--options`) are still valid.
-
-### Bash Completion
-
-You can enable completion commands by setting the `EnableBashCompletion` flag on
-the `App` object to `true`.  By default, this setting will allow auto-completion
-for an app's subcommands, but you can write your own completion methods for the
-App or its subcommands as well.
-
-#### Default auto-completion
-
-```go
-package main
-
-import (
-	"fmt"
-	"log"
-	"os"
-
-	"github.com/urfave/cli/v2"
-)
-
-func main() {
-	app := &cli.App{
-		EnableBashCompletion: true,
-		Commands: []*cli.Command{
-			{
-				Name:    "add",
-				Aliases: []string{"a"},
-				Usage:   "add a task to the list",
-				Action: func(cCtx *cli.Context) error {
-					fmt.Println("added task: ", cCtx.Args().First())
-					return nil
-				},
-			},
-			{
-				Name:    "complete",
-				Aliases: []string{"c"},
-				Usage:   "complete a task on the list",
-				Action: func(cCtx *cli.Context) error {
-					fmt.Println("completed task: ", cCtx.Args().First())
-					return nil
-				},
-			},
-			{
-				Name:    "template",
-				Aliases: []string{"t"},
-				Usage:   "options for task templates",
-				Subcommands: []*cli.Command{
-					{
-						Name:  "add",
-						Usage: "add a new template",
-						Action: func(cCtx *cli.Context) error {
-							fmt.Println("new task template: ", cCtx.Args().First())
-							return nil
-						},
-					},
-					{
-						Name:  "remove",
-						Usage: "remove an existing template",
-						Action: func(cCtx *cli.Context) error {
-							fmt.Println("removed task template: ", cCtx.Args().First())
-							return nil
-						},
-					},
-				},
-			},
-		},
-	}
-
-	if err := app.Run(os.Args); err != nil {
-		log.Fatal(err)
-	}
-}
-```
-![](/docs/v2/images/default-bash-autocomplete.gif)
-
-#### Custom auto-completion
-<!-- {
-  "args": ["complete", "&#45;&#45;generate&#45;bash&#45;completion"],
-  "output": "laundry"
-} -->
-```go
-package main
-
-import (
-	"fmt"
-	"log"
-	"os"
-
-	"github.com/urfave/cli/v2"
-)
-
-func main() {
-	tasks := []string{"cook", "clean", "laundry", "eat", "sleep", "code"}
-
-	app := &cli.App{
-		EnableBashCompletion: true,
-		Commands: []*cli.Command{
-			{
-				Name:    "complete",
-				Aliases: []string{"c"},
-				Usage:   "complete a task on the list",
-				Action: func(cCtx *cli.Context) error {
-					fmt.Println("completed task: ", cCtx.Args().First())
-					return nil
-				},
-				BashComplete: func(cCtx *cli.Context) {
-					// This will complete if no args are passed
-					if cCtx.NArg() > 0 {
-						return
-					}
-					for _, t := range tasks {
-						fmt.Println(t)
-					}
-				},
-			},
-		},
-	}
-
-	if err := app.Run(os.Args); err != nil {
-		log.Fatal(err)
-	}
-}
-```
-![](/docs/v2/images/custom-bash-autocomplete.gif)
-
-#### Enabling
-
-To enable auto-completion for the current shell session, a bash script,
-`autocomplete/bash_autocomplete` is included in this repo.
-
-To use `autocomplete/bash_autocomplete` set an environment variable named `PROG`
-to the name of your program and then `source` the
-`autocomplete/bash_autocomplete` file.
-
-For example, if your cli program is called `myprogram`:
-
-```sh-session
-$ PROG=myprogram source path/to/cli/autocomplete/bash_autocomplete
-```
-
-Auto-completion is now enabled for the current shell, but will not persist into
-a new shell.
-
-#### Distribution and Persistent Autocompletion
-
-Copy `autocomplete/bash_autocomplete` into `/etc/bash_completion.d/` and rename
-it to the name of the program you wish to add autocomplete support for (or
-automatically install it there if you are distributing a package). Don't forget
-to source the file or restart your shell to activate the auto-completion.
-
-```sh-session
-$ sudo cp path/to/autocomplete/bash_autocomplete /etc/bash_completion.d/<myprogram>
-$ source /etc/bash_completion.d/<myprogram>
-```
-
-Alternatively, you can just document that users should `source` the generic
-`autocomplete/bash_autocomplete` and set `$PROG` within their bash configuration
-file, adding these lines:
-
-```sh-session
-$ PROG=<myprogram>
-$ source path/to/cli/autocomplete/bash_autocomplete
-```
-
-Keep in mind that if they are enabling auto-completion for more than one
-program, they will need to set `PROG` and source
-`autocomplete/bash_autocomplete` for each program, like so:
-
-```sh-session
-$ PROG=<program1>
-$ source path/to/cli/autocomplete/bash_autocomplete
-
-$ PROG=<program2>
-$ source path/to/cli/autocomplete/bash_autocomplete
-```
-
-#### Customization
-
-The default shell completion flag (`--generate-bash-completion`) is defined as
-`cli.EnableBashCompletion`, and may be redefined if desired, e.g.:
-
-<!-- {
-  "args": ["&#45;&#45;generate&#45;bash&#45;completion"],
-  "output": "wat\nhelp\nh"
-} -->
-```go
-package main
-
-import (
-	"log"
-	"os"
-
-	"github.com/urfave/cli/v2"
-)
-
-func main() {
-	app := &cli.App{
-		EnableBashCompletion: true,
-		Commands: []*cli.Command{
-			{
-				Name: "wat",
-			},
-		},
-	}
-
-	if err := app.Run(os.Args); err != nil {
-		log.Fatal(err)
-	}
-}
-```
-
-#### ZSH Support
-
-Auto-completion for ZSH is also supported using the
-`autocomplete/zsh_autocomplete` file included in this repo. One environment
-variable is used, `PROG`.  Set `PROG` to the program name as before, and then
-`source path/to/autocomplete/zsh_autocomplete`.  Adding the following lines to
-your ZSH configuration file (usually `.zshrc`) will allow the auto-completion to
-persist across new shells:
-
-```sh-session
-$ PROG=<myprogram>
-$ source path/to/autocomplete/zsh_autocomplete
-```
-
-#### ZSH default auto-complete example
-![](/docs/v2/images/default-zsh-autocomplete.gif)
-
-#### ZSH custom auto-complete example
-![](/docs/v2/images/custom-zsh-autocomplete.gif)
-
-#### PowerShell Support
-
-Auto-completion for PowerShell is also supported using the
-`autocomplete/powershell_autocomplete.ps1` file included in this repo.
-
-Rename the script to `<my program>.ps1` and move it anywhere in your file
-system.  The location of script does not matter, only the file name of the
-script has to match the your program's binary name.
-
-To activate it, enter:
-
-```powershell
-& path/to/autocomplete/<my program>.ps1
-```
-
-To persist across new shells, open the PowerShell profile (with `code $profile`
-or `notepad $profile`) and add the line:
-
-```powershell
-& path/to/autocomplete/<my program>.ps1
-```
-
-### Generated Help Text
-
-The default help flag (`-h/--help`) is defined as `cli.HelpFlag` and is checked
-by the cli internals in order to print generated help text for the app, command,
-or subcommand, and break execution.
-
-#### Customization
-
-All of the help text generation may be customized, and at multiple levels.  The
-templates are exposed as variables `AppHelpTemplate`, `CommandHelpTemplate`, and
-`SubcommandHelpTemplate` which may be reassigned or augmented, and full override
-is possible by assigning a compatible func to the `cli.HelpPrinter` variable,
-e.g.:
-
-<!-- {
-  "output": "Ha HA.  I pwnd the help!!1"
-} -->
-```go
-package main
-
-import (
-	"fmt"
-	"io"
-	"os"
-
-	"github.com/urfave/cli/v2"
-)
-
-func main() {
-	// EXAMPLE: Append to an existing template
-	cli.AppHelpTemplate = fmt.Sprintf(`%s
-
-WEBSITE: http://awesometown.example.com
-
-SUPPORT: support@awesometown.example.com
-
-`, cli.AppHelpTemplate)
-
-	// EXAMPLE: Override a template
-	cli.AppHelpTemplate = `NAME:
-   {{.Name}} - {{.Usage}}
-USAGE:
-   {{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}
-   {{if len .Authors}}
-AUTHOR:
-   {{range .Authors}}{{ . }}{{end}}
-   {{end}}{{if .Commands}}
-COMMANDS:
-{{range .Commands}}{{if not .HideHelp}}   {{join .Names ", "}}{{ "\t"}}{{.Usage}}{{ "\n" }}{{end}}{{end}}{{end}}{{if .VisibleFlags}}
-GLOBAL OPTIONS:
-   {{range .VisibleFlags}}{{.}}
-   {{end}}{{end}}{{if .Copyright }}
-COPYRIGHT:
-   {{.Copyright}}
-   {{end}}{{if .Version}}
-VERSION:
-   {{.Version}}
-   {{end}}
-`
-
-	// EXAMPLE: Replace the `HelpPrinter` func
-	cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) {
-		fmt.Println("Ha HA.  I pwnd the help!!1")
-	}
-
-	(&cli.App{}).Run(os.Args)
-}
-```
-
-The default flag may be customized to something other than `-h/--help` by
-setting `cli.HelpFlag`, e.g.:
-
-<!-- {
-  "args": ["&#45;&#45halp"],
-  "output": "haaaaalp.*HALP"
-} -->
-```go
-package main
-
-import (
-	"os"
-
-	"github.com/urfave/cli/v2"
-)
-
-func main() {
-	cli.HelpFlag = &cli.BoolFlag{
-		Name:    "haaaaalp",
-		Aliases: []string{"halp"},
-		Usage:   "HALP",
-		EnvVars: []string{"SHOW_HALP", "HALPPLZ"},
-	}
-
-	(&cli.App{}).Run(os.Args)
-}
-```
-
-### Version Flag
-
-The default version flag (`-v/--version`) is defined as `cli.VersionFlag`, which
-is checked by the cli internals in order to print the `App.Version` via
-`cli.VersionPrinter` and break execution.
-
-#### Customization
-
-The default flag may be customized to something other than `-v/--version` by
-setting `cli.VersionFlag`, e.g.:
-
-<!-- {
-  "args": ["&#45;&#45print-version"],
-  "output": "partay version v19\\.99\\.0"
-} -->
-```go
-package main
-
-import (
-	"os"
-
-	"github.com/urfave/cli/v2"
-)
-
-func main() {
-	cli.VersionFlag = &cli.BoolFlag{
-		Name:    "print-version",
-		Aliases: []string{"V"},
-		Usage:   "print only the version",
-	}
-
-	app := &cli.App{
-		Name:    "partay",
-		Version: "v19.99.0",
-	}
-	app.Run(os.Args)
-}
-```
-
-Alternatively, the version printer at `cli.VersionPrinter` may be overridden,
-e.g.:
-
-<!-- {
-  "args": ["&#45;&#45version"],
-  "output": "version=v19\\.99\\.0 revision=fafafaf"
-} -->
-```go
-package main
-
-import (
-	"fmt"
-	"os"
-
-	"github.com/urfave/cli/v2"
-)
-
-var (
-	Revision = "fafafaf"
-)
-
-func main() {
-	cli.VersionPrinter = func(cCtx *cli.Context) {
-		fmt.Printf("version=%s revision=%s\n", cCtx.App.Version, Revision)
-	}
-
-	app := &cli.App{
-		Name:    "partay",
-		Version: "v19.99.0",
-	}
-	app.Run(os.Args)
-}
-```
-
-### Timestamp Flag
-
-Using the timestamp flag is simple. Please refer to
-[`time.Parse`](https://golang.org/pkg/time/#example_Parse) to get possible
-formats.
-
-<!-- {
-  "args": ["&#45;&#45;meeting", "2019-08-12T15:04:05"],
-  "output": "2019\\-08\\-12 15\\:04\\:05 \\+0000 UTC"
-} -->
-```go
-package main
-
-import (
-	"fmt"
-	"log"
-	"os"
-
-	"github.com/urfave/cli/v2"
-)
-
-func main() {
-	app := &cli.App{
-		Flags: []cli.Flag{
-			&cli.TimestampFlag{Name: "meeting", Layout: "2006-01-02T15:04:05"},
-		},
-		Action: func(cCtx *cli.Context) error {
-			fmt.Printf("%s", cCtx.Timestamp("meeting").String())
-			return nil
-		},
-	}
-
-	if err := app.Run(os.Args); err != nil {
-		log.Fatal(err)
-	}
-}
-```
-
-In this example the flag could be used like this:
-
-```sh-session
-$ myapp --meeting 2019-08-12T15:04:05
-```
-
-When the layout doesn't contain timezones, timestamp will render with UTC. To
-change behavior, a default timezone can be provided with flag definition:
-
-```go
-app := &cli.App{
-	Flags: []cli.Flag{
-		&cli.TimestampFlag{Name: "meeting", Layout: "2006-01-02T15:04:05", Timezone: time.Local},
-	},
-}
-```
-
-(time.Local contains the system's local time zone.)
-
-Side note: quotes may be necessary around the date depending on your layout (if
-you have spaces for instance)
-
-### Suggestions
-
-To enable flag and command suggestions, set `app.Suggest = true`. If the suggest
-feature is enabled, then the help output of the corresponding command will
-provide an appropriate suggestion for the provided flag or subcommand if
-available.
-
-### Full API Example
-
-**Notice**: This is a contrived (functioning) example meant strictly for API
-demonstration purposes. Use of one's imagination is encouraged.
-
-<!-- {
-  "output": "made it!\nPhew!"
-} -->
-```go
-package main
-
-import (
-	"errors"
-	"flag"
-	"fmt"
-	"io"
-	"io/ioutil"
-	"os"
-	"time"
-
-	"github.com/urfave/cli/v2"
-)
-
-func init() {
-	cli.AppHelpTemplate += "\nCUSTOMIZED: you bet ur muffins\n"
-	cli.CommandHelpTemplate += "\nYMMV\n"
-	cli.SubcommandHelpTemplate += "\nor something\n"
-
-	cli.HelpFlag = &cli.BoolFlag{Name: "halp"}
-	cli.VersionFlag = &cli.BoolFlag{Name: "print-version", Aliases: []string{"V"}}
-
-	cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) {
-		fmt.Fprintf(w, "best of luck to you\n")
-	}
-	cli.VersionPrinter = func(cCtx *cli.Context) {
-		fmt.Fprintf(cCtx.App.Writer, "version=%s\n", cCtx.App.Version)
-	}
-	cli.OsExiter = func(cCtx int) {
-		fmt.Fprintf(cli.ErrWriter, "refusing to exit %d\n", cCtx)
-	}
-	cli.ErrWriter = ioutil.Discard
-	cli.FlagStringer = func(fl cli.Flag) string {
-		return fmt.Sprintf("\t\t%s", fl.Names()[0])
-	}
-}
-
-type hexWriter struct{}
-
-func (w *hexWriter) Write(p []byte) (int, error) {
-	for _, b := range p {
-		fmt.Printf("%x", b)
-	}
-	fmt.Printf("\n")
-
-	return len(p), nil
-}
-
-type genericType struct {
-	s string
-}
-
-func (g *genericType) Set(value string) error {
-	g.s = value
-	return nil
-}
-
-func (g *genericType) String() string {
-	return g.s
-}
-
-func main() {
-	app := &cli.App{
-		Name:     "kənˈtrīv",
-		Version:  "v19.99.0",
-		Compiled: time.Now(),
-		Authors: []*cli.Author{
-			&cli.Author{
-				Name:  "Example Human",
-				Email: "human@example.com",
-			},
-		},
-		Copyright: "(c) 1999 Serious Enterprise",
-		HelpName:  "contrive",
-		Usage:     "demonstrate available API",
-		UsageText: "contrive - demonstrating the available API",
-		ArgsUsage: "[args and such]",
-		Commands: []*cli.Command{
-			&cli.Command{
-				Name:        "doo",
-				Aliases:     []string{"do"},
-				Category:    "motion",
-				Usage:       "do the doo",
-				UsageText:   "doo - does the dooing",
-				Description: "no really, there is a lot of dooing to be done",
-				ArgsUsage:   "[arrgh]",
-				Flags: []cli.Flag{
-					&cli.BoolFlag{Name: "forever", Aliases: []string{"forevvarr"}},
-				},
-				Subcommands: []*cli.Command{
-					&cli.Command{
-						Name:   "wop",
-						Action: wopAction,
-					},
-				},
-				SkipFlagParsing: false,
-				HideHelp:        false,
-				Hidden:          false,
-				HelpName:        "doo!",
-				BashComplete: func(cCtx *cli.Context) {
-					fmt.Fprintf(cCtx.App.Writer, "--better\n")
-				},
-				Before: func(cCtx *cli.Context) error {
-					fmt.Fprintf(cCtx.App.Writer, "brace for impact\n")
-					return nil
-				},
-				After: func(cCtx *cli.Context) error {
-					fmt.Fprintf(cCtx.App.Writer, "did we lose anyone?\n")
-					return nil
-				},
-				Action: func(cCtx *cli.Context) error {
-					cCtx.Command.FullName()
-					cCtx.Command.HasName("wop")
-					cCtx.Command.Names()
-					cCtx.Command.VisibleFlags()
-					fmt.Fprintf(cCtx.App.Writer, "dodododododoodododddooooododododooo\n")
-					if cCtx.Bool("forever") {
-						cCtx.Command.Run(cCtx)
-					}
-					return nil
-				},
-				OnUsageError: func(cCtx *cli.Context, err error, isSubcommand bool) error {
-					fmt.Fprintf(cCtx.App.Writer, "for shame\n")
-					return err
-				},
-			},
-		},
-		Flags: []cli.Flag{
-			&cli.BoolFlag{Name: "fancy"},
-			&cli.BoolFlag{Value: true, Name: "fancier"},
-			&cli.DurationFlag{Name: "howlong", Aliases: []string{"H"}, Value: time.Second * 3},
-			&cli.Float64Flag{Name: "howmuch"},
-			&cli.GenericFlag{Name: "wat", Value: &genericType{}},
-			&cli.Int64Flag{Name: "longdistance"},
-			&cli.Int64SliceFlag{Name: "intervals"},
-			&cli.IntFlag{Name: "distance"},
-			&cli.IntSliceFlag{Name: "times"},
-			&cli.StringFlag{Name: "dance-move", Aliases: []string{"d"}},
-			&cli.StringSliceFlag{Name: "names", Aliases: []string{"N"}},
-			&cli.UintFlag{Name: "age"},
-			&cli.Uint64Flag{Name: "bigage"},
-		},
-		EnableBashCompletion: true,
-		HideHelp:             false,
-		HideVersion:          false,
-		BashComplete: func(cCtx *cli.Context) {
-			fmt.Fprintf(cCtx.App.Writer, "lipstick\nkiss\nme\nlipstick\nringo\n")
-		},
-		Before: func(cCtx *cli.Context) error {
-			fmt.Fprintf(cCtx.App.Writer, "HEEEERE GOES\n")
-			return nil
-		},
-		After: func(cCtx *cli.Context) error {
-			fmt.Fprintf(cCtx.App.Writer, "Phew!\n")
-			return nil
-		},
-		CommandNotFound: func(cCtx *cli.Context, command string) {
-			fmt.Fprintf(cCtx.App.Writer, "Thar be no %q here.\n", command)
-		},
-		OnUsageError: func(cCtx *cli.Context, err error, isSubcommand bool) error {
-			if isSubcommand {
-				return err
-			}
-
-			fmt.Fprintf(cCtx.App.Writer, "WRONG: %#v\n", err)
-			return nil
-		},
-		Action: func(cCtx *cli.Context) error {
-			cli.DefaultAppComplete(cCtx)
-			cli.HandleExitCoder(errors.New("not an exit coder, though"))
-			cli.ShowAppHelp(cCtx)
-			cli.ShowCommandCompletions(cCtx, "nope")
-			cli.ShowCommandHelp(cCtx, "also-nope")
-			cli.ShowCompletions(cCtx)
-			cli.ShowSubcommandHelp(cCtx)
-			cli.ShowVersion(cCtx)
-
-			fmt.Printf("%#v\n", cCtx.App.Command("doo"))
-			if cCtx.Bool("infinite") {
-				cCtx.App.Run([]string{"app", "doo", "wop"})
-			}
-
-			if cCtx.Bool("forevar") {
-				cCtx.App.RunAsSubcommand(cCtx)
-			}
-			cCtx.App.Setup()
-			fmt.Printf("%#v\n", cCtx.App.VisibleCategories())
-			fmt.Printf("%#v\n", cCtx.App.VisibleCommands())
-			fmt.Printf("%#v\n", cCtx.App.VisibleFlags())
-
-			fmt.Printf("%#v\n", cCtx.Args().First())
-			if cCtx.Args().Len() > 0 {
-				fmt.Printf("%#v\n", cCtx.Args().Get(1))
-			}
-			fmt.Printf("%#v\n", cCtx.Args().Present())
-			fmt.Printf("%#v\n", cCtx.Args().Tail())
-
-			set := flag.NewFlagSet("contrive", 0)
-			nc := cli.NewContext(cCtx.App, set, cCtx)
-
-			fmt.Printf("%#v\n", nc.Args())
-			fmt.Printf("%#v\n", nc.Bool("nope"))
-			fmt.Printf("%#v\n", !nc.Bool("nerp"))
-			fmt.Printf("%#v\n", nc.Duration("howlong"))
-			fmt.Printf("%#v\n", nc.Float64("hay"))
-			fmt.Printf("%#v\n", nc.Generic("bloop"))
-			fmt.Printf("%#v\n", nc.Int64("bonk"))
-			fmt.Printf("%#v\n", nc.Int64Slice("burnks"))
-			fmt.Printf("%#v\n", nc.Int("bips"))
-			fmt.Printf("%#v\n", nc.IntSlice("blups"))
-			fmt.Printf("%#v\n", nc.String("snurt"))
-			fmt.Printf("%#v\n", nc.StringSlice("snurkles"))
-			fmt.Printf("%#v\n", nc.Uint("flub"))
-			fmt.Printf("%#v\n", nc.Uint64("florb"))
-
-			fmt.Printf("%#v\n", nc.FlagNames())
-			fmt.Printf("%#v\n", nc.IsSet("wat"))
-			fmt.Printf("%#v\n", nc.Set("wat", "nope"))
-			fmt.Printf("%#v\n", nc.NArg())
-			fmt.Printf("%#v\n", nc.NumFlags())
-			fmt.Printf("%#v\n", nc.Lineage()[1])
-			nc.Set("wat", "also-nope")
-
-			ec := cli.Exit("ohwell", 86)
-			fmt.Fprintf(cCtx.App.Writer, "%d", ec.ExitCode())
-			fmt.Printf("made it!\n")
-			return ec
-		},
-		Metadata: map[string]interface{}{
-			"layers":          "many",
-			"explicable":      false,
-			"whatever-values": 19.99,
-		},
-	}
-
-	if os.Getenv("HEXY") != "" {
-		app.Writer = &hexWriter{}
-		app.ErrWriter = &hexWriter{}
-	}
-
-	app.Run(os.Args)
-}
-
-func wopAction(cCtx *cli.Context) error {
-	fmt.Fprintf(cCtx.App.Writer, ":wave: over here, eh\n")
-	return nil
-}
-```
-
-## Migrating From Older Releases
-
-There are a small set of breaking changes between v1 and v2.  Converting is
-relatively straightforward and typically takes less than an hour. Specific steps
-are included in [Migration Guide: v1 to v2](../migrate-v1-to-v2.md). Also see
-the [pkg.go.dev docs](https://pkg.go.dev/github.com/urfave/cli/v2) for v2 API
-documentation.
diff --git a/docs/v2/migrating-from-older-releases.md b/docs/v2/migrating-from-older-releases.md
new file mode 100644
index 0000000..7bb33af
--- /dev/null
+++ b/docs/v2/migrating-from-older-releases.md
@@ -0,0 +1,12 @@
+---
+tags:
+  - v2
+search:
+  boost: 2
+---
+
+There are a small set of breaking changes between v1 and v2.  Converting is
+relatively straightforward and typically takes less than an hour. Specific steps
+are included in [Migration Guide: v1 to v2](../migrate-v1-to-v2.md). Also see
+the [pkg.go.dev docs](https://pkg.go.dev/github.com/urfave/cli/v2) for v2 API
+documentation.
diff --git a/flag-spec.yaml b/flag-spec.yaml
index 45f054d..1c594d7 100644
--- a/flag-spec.yaml
+++ b/flag-spec.yaml
@@ -1,15 +1,23 @@
 # NOTE: this file is used by the tool defined in
-# ./internal/genflags/cmd/genflags/main.go which uses the
-# `genflags.Spec` type that maps to this file structure.
+# ./cmd/urfave-cli-genflags/main.go which uses the
+# `Spec` type that maps to this file structure.
 
 flag_types:
   bool: {}
   float64: {}
-  int64: {}
-  int: {}
+  int64:
+    struct_fields:
+      - { name: Base, type: int }
+  int:
+    struct_fields:
+      - { name: Base, type: int }
   time.Duration: {}
-  uint64: {}
-  uint: {}
+  uint64:
+    struct_fields:
+      - { name: Base, type: int }
+  uint:
+    struct_fields:
+      - { name: Base, type: int }
 
   string:
     struct_fields:
diff --git a/flag_int.go b/flag_int.go
index c70b889..0f5c403 100644
--- a/flag_int.go
+++ b/flag_int.go
@@ -44,7 +44,7 @@ func (f *IntFlag) GetEnvVars() []string {
 func (f *IntFlag) Apply(set *flag.FlagSet) error {
 	if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
 		if val != "" {
-			valInt, err := strconv.ParseInt(val, 0, 64)
+			valInt, err := strconv.ParseInt(val, f.Base, 64)
 
 			if err != nil {
 				return fmt.Errorf("could not parse %q as int value from %s for flag %s: %s", val, source, f.Name, err)
diff --git a/flag_int64.go b/flag_int64.go
index 5e7038c..a392275 100644
--- a/flag_int64.go
+++ b/flag_int64.go
@@ -44,7 +44,7 @@ func (f *Int64Flag) GetEnvVars() []string {
 func (f *Int64Flag) Apply(set *flag.FlagSet) error {
 	if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
 		if val != "" {
-			valInt, err := strconv.ParseInt(val, 0, 64)
+			valInt, err := strconv.ParseInt(val, f.Base, 64)
 
 			if err != nil {
 				return fmt.Errorf("could not parse %q as int value from %s for flag %s: %s", val, source, f.Name, err)
diff --git a/flag_uint.go b/flag_uint.go
index 6092b1a..d25ff73 100644
--- a/flag_uint.go
+++ b/flag_uint.go
@@ -25,7 +25,7 @@ func (f *UintFlag) GetCategory() string {
 func (f *UintFlag) Apply(set *flag.FlagSet) error {
 	if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
 		if val != "" {
-			valInt, err := strconv.ParseUint(val, 0, 64)
+			valInt, err := strconv.ParseUint(val, f.Base, 64)
 			if err != nil {
 				return fmt.Errorf("could not parse %q as uint value from %s for flag %s: %s", val, source, f.Name, err)
 			}
diff --git a/flag_uint64.go b/flag_uint64.go
index a37f30d..975c733 100644
--- a/flag_uint64.go
+++ b/flag_uint64.go
@@ -25,7 +25,7 @@ func (f *Uint64Flag) GetCategory() string {
 func (f *Uint64Flag) Apply(set *flag.FlagSet) error {
 	if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
 		if val != "" {
-			valInt, err := strconv.ParseUint(val, 0, 64)
+			valInt, err := strconv.ParseUint(val, f.Base, 64)
 			if err != nil {
 				return fmt.Errorf("could not parse %q as uint64 value from %s for flag %s: %s", val, source, f.Name, err)
 			}
diff --git a/funcs.go b/funcs.go
index 0a9b22c..e77b0d0 100644
--- a/funcs.go
+++ b/funcs.go
@@ -23,6 +23,9 @@ type CommandNotFoundFunc func(*Context, string)
 // is displayed and the execution is interrupted.
 type OnUsageErrorFunc func(cCtx *Context, err error, isSubcommand bool) error
 
+// InvalidFlagAccessFunc is executed when an invalid flag is accessed from the context.
+type InvalidFlagAccessFunc func(*Context, string)
+
 // ExitErrHandlerFunc is executed if provided in order to handle exitError values
 // returned by Actions and Before/After functions.
 type ExitErrHandlerFunc func(cCtx *Context, err error)
diff --git a/go.mod b/go.mod
index 4e39308..09ef147 100644
--- a/go.mod
+++ b/go.mod
@@ -6,8 +6,10 @@ require (
 	github.com/BurntSushi/toml v1.1.0
 	github.com/cpuguy83/go-md2man/v2 v2.0.2
 	github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673
-	golang.org/x/text v0.3.7
 	gopkg.in/yaml.v3 v3.0.1
 )
 
-require github.com/russross/blackfriday/v2 v2.1.0 // indirect
+require (
+	github.com/russross/blackfriday/v2 v2.1.0 // indirect
+	golang.org/x/text v0.3.7 // indirect
+)
diff --git a/go.sum b/go.sum
index 6beae99..96058c7 100644
--- a/go.sum
+++ b/go.sum
@@ -1,7 +1,5 @@
 github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
 github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
-github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU=
-github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
 github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
@@ -10,8 +8,6 @@ github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRT
 github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
 golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e h1:FDhOuMEY4JVRztM/gsbk+IKUQ8kj74bxZrgw87eMMVc=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
diff --git a/godoc-current.txt b/godoc-current.txt
index 1ba48cc..a6a3887 100644
--- a/godoc-current.txt
+++ b/godoc-current.txt
@@ -64,8 +64,8 @@ GLOBAL OPTIONS:
 COPYRIGHT:
    {{wrap .Copyright 3}}{{end}}
 `
-    AppHelpTemplate is the text template for the Default help topic. cli.go uses
-    text/template to render templates. You can render custom help text by
+    AppHelpTemplate is the text template for the Default help topic. cli.go
+    uses text/template to render templates. You can render custom help text by
     setting this variable.
 
 var CommandHelpTemplate = `NAME:
@@ -201,9 +201,9 @@ func DefaultAppComplete(cCtx *Context)
 func DefaultCompleteWithFlags(cmd *Command) func(cCtx *Context)
 func FlagNames(name string, aliases []string) []string
 func HandleAction(action interface{}, cCtx *Context) (err error)
-    HandleAction attempts to figure out which Action signature was used. If it's
-    an ActionFunc or a func with the legacy signature for Action, the func is
-    run!
+    HandleAction attempts to figure out which Action signature was used.
+    If it's an ActionFunc or a func with the legacy signature for Action,
+    the func is run!
 
 func HandleExitCoder(err error)
     HandleExitCoder handles errors implementing ExitCoder by printing their
@@ -360,14 +360,14 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error)
     to generate command-specific flags
 
 func (a *App) RunContext(ctx context.Context, arguments []string) (err error)
-    RunContext is like Run except it takes a Context that will be passed to its
-    commands and sub-commands. Through this, you can propagate timeouts and
+    RunContext is like Run except it takes a Context that will be passed to
+    its commands and sub-commands. Through this, you can propagate timeouts and
     cancellation requests
 
 func (a *App) Setup()
-    Setup runs initialization code to ensure all data structures are ready for
-    `Run` or inspection prior to `Run`. It is internally called by `Run`, but
-    will return early if setup has already happened.
+    Setup runs initialization code to ensure all data structures are ready
+    for `Run` or inspection prior to `Run`. It is internally called by `Run`,
+    but will return early if setup has already happened.
 
 func (a *App) ToFishCompletion() (string, error)
     ToFishCompletion creates a fish completion string for the `*App` The
@@ -799,9 +799,9 @@ func Exit(message interface{}, exitCode int) ExitCoder
     Exit wraps a message and exit code into an error, which by default is
     handled with a call to os.Exit during default error handling.
 
-    This is the simplest way to trigger a non-zero exit code for an App without
-    having to call os.Exit manually. During testing, this behavior can be
-    avoided by overiding the ExitErrHandler function on an App or the
+    This is the simplest way to trigger a non-zero exit code for an App
+    without having to call os.Exit manually. During testing, this behavior
+    can be avoided by overiding the ExitErrHandler function on an App or the
     package-global OsExiter function.
 
 func NewExitError(message interface{}, exitCode int) ExitCoder
@@ -1131,6 +1131,8 @@ type Int64Flag struct {
 
 	Aliases []string
 	EnvVars []string
+
+	Base int
 }
     Int64Flag is a flag with type int64
 
@@ -1280,6 +1282,8 @@ type IntFlag struct {
 
 	Aliases []string
 	EnvVars []string
+
+	Base int
 }
     IntFlag is a flag with type int
 
@@ -1431,8 +1435,8 @@ type MultiInt64Flag = SliceFlag[*Int64SliceFlag, []int64, int64]
     directly, as Value and/or Destination. See also SliceFlag.
 
 type MultiIntFlag = SliceFlag[*IntSliceFlag, []int, int]
-    MultiIntFlag extends IntSliceFlag with support for using slices directly, as
-    Value and/or Destination. See also SliceFlag.
+    MultiIntFlag extends IntSliceFlag with support for using slices directly,
+    as Value and/or Destination. See also SliceFlag.
 
 type MultiStringFlag = SliceFlag[*StringSliceFlag, []string, string]
     MultiStringFlag extends StringSliceFlag with support for using slices
@@ -1513,8 +1517,8 @@ type RequiredFlag interface {
 
 	IsRequired() bool
 }
-    RequiredFlag is an interface that allows us to mark flags as required it
-    allows flags required flags to be backwards compatible with the Flag
+    RequiredFlag is an interface that allows us to mark flags as required
+    it allows flags required flags to be backwards compatible with the Flag
     interface
 
 type Serializer interface {
@@ -1527,9 +1531,9 @@ type SliceFlag[T SliceFlagTarget[E], S ~[]E, E any] struct {
 	Value       S
 	Destination *S
 }
-    SliceFlag extends implementations like StringSliceFlag and IntSliceFlag with
-    support for using slices directly, as Value and/or Destination. See also
-    SliceFlagTarget, MultiStringFlag, MultiFloat64Flag, MultiInt64Flag,
+    SliceFlag extends implementations like StringSliceFlag and IntSliceFlag
+    with support for using slices directly, as Value and/or Destination.
+    See also SliceFlagTarget, MultiStringFlag, MultiFloat64Flag, MultiInt64Flag,
     MultiIntFlag.
 
 func (x *SliceFlag[T, S, E]) Apply(set *flag.FlagSet) error
@@ -1850,6 +1854,8 @@ type Uint64Flag struct {
 
 	Aliases []string
 	EnvVars []string
+
+	Base int
 }
     Uint64Flag is a flag with type uint64
 
@@ -1910,6 +1916,8 @@ type UintFlag struct {
 
 	Aliases []string
 	EnvVars []string
+
+	Base int
 }
     UintFlag is a flag with type uint
 
@@ -1986,9 +1994,9 @@ func InitInputSource(flags []cli.Flag, createInputSource func() (InputSourceCont
     that are supported by the input source
 
 func InitInputSourceWithContext(flags []cli.Flag, createInputSource func(cCtx *cli.Context) (InputSourceContext, error)) cli.BeforeFunc
-    InitInputSourceWithContext is used to to setup an InputSourceContext on a
-    cli.Command Before method. It will create a new input source based on the
-    func provided with potentially using existing cli.Context values to
+    InitInputSourceWithContext is used to to setup an InputSourceContext on
+    a cli.Command Before method. It will create a new input source based on
+    the func provided with potentially using existing cli.Context values to
     initialize itself. If there is no error it will then apply the new input
     source to any flags that are supported by the input source
 
diff --git a/help.go b/help.go
index 9a8d243..d6caea4 100644
--- a/help.go
+++ b/help.go
@@ -295,12 +295,14 @@ func printHelpCustom(out io.Writer, templ string, data interface{}, customFuncs
 	const maxLineLength = 10000
 
 	funcMap := template.FuncMap{
-		"join":    strings.Join,
-		"indent":  indent,
-		"nindent": nindent,
-		"trim":    strings.TrimSpace,
-		"wrap":    func(input string, offset int) string { return wrap(input, offset, maxLineLength) },
-		"offset":  offset,
+		"join":           strings.Join,
+		"subtract":       subtract,
+		"indent":         indent,
+		"nindent":        nindent,
+		"trim":           strings.TrimSpace,
+		"wrap":           func(input string, offset int) string { return wrap(input, offset, maxLineLength) },
+		"offset":         offset,
+		"offsetCommands": offsetCommands,
 	}
 
 	if customFuncs["wrapAt"] != nil {
@@ -416,6 +418,10 @@ func checkCommandCompletions(c *Context, name string) bool {
 	return true
 }
 
+func subtract(a, b int) int {
+	return a - b
+}
+
 func indent(spaces int, v string) string {
 	pad := strings.Repeat(" ", spaces)
 	return pad + strings.Replace(v, "\n", "\n"+pad, -1)
@@ -476,3 +482,28 @@ func wrapLine(input string, offset int, wrapAt int, padding string) string {
 func offset(input string, fixed int) int {
 	return len(input) + fixed
 }
+
+// this function tries to find the max width of the names column
+// so say we have the following rows for help
+//
+//	foo1, foo2, foo3  some string here
+//	bar1, b2 some other string here
+//
+// We want to offset the 2nd row usage by some amount so that everything
+// is aligned
+//
+//	foo1, foo2, foo3  some string here
+//	bar1, b2          some other string here
+//
+// to find that offset we find the length of all the rows and use the max
+// to calculate the offset
+func offsetCommands(cmds []*Command, fixed int) int {
+	var max int = 0
+	for _, cmd := range cmds {
+		s := strings.Join(cmd.Names(), ", ")
+		if len(s) > max {
+			max = len(s)
+		}
+	}
+	return max + fixed
+}
diff --git a/internal/build/build.go b/internal/build/build.go
index d94dae7..930e30b 100644
--- a/internal/build/build.go
+++ b/internal/build/build.go
@@ -60,7 +60,14 @@ func main() {
 			Action: TestActionFunc,
 		},
 		{
-			Name:   "gfmrun",
+			Name: "gfmrun",
+			Flags: []cli.Flag{
+				&cli.BoolFlag{
+					Name:  "walk",
+					Value: false,
+					Usage: "Walk the specified directory and perform validation on all markdown files",
+				},
+			},
 			Action: GfmrunActionFunc,
 		},
 		{
@@ -94,7 +101,7 @@ func main() {
 		},
 		&cli.StringSliceFlag{
 			Name:  "packages",
-			Value: cli.NewStringSlice("cli", "altsrc", "internal/build", "internal/genflags"),
+			Value: cli.NewStringSlice("cli", "altsrc", "internal/build"),
 		},
 	}
 
@@ -215,36 +222,75 @@ func GfmrunActionFunc(cCtx *cli.Context) error {
 		return err
 	}
 
-	filename := cCtx.Args().Get(0)
-	if filename == "" {
-		filename = "README.md"
+	dirPath := cCtx.Args().Get(0)
+	if dirPath == "" {
+		dirPath = "README.md"
 	}
 
-	file, err := os.Open(filename)
-	if err != nil {
-		return err
+	walk := cCtx.Bool("walk")
+	sources := []string{}
+
+	if walk {
+		// Walk the directory and find all markdown files.
+		err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
+			if err != nil {
+				return err
+			}
+
+			if info.IsDir() {
+				return nil
+			}
+
+			if filepath.Ext(path) != ".md" {
+				return nil
+			}
+
+			sources = append(sources, path)
+			return nil
+		})
+		if err != nil {
+			return err
+		}
+	} else {
+		sources = append(sources, dirPath)
 	}
-	defer file.Close()
 
 	var counter int
-	scanner := bufio.NewScanner(file)
-	for scanner.Scan() {
-		if strings.Contains(scanner.Text(), "package main") {
-			counter++
+
+	for _, src := range sources {
+		file, err := os.Open(src)
+		if err != nil {
+			return err
 		}
-	}
+		defer file.Close()
 
-	err = file.Close()
-	if err != nil {
-		return err
+		scanner := bufio.NewScanner(file)
+		for scanner.Scan() {
+			if strings.Contains(scanner.Text(), "package main") {
+				counter++
+			}
+		}
+
+		err = file.Close()
+		if err != nil {
+			return err
+		}
+
+		err = scanner.Err()
+		if err != nil {
+			return err
+		}
 	}
 
-	err = scanner.Err()
-	if err != nil {
-		return err
+	gfmArgs := []string{
+		"--count",
+		fmt.Sprint(counter),
+	}
+	for _, src := range sources {
+		gfmArgs = append(gfmArgs, "--sources", src)
 	}
 
-	if err := runCmd("gfmrun", "-c", fmt.Sprint(counter), "-s", filename); err != nil {
+	if err := runCmd("gfmrun", gfmArgs...); err != nil {
 		return err
 	}
 
diff --git a/internal/genflags/package.go b/internal/genflags/package.go
deleted file mode 100644
index 4e5de41..0000000
--- a/internal/genflags/package.go
+++ /dev/null
@@ -1,34 +0,0 @@
-package genflags
-
-import (
-	_ "embed"
-	"strings"
-
-	"golang.org/x/text/cases"
-	"golang.org/x/text/language"
-)
-
-var (
-	//go:embed generated.gotmpl
-	TemplateString string
-
-	//go:embed generated_test.gotmpl
-	TestTemplateString string
-
-	titler = cases.Title(language.Und, cases.NoLower)
-)
-
-func TypeName(goType string, fc *FlagTypeConfig) string {
-	if fc != nil && strings.TrimSpace(fc.TypeName) != "" {
-		return strings.TrimSpace(fc.TypeName)
-	}
-
-	dotSplit := strings.Split(goType, ".")
-	goType = dotSplit[len(dotSplit)-1]
-
-	if strings.HasPrefix(goType, "[]") {
-		return titler.String(strings.TrimPrefix(goType, "[]")) + "SliceFlag"
-	}
-
-	return titler.String(goType) + "Flag"
-}
diff --git a/internal/genflags/spec.go b/internal/genflags/spec.go
deleted file mode 100644
index a7afb22..0000000
--- a/internal/genflags/spec.go
+++ /dev/null
@@ -1,108 +0,0 @@
-package genflags
-
-import (
-	"sort"
-	"strings"
-)
-
-type Spec struct {
-	FlagTypes              map[string]*FlagTypeConfig `yaml:"flag_types"`
-	PackageName            string                     `yaml:"package_name"`
-	TestPackageName        string                     `yaml:"test_package_name"`
-	UrfaveCLINamespace     string                     `yaml:"urfave_cli_namespace"`
-	UrfaveCLITestNamespace string                     `yaml:"urfave_cli_test_namespace"`
-}
-
-func (gfs *Spec) SortedFlagTypes() []*FlagType {
-	typeNames := []string{}
-
-	for name := range gfs.FlagTypes {
-		if strings.HasPrefix(name, "[]") {
-			name = strings.TrimPrefix(name, "[]") + "Slice"
-		}
-
-		typeNames = append(typeNames, name)
-	}
-
-	sort.Strings(typeNames)
-
-	ret := make([]*FlagType, len(typeNames))
-
-	for i, typeName := range typeNames {
-		ret[i] = &FlagType{
-			GoType: typeName,
-			Config: gfs.FlagTypes[typeName],
-		}
-	}
-
-	return ret
-}
-
-type FlagTypeConfig struct {
-	SkipInterfaces []string           `yaml:"skip_interfaces"`
-	StructFields   []*FlagStructField `yaml:"struct_fields"`
-	TypeName       string             `yaml:"type_name"`
-	ValuePointer   bool               `yaml:"value_pointer"`
-}
-
-type FlagStructField struct {
-	Name string
-	Type string
-}
-
-type FlagType struct {
-	GoType string
-	Config *FlagTypeConfig
-}
-
-func (ft *FlagType) StructFields() []*FlagStructField {
-	if ft.Config == nil || ft.Config.StructFields == nil {
-		return []*FlagStructField{}
-	}
-
-	return ft.Config.StructFields
-}
-
-func (ft *FlagType) ValuePointer() bool {
-	if ft.Config == nil {
-		return false
-	}
-
-	return ft.Config.ValuePointer
-}
-
-func (ft *FlagType) TypeName() string {
-	return TypeName(ft.GoType, ft.Config)
-}
-
-func (ft *FlagType) GenerateFmtStringerInterface() bool {
-	return ft.skipInterfaceNamed("fmt.Stringer")
-}
-
-func (ft *FlagType) GenerateFlagInterface() bool {
-	return ft.skipInterfaceNamed("Flag")
-}
-
-func (ft *FlagType) GenerateRequiredFlagInterface() bool {
-	return ft.skipInterfaceNamed("RequiredFlag")
-}
-
-func (ft *FlagType) GenerateVisibleFlagInterface() bool {
-	return ft.skipInterfaceNamed("VisibleFlag")
-}
-
-func (ft *FlagType) skipInterfaceNamed(name string) bool {
-	if ft.Config == nil {
-		return true
-	}
-
-	lowName := strings.ToLower(name)
-
-	for _, interfaceName := range ft.Config.SkipInterfaces {
-		if strings.ToLower(interfaceName) == lowName {
-			return false
-		}
-	}
-
-	return true
-}
diff --git a/mkdocs.yml b/mkdocs.yml
index 73b88c5..02657b9 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -9,9 +9,45 @@ site_url: https://cli.urfave.org/
 repo_url: https://github.com/urfave/cli
 edit_uri: edit/main/docs/
 nav:
-  - Home: index.md
-  - v2 Manual: v2/index.md
-  - v1 Manual: v1/index.md
+  - Home:
+      - Welcome: index.md
+      - Contributing: CONTRIBUTING.md
+      - Code of Conduct: CODE_OF_CONDUCT.md
+      - Releasing: RELEASING.md
+      - Security: SECURITY.md
+      - Migrate v1 to v2: migrate-v1-to-v2.md
+  - v2 Manual:
+      - Getting Started: v2/getting-started.md
+      - Migrating From Older Releases: v2/migrating-from-older-releases.md
+      - Examples:
+          - Greet: v2/examples/greet.md
+          - Arguments: v2/examples/arguments.md
+          - Flags: v2/examples/flags.md
+          - Subcommands: v2/examples/subcommands.md
+          - Subcommands Categories: v2/examples/subcommands-categories.md
+          - Exit Codes: v2/examples/exit-codes.md
+          - Combining Short Options: v2/examples/combining-short-options.md
+          - Bash Completions: v2/examples/bash-completions.md
+          - Generated Help Text: v2/examples/generated-help-text.md
+          - Version Flag: v2/examples/version-flag.md
+          - Timestamp Flag: v2/examples/timestamp-flag.md
+          - Suggestions: v2/examples/suggestions.md
+          - Full API Example: v2/examples/full-api-example.md
+  - v1 Manual:
+      - Getting Started: v1/getting-started.md
+      - Migrating to v2: v1/migrating-to-v2.md
+      - Examples:
+          - Greet: v1/examples/greet.md
+          - Arguments: v1/examples/arguments.md
+          - Flags: v1/examples/flags.md
+          - Subcommands: v1/examples/subcommands.md
+          - Subcommands (Categories): v1/examples/subcommands-categories.md
+          - Exit Codes: v1/examples/exit-codes.md
+          - Combining Short Options: v1/examples/combining-short-options.md
+          - Bash Completions: v1/examples/bash-completions.md
+          - Generated Help Text: v1/examples/generated-help-text.md
+          - Version Flag: v1/examples/version-flag.md
+
 theme:
   name: material
   palette:
@@ -25,9 +61,18 @@ theme:
       toggle:
         icon: material/brightness-7
         name: light mode
+  features:
+    - content.code.annotate
+    - navigation.top
+    - navigation.instant
+    - navigation.expand
+    - navigation.sections
+    - navigation.tabs
+    - navigation.tabs.sticky
 plugins:
   - git-revision-date-localized
   - search
+  - tags
 # NOTE: this is the recommended configuration from
 # https://squidfunk.github.io/mkdocs-material/setup/extensions/#recommended-configuration
 markdown_extensions:
diff --git a/template.go b/template.go
index f3116fd..7ed2370 100644
--- a/template.go
+++ b/template.go
@@ -21,8 +21,8 @@ AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}:
 
 COMMANDS:{{range .VisibleCategories}}{{if .Name}}
    {{.Name}}:{{range .VisibleCommands}}
-     {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{range .VisibleCommands}}
-   {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{end}}{{if .VisibleFlagCategories}}
+     {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{ $cv := offsetCommands .VisibleCommands 5}}{{range .VisibleCommands}}
+   {{$s := join .Names ", "}}{{$s}}{{ $sp := subtract $cv (offset $s 3) }}{{ indent $sp ""}}{{wrap .Usage $cv}}{{end}}{{end}}{{end}}{{end}}{{if .VisibleFlagCategories}}
 
 GLOBAL OPTIONS:{{range .VisibleFlagCategories}}
    {{if .Name}}{{.Name}}
@@ -76,8 +76,8 @@ DESCRIPTION:
 
 COMMANDS:{{range .VisibleCategories}}{{if .Name}}
    {{.Name}}:{{range .VisibleCommands}}
-     {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{range .VisibleCommands}}
-   {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{if .VisibleFlags}}
+     {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{ $cv := offsetCommands .VisibleCommands 5}}{{range .VisibleCommands}}
+     {{$s := join .Names ", "}}{{$s}}{{ $sp := subtract $cv (offset $s 3) }}{{ indent $sp ""}}{{wrap .Usage $cv}}{{end}}{{end}}{{end}}{{if .VisibleFlags}}
 
 OPTIONS:
    {{range .VisibleFlags}}{{.}}
diff --git a/zz_generated.flags.go b/zz_generated.flags.go
index b89566f..f5b73ba 100644
--- a/zz_generated.flags.go
+++ b/zz_generated.flags.go
@@ -417,6 +417,8 @@ type IntFlag struct {
 
 	Aliases []string
 	EnvVars []string
+
+	Base int
 }
 
 // String returns a readable representation of this value (for usage defaults)
@@ -462,6 +464,8 @@ type Int64Flag struct {
 
 	Aliases []string
 	EnvVars []string
+
+	Base int
 }
 
 // String returns a readable representation of this value (for usage defaults)
@@ -599,6 +603,8 @@ type UintFlag struct {
 
 	Aliases []string
 	EnvVars []string
+
+	Base int
 }
 
 // String returns a readable representation of this value (for usage defaults)
@@ -644,6 +650,8 @@ type Uint64Flag struct {
 
 	Aliases []string
 	EnvVars []string
+
+	Base int
 }
 
 // String returns a readable representation of this value (for usage defaults)


Test patch:
diff --git a/altsrc/flag_test.go b/altsrc/flag_test.go
index e3725f7..bece6aa 100644
--- a/altsrc/flag_test.go
+++ b/altsrc/flag_test.go
@@ -37,6 +37,20 @@ func (ris *racyInputSource) isSet(name string) bool {
 	return true
 }
 
+func TestGenericApplyInputSourceValue_Alias(t *testing.T) {
+	v := &Parser{"abc", "def"}
+	tis := testApplyInputSource{
+		Flag:     NewGenericFlag(&cli.GenericFlag{Name: "test",  Aliases: []string{"test_alias"}, Value: &Parser{}}),
+		FlagName: "test_alias",
+		MapValue: v,
+	}
+	c := runTest(t, tis)
+	expect(t, v, c.Generic("test_alias"))
+
+	c = runRacyTest(t, tis)
+	refute(t, v, c.Generic("test_alias"))
+}
+
 func TestGenericApplyInputSourceValue(t *testing.T) {
 	v := &Parser{"abc", "def"}
 	tis := testApplyInputSource{
@@ -85,6 +99,19 @@ func TestGenericApplyInputSourceMethodEnvVarSet(t *testing.T) {
 	refute(t, &Parser{"abc", "def"}, c.Generic("test"))
 }
 
+func TestStringSliceApplyInputSourceValue_Alias(t *testing.T) {
+	tis := testApplyInputSource{
+		Flag:     NewStringSliceFlag(&cli.StringSliceFlag{Name: "test", Aliases: []string{"test_alias"}}),
+		FlagName: "test_alias",
+		MapValue: []interface{}{"hello", "world"},
+	}
+	c := runTest(t, tis)
+	expect(t, c.StringSlice("test_alias"), []string{"hello", "world"})
+
+	c = runRacyTest(t, tis)
+	refute(t, c.StringSlice("test_alias"), []string{"hello", "world"})
+}
+
 func TestStringSliceApplyInputSourceValue(t *testing.T) {
 	tis := testApplyInputSource{
 		Flag:     NewStringSliceFlag(&cli.StringSliceFlag{Name: "test"}),
@@ -123,6 +150,19 @@ func TestStringSliceApplyInputSourceMethodEnvVarSet(t *testing.T) {
 	refute(t, c.StringSlice("test"), []string{"oh", "no"})
 }
 
+func TestIntSliceApplyInputSourceValue_Alias(t *testing.T) {
+	tis := testApplyInputSource{
+		Flag:     NewIntSliceFlag(&cli.IntSliceFlag{Name: "test", Aliases: []string{"test_alias"}}),
+		FlagName: "test_alias",
+		MapValue: []interface{}{1, 2},
+	}
+	c := runTest(t, tis)
+	expect(t, c.IntSlice("test_alias"), []int{1, 2})
+
+	c = runRacyTest(t, tis)
+	refute(t, c.IntSlice("test_alias"), []int{1, 2})
+}
+
 func TestIntSliceApplyInputSourceValue(t *testing.T) {
 	tis := testApplyInputSource{
 		Flag:     NewIntSliceFlag(&cli.IntSliceFlag{Name: "test"}),
@@ -178,6 +218,19 @@ func TestBoolApplyInputSourceMethodSet(t *testing.T) {
 	refute(t, true, c.Bool("test"))
 }
 
+func TestBoolApplyInputSourceMethodSet_Alias(t *testing.T) {
+	tis := testApplyInputSource{
+		Flag:     NewBoolFlag(&cli.BoolFlag{Name: "test", Aliases: []string{"test_alias"}}),
+		FlagName: "test_alias",
+		MapValue: true,
+	}
+	c := runTest(t, tis)
+	expect(t, true, c.Bool("test_alias"))
+
+	c = runRacyTest(t, tis)
+	refute(t, true, c.Bool("test_alias"))
+}
+
 func TestBoolApplyInputSourceMethodContextSet(t *testing.T) {
 	tis := testApplyInputSource{
 		Flag:               NewBoolFlag(&cli.BoolFlag{Name: "test"}),
@@ -207,6 +260,19 @@ func TestBoolApplyInputSourceMethodEnvVarSet(t *testing.T) {
 	refute(t, true, c.Bool("test"))
 }
 
+func TestStringApplyInputSourceMethodSet_Alias(t *testing.T) {
+	tis := testApplyInputSource{
+		Flag:     NewStringFlag(&cli.StringFlag{Name: "test", Aliases: []string{"test_alias"}}),
+		FlagName: "test_alias",
+		MapValue: "hello",
+	}
+	c := runTest(t, tis)
+	expect(t, "hello", c.String("test_alias"))
+
+	c = runRacyTest(t, tis)
+	refute(t, "hello", c.String("test_alias"))
+}
+
 func TestStringApplyInputSourceMethodSet(t *testing.T) {
 	tis := testApplyInputSource{
 		Flag:     NewStringFlag(&cli.StringFlag{Name: "test"}),
@@ -249,6 +315,31 @@ func TestStringApplyInputSourceMethodEnvVarSet(t *testing.T) {
 	refute(t, "goodbye", c.String("test"))
 }
 
+func TestPathApplyInputSourceMethodSet_Alias(t *testing.T) {
+	tis := testApplyInputSource{
+		Flag:       NewPathFlag(&cli.PathFlag{Name: "test", Aliases: []string{"test_alias"}}),
+		FlagName:   "test_alias",
+		MapValue:   "hello",
+		SourcePath: "/path/to/source/file",
+	}
+	c := runTest(t, tis)
+
+	expected := "/path/to/source/hello"
+	if runtime.GOOS == "windows" {
+		var err error
+		// Prepend the corresponding drive letter (or UNC path?), and change
+		// to windows-style path:
+		expected, err = filepath.Abs(expected)
+		if err != nil {
+			t.Fatal(err)
+		}
+	}
+	expect(t, expected, c.String("test_alias"))
+
+	c = runRacyTest(t, tis)
+	refute(t, expected, c.String("test_alias"))
+}
+
 func TestPathApplyInputSourceMethodSet(t *testing.T) {
 	tis := testApplyInputSource{
 		Flag:       NewPathFlag(&cli.PathFlag{Name: "test"}),
@@ -305,6 +396,19 @@ func TestPathApplyInputSourceMethodEnvVarSet(t *testing.T) {
 	refute(t, "goodbye", c.String("test"))
 }
 
+func TestIntApplyInputSourceMethodSet_Alias(t *testing.T) {
+	tis := testApplyInputSource{
+		Flag:     NewIntFlag(&cli.IntFlag{Name: "test", Aliases: []string{"test_alias"}}),
+		FlagName: "test_alias",
+		MapValue: 15,
+	}
+	c := runTest(t, tis)
+	expect(t, 15, c.Int("test_alias"))
+
+	c = runRacyTest(t, tis)
+	refute(t, 15, c.Int("test_alias"))
+}
+
 func TestIntApplyInputSourceMethodSet(t *testing.T) {
 	tis := testApplyInputSource{
 		Flag:     NewIntFlag(&cli.IntFlag{Name: "test"}),
@@ -360,6 +464,19 @@ func TestIntApplyInputSourceMethodEnvVarSet(t *testing.T) {
 	refute(t, 12, c.Int("test"))
 }
 
+func TestDurationApplyInputSourceMethodSet_Alias(t *testing.T) {
+	tis := testApplyInputSource{
+		Flag:     NewDurationFlag(&cli.DurationFlag{Name: "test", Aliases: []string{"test_alias"}}),
+		FlagName: "test_alias",
+		MapValue: 30 * time.Second,
+	}
+	c := runTest(t, tis)
+	expect(t, 30*time.Second, c.Duration("test_alias"))
+
+	c = runRacyTest(t, tis)
+	refute(t, 30*time.Second, c.Duration("test_alias"))
+}
+
 func TestDurationApplyInputSourceMethodSet(t *testing.T) {
 	tis := testApplyInputSource{
 		Flag:     NewDurationFlag(&cli.DurationFlag{Name: "test"}),
@@ -428,6 +545,19 @@ func TestFloat64ApplyInputSourceMethodSet(t *testing.T) {
 	refute(t, 1.3, c.Float64("test"))
 }
 
+func TestFloat64ApplyInputSourceMethodSetNegativeValue_Alias(t *testing.T) {
+	tis := testApplyInputSource{
+		Flag:     NewFloat64Flag(&cli.Float64Flag{Name: "test", Aliases: []string{"test_alias"}}),
+		FlagName: "test_alias",
+		MapValue: -1.3,
+	}
+	c := runTest(t, tis)
+	expect(t, -1.3, c.Float64("test_alias"))
+
+	c = runRacyTest(t, tis)
+	refute(t, -1.3, c.Float64("test_alias"))
+}
+
 func TestFloat64ApplyInputSourceMethodSetNegativeValue(t *testing.T) {
 	tis := testApplyInputSource{
 		Flag:     NewFloat64Flag(&cli.Float64Flag{Name: "test"}),
diff --git a/altsrc/json_command_test.go b/altsrc/json_command_test.go
index bd0022b..297fc9a 100644
--- a/altsrc/json_command_test.go
+++ b/altsrc/json_command_test.go
@@ -11,7 +11,7 @@ import (
 
 const (
 	fileName   = "current.json"
-	simpleJSON = `{"test": 15}`
+	simpleJSON = `{"test": 15, "testb": false}`
 	nestedJSON = `{"top": {"test": 15}}`
 )
 
@@ -34,11 +34,16 @@ func TestCommandJSONFileTest(t *testing.T) {
 		Action: func(c *cli.Context) error {
 			val := c.Int("test")
 			expect(t, val, 15)
+
+			valb := c.Bool("testb")
+			expect(t, valb, false)
 			return nil
 		},
 		Flags: []cli.Flag{
 			NewIntFlag(&cli.IntFlag{Name: "test"}),
-			&cli.StringFlag{Name: "load"}},
+			&cli.StringFlag{Name: "load"},
+			NewBoolFlag(&cli.BoolFlag{Name: "testb", Value: true}),
+		},
 	}
 	command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load"))
 	err := command.Run(c)
diff --git a/app_test.go b/app_test.go
index cc77ce3..64316fc 100644
--- a/app_test.go
+++ b/app_test.go
@@ -1245,6 +1245,58 @@ func TestApp_BeforeFunc(t *testing.T) {
 	}
 }
 
+func TestApp_BeforeAfterFuncShellCompletion(t *testing.T) {
+	counts := &opCounts{}
+	var err error
+
+	app := &App{
+		EnableBashCompletion: true,
+		Before: func(c *Context) error {
+			counts.Total++
+			counts.Before = counts.Total
+			return nil
+		},
+		After: func(c *Context) error {
+			counts.Total++
+			counts.After = counts.Total
+			return nil
+		},
+		Commands: []*Command{
+			{
+				Name: "sub",
+				Action: func(c *Context) error {
+					counts.Total++
+					counts.SubCommand = counts.Total
+					return nil
+				},
+			},
+		},
+		Flags: []Flag{
+			&StringFlag{Name: "opt"},
+		},
+		Writer: ioutil.Discard,
+	}
+
+	// run with the Before() func succeeding
+	err = app.Run([]string{"command", "--opt", "succeed", "sub", "--generate-bash-completion"})
+
+	if err != nil {
+		t.Fatalf("Run error: %s", err)
+	}
+
+	if counts.Before != 0 {
+		t.Errorf("Before() executed when not expected")
+	}
+
+	if counts.After != 0 {
+		t.Errorf("After() executed when not expected")
+	}
+
+	if counts.SubCommand != 0 {
+		t.Errorf("Subcommand executed more than expected")
+	}
+}
+
 func TestApp_AfterFunc(t *testing.T) {
 	counts := &opCounts{}
 	afterError := fmt.Errorf("fail")
diff --git a/internal/genflags/spec_test.go b/cmd/urfave-cli-genflags/main_test.go
similarity index 52%
rename from internal/genflags/spec_test.go
rename to cmd/urfave-cli-genflags/main_test.go
index 25a9c8b..b5c9fee 100644
--- a/internal/genflags/spec_test.go
+++ b/cmd/urfave-cli-genflags/main_test.go
@@ -1,29 +1,63 @@
-package genflags_test
+package main_test
 
 import (
+	"fmt"
 	"reflect"
 	"testing"
 
-	"github.com/urfave/cli/v2/internal/genflags"
+	main "github.com/urfave/cli/v2/cmd/urfave-cli-genflags"
 )
 
+func TestTypeName(t *testing.T) {
+	for _, tc := range []struct {
+		gt       string
+		fc       *main.FlagTypeConfig
+		expected string
+	}{
+		{gt: "int", fc: nil, expected: "IntFlag"},
+		{gt: "int", fc: &main.FlagTypeConfig{}, expected: "IntFlag"},
+		{gt: "int", fc: &main.FlagTypeConfig{TypeName: "VeryIntyFlag"}, expected: "VeryIntyFlag"},
+		{gt: "[]bool", fc: nil, expected: "BoolSliceFlag"},
+		{gt: "[]bool", fc: &main.FlagTypeConfig{}, expected: "BoolSliceFlag"},
+		{gt: "[]bool", fc: &main.FlagTypeConfig{TypeName: "ManyTruthsFlag"}, expected: "ManyTruthsFlag"},
+		{gt: "time.Rumination", fc: nil, expected: "RuminationFlag"},
+		{gt: "time.Rumination", fc: &main.FlagTypeConfig{}, expected: "RuminationFlag"},
+		{gt: "time.Rumination", fc: &main.FlagTypeConfig{TypeName: "PonderFlag"}, expected: "PonderFlag"},
+	} {
+		t.Run(
+			fmt.Sprintf("type=%s,cfg=%v", tc.gt, func() string {
+				if tc.fc != nil {
+					return tc.fc.TypeName
+				}
+				return "nil"
+			}()),
+			func(ct *testing.T) {
+				actual := main.TypeName(tc.gt, tc.fc)
+				if tc.expected != actual {
+					ct.Errorf("expected %q, got %q", tc.expected, actual)
+				}
+			},
+		)
+	}
+}
+
 func TestSpec_SortedFlagTypes(t *testing.T) {
-	spec := &genflags.Spec{
-		FlagTypes: map[string]*genflags.FlagTypeConfig{
-			"nerf": &genflags.FlagTypeConfig{},
+	spec := &main.Spec{
+		FlagTypes: map[string]*main.FlagTypeConfig{
+			"nerf": &main.FlagTypeConfig{},
 			"gerf": nil,
 		},
 	}
 
 	actual := spec.SortedFlagTypes()
-	expected := []*genflags.FlagType{
+	expected := []*main.FlagType{
 		{
 			GoType: "gerf",
 			Config: nil,
 		},
 		{
 			GoType: "nerf",
-			Config: &genflags.FlagTypeConfig{},
+			Config: &main.FlagTypeConfig{},
 		},
 	}
 	if !reflect.DeepEqual(expected, actual) {
@@ -31,12 +65,12 @@ func TestSpec_SortedFlagTypes(t *testing.T) {
 	}
 }
 
-func genFlagType() *genflags.FlagType {
-	return &genflags.FlagType{
+func genFlagType() *main.FlagType {
+	return &main.FlagType{
 		GoType: "blerf",
-		Config: &genflags.FlagTypeConfig{
+		Config: &main.FlagTypeConfig{
 			SkipInterfaces: []string{"fmt.Stringer"},
-			StructFields: []*genflags.FlagStructField{
+			StructFields: []*main.FlagStructField{
 				{
 					Name: "Foibles",
 					Type: "int",
diff --git a/context_test.go b/context_test.go
index 8475706..6601155 100644
--- a/context_test.go
+++ b/context_test.go
@@ -150,6 +150,31 @@ func TestContext_Value(t *testing.T) {
 	expect(t, c.Value("unknown-flag"), nil)
 }
 
+func TestContext_Value_InvalidFlagAccessHandler(t *testing.T) {
+	var flagName string
+	app := &App{
+		InvalidFlagAccessHandler: func(_ *Context, name string) {
+			flagName = name
+		},
+		Commands: []*Command{
+			{
+				Name: "command",
+				Subcommands: []*Command{
+					{
+						Name: "subcommand",
+						Action: func(ctx *Context) error {
+							ctx.Value("missing")
+							return nil
+						},
+					},
+				},
+			},
+		},
+	}
+	expect(t, app.Run([]string{"run", "command", "subcommand"}), nil)
+	expect(t, flagName, "missing")
+}
+
 func TestContext_Args(t *testing.T) {
 	set := flag.NewFlagSet("test", 0)
 	set.Bool("myflag", false, "doc")
@@ -258,6 +283,19 @@ func TestContext_Set(t *testing.T) {
 	expect(t, c.IsSet("int"), true)
 }
 
+func TestContext_Set_InvalidFlagAccessHandler(t *testing.T) {
+	set := flag.NewFlagSet("test", 0)
+	var flagName string
+	app := &App{
+		InvalidFlagAccessHandler: func(_ *Context, name string) {
+			flagName = name
+		},
+	}
+	c := NewContext(app, set, nil)
+	c.Set("missing", "")
+	expect(t, flagName, "missing")
+}
+
 func TestContext_LocalFlagNames(t *testing.T) {
 	set := flag.NewFlagSet("test", 0)
 	set.Bool("one-flag", false, "doc")
@@ -556,6 +594,14 @@ func TestCheckRequiredFlags(t *testing.T) {
 				&StringSliceFlag{Name: "names, n", Required: true},
 			},
 		},
+		{
+			testCase:              "required_flag_with_one_character",
+			expectedAnError:       true,
+			expectedErrorContents: []string{"Required flag \"n\" not set"},
+			flags: []Flag{
+				&StringFlag{Name: "n", Required: true},
+			},
+		},
 	}
 
 	for _, test := range tdata {
diff --git a/flag_test.go b/flag_test.go
index d3ded72..456d1b8 100644
--- a/flag_test.go
+++ b/flag_test.go
@@ -110,6 +110,10 @@ func TestFlagsFromEnv(t *testing.T) {
 		{"foobar", 0, &Int64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as int value from environment variable "SECONDS" for flag seconds: .*`},
 
 		{"1", 1, &IntFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
+		{"08", 8, &IntFlag{Name: "seconds", EnvVars: []string{"SECONDS"}, Base: 10}, ""},
+		{"755", 493, &IntFlag{Name: "seconds", EnvVars: []string{"SECONDS"}, Base: 8}, ""},
+		{"deadBEEF", 3735928559, &IntFlag{Name: "seconds", EnvVars: []string{"SECONDS"}, Base: 16}, ""},
+		{"08", 0, &IntFlag{Name: "seconds", EnvVars: []string{"SECONDS"}, Base: 0}, `could not parse "08" as int value from environment variable "SECONDS" for flag seconds: .*`},
 		{"1.2", 0, &IntFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2" as int value from environment variable "SECONDS" for flag seconds: .*`},
 		{"foobar", 0, &IntFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as int value from environment variable "SECONDS" for flag seconds: .*`},
 
@@ -130,10 +134,18 @@ func TestFlagsFromEnv(t *testing.T) {
 		{"foo,bar", newSetStringSlice("foo", "bar"), &StringSliceFlag{Name: "names", EnvVars: []string{"NAMES"}}, ""},
 
 		{"1", uint(1), &UintFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
+		{"08", uint(8), &UintFlag{Name: "seconds", EnvVars: []string{"SECONDS"}, Base: 10}, ""},
+		{"755", uint(493), &UintFlag{Name: "seconds", EnvVars: []string{"SECONDS"}, Base: 8}, ""},
+		{"deadBEEF", uint(3735928559), &UintFlag{Name: "seconds", EnvVars: []string{"SECONDS"}, Base: 16}, ""},
+		{"08", 0, &UintFlag{Name: "seconds", EnvVars: []string{"SECONDS"}, Base: 0}, `could not parse "08" as uint value from environment variable "SECONDS" for flag seconds: .*`},
 		{"1.2", 0, &UintFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2" as uint value from environment variable "SECONDS" for flag seconds: .*`},
 		{"foobar", 0, &UintFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as uint value from environment variable "SECONDS" for flag seconds: .*`},
 
 		{"1", uint64(1), &Uint64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
+		{"08", uint64(8), &Uint64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}, Base: 10}, ""},
+		{"755", uint64(493), &Uint64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}, Base: 8}, ""},
+		{"deadBEEF", uint64(3735928559), &Uint64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}, Base: 16}, ""},
+		{"08", 0, &Uint64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}, Base: 0}, `could not parse "08" as uint64 value from environment variable "SECONDS" for flag seconds: .*`},
 		{"1.2", 0, &Uint64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2" as uint64 value from environment variable "SECONDS" for flag seconds: .*`},
 		{"foobar", 0, &Uint64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as uint64 value from environment variable "SECONDS" for flag seconds: .*`},
 
@@ -2398,43 +2410,43 @@ func TestFlagDefaultValue(t *testing.T) {
 			name:    "stringSclice",
 			flag:    &StringSliceFlag{Name: "flag", Value: NewStringSlice("default1", "default2")},
 			toParse: []string{"--flag", "parsed"},
-			expect: `--flag value [ --flag value ]	(default: "default1", "default2")`,
+			expect:  `--flag value [ --flag value ]	(default: "default1", "default2")`,
 		},
 		{
 			name:    "float64Sclice",
 			flag:    &Float64SliceFlag{Name: "flag", Value: NewFloat64Slice(1.1, 2.2)},
 			toParse: []string{"--flag", "13.3"},
-			expect: `--flag value [ --flag value ]	(default: 1.1, 2.2)`,
+			expect:  `--flag value [ --flag value ]	(default: 1.1, 2.2)`,
 		},
 		{
 			name:    "int64Sclice",
 			flag:    &Int64SliceFlag{Name: "flag", Value: NewInt64Slice(1, 2)},
 			toParse: []string{"--flag", "13"},
-			expect: `--flag value [ --flag value ]	(default: 1, 2)`,
+			expect:  `--flag value [ --flag value ]	(default: 1, 2)`,
 		},
 		{
 			name:    "intSclice",
 			flag:    &IntSliceFlag{Name: "flag", Value: NewIntSlice(1, 2)},
 			toParse: []string{"--flag", "13"},
-			expect: `--flag value [ --flag value ]	(default: 1, 2)`,
+			expect:  `--flag value [ --flag value ]	(default: 1, 2)`,
 		},
 		{
 			name:    "string",
 			flag:    &StringFlag{Name: "flag", Value: "default"},
 			toParse: []string{"--flag", "parsed"},
-			expect: `--flag value	(default: "default")`,
+			expect:  `--flag value	(default: "default")`,
 		},
 		{
 			name:    "bool",
 			flag:    &BoolFlag{Name: "flag", Value: true},
 			toParse: []string{"--flag", "false"},
-			expect: `--flag	(default: true)`,
+			expect:  `--flag	(default: true)`,
 		},
 		{
 			name:    "uint64",
 			flag:    &Uint64Flag{Name: "flag", Value: 1},
 			toParse: []string{"--flag", "13"},
-			expect: `--flag value	(default: 1)`,
+			expect:  `--flag value	(default: 1)`,
 		},
 	}
 	for i, v := range cases {
diff --git a/help_test.go b/help_test.go
index 4feb7f0..9422b4a 100644
--- a/help_test.go
+++ b/help_test.go
@@ -895,6 +895,42 @@ App UsageText`,
 	}
 }
 
+func TestShowAppHelp_CommandMultiLine_UsageText(t *testing.T) {
+	app := &App{
+		UsageText: `This is a
+multi
+line
+App UsageText`,
+		Commands: []*Command{
+			{
+				Name:    "frobbly",
+				Aliases: []string{"frb1", "frbb2", "frl2"},
+				Usage:   "this is a long help output for the run command, long usage \noutput, long usage output, long usage output, long usage output\noutput, long usage output, long usage output",
+			},
+			{
+				Name:    "grobbly",
+				Aliases: []string{"grb1", "grbb2"},
+				Usage:   "this is another long help output for the run command, long usage \noutput, long usage output",
+			},
+		},
+	}
+
+	output := &bytes.Buffer{}
+	app.Writer = output
+
+	_ = app.Run([]string{"foo"})
+
+	expected := "COMMANDS:\n" +
+		"   frobbly, frb1, frbb2, frl2  this is a long help output for the run command, long usage \n" +
+		"                               output, long usage output, long usage output, long usage output\n" +
+		"                               output, long usage output, long usage output\n" +
+		"   grobbly, grb1, grbb2        this is another long help output for the run command, long usage \n" +
+		"                               output, long usage output"
+	if !strings.Contains(output.String(), expected) {
+		t.Errorf("expected output to include usage text; got: %q", output.String())
+	}
+}
+
 func TestHideHelpCommand(t *testing.T) {
 	app := &App{
 		HideHelpCommand: true,
diff --git a/internal/genflags/package_test.go b/internal/genflags/package_test.go
deleted file mode 100644
index 3920540..0000000
--- a/internal/genflags/package_test.go
+++ /dev/null
@@ -1,41 +0,0 @@
-package genflags_test
-
-import (
-	"fmt"
-	"testing"
-
-	"github.com/urfave/cli/v2/internal/genflags"
-)
-
-func TestTypeName(t *testing.T) {
-	for _, tc := range []struct {
-		gt       string
-		fc       *genflags.FlagTypeConfig
-		expected string
-	}{
-		{gt: "int", fc: nil, expected: "IntFlag"},
-		{gt: "int", fc: &genflags.FlagTypeConfig{}, expected: "IntFlag"},
-		{gt: "int", fc: &genflags.FlagTypeConfig{TypeName: "VeryIntyFlag"}, expected: "VeryIntyFlag"},
-		{gt: "[]bool", fc: nil, expected: "BoolSliceFlag"},
-		{gt: "[]bool", fc: &genflags.FlagTypeConfig{}, expected: "BoolSliceFlag"},
-		{gt: "[]bool", fc: &genflags.FlagTypeConfig{TypeName: "ManyTruthsFlag"}, expected: "ManyTruthsFlag"},
-		{gt: "time.Rumination", fc: nil, expected: "RuminationFlag"},
-		{gt: "time.Rumination", fc: &genflags.FlagTypeConfig{}, expected: "RuminationFlag"},
-		{gt: "time.Rumination", fc: &genflags.FlagTypeConfig{TypeName: "PonderFlag"}, expected: "PonderFlag"},
-	} {
-		t.Run(
-			fmt.Sprintf("type=%s,cfg=%v", tc.gt, func() string {
-				if tc.fc != nil {
-					return tc.fc.TypeName
-				}
-				return "nil"
-			}()),
-			func(ct *testing.T) {
-				actual := genflags.TypeName(tc.gt, tc.fc)
-				if tc.expected != actual {
-					ct.Errorf("expected %q, got %q", tc.expected, actual)
-				}
-			},
-		)
-	}
-}
diff --git a/testdata/godoc-v2.x.txt b/testdata/godoc-v2.x.txt
index 1ba48cc..6d0a908 100644
--- a/testdata/godoc-v2.x.txt
+++ b/testdata/godoc-v2.x.txt
@@ -64,8 +64,8 @@ GLOBAL OPTIONS:
 COPYRIGHT:
    {{wrap .Copyright 3}}{{end}}
 `
-    AppHelpTemplate is the text template for the Default help topic. cli.go uses
-    text/template to render templates. You can render custom help text by
+    AppHelpTemplate is the text template for the Default help topic. cli.go
+    uses text/template to render templates. You can render custom help text by
     setting this variable.
 
 var CommandHelpTemplate = `NAME:
@@ -201,9 +201,9 @@ func DefaultAppComplete(cCtx *Context)
 func DefaultCompleteWithFlags(cmd *Command) func(cCtx *Context)
 func FlagNames(name string, aliases []string) []string
 func HandleAction(action interface{}, cCtx *Context) (err error)
-    HandleAction attempts to figure out which Action signature was used. If it's
-    an ActionFunc or a func with the legacy signature for Action, the func is
-    run!
+    HandleAction attempts to figure out which Action signature was used.
+    If it's an ActionFunc or a func with the legacy signature for Action,
+    the func is run!
 
 func HandleExitCoder(err error)
     HandleExitCoder handles errors implementing ExitCoder by printing their
@@ -360,14 +360,14 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error)
     to generate command-specific flags
 
 func (a *App) RunContext(ctx context.Context, arguments []string) (err error)
-    RunContext is like Run except it takes a Context that will be passed to its
-    commands and sub-commands. Through this, you can propagate timeouts and
+    RunContext is like Run except it takes a Context that will be passed to
+    its commands and sub-commands. Through this, you can propagate timeouts and
     cancellation requests
 
 func (a *App) Setup()
-    Setup runs initialization code to ensure all data structures are ready for
-    `Run` or inspection prior to `Run`. It is internally called by `Run`, but
-    will return early if setup has already happened.
+    Setup runs initialization code to ensure all data structures are ready
+    for `Run` or inspection prior to `Run`. It is internally called by `Run`,
+    but will return early if setup has already happened.
 
 func (a *App) ToFishCompletion() (string, error)
     ToFishCompletion creates a fish completion string for the `*App` The
@@ -799,9 +799,9 @@ func Exit(message interface{}, exitCode int) ExitCoder
     Exit wraps a message and exit code into an error, which by default is
     handled with a call to os.Exit during default error handling.
 
-    This is the simplest way to trigger a non-zero exit code for an App without
-    having to call os.Exit manually. During testing, this behavior can be
-    avoided by overiding the ExitErrHandler function on an App or the
+    This is the simplest way to trigger a non-zero exit code for an App
+    without having to call os.Exit manually. During testing, this behavior
+    can be avoided by overiding the ExitErrHandler function on an App or the
     package-global OsExiter function.
 
 func NewExitError(message interface{}, exitCode int) ExitCoder
@@ -1431,8 +1431,8 @@ type MultiInt64Flag = SliceFlag[*Int64SliceFlag, []int64, int64]
     directly, as Value and/or Destination. See also SliceFlag.
 
 type MultiIntFlag = SliceFlag[*IntSliceFlag, []int, int]
-    MultiIntFlag extends IntSliceFlag with support for using slices directly, as
-    Value and/or Destination. See also SliceFlag.
+    MultiIntFlag extends IntSliceFlag with support for using slices directly,
+    as Value and/or Destination. See also SliceFlag.
 
 type MultiStringFlag = SliceFlag[*StringSliceFlag, []string, string]
     MultiStringFlag extends StringSliceFlag with support for using slices
@@ -1513,8 +1513,8 @@ type RequiredFlag interface {
 
 	IsRequired() bool
 }
-    RequiredFlag is an interface that allows us to mark flags as required it
-    allows flags required flags to be backwards compatible with the Flag
+    RequiredFlag is an interface that allows us to mark flags as required
+    it allows flags required flags to be backwards compatible with the Flag
     interface
 
 type Serializer interface {
@@ -1527,9 +1527,9 @@ type SliceFlag[T SliceFlagTarget[E], S ~[]E, E any] struct {
 	Value       S
 	Destination *S
 }
-    SliceFlag extends implementations like StringSliceFlag and IntSliceFlag with
-    support for using slices directly, as Value and/or Destination. See also
-    SliceFlagTarget, MultiStringFlag, MultiFloat64Flag, MultiInt64Flag,
+    SliceFlag extends implementations like StringSliceFlag and IntSliceFlag
+    with support for using slices directly, as Value and/or Destination.
+    See also SliceFlagTarget, MultiStringFlag, MultiFloat64Flag, MultiInt64Flag,
     MultiIntFlag.
 
 func (x *SliceFlag[T, S, E]) Apply(set *flag.FlagSet) error
@@ -1986,9 +1986,9 @@ func InitInputSource(flags []cli.Flag, createInputSource func() (InputSourceCont
     that are supported by the input source
 
 func InitInputSourceWithContext(flags []cli.Flag, createInputSource func(cCtx *cli.Context) (InputSourceContext, error)) cli.BeforeFunc
-    InitInputSourceWithContext is used to to setup an InputSourceContext on a
-    cli.Command Before method. It will create a new input source based on the
-    func provided with potentially using existing cli.Context values to
+    InitInputSourceWithContext is used to to setup an InputSourceContext on
+    a cli.Command Before method. It will create a new input source based on
+    the func provided with potentially using existing cli.Context values to
     initialize itself. If there is no error it will then apply the new input
     source to any flags that are supported by the input source