timewarpengineering/timewarp-nuru/tw-nuru

Claude Code and agent skill by timewarpengineering/timewarp-nuru

Trust tier 2
  • community
LLM review pending
Source check pending

Checking that the SKILL.md file is still in the repository.

Build CLI applications with TimeWarp.Nuru - a .NET route-based CLI framework with web-style routing, source generators, and AOT support. Use when creating CLI tools, defining commands/queries, adding parameters or options, testing CLI output, or working with TimeWarp.Nuru.

Install

npx vskill@latest install timewarpengineering/timewarp-nuru/tw-nuru

SKILL.md

TimeWarp.Nuru

Route-based CLI framework for .NET. Define commands like web routes. Source-generated for AOT compatibility.

Package: TimeWarp.Nuru Repository: https://github.com/TimeWarpEngineering/timewarp-nuru Depends on: TimeWarp.Terminal - console abstractions (IConsole, ITerminal), widgets (panels, tables, rules), ANSI colors, and TestTerminal for testable output. Included transitively via the Nuru package. For full Terminal API docs (colors, widgets, builders), fetch the README at https://raw.githubusercontent.com/TimeWarpEngineering/timewarp-terminal/refs/heads/master/README.md

Reference: Calculator CLI

For a complete working example of a multi-command Nuru CLI, see the calculator sample. Replicate this folder structure for new CLIs:

my-cli/
├── my-cli.cs # Entry point runfile
├── Directory.Build.props # Includes endpoints/**/*.cs into compilation
├── behaviors/
│ └── *.cs # Pipeline behaviors
├── endpoints/
│ └── *.cs # One endpoint class per file
└── services/
└── *.cs # Injected services

Key patterns:

  • Entry point runfile with #:project directive referencing the Nuru project
  • Directory.Build.props that glob-includes endpoints/**/*.cs and excludes the entry point
  • One endpoint class per file in endpoints/
  • File-scoped namespace in each endpoint file
  • DiscoverEndpoints() with .AddRepl(options => { options.AutoStartWhenEmpty = true; })

Choose a DSL

Nuru offers two DSLs. Pick one based on your needs:

  • Endpoint DSL (Recommended): Class-based with [NuruRoute] attribute. Best for testable, structured apps with dependency injection. Scales to large CLIs.
  • Fluent DSL: Inline lambdas with Map().WithHandler().AsCommand().Done(). Best for quick scripts and simple CLIs.

Endpoint DSL (Recommended)

Basics
csharp
NuruApp app = NuruApp.CreateBuilder()
.DiscoverEndpoints()
.AddRepl(options =>
{
options.AutoStartWhenEmpty = true;
})
.Build();
return await app.RunAsync(args);

DiscoverEndpoints() auto-discovers all classes with [NuruRoute]. When one compilation builds apps in several source files (a multi-file test assembly), analyzer NURU_A003 warns on unfiltered DiscoverEndpoints(): each app would get every file's endpoints. Use Map<TEndpoint>() there. Endpoints in a referenced contracts assembly are discovered only after an opt-in: [assembly: NuruEndpointAssembly(typeof(AnyTypeInIt))], or Map<T>() for one type. Do not link contract files into the CLI as sources. See documentation/user/features/neutral-contracts.md. The generator intercepts each RunAsync() call on an app it can trace to a Build() call (a local, a field, or the Build() chain itself), in any expression. NURU_I001 warns when it cannot, such as RunAsync() on a NuruApp parameter, because that call would throw at runtime. .AddRepl() enables interactive REPL mode. AutoStartWhenEmpty = true starts REPL when no args provided.

Commands, Queries, and Idempotent Commands

These contracts live in TimeWarp.Mediator (15.x), not TimeWarp.Nuru. Add using TimeWarp.Mediator;. The endpoint's message marker sets the kind; every nested Handler implements IRequestHandler<TEndpoint, T> and returns ValueTask<T>. ICommandHandler, IQueryHandler, and IIdempotentCommandHandler were removed in TimeWarp.Mediator 15.

  • ICommand<T>: Actions with side effects, NOT safe to retry (deploy, build, delete)
  • IQuery<T>: Read-only operations, safe to retry (status, greet, version)
  • IIdempotentCommand<T>: Mutating but safe to retry (set config, PUT/DELETE-style operations)
  • Use Unit as return type when there's no meaningful return value
