Repository: spf13/cobra
Pull request #1282 (2020-11-25 16:15:38)

fix home directory config not loading
### Issue 
The provision to use the convention of an extension-less config file from the user's home directory does not work as expected. The dependency being used to load the config file, `viper`, requires explicit knowledge of the "type" of the file, as it cannot deduce the implicit type from the file extension. Without knowledge of the file's "type", `viper` simply defaults to a "type" of `""`, which results in no attempt being made to load the file.

### Solution 
Specify the config type when specifying the extension-less config file name.

For example:
```go
viper.SetConfigName(".cobra")
viper.SetConfigType("yaml")
```

### Fix
This fix adds an explicit `viper.SetConfigType()` call at points referencing the typically extension-less config file

CLAassistant: [![CLA assistant check](https://cla-assistant.io/pull/badge/not_signed)](https://cla-assistant.io/spf13/cobra?pullRequest=1282) <br/>Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our [Contributor License Agreement](https://cla-assistant.io/spf13/cobra?pullRequest=1282) before we can accept your contribution.<br/><sub>You have signed the CLA already but the status is still pending? Let us [recheck](https://cla-assistant.io/check/spf13/cobra?pullRequest=1282) it.</sub>

Patch:
diff --git a/.github/labeler.yml b/.github/labeler.yml
new file mode 100644
index 0000000..bd2b3bf
--- /dev/null
+++ b/.github/labeler.yml
@@ -0,0 +1,16 @@
+# changes to documentation generation
+"area/doc-gen": doc/**/*
+
+# changes to the core Go cobra lib package
+"area/lib": ./*.go
+
+# changes to the Cobra CLI
+"area/cli": cobra/**/*
+
+# changes to Github workflows
+"area/github": .github/**/*
+
+# changes to shell completions
+"area/*sh completion":
+  - ./*completions*
+
diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml
new file mode 100644
index 0000000..ce4fc1f
--- /dev/null
+++ b/.github/workflows/labeler.yml
@@ -0,0 +1,12 @@
+name: "Pull Request Labeler"
+on:
+- pull_request_target
+
+jobs:
+  triage:
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/labeler@v3
+      with:
+        repo-token: "${{ github.token }}"
+
diff --git a/.golangci.yml b/.golangci.yml
new file mode 100644
index 0000000..0d6e617
--- /dev/null
+++ b/.golangci.yml
@@ -0,0 +1,48 @@
+run:
+  deadline: 5m
+
+linters:
+  disable-all: true
+  enable:
+    #- bodyclose
+    - deadcode
+    #- depguard
+    #- dogsled
+    #- dupl
+    - errcheck
+    #- exhaustive
+    #- funlen
+    - gas
+    #- gochecknoinits
+    - goconst
+    #- gocritic
+    #- gocyclo
+    #- gofmt
+    - goimports
+    - golint
+    #- gomnd
+    #- goprintffuncname
+    #- gosec
+    #- gosimple
+    - govet
+    - ineffassign
+    - interfacer
+    #- lll
+    - maligned
+    - megacheck
+    #- misspell
+    #- nakedret
+    #- noctx
+    #- nolintlint
+    #- rowserrcheck
+    #- scopelint
+    #- staticcheck
+    - structcheck
+    #- stylecheck
+    #- typecheck
+    - unconvert
+    #- unparam
+    #- unused
+    - varcheck
+    #- whitespace
+  fast: false
diff --git a/.travis.yml b/.travis.yml
index a9bd4e5..0821efd 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,29 +1,28 @@
 language: go
 
 stages:
-  - diff
   - test
   - build
 
 go:
-  - 1.12.x
-  - 1.13.x
+  - 1.14.x
+  - 1.15.x
   - tip
 
+env: GO111MODULE=on
+
 before_install:
   - go get -u github.com/kyoh86/richgo
   - go get -u github.com/mitchellh/gox
+  - curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin latest
 
 matrix:
   allow_failures:
     - go: tip
   include:
-    - stage: diff
-      go: 1.13.x
-      script: make fmt
     - stage: build
-      go: 1.13.x
+      go: 1.14.x
       script: make cobra_generator
 
-script: 
+script:
  - make test
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 742d6d6..8a23b4f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,11 +1,40 @@
 # Cobra Changelog
 
-## Pending
-* Fix man page doc generation - no auto generated tag when `cmd.DisableAutoGenTag = true` @jpmcb
+## v1.1.3
+
+* **Fix:** release-branch.cobra1.1 only: Revert "Deprecate Go < 1.14" to maintain backward compatibility
+
+## v1.1.2
+
+### Notable Changes
+
+* Bump license year to 2021 in golden files (#1309) @Bowbaq
+* Enhance PowerShell completion with custom comp (#1208) @Luap99
+* Update gopkg.in/yaml.v2 to v2.4.0: The previous breaking change in yaml.v2 v2.3.0 has been reverted, see go-yaml/yaml#670
+* Documentation readability improvements (#1228 etc.) @zaataylor etc.
+* Use golangci-lint: Repair warnings and errors resulting from linting (#1044) @umarcor
+
+## v1.1.1
+
+* **Fix:** yaml.v2 2.3.0 contained a unintended breaking change. This release reverts to yaml.v2 v2.2.8 which has recent critical CVE fixes, but does not have the breaking changes. See https://github.com/spf13/cobra/pull/1259 for context.
+* **Fix:** correct internal formatting for go-md2man v2 (which caused man page generation to be broken). See https://github.com/spf13/cobra/issues/1049 for context.
+
+## v1.1.0
+
+### Notable Changes
+
+* Extend Go completions and revamp zsh comp (#1070)
+* Fix man page doc generation - no auto generated tag when `cmd.DisableAutoGenTag = true` (#1104) @jpmcb
+* Add completion for help command (#1136)
+* Complete subcommands when TraverseChildren is set (#1171)
+* Fix stderr printing functions (#894)
+* fix: fish output redirection (#1247)
 
 ## v1.0.0
+
 Announcing v1.0.0 of Cobra. 🎉
-**Notable Changes**
+
+### Notable Changes
 * Fish completion (including support for Go custom completion) @marckhouzam
 * API (urgent): Rename BashCompDirectives to ShellCompDirectives @marckhouzam
 * Remove/replace SetOutput on Command - deprecated @jpmcb
diff --git a/CONDUCT.md b/CONDUCT.md
new file mode 100644
index 0000000..9d16f88
--- /dev/null
+++ b/CONDUCT.md
@@ -0,0 +1,37 @@
+## Cobra User Contract
+
+### Versioning
+Cobra will follow a steady release cadence. Non breaking changes will be released as minor versions quarterly. Patch bug releases are at the discretion of the maintainers. Users can expect security patch fixes to be released within relatively short order of a CVE becoming known. For more information on security patch fixes see the CVE section below. Releases will follow [Semantic Versioning](https://semver.org/). Users tracking the Master branch should expect unpredictable breaking changes as the project continues to move forward. For stability, it is highly recommended to use a release.
+
+### Backward Compatibility
+We will maintain two major releases in a moving window. The N-1 release will only receive bug fixes and security updates and will be dropped once N+1 is released.
+
+### Deprecation
+Deprecation of Go versions or dependent packages will only occur in major releases. To reduce the change of this taking users by surprise, any large deprecation will be preceded by an announcement in the [#cobra slack channel](https://gophers.slack.com/archives/CD3LP1199) and an Issue on Github.
+
+### CVE
+Maintainers will make every effort to release security patches in the case of a medium to high severity CVE directly impacting the library. The speed in which these patches reach a release is up to the discretion of the maintainers. A low severity CVE may be a lower priority than a high severity one.
+
+### Communication
+Cobra maintainers will use GitHub issues and the [#cobra slack channel](https://gophers.slack.com/archives/CD3LP1199) as the primary means of communication with the community. This is to foster open communication with all users and contributors.
+
+### Breaking Changes
+Breaking changes are generally allowed in the master branch, as this is the branch used to develop the next release of Cobra.
+
+There may be times, however, when master is closed for breaking changes. This is likely to happen as we near the release of a new version.
+
+Breaking changes are not allowed in release branches, as these represent minor versions that have already been released. These version have consumers who expect the APIs, behaviors, etc, to remain stable during the lifetime of the patch stream for the minor release.
+
+Examples of breaking changes include:
+- Removing or renaming exported constant, variable, type, or function.
+- Updating the version of critical libraries such as `spf13/pflag`, `spf13/viper` etc...
+  - Some version updates may be acceptable for picking up bug fixes, but maintainers must exercise caution when reviewing.
+
+There may, at times, need to be exceptions where breaking changes are allowed in release branches. These are at the discretion of the project's maintainers, and must be carefully considered before merging.
+
+### CI Testing
+Maintainers will ensure the Cobra test suite utilizes the current supported versions of Golang.
+
+### Disclaimer
+Changes to this document and the contents therein are at the discretion of the maintainers.
+None of the contents of this document are legally binding in any way to the maintainers or the users.
diff --git a/Makefile b/Makefile
index e9740d1..472c73b 100644
--- a/Makefile
+++ b/Makefile
@@ -1,21 +1,29 @@
 BIN="./bin"
 SRC=$(shell find . -name "*.go")
 
+ifeq (, $(shell which golangci-lint))
+$(warning "could not find golangci-lint in $(PATH), run: curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh")
+endif
+
 ifeq (, $(shell which richgo))
 $(warning "could not find richgo in $(PATH), run: go get github.com/kyoh86/richgo")
 endif
 
-.PHONY: fmt vet test cobra_generator install_deps clean
+.PHONY: fmt lint test cobra_generator install_deps clean
 
 default: all
 
-all: fmt vet test cobra_generator	
+all: fmt test cobra_generator
 
 fmt:
 	$(info ******************** checking formatting ********************)
 	@test -z $(shell gofmt -l $(SRC)) || (gofmt -d $(SRC); exit 1)
 
-test: install_deps vet
+lint:
+	$(info ******************** running lint tools ********************)
+	golangci-lint run -v
+
+test: install_deps lint
 	$(info ******************** running tests ********************)
 	richgo test -v ./...
 
@@ -28,9 +36,5 @@ install_deps:
 	$(info ******************** downloading dependencies ********************)
 	go get -v ./...
 
-vet:
-	$(info ******************** vetting ********************)
-	go vet ./...
-
 clean:
 	rm -rf $(BIN)
diff --git a/README.md b/README.md
index 3cf1b25..3c290f4 100644
--- a/README.md
+++ b/README.md
@@ -6,6 +6,7 @@ Cobra is used in many Go projects such as [Kubernetes](http://kubernetes.io/),
 [Hugo](https://gohugo.io), and [Github CLI](https://github.com/cli/cli) to
 name a few. [This list](./projects_using_cobra.md) contains a more extensive list of projects using Cobra.
 
+[![](https://img.shields.io/github/workflow/status/spf13/cobra/Test?longCache=tru&label=Test&logo=github%20actions&logoColor=fff)](https://github.com/spf13/cobra/actions?query=workflow%3ATest)
 [![Build Status](https://travis-ci.org/spf13/cobra.svg "Travis CI status")](https://travis-ci.org/spf13/cobra)
 [![GoDoc](https://godoc.org/github.com/spf13/cobra?status.svg)](https://godoc.org/github.com/spf13/cobra)
 [![Go Report Card](https://goreportcard.com/badge/github.com/spf13/cobra)](https://goreportcard.com/report/github.com/spf13/cobra)
@@ -62,8 +63,8 @@ Cobra is built on a structure of commands, arguments & flags.
 
 **Commands** represent actions, **Args** are things and **Flags** are modifiers for those actions.
 
-The best applications will read like sentences when used. Users will know how
-to use the application because they will natively understand how to use it.
+The best applications read like sentences when used, and as a result, users
+intuitively know how to interact with them.
 
 The pattern to follow is
 `APPNAME VERB NOUN --ADJECTIVE.`
@@ -234,11 +235,6 @@ func init() {
 	rootCmd.AddCommand(initCmd)
 }
 
-func er(msg interface{}) {
-	fmt.Println("Error:", msg)
-	os.Exit(1)
-}
-
 func initConfig() {
 	if cfgFile != "" {
 		// Use config file from the flag.
@@ -246,12 +242,11 @@ func initConfig() {
 	} else {
 		// Find home directory.
 		home, err := homedir.Dir()
-		if err != nil {
-			er(err)
-		}
+		cobra.CheckErr(err)
 
 		// Search config in home directory with name ".cobra" (without extension).
 		viper.AddConfigPath(home)
+		viper.SetConfigType("yaml")
 		viper.SetConfigName(".cobra")
 	}
 
@@ -268,7 +263,7 @@ func initConfig() {
 With the root command you need to have your main function execute it.
 Execute should be run on the root for clarity, though it can be called on any command.
 
-In a Cobra app, typically the main.go file is very bare. It serves, one purpose, to initialize Cobra.
+In a Cobra app, typically the main.go file is very bare. It serves one purpose: to initialize Cobra.
 
 ```go
 package main
@@ -363,7 +358,7 @@ There are two different approaches to assign a flag.
 
 ### Persistent Flags
 
-A flag can be 'persistent' meaning that this flag will be available to the
+A flag can be 'persistent', meaning that this flag will be available to the
 command it's assigned to as well as every command under that command. For
 global flags, assign a flag as a persistent flag on the root.
 
@@ -373,7 +368,7 @@ rootCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose out
 
 ### Local Flags
 
-A flag can also be assigned locally which will only apply to that specific command.
+A flag can also be assigned locally, which will only apply to that specific command.
 
 ```go
 localCmd.Flags().StringVarP(&Source, "source", "s", "", "Source directory to read from")
@@ -381,8 +376,8 @@ localCmd.Flags().StringVarP(&Source, "source", "s", "", "Source directory to rea
 
 ### Local Flag on Parent Commands
 
-By default Cobra only parses local flags on the target command, any local flags on
-parent commands are ignored. By enabling `Command.TraverseChildren` Cobra will
+By default, Cobra only parses local flags on the target command, and any local flags on
+parent commands are ignored. By enabling `Command.TraverseChildren`, Cobra will
 parse local flags on each command before executing the target command.
 
 ```go
@@ -404,8 +399,8 @@ func init() {
 }
 ```
 
-In this example the persistent flag `author` is bound with `viper`.
-**Note**, that the variable `author` will not be set to the value from config,
+In this example, the persistent flag `author` is bound with `viper`.
+**Note**: the variable `author` will not be set to the value from config,
 when the `--author` flag is not provided by user.
 
 More in [viper documentation](https://github.com/spf13/viper#working-with-flags).
@@ -465,7 +460,7 @@ var cmd = &cobra.Command{
 
 In the example below, we have defined three commands. Two are at the top level
 and one (cmdTimes) is a child of one of the top commands. In this case the root
-is not executable meaning that a subcommand is required. This is accomplished
+is not executable, meaning that a subcommand is required. This is accomplished
 by not providing a 'Run' for the 'rootCmd'.
 
 We have only defined one flag for a single command.
@@ -759,7 +754,7 @@ Cobra can generate documentation based on subcommands, flags, etc. Read more abo
 
 ## Generating shell completions
 
-Cobra can generate a shell-completion file for the following shells: Bash, Zsh, Fish, Powershell. If you add more information to your commands, these completions can be amazingly powerful and flexible.  Read more about it in [Shell Completions](shell_completions.md).
+Cobra can generate a shell-completion file for the following shells: bash, zsh, fish, PowerShell. If you add more information to your commands, these completions can be amazingly powerful and flexible.  Read more about it in [Shell Completions](shell_completions.md).
 
 # License
 
diff --git a/bash_completions.go b/bash_completions.go
index 846636d..b47f898 100644
--- a/bash_completions.go
+++ b/bash_completions.go
@@ -19,9 +19,9 @@ const (
 	BashCompSubdirsInDir    = "cobra_annotation_bash_completion_subdirs_in_dir"
 )
 
-func writePreamble(buf *bytes.Buffer, name string) {
-	buf.WriteString(fmt.Sprintf("# bash completion for %-36s -*- shell-script -*-\n", name))
-	buf.WriteString(fmt.Sprintf(`
+func writePreamble(buf io.StringWriter, name string) {
+	WriteStringAndCheck(buf, fmt.Sprintf("# bash completion for %-36s -*- shell-script -*-\n", name))
+	WriteStringAndCheck(buf, fmt.Sprintf(`
 __%[1]s_debug()
 {
     if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then
@@ -380,11 +380,11 @@ __%[1]s_handle_word()
 		ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs))
 }
 
-func writePostscript(buf *bytes.Buffer, name string) {
+func writePostscript(buf io.StringWriter, name string) {
 	name = strings.Replace(name, ":", "__", -1)
-	buf.WriteString(fmt.Sprintf("__start_%s()\n", name))
-	buf.WriteString(fmt.Sprintf(`{
-    local cur prev words cword
+	WriteStringAndCheck(buf, fmt.Sprintf("__start_%s()\n", name))
+	WriteStringAndCheck(buf, fmt.Sprintf(`{
+    local cur prev words cword split
     declare -A flaghash 2>/dev/null || :
     declare -A aliashash 2>/dev/null || :
     if declare -F _init_completion >/dev/null 2>&1; then
@@ -400,43 +400,45 @@ func writePostscript(buf *bytes.Buffer, name string) {
     local flags_with_completion=()
     local flags_completion=()
     local commands=("%[1]s")
+    local command_aliases=()
     local must_have_one_flag=()
     local must_have_one_noun=()
     local has_completion_function
     local last_command
     local nouns=()
+    local noun_aliases=()
 
     __%[1]s_handle_word
 }
 
 `, name))
-	buf.WriteString(fmt.Sprintf(`if [[ $(type -t compopt) = "builtin" ]]; then
+	WriteStringAndCheck(buf, fmt.Sprintf(`if [[ $(type -t compopt) = "builtin" ]]; then
     complete -o default -F __start_%s %s
 else
     complete -o default -o nospace -F __start_%s %s
 fi
 
 `, name, name, name, name))
-	buf.WriteString("# ex: ts=4 sw=4 et filetype=sh\n")
+	WriteStringAndCheck(buf, "# ex: ts=4 sw=4 et filetype=sh\n")
 }
 
-func writeCommands(buf *bytes.Buffer, cmd *Command) {
-	buf.WriteString("    commands=()\n")
+func writeCommands(buf io.StringWriter, cmd *Command) {
+	WriteStringAndCheck(buf, "    commands=()\n")
 	for _, c := range cmd.Commands() {
 		if !c.IsAvailableCommand() && c != cmd.helpCommand {
 			continue
 		}
-		buf.WriteString(fmt.Sprintf("    commands+=(%q)\n", c.Name()))
+		WriteStringAndCheck(buf, fmt.Sprintf("    commands+=(%q)\n", c.Name()))
 		writeCmdAliases(buf, c)
 	}
-	buf.WriteString("\n")
+	WriteStringAndCheck(buf, "\n")
 }
 
-func writeFlagHandler(buf *bytes.Buffer, name string, annotations map[string][]string, cmd *Command) {
+func writeFlagHandler(buf io.StringWriter, name string, annotations map[string][]string, cmd *Command) {
 	for key, value := range annotations {
 		switch key {
 		case BashCompFilenameExt:
-			buf.WriteString(fmt.Sprintf("    flags_with_completion+=(%q)\n", name))
+			WriteStringAndCheck(buf, fmt.Sprintf("    flags_with_completion+=(%q)\n", name))
 
 			var ext string
 			if len(value) > 0 {
@@ -444,17 +446,18 @@ func writeFlagHandler(buf *bytes.Buffer, name string, annotations map[string][]s
 			} else {
 				ext = "_filedir"
 			}
-			buf.WriteString(fmt.Sprintf("    flags_completion+=(%q)\n", ext))
+			WriteStringAndCheck(buf, fmt.Sprintf("    flags_completion+=(%q)\n", ext))
 		case BashCompCustom:
-			buf.WriteString(fmt.Sprintf("    flags_with_completion+=(%q)\n", name))
+			WriteStringAndCheck(buf, fmt.Sprintf("    flags_with_completion+=(%q)\n", name))
+
 			if len(value) > 0 {
 				handlers := strings.Join(value, "; ")
-				buf.WriteString(fmt.Sprintf("    flags_completion+=(%q)\n", handlers))
+				WriteStringAndCheck(buf, fmt.Sprintf("    flags_completion+=(%q)\n", handlers))
 			} else {
-				buf.WriteString("    flags_completion+=(:)\n")
+				WriteStringAndCheck(buf, "    flags_completion+=(:)\n")
 			}
 		case BashCompSubdirsInDir:
-			buf.WriteString(fmt.Sprintf("    flags_with_completion+=(%q)\n", name))
+			WriteStringAndCheck(buf, fmt.Sprintf("    flags_with_completion+=(%q)\n", name))
 
 			var ext string
 			if len(value) == 1 {
@@ -462,46 +465,48 @@ func writeFlagHandler(buf *bytes.Buffer, name string, annotations map[string][]s
 			} else {
 				ext = "_filedir -d"
 			}
-			buf.WriteString(fmt.Sprintf("    flags_completion+=(%q)\n", ext))
+			WriteStringAndCheck(buf, fmt.Sprintf("    flags_completion+=(%q)\n", ext))
 		}
 	}
 }
 
-func writeShortFlag(buf *bytes.Buffer, flag *pflag.Flag, cmd *Command) {
+const cbn = "\")\n"
+
+func writeShortFlag(buf io.StringWriter, flag *pflag.Flag, cmd *Command) {
 	name := flag.Shorthand
 	format := "    "
 	if len(flag.NoOptDefVal) == 0 {
 		format += "two_word_"
 	}
-	format += "flags+=(\"-%s\")\n"
-	buf.WriteString(fmt.Sprintf(format, name))
+	format += "flags+=(\"-%s" + cbn
+	WriteStringAndCheck(buf, fmt.Sprintf(format, name))
 	writeFlagHandler(buf, "-"+name, flag.Annotations, cmd)
 }
 
-func writeFlag(buf *bytes.Buffer, flag *pflag.Flag, cmd *Command) {
+func writeFlag(buf io.StringWriter, flag *pflag.Flag, cmd *Command) {
 	name := flag.Name
 	format := "    flags+=(\"--%s"
 	if len(flag.NoOptDefVal) == 0 {
 		format += "="
 	}
-	format += "\")\n"
-	buf.WriteString(fmt.Sprintf(format, name))
+	format += cbn
+	WriteStringAndCheck(buf, fmt.Sprintf(format, name))
 	if len(flag.NoOptDefVal) == 0 {
-		format = "    two_word_flags+=(\"--%s\")\n"
-		buf.WriteString(fmt.Sprintf(format, name))
+		format = "    two_word_flags+=(\"--%s" + cbn
+		WriteStringAndCheck(buf, fmt.Sprintf(format, name))
 	}
 	writeFlagHandler(buf, "--"+name, flag.Annotations, cmd)
 }
 
-func writeLocalNonPersistentFlag(buf *bytes.Buffer, flag *pflag.Flag) {
+func writeLocalNonPersistentFlag(buf io.StringWriter, flag *pflag.Flag) {
 	name := flag.Name
-	format := "    local_nonpersistent_flags+=(\"--%[1]s\")\n"
+	format := "    local_nonpersistent_flags+=(\"--%[1]s" + cbn
 	if len(flag.NoOptDefVal) == 0 {
-		format += "    local_nonpersistent_flags+=(\"--%[1]s=\")\n"
+		format += "    local_nonpersistent_flags+=(\"--%[1]s=" + cbn
 	}
-	buf.WriteString(fmt.Sprintf(format, name))
+	WriteStringAndCheck(buf, fmt.Sprintf(format, name))
 	if len(flag.Shorthand) > 0 {
-		buf.WriteString(fmt.Sprintf("    local_nonpersistent_flags+=(\"-%s\")\n", flag.Shorthand))
+		WriteStringAndCheck(buf, fmt.Sprintf("    local_nonpersistent_flags+=(\"-%s\")\n", flag.Shorthand))
 	}
 }
 
@@ -519,9 +524,9 @@ func prepareCustomAnnotationsForFlags(cmd *Command) {
 	}
 }
 
-func writeFlags(buf *bytes.Buffer, cmd *Command) {
+func writeFlags(buf io.StringWriter, cmd *Command) {
 	prepareCustomAnnotationsForFlags(cmd)
-	buf.WriteString(`    flags=()
+	WriteStringAndCheck(buf, `    flags=()
     two_word_flags=()
     local_nonpersistent_flags=()
     flags_with_completion=()
@@ -553,11 +558,11 @@ func writeFlags(buf *bytes.Buffer, cmd *Command) {
 		}
 	})
 
-	buf.WriteString("\n")
+	WriteStringAndCheck(buf, "\n")
 }
 
-func writeRequiredFlag(buf *bytes.Buffer, cmd *Command) {
-	buf.WriteString("    must_have_one_flag=()\n")
+func writeRequiredFlag(buf io.StringWriter, cmd *Command) {
+	WriteStringAndCheck(buf, "    must_have_one_flag=()\n")
 	flags := cmd.NonInheritedFlags()
 	flags.VisitAll(func(flag *pflag.Flag) {
 		if nonCompletableFlag(flag) {
@@ -570,55 +575,55 @@ func writeRequiredFlag(buf *bytes.Buffer, cmd *Command) {
 				if flag.Value.Type() != "bool" {
 					format += "="
 				}
-				format += "\")\n"
-				buf.WriteString(fmt.Sprintf(format, flag.Name))
+				format += cbn
+				WriteStringAndCheck(buf, fmt.Sprintf(format, flag.Name))
 
 				if len(flag.Shorthand) > 0 {
-					buf.WriteString(fmt.Sprintf("    must_have_one_flag+=(\"-%s\")\n", flag.Shorthand))
+					WriteStringAndCheck(buf, fmt.Sprintf("    must_have_one_flag+=(\"-%s"+cbn, flag.Shorthand))
 				}
 			}
 		}
 	})
 }
 
-func writeRequiredNouns(buf *bytes.Buffer, cmd *Command) {
-	buf.WriteString("    must_have_one_noun=()\n")
-	sort.Sort(sort.StringSlice(cmd.ValidArgs))
+func writeRequiredNouns(buf io.StringWriter, cmd *Command) {
+	WriteStringAndCheck(buf, "    must_have_one_noun=()\n")
+	sort.Strings(cmd.ValidArgs)
 	for _, value := range cmd.ValidArgs {
 		// Remove any description that may be included following a tab character.
 		// Descriptions are not supported by bash completion.
 		value = strings.Split(value, "\t")[0]
-		buf.WriteString(fmt.Sprintf("    must_have_one_noun+=(%q)\n", value))
+		WriteStringAndCheck(buf, fmt.Sprintf("    must_have_one_noun+=(%q)\n", value))
 	}
 	if cmd.ValidArgsFunction != nil {
-		buf.WriteString("    has_completion_function=1\n")
+		WriteStringAndCheck(buf, "    has_completion_function=1\n")
 	}
 }
 
-func writeCmdAliases(buf *bytes.Buffer, cmd *Command) {
+func writeCmdAliases(buf io.StringWriter, cmd *Command) {
 	if len(cmd.Aliases) == 0 {
 		return
 	}
 
-	sort.Sort(sort.StringSlice(cmd.Aliases))
+	sort.Strings(cmd.Aliases)
 
-	buf.WriteString(fmt.Sprint(`    if [[ -z "${BASH_VERSION}" || "${BASH_VERSINFO[0]}" -gt 3 ]]; then`, "\n"))
+	WriteStringAndCheck(buf, fmt.Sprint(`    if [[ -z "${BASH_VERSION}" || "${BASH_VERSINFO[0]}" -gt 3 ]]; then`, "\n"))
 	for _, value := range cmd.Aliases {
-		buf.WriteString(fmt.Sprintf("        command_aliases+=(%q)\n", value))
-		buf.WriteString(fmt.Sprintf("        aliashash[%q]=%q\n", value, cmd.Name()))
+		WriteStringAndCheck(buf, fmt.Sprintf("        command_aliases+=(%q)\n", value))
+		WriteStringAndCheck(buf, fmt.Sprintf("        aliashash[%q]=%q\n", value, cmd.Name()))
 	}
-	buf.WriteString(`    fi`)
-	buf.WriteString("\n")
+	WriteStringAndCheck(buf, `    fi`)
+	WriteStringAndCheck(buf, "\n")
 }
-func writeArgAliases(buf *bytes.Buffer, cmd *Command) {
-	buf.WriteString("    noun_aliases=()\n")
-	sort.Sort(sort.StringSlice(cmd.ArgAliases))
+func writeArgAliases(buf io.StringWriter, cmd *Command) {
+	WriteStringAndCheck(buf, "    noun_aliases=()\n")
+	sort.Strings(cmd.ArgAliases)
 	for _, value := range cmd.ArgAliases {
-		buf.WriteString(fmt.Sprintf("    noun_aliases+=(%q)\n", value))
+		WriteStringAndCheck(buf, fmt.Sprintf("    noun_aliases+=(%q)\n", value))
 	}
 }
 
-func gen(buf *bytes.Buffer, cmd *Command) {
+func gen(buf io.StringWriter, cmd *Command) {
 	for _, c := range cmd.Commands() {
 		if !c.IsAvailableCommand() && c != cmd.helpCommand {
 			continue
@@ -630,22 +635,22 @@ func gen(buf *bytes.Buffer, cmd *Command) {
 	commandName = strings.Replace(commandName, ":", "__", -1)
 
 	if cmd.Root() == cmd {
-		buf.WriteString(fmt.Sprintf("_%s_root_command()\n{\n", commandName))
+		WriteStringAndCheck(buf, fmt.Sprintf("_%s_root_command()\n{\n", commandName))
 	} else {
-		buf.WriteString(fmt.Sprintf("_%s()\n{\n", commandName))
+		WriteStringAndCheck(buf, fmt.Sprintf("_%s()\n{\n", commandName))
 	}
 
-	buf.WriteString(fmt.Sprintf("    last_command=%q\n", commandName))
-	buf.WriteString("\n")
-	buf.WriteString("    command_aliases=()\n")
-	buf.WriteString("\n")
+	WriteStringAndCheck(buf, fmt.Sprintf("    last_command=%q\n", commandName))
+	WriteStringAndCheck(buf, "\n")
+	WriteStringAndCheck(buf, "    command_aliases=()\n")
+	WriteStringAndCheck(buf, "\n")
 
 	writeCommands(buf, cmd)
 	writeFlags(buf, cmd)
 	writeRequiredFlag(buf, cmd)
 	writeRequiredNouns(buf, cmd)
 	writeArgAliases(buf, cmd)
-	buf.WriteString("}\n\n")
+	WriteStringAndCheck(buf, "}\n\n")
 }
 
 // GenBashCompletion generates bash completion file and writes to the passed writer.
diff --git a/bash_completions.md b/bash_completions.md
index a82d5bb..130f99b 100644
--- a/bash_completions.md
+++ b/bash_completions.md
@@ -4,7 +4,7 @@ Please refer to [Shell Completions](shell_completions.md) for details.
 
 ## Bash legacy dynamic completions
 
-For backwards-compatibility, Cobra still supports its legacy dynamic completion solution (described below).  Unlike the `ValidArgsFunction` solution, the legacy solution will only work for Bash shell-completion and not for other shells. This legacy solution can be used along-side `ValidArgsFunction` and `RegisterFlagCompletionFunc()`, as long as both solutions are not used for the same command.  This provides a path to gradually migrate from the legacy solution to the new solution.
+For backward compatibility, Cobra still supports its legacy dynamic completion solution (described below).  Unlike the `ValidArgsFunction` solution, the legacy solution will only work for Bash shell-completion and not for other shells. This legacy solution can be used along-side `ValidArgsFunction` and `RegisterFlagCompletionFunc()`, as long as both solutions are not used for the same command.  This provides a path to gradually migrate from the legacy solution to the new solution.
 
 The legacy solution allows you to inject bash functions into the bash completion script.  Those bash functions are responsible for providing the completion choices for your own completions.
 
diff --git a/cobra.go b/cobra.go
index d01becc..d6cbfd7 100644
--- a/cobra.go
+++ b/cobra.go
@@ -19,6 +19,7 @@ package cobra
 import (
 	"fmt"
 	"io"
+	"os"
 	"reflect"
 	"strconv"
 	"strings"
@@ -205,3 +206,17 @@ func stringInSlice(a string, list []string) bool {
 	}
 	return false
 }
+
+// CheckErr prints the msg with the prefix 'Error:' and exits with error code 1. If the msg is nil, it does nothing.
+func CheckErr(msg interface{}) {
+	if msg != nil {
+		fmt.Fprintln(os.Stderr, "Error:", msg)
+		os.Exit(1)
+	}
+}
+
+// WriteStringAndCheck writes a string into a buffer, and checks if the error is not nil.
+func WriteStringAndCheck(b io.StringWriter, s string) {
+	_, err := b.WriteString(s)
+	CheckErr(err)
+}
diff --git a/cobra/cmd/add.go b/cobra/cmd/add.go
index 6645a75..8377411 100644
--- a/cobra/cmd/add.go
+++ b/cobra/cmd/add.go
@@ -40,13 +40,11 @@ Example: cobra add server -> resulting in a new cmd/server.go`,
 
 		Run: func(cmd *cobra.Command, args []string) {
 			if len(args) < 1 {
-				er("add needs a name for the command")
+				cobra.CheckErr(fmt.Errorf("add needs a name for the command"))
 			}
 
 			wd, err := os.Getwd()
-			if err != nil {
-				er(err)
-			}
+			cobra.CheckErr(err)
 
 			commandName := validateCmdName(args[0])
 			command := &Command{
@@ -59,10 +57,7 @@ Example: cobra add server -> resulting in a new cmd/server.go`,
 				},
 			}
 
-			err = command.Create()
-			if err != nil {
-				er(err)
-			}
+			cobra.CheckErr(command.Create())
 
 			fmt.Printf("%s created at %s\n", command.CmdName, command.AbsolutePath)
 		},
@@ -72,7 +67,7 @@ Example: cobra add server -> resulting in a new cmd/server.go`,
 func init() {
 	addCmd.Flags().StringVarP(&packageName, "package", "t", "", "target package name (e.g. github.com/spf13/hugo)")
 	addCmd.Flags().StringVarP(&parentName, "parent", "p", "rootCmd", "variable name of parent command for this command")
-	addCmd.Flags().MarkDeprecated("package", "this operation has been removed.")
+	cobra.CheckErr(addCmd.Flags().MarkDeprecated("package", "this operation has been removed."))
 }
 
 // validateCmdName returns source without any dashes and underscore.
diff --git a/cobra/cmd/helpers.go b/cobra/cmd/helpers.go
index cd94b3e..6a8047e 100644
--- a/cobra/cmd/helpers.go
+++ b/cobra/cmd/helpers.go
@@ -14,14 +14,12 @@
 package cmd
 
 import (
-	"bytes"
-	"fmt"
-	"io"
 	"os"
 	"os/exec"
 	"path/filepath"
 	"strings"
-	"text/template"
+
+	"github.com/spf13/cobra"
 )
 
 var srcPaths []string
@@ -43,14 +41,12 @@ func init() {
 		}
 
 		out, err := exec.Command(goExecutable, "env", "GOPATH").Output()
-		if err != nil {
-			er(err)
-		}
+		cobra.CheckErr(err)
 
 		toolchainGoPath := strings.TrimSpace(string(out))
 		goPaths = filepath.SplitList(toolchainGoPath)
 		if len(goPaths) == 0 {
-			er("$GOPATH is not set")
+			cobra.CheckErr("$GOPATH is not set")
 		}
 	}
 	srcPaths = make([]string, 0, len(goPaths))
@@ -58,111 +54,3 @@ func init() {
 		srcPaths = append(srcPaths, filepath.Join(goPath, "src"))
 	}
 }
-
-func er(msg interface{}) {
-	fmt.Println("Error:", msg)
-	os.Exit(1)
-}
-
-// isEmpty checks if a given path is empty.
-// Hidden files in path are ignored.
-func isEmpty(path string) bool {
-	fi, err := os.Stat(path)
-	if err != nil {
-		er(err)
-	}
-
-	if !fi.IsDir() {
-		return fi.Size() == 0
-	}
-
-	f, err := os.Open(path)
-	if err != nil {
-		er(err)
-	}
-	defer f.Close()
-
-	names, err := f.Readdirnames(-1)
-	if err != nil && err != io.EOF {
-		er(err)
-	}
-
-	for _, name := range names {
-		if len(name) > 0 && name[0] != '.' {
-			return false
-		}
-	}
-	return true
-}
-
-// exists checks if a file or directory exists.
-func exists(path string) bool {
-	if path == "" {
-		return false
-	}
-	_, err := os.Stat(path)
-	if err == nil {
-		return true
-	}
-	if !os.IsNotExist(err) {
-		er(err)
-	}
-	return false
-}
-
-func executeTemplate(tmplStr string, data interface{}) (string, error) {
-	tmpl, err := template.New("").Funcs(template.FuncMap{"comment": commentifyString}).Parse(tmplStr)
-	if err != nil {
-		return "", err
-	}
-
-	buf := new(bytes.Buffer)
-	err = tmpl.Execute(buf, data)
-	return buf.String(), err
-}
-
-func writeStringToFile(path string, s string) error {
-	return writeToFile(path, strings.NewReader(s))
-}
-
-// writeToFile writes r to file with path only
-// if file/directory on given path doesn't exist.
-func writeToFile(path string, r io.Reader) error {
-	if exists(path) {
-		return fmt.Errorf("%v already exists", path)
-	}
-
-	dir := filepath.Dir(path)
-	if dir != "" {
-		if err := os.MkdirAll(dir, 0777); err != nil {
-			return err
-		}
-	}
-
-	file, err := os.Create(path)
-	if err != nil {
-		return err
-	}
-	defer file.Close()
-
-	_, err = io.Copy(file, r)
-	return err
-}
-
-// commentfyString comments every line of in.
-func commentifyString(in string) string {
-	var newlines []string
-	lines := strings.Split(in, "\n")
-	for _, line := range lines {
-		if strings.HasPrefix(line, "//") {
-			newlines = append(newlines, line)
-		} else {
-			if line == "" {
-				newlines = append(newlines, "//")
-			} else {
-				newlines = append(newlines, "// "+line)
-			}
-		}
-	}
-	return strings.Join(newlines, "\n")
-}
diff --git a/cobra/cmd/init.go b/cobra/cmd/init.go
index 504a478..8c0e617 100644
--- a/cobra/cmd/init.go
+++ b/cobra/cmd/init.go
@@ -39,9 +39,7 @@ and the appropriate structure for a Cobra-based CLI application.
 		Run: func(_ *cobra.Command, args []string) {
 
 			projectPath, err := initializeProject(args)
-			if err != nil {
-				er(err)
-			}
+			cobra.CheckErr(err)
 			fmt.Printf("Your Cobra application is ready at\n%s\n", projectPath)
 		},
 	}
@@ -49,7 +47,7 @@ and the appropriate structure for a Cobra-based CLI application.
 
 func init() {
 	initCmd.Flags().StringVar(&pkgName, "pkg-name", "", "fully qualified pkg name")
-	initCmd.MarkFlagRequired("pkg-name")
+	cobra.CheckErr(initCmd.MarkFlagRequired("pkg-name"))
 }
 
 func initializeProject(args []string) (string, error) {
diff --git a/cobra/cmd/licenses.go b/cobra/cmd/licenses.go
index a070134..2b3a424 100644
--- a/cobra/cmd/licenses.go
+++ b/cobra/cmd/licenses.go
@@ -16,9 +16,11 @@
 package cmd
 
 import (
+	"fmt"
 	"strings"
 	"time"
 
+	"github.com/spf13/cobra"
 	"github.com/spf13/viper"
 )
 
@@ -92,7 +94,7 @@ func copyrightLine() string {
 func findLicense(name string) License {
 	found := matchLicense(name)
 	if found == "" {
-		er("unknown license: " + name)
+		cobra.CheckErr(fmt.Errorf("unknown license: " + name))
 	}
 	return Licenses[found]
 }
diff --git a/cobra/cmd/project.go b/cobra/cmd/project.go
index ecd783d..bd68a31 100644
--- a/cobra/cmd/project.go
+++ b/cobra/cmd/project.go
@@ -5,6 +5,7 @@ import (
 	"os"
 	"text/template"
 
+	"github.com/spf13/cobra"
 	"github.com/spf13/cobra/cobra/tpl"
 )
 
@@ -49,7 +50,7 @@ func (p *Project) Create() error {
 
 	// create cmd/root.go
 	if _, err = os.Stat(fmt.Sprintf("%s/cmd", p.AbsolutePath)); os.IsNotExist(err) {
-		os.Mkdir(fmt.Sprintf("%s/cmd", p.AbsolutePath), 0751)
+		cobra.CheckErr(os.Mkdir(fmt.Sprintf("%s/cmd", p.AbsolutePath), 0751))
 	}
 	rootFile, err := os.Create(fmt.Sprintf("%s/cmd/root.go", p.AbsolutePath))
 	if err != nil {
diff --git a/cobra/cmd/root.go b/cobra/cmd/root.go
index 97f404b..86254c6 100644
--- a/cobra/cmd/root.go
+++ b/cobra/cmd/root.go
@@ -47,8 +47,8 @@ func init() {
 	rootCmd.PersistentFlags().StringP("author", "a", "YOUR NAME", "author name for copyright attribution")
 	rootCmd.PersistentFlags().StringVarP(&userLicense, "license", "l", "", "name of license for the project")
 	rootCmd.PersistentFlags().Bool("viper", true, "use Viper for configuration")
-	viper.BindPFlag("author", rootCmd.PersistentFlags().Lookup("author"))
-	viper.BindPFlag("useViper", rootCmd.PersistentFlags().Lookup("viper"))
+	cobra.CheckErr(viper.BindPFlag("author", rootCmd.PersistentFlags().Lookup("author")))
+	cobra.CheckErr(viper.BindPFlag("useViper", rootCmd.PersistentFlags().Lookup("viper")))
 	viper.SetDefault("author", "NAME HERE <EMAIL ADDRESS>")
 	viper.SetDefault("license", "apache")
 
@@ -63,12 +63,11 @@ func initConfig() {
 	} else {
 		// Find home directory.
 		home, err := homedir.Dir()
-		if err != nil {
-			er(err)
-		}
+		cobra.CheckErr(err)
 
 		// Search config in home directory with name ".cobra" (without extension).
 		viper.AddConfigPath(home)
+		viper.SetConfigType("yaml")
 		viper.SetConfigName(".cobra")
 	}
 
diff --git a/cobra/tpl/main.go b/cobra/tpl/main.go
index 4348e56..fbb3372 100644
--- a/cobra/tpl/main.go
+++ b/cobra/tpl/main.go
@@ -24,12 +24,11 @@ package cmd
 
 import (
 	"fmt"
-	"github.com/spf13/cobra"
 	"os"
+	"github.com/spf13/cobra"
 {{ if .Viper }}
 	homedir "github.com/mitchellh/go-homedir"
-	"github.com/spf13/viper"
-{{ end -}}
+	"github.com/spf13/viper"{{ end }}
 )
 
 {{ if .Viper -}}
@@ -48,16 +47,13 @@ This application is a tool to generate the needed files
 to quickly create a Cobra application.` + "`" + `,
 	// Uncomment the following line if your bare application
 	// has an action associated with it:
-	//	Run: func(cmd *cobra.Command, args []string) { },
+	// Run: func(cmd *cobra.Command, args []string) { },
 }
 
 // Execute adds all child commands to the root command and sets flags appropriately.
 // This is called by main.main(). It only needs to happen once to the rootCmd.
 func Execute() {
-	if err := rootCmd.Execute(); err != nil {
-		fmt.Println(err)
-		os.Exit(1)
-	}
+	cobra.CheckErr(rootCmd.Execute())
 }
 
 func init() {
@@ -86,13 +82,11 @@ func initConfig() {
 	} else {
 		// Find home directory.
 		home, err := homedir.Dir()
-		if err != nil {
-			fmt.Println(err)
-			os.Exit(1)
-		}
+		cobra.CheckErr(err)
 
 		// Search config in home directory with name ".{{ .AppName }}" (without extension).
 		viper.AddConfigPath(home)
+		viper.SetConfigType("yaml")
 		viper.SetConfigName(".{{ .AppName }}")
 	}
 
@@ -100,7 +94,7 @@ func initConfig() {
 
 	// If a config file is found, read it in.
 	if err := viper.ReadInConfig(); err == nil {
-		fmt.Println("Using config file:", viper.ConfigFileUsed())
+		fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed())
 	}
 }
 {{- end }}
diff --git a/command.go b/command.go
index 77b399e..5c85c89 100644
--- a/command.go
+++ b/command.go
@@ -84,9 +84,6 @@ type Command struct {
 	// Deprecated defines, if this command is deprecated and should print this string when used.
 	Deprecated string
 
-	// Hidden defines, if this command is hidden and should NOT show up in the list of available commands.
-	Hidden bool
-
 	// Annotations are key/value pairs that can be used by applications to identify or
 	// group commands.
 	Annotations map[string]string
@@ -126,55 +123,6 @@ type Command struct {
 	// PersistentPostRunE: PersistentPostRun but returns an error.
 	PersistentPostRunE func(cmd *Command, args []string) error
 
-	// SilenceErrors is an option to quiet errors down stream.
-	SilenceErrors bool
-
-	// SilenceUsage is an option to silence usage when an error occurs.
-	SilenceUsage bool
-
-	// DisableFlagParsing disables the flag parsing.
-	// If this is true all flags will be passed to the command as arguments.
-	DisableFlagParsing bool
-
-	// DisableAutoGenTag defines, if gen tag ("Auto generated by spf13/cobra...")
-	// will be printed by generating docs for this command.
-	DisableAutoGenTag bool
-
-	// DisableFlagsInUseLine will disable the addition of [flags] to the usage
-	// line of a command when printing help or generating docs
-	DisableFlagsInUseLine bool
-
-	// DisableSuggestions disables the suggestions based on Levenshtein distance
-	// that go along with 'unknown command' messages.
-	DisableSuggestions bool
-	// SuggestionsMinimumDistance defines minimum levenshtein distance to display suggestions.
-	// Must be > 0.
-	SuggestionsMinimumDistance int
-
-	// TraverseChildren parses flags on all parents before executing child command.
-	TraverseChildren bool
-
-	// FParseErrWhitelist flag parse errors to be ignored
-	FParseErrWhitelist FParseErrWhitelist
-
-	ctx context.Context
-
-	// commands is the list of commands supported by this program.
-	commands []*Command
-	// parent is a parent command for this command.
-	parent *Command
-	// Max lengths of commands' string lengths for use in padding.
-	commandsMaxUseLen         int
-	commandsMaxCommandPathLen int
-	commandsMaxNameLen        int
-	// commandsAreSorted defines, if command slice are sorted or not.
-	commandsAreSorted bool
-	// commandCalledAs is the name or alias value used to call this command.
-	commandCalledAs struct {
-		name   string
-		called bool
-	}
-
 	// args is actual args parsed from flags.
 	args []string
 	// flagErrorBuf contains all error messages from pflag.
@@ -216,6 +164,63 @@ type Command struct {
 	outWriter io.Writer
 	// errWriter is a writer defined by the user that replaces stderr
 	errWriter io.Writer
+
+	//FParseErrWhitelist flag parse errors to be ignored
+	FParseErrWhitelist FParseErrWhitelist
+
+	// CompletionOptions is a set of options to control the handling of shell completion
+	CompletionOptions CompletionOptions
+
+	// commandsAreSorted defines, if command slice are sorted or not.
+	commandsAreSorted bool
+	// commandCalledAs is the name or alias value used to call this command.
+	commandCalledAs struct {
+		name   string
+		called bool
+	}
+
+	ctx context.Context
+
+	// commands is the list of commands supported by this program.
+	commands []*Command
+	// parent is a parent command for this command.
+	parent *Command
+	// Max lengths of commands' string lengths for use in padding.
+	commandsMaxUseLen         int
+	commandsMaxCommandPathLen int
+	commandsMaxNameLen        int
+
+	// TraverseChildren parses flags on all parents before executing child command.
+	TraverseChildren bool
+
+	// Hidden defines, if this command is hidden and should NOT show up in the list of available commands.
+	Hidden bool
+
+	// SilenceErrors is an option to quiet errors down stream.
+	SilenceErrors bool
+
+	// SilenceUsage is an option to silence usage when an error occurs.
+	SilenceUsage bool
+
+	// DisableFlagParsing disables the flag parsing.
+	// If this is true all flags will be passed to the command as arguments.
+	DisableFlagParsing bool
+
+	// DisableAutoGenTag defines, if gen tag ("Auto generated by spf13/cobra...")
+	// will be printed by generating docs for this command.
+	DisableAutoGenTag bool
+
+	// DisableFlagsInUseLine will disable the addition of [flags] to the usage
+	// line of a command when printing help or generating docs
+	DisableFlagsInUseLine bool
+
+	// DisableSuggestions disables the suggestions based on Levenshtein distance
+	// that go along with 'unknown command' messages.
+	DisableSuggestions bool
+
+	// SuggestionsMinimumDistance defines minimum levenshtein distance to display suggestions.
+	// Must be > 0.
+	SuggestionsMinimumDistance int
 }
 
 // Context returns underlying command context. If command wasn't
@@ -418,7 +423,7 @@ func (c *Command) UsageString() string {
 	c.outWriter = bb
 	c.errWriter = bb
 
-	c.Usage()
+	CheckErr(c.Usage())
 
 	// Setting things back to normal
 	c.outWriter = tmpOutput
@@ -882,7 +887,8 @@ func (c *Command) preRun() {
 }
 
 // ExecuteContext is the same as Execute(), but sets the ctx on the command.
-// Retrieve ctx by calling cmd.Context() inside your *Run lifecycle functions.
+// Retrieve ctx by calling cmd.Context() inside your *Run lifecycle or ValidArgs
+// functions.
 func (c *Command) ExecuteContext(ctx context.Context) error {
 	c.ctx = ctx
 	return c.Execute()
@@ -896,6 +902,14 @@ func (c *Command) Execute() error {
 	return err
 }
 
+// ExecuteContextC is the same as ExecuteC(), but sets the ctx on the command.
+// Retrieve ctx by calling cmd.Context() inside your *Run lifecycle or ValidArgs
+// functions.
+func (c *Command) ExecuteContextC(ctx context.Context) (*Command, error) {
+	c.ctx = ctx
+	return c.ExecuteC()
+}
+
 // ExecuteC executes the command.
 func (c *Command) ExecuteC() (cmd *Command, err error) {
 	if c.ctx == nil {
@@ -912,9 +926,10 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
 		preExecHookFn(c)
 	}
 
-	// initialize help as the last point possible to allow for user
-	// overriding
+	// initialize help at the last point to allow for user overriding
 	c.InitDefaultHelpCmd()
+	// initialize completion at the last point to allow for user overriding
+	c.initDefaultCompletionCmd()
 
 	args := c.args
 
@@ -964,13 +979,13 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
 			return cmd, nil
 		}
 
-		// If root command has SilentErrors flagged,
+		// If root command has SilenceErrors flagged,
 		// all subcommands should respect it
 		if !cmd.SilenceErrors && !c.SilenceErrors {
 			c.PrintErrln("Error:", err.Error())
 		}
 
-		// If root command has SilentUsage flagged,
+		// If root command has SilenceUsage flagged,
 		// all subcommands should respect it
 		if !cmd.SilenceUsage && !c.SilenceUsage {
 			c.Println(cmd.UsageString())
@@ -1087,10 +1102,10 @@ Simply type ` + c.Name() + ` help [path to command] for full details.`,
 				cmd, _, e := c.Root().Find(args)
 				if cmd == nil || e != nil {
 					c.Printf("Unknown help topic %#q\n", args)
-					c.Root().Usage()
+					CheckErr(c.Root().Usage())
 				} else {
 					cmd.InitDefaultHelpFlag() // make possible 'help' flag to be shown
-					cmd.Help()
+					CheckErr(cmd.Help())
 				}
 			},
 		}
diff --git a/custom_completions.go b/completions.go
similarity index 76%
rename from custom_completions.go
rename to completions.go
index f9e88e0..04dc71e 100644
--- a/custom_completions.go
+++ b/completions.go
@@ -63,6 +63,32 @@ const (
 	ShellCompDirectiveDefault ShellCompDirective = 0
 )
 
+const (
+	// Constants for the completion command
+	compCmdName              = "completion"
+	compCmdNoDescFlagName    = "no-descriptions"
+	compCmdNoDescFlagDesc    = "disable completion descriptions"
+	compCmdNoDescFlagDefault = false
+)
+
+// CompletionOptions are the options to control shell completion
+type CompletionOptions struct {
+	// DisableDefaultCmd prevents Cobra from creating a default 'completion' command
+	DisableDefaultCmd bool
+	// DisableNoDescFlag prevents Cobra from creating the '--no-descriptions' flag
+	// for shells that support completion descriptions
+	DisableNoDescFlag bool
+	// DisableDescriptions turns off all completion descriptions for shells
+	// that support them
+	DisableDescriptions bool
+}
+
+// NoFileCompletions can be used to disable file completion for commands that should
+// not trigger file completions.
+func NoFileCompletions(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+	return nil, ShellCompDirectiveNoFileComp
+}
+
 // RegisterFlagCompletionFunc should be called to register a function to provide completion for a flag.
 func (c *Command) RegisterFlagCompletionFunc(flagName string, f func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective)) error {
 	flag := c.Flag(flagName)
@@ -149,10 +175,6 @@ func (c *Command) initCompleteCmd(args []string) {
 				fmt.Fprintln(finalCmd.OutOrStdout(), comp)
 			}
 
-			if directive >= shellCompDirectiveMaxValue {
-				directive = ShellCompDirectiveDefault
-			}
-
 			// As the last printout, print the completion directive for the completion script to parse.
 			// The directive integer must be that last character following a single colon (:).
 			// The completion script expects :<directive>
@@ -195,6 +217,7 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi
 		// Unable to find the real command. E.g., <program> someInvalidCmd <TAB>
 		return c, []string{}, ShellCompDirectiveDefault, fmt.Errorf("Unable to find a command for arguments: %v", trimmedArgs)
 	}
+	finalCmd.ctx = c.ctx
 
 	// Check if we are doing flag value completion before parsing the flags.
 	// This is important because if we are completing a flag value, we need to also
@@ -302,7 +325,7 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi
 		if len(finalArgs) == 0 && !foundLocalNonPersistentFlag {
 			// We only complete sub-commands if:
 			// - there are no arguments on the command-line and
-			// - there are no local, non-peristent flag on the command-line or TraverseChildren is true
+			// - there are no local, non-persistent flags on the command-line or TraverseChildren is true
 			for _, subCmd := range finalCmd.Commands() {
 				if subCmd.IsAvailableCommand() || subCmd == finalCmd.helpCommand {
 					if strings.HasPrefix(subCmd.Name(), toComplete) {
@@ -442,7 +465,16 @@ func checkIfFlagCompletion(finalCmd *Command, args []string, lastArg string) (*p
 	if len(lastArg) > 0 && lastArg[0] == '-' {
 		if index := strings.Index(lastArg, "="); index >= 0 {
 			// Flag with an =
-			flagName = strings.TrimLeft(lastArg[:index], "-")
+			if strings.HasPrefix(lastArg[:index], "--") {
+				// Flag has full name
+				flagName = lastArg[2:index]
+			} else {
+				// Flag is shorthand
+				// We have to get the last shorthand flag name
+				// e.g. `-asd` => d to provide the correct completion
+				// https://github.com/spf13/cobra/issues/1257
+				flagName = lastArg[index-1 : index]
+			}
 			lastArg = lastArg[index+1:]
 			flagWithEqual = true
 		} else {
@@ -459,8 +491,16 @@ func checkIfFlagCompletion(finalCmd *Command, args []string, lastArg string) (*p
 				// If the flag contains an = it means it has already been fully processed,
 				// so we don't need to deal with it here.
 				if index := strings.Index(prevArg, "="); index < 0 {
-					flagName = strings.TrimLeft(prevArg, "-")
-
+					if strings.HasPrefix(prevArg, "--") {
+						// Flag has full name
+						flagName = prevArg[2:]
+					} else {
+						// Flag is shorthand
+						// We have to get the last shorthand flag name
+						// e.g. `-asd` => d to provide the correct completion
+						// https://github.com/spf13/cobra/issues/1257
+						flagName = prevArg[len(prevArg)-1:]
+					}
 					// Remove the uncompleted flag or else there could be an error created
 					// for an invalid value for that flag
 					trimmedArgs = args[:len(args)-1]
@@ -494,6 +534,150 @@ func checkIfFlagCompletion(finalCmd *Command, args []string, lastArg string) (*p
 	return flag, trimmedArgs, lastArg, nil
 }
 
+// initDefaultCompletionCmd adds a default 'completion' command to c.
+// This function will do nothing if any of the following is true:
+// 1- the feature has been explicitly disabled by the program,
+// 2- c has no subcommands (to avoid creating one),
+// 3- c already has a 'completion' command provided by the program.
+func (c *Command) initDefaultCompletionCmd() {
+	if c.CompletionOptions.DisableDefaultCmd || !c.HasSubCommands() {
+		return
+	}
+
+	for _, cmd := range c.commands {
+		if cmd.Name() == compCmdName || cmd.HasAlias(compCmdName) {
+			// A completion command is already available
+			return
+		}
+	}
+
+	haveNoDescFlag := !c.CompletionOptions.DisableNoDescFlag && !c.CompletionOptions.DisableDescriptions
+
+	completionCmd := &Command{
+		Use:   compCmdName,
+		Short: "generate the autocompletion script for the specified shell",
+		Long: fmt.Sprintf(`
+Generate the autocompletion script for %[1]s for the specified shell.
+See each sub-command's help for details on how to use the generated script.
+`, c.Root().Name()),
+		Args:              NoArgs,
+		ValidArgsFunction: NoFileCompletions,
+	}
+	c.AddCommand(completionCmd)
+
+	out := c.OutOrStdout()
+	noDesc := c.CompletionOptions.DisableDescriptions
+	shortDesc := "generate the autocompletion script for %s"
+	bash := &Command{
+		Use:   "bash",
+		Short: fmt.Sprintf(shortDesc, "bash"),
+		Long: fmt.Sprintf(`
+Generate the autocompletion script for the bash shell.
+
+This script depends on the 'bash-completion' package.
+If it is not installed already, you can install it via your OS's package manager.
+
+To load completions in your current shell session:
+$ source <(%[1]s completion bash)
+
+To load completions for every new session, execute once:
+Linux:
+  $ %[1]s completion bash > /etc/bash_completion.d/%[1]s
+MacOS:
+  $ %[1]s completion bash > /usr/local/etc/bash_completion.d/%[1]s
+
+You will need to start a new shell for this setup to take effect.
+  `, c.Root().Name()),
+		Args:                  NoArgs,
+		DisableFlagsInUseLine: true,
+		ValidArgsFunction:     NoFileCompletions,
+		RunE: func(cmd *Command, args []string) error {
+			return cmd.Root().GenBashCompletion(out)
+		},
+	}
+
+	zsh := &Command{
+		Use:   "zsh",
+		Short: fmt.Sprintf(shortDesc, "zsh"),
+		Long: fmt.Sprintf(`
+Generate the autocompletion script for the zsh shell.
+
+If shell completion is not already enabled in your environment you will need
+to enable it.  You can execute the following once:
+
+$ echo "autoload -U compinit; compinit" >> ~/.zshrc
+
+To load completions for every new session, execute once:
+$ %[1]s completion zsh > "${fpath[1]}/_%[1]s"
+
+You will need to start a new shell for this setup to take effect.
+`, c.Root().Name()),
+		Args:              NoArgs,
+		ValidArgsFunction: NoFileCompletions,
+		RunE: func(cmd *Command, args []string) error {
+			if noDesc {
+				return cmd.Root().GenZshCompletionNoDesc(out)
+			}
+			return cmd.Root().GenZshCompletion(out)
+		},
+	}
+	if haveNoDescFlag {
+		zsh.Flags().BoolVar(&noDesc, compCmdNoDescFlagName, compCmdNoDescFlagDefault, compCmdNoDescFlagDesc)
+	}
+
+	fish := &Command{
+		Use:   "fish",
+		Short: fmt.Sprintf(shortDesc, "fish"),
+		Long: fmt.Sprintf(`
+Generate the autocompletion script for the fish shell.
+
+To load completions in your current shell session:
+$ %[1]s completion fish | source
+
+To load completions for every new session, execute once:
+$ %[1]s completion fish > ~/.config/fish/completions/%[1]s.fish
+
+You will need to start a new shell for this setup to take effect.
+`, c.Root().Name()),
+		Args:              NoArgs,
+		ValidArgsFunction: NoFileCompletions,
+		RunE: func(cmd *Command, args []string) error {
+			return cmd.Root().GenFishCompletion(out, !noDesc)
+		},
+	}
+	if haveNoDescFlag {
+		fish.Flags().BoolVar(&noDesc, compCmdNoDescFlagName, compCmdNoDescFlagDefault, compCmdNoDescFlagDesc)
+	}
+
+	powershell := &Command{
+		Use:   "powershell",
+		Short: fmt.Sprintf(shortDesc, "powershell"),
+		Long: fmt.Sprintf(`
+Generate the autocompletion script for powershell.
+
+To load completions in your current shell session:
+PS C:\> %[1]s completion powershell | Out-String | Invoke-Expression
+
+To load completions for every new session, add the output of the above command
+to your powershell profile.
+`, c.Root().Name()),
+		Args:              NoArgs,
+		ValidArgsFunction: NoFileCompletions,
+		RunE: func(cmd *Command, args []string) error {
+			if noDesc {
+				return cmd.Root().GenPowerShellCompletion(out)
+			}
+			return cmd.Root().GenPowerShellCompletionWithDesc(out)
+
+		},
+	}
+	if haveNoDescFlag {
+		powershell.Flags().BoolVar(&noDesc, compCmdNoDescFlagName, compCmdNoDescFlagDefault, compCmdNoDescFlagDesc)
+	}
+
+	completionCmd.AddCommand(bash, zsh, fish, powershell)
+}
+
 func findFlag(cmd *Command, name string) *pflag.Flag {
 	flagSet := cmd.Flags()
 	if len(name) == 1 {
@@ -527,13 +711,13 @@ func CompDebug(msg string, printToStdErr bool) {
 			os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
 		if err == nil {
 			defer f.Close()
-			f.WriteString(msg)
+			WriteStringAndCheck(f, msg)
 		}
 	}
 
 	if printToStdErr {
 		// Must print to stderr for this not to be read by the completion script.
-		fmt.Fprintf(os.Stderr, msg)
+		fmt.Fprint(os.Stderr, msg)
 	}
 }
 
diff --git a/doc/man_docs.go b/doc/man_docs.go
index b67ac1f..916e361 100644
--- a/doc/man_docs.go
+++ b/doc/man_docs.go
@@ -139,23 +139,23 @@ func fillHeader(header *GenManHeader, name string, disableAutoGen bool) error {
 	return nil
 }
 
-func manPreamble(buf *bytes.Buffer, header *GenManHeader, cmd *cobra.Command, dashedName string) {
+func manPreamble(buf io.StringWriter, header *GenManHeader, cmd *cobra.Command, dashedName string) {
 	description := cmd.Long
 	if len(description) == 0 {
 		description = cmd.Short
 	}
 
-	buf.WriteString(fmt.Sprintf(`%% "%s" "%s" "%s" "%s" "%s"
+	cobra.WriteStringAndCheck(buf, fmt.Sprintf(`%% "%s" "%s" "%s" "%s" "%s"
 # NAME
 `, header.Title, header.Section, header.date, header.Source, header.Manual))
-	buf.WriteString(fmt.Sprintf("%s \\- %s\n\n", dashedName, cmd.Short))
-	buf.WriteString("# SYNOPSIS\n")
-	buf.WriteString(fmt.Sprintf("**%s**\n\n", cmd.UseLine()))
-	buf.WriteString("# DESCRIPTION\n")
-	buf.WriteString(description + "\n\n")
+	cobra.WriteStringAndCheck(buf, fmt.Sprintf("%s \\- %s\n\n", dashedName, cmd.Short))
+	cobra.WriteStringAndCheck(buf, "# SYNOPSIS\n")
+	cobra.WriteStringAndCheck(buf, fmt.Sprintf("**%s**\n\n", cmd.UseLine()))
+	cobra.WriteStringAndCheck(buf, "# DESCRIPTION\n")
+	cobra.WriteStringAndCheck(buf, description+"\n\n")
 }
 
-func manPrintFlags(buf *bytes.Buffer, flags *pflag.FlagSet) {
+func manPrintFlags(buf io.StringWriter, flags *pflag.FlagSet) {
 	flags.VisitAll(func(flag *pflag.Flag) {
 		if len(flag.Deprecated) > 0 || flag.Hidden {
 			return
@@ -179,22 +179,22 @@ func manPrintFlags(buf *bytes.Buffer, flags *pflag.FlagSet) {
 			format += "]"
 		}
 		format += "\n\t%s\n\n"
-		buf.WriteString(fmt.Sprintf(format, flag.DefValue, flag.Usage))
+		cobra.WriteStringAndCheck(buf, fmt.Sprintf(format, flag.DefValue, flag.Usage))
 	})
 }
 
-func manPrintOptions(buf *bytes.Buffer, command *cobra.Command) {
+func manPrintOptions(buf io.StringWriter, command *cobra.Command) {
 	flags := command.NonInheritedFlags()
 	if flags.HasAvailableFlags() {
-		buf.WriteString("# OPTIONS\n")
+		cobra.WriteStringAndCheck(buf, "# OPTIONS\n")
 		manPrintFlags(buf, flags)
-		buf.WriteString("\n")
+		cobra.WriteStringAndCheck(buf, "\n")
 	}
 	flags = command.InheritedFlags()
 	if flags.HasAvailableFlags() {
-		buf.WriteString("# OPTIONS INHERITED FROM PARENT COMMANDS\n")
+		cobra.WriteStringAndCheck(buf, "# OPTIONS INHERITED FROM PARENT COMMANDS\n")
 		manPrintFlags(buf, flags)
-		buf.WriteString("\n")
+		cobra.WriteStringAndCheck(buf, "\n")
 	}
 }
 
diff --git a/fish_completions.go b/fish_completions.go
index eaae9bc..b0db323 100644
--- a/fish_completions.go
+++ b/fish_completions.go
@@ -8,7 +8,7 @@ import (
 	"strings"
 )
 
-func genFishComp(buf *bytes.Buffer, name string, includeDesc bool) {
+func genFishComp(buf io.StringWriter, name string, includeDesc bool) {
 	// Variables should not contain a '-' or ':' character
 	nameForVar := name
 	nameForVar = strings.Replace(nameForVar, "-", "_", -1)
@@ -18,47 +18,50 @@ func genFishComp(buf *bytes.Buffer, name string, includeDesc bool) {
 	if !includeDesc {
 		compCmd = ShellCompNoDescRequestCmd
 	}
-	buf.WriteString(fmt.Sprintf("# fish completion for %-36s -*- shell-script -*-\n", name))
-	buf.WriteString(fmt.Sprintf(`
+	WriteStringAndCheck(buf, fmt.Sprintf("# fish completion for %-36s -*- shell-script -*-\n", name))
+	WriteStringAndCheck(buf, fmt.Sprintf(`
 function __%[1]s_debug
-    set file "$BASH_COMP_DEBUG_FILE"
+    set -l file "$BASH_COMP_DEBUG_FILE"
     if test -n "$file"
         echo "$argv" >> $file
     end
 end
 
 function __%[1]s_perform_completion
-    __%[1]s_debug "Starting __%[1]s_perform_completion with: $argv"
+    __%[1]s_debug "Starting __%[1]s_perform_completion"
 
-    set args (string split -- " " "$argv")
-    set lastArg "$args[-1]"
+    # Extract all args except the last one
+    set -l args (commandline -opc)
+    # Extract the last arg and escape it in case it is a space
+    set -l lastArg (string escape -- (commandline -ct))
 
     __%[1]s_debug "args: $args"
     __%[1]s_debug "last arg: $lastArg"
 
-    set emptyArg ""
-    if test -z "$lastArg"
-        __%[1]s_debug "Setting emptyArg"
-        set emptyArg \"\"
-    end
-    __%[1]s_debug "emptyArg: $emptyArg"
-
-    if not type -q "$args[1]"
-        # This can happen when "complete --do-complete %[2]s" is called when running this script.
-        __%[1]s_debug "Cannot find $args[1]. No completions."
-        return
-    end
+    set -l requestComp "$args[1] %[3]s $args[2..-1] $lastArg"
 
-    set requestComp "$args[1] %[3]s $args[2..-1] $emptyArg"
     __%[1]s_debug "Calling $requestComp"
+    set -l results (eval $requestComp 2> /dev/null)
+
+    # Some programs may output extra empty lines after the directive.
+    # Let's ignore them or else it will break completion.
+    # Ref: https://github.com/spf13/cobra/issues/1279
+    for line in $results[-1..1]
+        if test (string trim -- $line) = ""
+            # Found an empty line, remove it
+            set results $results[1..-2]
+        else
+            # Found non-empty line, we have our proper output
+            break
+        end
+    end
 
-    set results (eval $requestComp 2> /dev/null)
-    set comps $results[1..-2]
-    set directiveLine $results[-1]
+    set -l comps $results[1..-2]
+    set -l directiveLine $results[-1]
 
     # For Fish, when completing a flag with an = (e.g., <program> -n=<TAB>)
     # completions must be prefixed with the flag
-    set flagPrefix (string match -r -- '-.*=' "$lastArg")
+    set -l flagPrefix (string match -r -- '-.*=' "$lastArg")
 
     __%[1]s_debug "Comps: $comps"
     __%[1]s_debug "DirectiveLine: $directiveLine"
@@ -71,115 +74,124 @@ function __%[1]s_perform_completion
     printf "%%s\n" "$directiveLine"
 end
 
-# This function does three things:
-# 1- Obtain the completions and store them in the global __%[1]s_comp_results
-# 2- Set the __%[1]s_comp_do_file_comp flag if file completion should be performed
-#    and unset it otherwise
-# 3- Return true if the completion results are not empty
+# This function does two things:
+# - Obtain the completions and store them in the global __%[1]s_comp_results
+# - Return false if file completion should be performed
 function __%[1]s_prepare_completions
+    __%[1]s_debug ""
+    __%[1]s_debug "========= starting completion logic =========="
+
     # Start fresh
-    set --erase __%[1]s_comp_do_file_comp
     set --erase __%[1]s_comp_results
 
-    # Check if the command-line is already provided.  This is useful for testing.
-    if not set --query __%[1]s_comp_commandLine
-        # Use the -c flag to allow for completion in the middle of the line
-        set __%[1]s_comp_commandLine (commandline -c)
-    end
-    __%[1]s_debug "commandLine is: $__%[1]s_comp_commandLine"
-
-    set results (__%[1]s_perform_completion "$__%[1]s_comp_commandLine")
-    set --erase __%[1]s_comp_commandLine
+    set -l results (__%[1]s_perform_completion)
     __%[1]s_debug "Completion results: $results"
 
     if test -z "$results"
         __%[1]s_debug "No completion, probably due to a failure"
         # Might as well do file completion, in case it helps
-        set --global __%[1]s_comp_do_file_comp 1
         return 1
     end
 
-    set directive (string sub --start 2 $results[-1])
+    set -l directive (string sub --start 2 $results[-1])
     set --global __%[1]s_comp_results $results[1..-2]
 
     __%[1]s_debug "Completions are: $__%[1]s_comp_results"
     __%[1]s_debug "Directive is: $directive"
 
-    set shellCompDirectiveError %[4]d
-    set shellCompDirectiveNoSpace %[5]d
-    set shellCompDirectiveNoFileComp %[6]d
-    set shellCompDirectiveFilterFileExt %[7]d
-    set shellCompDirectiveFilterDirs %[8]d
+    set -l shellCompDirectiveError %[4]d
+    set -l shellCompDirectiveNoSpace %[5]d
+    set -l shellCompDirectiveNoFileComp %[6]d
+    set -l shellCompDirectiveFilterFileExt %[7]d
+    set -l shellCompDirectiveFilterDirs %[8]d
 
     if test -z "$directive"
         set directive 0
     end
 
-    set compErr (math (math --scale 0 $directive / $shellCompDirectiveError) %% 2)
+    set -l compErr (math (math --scale 0 $directive / $shellCompDirectiveError) %% 2)
     if test $compErr -eq 1
         __%[1]s_debug "Received error directive: aborting."
         # Might as well do file completion, in case it helps
-        set --global __%[1]s_comp_do_file_comp 1
         return 1
     end
 
-    set filefilter (math (math --scale 0 $directive / $shellCompDirectiveFilterFileExt) %% 2)
-    set dirfilter (math (math --scale 0 $directive / $shellCompDirectiveFilterDirs) %% 2)
+    set -l filefilter (math (math --scale 0 $directive / $shellCompDirectiveFilterFileExt) %% 2)
+    set -l dirfilter (math (math --scale 0 $directive / $shellCompDirectiveFilterDirs) %% 2)
     if test $filefilter -eq 1; or test $dirfilter -eq 1
         __%[1]s_debug "File extension filtering or directory filtering not supported"
         # Do full file completion instead
-        set --global __%[1]s_comp_do_file_comp 1
         return 1
     end
 
-    set nospace (math (math --scale 0 $directive / $shellCompDirectiveNoSpace) %% 2)
-    set nofiles (math (math --scale 0 $directive / $shellCompDirectiveNoFileComp) %% 2)
+    set -l nospace (math (math --scale 0 $directive / $shellCompDirectiveNoSpace) %% 2)
+    set -l nofiles (math (math --scale 0 $directive / $shellCompDirectiveNoFileComp) %% 2)
 
     __%[1]s_debug "nospace: $nospace, nofiles: $nofiles"
 
-    # Important not to quote the variable for count to work
-    set numComps (count $__%[1]s_comp_results)
-    __%[1]s_debug "numComps: $numComps"
-
-    if test $numComps -eq 1; and test $nospace -ne 0
-        # To support the "nospace" directive we trick the shell
-        # by outputting an extra, longer completion.
-        __%[1]s_debug "Adding second completion to perform nospace directive"
-        set --append __%[1]s_comp_results $__%[1]s_comp_results[1].
-    end
-
-    if test $numComps -eq 0; and test $nofiles -eq 0
-        __%[1]s_debug "Requesting file completion"
-        set --global __%[1]s_comp_do_file_comp 1
+    # If we want to prevent a space, or if file completion is NOT disabled,
+    # we need to count the number of valid completions.
+    # To do so, we will filter on prefix as the completions we have received
+    # may not already be filtered so as to allow fish to match on different
+    # criteria than the prefix.
+    if test $nospace -ne 0; or test $nofiles -eq 0
+        set -l prefix (commandline -t | string escape --style=regex)
+        __%[1]s_debug "prefix: $prefix"
+
+        set -l completions (string match -r -- "^$prefix.*" $__%[1]s_comp_results)
+        set --global __%[1]s_comp_results $completions
+        __%[1]s_debug "Filtered completions are: $__%[1]s_comp_results"
+
+        # Important not to quote the variable for count to work
+        set -l numComps (count $__%[1]s_comp_results)
+        __%[1]s_debug "numComps: $numComps"
+
+        if test $numComps -eq 1; and test $nospace -ne 0
+            # We must first split on \t to get rid of the descriptions to be
+            # able to check what the actual completion will be.
+            # We don't need descriptions anyway since there is only a single
+            # real completion which the shell will expand immediately.
+            set -l split (string split --max 1 \t $__%[1]s_comp_results[1])
+        
+            # Fish won't add a space if the completion ends with any
+            # of the following characters: @=/:.,
+            set -l lastChar (string sub -s -1 -- $split)
+            if not string match -r -q "[@=/:.,]" -- "$lastChar"                
+                # In other cases, to support the "nospace" directive we trick the shell
+                # by outputting an extra, longer completion.
+                __%[1]s_debug "Adding second completion to perform nospace directive"
+                set --global __%[1]s_comp_results $split[1] $split[1].
+                __%[1]s_debug "Completions are now: $__%[1]s_comp_results"
+            end
+        end
+
+        if test $numComps -eq 0; and test $nofiles -eq 0
+            # To be consistent with bash and zsh, we only trigger file
+            # completion when there are no other completions
+            __%[1]s_debug "Requesting file completion"
+            return 1
+        end
     end
 
-    # If we don't want file completion, we must return true even if there
-    # are no completions found.  This is because fish will perform the last
-    # completion command, even if its condition is false, if no other
-    # completion command was triggered
-    return (not set --query __%[1]s_comp_do_file_comp)
+    return 0
 end
 
 # Since Fish completions are only loaded once the user triggers them, we trigger them ourselves
 # so we can properly delete any completions provided by another script.
-# The space after the the program name is essential to trigger completion for the program
-# and not completion of the program name itself.
-complete --do-complete "%[2]s " > /dev/null 2>&1
-# Using '> /dev/null 2>&1' since '&>' is not supported in older versions of fish.
+# Only do this if the program can be found, or else fish may print some errors; besides,
+# the existing completions will only be loaded if the program can be found.
+if type -q "%[2]s"
+    # The space after the program name is essential to trigger completion for the program
+    # and not completion of the program name itself.
+    # Also, we use '> /dev/null 2>&1' since '&>' is not supported in older versions of fish.
+    complete --do-complete "%[2]s " > /dev/null 2>&1
+end
 
 # Remove any pre-existing completions for the program since we will be handling all of them.
 complete -c %[2]s -e
 
-# The order in which the below two lines are defined is very important so that __%[1]s_prepare_completions
-# is called first.  It is __%[1]s_prepare_completions that sets up the __%[1]s_comp_do_file_comp variable.
-#
-# This completion will be run second as complete commands are added FILO.
-# It triggers file completion choices when __%[1]s_comp_do_file_comp is set.
-complete -c %[2]s -n 'set --query __%[1]s_comp_do_file_comp'
-
-# This completion will be run first as complete commands are added FILO.
-# The call to __%[1]s_prepare_completions will setup both __%[1]s_comp_results and __%[1]s_comp_do_file_comp.
-# It provides the program's completion choices.
+# The call to __%[1]s_prepare_completions will setup __%[1]s_comp_results
+# which provides the program's completion choices.
 complete -c %[2]s -n '__%[1]s_prepare_completions' -f -a '$__%[1]s_comp_results'
 
 `, nameForVar, name, compCmd,
diff --git a/go.mod b/go.mod
index 57e3244..eb7d47f 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
 module github.com/spf13/cobra
 
-go 1.12
+go 1.14
 
 require (
 	github.com/cpuguy83/go-md2man/v2 v2.0.0
@@ -8,5 +8,5 @@ require (
 	github.com/mitchellh/go-homedir v1.1.0
 	github.com/spf13/pflag v1.0.5
 	github.com/spf13/viper v1.7.0
-	gopkg.in/yaml.v2 v2.2.8
+	gopkg.in/yaml.v2 v2.4.0
 )
diff --git a/go.sum b/go.sum
index 0aae738..9328ee3 100644
--- a/go.sum
+++ b/go.sum
@@ -304,8 +304,8 @@ gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
 gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
 gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
-gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/powershell_completions.go b/powershell_completions.go
index 756c61b..59234c0 100644
--- a/powershell_completions.go
+++ b/powershell_completions.go
@@ -1,6 +1,3 @@
-// PowerShell completions are based on the amazing work from clap:
-// https://github.com/clap-rs/clap/blob/3294d18efe5f264d12c9035f404c7d189d4824e1/src/completions/powershell.rs
-//
 // The generated scripts require PowerShell v5.0+ (which comes Windows 10, but
 // can be downloaded separately for windows 7 or 8.1).
 
@@ -11,90 +8,278 @@ import (
 	"fmt"
 	"io"
 	"os"
-	"strings"
-
-	"github.com/spf13/pflag"
 )
 
-var powerShellCompletionTemplate = `using namespace System.Management.Automation
-using namespace System.Management.Automation.Language
-Register-ArgumentCompleter -Native -CommandName '%s' -ScriptBlock {
-    param($wordToComplete, $commandAst, $cursorPosition)
-    $commandElements = $commandAst.CommandElements
-    $command = @(
-        '%s'
-        for ($i = 1; $i -lt $commandElements.Count; $i++) {
-            $element = $commandElements[$i]
-            if ($element -isnot [StringConstantExpressionAst] -or
-                $element.StringConstantType -ne [StringConstantType]::BareWord -or
-                $element.Value.StartsWith('-')) {
-                break
-            }
-            $element.Value
-        }
-    ) -join ';'
-    $completions = @(switch ($command) {%s
-    })
-    $completions.Where{ $_.CompletionText -like "$wordToComplete*" } |
-        Sort-Object -Property ListItemText
-}`
-
-func generatePowerShellSubcommandCases(out io.Writer, cmd *Command, previousCommandName string) {
-	var cmdName string
-	if previousCommandName == "" {
-		cmdName = cmd.Name()
-	} else {
-		cmdName = fmt.Sprintf("%s;%s", previousCommandName, cmd.Name())
-	}
-
-	fmt.Fprintf(out, "\n        '%s' {", cmdName)
-
-	cmd.Flags().VisitAll(func(flag *pflag.Flag) {
-		if nonCompletableFlag(flag) {
-			return
-		}
-		usage := escapeStringForPowerShell(flag.Usage)
-		if len(flag.Shorthand) > 0 {
-			fmt.Fprintf(out, "\n            [CompletionResult]::new('-%s', '%s', [CompletionResultType]::ParameterName, '%s')", flag.Shorthand, flag.Shorthand, usage)
-		}
-		fmt.Fprintf(out, "\n            [CompletionResult]::new('--%s', '%s', [CompletionResultType]::ParameterName, '%s')", flag.Name, flag.Name, usage)
-	})
-
-	for _, subCmd := range cmd.Commands() {
-		usage := escapeStringForPowerShell(subCmd.Short)
-		fmt.Fprintf(out, "\n            [CompletionResult]::new('%s', '%s', [CompletionResultType]::ParameterValue, '%s')", subCmd.Name(), subCmd.Name(), usage)
+func genPowerShellComp(buf io.StringWriter, name string, includeDesc bool) {
+	compCmd := ShellCompRequestCmd
+	if !includeDesc {
+		compCmd = ShellCompNoDescRequestCmd
 	}
+	WriteStringAndCheck(buf, fmt.Sprintf(`# powershell completion for %-36[1]s -*- shell-script -*-
 
-	fmt.Fprint(out, "\n            break\n        }")
-
-	for _, subCmd := range cmd.Commands() {
-		generatePowerShellSubcommandCases(out, subCmd, cmdName)
-	}
+function __%[1]s_debug {
+    if ($env:BASH_COMP_DEBUG_FILE) {
+        "$args" | Out-File -Append -FilePath "$env:BASH_COMP_DEBUG_FILE"
+    }
 }
 
-func escapeStringForPowerShell(s string) string {
-	return strings.Replace(s, "'", "''", -1)
+filter __%[1]s_escapeStringWithSpecialChars {
+`+"    $_ -replace '\\s|#|@|\\$|;|,|''|\\{|\\}|\\(|\\)|\"|`|\\||<|>|&','`$&'"+`
 }
 
-// GenPowerShellCompletion generates PowerShell completion file and writes to the passed writer.
-func (c *Command) GenPowerShellCompletion(w io.Writer) error {
-	buf := new(bytes.Buffer)
+Register-ArgumentCompleter -CommandName '%[1]s' -ScriptBlock {
+    param(
+            $WordToComplete,
+            $CommandAst,
+            $CursorPosition
+        )
+
+    # Get the current command line and convert into a string
+    $Command = $CommandAst.CommandElements
+    $Command = "$Command"
+
+    __%[1]s_debug ""
+    __%[1]s_debug "========= starting completion logic =========="
+    __%[1]s_debug "WordToComplete: $WordToComplete Command: $Command CursorPosition: $CursorPosition"
+
+    # The user could have moved the cursor backwards on the command-line.
+    # We need to trigger completion from the $CursorPosition location, so we need
+    # to truncate the command-line ($Command) up to the $CursorPosition location.
+    # Make sure the $Command is longer then the $CursorPosition before we truncate.
+    # This happens because the $Command does not include the last space.
+    if ($Command.Length -gt $CursorPosition) {
+        $Command=$Command.Substring(0,$CursorPosition)
+    }
+	__%[1]s_debug "Truncated command: $Command"
+
+    $ShellCompDirectiveError=%[3]d
+    $ShellCompDirectiveNoSpace=%[4]d
+    $ShellCompDirectiveNoFileComp=%[5]d
+    $ShellCompDirectiveFilterFileExt=%[6]d
+    $ShellCompDirectiveFilterDirs=%[7]d
+
+	# Prepare the command to request completions for the program.
+    # Split the command at the first space to separate the program and arguments.
+    $Program,$Arguments = $Command.Split(" ",2)
+    $RequestComp="$Program %[2]s $Arguments"
+    __%[1]s_debug "RequestComp: $RequestComp"
+
+    # we cannot use $WordToComplete because it
+    # has the wrong values if the cursor was moved
+    # so use the last argument
+    if ($WordToComplete -ne "" ) {
+        $WordToComplete = $Arguments.Split(" ")[-1]
+    }
+    __%[1]s_debug "New WordToComplete: $WordToComplete"
+
+
+    # Check for flag with equal sign
+    $IsEqualFlag = ($WordToComplete -Like "--*=*" )
+    if ( $IsEqualFlag ) {
+        __%[1]s_debug "Completing equal sign flag"
+        # Remove the flag part
+        $Flag,$WordToComplete = $WordToComplete.Split("=",2)
+    }
+
+    if ( $WordToComplete -eq "" -And ( -Not $IsEqualFlag )) {
+        # If the last parameter is complete (there is a space following it)
+        # We add an extra empty parameter so we can indicate this to the go method.
+        __%[1]s_debug "Adding extra empty parameter"
+`+"        # We need to use `\"`\" to pass an empty argument a \"\" or '' does not work!!!"+`
+`+"        $RequestComp=\"$RequestComp\" + ' `\"`\"'"+`
+    }
+
+    __%[1]s_debug "Calling $RequestComp"
+    #call the command store the output in $out and redirect stderr and stdout to null
+    # $Out is an array contains each line per element
+    Invoke-Expression -OutVariable out "$RequestComp" 2>&1 | Out-Null
+
+
+    # get directive from last line
+    [int]$Directive = $Out[-1].TrimStart(':')
+    if ($Directive -eq "") {
+        # There is no directive specified
+        $Directive = 0
+    }
+    __%[1]s_debug "The completion directive is: $Directive"
+
+    # remove directive (last element) from out
+    $Out = $Out | Where-Object { $_ -ne $Out[-1] }
+    __%[1]s_debug "The completions are: $Out"
+
+    if (($Directive -band $ShellCompDirectiveError) -ne 0 ) {
+        # Error code.  No completion.
+        __%[1]s_debug "Received error from custom completion go code"
+        return
+    }
+
+    $Longest = 0
+    $Values = $Out | ForEach-Object {
+        #Split the output in name and description
+`+"        $Name, $Description = $_.Split(\"`t\",2)"+`
+        __%[1]s_debug "Name: $Name Description: $Description"
+
+        # Look for the longest completion so that we can format things nicely
+        if ($Longest -lt $Name.Length) {
+            $Longest = $Name.Length
+        }
+
+        # Set the description to a one space string if there is none set.
+        # This is needed because the CompletionResult does not accept an empty string as argument
+        if (-Not $Description) {
+            $Description = " "
+        }
+        @{Name="$Name";Description="$Description"}
+    }
+
+
+    $Space = " "
+    if (($Directive -band $ShellCompDirectiveNoSpace) -ne 0 ) {
+        # remove the space here
+        __%[1]s_debug "ShellCompDirectiveNoSpace is called"
+        $Space = ""
+    }
+
+    if ((($Directive -band $ShellCompDirectiveFilterFileExt) -ne 0 ) -or
+       (($Directive -band $ShellCompDirectiveFilterDirs) -ne 0 ))  {
+        __%[1]s_debug "ShellCompDirectiveFilterFileExt ShellCompDirectiveFilterDirs are not supported"
+
+        # return here to prevent the completion of the extensions
+        return
+    }
+
+    $Values = $Values | Where-Object {
+        # filter the result
+        $_.Name -like "$WordToComplete*"
+
+        # Join the flag back if we have an equal sign flag
+        if ( $IsEqualFlag ) {
+            __%[1]s_debug "Join the equal sign flag back to the completion value"
+            $_.Name = $Flag + "=" + $_.Name
+        }
+    }
 
-	var subCommandCases bytes.Buffer
-	generatePowerShellSubcommandCases(&subCommandCases, c, "")
-	fmt.Fprintf(buf, powerShellCompletionTemplate, c.Name(), c.Name(), subCommandCases.String())
+    if (($Directive -band $ShellCompDirectiveNoFileComp) -ne 0 ) {
+        __%[1]s_debug "ShellCompDirectiveNoFileComp is called"
 
+        if ($Values.Length -eq 0) {
+            # Just print an empty string here so the
+            # shell does not start to complete paths.
+            # We cannot use CompletionResult here because
+            # it does not accept an empty string as argument.
+            ""
+            return
+        }
+    }
+
+    # Get the current mode
+    $Mode = (Get-PSReadLineKeyHandler | Where-Object {$_.Key -eq "Tab" }).Function
+    __%[1]s_debug "Mode: $Mode"
+
+    $Values | ForEach-Object {
+
+        # store temporary because switch will overwrite $_
+        $comp = $_
+
+        # PowerShell supports three different completion modes
+        # - TabCompleteNext (default windows style - on each key press the next option is displayed)
+        # - Complete (works like bash)
+        # - MenuComplete (works like zsh)
+        # You set the mode with Set-PSReadLineKeyHandler -Key Tab -Function <mode>
+
+        # CompletionResult Arguments:
+        # 1) CompletionText text to be used as the auto completion result
+        # 2) ListItemText   text to be displayed in the suggestion list
+        # 3) ResultType     type of completion result
+        # 4) ToolTip        text for the tooltip with details about the object
+
+        switch ($Mode) {
+
+            # bash like
+            "Complete" {
+
+                if ($Values.Length -eq 1) {
+                    __%[1]s_debug "Only one completion left"
+
+                    # insert space after value
+                    [System.Management.Automation.CompletionResult]::new($($comp.Name | __%[1]s_escapeStringWithSpecialChars) + $Space, "$($comp.Name)", 'ParameterValue', "$($comp.Description)")
+
+                } else {
+                    # Add the proper number of spaces to align the descriptions
+                    while($comp.Name.Length -lt $Longest) {
+                        $comp.Name = $comp.Name + " "
+                    }
+
+                    # Check for empty description and only add parentheses if needed
+                    if ($($comp.Description) -eq " " ) {
+                        $Description = ""
+                    } else {
+                        $Description = "  ($($comp.Description))"
+                    }
+
+                    [System.Management.Automation.CompletionResult]::new("$($comp.Name)$Description", "$($comp.Name)$Description", 'ParameterValue', "$($comp.Description)")
+                }
+             }
+
+            # zsh like
+            "MenuComplete" {
+                # insert space after value
+                # MenuComplete will automatically show the ToolTip of
+                # the highlighted value at the bottom of the suggestions.
+                [System.Management.Automation.CompletionResult]::new($($comp.Name | __%[1]s_escapeStringWithSpecialChars) + $Space, "$($comp.Name)", 'ParameterValue', "$($comp.Description)")
+            }
+
+            # TabCompleteNext and in case we get something unknown
+            Default {
+                # Like MenuComplete but we don't want to add a space here because
+                # the user need to press space anyway to get the completion.
+                # Description will not be shown because thats not possible with TabCompleteNext
+                [System.Management.Automation.CompletionResult]::new($($comp.Name | __%[1]s_escapeStringWithSpecialChars), "$($comp.Name)", 'ParameterValue', "$($comp.Description)")
+            }
+        }
+
+    }
+}
+`, name, compCmd,
+		ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp,
+		ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs))
+}
+
+func (c *Command) genPowerShellCompletion(w io.Writer, includeDesc bool) error {
+	buf := new(bytes.Buffer)
+	genPowerShellComp(buf, c.Name(), includeDesc)
 	_, err := buf.WriteTo(w)
 	return err
 }
 
-// GenPowerShellCompletionFile generates PowerShell completion file.
-func (c *Command) GenPowerShellCompletionFile(filename string) error {
+func (c *Command) genPowerShellCompletionFile(filename string, includeDesc bool) error {
 	outFile, err := os.Create(filename)
 	if err != nil {
 		return err
 	}
 	defer outFile.Close()
 
-	return c.GenPowerShellCompletion(outFile)
+	return c.genPowerShellCompletion(outFile, includeDesc)
+}
+
+// GenPowerShellCompletionFile generates powershell completion file without descriptions.
+func (c *Command) GenPowerShellCompletionFile(filename string) error {
+	return c.genPowerShellCompletionFile(filename, false)
+}
+
+// GenPowerShellCompletion generates powershell completion file without descriptions
+// and writes it to the passed writer.
+func (c *Command) GenPowerShellCompletion(w io.Writer) error {
+	return c.genPowerShellCompletion(w, false)
+}
+
+// GenPowerShellCompletionFileWithDesc generates powershell completion file with descriptions.
+func (c *Command) GenPowerShellCompletionFileWithDesc(filename string) error {
+	return c.genPowerShellCompletionFile(filename, true)
+}
+
+// GenPowerShellCompletionWithDesc generates powershell completion file with descriptions
+// and writes it to the passed writer.
+func (c *Command) GenPowerShellCompletionWithDesc(w io.Writer) error {
+	return c.genPowerShellCompletion(w, true)
 }
diff --git a/powershell_completions.md b/powershell_completions.md
index 55f154a..c449f1e 100644
--- a/powershell_completions.md
+++ b/powershell_completions.md
@@ -1,16 +1,3 @@
 # Generating PowerShell Completions For Your Own cobra.Command
 
-Cobra can generate PowerShell completion scripts. Users need PowerShell version 5.0 or above, which comes with Windows 10 and can be downloaded separately for Windows 7 or 8.1. They can then write the completions to a file and source this file from their PowerShell profile, which is referenced by the `$Profile` environment variable. See `Get-Help about_Profiles` for more info about PowerShell profiles.
-
-*Note*: PowerShell completions have not (yet?) been aligned to Cobra's generic shell completion support.  This implies the PowerShell completions are not as rich as for other shells (see [What's not yet supported](#whats-not-yet-supported)), and may behave slightly differently.  They are still very useful for PowerShell users.
-
-# What's supported
-
-- Completion for subcommands using their `.Short` description
-- Completion for non-hidden flags using their `.Name` and `.Shorthand`
-
-# What's not yet supported
-
-- Command aliases
-- Required, filename or custom flags (they will work like normal flags)
-- Custom completion scripts
+Please refer to [Shell Completions](shell_completions.md#powershell-completions) for details.
diff --git a/projects_using_cobra.md b/projects_using_cobra.md
index 0d8755c..d98a71e 100644
--- a/projects_using_cobra.md
+++ b/projects_using_cobra.md
@@ -34,4 +34,5 @@
 - [Rclone](https://rclone.org/)
 - [Skaffold](https://skaffold.dev/)
 - [Tendermint](https://github.com/tendermint/tendermint)
+- [Twitch CLI](https://github.com/twitchdev/twitch-cli)
 - [Werf](https://werf.io/)
diff --git a/shell_completions.md b/shell_completions.md
index d8416ab..0b51cff 100644
--- a/shell_completions.md
+++ b/shell_completions.md
@@ -4,10 +4,19 @@ Cobra can generate shell completions for multiple shells.
 The currently supported shells are:
 - Bash
 - Zsh
-- Fish
+- fish
 - PowerShell
 
-If you are using the generator you can create a completion command by running
+Cobra will automatically provide your program with a fully functional `completion` command,
+similarly to how it provides the `help` command.
+
+## Creating your own completion command
+
+If you do not wish to use the default `completion` command, you can choose to
+provide your own, which will take precedence over the default one. (This also provides
+backwards-compatibility with programs that already have their own `completion` command.)
+
+If you are using the generator, you can create a completion command by running
 
 ```bash
 cobra add completion
@@ -17,38 +26,46 @@ and then modifying the generated `cmd/completion.go` file to look something like
 
 ```go
 var completionCmd = &cobra.Command{
-	Use:                   "completion [bash|zsh|fish|powershell]",
-	Short:                 "Generate completion script",
+	Use:   "completion [bash|zsh|fish|powershell]",
+	Short: "Generate completion script",
 	Long: `To load completions:
 
 Bash:
 
-$ source <(yourprogram completion bash)
+  $ source <(yourprogram completion bash)
 
-# To load completions for each session, execute once:
-Linux:
+  # To load completions for each session, execute once:
+  # Linux:
   $ yourprogram completion bash > /etc/bash_completion.d/yourprogram
-MacOS:
+  # macOS:
   $ yourprogram completion bash > /usr/local/etc/bash_completion.d/yourprogram
 
 Zsh:
 
-# If shell completion is not already enabled in your environment you will need
-# to enable it.  You can execute the following once:
+  # If shell completion is not already enabled in your environment,
+  # you will need to enable it.  You can execute the following once:
+
+  $ echo "autoload -U compinit; compinit" >> ~/.zshrc
+
+  # To load completions for each session, execute once:
+  $ yourprogram completion zsh > "${fpath[1]}/_yourprogram"
+
+  # You will need to start a new shell for this setup to take effect.
 
-$ echo "autoload -U compinit; compinit" >> ~/.zshrc
+fish:
 
-# To load completions for each session, execute once:
-$ yourprogram completion zsh > "${fpath[1]}/_yourprogram"
+  $ yourprogram completion fish | source
 
-# You will need to start a new shell for this setup to take effect.
+  # To load completions for each session, execute once:
+  $ yourprogram completion fish > ~/.config/fish/completions/yourprogram.fish
 
-Fish:
+PowerShell:
 
-$ yourprogram completion fish | source
+  PS> yourprogram completion powershell | Out-String | Invoke-Expression
 
-# To load completions for each session, execute once:
-$ yourprogram completion fish > ~/.config/fish/completions/yourprogram.fish
+  # To load completions for every new session, run:
+  PS> yourprogram completion powershell > yourprogram.ps1
+  # and source this file from your PowerShell profile.
 `,
 	DisableFlagsInUseLine: true,
 	ValidArgs:             []string{"bash", "zsh", "fish", "powershell"},
@@ -62,13 +79,33 @@ $ yourprogram completion fish > ~/.config/fish/completions/yourprogram.fish
 		case "fish":
 			cmd.Root().GenFishCompletion(os.Stdout, true)
 		case "powershell":
-			cmd.Root().GenPowerShellCompletion(os.Stdout)
+			cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout)
 		}
 	},
 }
 ```
 
-**Note:** The cobra generator may include messages printed to stdout for example if the config file is loaded, this will break the auto complete script so must be removed.
+**Note:** The cobra generator may include messages printed to stdout, for example, if the config file is loaded; this will break the auto-completion script so must be removed.
+
+## Adapting the default completion command
+
+Cobra provides a few options for the default `completion` command.  To configure such options you must set
+the `CompletionOptions` field on the *root* command.
+
+To tell Cobra *not* to provide the default `completion` command:
+```
+rootCmd.CompletionOptions.DisableDefaultCmd = true
+```
+
+To tell Cobra *not* to provide the user with the `--no-descriptions` flag to the completion sub-commands:
+```
+rootCmd.CompletionOptions.DisableNoDescFlag = true
+```
+
+To tell Cobra to completely disable descriptions for completions:
+```
+rootCmd.CompletionOptions.DisableDescriptions = true
+```
 
 # Customizing completions
 
@@ -91,8 +128,7 @@ cmd := &cobra.Command{
 	Long:    get_long,
 	Example: get_example,
 	Run: func(cmd *cobra.Command, args []string) {
-		err := RunGet(f, out, cmd, args)
-		util.CheckErr(err)
+		cobra.CheckErr(RunGet(f, out, cmd, args))
 	},
 	ValidArgs: validArgs,
 }
@@ -124,7 +160,7 @@ the completion algorithm if entered manually, e.g. in:
 
 ```bash
 $ kubectl get rc [tab][tab]
-backend        frontend       database 
+backend        frontend       database
 ```
 
 Note that without declaring `rc` as an alias, the completion algorithm would not know to show the list of
@@ -246,7 +282,7 @@ and you'll get something like
 
 ```bash
 $ kubectl exec [tab][tab]
--c            --container=  -p            --pod=  
+-c            --container=  -p            --pod=
 ```
 
 ### Specify dynamic flag completion
@@ -316,7 +352,7 @@ cmd.RegisterFlagCompletionFunc(flagName, func(cmd *cobra.Command, args []string,
 ```
 ### Descriptions for completions
 
-Both `zsh` and `fish` allow for descriptions to annotate completion choices.  For commands and flags, Cobra will provide the descriptions automatically, based on usage information.  For example, using zsh:
+`zsh`, `fish` and `powershell` allow for descriptions to annotate completion choices.  For commands and flags, Cobra will provide the descriptions automatically, based on usage information.  For example, using zsh:
 ```
 $ helm s[tab]
 search  -- search for a keyword in charts
@@ -361,12 +397,12 @@ completion     firstcommand   secondcommand
 ```
 ### Bash legacy dynamic completions
 
-For backwards-compatibility, Cobra still supports its bash legacy dynamic completion solution.
+For backward compatibility, Cobra still supports its bash legacy dynamic completion solution.
 Please refer to [Bash Completions](bash_completions.md) for details.
 
 ## Zsh completions
 
-Cobra supports native Zsh completion generated from the root `cobra.Command`.
+Cobra supports native zsh completion generated from the root `cobra.Command`.
 The generated completion script should be put somewhere in your `$fpath` and be named
 `_<yourProgram>`.  You will need to start a new shell for the completions to become available.
 
@@ -385,23 +421,23 @@ status  -- displays the status of the named release
 $ helm s[tab]
 search  show  status
 ```
-*Note*: Because of backwards-compatibility requirements, we were forced to have a different API to disable completion descriptions between `Zsh` and `Fish`.
+*Note*: Because of backward-compatibility requirements, we were forced to have a different API to disable completion descriptions between `zsh` and `fish`.
 
 ### Limitations
 
 * Custom completions implemented in Bash scripting (legacy) are not supported and will be ignored for `zsh` (including the use of the `BashCompCustom` flag annotation).
-  * You should instead use `ValidArgsFunction` and `RegisterFlagCompletionFunc()` which are portable to the different shells (`bash`, `zsh`, `fish`).
+  * You should instead use `ValidArgsFunction` and `RegisterFlagCompletionFunc()` which are portable to the different shells (`bash`, `zsh`, `fish`, `powershell`).
 * The function `MarkFlagCustom()` is not supported and will be ignored for `zsh`.
   * You should instead use `RegisterFlagCompletionFunc()`.
 
 ### Zsh completions standardization
 
-Cobra 1.1 standardized its zsh completion support to align it with its other shell completions.  Although the API was kept backwards-compatible, some small changes in behavior were introduced.
+Cobra 1.1 standardized its zsh completion support to align it with its other shell completions.  Although the API was kept backward-compatible, some small changes in behavior were introduced.
 Please refer to [Zsh Completions](zsh_completions.md) for details.
 
-## Fish completions
+## fish completions
 
-Cobra supports native Fish completions generated from the root `cobra.Command`.  You can use the `command.GenFishCompletion()` or `command.GenFishCompletionFile()` functions. You must provide these functions with a parameter indicating if the completions should be annotated with a description; Cobra will provide the description automatically based on usage information.  You can choose to make this option configurable by your users.
+Cobra supports native fish completions generated from the root `cobra.Command`.  You can use the `command.GenFishCompletion()` or `command.GenFishCompletionFile()` functions. You must provide these functions with a parameter indicating if the completions should be annotated with a description; Cobra will provide the description automatically based on usage information.  You can choose to make this option configurable by your users.
 ```
 # With descriptions
 $ helm s[tab]
@@ -411,12 +447,12 @@ search  (search for a keyword in charts)  show  (show information of a chart)  s
 $ helm s[tab]
 search  show  status
 ```
-*Note*: Because of backwards-compatibility requirements, we were forced to have a different API to disable completion descriptions between `Zsh` and `Fish`.
+*Note*: Because of backward-compatibility requirements, we were forced to have a different API to disable completion descriptions between `zsh` and `fish`.
 
 ### Limitations
 
-* Custom completions implemented in Bash scripting (legacy) are not supported and will be ignored for `fish` (including the use of the `BashCompCustom` flag annotation).
-  * You should instead use `ValidArgsFunction` and `RegisterFlagCompletionFunc()` which are portable to the different shells (`bash`, `zsh`, `fish`).
+* Custom completions implemented in bash scripting (legacy) are not supported and will be ignored for `fish` (including the use of the `BashCompCustom` flag annotation).
+  * You should instead use `ValidArgsFunction` and `RegisterFlagCompletionFunc()` which are portable to the different shells (`bash`, `zsh`, `fish`, `powershell`).
 * The function `MarkFlagCustom()` is not supported and will be ignored for `fish`.
   * You should instead use `RegisterFlagCompletionFunc()`.
 * The following flag completion annotations are not supported and will be ignored for `fish`:
@@ -431,4 +467,46 @@ search  show  status
 
 ## PowerShell completions
 
-Please refer to [PowerShell Completions](powershell_completions.md) for details.
+Cobra supports native PowerShell completions generated from the root `cobra.Command`. You can use the `command.GenPowerShellCompletion()` or `command.GenPowerShellCompletionFile()` functions. To include descriptions use `command.GenPowerShellCompletionWithDesc()` and `command.GenPowerShellCompletionFileWithDesc()`. Cobra will provide the description automatically based on usage information. You can choose to make this option configurable by your users.
+
+The script is designed to support all three PowerShell completion modes:
+
+* TabCompleteNext (default windows style - on each key press the next option is displayed)
+* Complete (works like bash)
+* MenuComplete (works like zsh)
+
+You set the mode with `Set-PSReadLineKeyHandler -Key Tab -Function <mode>`. Descriptions are only displayed when using the `Complete` or `MenuComplete` mode.
+
+Users need PowerShell version 5.0 or above, which comes with Windows 10 and can be downloaded separately for Windows 7 or 8.1. They can then write the completions to a file and source this file from their PowerShell profile, which is referenced by the `$Profile` environment variable. See `Get-Help about_Profiles` for more info about PowerShell profiles.
+
+```
+# With descriptions and Mode 'Complete'
+$ helm s[tab]
+search  (search for a keyword in charts)  show  (show information of a chart)  status  (displays the status of the named release)
+
+# With descriptions and Mode 'MenuComplete' The description of the current selected value will be displayed below the suggestions.
+$ helm s[tab]
+search    show     status  
+
+search for a keyword in charts
+
+# Without descriptions
+$ helm s[tab]
+search  show  status
+```
+
+### Limitations
+
+* Custom completions implemented in bash scripting (legacy) are not supported and will be ignored for `powershell` (including the use of the `BashCompCustom` flag annotation).
+  * You should instead use `ValidArgsFunction` and `RegisterFlagCompletionFunc()` which are portable to the different shells (`bash`, `zsh`, `fish`, `powershell`).
+* The function `MarkFlagCustom()` is not supported and will be ignored for `powershell`.
+  * You should instead use `RegisterFlagCompletionFunc()`.
+* The following flag completion annotations are not supported and will be ignored for `powershell`:
+  * `BashCompFilenameExt` (filtering by file extension)
+  * `BashCompSubdirsInDir` (filtering by directory)
+* The functions corresponding to the above annotations are consequently not supported and will be ignored for `powershell`:
+  * `MarkFlagFilename()` and `MarkPersistentFlagFilename()` (filtering by file extension)
+  * `MarkFlagDirname()` and `MarkPersistentFlagDirname()` (filtering by directory)
+* Similarly, the following completion directives are not supported and will be ignored for `powershell`:
+  * `ShellCompDirectiveFilterFileExt` (filtering by file extension)
+  * `ShellCompDirectiveFilterDirs` (filtering by directory)
diff --git a/zsh_completions.go b/zsh_completions.go
index 92a7039..1afec30 100644
--- a/zsh_completions.go
+++ b/zsh_completions.go
@@ -70,12 +70,12 @@ func (c *Command) genZshCompletion(w io.Writer, includeDesc bool) error {
 	return err
 }
 
-func genZshComp(buf *bytes.Buffer, name string, includeDesc bool) {
+func genZshComp(buf io.StringWriter, name string, includeDesc bool) {
 	compCmd := ShellCompRequestCmd
 	if !includeDesc {
 		compCmd = ShellCompNoDescRequestCmd
 	}
-	buf.WriteString(fmt.Sprintf(`#compdef _%[1]s %[1]s
+	WriteStringAndCheck(buf, fmt.Sprintf(`#compdef _%[1]s %[1]s
 
 # zsh completion for %-36[1]s -*- shell-script -*-
 
@@ -95,7 +95,7 @@ _%[1]s()
     local shellCompDirectiveFilterFileExt=%[6]d
     local shellCompDirectiveFilterDirs=%[7]d
 
-    local lastParam lastChar flagPrefix requestComp out directive compCount comp lastComp
+    local lastParam lastChar flagPrefix requestComp out directive comp lastComp noSpace
     local -a completions
 
     __%[1]s_debug "\n========= starting completion logic =========="
@@ -163,7 +163,6 @@ _%[1]s()
         return
     fi
 
-    compCount=0
     while IFS='\n' read -r comp; do
         if [ -n "$comp" ]; then
             # If requested, completions are returned with a description.
@@ -175,13 +174,17 @@ _%[1]s()
             local tab=$(printf '\t')
             comp=${comp//$tab/:}
 
-            ((compCount++))
             __%[1]s_debug "Adding completion: ${comp}"
             completions+=${comp}
             lastComp=$comp
         fi
     done < <(printf "%%s\n" "${out[@]}")
 
+    if [ $((directive & shellCompDirectiveNoSpace)) -ne 0 ]; then
+        __%[1]s_debug "Activating nospace."
+        noSpace="-S ''"
+    fi
+
     if [ $((directive & shellCompDirectiveFilterFileExt)) -ne 0 ]; then
         # File extension filtering
         local filteringCmd
@@ -208,25 +211,40 @@ _%[1]s()
             __%[1]s_debug "Listing directories in ."
         fi
 
+        local result
         _arguments '*:dirname:_files -/'" ${flagPrefix}"
+        result=$?
         if [ -n "$subdir" ]; then
             popd >/dev/null 2>&1
         fi
-    elif [ $((directive & shellCompDirectiveNoSpace)) -ne 0 ] && [ ${compCount} -eq 1 ]; then
-        __%[1]s_debug "Activating nospace."
-        # We can use compadd here as there is no description when
-        # there is only one completion.
-        compadd -S '' "${lastComp}"
-    elif [ ${compCount} -eq 0 ]; then
-        if [ $((directive & shellCompDirectiveNoFileComp)) -ne 0 ]; then
-            __%[1]s_debug "deactivating file completion"
+        return $result
+    else
+        __%[1]s_debug "Calling _describe"
+        if eval _describe "completions" completions $flagPrefix $noSpace; then
+            __%[1]s_debug "_describe found some completions"
+
+            # Return the success of having called _describe
+            return 0
         else
-            # Perform file completion
-            __%[1]s_debug "activating file completion"
-            _arguments '*:filename:_files'" ${flagPrefix}"
+            __%[1]s_debug "_describe did not find completions."
+            __%[1]s_debug "Checking if we should do file completion."
+            if [ $((directive & shellCompDirectiveNoFileComp)) -ne 0 ]; then
+                __%[1]s_debug "deactivating file completion"
+
+                # We must return an error code here to let zsh know that there were no
+                # completions found by _describe; this is what will trigger other
+                # matching algorithms to attempt to find completions.
+                # For example zsh can match letters in the middle of words.
+                return 1
+            else
+                # Perform file completion
+                __%[1]s_debug "Activating file completion"
+
+                # We must return the result of this command, so it must be the
+                # last command, or else we must store its result to return it.
+                _arguments '*:filename:_files'" ${flagPrefix}"
+            fi
         fi
-    else
-        _describe "completions" completions $(echo $flagPrefix)
     fi
 }
 


Test patch:
diff --git a/.github/workflows/Test.yml b/.github/workflows/Test.yml
new file mode 100644
index 0000000..c34c2fb
--- /dev/null
+++ b/.github/workflows/Test.yml
@@ -0,0 +1,83 @@
+name: Test
+
+on:
+  push:
+  pull_request:
+
+env:
+  GO111MODULE: on
+
+jobs:
+
+
+  test-unix:
+    strategy:
+      fail-fast: false
+      matrix:
+        platform:
+        - ubuntu
+        - macOS
+        go:
+        - 1.14.x
+        - 1.15.x
+    name: '${{ matrix.platform }} | ${{ matrix.go }}'
+    runs-on: ${{ matrix.platform }}-latest
+    steps:
+
+    - uses: actions/setup-go@v2
+      with:
+        go-version: ${{ matrix.go }}
+
+    - uses: actions/checkout@v2
+
+    - uses: actions/cache@v2
+      with:
+        path: ~/go/pkg/mod
+        key: ${{ runner.os }}-${{ matrix.go }}-${{ hashFiles('**/go.sum') }}
+        restore-keys: ${{ runner.os }}-${{ matrix.go }}-
+
+    - run: |
+        export GOBIN=$HOME/go/bin
+        curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | bash -s -- -b $GOBIN latest
+        go install github.com/kyoh86/richgo
+        go install github.com/mitchellh/gox
+
+    - run: PATH=$HOME/go/bin/:$PATH make
+
+
+  test-win:
+    name: MINGW64
+    defaults:
+      run:
+        shell: msys2 {0}
+    runs-on: windows-latest
+    steps:
+
+    - shell: bash
+      run: git config --global core.autocrlf input
+
+    - uses: msys2/setup-msys2@v2
+      with:
+        msystem: MINGW64
+        update: true
+        install: >
+          git
+          make
+          unzip
+          mingw-w64-x86_64-go
+
+    - uses: actions/checkout@v2
+
+    - uses: actions/cache@v2
+      with:
+        path: ~/go/pkg/mod
+        key: ${{ runner.os }}-${{ matrix.go }}-${{ hashFiles('**/go.sum') }}
+        restore-keys: ${{ runner.os }}-${{ matrix.go }}-
+
+    - run: |
+        export GOBIN=$HOME/go/bin
+        curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | bash -s -- -b $GOBIN latest
+        go install github.com/kyoh86/richgo@latest
+        go install github.com/mitchellh/gox@latest
+
+    - run: PATH=$HOME/go/bin:$PATH make
diff --git a/bash_completions_test.go b/bash_completions_test.go
index 2c182ba..21d1fe0 100644
--- a/bash_completions_test.go
+++ b/bash_completions_test.go
@@ -40,10 +40,9 @@ func checkRegex(t *testing.T, found, pattern string) {
 }
 
 func runShellCheck(s string) error {
-	excluded := []string{
+	cmd := exec.Command("shellcheck", "-s", "bash", "-", "-e",
 		"SC2034", // PREFIX appears unused. Verify it or export it.
-	}
-	cmd := exec.Command("shellcheck", "-s", "bash", "-", "-e", strings.Join(excluded, ","))
+	)
 	cmd.Stderr = os.Stderr
 	cmd.Stdout = os.Stdout
 
@@ -52,7 +51,9 @@ func runShellCheck(s string) error {
 		return err
 	}
 	go func() {
-		stdin.Write([]byte(s))
+		_, err := stdin.Write([]byte(s))
+		CheckErr(err)
+
 		stdin.Close()
 	}()
 
@@ -74,26 +75,26 @@ func TestBashCompletions(t *testing.T) {
 		Run:                    emptyRun,
 	}
 	rootCmd.Flags().IntP("introot", "i", -1, "help message for flag introot")
-	rootCmd.MarkFlagRequired("introot")
+	assertNoErr(t, rootCmd.MarkFlagRequired("introot"))
 
 	// Filename.
 	rootCmd.Flags().String("filename", "", "Enter a filename")
-	rootCmd.MarkFlagFilename("filename", "json", "yaml", "yml")
+	assertNoErr(t, rootCmd.MarkFlagFilename("filename", "json", "yaml", "yml"))
 
 	// Persistent filename.
 	rootCmd.PersistentFlags().String("persistent-filename", "", "Enter a filename")
-	rootCmd.MarkPersistentFlagFilename("persistent-filename")
-	rootCmd.MarkPersistentFlagRequired("persistent-filename")
+	assertNoErr(t, rootCmd.MarkPersistentFlagFilename("persistent-filename"))
+	assertNoErr(t, rootCmd.MarkPersistentFlagRequired("persistent-filename"))
 
 	// Filename extensions.
 	rootCmd.Flags().String("filename-ext", "", "Enter a filename (extension limited)")
-	rootCmd.MarkFlagFilename("filename-ext")
+	assertNoErr(t, rootCmd.MarkFlagFilename("filename-ext"))
 	rootCmd.Flags().String("custom", "", "Enter a filename (extension limited)")
-	rootCmd.MarkFlagCustom("custom", "__complete_custom")
+	assertNoErr(t, rootCmd.MarkFlagCustom("custom", "__complete_custom"))
 
 	// Subdirectories in a given directory.
 	rootCmd.Flags().String("theme", "", "theme to use (located in /themes/THEMENAME/)")
-	rootCmd.Flags().SetAnnotation("theme", BashCompSubdirsInDir, []string{"themes"})
+	assertNoErr(t, rootCmd.Flags().SetAnnotation("theme", BashCompSubdirsInDir, []string{"themes"}))
 
 	// For two word flags check
 	rootCmd.Flags().StringP("two", "t", "", "this is two word flags")
@@ -109,9 +110,9 @@ func TestBashCompletions(t *testing.T) {
 	}
 
 	echoCmd.Flags().String("filename", "", "Enter a filename")
-	echoCmd.MarkFlagFilename("filename", "json", "yaml", "yml")
+	assertNoErr(t, echoCmd.MarkFlagFilename("filename", "json", "yaml", "yml"))
 	echoCmd.Flags().String("config", "", "config to use (located in /config/PROFILE/)")
-	echoCmd.Flags().SetAnnotation("config", BashCompSubdirsInDir, []string{"config"})
+	assertNoErr(t, echoCmd.Flags().SetAnnotation("config", BashCompSubdirsInDir, []string{"config"}))
 
 	printCmd := &Command{
 		Use:   "print [string to print]",
@@ -149,7 +150,7 @@ func TestBashCompletions(t *testing.T) {
 	rootCmd.AddCommand(echoCmd, printCmd, deprecatedCmd, colonCmd)
 
 	buf := new(bytes.Buffer)
-	rootCmd.GenBashCompletion(buf)
+	assertNoErr(t, rootCmd.GenBashCompletion(buf))
 	output := buf.String()
 
 	check(t, output, "_root")
@@ -216,10 +217,10 @@ func TestBashCompletionHiddenFlag(t *testing.T) {
 
 	const flagName = "hiddenFlag"
 	c.Flags().Bool(flagName, false, "")
-	c.Flags().MarkHidden(flagName)
+	assertNoErr(t, c.Flags().MarkHidden(flagName))
 
 	buf := new(bytes.Buffer)
-	c.GenBashCompletion(buf)
+	assertNoErr(t, c.GenBashCompletion(buf))
 	output := buf.String()
 
 	if strings.Contains(output, flagName) {
@@ -232,10 +233,10 @@ func TestBashCompletionDeprecatedFlag(t *testing.T) {
 
 	const flagName = "deprecated-flag"
 	c.Flags().Bool(flagName, false, "")
-	c.Flags().MarkDeprecated(flagName, "use --not-deprecated instead")
+	assertNoErr(t, c.Flags().MarkDeprecated(flagName, "use --not-deprecated instead"))
 
 	buf := new(bytes.Buffer)
-	c.GenBashCompletion(buf)
+	assertNoErr(t, c.GenBashCompletion(buf))
 	output := buf.String()
 
 	if strings.Contains(output, flagName) {
@@ -250,7 +251,7 @@ func TestBashCompletionTraverseChildren(t *testing.T) {
 	c.Flags().BoolP("bool-flag", "b", false, "bool flag")
 
 	buf := new(bytes.Buffer)
-	c.GenBashCompletion(buf)
+	assertNoErr(t, c.GenBashCompletion(buf))
 	output := buf.String()
 
 	// check that local nonpersistent flag are not set since we have TraverseChildren set to true
diff --git a/cobra/cmd/add_test.go b/cobra/cmd/add_test.go
index de92fce..0b32ca6 100644
--- a/cobra/cmd/add_test.go
+++ b/cobra/cmd/add_test.go
@@ -14,10 +14,8 @@ func TestGoldenAddCmd(t *testing.T) {
 	}
 	defer os.RemoveAll(command.AbsolutePath)
 
-	command.Project.Create()
-	if err := command.Create(); err != nil {
-		t.Fatal(err)
-	}
+	assertNoErr(t, command.Project.Create())
+	assertNoErr(t, command.Create())
 
 	generatedFile := fmt.Sprintf("%s/cmd/%s.go", command.AbsolutePath, command.CmdName)
 	goldenFile := fmt.Sprintf("testdata/%s.go.golden", command.CmdName)
diff --git a/cobra/cmd/golden_test.go b/cobra/cmd/golden_test.go
index e4055f3..99b92e3 100644
--- a/cobra/cmd/golden_test.go
+++ b/cobra/cmd/golden_test.go
@@ -3,14 +3,11 @@ package cmd
 import (
 	"bytes"
 	"errors"
-	"flag"
 	"fmt"
 	"io/ioutil"
 	"os/exec"
 )
 
-var update = flag.Bool("update", false, "update .golden files")
-
 func init() {
 	// Mute commands.
 	addCmd.SetOut(new(bytes.Buffer))
@@ -58,27 +55,3 @@ func compareFiles(pathA, pathB string) error {
 	}
 	return nil
 }
-
-// checkLackFiles checks if all elements of expected are in got.
-func checkLackFiles(expected, got []string) error {
-	lacks := make([]string, 0, len(expected))
-	for _, ev := range expected {
-		if !stringInStringSlice(ev, got) {
-			lacks = append(lacks, ev)
-		}
-	}
-	if len(lacks) > 0 {
-		return fmt.Errorf("Lack %v file(s): %v", len(lacks), lacks)
-	}
-	return nil
-}
-
-// stringInStringSlice checks if s is an element of slice.
-func stringInStringSlice(s string, slice []string) bool {
-	for _, v := range slice {
-		if s == v {
-			return true
-		}
-	}
-	return false
-}
diff --git a/cobra/cmd/helpers_test.go b/cobra/cmd/helpers_test.go
new file mode 100644
index 0000000..c5d2002
--- /dev/null
+++ b/cobra/cmd/helpers_test.go
@@ -0,0 +1,9 @@
+package cmd
+
+import "testing"
+
+func assertNoErr(t *testing.T, e error) {
+	if e != nil {
+		t.Error(e)
+	}
+}
diff --git a/cobra/cmd/init_test.go b/cobra/cmd/init_test.go
index c4b3f09..e364f40 100644
--- a/cobra/cmd/init_test.go
+++ b/cobra/cmd/init_test.go
@@ -49,7 +49,7 @@ func TestGoldenInitCmd(t *testing.T) {
 			expectErr: true,
 		},
 		{
-			name:      "returns error when passing an relative path for project",
+			name:      "returns error when passing a relative path for project",
 			args:      []string{"github.com/spf13/testproject"},
 			pkgName:   "github.com/spf13/testproject",
 			expectErr: true,
@@ -59,7 +59,7 @@ func TestGoldenInitCmd(t *testing.T) {
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 
-			initCmd.Flags().Set("pkg-name", tt.pkgName)
+			assertNoErr(t, initCmd.Flags().Set("pkg-name", tt.pkgName))
 			viper.Set("useViper", true)
 			projectPath, err := initializeProject(tt.args)
 			defer func() {
diff --git a/cobra/cmd/testdata/main.go.golden b/cobra/cmd/testdata/main.go.golden
index 0bf75c5..f69d34a 100644
--- a/cobra/cmd/testdata/main.go.golden
+++ b/cobra/cmd/testdata/main.go.golden
@@ -1,5 +1,5 @@
 /*
-Copyright © 2020 NAME HERE <EMAIL ADDRESS>
+Copyright © 2021 NAME HERE <EMAIL ADDRESS>
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/cobra/cmd/testdata/root.go.golden b/cobra/cmd/testdata/root.go.golden
index 1db829c..26dedff 100644
--- a/cobra/cmd/testdata/root.go.golden
+++ b/cobra/cmd/testdata/root.go.golden
@@ -1,5 +1,5 @@
 /*
-Copyright © 2020 NAME HERE <EMAIL ADDRESS>
+Copyright © 2021 NAME HERE <EMAIL ADDRESS>
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
@@ -17,8 +17,8 @@ package cmd
 
 import (
 	"fmt"
-	"github.com/spf13/cobra"
 	"os"
+	"github.com/spf13/cobra"
 
 	homedir "github.com/mitchellh/go-homedir"
 	"github.com/spf13/viper"
@@ -38,16 +38,13 @@ This application is a tool to generate the needed files
 to quickly create a Cobra application.`,
 	// Uncomment the following line if your bare application
 	// has an action associated with it:
-	//	Run: func(cmd *cobra.Command, args []string) { },
+	// Run: func(cmd *cobra.Command, args []string) { },
 }
 
 // Execute adds all child commands to the root command and sets flags appropriately.
 // This is called by main.main(). It only needs to happen once to the rootCmd.
 func Execute() {
-	if err := rootCmd.Execute(); err != nil {
-		fmt.Println(err)
-		os.Exit(1)
-	}
+	cobra.CheckErr(rootCmd.Execute())
 }
 
 func init() {
@@ -72,13 +69,11 @@ func initConfig() {
 	} else {
 		// Find home directory.
 		home, err := homedir.Dir()
-		if err != nil {
-			fmt.Println(err)
-			os.Exit(1)
-		}
+		cobra.CheckErr(err)
 
 		// Search config in home directory with name ".testproject" (without extension).
 		viper.AddConfigPath(home)
+		viper.SetConfigType("yaml")
 		viper.SetConfigName(".testproject")
 	}
 
@@ -86,6 +81,6 @@ func initConfig() {
 
 	// If a config file is found, read it in.
 	if err := viper.ReadInConfig(); err == nil {
-		fmt.Println("Using config file:", viper.ConfigFileUsed())
+		fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed())
 	}
 }
diff --git a/cobra/cmd/testdata/test.go.golden b/cobra/cmd/testdata/test.go.golden
index 3eef95f..584daa2 100644
--- a/cobra/cmd/testdata/test.go.golden
+++ b/cobra/cmd/testdata/test.go.golden
@@ -1,5 +1,5 @@
 /*
-Copyright © 2020 NAME HERE <EMAIL ADDRESS>
+Copyright © 2021 NAME HERE <EMAIL ADDRESS>
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
diff --git a/cobra_test.go b/cobra_test.go
index 0d1755b..1219cc0 100644
--- a/cobra_test.go
+++ b/cobra_test.go
@@ -5,6 +5,12 @@ import (
 	"text/template"
 )
 
+func assertNoErr(t *testing.T, e error) {
+	if e != nil {
+		t.Error(e)
+	}
+}
+
 func TestAddTemplateFunctions(t *testing.T) {
 	AddTemplateFunc("t", func() bool { return true })
 	AddTemplateFuncs(template.FuncMap{
diff --git a/command_test.go b/command_test.go
index 3a47a81..583cb02 100644
--- a/command_test.go
+++ b/command_test.go
@@ -42,6 +42,17 @@ func executeCommandC(root *Command, args ...string) (c *Command, output string,
 	return c, buf.String(), err
 }
 
+func executeCommandWithContextC(ctx context.Context, root *Command, args ...string) (c *Command, output string, err error) {
+	buf := new(bytes.Buffer)
+	root.SetOut(buf)
+	root.SetErr(buf)
+	root.SetArgs(args)
+
+	c, err = root.ExecuteContextC(ctx)
+
+	return c, buf.String(), err
+}
+
 func resetCommandLineFlagSet() {
 	pflag.CommandLine = pflag.NewFlagSet(os.Args[0], pflag.ExitOnError)
 }
@@ -58,6 +69,8 @@ func checkStringOmits(t *testing.T, got, expected string) {
 	}
 }
 
+const onetwo = "one two"
+
 func TestSingleCommand(t *testing.T) {
 	var rootCmdArgs []string
 	rootCmd := &Command{
@@ -78,9 +91,8 @@ func TestSingleCommand(t *testing.T) {
 	}
 
 	got := strings.Join(rootCmdArgs, " ")
-	expected := "one two"
-	if got != expected {
-		t.Errorf("rootCmdArgs expected: %q, got: %q", expected, got)
+	if got != onetwo {
+		t.Errorf("rootCmdArgs expected: %q, got: %q", onetwo, got)
 	}
 }
 
@@ -104,9 +116,8 @@ func TestChildCommand(t *testing.T) {
 	}
 
 	got := strings.Join(child1CmdArgs, " ")
-	expected := "one two"
-	if got != expected {
-		t.Errorf("child1CmdArgs expected: %q, got: %q", expected, got)
+	if got != onetwo {
+		t.Errorf("child1CmdArgs expected: %q, got: %q", onetwo, got)
 	}
 }
 
@@ -145,7 +156,7 @@ func TestSubcommandExecuteC(t *testing.T) {
 	}
 
 	if c.Name() != "child" {
-		t.Errorf(`invalid command returned from ExecuteC: expected "child"', got %q`, c.Name())
+		t.Errorf(`invalid command returned from ExecuteC: expected "child"', got: %q`, c.Name())
 	}
 }
 
@@ -178,6 +189,35 @@ func TestExecuteContext(t *testing.T) {
 	}
 }
 
+func TestExecuteContextC(t *testing.T) {
+	ctx := context.TODO()
+
+	ctxRun := func(cmd *Command, args []string) {
+		if cmd.Context() != ctx {
+			t.Errorf("Command %q must have context when called with ExecuteContext", cmd.Use)
+		}
+	}
+
+	rootCmd := &Command{Use: "root", Run: ctxRun, PreRun: ctxRun}
+	childCmd := &Command{Use: "child", Run: ctxRun, PreRun: ctxRun}
+	granchildCmd := &Command{Use: "grandchild", Run: ctxRun, PreRun: ctxRun}
+
+	childCmd.AddCommand(granchildCmd)
+	rootCmd.AddCommand(childCmd)
+
+	if _, _, err := executeCommandWithContextC(ctx, rootCmd, ""); err != nil {
+		t.Errorf("Root command must not fail: %+v", err)
+	}
+
+	if _, _, err := executeCommandWithContextC(ctx, rootCmd, "child"); err != nil {
+		t.Errorf("Subcommand must not fail: %+v", err)
+	}
+
+	if _, _, err := executeCommandWithContextC(ctx, rootCmd, "child", "grandchild"); err != nil {
+		t.Errorf("Command child must not fail: %+v", err)
+	}
+}
+
 func TestExecute_NoContext(t *testing.T) {
 	run := func(cmd *Command, args []string) {
 		if cmd.Context() != context.Background() {
@@ -243,9 +283,8 @@ func TestCommandAlias(t *testing.T) {
 	}
 
 	got := strings.Join(timesCmdArgs, " ")
-	expected := "one two"
-	if got != expected {
-		t.Errorf("timesCmdArgs expected: %v, got: %v", expected, got)
+	if got != onetwo {
+		t.Errorf("timesCmdArgs expected: %v, got: %v", onetwo, got)
 	}
 }
 
@@ -271,9 +310,8 @@ func TestEnablePrefixMatching(t *testing.T) {
 	}
 
 	got := strings.Join(aCmdArgs, " ")
-	expected := "one two"
-	if got != expected {
-		t.Errorf("aCmdArgs expected: %q, got: %q", expected, got)
+	if got != onetwo {
+		t.Errorf("aCmdArgs expected: %q, got: %q", onetwo, got)
 	}
 
 	EnablePrefixMatching = false
@@ -307,9 +345,8 @@ func TestAliasPrefixMatching(t *testing.T) {
 	}
 
 	got := strings.Join(timesCmdArgs, " ")
-	expected := "one two"
-	if got != expected {
-		t.Errorf("timesCmdArgs expected: %v, got: %v", expected, got)
+	if got != onetwo {
+		t.Errorf("timesCmdArgs expected: %v, got: %v", onetwo, got)
 	}
 
 	EnablePrefixMatching = false
@@ -338,9 +375,8 @@ func TestChildSameName(t *testing.T) {
 	}
 
 	got := strings.Join(fooCmdArgs, " ")
-	expected := "one two"
-	if got != expected {
-		t.Errorf("fooCmdArgs expected: %v, got: %v", expected, got)
+	if got != onetwo {
+		t.Errorf("fooCmdArgs expected: %v, got: %v", onetwo, got)
 	}
 }
 
@@ -368,9 +404,8 @@ func TestGrandChildSameName(t *testing.T) {
 	}
 
 	got := strings.Join(fooCmdArgs, " ")
-	expected := "one two"
-	if got != expected {
-		t.Errorf("fooCmdArgs expected: %v, got: %v", expected, got)
+	if got != onetwo {
+		t.Errorf("fooCmdArgs expected: %v, got: %v", onetwo, got)
 	}
 }
 
@@ -406,9 +441,8 @@ func TestFlagLong(t *testing.T) {
 	}
 
 	got := strings.Join(cArgs, " ")
-	expected := "one two"
-	if got != expected {
-		t.Errorf("Expected arguments: %q, got %q", expected, got)
+	if got != onetwo {
+		t.Errorf("rootCmdArgs expected: %q, got: %q", onetwo, got)
 	}
 }
 
@@ -441,9 +475,8 @@ func TestFlagShort(t *testing.T) {
 	}
 
 	got := strings.Join(cArgs, " ")
-	expected := "one two"
-	if got != expected {
-		t.Errorf("Expected arguments: %q, got %q", expected, got)
+	if got != onetwo {
+		t.Errorf("rootCmdArgs expected: %q, got: %q", onetwo, got)
 	}
 }
 
@@ -645,9 +678,8 @@ func TestPersistentFlagsOnSameCommand(t *testing.T) {
 	}
 
 	got := strings.Join(rootCmdArgs, " ")
-	expected := "one two"
-	if got != expected {
-		t.Errorf("rootCmdArgs expected: %q, got %q", expected, got)
+	if got != onetwo {
+		t.Errorf("rootCmdArgs expected: %q, got %q", onetwo, got)
 	}
 	if flagValue != 7 {
 		t.Errorf("flagValue expected: %v, got %v", 7, flagValue)
@@ -731,9 +763,8 @@ func TestPersistentFlagsOnChild(t *testing.T) {
 	}
 
 	got := strings.Join(childCmdArgs, " ")
-	expected := "one two"
-	if got != expected {
-		t.Errorf("childCmdArgs expected: %q, got %q", expected, got)
+	if got != onetwo {
+		t.Errorf("rootCmdArgs expected: %q, got: %q", onetwo, got)
 	}
 	if parentFlagValue != 8 {
 		t.Errorf("parentFlagValue expected: %v, got %v", 8, parentFlagValue)
@@ -746,9 +777,9 @@ func TestPersistentFlagsOnChild(t *testing.T) {
 func TestRequiredFlags(t *testing.T) {
 	c := &Command{Use: "c", Run: emptyRun}
 	c.Flags().String("foo1", "", "")
-	c.MarkFlagRequired("foo1")
+	assertNoErr(t, c.MarkFlagRequired("foo1"))
 	c.Flags().String("foo2", "", "")
-	c.MarkFlagRequired("foo2")
+	assertNoErr(t, c.MarkFlagRequired("foo2"))
 	c.Flags().String("bar", "", "")
 
 	expected := fmt.Sprintf("required flag(s) %q, %q not set", "foo1", "foo2")
@@ -764,16 +795,16 @@ func TestRequiredFlags(t *testing.T) {
 func TestPersistentRequiredFlags(t *testing.T) {
 	parent := &Command{Use: "parent", Run: emptyRun}
 	parent.PersistentFlags().String("foo1", "", "")
-	parent.MarkPersistentFlagRequired("foo1")
+	assertNoErr(t, parent.MarkPersistentFlagRequired("foo1"))
 	parent.PersistentFlags().String("foo2", "", "")
-	parent.MarkPersistentFlagRequired("foo2")
+	assertNoErr(t, parent.MarkPersistentFlagRequired("foo2"))
 	parent.Flags().String("foo3", "", "")
 
 	child := &Command{Use: "child", Run: emptyRun}
 	child.Flags().String("bar1", "", "")
-	child.MarkFlagRequired("bar1")
+	assertNoErr(t, child.MarkFlagRequired("bar1"))
 	child.Flags().String("bar2", "", "")
-	child.MarkFlagRequired("bar2")
+	assertNoErr(t, child.MarkFlagRequired("bar2"))
 	child.Flags().String("bar3", "", "")
 
 	parent.AddCommand(child)
@@ -793,7 +824,7 @@ func TestPersistentRequiredFlagsWithDisableFlagParsing(t *testing.T) {
 	parent := &Command{Use: "parent", Run: emptyRun}
 	parent.PersistentFlags().Bool("foo", false, "")
 	flag := parent.PersistentFlags().Lookup("foo")
-	parent.MarkPersistentFlagRequired("foo")
+	assertNoErr(t, parent.MarkPersistentFlagRequired("foo"))
 
 	child := &Command{Use: "child", Run: emptyRun}
 	child.DisableFlagParsing = true
@@ -1299,20 +1330,19 @@ func TestHooks(t *testing.T) {
 		t.Errorf("Unexpected error: %v", err)
 	}
 
-	if persPreArgs != "one two" {
-		t.Errorf("Expected persPreArgs %q, got %q", "one two", persPreArgs)
-	}
-	if preArgs != "one two" {
-		t.Errorf("Expected preArgs %q, got %q", "one two", preArgs)
-	}
-	if runArgs != "one two" {
-		t.Errorf("Expected runArgs %q, got %q", "one two", runArgs)
-	}
-	if postArgs != "one two" {
-		t.Errorf("Expected postArgs %q, got %q", "one two", postArgs)
-	}
-	if persPostArgs != "one two" {
-		t.Errorf("Expected persPostArgs %q, got %q", "one two", persPostArgs)
+	for _, v := range []struct {
+		name string
+		got  string
+	}{
+		{"persPreArgs", persPreArgs},
+		{"preArgs", preArgs},
+		{"runArgs", runArgs},
+		{"postArgs", postArgs},
+		{"persPostArgs", persPostArgs},
+	} {
+		if v.got != onetwo {
+			t.Errorf("Expected %s %q, got %q", v.name, onetwo, v.got)
+		}
 	}
 }
 
@@ -1380,44 +1410,42 @@ func TestPersistentHooks(t *testing.T) {
 		t.Errorf("Unexpected error: %v", err)
 	}
 
-	// TODO: currently PersistenPreRun* defined in parent does not
-	// run if the matchin child subcommand has PersistenPreRun.
-	// If the behavior changes (https://github.com/spf13/cobra/issues/252)
-	// this test must be fixed.
-	if parentPersPreArgs != "" {
-		t.Errorf("Expected blank parentPersPreArgs, got %q", parentPersPreArgs)
-	}
-	if parentPreArgs != "" {
-		t.Errorf("Expected blank parentPreArgs, got %q", parentPreArgs)
-	}
-	if parentRunArgs != "" {
-		t.Errorf("Expected blank parentRunArgs, got %q", parentRunArgs)
-	}
-	if parentPostArgs != "" {
-		t.Errorf("Expected blank parentPostArgs, got %q", parentPostArgs)
-	}
-	// TODO: currently PersistenPostRun* defined in parent does not
-	// run if the matchin child subcommand has PersistenPostRun.
-	// If the behavior changes (https://github.com/spf13/cobra/issues/252)
-	// this test must be fixed.
-	if parentPersPostArgs != "" {
-		t.Errorf("Expected blank parentPersPostArgs, got %q", parentPersPostArgs)
+	for _, v := range []struct {
+		name string
+		got  string
+	}{
+		// TODO: currently PersistenPreRun* defined in parent does not
+		// run if the matchin child subcommand has PersistenPreRun.
+		// If the behavior changes (https://github.com/spf13/cobra/issues/252)
+		// this test must be fixed.
+		{"parentPersPreArgs", parentPersPreArgs},
+		{"parentPreArgs", parentPreArgs},
+		{"parentRunArgs", parentRunArgs},
+		{"parentPostArgs", parentPostArgs},
+		// TODO: currently PersistenPostRun* defined in parent does not
+		// run if the matchin child subcommand has PersistenPostRun.
+		// If the behavior changes (https://github.com/spf13/cobra/issues/252)
+		// this test must be fixed.
+		{"parentPersPostArgs", parentPersPostArgs},
+	} {
+		if v.got != "" {
+			t.Errorf("Expected blank %s, got %q", v.name, v.got)
+		}
 	}
 
-	if childPersPreArgs != "one two" {
-		t.Errorf("Expected childPersPreArgs %q, got %q", "one two", childPersPreArgs)
-	}
-	if childPreArgs != "one two" {
-		t.Errorf("Expected childPreArgs %q, got %q", "one two", childPreArgs)
-	}
-	if childRunArgs != "one two" {
-		t.Errorf("Expected childRunArgs %q, got %q", "one two", childRunArgs)
-	}
-	if childPostArgs != "one two" {
-		t.Errorf("Expected childPostArgs %q, got %q", "one two", childPostArgs)
-	}
-	if childPersPostArgs != "one two" {
-		t.Errorf("Expected childPersPostArgs %q, got %q", "one two", childPersPostArgs)
+	for _, v := range []struct {
+		name string
+		got  string
+	}{
+		{"childPersPreArgs", childPersPreArgs},
+		{"childPreArgs", childPreArgs},
+		{"childRunArgs", childRunArgs},
+		{"childPostArgs", childPostArgs},
+		{"childPersPostArgs", childPersPostArgs},
+	} {
+		if v.got != onetwo {
+			t.Errorf("Expected %s %q, got %q", v.name, onetwo, v.got)
+		}
 	}
 }
 
@@ -1741,7 +1769,7 @@ func TestMergeCommandLineToFlags(t *testing.T) {
 func TestUseDeprecatedFlags(t *testing.T) {
 	c := &Command{Use: "c", Run: emptyRun}
 	c.Flags().BoolP("deprecated", "d", false, "deprecated flag")
-	c.Flags().MarkDeprecated("deprecated", "This flag is deprecated")
+	assertNoErr(t, c.Flags().MarkDeprecated("deprecated", "This flag is deprecated"))
 
 	output, err := executeCommand(c, "c", "-d")
 	if err != nil {
@@ -1868,7 +1896,6 @@ type calledAsTestcase struct {
 	call string
 	want string
 	epm  bool
-	tc   bool
 }
 
 func (tc *calledAsTestcase) test(t *testing.T) {
@@ -1890,7 +1917,7 @@ func (tc *calledAsTestcase) test(t *testing.T) {
 	parent.SetOut(output)
 	parent.SetErr(output)
 
-	parent.Execute()
+	_ = parent.Execute()
 
 	if called == nil {
 		if tc.call != "" {
@@ -1908,18 +1935,18 @@ func (tc *calledAsTestcase) test(t *testing.T) {
 
 func TestCalledAs(t *testing.T) {
 	tests := map[string]calledAsTestcase{
-		"find/no-args":            {nil, "parent", "parent", false, false},
-		"find/real-name":          {[]string{"child1"}, "child1", "child1", false, false},
-		"find/full-alias":         {[]string{"that"}, "child2", "that", false, false},
-		"find/part-no-prefix":     {[]string{"thi"}, "", "", false, false},
-		"find/part-alias":         {[]string{"thi"}, "child1", "this", true, false},
-		"find/conflict":           {[]string{"th"}, "", "", true, false},
-		"traverse/no-args":        {nil, "parent", "parent", false, true},
-		"traverse/real-name":      {[]string{"child1"}, "child1", "child1", false, true},
-		"traverse/full-alias":     {[]string{"that"}, "child2", "that", false, true},
-		"traverse/part-no-prefix": {[]string{"thi"}, "", "", false, true},
-		"traverse/part-alias":     {[]string{"thi"}, "child1", "this", true, true},
-		"traverse/conflict":       {[]string{"th"}, "", "", true, true},
+		"find/no-args":            {nil, "parent", "parent", false},
+		"find/real-name":          {[]string{"child1"}, "child1", "child1", false},
+		"find/full-alias":         {[]string{"that"}, "child2", "that", false},
+		"find/part-no-prefix":     {[]string{"thi"}, "", "", false},
+		"find/part-alias":         {[]string{"thi"}, "child1", "this", true},
+		"find/conflict":           {[]string{"th"}, "", "", true},
+		"traverse/no-args":        {nil, "parent", "parent", false},
+		"traverse/real-name":      {[]string{"child1"}, "child1", "child1", false},
+		"traverse/full-alias":     {[]string{"that"}, "child2", "that", false},
+		"traverse/part-no-prefix": {[]string{"thi"}, "", "", false},
+		"traverse/part-alias":     {[]string{"thi"}, "child1", "this", true},
+		"traverse/conflict":       {[]string{"th"}, "", "", true},
 	}
 
 	for name, tc := range tests {
diff --git a/custom_completions_test.go b/completions_test.go
similarity index 82%
rename from custom_completions_test.go
rename to completions_test.go
index 14ec5a9..9bd6a41 100644
--- a/custom_completions_test.go
+++ b/completions_test.go
@@ -2,6 +2,7 @@ package cobra
 
 import (
 	"bytes"
+	"context"
 	"strings"
 	"testing"
 )
@@ -75,6 +76,7 @@ func TestCmdNameCompletionInGo(t *testing.T) {
 
 	expected := strings.Join([]string{
 		"aliased",
+		"completion",
 		"firstChild",
 		"help",
 		"secondChild",
@@ -123,6 +125,7 @@ func TestCmdNameCompletionInGo(t *testing.T) {
 
 	expected = strings.Join([]string{
 		"aliased\tA command with aliases",
+		"completion\tgenerate the autocompletion script for the specified shell",
 		"firstChild\tFirst command",
 		"help\tHelp about any command",
 		"secondChild",
@@ -202,6 +205,7 @@ func TestNoCmdNameCompletionInGo(t *testing.T) {
 
 	expected = strings.Join([]string{
 		"childCmd1",
+		"completion",
 		"help",
 		":4",
 		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
@@ -373,6 +377,7 @@ func TestValidArgsAndCmdCompletionInGo(t *testing.T) {
 	}
 
 	expected := strings.Join([]string{
+		"completion",
 		"help",
 		"thechild",
 		"one",
@@ -423,6 +428,7 @@ func TestValidArgsFuncAndCmdCompletionInGo(t *testing.T) {
 	}
 
 	expected := strings.Join([]string{
+		"completion",
 		"help",
 		"thechild",
 		"one",
@@ -490,6 +496,7 @@ func TestFlagNameCompletionInGo(t *testing.T) {
 
 	expected := strings.Join([]string{
 		"childCmd",
+		"completion",
 		"help",
 		":4",
 		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
@@ -573,6 +580,7 @@ func TestFlagNameCompletionInGoWithDesc(t *testing.T) {
 
 	expected := strings.Join([]string{
 		"childCmd\tfirst command",
+		"completion\tgenerate the autocompletion script for the specified shell",
 		"help\tHelp about any command",
 		":4",
 		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
@@ -780,17 +788,17 @@ func TestRequiredFlagNameCompletionInGo(t *testing.T) {
 	rootCmd.AddCommand(childCmd)
 
 	rootCmd.Flags().IntP("requiredFlag", "r", -1, "required flag")
-	rootCmd.MarkFlagRequired("requiredFlag")
+	assertNoErr(t, rootCmd.MarkFlagRequired("requiredFlag"))
 	requiredFlag := rootCmd.Flags().Lookup("requiredFlag")
 
 	rootCmd.PersistentFlags().IntP("requiredPersistent", "p", -1, "required persistent")
-	rootCmd.MarkPersistentFlagRequired("requiredPersistent")
+	assertNoErr(t, rootCmd.MarkPersistentFlagRequired("requiredPersistent"))
 	requiredPersistent := rootCmd.PersistentFlags().Lookup("requiredPersistent")
 
 	rootCmd.Flags().StringP("release", "R", "", "Release name")
 
 	childCmd.Flags().BoolP("subRequired", "s", false, "sub required flag")
-	childCmd.MarkFlagRequired("subRequired")
+	assertNoErr(t, childCmd.MarkFlagRequired("subRequired"))
 	childCmd.Flags().BoolP("subNotRequired", "n", false, "sub not required flag")
 
 	// Test that a required flag is suggested even without the - prefix
@@ -801,6 +809,7 @@ func TestRequiredFlagNameCompletionInGo(t *testing.T) {
 
 	expected := strings.Join([]string{
 		"childCmd",
+		"completion",
 		"help",
 		"--requiredFlag",
 		"-r",
@@ -926,6 +935,7 @@ func TestRequiredFlagNameCompletionInGo(t *testing.T) {
 
 	expected = strings.Join([]string{
 		"childCmd",
+		"completion",
 		"help",
 		"--requiredFlag",
 		"-r",
@@ -964,19 +974,19 @@ func TestFlagFileExtFilterCompletionInGo(t *testing.T) {
 
 	// No extensions.  Should be ignored.
 	rootCmd.Flags().StringP("file", "f", "", "file flag")
-	rootCmd.MarkFlagFilename("file")
+	assertNoErr(t, rootCmd.MarkFlagFilename("file"))
 
 	// Single extension
 	rootCmd.Flags().StringP("log", "l", "", "log flag")
-	rootCmd.MarkFlagFilename("log", "log")
+	assertNoErr(t, rootCmd.MarkFlagFilename("log", "log"))
 
 	// Multiple extensions
 	rootCmd.Flags().StringP("yaml", "y", "", "yaml flag")
-	rootCmd.MarkFlagFilename("yaml", "yaml", "yml")
+	assertNoErr(t, rootCmd.MarkFlagFilename("yaml", "yaml", "yml"))
 
 	// Directly using annotation
 	rootCmd.Flags().StringP("text", "t", "", "text flag")
-	rootCmd.Flags().SetAnnotation("text", BashCompFilenameExt, []string{"txt"})
+	assertNoErr(t, rootCmd.Flags().SetAnnotation("text", BashCompFilenameExt, []string{"txt"}))
 
 	// Test that the completion logic returns the proper info for the completion
 	// script to handle the file filtering
@@ -1086,15 +1096,15 @@ func TestFlagDirFilterCompletionInGo(t *testing.T) {
 
 	// Filter directories
 	rootCmd.Flags().StringP("dir", "d", "", "dir flag")
-	rootCmd.MarkFlagDirname("dir")
+	assertNoErr(t, rootCmd.MarkFlagDirname("dir"))
 
 	// Filter directories within a directory
 	rootCmd.Flags().StringP("subdir", "s", "", "subdir")
-	rootCmd.Flags().SetAnnotation("subdir", BashCompSubdirsInDir, []string{"themes"})
+	assertNoErr(t, rootCmd.Flags().SetAnnotation("subdir", BashCompSubdirsInDir, []string{"themes"}))
 
 	// Multiple directory specification get ignored
 	rootCmd.Flags().StringP("manydir", "m", "", "manydir")
-	rootCmd.Flags().SetAnnotation("manydir", BashCompSubdirsInDir, []string{"themes", "colors"})
+	assertNoErr(t, rootCmd.Flags().SetAnnotation("manydir", BashCompSubdirsInDir, []string{"themes", "colors"}))
 
 	// Test that the completion logic returns the proper info for the completion
 	// script to handle the directory filtering
@@ -1194,6 +1204,48 @@ func TestFlagDirFilterCompletionInGo(t *testing.T) {
 	}
 }
 
+func TestValidArgsFuncCmdContext(t *testing.T) {
+	validArgsFunc := func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+		ctx := cmd.Context()
+
+		if ctx == nil {
+			t.Error("Received nil context in completion func")
+		} else if ctx.Value("testKey") != "123" {
+			t.Error("Received invalid context")
+		}
+
+		return nil, ShellCompDirectiveDefault
+	}
+
+	rootCmd := &Command{
+		Use: "root",
+		Run: emptyRun,
+	}
+	childCmd := &Command{
+		Use:               "childCmd",
+		ValidArgsFunction: validArgsFunc,
+		Run:               emptyRun,
+	}
+	rootCmd.AddCommand(childCmd)
+
+	//nolint:golint,staticcheck // We can safely use a basic type as key in tests.
+	ctx := context.WithValue(context.Background(), "testKey", "123")
+
+	// Test completing an empty string on the childCmd
+	_, output, err := executeCommandWithContextC(ctx, rootCmd, ShellCompNoDescRequestCmd, "childCmd", "")
+	if err != nil {
+		t.Errorf("Unexpected error: %v", err)
+	}
+
+	expected := strings.Join([]string{
+		":0",
+		"Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n")
+
+	if output != expected {
+		t.Errorf("expected: %q, got: %q", expected, output)
+	}
+}
+
 func TestValidArgsFuncSingleCmd(t *testing.T) {
 	rootCmd := &Command{
 		Use:               "root",
@@ -1430,7 +1482,7 @@ func TestValidArgsFuncInBashScript(t *testing.T) {
 	rootCmd.AddCommand(child)
 
 	buf := new(bytes.Buffer)
-	rootCmd.GenBashCompletion(buf)
+	assertNoErr(t, rootCmd.GenBashCompletion(buf))
 	output := buf.String()
 
 	check(t, output, "has_completion_function=1")
@@ -1445,7 +1497,7 @@ func TestNoValidArgsFuncInBashScript(t *testing.T) {
 	rootCmd.AddCommand(child)
 
 	buf := new(bytes.Buffer)
-	rootCmd.GenBashCompletion(buf)
+	assertNoErr(t, rootCmd.GenBashCompletion(buf))
 	output := buf.String()
 
 	checkOmit(t, output, "has_completion_function=1")
@@ -1461,7 +1513,7 @@ func TestCompleteCmdInBashScript(t *testing.T) {
 	rootCmd.AddCommand(child)
 
 	buf := new(bytes.Buffer)
-	rootCmd.GenBashCompletion(buf)
+	assertNoErr(t, rootCmd.GenBashCompletion(buf))
 	output := buf.String()
 
 	check(t, output, ShellCompNoDescRequestCmd)
@@ -1477,7 +1529,7 @@ func TestCompleteNoDesCmdInZshScript(t *testing.T) {
 	rootCmd.AddCommand(child)
 
 	buf := new(bytes.Buffer)
-	rootCmd.GenZshCompletionNoDesc(buf)
+	assertNoErr(t, rootCmd.GenZshCompletionNoDesc(buf))
 	output := buf.String()
 
 	check(t, output, ShellCompNoDescRequestCmd)
@@ -1493,7 +1545,7 @@ func TestCompleteCmdInZshScript(t *testing.T) {
 	rootCmd.AddCommand(child)
 
 	buf := new(bytes.Buffer)
-	rootCmd.GenZshCompletion(buf)
+	assertNoErr(t, rootCmd.GenZshCompletion(buf))
 	output := buf.String()
 
 	check(t, output, ShellCompRequestCmd)
@@ -1506,7 +1558,7 @@ func TestFlagCompletionInGo(t *testing.T) {
 		Run: emptyRun,
 	}
 	rootCmd.Flags().IntP("introot", "i", -1, "help message for flag introot")
-	rootCmd.RegisterFlagCompletionFunc("introot", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+	assertNoErr(t, rootCmd.RegisterFlagCompletionFunc("introot", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
 		completions := []string{}
 		for _, comp := range []string{"1\tThe first", "2\tThe second", "10\tThe tenth"} {
 			if strings.HasPrefix(comp, toComplete) {
@@ -1514,9 +1566,9 @@ func TestFlagCompletionInGo(t *testing.T) {
 			}
 		}
 		return completions, ShellCompDirectiveDefault
-	})
+	}))
 	rootCmd.Flags().String("filename", "", "Enter a filename")
-	rootCmd.RegisterFlagCompletionFunc("filename", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+	assertNoErr(t, rootCmd.RegisterFlagCompletionFunc("filename", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
 		completions := []string{}
 		for _, comp := range []string{"file.yaml\tYAML format", "myfile.json\tJSON format", "file.xml\tXML format"} {
 			if strings.HasPrefix(comp, toComplete) {
@@ -1524,7 +1576,7 @@ func TestFlagCompletionInGo(t *testing.T) {
 			}
 		}
 		return completions, ShellCompDirectiveNoSpace | ShellCompDirectiveNoFileComp
-	})
+	}))
 
 	// Test completing an empty string
 	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--introot", "")
@@ -1703,7 +1755,7 @@ func TestFlagCompletionInGoWithDesc(t *testing.T) {
 		Run: emptyRun,
 	}
 	rootCmd.Flags().IntP("introot", "i", -1, "help message for flag introot")
-	rootCmd.RegisterFlagCompletionFunc("introot", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+	assertNoErr(t, rootCmd.RegisterFlagCompletionFunc("introot", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
 		completions := []string{}
 		for _, comp := range []string{"1\tThe first", "2\tThe second", "10\tThe tenth"} {
 			if strings.HasPrefix(comp, toComplete) {
@@ -1711,9 +1763,9 @@ func TestFlagCompletionInGoWithDesc(t *testing.T) {
 			}
 		}
 		return completions, ShellCompDirectiveDefault
-	})
+	}))
 	rootCmd.Flags().String("filename", "", "Enter a filename")
-	rootCmd.RegisterFlagCompletionFunc("filename", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
+	assertNoErr(t, rootCmd.RegisterFlagCompletionFunc("filename", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
 		completions := []string{}
 		for _, comp := range []string{"file.yaml\tYAML format", "myfile.json\tJSON format", "file.xml\tXML format"} {
 			if strings.HasPrefix(comp, toComplete) {
@@ -1721,7 +1773,7 @@ func TestFlagCompletionInGoWithDesc(t *testing.T) {
 			}
 		}
 		return completions, ShellCompDirectiveNoSpace | ShellCompDirectiveNoFileComp
-	})
+	}))
 
 	// Test completing an empty string
 	output, err := executeCommand(rootCmd, ShellCompRequestCmd, "--introot", "")
@@ -1918,6 +1970,7 @@ func TestCompleteHelp(t *testing.T) {
 	expected := strings.Join([]string{
 		"child1",
 		"child2",
+		"completion",
 		"help",
 		":4",
 		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
@@ -1935,6 +1988,7 @@ func TestCompleteHelp(t *testing.T) {
 	expected = strings.Join([]string{
 		"child1",
 		"child2",
+		"completion",
 		"help", // "<program> help help" is a valid command, so should be completed
 		":4",
 		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
@@ -1958,3 +2012,285 @@ func TestCompleteHelp(t *testing.T) {
 		t.Errorf("expected: %q, got: %q", expected, output)
 	}
 }
+
+func removeCompCmd(rootCmd *Command) {
+	// Remove completion command for the next test
+	for _, cmd := range rootCmd.commands {
+		if cmd.Name() == compCmdName {
+			rootCmd.RemoveCommand(cmd)
+			return
+		}
+	}
+}
+
+func TestDefaultCompletionCmd(t *testing.T) {
+	rootCmd := &Command{
+		Use:  "root",
+		Args: NoArgs,
+		Run:  emptyRun,
+	}
+
+	// Test that no completion command is created if there are not other sub-commands
+	assertNoErr(t, rootCmd.Execute())
+	for _, cmd := range rootCmd.commands {
+		if cmd.Name() == compCmdName {
+			t.Errorf("Should not have a 'completion' command when there are no other sub-commands of root")
+			break
+		}
+	}
+
+	subCmd := &Command{
+		Use: "sub",
+		Run: emptyRun,
+	}
+	rootCmd.AddCommand(subCmd)
+
+	// Test that a completion command is created if there are other sub-commands
+	found := false
+	assertNoErr(t, rootCmd.Execute())
+	for _, cmd := range rootCmd.commands {
+		if cmd.Name() == compCmdName {
+			found = true
+			break
+		}
+	}
+	if !found {
+		t.Errorf("Should have a 'completion' command when there are other sub-commands of root")
+	}
+	// Remove completion command for the next test
+	removeCompCmd(rootCmd)
+
+	// Test that the default completion command can be disabled
+	rootCmd.CompletionOptions.DisableDefaultCmd = true
+	assertNoErr(t, rootCmd.Execute())
+	for _, cmd := range rootCmd.commands {
+		if cmd.Name() == compCmdName {
+			t.Errorf("Should not have a 'completion' command when the feature is disabled")
+			break
+		}
+	}
+	// Re-enable for next test
+	rootCmd.CompletionOptions.DisableDefaultCmd = false
+
+	// Test that completion descriptions are enabled by default
+	output, err := executeCommand(rootCmd, compCmdName, "zsh")
+	if err != nil {
+		t.Errorf("Unexpected error: %v", err)
+	}
+
+	check(t, output, ShellCompRequestCmd)
+	checkOmit(t, output, ShellCompNoDescRequestCmd)
+	// Remove completion command for the next test
+	removeCompCmd(rootCmd)
+
+	// Test that completion descriptions can be disabled completely
+	rootCmd.CompletionOptions.DisableDescriptions = true
+	output, err = executeCommand(rootCmd, compCmdName, "zsh")
+	if err != nil {
+		t.Errorf("Unexpected error: %v", err)
+	}
+
+	check(t, output, ShellCompNoDescRequestCmd)
+	// Re-enable for next test
+	rootCmd.CompletionOptions.DisableDescriptions = false
+	// Remove completion command for the next test
+	removeCompCmd(rootCmd)
+
+	var compCmd *Command
+	// Test that the --no-descriptions flag is present for the relevant shells only
+	assertNoErr(t, rootCmd.Execute())
+	for _, shell := range []string{"fish", "powershell", "zsh"} {
+		if compCmd, _, err = rootCmd.Find([]string{compCmdName, shell}); err != nil {
+			t.Errorf("Unexpected error: %v", err)
+		}
+		if flag := compCmd.Flags().Lookup(compCmdNoDescFlagName); flag == nil {
+			t.Errorf("Missing --%s flag for %s shell", compCmdNoDescFlagName, shell)
+		}
+	}
+	for _, shell := range []string{"bash"} {
+		if compCmd, _, err = rootCmd.Find([]string{compCmdName, shell}); err != nil {
+			t.Errorf("Unexpected error: %v", err)
+		}
+		if flag := compCmd.Flags().Lookup(compCmdNoDescFlagName); flag != nil {
+			t.Errorf("Unexpected --%s flag for %s shell", compCmdNoDescFlagName, shell)
+		}
+	}
+	// Remove completion command for the next test
+	removeCompCmd(rootCmd)
+
+	// Test that the '--no-descriptions' flag can be disabled
+	rootCmd.CompletionOptions.DisableNoDescFlag = true
+	assertNoErr(t, rootCmd.Execute())
+	for _, shell := range []string{"fish", "zsh", "bash", "powershell"} {
+		if compCmd, _, err = rootCmd.Find([]string{compCmdName, shell}); err != nil {
+			t.Errorf("Unexpected error: %v", err)
+		}
+		if flag := compCmd.Flags().Lookup(compCmdNoDescFlagName); flag != nil {
+			t.Errorf("Unexpected --%s flag for %s shell", compCmdNoDescFlagName, shell)
+		}
+	}
+	// Re-enable for next test
+	rootCmd.CompletionOptions.DisableNoDescFlag = false
+	// Remove completion command for the next test
+	removeCompCmd(rootCmd)
+
+	// Test that the '--no-descriptions' flag is disabled when descriptions are disabled
+	rootCmd.CompletionOptions.DisableDescriptions = true
+	assertNoErr(t, rootCmd.Execute())
+	for _, shell := range []string{"fish", "zsh", "bash", "powershell"} {
+		if compCmd, _, err = rootCmd.Find([]string{compCmdName, shell}); err != nil {
+			t.Errorf("Unexpected error: %v", err)
+		}
+		if flag := compCmd.Flags().Lookup(compCmdNoDescFlagName); flag != nil {
+			t.Errorf("Unexpected --%s flag for %s shell", compCmdNoDescFlagName, shell)
+		}
+	}
+	// Re-enable for next test
+	rootCmd.CompletionOptions.DisableDescriptions = false
+	// Remove completion command for the next test
+	removeCompCmd(rootCmd)
+}
+
+func TestCompleteCompletion(t *testing.T) {
+	rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun}
+	subCmd := &Command{
+		Use: "sub",
+		Run: emptyRun,
+	}
+	rootCmd.AddCommand(subCmd)
+
+	// Test sub-commands of the completion command
+	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "completion", "")
+	if err != nil {
+		t.Errorf("Unexpected error: %v", err)
+	}
+
+	expected := strings.Join([]string{
+		"bash",
+		"fish",
+		"powershell",
+		"zsh",
+		":4",
+		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+	if output != expected {
+		t.Errorf("expected: %q, got: %q", expected, output)
+	}
+
+	// Test there are no completions for the sub-commands of the completion command
+	var compCmd *Command
+	for _, cmd := range rootCmd.Commands() {
+		if cmd.Name() == compCmdName {
+			compCmd = cmd
+			break
+		}
+	}
+
+	for _, shell := range compCmd.Commands() {
+		output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, compCmdName, shell.Name(), "")
+		if err != nil {
+			t.Errorf("Unexpected error: %v", err)
+		}
+
+		expected = strings.Join([]string{
+			":4",
+			"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+		if output != expected {
+			t.Errorf("expected: %q, got: %q", expected, output)
+		}
+	}
+}
+
+func TestMultipleShorthandFlagCompletion(t *testing.T) {
+	rootCmd := &Command{
+		Use:       "root",
+		ValidArgs: []string{"foo", "bar"},
+		Run:       emptyRun,
+	}
+	f := rootCmd.Flags()
+	f.BoolP("short", "s", false, "short flag 1")
+	f.BoolP("short2", "d", false, "short flag 2")
+	f.StringP("short3", "f", "", "short flag 3")
+	_ = rootCmd.RegisterFlagCompletionFunc("short3", func(*Command, []string, string) ([]string, ShellCompDirective) {
+		return []string{"works"}, ShellCompDirectiveNoFileComp
+	})
+
+	// Test that a single shorthand flag works
+	output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-s", "")
+	if err != nil {
+		t.Errorf("Unexpected error: %v", err)
+	}
+
+	expected := strings.Join([]string{
+		"foo",
+		"bar",
+		":4",
+		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+	if output != expected {
+		t.Errorf("expected: %q, got: %q", expected, output)
+	}
+
+	// Test that multiple boolean shorthand flags work
+	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-sd", "")
+	if err != nil {
+		t.Errorf("Unexpected error: %v", err)
+	}
+
+	expected = strings.Join([]string{
+		"foo",
+		"bar",
+		":4",
+		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+	if output != expected {
+		t.Errorf("expected: %q, got: %q", expected, output)
+	}
+
+	// Test that multiple boolean + string shorthand flags work
+	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-sdf", "")
+	if err != nil {
+		t.Errorf("Unexpected error: %v", err)
+	}
+
+	expected = strings.Join([]string{
+		"works",
+		":4",
+		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+	if output != expected {
+		t.Errorf("expected: %q, got: %q", expected, output)
+	}
+
+	// Test that multiple boolean + string with equal sign shorthand flags work
+	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-sdf=")
+	if err != nil {
+		t.Errorf("Unexpected error: %v", err)
+	}
+
+	expected = strings.Join([]string{
+		"works",
+		":4",
+		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+	if output != expected {
+		t.Errorf("expected: %q, got: %q", expected, output)
+	}
+
+	// Test that multiple boolean + string with equal sign with value shorthand flags work
+	output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-sdf=abc", "")
+	if err != nil {
+		t.Errorf("Unexpected error: %v", err)
+	}
+
+	expected = strings.Join([]string{
+		"foo",
+		"bar",
+		":4",
+		"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")
+
+	if output != expected {
+		t.Errorf("expected: %q, got: %q", expected, output)
+	}
+}
diff --git a/doc/man_docs_test.go b/doc/man_docs_test.go
index ee9b875..aa3f5f2 100644
--- a/doc/man_docs_test.go
+++ b/doc/man_docs_test.go
@@ -13,6 +13,12 @@ import (
 	"github.com/spf13/cobra"
 )
 
+func assertNoErr(t *testing.T, e error) {
+	if e != nil {
+		t.Error(e)
+	}
+}
+
 func translate(in string) string {
 	return strings.Replace(in, "-", "\\-", -1)
 }
@@ -133,7 +139,7 @@ func TestGenManSeeAlso(t *testing.T) {
 func TestManPrintFlagsHidesShortDeperecated(t *testing.T) {
 	c := &cobra.Command{}
 	c.Flags().StringP("foo", "f", "default", "Foo flag")
-	c.Flags().MarkShorthandDeprecated("foo", "don't use it no more")
+	assertNoErr(t, c.Flags().MarkShorthandDeprecated("foo", "don't use it no more"))
 
 	buf := new(bytes.Buffer)
 	manPrintFlags(buf, c.Flags())
diff --git a/doc/man_examples_test.go b/doc/man_examples_test.go
index db66042..e20a34c 100644
--- a/doc/man_examples_test.go
+++ b/doc/man_examples_test.go
@@ -17,7 +17,7 @@ func ExampleGenManTree() {
 		Title:   "MINE",
 		Section: "3",
 	}
-	doc.GenManTree(cmd, header, "/tmp")
+	cobra.CheckErr(doc.GenManTree(cmd, header, "/tmp"))
 }
 
 func ExampleGenMan() {
@@ -30,6 +30,6 @@ func ExampleGenMan() {
 		Section: "3",
 	}
 	out := new(bytes.Buffer)
-	doc.GenMan(cmd, header, out)
+	cobra.CheckErr(doc.GenMan(cmd, header, out))
 	fmt.Print(out.String())
 }
diff --git a/fish_completions_test.go b/fish_completions_test.go
index 532b605..a3171e4 100644
--- a/fish_completions_test.go
+++ b/fish_completions_test.go
@@ -15,7 +15,7 @@ func TestCompleteNoDesCmdInFishScript(t *testing.T) {
 	rootCmd.AddCommand(child)
 
 	buf := new(bytes.Buffer)
-	rootCmd.GenFishCompletion(buf, false)
+	assertNoErr(t, rootCmd.GenFishCompletion(buf, false))
 	output := buf.String()
 
 	check(t, output, ShellCompNoDescRequestCmd)
@@ -31,7 +31,7 @@ func TestCompleteCmdInFishScript(t *testing.T) {
 	rootCmd.AddCommand(child)
 
 	buf := new(bytes.Buffer)
-	rootCmd.GenFishCompletion(buf, true)
+	assertNoErr(t, rootCmd.GenFishCompletion(buf, true))
 	output := buf.String()
 
 	check(t, output, ShellCompRequestCmd)
@@ -41,7 +41,7 @@ func TestCompleteCmdInFishScript(t *testing.T) {
 func TestProgWithDash(t *testing.T) {
 	rootCmd := &Command{Use: "root-dash", Args: NoArgs, Run: emptyRun}
 	buf := new(bytes.Buffer)
-	rootCmd.GenFishCompletion(buf, false)
+	assertNoErr(t, rootCmd.GenFishCompletion(buf, false))
 	output := buf.String()
 
 	// Functions name should have replace the '-'
@@ -56,7 +56,7 @@ func TestProgWithDash(t *testing.T) {
 func TestProgWithColon(t *testing.T) {
 	rootCmd := &Command{Use: "root:colon", Args: NoArgs, Run: emptyRun}
 	buf := new(bytes.Buffer)
-	rootCmd.GenFishCompletion(buf, false)
+	assertNoErr(t, rootCmd.GenFishCompletion(buf, false))
 	output := buf.String()
 
 	// Functions name should have replace the ':'
diff --git a/powershell_completions_test.go b/powershell_completions_test.go
deleted file mode 100644
index 29b609d..0000000
--- a/powershell_completions_test.go
+++ /dev/null
@@ -1,122 +0,0 @@
-package cobra
-
-import (
-	"bytes"
-	"strings"
-	"testing"
-)
-
-func TestPowerShellCompletion(t *testing.T) {
-	tcs := []struct {
-		name                string
-		root                *Command
-		expectedExpressions []string
-	}{
-		{
-			name: "trivial",
-			root: &Command{Use: "trivialapp"},
-			expectedExpressions: []string{
-				"Register-ArgumentCompleter -Native -CommandName 'trivialapp' -ScriptBlock",
-				"$command = @(\n        'trivialapp'\n",
-			},
-		},
-		{
-			name: "tree",
-			root: func() *Command {
-				r := &Command{Use: "tree"}
-
-				sub1 := &Command{Use: "sub1"}
-				r.AddCommand(sub1)
-
-				sub11 := &Command{Use: "sub11"}
-				sub12 := &Command{Use: "sub12"}
-
-				sub1.AddCommand(sub11)
-				sub1.AddCommand(sub12)
-
-				sub2 := &Command{Use: "sub2"}
-				r.AddCommand(sub2)
-
-				sub21 := &Command{Use: "sub21"}
-				sub22 := &Command{Use: "sub22"}
-
-				sub2.AddCommand(sub21)
-				sub2.AddCommand(sub22)
-
-				return r
-			}(),
-			expectedExpressions: []string{
-				"'tree'",
-				"[CompletionResult]::new('sub1', 'sub1', [CompletionResultType]::ParameterValue, '')",
-				"[CompletionResult]::new('sub2', 'sub2', [CompletionResultType]::ParameterValue, '')",
-				"'tree;sub1'",
-				"[CompletionResult]::new('sub11', 'sub11', [CompletionResultType]::ParameterValue, '')",
-				"[CompletionResult]::new('sub12', 'sub12', [CompletionResultType]::ParameterValue, '')",
-				"'tree;sub1;sub11'",
-				"'tree;sub1;sub12'",
-				"'tree;sub2'",
-				"[CompletionResult]::new('sub21', 'sub21', [CompletionResultType]::ParameterValue, '')",
-				"[CompletionResult]::new('sub22', 'sub22', [CompletionResultType]::ParameterValue, '')",
-				"'tree;sub2;sub21'",
-				"'tree;sub2;sub22'",
-			},
-		},
-		{
-			name: "flags",
-			root: func() *Command {
-				r := &Command{Use: "flags"}
-				r.Flags().StringP("flag1", "a", "", "")
-				r.Flags().String("flag2", "", "")
-
-				sub1 := &Command{Use: "sub1"}
-				sub1.Flags().StringP("flag3", "c", "", "")
-				r.AddCommand(sub1)
-
-				return r
-			}(),
-			expectedExpressions: []string{
-				"'flags'",
-				"[CompletionResult]::new('-a', 'a', [CompletionResultType]::ParameterName, '')",
-				"[CompletionResult]::new('--flag1', 'flag1', [CompletionResultType]::ParameterName, '')",
-				"[CompletionResult]::new('--flag2', 'flag2', [CompletionResultType]::ParameterName, '')",
-				"[CompletionResult]::new('sub1', 'sub1', [CompletionResultType]::ParameterValue, '')",
-				"'flags;sub1'",
-				"[CompletionResult]::new('-c', 'c', [CompletionResultType]::ParameterName, '')",
-				"[CompletionResult]::new('--flag3', 'flag3', [CompletionResultType]::ParameterName, '')",
-			},
-		},
-		{
-			name: "usage",
-			root: func() *Command {
-				r := &Command{Use: "usage"}
-				r.Flags().String("flag", "", "this describes the usage of the 'flag' flag")
-
-				sub1 := &Command{
-					Use:   "sub1",
-					Short: "short describes 'sub1'",
-				}
-				r.AddCommand(sub1)
-
-				return r
-			}(),
-			expectedExpressions: []string{
-				"[CompletionResult]::new('--flag', 'flag', [CompletionResultType]::ParameterName, 'this describes the usage of the ''flag'' flag')",
-				"[CompletionResult]::new('sub1', 'sub1', [CompletionResultType]::ParameterValue, 'short describes ''sub1''')",
-			},
-		},
-	}
-
-	for _, tc := range tcs {
-		t.Run(tc.name, func(t *testing.T) {
-			buf := new(bytes.Buffer)
-			tc.root.GenPowerShellCompletion(buf)
-			output := buf.String()
-
-			for _, expectedExpression := range tc.expectedExpressions {
-				if !strings.Contains(output, expectedExpression) {
-					t.Errorf("Expected completion to contain %q somewhere; got %q", expectedExpression, output)
-				}
-			}
-		})
-	}
-}