@@ -201,6 +201,15 @@ const SUBCOMMAND_HELP_TEMPLATE: &str = "\
201201{options}
202202{after-help}" ;
203203
204+ // Help template for leaf commands (sandbox create, provider list, etc.)
205+ const LEAF_HELP_TEMPLATE : & str = "\
206+ {about-with-newline}
207+ \x1b [1mUSAGE\x1b [0m
208+ {usage}
209+
210+ {all-args}
211+ {after-help}" ;
212+
204213const SANDBOX_EXAMPLES : & str = "\x1b [1mALIAS\x1b [0m
205214 sb
206215
@@ -273,20 +282,40 @@ const INFERENCE_EXAMPLES: &str = "\x1b[1mEXAMPLES\x1b[0m
273282#[ command( propagate_version = true ) ]
274283#[ command( help_template = HELP_TEMPLATE ) ]
275284#[ command( disable_help_subcommand = true ) ]
285+ #[ command( disable_help_flag = true , disable_version_flag = true ) ]
276286struct Cli {
277- /// Increase verbosity (-v, -vv, -vvv).
278- #[ arg( short, long, action = clap:: ArgAction :: Count , global = true ) ]
279- verbose : u8 ,
280-
281287 /// Gateway name to operate on (resolved from stored metadata).
282- #[ arg( long, short = 'g' , global = true , env = "OPENSHELL_GATEWAY" ) ]
288+ #[ arg(
289+ long,
290+ short = 'g' ,
291+ global = true ,
292+ env = "OPENSHELL_GATEWAY" ,
293+ help_heading = "GATEWAY FLAGS"
294+ ) ]
283295 gateway : Option < String > ,
284296
285297 /// Gateway endpoint URL (e.g. https://gateway.example.com).
286298 /// Connects directly without looking up gateway metadata.
287- #[ arg( long, global = true , env = "OPENSHELL_GATEWAY_ENDPOINT" ) ]
299+ #[ arg(
300+ long,
301+ global = true ,
302+ env = "OPENSHELL_GATEWAY_ENDPOINT" ,
303+ help_heading = "GATEWAY FLAGS"
304+ ) ]
288305 gateway_endpoint : Option < String > ,
289306
307+ /// Increase verbosity (-v, -vv, -vvv).
308+ #[ arg( short, long, action = clap:: ArgAction :: Count , global = true , help_heading = "GLOBAL FLAGS" ) ]
309+ verbose : u8 ,
310+
311+ /// Print help.
312+ #[ arg( short = 'h' , long, action = clap:: ArgAction :: Help , global = true , help_heading = "GLOBAL FLAGS" ) ]
313+ help : ( ) ,
314+
315+ /// Print version.
316+ #[ arg( short = 'V' , long, action = clap:: ArgAction :: Version , global = true , help_heading = "GLOBAL FLAGS" ) ]
317+ version : ( ) ,
318+
290319 #[ command( subcommand) ]
291320 command : Option < Commands > ,
292321}
@@ -311,7 +340,7 @@ enum Commands {
311340 } ,
312341
313342 /// View sandbox logs.
314- #[ command( visible_alias = "lg" , hide = true , after_help = LOGS_EXAMPLES ) ]
343+ #[ command( visible_alias = "lg" , hide = true , after_help = LOGS_EXAMPLES , help_template = LEAF_HELP_TEMPLATE , next_help_heading = "FLAGS" ) ]
315344 Logs {
316345 /// Sandbox name (defaults to last-used sandbox).
317346 name : Option < String > ,
@@ -363,7 +392,7 @@ enum Commands {
363392 } ,
364393
365394 /// Show gateway status and information.
366- #[ command( hide = true ) ]
395+ #[ command( hide = true , help_template = LEAF_HELP_TEMPLATE , next_help_heading = "FLAGS" ) ]
367396 Status ,
368397
369398 /// Manage inference configuration.
@@ -377,11 +406,11 @@ enum Commands {
377406 // ADDITIONAL COMMANDS
378407 // ===================================================================
379408 /// Launch the OpenShell interactive TUI.
380- #[ command( hide = true ) ]
409+ #[ command( hide = true , help_template = LEAF_HELP_TEMPLATE , next_help_heading = "FLAGS" ) ]
381410 Term ,
382411
383412 /// Generate shell completions.
384- #[ command( hide = true , after_long_help = COMPLETIONS_HELP ) ]
413+ #[ command( hide = true , after_long_help = COMPLETIONS_HELP , help_template = LEAF_HELP_TEMPLATE , next_help_heading = "FLAGS" ) ]
385414 Completions {
386415 /// Shell to generate completions for.
387416 shell : CompletionShell ,
@@ -396,7 +425,7 @@ enum Commands {
396425 ///
397426 /// **Name mode** (for use in `~/.ssh/config`):
398427 /// `openshell ssh-proxy --gateway <name> --name <sandbox-name>`
399- #[ command( hide = true ) ]
428+ #[ command( hide = true , help_template = LEAF_HELP_TEMPLATE , next_help_heading = "FLAGS" ) ]
400429 SshProxy {
401430 /// Gateway URL (e.g., <https://gw.example.com:443/proxy/connect>).
402431 /// Required in token mode. In name mode, can be a gateway name.
@@ -523,7 +552,7 @@ impl CliProviderType {
523552#[ derive( Subcommand , Debug ) ]
524553enum ProviderCommands {
525554 /// Create a provider config.
526- #[ command( group = clap:: ArgGroup :: new( "cred_source" ) . required( true ) . args( [ "from_existing" , "credentials" ] ) ) ]
555+ #[ command( group = clap:: ArgGroup :: new( "cred_source" ) . required( true ) . args( [ "from_existing" , "credentials" ] ) , help_template = LEAF_HELP_TEMPLATE , next_help_heading = "FLAGS" ) ]
527556 Create {
528557 /// Provider name.
529558 #[ arg( long) ]
@@ -551,13 +580,15 @@ enum ProviderCommands {
551580 } ,
552581
553582 /// Fetch a provider by name.
583+ #[ command( help_template = LEAF_HELP_TEMPLATE , next_help_heading = "FLAGS" ) ]
554584 Get {
555585 /// Provider name.
556586 #[ arg( add = ArgValueCompleter :: new( completers:: complete_provider_names) ) ]
557587 name : String ,
558588 } ,
559589
560590 /// List providers.
591+ #[ command( help_template = LEAF_HELP_TEMPLATE , next_help_heading = "FLAGS" ) ]
561592 List {
562593 /// Maximum number of providers to return.
563594 #[ arg( long, default_value_t = 100 ) ]
@@ -573,6 +604,7 @@ enum ProviderCommands {
573604 } ,
574605
575606 /// Update an existing provider's credentials or config.
607+ #[ command( help_template = LEAF_HELP_TEMPLATE , next_help_heading = "FLAGS" ) ]
576608 Update {
577609 /// Provider name.
578610 #[ arg( add = ArgValueCompleter :: new( completers:: complete_provider_names) ) ]
@@ -596,6 +628,7 @@ enum ProviderCommands {
596628 } ,
597629
598630 /// Delete providers by name.
631+ #[ command( help_template = LEAF_HELP_TEMPLATE , next_help_heading = "FLAGS" ) ]
599632 Delete {
600633 /// Provider names.
601634 #[ arg( required = true , num_args = 1 .., value_name = "NAME" , add = ArgValueCompleter :: new( completers:: complete_provider_names) ) ]
@@ -610,6 +643,7 @@ enum ProviderCommands {
610643#[ derive( Subcommand , Debug ) ]
611644enum GatewayCommands {
612645 /// Deploy/start the gateway.
646+ #[ command( help_template = LEAF_HELP_TEMPLATE , next_help_heading = "FLAGS" ) ]
613647 Start {
614648 /// Gateway name.
615649 #[ arg( long, default_value = "openshell" , env = "OPENSHELL_GATEWAY" ) ]
@@ -685,6 +719,7 @@ enum GatewayCommands {
685719 } ,
686720
687721 /// Stop the gateway (preserves state).
722+ #[ command( help_template = LEAF_HELP_TEMPLATE , next_help_heading = "FLAGS" ) ]
688723 Stop {
689724 /// Gateway name (defaults to active gateway).
690725 #[ arg( long, env = "OPENSHELL_GATEWAY" ) ]
@@ -700,6 +735,7 @@ enum GatewayCommands {
700735 } ,
701736
702737 /// Destroy the gateway and its state.
738+ #[ command( help_template = LEAF_HELP_TEMPLATE , next_help_heading = "FLAGS" ) ]
703739 Destroy {
704740 /// Gateway name (defaults to active gateway).
705741 #[ arg( long, env = "OPENSHELL_GATEWAY" ) ]
@@ -720,6 +756,7 @@ enum GatewayCommands {
720756 /// edge proxy (e.g., Cloudflare Access). Opens a browser for
721757 /// authentication and stores the token locally. After adding, the
722758 /// gateway appears in `openshell gateway select`.
759+ #[ command( help_template = LEAF_HELP_TEMPLATE , next_help_heading = "FLAGS" ) ]
723760 Add {
724761 /// Gateway endpoint URL (e.g., `https://8080-3vdegyusg.brevlab.com`).
725762 endpoint : String ,
@@ -738,6 +775,7 @@ enum GatewayCommands {
738775 /// Opens a browser for the edge proxy's login flow and stores the
739776 /// token locally. Use this to re-authenticate when a token expires
740777 /// or to authenticate a gateway added with `--no-auth`.
778+ #[ command( help_template = LEAF_HELP_TEMPLATE , next_help_heading = "FLAGS" ) ]
741779 Login {
742780 /// Gateway name (defaults to the active gateway).
743781 #[ arg( add = ArgValueCompleter :: new( completers:: complete_gateway_names) ) ]
@@ -747,20 +785,23 @@ enum GatewayCommands {
747785 /// Select the active gateway.
748786 ///
749787 /// When called without a name, lists available gateways to choose from.
788+ #[ command( help_template = LEAF_HELP_TEMPLATE , next_help_heading = "FLAGS" ) ]
750789 Select {
751790 /// Gateway name (omit to list available gateways).
752791 #[ arg( add = ArgValueCompleter :: new( completers:: complete_gateway_names) ) ]
753792 name : Option < String > ,
754793 } ,
755794
756795 /// Show gateway deployment details.
796+ #[ command( help_template = LEAF_HELP_TEMPLATE , next_help_heading = "FLAGS" ) ]
757797 Info {
758798 /// Gateway name (defaults to active gateway).
759799 #[ arg( long, env = "OPENSHELL_GATEWAY" ) ]
760800 name : Option < String > ,
761801 } ,
762802
763803 /// Print or start an SSH tunnel for kubectl access to a remote gateway.
804+ #[ command( help_template = LEAF_HELP_TEMPLATE , next_help_heading = "FLAGS" ) ]
764805 Tunnel {
765806 /// Gateway name (defaults to active gateway).
766807 #[ arg( long, env = "OPENSHELL_GATEWAY" ) ]
@@ -787,6 +828,7 @@ enum GatewayCommands {
787828#[ derive( Subcommand , Debug ) ]
788829enum InferenceCommands {
789830 /// Set gateway-level inference provider and model.
831+ #[ command( help_template = LEAF_HELP_TEMPLATE , next_help_heading = "FLAGS" ) ]
790832 Set {
791833 /// Provider name.
792834 #[ arg( long, add = ArgValueCompleter :: new( completers:: complete_provider_names) ) ]
@@ -804,6 +846,7 @@ enum InferenceCommands {
804846 } ,
805847
806848 /// Update gateway-level inference configuration (partial update).
849+ #[ command( help_template = LEAF_HELP_TEMPLATE , next_help_heading = "FLAGS" ) ]
807850 Update {
808851 /// Provider name (unchanged if omitted).
809852 #[ arg( long, add = ArgValueCompleter :: new( completers:: complete_provider_names) ) ]
@@ -819,6 +862,7 @@ enum InferenceCommands {
819862 } ,
820863
821864 /// Get gateway-level inference provider and model.
865+ #[ command( help_template = LEAF_HELP_TEMPLATE , next_help_heading = "FLAGS" ) ]
822866 Get {
823867 /// Show the system inference route instead of the user-facing route.
824868 /// When omitted, both routes are displayed.
@@ -830,6 +874,7 @@ enum InferenceCommands {
830874#[ derive( Subcommand , Debug ) ]
831875enum SandboxCommands {
832876 /// Create a sandbox.
877+ #[ command( help_template = LEAF_HELP_TEMPLATE , next_help_heading = "FLAGS" ) ]
833878 Create {
834879 /// Optional sandbox name (auto-generated when omitted).
835880 #[ arg( long) ]
@@ -855,11 +900,11 @@ enum SandboxCommands {
855900 /// working directory (`/sandbox`).
856901 /// `.gitignore` rules are applied by default; use `--no-git-ignore` to
857902 /// upload everything.
858- #[ arg( long, value_hint = ValueHint :: AnyPath ) ]
903+ #[ arg( long, value_hint = ValueHint :: AnyPath , help_heading = "UPLOAD FLAGS" ) ]
859904 upload : Option < String > ,
860905
861906 /// Disable `.gitignore` filtering for `--upload`.
862- #[ arg( long, requires = "upload" ) ]
907+ #[ arg( long, requires = "upload" , help_heading = "UPLOAD FLAGS" ) ]
863908 no_git_ignore : bool ,
864909
865910 /// Keep the sandbox alive after non-interactive commands.
@@ -869,11 +914,11 @@ enum SandboxCommands {
869914 /// SSH destination for remote bootstrap (e.g., user@hostname).
870915 /// Only used when no cluster exists yet; ignored if a cluster is
871916 /// already active.
872- #[ arg( long) ]
917+ #[ arg( long, help_heading = "BOOTSTRAP FLAGS" ) ]
873918 remote : Option < String > ,
874919
875920 /// Path to SSH private key for remote bootstrap.
876- #[ arg( long, value_hint = ValueHint :: FilePath ) ]
921+ #[ arg( long, value_hint = ValueHint :: FilePath , help_heading = "BOOTSTRAP FLAGS" ) ]
877922 ssh_key : Option < String > ,
878923
879924 /// Provider names to attach to this sandbox.
@@ -905,11 +950,15 @@ enum SandboxCommands {
905950 ///
906951 /// Without this flag, an interactive prompt asks whether to bootstrap;
907952 /// in non-interactive mode the command errors.
908- #[ arg( long, overrides_with = "no_bootstrap" ) ]
953+ #[ arg(
954+ long,
955+ overrides_with = "no_bootstrap" ,
956+ help_heading = "BOOTSTRAP FLAGS"
957+ ) ]
909958 bootstrap : bool ,
910959
911960 /// Never bootstrap a gateway automatically; error if none is available.
912- #[ arg( long, overrides_with = "bootstrap" ) ]
961+ #[ arg( long, overrides_with = "bootstrap" , help_heading = "BOOTSTRAP FLAGS" ) ]
913962 no_bootstrap : bool ,
914963
915964 /// Auto-create missing providers from local credentials.
@@ -929,13 +978,15 @@ enum SandboxCommands {
929978 } ,
930979
931980 /// Fetch a sandbox by name.
981+ #[ command( help_template = LEAF_HELP_TEMPLATE , next_help_heading = "FLAGS" ) ]
932982 Get {
933983 /// Sandbox name (defaults to last-used sandbox).
934984 #[ arg( add = ArgValueCompleter :: new( completers:: complete_sandbox_names) ) ]
935985 name : Option < String > ,
936986 } ,
937987
938988 /// List sandboxes.
989+ #[ command( help_template = LEAF_HELP_TEMPLATE , next_help_heading = "FLAGS" ) ]
939990 List {
940991 /// Maximum number of sandboxes to return.
941992 #[ arg( long, default_value_t = 100 ) ]
@@ -955,6 +1006,7 @@ enum SandboxCommands {
9551006 } ,
9561007
9571008 /// Delete a sandbox by name.
1009+ #[ command( help_template = LEAF_HELP_TEMPLATE , next_help_heading = "FLAGS" ) ]
9581010 Delete {
9591011 /// Sandbox names.
9601012 #[ arg( required = true , num_args = 1 .., value_name = "NAME" , add = ArgValueCompleter :: new( completers:: complete_sandbox_names) ) ]
@@ -964,13 +1016,15 @@ enum SandboxCommands {
9641016 /// Connect to a sandbox.
9651017 ///
9661018 /// When no name is given, reconnects to the last-used sandbox.
1019+ #[ command( help_template = LEAF_HELP_TEMPLATE , next_help_heading = "FLAGS" ) ]
9671020 Connect {
9681021 /// Sandbox name (defaults to last-used sandbox).
9691022 #[ arg( add = ArgValueCompleter :: new( completers:: complete_sandbox_names) ) ]
9701023 name : Option < String > ,
9711024 } ,
9721025
9731026 /// Upload local files to a sandbox.
1027+ #[ command( help_template = LEAF_HELP_TEMPLATE , next_help_heading = "FLAGS" ) ]
9741028 Upload {
9751029 /// Sandbox name.
9761030 #[ arg( add = ArgValueCompleter :: new( completers:: complete_sandbox_names) ) ]
@@ -989,6 +1043,7 @@ enum SandboxCommands {
9891043 } ,
9901044
9911045 /// Download files from a sandbox.
1046+ #[ command( help_template = LEAF_HELP_TEMPLATE , next_help_heading = "FLAGS" ) ]
9921047 Download {
9931048 /// Sandbox name.
9941049 #[ arg( add = ArgValueCompleter :: new( completers:: complete_sandbox_names) ) ]
@@ -1005,6 +1060,7 @@ enum SandboxCommands {
10051060 ///
10061061 /// Outputs a Host block suitable for appending to ~/.ssh/config,
10071062 /// enabling tools like `VSCode` Remote-SSH to connect to the sandbox.
1063+ #[ command( help_template = LEAF_HELP_TEMPLATE , next_help_heading = "FLAGS" ) ]
10081064 SshConfig {
10091065 /// Sandbox name (defaults to last-used sandbox).
10101066 name : Option < String > ,
@@ -1014,6 +1070,7 @@ enum SandboxCommands {
10141070#[ derive( Subcommand , Debug ) ]
10151071enum PolicyCommands {
10161072 /// Update policy on a live sandbox.
1073+ #[ command( help_template = LEAF_HELP_TEMPLATE , next_help_heading = "FLAGS" ) ]
10171074 Set {
10181075 /// Sandbox name (defaults to last-used sandbox).
10191076 name : Option < String > ,
@@ -1032,6 +1089,7 @@ enum PolicyCommands {
10321089 } ,
10331090
10341091 /// Show current active policy for a sandbox.
1092+ #[ command( help_template = LEAF_HELP_TEMPLATE , next_help_heading = "FLAGS" ) ]
10351093 Get {
10361094 /// Sandbox name (defaults to last-used sandbox).
10371095 name : Option < String > ,
@@ -1046,6 +1104,7 @@ enum PolicyCommands {
10461104 } ,
10471105
10481106 /// List policy history for a sandbox.
1107+ #[ command( help_template = LEAF_HELP_TEMPLATE , next_help_heading = "FLAGS" ) ]
10491108 List {
10501109 /// Sandbox name (defaults to last-used sandbox).
10511110 name : Option < String > ,
@@ -1059,6 +1118,7 @@ enum PolicyCommands {
10591118#[ derive( Subcommand , Debug ) ]
10601119enum ForwardCommands {
10611120 /// Start forwarding a local port to a sandbox.
1121+ #[ command( help_template = LEAF_HELP_TEMPLATE , next_help_heading = "FLAGS" ) ]
10621122 Start {
10631123 /// Port to forward (used as both local and remote port).
10641124 port : u16 ,
@@ -1073,6 +1133,7 @@ enum ForwardCommands {
10731133 } ,
10741134
10751135 /// Stop a background port forward.
1136+ #[ command( help_template = LEAF_HELP_TEMPLATE , next_help_heading = "FLAGS" ) ]
10761137 Stop {
10771138 /// Port that was forwarded.
10781139 port : u16 ,
@@ -1083,6 +1144,7 @@ enum ForwardCommands {
10831144 } ,
10841145
10851146 /// List active port forwards.
1147+ #[ command( help_template = LEAF_HELP_TEMPLATE , next_help_heading = "FLAGS" ) ]
10861148 List ,
10871149}
10881150
0 commit comments