Skip to content

Commit 54022cc

Browse files
authored
feat(cli): group help flags and make help for commands consistent with groups (#216)
1 parent 4619171 commit 54022cc

1 file changed

Lines changed: 80 additions & 18 deletions

File tree

‎crates/navigator-cli/src/main.rs‎

Lines changed: 80 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
204213
const 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)]
276286
struct 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)]
524553
enum 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)]
611644
enum 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)]
788829
enum 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)]
831875
enum 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)]
10151071
enum 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)]
10601119
enum 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

Comments
 (0)