Core Basics
This recipe covers the fundamentals using Repl.Core only — no DI, no hosting extras. It’s the minimal building block everything else is composed from.
The command graph
Section titled “The command graph”using System.ComponentModel;using Repl;using Repl.Parameters;
var store = new ContactStore();var commands = new ContactCommands(store);
var app = CoreReplApp.Create() .WithDescription("Core basics: minimal contacts app without DI.") .WithBanner("Try: list, add Alice alice@test.com, show 1, count");
app.Map("list", commands.List);app.Map("add {name} {email:email}", commands.Add);app.Map("show {id:int}", commands.Show);app.Map("count", commands.Count);app.Map("debug reset", commands.Reset); // hidden below
return app.Run(args);Route constraints
Section titled “Route constraints”Constraints enforce types at parse time — your handler only runs when input is valid:
| Constraint | Example | Accepts |
|---|---|---|
:int | {id:int} | Integer |
:email | {email:email} | Valid email |
:guid | {token:guid} | GUID |
:bool | {flag:bool} | true/false |
:double | {rate:double} | Floating point |
Types are also inferred from parameter types — (int id) implies :int automatically.
Attributes for help and discovery
Section titled “Attributes for help and discovery”[Description("Add a new contact")]public Contact Add( [Description("Full name")] string name, [Description("Email address")] string email){ // ...}This populates --help automatically:
$ myapp add --helpUsage: add {name} {email}
Arguments: name Full name email Email addressHiding commands
Section titled “Hiding commands”app.Map("debug reset", commands.Reset);[Browsable(false)]public void Reset() { /* ... */ }[Browsable(false)] hides the command from --help and discovery, but it still runs if invoked directly.
Return value rendering
Section titled “Return value rendering”| Return type | Default output |
|---|---|
string | Plain text |
IEnumerable<T> | Table |
| Any object | Structured (key: value) |
Any + --json | JSON |
Results.Error(...) | Error message, exit code 1 |
Results.Cancelled(...) | Message, exit code 1 |
Results.Exit(n) | (none) — explicit exit code n |
Reusable options groups
Section titled “Reusable options groups”[ReplOptionsGroup]public class FilterOptions{ [ReplOption(Aliases = ["--limit"])] public int Limit { get; init; } = 20;
[ReplOption(Aliases = ["--sort"])] public string Sort { get; init; } = "name";}app.Map("list", (FilterOptions opts) => commands.List(opts));app.Map("report", (FilterOptions opts) => commands.Report(opts));Every command that accepts FilterOptions shares the same --limit / --sort flags. Avoid names already reserved by Repl — --format, --json, --no-logo, --help, --interactive, --output, and --answer:* are all taken.
Temporal ranges
Section titled “Temporal ranges”Three range types cover different precision needs:
| Type | Start/end | Duration constraint |
|---|---|---|
ReplDateRange | DateOnly | Whole days only |
ReplDateTimeRange | DateTime | Any duration |
ReplDateTimeOffsetRange | DateTimeOffset | Any duration |
app.Map("report period", (ReplDateRange period) => commands.Report(period));app.Map("logs range", (ReplDateTimeRange range) => commands.Logs(range));All three accept start..end and start@duration syntax:
myapp report period --period 2024-01-15..2024-02-15myapp report period --period 2024-01-15@30d
myapp logs range --range 2024-03-15T08:00..2024-03-15T18:00myapp logs range --range 2024-03-15T08:00@8hFor the full list of accepted timestamp and duration formats, see Built-in Types & Formats.
Further reading
Section titled “Further reading”- Routes & Parameters — full route constraint syntax, named options,
[ReplOptionsGroup], and parameter binding rules. - Dependency Injection — service lifetimes, static lambdas, and DI injection patterns for handlers.
- Best Practices — recommended patterns for mapping, output, and testing.