diff --git a/doc/cmd_test.go b/doc/cmd_test.go
index 0d022c77..70b94a58 100644
--- a/doc/cmd_test.go
+++ b/doc/cmd_test.go
@@ -15,6 +15,7 @@
 package doc
 
 import (
+	"regexp"
 	"strings"
 	"testing"
 
@@ -103,3 +104,9 @@ func checkStringOmits(t *testing.T, got, expected string) {
 		t.Errorf("Expected to not contain: \n %v\nGot: %v", expected, got)
 	}
 }
+
+func checkStringMatch(t *testing.T, got, pattern string) {
+	if ok, _ := regexp.MatchString(pattern, got); !ok {
+		t.Errorf("Expected to match: \n%v\nGot:\n %v\n", pattern, got)
+	}
+}
diff --git a/doc/man_docs.go b/doc/man_docs.go
index 2138f248..fee96ced 100644
--- a/doc/man_docs.go
+++ b/doc/man_docs.go
@@ -156,6 +156,31 @@ func manPreamble(buf io.StringWriter, header *GenManHeader, cmd *cobra.Command,
 	cobra.WriteStringAndCheck(buf, description+"\n\n")
 }
 
+func manPrintCommands(buf io.StringWriter, header *GenManHeader, cmd *cobra.Command) {
+	// Find sub-commands that need to be documented
+	subCommands := []*cobra.Command{}
+	for _, c := range cmd.Commands() {
+		if !c.IsAvailableCommand() || c.IsAdditionalHelpTopicCommand() {
+			continue
+		}
+		subCommands = append(subCommands, c)
+	}
+
+	// No need to go further if there is no sub-commands to document
+	if len(subCommands) <= 0 {
+		return
+	}
+
+	// Add a 'COMMANDS' section in the generated documentation
+	cobra.WriteStringAndCheck(buf, "# COMMANDS\n")
+	// For each sub-commands, and an entry with the command name and it's Short description and reference to dedicated
+	// man page
+	for _, c := range subCommands {
+		dashedPath := strings.Replace(c.CommandPath(), " ", "-", -1)
+		cobra.WriteStringAndCheck(buf, fmt.Sprintf("**%s**\n    %s \n    See **%s(%s)**.\n\n", c.Name(), c.Short, dashedPath, header.Section))
+	}
+}
+
 func manPrintFlags(buf io.StringWriter, flags *pflag.FlagSet) {
 	flags.VisitAll(func(flag *pflag.Flag) {
 		if len(flag.Deprecated) > 0 || flag.Hidden {
@@ -209,6 +234,7 @@ func genMan(cmd *cobra.Command, header *GenManHeader) []byte {
 	buf := new(bytes.Buffer)
 
 	manPreamble(buf, header, cmd, dashCommandName)
+	manPrintCommands(buf, header, cmd)
 	manPrintOptions(buf, cmd)
 	if len(cmd.Example) > 0 {
 		buf.WriteString("# EXAMPLE\n")
diff --git a/doc/man_docs_test.go b/doc/man_docs_test.go
index ae6c8e53..b7b6f7ec 100644
--- a/doc/man_docs_test.go
+++ b/doc/man_docs_test.go
@@ -161,6 +161,45 @@ func TestManPrintFlagsHidesShortDeprecated(t *testing.T) {
 	}
 }
 
+func TestGenManCommands(t *testing.T) {
+	header := &GenManHeader{
+		Title:   "Project",
+		Section: "2",
+	}
+
+	// Root command
+	buf := new(bytes.Buffer)
+	if err := GenMan(rootCmd, header, buf); err != nil {
+		t.Fatal(err)
+	}
+	output := buf.String()
+
+	checkStringContains(t, output, ".SH COMMANDS")
+	checkStringMatch(t, output, "\\\\fBecho\\\\fP\n[ \t]+Echo anything to the screen\n[ \t]+See \\\\fBroot-echo\\(2\\)\\\\fP\\\\&\\.")
+	checkStringOmits(t, output, ".PP\n\\fBprint\\fP\n")
+
+	// Echo command
+	buf = new(bytes.Buffer)
+	if err := GenMan(echoCmd, header, buf); err != nil {
+		t.Fatal(err)
+	}
+	output = buf.String()
+
+	checkStringContains(t, output, ".SH COMMANDS")
+	checkStringMatch(t, output, "\\\\fBtimes\\\\fP\n[ \t]+Echo anything to the screen more times\n[ \t]+See \\\\fBroot-echo-times\\(2\\)\\\\fP\\\\&\\.")
+	checkStringMatch(t, output, "\\\\fBechosub\\\\fP\n[ \t]+second sub command for echo\n[ \t]+See \\\\fBroot-echo-echosub\\(2\\)\\\\fP\\\\&\\.")
+	checkStringOmits(t, output, ".PP\n\\fBdeprecated\\fP\n")
+
+	// Time command as echo's subcommand
+	buf = new(bytes.Buffer)
+	if err := GenMan(timesCmd, header, buf); err != nil {
+		t.Fatal(err)
+	}
+	output = buf.String()
+
+	checkStringOmits(t, output, ".SH COMMANDS")
+}
+
 func TestGenManTree(t *testing.T) {
 	c := &cobra.Command{Use: "do [OPTIONS] arg1 arg2"}
 	header := &GenManHeader{Section: "2"}