Kiwify.Kiwi.CLI
1.0.0
Prefix Reserved
dotnet add package Kiwify.Kiwi.CLI --version 1.0.0
NuGet\Install-Package Kiwify.Kiwi.CLI -Version 1.0.0
<PackageReference Include="Kiwify.Kiwi.CLI" Version="1.0.0" />
<PackageVersion Include="Kiwify.Kiwi.CLI" Version="1.0.0" />
<PackageReference Include="Kiwify.Kiwi.CLI" />
paket add Kiwify.Kiwi.CLI --version 1.0.0
#r "nuget: Kiwify.Kiwi.CLI, 1.0.0"
#:package Kiwify.Kiwi.CLI@1.0.0
#addin nuget:?package=Kiwify.Kiwi.CLI&version=1.0.0
#tool nuget:?package=Kiwify.Kiwi.CLI&version=1.0.0
Kiwify.Kiwi.CLI
Strongly-typed command-line parsing and execution framework for .NET. Provides a fluent API for defining commands, options, validation rules, and help generation.
Kiwi CLI is designed for both interactive terminal tools and automation-oriented command execution, with pluggable output destinations and structured parsing for complex configuration input.
Target: netstandard2.1
Dependencies: Kiwify.Kiwi.Presentation (shared output and interaction abstractions), System.Text.Json
Why Kiwi CLI?
System.CommandLine is the official .NET command-line parsing library, actively maintained by Microsoft and a suitable choice for a wide range of CLI applications.
Kiwi CLI targets a different set of design priorities.
Rather than modelling options as first-class objects threaded through handler signatures, Kiwi CLI keeps the command definition and the command object separate. Options bind to plain C# properties via expression selectors, and cross-property constraints are declared as dedicated validation rule builders - not middleware or handler-embedded logic.
Different Design Priorities
Expression-based property binding. Options are associated with command object properties via Expression<Func<TCommand, object?>> selectors. No Option<T> objects to thread through handler lambdas, no string keys, no manual value extraction after parsing.
Cross-property validation as first-class builders. RequiredWhen, RequiredWholeFieldGroupOrNone, RequiredOnlyOneFieldGroup, and CustomValidationRule are dedicated builder types that attach directly to the command. Cross-property constraints are declared alongside option definitions rather than embedded in handler logic or custom middleware.
Structured configuration input from the command line. ComplexPropertyParser handles objects, collections, and dictionaries from inline key=value pairs or loader syntax (json::, text::, env::). Structured configuration values can be supplied directly from the command line without writing custom binders.
Two configuration styles. Fluent registration and declarative attribute-based definition produce identical Command<T> instances internally. Teams can choose by preference; both styles compose with the same validation and parsing infrastructure.
Shared output model. Command handlers receive IOutput from Kiwi Presentation rather than framework-specific context objects. Output can be redirected to files, captured in tests with StringOutput, or written to any custom sink without mocking the console.
Kiwi ecosystem integration. Kiwi CLI composes naturally with Kiwi Presentation and Kiwi Renderer. The same IOutput and IOutputWriter pipeline used for prompts and progress bars extends to command handlers and rendered output without additional wiring.
Choosing Between Them
If your application relies heavily on middleware pipelines, DragonFruit-style minimal binding, tight dotnet tooling integration, or native shell completion, System.CommandLine is an appropriate choice.
If your focus is on strongly-typed command objects, expression-based option binding, structured cross-property validation, complex type parsing with loader syntax, and output that composes cleanly with the rest of the Kiwi ecosystem, Kiwi CLI provides a more cohesive model.
Key types
| Type | Purpose |
|---|---|
CommandLineApplication |
Top-level app - routes args to the correct command |
Command<TCommand> |
Single command definition with options and a handler |
GenericOption |
String-valued option with optional validation |
BooleanOption |
Flag option; presence = true, or explicit --flag true/false |
EnumOption<TEnum> |
Option whose valid values are the names of a .NET enum |
ParseResult<TCommand> |
Result of parsing without executing |
ArgumentParser<TCommand> |
Low-level parser (used internally by Command<T>) |
Quick start
var app = new CommandLineApplication("mytool", "1.0.0", "My CLI tool.");
var greet = new Command<GreetOptions>("greet", (opts, output) =>
{
output.WriteResult($"Hello, {opts.Name}!");
});
greet.AddOption(
new GenericOption("--name", "-n").SetHelpText("Name to greet").FieldRequired(),
o => o.Name);
app.AddCommand(greet);
app.Execute(args);
class GreetOptions { public string Name { get; set; } = ""; }
$ mytool greet --name Alice
Hello, Alice!
$ mytool greet
Error: --name is required.
Run 'mytool greet --help' for usage.
Options
GenericOption
new GenericOption("--output", "-o", "--out") // name + aliases
.SetHelpText("Output file path")
.FieldRequired() // must be provided
.SetDefaultValue("result.json") // used when not supplied
.SetValidValues("json", "csv", "xml") // restricts accepted input
.SetRegExValidation(@"^\w+\.json$") // regex constraint
.CustomValidation(v => v.Length <= 255) // arbitrary predicate
BooleanOption
new BooleanOption("--verbose", "-v")
.SetHelpText("Enable verbose output")
.SetTrueValues("on", "yes", "true", "1") // accepted as true
.SetFalseValues("off", "no", "false", "0") // accepted as false
.SetDefaultValue("false")
EnumOption<TEnum>
new EnumOption<OutputFormat>("--format", "-f")
.SetHelpText("Output format")
.SetDefaultValue("json")
.FieldRequired()
Validation rules
Attach rules fluently via extension methods on Command<T>:
using Kiwify.Kiwi.CLI.Validation.Extensions;
// 1. RequiredWhen - field required when a condition holds
cmd.CreateRequiredWhen()
.AddCondition(o => o.Environment == "production")
.AddField(o => o.BackupPath)
.SetErrorMessage("--backup-path is required in production.")
.AttachRule();
// 2. RequiredWholeFieldGroupOrNone - all fields must appear together or not at all
cmd.CreateRequiredWholeFieldGroupOrNone()
.AddField(o => o.Username)
.AddField(o => o.Password)
.SetErrorMessage("Provide both --username and --password, or neither.")
.AttachRule();
// 3. RequiredOnlyOneFieldGroup - exactly one group must be fully provided
cmd.CreateRequiredOnlyOneFieldGroupValidationRule()
.AddGroup(g => g.AddField(o => o.ConfigFile).AddField(o => o.ConfigFormat))
.AddGroup(g => g.AddField(o => o.ConfigUrl))
.AttachRule();
// 4. CustomValidation - arbitrary predicate
cmd.CreateCustomValidationRule()
.AddCondition(o => o.Port >= 1 && o.Port <= 65535)
.SetErrorMessage("--port must be between 1 and 65535.")
.AttachRule();
When validation fails, the error is written via IOutput.WriteError and execution stops before the handler is called:
$ mytool deploy --environment production --version v2.0.0
Error: --backup-path is required when --environment is production.
Run 'mytool deploy --help' for usage.
Multi-command application
var app = new CommandLineApplication("kiwi", "2.0.0", "Developer toolchain.");
// Root command runs when no subcommand is given
app.AttachRootCommand(new Command<RootOptions>("kiwi", opts => { ... }));
// Subcommands
app.AddCommand(ServeCommand.Create());
app.AddCommand(BuildCommand.Create());
app.Execute(args);
Built-in flags handled by the application:
| Flag | Effect |
|---|---|
--help / -h |
Displays app or command help |
--version / -v |
Displays the version string |
Help Generation
Help is generated automatically for both the application and each command. It is validation-rule-aware: any rule that carries a SetValidationDescription(...) label is included in the help output alongside the option list.
Application-level help (invoked with no command, or --help):
$ mytool --help
mytool 1.0.0
My CLI tool.
Usage:
mytool [command] [options]
Commands:
greet Greet a user by name
deploy Deploy the application to a target environment
Options:
-h, --help Show help
--version, -v Show version
Command-level help with validation rules surfaced inline:
$ mytool deploy --help
mytool deploy
Deploy the application to a target environment.
Usage:
mytool deploy [options]
Options:
--environment, -e Target environment (dev, staging, production) [required]
--backup-path Backup storage path
--version, -v Application version tag [required]
--dry-run Preview changes without applying
--tags Comma-separated deployment tags
-h, --help Show help
Validation:
--backup-path is required when --environment is 'production'
Provide both --username and --password, or neither
Descriptions are registered alongside rules and appear in help automatically:
cmd.CreateRequiredWhen()
.AddCondition(o => o.Environment == "production")
.AddField(o => o.BackupPath)
.SetErrorMessage("--backup-path is required when --environment is production.")
.SetValidationDescription("--backup-path required in production")
.AttachRule();
Help can also be generated programmatically via HelpGenerator for embedding in custom UI or documentation pipelines.
Output Routing
Command handlers receive IOutput rather than writing directly to Console. This makes the output destination pluggable without changing handler logic.
// Interactive terminal (default)
app.Execute(args);
// Write to a file - always use `using var` so the writer is flushed on dispose
using var log = new FileOutput("deploy.log");
app.SetOutput(log);
app.Execute(args);
// Capture in memory - no Console dependency in tests
var captured = new StringOutput();
app.SetOutput(captured);
app.Execute(args);
Assert.Contains("Deploy complete", captured.GetResults());
Assert.Empty(captured.GetErrors());
The same pattern extends to any custom IOutput implementation - log aggregators, remote streams, message queues, or AI pipeline sinks - without modifying the command or its handler.
Interactive features automatically degrade gracefully when output is redirected or running in non-interactive environments.
Complex type and collection options
The ArgumentParser uses ComplexPropertyParser and CollectionParser for non-primitive property types.
Collection (string[], List<T>, etc.) - supply comma-separated values:
--tags api,web,core
Dictionary<string, object> - supply key=value pairs; value types are inferred:
--labels Env=prod,Replicas=3,Debug=true
Complex object - supply key=value pairs mapped to properties, or use a loader:
--config "Host=db.local,Port=5432,UseSsl=true"
--config "json::appsettings.json"
--config "text::db.conf"
--config "env::DB_CONFIG_JSON"
Use [PropertyName] or [JsonPropertyName] on properties to define alternative key names for inline and file-based parsing.
Retrieving the executed command
app.Execute(args);
var cmd = app.GetExecutedCommand<MyOptions>();
// null when --help was shown, parsing failed, or a different command ran
if (cmd is not null)
{
// cmd.CommandInstance is the populated MyOptions object
}
Dependency injection
Command objects can receive injected services via constructor injection. Attach an ICommandResolver before calling Execute:
using Kiwify.Kiwi.CLI.DependencyInjection; // separate package
using Microsoft.Extensions.DependencyInjection;
var services = new ServiceCollection();
services.AddSingleton<IMyService, MyService>();
services.AddTransient<MyOptions>();
var provider = services.BuildServiceProvider();
var resolver = new ServiceProviderCommandResolver(provider);
var app = new CommandLineApplication("mytool", "1.0.0", "My tool.");
app.AddCommand(myCommand);
app.AttachCommandResolver(resolver); // must be called before Execute
app.Execute(args);
The command object owns both its CLI properties and its injected dependencies:
public class MyOptions
{
private readonly IMyService? _svc;
public MyOptions() { } // used without a resolver
public MyOptions(IMyService svc) { _svc = svc; } // used when resolver is attached
public string Target { get; set; } = "";
public void Handle(IOutput output) => _svc?.DoWork(Target);
}
ICommandResolver is defined in this package (no external dependencies). The concrete ServiceProviderCommandResolver lives in Kiwify.Kiwi.CLI.DependencyInjection, which references Microsoft.Extensions.DependencyInjection.Abstractions. To use a different container, implement ICommandResolver directly.
See DependencyInjectionSample and Advanced.md for full details.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.1 is compatible. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.1
- Kiwify.Kiwi.Presentation (>= 1.0.0)
- System.Text.Json (>= 10.0.8)
NuGet packages (3)
Showing the top 3 NuGet packages that depend on Kiwify.Kiwi.CLI:
| Package | Downloads |
|---|---|
|
Kiwify.Kiwi.CLI.DependencyInjection
Wires a standard IServiceProvider into Kiwi CLI command activation. ServiceProviderCommandResolver resolves command objects via dependency injection at handler invocation time, keeping the parsing and validation pipeline independent of the DI container. |
|
|
Kiwify.Kiwi.CLI.Declarative
Attribute-based command definition for Kiwi CLI. Decorate plain C# classes with [CliCommand], [CliOption], and validation attributes and let CommandLoader build Command<T> objects automatically - no manual AddOption or validation-rule wiring required. |
|
|
Kiwify.Kiwi.CLI.Configuration
Bridges Kiwi CLI parsed command values into the standard Microsoft.Extensions.Configuration pipeline. Command-line options flow into IConfiguration alongside JSON files, environment variables, and other providers using the standard priority chain. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 198 | 5/28/2026 |