Summary
CommandBuilder.Hidden() can hide an entire command or context, but there is no equivalent way to hide one option while keeping the command discoverable and the option bindable for compatibility/internal callers. Global options are likewise always advertised once registered.
Add option-level hidden metadata and a fluent .Hidden(bool) surface for:
- direct command options;
- properties supplied through a command options group;
- typed global options registered through
UseGlobalOptions<T>();
- manually registered global options created through
AddGlobalOption(...).
Motivation
Some options must remain accepted without being advertised as normal user-facing choices, for example:
- deprecated compatibility switches;
- internal or diagnostic escape hatches;
- migration aliases that should remain bindable temporarily;
- advanced options intended only for callers that already know about them;
- host-wide compatibility switches implemented as global options.
Today the only fluent visibility control is to hide the whole command:
app.Map("deploy", Deploy).Hidden();
That is too coarse when only one command or global option should disappear from discovery.
Desired API
The exact fluent shape can follow the existing builder conventions, but it should expose option metadata/builders with .Hidden(bool isHidden = true).
For a command option, for example:
var command = app.Map(
"deploy",
([ReplOption(Name = "internal-mode")] bool internalMode = false) => Deploy(internalMode));
command.Option("internalMode").Hidden();
or equivalently:
app.Map("deploy", Deploy)
.WithOption("internalMode", option => option.Hidden());
For a manually registered global option, the design could return a global-option builder:
options.Parsing
.AddGlobalOption<string>("internal-tenant")
.Hidden();
or provide an equivalent configuration callback if changing the current void return type is undesirable.
Typed global options should have attribute parity, for example:
public sealed class HostOptions
{
[ReplOption(Name = "internal-tenant", Hidden = true)]
public string? InternalTenant { get; init; }
}
Honoring [Browsable(false)] consistently for command parameters, options-group properties, and typed global-option properties should also be considered.
The exact spelling is open, but both command and global option systems must expose a coherent way to toggle hidden state.
Expected semantics
- Hiding a command option does not hide its command.
- Hiding one global option does not hide the other global options.
- A hidden option remains accepted by parsing and binding when explicitly supplied.
- Its canonical token, aliases, reverse aliases, value aliases, description, defaults, and completion candidates are omitted from discovery surfaces.
- Filtering is consistent across:
- root and command help;
- global-options help sections;
- interactive autocomplete/live hints;
- generated shell completion;
- exported documentation models/renderers.
- MCP/tool-schema behavior for hidden command options is explicit and consistent with the meaning of command-level
.Hidden(); if hidden means all discovery surfaces, the option should not be advertised in generated MCP schemas while ordinary CLI binding remains supported.
.Hidden(false) restores normal visibility.
- Existing visible-option behavior remains unchanged.
Current implementation notes
The option metadata pipeline currently has no hidden flag:
ReplOptionAttribute configures name, aliases, reverse aliases, mode, case sensitivity, and arity, but not visibility.
ReplDocOption has no IsHidden field.
CommandBuilder.Hidden() only controls CommandBuilder.IsHidden for the entire command.
- There is no public command-option builder/configuration surface analogous to
CommandBuilder today.
GlobalOptionDefinition has no hidden metadata.
ParsingOptions.AddGlobalOption(...) currently returns void, so fluent global-option configuration needs a deliberate API shape.
UseGlobalOptions<T>() copies name, aliases, defaults, and descriptions from properties but has no visibility path.
A likely implementation needs to preserve hidden options in parsing/binding metadata while carrying IsHidden through documentation metadata so each discovery renderer can filter it without disabling execution.
Acceptance criteria
- A direct command option can be marked hidden through a fluent
.Hidden() API.
- A command options-group property can be marked hidden.
- A manually registered global option can be marked hidden through a fluent
.Hidden() API or an equivalent builder callback that exposes .Hidden().
- A typed global-option property can be marked hidden, with attribute parity for the fluent metadata.
- Commands and unrelated global options remain discoverable when one option is hidden.
- Hidden command and global options remain parsable/bindable when explicitly supplied.
- Hidden options are absent from root/command help, global-options help, interactive completion, shell completion, and documentation exports.
- MCP schema behavior for command options is defined and regression-tested.
- Canonical names and every alias form are hidden consistently.
.Hidden(false) makes the option visible again.
- Visible command and global options have no behavioral or rendering regression.
- Required hidden options receive deliberate behavior: either reject that configuration with a useful diagnostic or document/test the intended contract.
Summary
CommandBuilder.Hidden()can hide an entire command or context, but there is no equivalent way to hide one option while keeping the command discoverable and the option bindable for compatibility/internal callers. Global options are likewise always advertised once registered.Add option-level hidden metadata and a fluent
.Hidden(bool)surface for:UseGlobalOptions<T>();AddGlobalOption(...).Motivation
Some options must remain accepted without being advertised as normal user-facing choices, for example:
Today the only fluent visibility control is to hide the whole command:
That is too coarse when only one command or global option should disappear from discovery.
Desired API
The exact fluent shape can follow the existing builder conventions, but it should expose option metadata/builders with
.Hidden(bool isHidden = true).For a command option, for example:
or equivalently:
For a manually registered global option, the design could return a global-option builder:
or provide an equivalent configuration callback if changing the current
voidreturn type is undesirable.Typed global options should have attribute parity, for example:
Honoring
[Browsable(false)]consistently for command parameters, options-group properties, and typed global-option properties should also be considered.The exact spelling is open, but both command and global option systems must expose a coherent way to toggle hidden state.
Expected semantics
.Hidden(); if hidden means all discovery surfaces, the option should not be advertised in generated MCP schemas while ordinary CLI binding remains supported..Hidden(false)restores normal visibility.Current implementation notes
The option metadata pipeline currently has no hidden flag:
ReplOptionAttributeconfigures name, aliases, reverse aliases, mode, case sensitivity, and arity, but not visibility.ReplDocOptionhas noIsHiddenfield.CommandBuilder.Hidden()only controlsCommandBuilder.IsHiddenfor the entire command.CommandBuildertoday.GlobalOptionDefinitionhas no hidden metadata.ParsingOptions.AddGlobalOption(...)currently returnsvoid, so fluent global-option configuration needs a deliberate API shape.UseGlobalOptions<T>()copies name, aliases, defaults, and descriptions from properties but has no visibility path.A likely implementation needs to preserve hidden options in parsing/binding metadata while carrying
IsHiddenthrough documentation metadata so each discovery renderer can filter it without disabling execution.Acceptance criteria
.Hidden()API..Hidden()API or an equivalent builder callback that exposes.Hidden()..Hidden(false)makes the option visible again.