@@ -282,7 +282,8 @@ func generateHelpOutput(root *cobra.Command, path string, wrap wrapConfig) strin
282282 mainBuf .WriteString (globalFlagsTable (root ))
283283 mainStr := strings .TrimRight (mainBuf .String (), "\n " )
284284
285- // Examples: available commands (comes after flags, no heading to avoid layout issues)
285+ // Examples: available commands (comes after flags, no heading to avoid layout issues).
286+ // For leaf subcommands, append placeholders for required flags/args so the line is runnable.
286287 var examplesBuf bytes.Buffer
287288 if c .HasAvailableSubCommands () {
288289 examplesBuf .WriteString ("```bash\n " )
@@ -292,6 +293,17 @@ func generateHelpOutput(root *cobra.Command, path string, wrap wrapConfig) strin
292293 }
293294 cmdPath := sub .CommandPath ()
294295 examplesBuf .WriteString (cmdPath )
296+ // Append required flags/args placeholders for leaf commands so aside lines are runnable.
297+ if ! sub .HasAvailableSubCommands () {
298+ suffix := exampleSuffixForRequired (sub )
299+ // Metrics subcommands always need --start/--end and --measures; ensure we never emit a bare line.
300+ if suffix == "" && strings .Contains (cmdPath , " metrics " ) {
301+ suffix = "--start 2025-01-01T00:00:00Z --end 2025-01-02T00:00:00Z --measures count"
302+ }
303+ if suffix != "" {
304+ examplesBuf .WriteString (" " + suffix )
305+ }
306+ }
295307 if sub .Short != "" {
296308 examplesBuf .WriteString (" # " + sub .Short )
297309 }
@@ -633,6 +645,126 @@ type argSpec struct {
633645 Required bool `json:"required"`
634646}
635647
648+ // bashCompOneRequiredFlag is the pflag annotation key Cobra sets for required flags.
649+ // Cobra uses this in ValidateRequiredFlags; we use it to discover required flags for example placeholders.
650+ const bashCompOneRequiredFlag = "cobra_annotation_bash_completion_one_required_flag"
651+
652+ // requiredFlags returns the list of required flag names for c (local flags only), in visit order.
653+ func requiredFlags (c * cobra.Command ) []string {
654+ var names []string
655+ c .Flags ().VisitAll (func (f * pflag.Flag ) {
656+ if f .Hidden {
657+ return
658+ }
659+ ann , ok := f .Annotations [bashCompOneRequiredFlag ]
660+ if ! ok || len (ann ) == 0 || ann [0 ] != "true" {
661+ return
662+ }
663+ names = append (names , f .Name )
664+ })
665+ return names
666+ }
667+
668+ // requiredArgsFromAnnotations returns required positional args from c.Annotations["cli.arguments"] in order.
669+ func requiredArgsFromAnnotations (c * cobra.Command ) []argSpec {
670+ if c .Annotations == nil {
671+ return nil
672+ }
673+ raw , ok := c .Annotations ["cli.arguments" ]
674+ if ! ok || raw == "" {
675+ return nil
676+ }
677+ var args []argSpec
678+ if err := json .Unmarshal ([]byte (raw ), & args ); err != nil {
679+ return nil
680+ }
681+ var out []argSpec
682+ for _ , a := range args {
683+ if a .Required {
684+ out = append (out , a )
685+ }
686+ }
687+ return out
688+ }
689+
690+ // exampleSuffixForRequired returns a placeholder string for required flags and args for use in example lines.
691+ // Used by the command-group aside and (optionally) per-command examples. Returns empty if no required flags/args.
692+ func exampleSuffixForRequired (c * cobra.Command ) string {
693+ flags := requiredFlags (c )
694+ args := requiredArgsFromAnnotations (c )
695+ path := c .CommandPath ()
696+ isMetrics := strings .Contains (path , " metrics " )
697+ // Metrics subcommands always require --start, --end, and --measures (CLI/API enforce this). If annotation wasn't found, still add them.
698+ if isMetrics && len (flags ) == 0 && len (args ) == 0 {
699+ return "--start 2025-01-01T00:00:00Z --end 2025-01-02T00:00:00Z --measures count"
700+ }
701+ if len (flags ) == 0 && len (args ) == 0 {
702+ return ""
703+ }
704+ var parts []string
705+
706+ // Time range: start and end (metrics subcommands)
707+ hasStart := false
708+ hasEnd := false
709+ for _ , n := range flags {
710+ if n == "start" {
711+ hasStart = true
712+ }
713+ if n == "end" {
714+ hasEnd = true
715+ }
716+ }
717+ if hasStart && hasEnd && isMetrics {
718+ parts = append (parts , "--start 2025-01-01T00:00:00Z" , "--end 2025-01-02T00:00:00Z" )
719+ }
720+ // measures (metrics)
721+ for _ , n := range flags {
722+ if n == "measures" && isMetrics {
723+ parts = append (parts , "--measures count" )
724+ break
725+ }
726+ }
727+ // Other required flags not yet added
728+ for _ , n := range flags {
729+ if (n == "start" || n == "end" ) && isMetrics {
730+ continue
731+ }
732+ if n == "measures" && isMetrics {
733+ continue
734+ }
735+ switch n {
736+ case "name" :
737+ if ! sliceContains (parts , "--name" ) {
738+ parts = append (parts , "--name <name>" )
739+ }
740+ case "type" :
741+ if ! sliceContains (parts , "--type" ) {
742+ parts = append (parts , "--type <type>" )
743+ }
744+ case "status" :
745+ if ! sliceContains (parts , "--status" ) {
746+ parts = append (parts , "--status acknowledged" )
747+ }
748+ default :
749+ parts = append (parts , "--" + n + " <value>" )
750+ }
751+ }
752+ // Required positionals
753+ for _ , a := range args {
754+ parts = append (parts , "<" + a .Name + ">" )
755+ }
756+ return strings .Join (parts , " " )
757+ }
758+
759+ func sliceContains (parts []string , s string ) bool {
760+ for _ , p := range parts {
761+ if strings .HasPrefix (p , s + " " ) || p == s {
762+ return true
763+ }
764+ }
765+ return false
766+ }
767+
636768// renderArgumentsTable returns a markdown Arguments table if c.Annotations["cli.arguments"] contains
637769// valid JSON array of argSpec. Used to document positional args before the Flags table.
638770func renderArgumentsTable (c * cobra.Command ) string {
0 commit comments