Simple Endpoint

[NuruRoute] takes a single literal (e.g. "greet", "status") or empty string ("" for default route). The analyzer enforces this - multiple words or parameters in [NuruRoute] will produce a compile error.

csharp
[NuruRoute("greet", Description = "Greet someone")]
public sealed class GreetQuery : IQuery<Unit>
{
[Parameter(Description = "Name of the person to greet")]
public string Name { get; set; } = string.Empty;
public sealed class Handler : IRequestHandler<GreetQuery, Unit>
{
public ValueTask<Unit> Handle(GreetQuery query, CancellationToken ct)
{
Console.WriteLine($"Hello {query.Name}");
return Unit.ValueTask;
}
}
}
Route Examples

[NuruRouteExample("command", Description = "...")] (repeatable, Description optional) declares a usage example. It renders in an "Examples:" section after Options in <command> --help, and as an examples array in --capabilities JSON. The command text is verbatim - write it exactly as a user would type it after the executable name.

csharp
[NuruRoute("deploy", Description = "Deploy to an environment")]
[NuruRouteExample("deploy prod", Description = "Deploy to production")]
[NuruRouteExample("deploy staging --dry-run")]
public sealed class DeployCommand : ICommand<Unit> { /* ... */ }
Parameters

When a command has multiple `[Parameter]` attributes, each must specify an explicit Order value (enforced by analyzer NURU_A002). A single parameter does not need Order.

csharp
[NuruRoute("deploy", Description = "Deploy to an environment")]
public sealed class DeployCommand : ICommand<Unit>
{
[Parameter(Order = 0, Description = "Target environment")]
public string Env { get; set; } = string.Empty;
// Optional parameter: use nullable type (string?), NOT IsOptional=true
[Parameter(Order = 1, Description = "Optional tag to deploy")]
public string? Tag { get; set; }
public sealed class Handler : IRequestHandler<DeployCommand, Unit>
{
public ValueTask<Unit> Handle(DeployCommand command, CancellationToken ct)
{
Console.WriteLine($"Deploying to {command.Env}");
return Unit.ValueTask;
}
}
}
Options (flags and named values)
csharp
[NuruRoute("build", Description = "Build a project")]
public sealed class BuildCommand : ICommand<Unit>
{
[Parameter(Description = "Project to build")]
public string Project { get; set; } = string.Empty;
[Option("mode", "m", Description = "Build mode (Debug or Release)")]
public string Mode { get; set; } = "Debug";
[Option("verbose", "v", Description = "Verbose output")]
public bool Verbose { get; set; }
public sealed class Handler : IRequestHandler<BuildCommand, Unit>
{
public ValueTask<Unit> Handle(BuildCommand command, CancellationToken ct)
{
Console.WriteLine($"Building {command.Project} ({command.Mode}, verbose: {command.Verbose})");
return Unit.ValueTask;
}
}
}

Usage: myapp build MyApp --mode Release --verbose or myapp build MyApp -m Release -v

Catch-all Parameters
csharp
[NuruRoute("exec", Description = "Run with arbitrary arguments")]
public sealed class ExecCommand : ICommand<Unit>
{
[Parameter(IsCatchAll = true, Description = "Arguments")]
public string[] Args { get; set; } = [];
public sealed class Handler : IRequestHandler<ExecCommand, Unit>
{
public ValueTask<Unit> Handle(ExecCommand command, CancellationToken ct)
{
Console.WriteLine($"Args: {string.Join(" ", command.Args)}");
return Unit.ValueTask;
}
}
}
Route Groups (sub-command hierarchies)

The shell splits myapp docker build . so the app only receives ["docker", "build", "."] as args. Since [NuruRoute] accepts only a single literal, sub-command hierarchies use [NuruRouteGroup] on an abstract base class. The group prefix is prepended to the child's [NuruRoute] literal. Groups can nest to any depth.

