Ajna_

A convention-driven REPL framework for .NET. Point it at a plain C# class and it becomes an interactive command line: methods are commands, parameters are flags, and your XML comments are the help text.

net10.0 v0.9.10 MIT licensed one package, generator included
$ dotnet add package Ajna
Program.cs — the whole program
using Ajna;

CLI.From<Toolbox>( ).Run( );

public class Toolbox
{
  /// <summary>Greets a person by name.</summary>
  /// <param name="name">The name to greet.</param>
  /// <returns>A greeting line.</returns>
  public string Greet ( string name ) => $"Hello, {name}!";

  /// <summary>Adds two integers together.</summary>
  /// <param name="left">First addend.</param>
  /// <param name="right">Second addend.</param>
  /// <returns>The sum.</returns>
  public int AddNumbers ( int left, int right ) => left + right;
}
▶ open the playground recorded session
Taiji@demo> greet --name world
Hello, world!
Taiji@demo> add numbers --left 20 --right 22
42
Taiji@demo> add numbers -h

Adds two integers together.

add numbers (
  [ Required ]

  --left : Int32

    First addend.

  [ Required ]

  --right : Int32

    Second addend.

) -> Int32

The sum.

Taiji@demo> 

Quick start

Write idiomatic C#, get a CLI

Ajna maps C# onto the command line as directly as it can, then gets out of the way. There is no command registry to populate, no builder chain to learn and no attributes to apply. A method is a command, a parameter is a flag, an XML summary is help text.

The program at the top of this page is complete: one using, one call to CLI.From<Toolbox>( ).Run( ), one plain class. The session beside it is what it produces — including the help output, which nobody wrote by hand.

Every invocation below reaches the same method:

add numbers 20 22                    // positional
add numbers --left 20 --right 22     // long flags, any order
add numbers -l 20 -r 22              // short flags
add numbers 20 --right 22            // mixed

You can run and edit this exact program in the playground — the library compiled to WebAssembly, bound to a DOM terminal, with Roslyn recompiling your edits in the browser. For binding to an instance or a static class, see Command sources.

Conventions

How names become syntax

Names are rewritten mechanically. Each uppercase letter starts a new word; commands join words with spaces, flags join them with hyphens. Underscores are ignored and can be used for visual aid.

Greetgreet
AddNumbersadd numbers
ListActiveUserslist active users
name--name  /  -n
maxRetries--max-retries  /  -m

Up to 25 parameters are supported — one per lowercase ASCII letter, with -h reserved for help.

API design

How it stays out of the way

The conventions above are the visible half. The other half is what Ajna does not ask of your code.

Your class stays a plain class

No base type, no interface, no attributes. A command source is any type; its public methods are the commands and can be called, tested and reused like any other methods. The framework appears in your code only where you opt in — [CLINoCommand] to hide a public method, a CancellationToken member to receive Ctrl+C.

Parameters use the types you have

Primitives, enums, arrays and nullables work as-is. Any type with a static Parse(string) method or a (string) constructor is a valid parameter type without registration; CLI.AddParseFor<T> is there when you would rather be explicit.

There is no output API

Return whatever is natural. Scalars and objects print through ToString; an IEnumerable<T> prints as it is enumerated, so a yield return loop is a live progress readout without a single call into the library.

Errors are exceptions

Throw CLICustomCommandException or CLICustomParseException and the message is shown under the command's name. Anything else propagates untouched — no result wrappers, no error codes.

Help writes itself

The /// comments you would write anyway become the -h text. Nothing is written twice, and the help cannot drift from the signature it documents.

I/O is a seam, not a dependency

System.Console is the default, not a requirement. With<> swaps in your own ITerminalSurface and IKeyInputSource — the playground on this site is the unchanged library running against a DOM.

Under the hood

What you never have to think about

None of this shows in the API; it is why the API can afford to be this small.

Command invocationReflection runs once, when the command table is built. Commands invoke through arity-specialized delegates — no MethodInfo.Invoke, no object[], no reflection on the hot path.
Command lookupA trie stored in a single flat array. Tab completion is the same walk, stopped early.
Line editingA gap buffer, so insertion at the caret is constant-time regardless of line length.
Key dispatchKey and modifiers pack into one integer that indexes a flat handler table.
Help textAn incremental source generator reads the XML comments at compile time and emits them as string literals into your assembly. No XML file is shipped or read.

Next

Read on

Command sources and discovery, the full flag model — booleans, optionals, arrays, enums — argument parsing and custom types, command results and streaming, cancellation, error handling, help generation and I/O redirection.