Skip to content

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.

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);

Constraints enforce types at parse time — your handler only runs when input is valid:

ConstraintExampleAccepts
: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.

[Description("Add a new contact")]
public Contact Add(
[Description("Full name")] string name,
[Description("Email address")] string email)
{
// ...
}

This populates --help automatically:

$ myapp add --help
Usage: add {name} {email}
Arguments:
name Full name
email Email address
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 typeDefault output
stringPlain text
IEnumerable<T>Table
Any objectStructured (key: value)
Any + --jsonJSON
Results.Error(...)Error message, exit code 1
Results.Cancelled(...)Message, exit code 1
Results.Exit(n)(none) — explicit exit code n
[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.

Three range types cover different precision needs:

TypeStart/endDuration constraint
ReplDateRangeDateOnlyWhole days only
ReplDateTimeRangeDateTimeAny duration
ReplDateTimeOffsetRangeDateTimeOffsetAny 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-15
myapp report period --period 2024-01-15@30d
myapp logs range --range 2024-03-15T08:00..2024-03-15T18:00
myapp logs range --range 2024-03-15T08:00@8h

For the full list of accepted timestamp and duration formats, see Built-in Types & Formats.


  • 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.