diff --git a/help-util.go b/help-util.go
new file mode 100644
index 0000000000000000000000000000000000000000..3a153d09d5b2def425cb1d3c12706ca3d092fd14
--- /dev/null
+++ b/help-util.go
@@ -0,0 +1,138 @@
+/**
+* Copyright (c) 2009 The Go Authors. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*
+* * Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* * Redistributions in binary form must reproduce the above
+* copyright notice, this list of conditions and the following disclaimer
+* in the documentation and/or other materials provided with the
+* distribution.
+* * Neither the name of Google Inc. nor the names of its
+* contributors may be used to endorse or promote products derived from
+* this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**/
+
+package xflags
+
+// this file contain adapted standard function from flag.go
+
+import (
+	"flag"
+	"fmt"
+	"reflect"
+	"strings"
+)
+
+func getFlagTable(f *flag.FlagSet) []string {
+	var isZeroValueErrs []error
+
+	result := []string{}
+
+	f.VisitAll(func(f *flag.Flag) {
+		var b strings.Builder
+		fmt.Fprintf(&b, "  -%s", f.Name) // Two spaces before -; see next two comments.
+
+		name, usage := flag.UnquoteUsage(f)
+		if len(name) > 0 {
+			b.WriteString(" ")
+			b.WriteString(name)
+		}
+		// Boolean flags of one ASCII letter are so common we
+		// treat them specially, putting their usage on the same line.
+		if b.Len() <= 4 { // space, space, '-', 'x'.
+			b.WriteString("\t")
+		} else {
+			// Four spaces before the tab triggers good alignment
+			// for both 4- and 8-space tab stops.
+			b.WriteString("\n    \t")
+		}
+		b.WriteString(strings.ReplaceAll(usage, "\n", "\n    \t"))
+
+		// Print the default value only if it differs to the zero value
+		// for this flag type.
+		if isZero, err := isZeroValue(f, f.DefValue); err != nil {
+			isZeroValueErrs = append(isZeroValueErrs, err)
+		} else if !isZero {
+			if _, ok := f.Value.(*stringValue); ok {
+				// put quotes on the value
+				fmt.Fprintf(&b, " (default %q)", f.DefValue)
+			} else {
+				fmt.Fprintf(&b, " (default %v)", f.DefValue)
+			}
+		}
+
+		result = append(result, b.String())
+
+	})
+	// If calling String on any zero flag.Values triggered a panic, print
+	// the messages after the full set of defaults so that the programmer
+	// knows to fix the panic.
+	if errs := isZeroValueErrs; len(errs) > 0 {
+		fmt.Fprintln(f.Output())
+		for _, err := range errs {
+			result = append(result, err.Error())
+		}
+	}
+
+	return result
+}
+
+// isZeroValue determines whether the string represents the zero
+// value for a flag.
+func isZeroValue(f *flag.Flag, value string) (ok bool, err error) {
+	// Build a zero value of the flag's Value type, and see if the
+	// result of calling its String method equals the value passed in.
+	// This works unless the Value type is itself an interface type.
+	typ := reflect.TypeOf(f.Value)
+	var z reflect.Value
+	if typ.Kind() == reflect.Pointer {
+		z = reflect.New(typ.Elem())
+	} else {
+		z = reflect.Zero(typ)
+	}
+	// Catch panics calling the String method, which shouldn't prevent the
+	// usage message from being printed, but that we should report to the
+	// user so that they know to fix their code.
+	defer func() {
+		if e := recover(); e != nil {
+			if typ.Kind() == reflect.Pointer {
+				typ = typ.Elem()
+			}
+			err = fmt.Errorf("panic calling String method on zero %v for flag %s: %v", typ, f.Name, e)
+		}
+	}()
+	return value == z.Interface().(flag.Value).String(), nil
+}
+
+// -- string Value
+type stringValue string
+
+func newStringValue(val string, p *string) *stringValue {
+	*p = val
+	return (*stringValue)(p)
+}
+
+func (s *stringValue) Set(val string) error {
+	*s = stringValue(val)
+	return nil
+}
+
+func (s *stringValue) Get() any { return string(*s) }
+
+func (s *stringValue) String() string { return string(*s) }
diff --git a/help.go b/help.go
new file mode 100644
index 0000000000000000000000000000000000000000..2ba9127228f81e646c334b1c86ca8c820c7b3bde
--- /dev/null
+++ b/help.go
@@ -0,0 +1,69 @@
+package xflags
+
+import (
+	"strings"
+)
+
+func (c *cmd[C]) getCommandLevel() (*cmd[C], []string) {
+
+	result := c
+
+	path := []string{}
+	path = append(path, c.name)
+
+	for _, c := range c.commands {
+
+		if c.flagSet.Parsed() {
+			var p []string
+			result, p = c.getCommandLevel()
+			path = append(path, p...)
+			break
+		}
+	}
+
+	return result, path
+}
+
+// Help returns the help text for the command
+func (s *Settings[C]) Help() string {
+
+	cmd, path := s.command.getCommandLevel()
+
+	h := strings.Join(path, " ")
+	var help string
+
+	help = "Usage: " + h + " "
+
+	g := getFlagTable(cmd.settings.command.flagSet)
+	if len(g) > 0 {
+		help += "[global options] "
+	}
+
+	if len(cmd.commands) > 0 {
+		help += "[command] "
+	}
+
+	help += "[arguments]"
+	help += "\n"
+
+	if len(g) > 0 {
+		help += "\nGlobal Options:\n"
+		help += strings.Join(g, "\n") + "\n"
+	}
+
+	if len(cmd.commands) > 0 {
+		for _, c := range cmd.commands {
+			help += "\nCommand: " + c.name + "\n"
+			//help += fmt.Sprintf("  %s\t%s", c.name, c.tagMapping[tagDescription])
+			s := getFlagTable(c.flagSet)
+
+			if len(s) > 0 {
+				help += "\nOptions:\n"
+				help += strings.Join(s, "\n") + "\n"
+			}
+
+		}
+	}
+
+	return help
+}
diff --git a/help_test.go b/help_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..3bebc4adae18dac0d64b557fc0fa5ac96e05d2f4
--- /dev/null
+++ b/help_test.go
@@ -0,0 +1,47 @@
+package xflags
+
+import (
+	"github.com/stretchr/testify/assert"
+	"testing"
+)
+
+type CmdTestHelp1 struct {
+	A    bool `short:"a" description:"Message A"`
+	Sub1 struct {
+		B    bool `short:"b" description:"Message B"`
+		Sub2 struct {
+			C    bool `short:"c" description:"Message C"`
+			Sub3 struct {
+				D bool `short:"d" description:"Message D"`
+			} `command:"sub3"`
+			Sub4 struct {
+				E bool `short:"e" description:"Message E"`
+			} `command:"sub4"`
+		} `command:"sub2"`
+	} `command:"sub1"`
+	aa int `short:"x" description:"Message X"`
+}
+
+func TestGetHelp(t *testing.T) {
+
+	tables := []struct {
+		name      string
+		args      []string
+		substring string
+	}{
+		{"test1", []string{"sub1", "--help"}, "Usage: test1 sub1 [global options] [command] [arguments]"},
+		{"test1", []string{"xsd", "help"}, "Usage: test1 [global options] [command] [arguments]"},
+		{"test2", []string{"sub1", "sub2"}, "Usage: test2 sub1 sub2 [global options] [command] [arguments]"},
+	}
+
+	for _, table := range tables {
+		t.Run(table.name, func(t *testing.T) {
+			s := New(table.name, CmdTestHelp1{})
+			s.Parse(table.args)
+			help := s.Help()
+			assert.NotEmpty(t, help)
+			assert.Contains(t, help, table.substring)
+		})
+	}
+
+}