Skip to content

Scoped Contexts

Scoped contexts let users navigate into sub-trees of your command graph — like cd for your app’s data model.

app.Context("client", client =>
{
client.Map("list", static (IContactStore store) => store.All());
client.Context("{id:int}", scoped =>
{
scoped.Map("show", static (int id, IContactStore store) => store.Get(id));
scoped.Map("remove", static (int id, IContactStore store) =>
{
store.Remove(id);
return Results.Success("Removed.");
});
});
});

The shape of this graph:

myapp
└── client
├── list
└── {id:int}
├── show
└── remove

In REPL mode, enter a context by typing its name. Use .. to go up:

> client
[client]> list
Alice (1)
Bob (2)
[client]> 1
[client/1]> show
Id: 1 Name: Alice
[client/1]> ..
[client]> ..
>

In CLI mode, the same routes work as a flat command:

Terminal window
myapp client 1 show
myapp client 1 remove

With ReplApp (vs. the lighter CoreReplApp), handlers can take DI-injected services:

var app = ReplApp.Create(services =>
{
services.AddSingleton<IContactStore, ContactStore>();
}).UseDefaultInteractive();
app.Context("client", client =>
{
client.Map("list", (IContactStore store) => store.All());
client.Context("{id:int}", scoped =>
{
scoped.Map("show", static (int id, IContactStore store) => store.Get(id));
});
});
return app.Run(args);

Parameters that match registered services are injected automatically. Parameters that match route segments are bound from the route.

A context can carry state captured at navigation time:

client.Context("{id:int}", (int id, IContactStore store) =>
{
var contact = store.Get(id) ?? throw new KeyNotFoundException($"Contact {id} not found.");
return scoped =>
{
scoped.Map("show", () => contact);
scoped.Map("update {name}", (string name) =>
{
contact = contact with { Name = name };
store.Save(contact);
});
};
});

The outer lambda resolves contact once when the scope is entered. All commands inside use that resolved value.

Register a completion provider to populate Tab suggestions in REPL mode:

client.Context("{id:int}", scoped => { /* ... */ })
.WithCompletion((IContactStore store) =>
store.All().Select(c => new CompletionItem(c.Id.ToString(), c.Name)));

  • Routes & Parameters — route constraint syntax, completion providers, and parameter binding.
  • Modules — packaging reusable scope patterns into IReplModule classes.
  • Dependency Injection — injecting services into scoped handlers.