csharp
// Base class defines the "docker" prefix
[NuruRouteGroup("docker")]
public abstract class DockerGroupBase;
// Matched by: myapp docker build . --tag foo
// App receives args: ["docker", "build", ".", "--tag", "foo"]
[NuruRoute("build", Description = "Build an image from a Dockerfile")]
public sealed class DockerBuildCommand : DockerGroupBase, ICommand<Unit>
{
[Parameter(Description = "Path to Dockerfile or build context")]
public string Path { get; set; } = string.Empty;
[Option("tag", "t", Description = "Name and optionally a tag")]
public string? Tag { get; set; }
[Option("no-cache", null, Description = "Do not use cache")]
public bool NoCache { get; set; }
public sealed class Handler : IRequestHandler<DockerBuildCommand, Unit>
{
public ValueTask<Unit> Handle(DockerBuildCommand command, CancellationToken ct)
{
Console.WriteLine($"Building image from: {command.Path}");
return Unit.ValueTask;
}
}
}

Nested groups concatenate prefixes through inheritance:

csharp
[NuruRouteGroup("cloud")]
public abstract class CloudGroupBase;
[NuruRouteGroup("azure")]
public abstract class AzureGroupBase : CloudGroupBase;
[NuruRouteGroup("storage")]
public abstract class AzureStorageGroupBase : AzureGroupBase;
// Matched by: myapp cloud azure storage upload myfile.txt
[NuruRoute("upload", Description = "Upload a file to Azure storage")]
public sealed class AzureStorageUploadCommand : AzureStorageGroupBase, ICommand<Unit>
{
[Parameter(Description = "File to upload")]
public string File { get; set; } = string.Empty;
public sealed class Handler : IRequestHandler<AzureStorageUploadCommand, Unit>
{
public ValueTask<Unit> Handle(AzureStorageUploadCommand command, CancellationToken ct)
{
Console.WriteLine($"Uploading: {command.File}");
return Unit.ValueTask;
}
}
}

Shared options across a group use [GroupOption] on the base class properties. All routes inheriting from the group automatically get these options. Do not include dashes in the attribute - the generator adds them.

csharp
[NuruRouteGroup("docker")]
public abstract class DockerGroupBase
{
[GroupOption("verbose", "v", Description = "Verbose output")]
public bool Verbose { get; set; }
}
// Both DockerBuildCommand and DockerRunCommand inherit --verbose/-v from DockerGroupBase
Subset Publishing (Group Filtering)

Create multiple specialized CLI editions from one codebase by filtering endpoints by group type. Parent prefixes above the matched group are stripped.

csharp
// Full edition - all commands
NuruApp.CreateBuilder()
.DiscoverEndpoints()
.Build();
// Kanban-only edition - "ganda" prefix stripped, only kanban commands
NuruApp.CreateBuilder()
.DiscoverEndpoints(typeof(KanbanGroup))
.Build();
// Multiple groups - OR logic
NuruApp.CreateBuilder()
.DiscoverEndpoints(typeof(KanbanGroup), typeof(GitGroup))
.Build();

Behavior:

  • Include endpoint if it inherits (directly or indirectly) from any specified group type
  • Strip all group prefixes above the matched type
  • Ungrouped endpoints excluded when filter is active
  • Type-based (typeof()) for refactoring safety

Project structure for multi-edition CLIs using runfiles:

my-cli/
├── Directory.Build.props # Includes ganda/endpoints/** for all editions
├── ganda/
│ ├── ganda.cs # Full edition entry point
│ └── endpoints/ # Shared command files (one per file)
│ ├── ganda-group.cs
│ ├── kanban-group.cs
│ ├── kanban-add-command.cs
│ └── git-commit-command.cs
├── kanban/
│ └── kanban.cs # .DiscoverEndpoints(typeof(KanbanGroup))
└── git/
└── git.cs # .DiscoverEndpoints(typeof(GitGroup))

See samples/editions/01-group-filtering/ for a complete working example.

Endpoint Key Rules

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewNot runA model review is scheduled for the next rescan.

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
114
Repository forks
4
7-day trend score
29

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/timewarpengineering/timewarp-nuru/tw-nuru/badge)](https://verified-skill.com/skills/timewarpengineering/timewarp-nuru/tw-nuru)

Something wrong with this skill or its page? Tell us.