# cligen: a command line interface inferred from Nim proc signatures

> cligen is a Nim library that turns a proc signature into a command line interface, using reflection to generate a parser and dispatcher that converts string arguments into calls on the wrapped proc. Adding a CLI to a proc is one line, and the resulting help output comes from the signature and the doc comment.

**c-blake/cligen** — Nim library to infer/generate command-line-interfaces / option / argument parsing; Docs at

- Repository: https://github.com/c-blake/cligen
- Website: https://c-blake.github.io/cligen/
- Stars: 584 · Forks: 28
- Language: Nim
- License: ISC
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/c-blake-cligen

## The signature is the specification

cligen is described as a native API inferred command line interface generator for Nim, and the key observation in the README is that proc signatures already encode what you need to generate a command line interface, namely names, types and default values, so reflection suffices to generate parser dispatchers that translate a sequence of strings into calls to a wrapped proc. The approach was inspired by Andrey Mikhaylenko's Python argh, and the README notes that Click became more popular. The payoff in Nim is stated bluntly, adding a CLI can be one line of code, and there is no specification language, no complex argument parsing API and no Nim pragma tags to learn. The library is written in Nim, the licence is ISC, and the documentation is at c-blake.github.io/cligen.

## One line of code, and the help you get

The example in the README is a proc with a mix of defaults and a trailing sequence of strings, followed by an import of cligen and a dispatch call. Compiled with the Nim compiler and run with the help flag, it produces a usage line, the doc comment as the description, and a table of options with types and defaults, where the short forms are derived from the parameter names:

```nim
proc fun(foo=1,bar=2.0,baz="x",verb=false,args: seq[string]): int=
  ## An API call doc comment
  result = 1      # Of course, real code would have real work here
import cligen; dispatch fun # Whoa..Just 1 line??
```

The help column is filled with generic placeholders by default, and the README says you can override that using parameter keyed association list literals, so you write `dispatch fun, help={"foo": "the beginning", "bar": "the rate"}` and the text in the help column changes. The exact spelling of the key in the help dictionary controls the look of the printed help, and the same mechanism handles short versions of the arguments. Installation is as simple as `nimble install cligen`, or you can clone the repository and compile against it directly with the compiler's path option.

## Prefix matching and kebab-case insensitivity

The parser is more permissive than a conventional one, and this is the feature that distinguishes the library. Generated parsers accept any unambiguous prefix for long options, so a long option can be as short as possible, and hitting the tab key to complete is unnecessary if the completion is unique. The README says this is patterned after tools such as gnuplot, gdb and Vim ex-commands. Long options can also be spelled flexibly, so `--dry-run` and `--dryRun` both work, like Nim's own style insensitive identifiers but with extra kebab-case insensitivity. The trade-off is worth naming, because it is the classic trade-off in argument parsing. Unambiguous prefixes are convenient to type, and they are also ambiguous the moment two options share a prefix, at which point the behaviour is no longer obvious to a user reading a script.

## Trailing arguments and required parameters

Most commands have a trailing variable length sequence of arguments, and cligen automatically treats the first non-defaulted sequence parameter as such an optional sequence, applying the same string to native type converters used for option values. The flip side is that a parameter with no explicit default value becomes required, with the same input syntax as optional values. The README's second example shows a proc with a required float, a sequence of ints and a defaulted int, and notes that the command line user must give the required parameter somewhere, while non-option arguments must be parsable as an integer with whitespace stripped, with a double dash used to stop option parsing. More general semantics for argument validation, including required-ness, length and sub-parsing, are described as being doable the way a user error module does it, so validation beyond the built in rules is a pattern to copy rather than a feature to switch on.

## Custom types through argument IO overloads

The parsing and printing system is extensible, through in-dispatch scope overloads of the argument parse and argument help procs for a type. The example in the README defines a proc taking an integer and a timestamp, and then supplies a custom parse function that tries to read a date in a fixed format and a custom help function that renders the parameter name, the type name and the default. Both take a shared coordination parameter carrying argument metadata, and the README notes that the parameter name field lets you parse the same Nim type differently depending on which parameter it is, detect when a user changes a parameter from its default, or count uses. The same mechanism can override the built-in parsers entirely, and consulting the argcvt module for examples is recommended, with a test file in the repository showing how to change the behaviour for integers.

## Subcommands and the dispatch to object initialisers

The dispatchMulti variant lets you expose two or more procs as subcommands, the way git or nimble do, and the README gives a worked example with two procs, each with its own doc comment, dispatched with a help override on the first. The result is a binary where the user runs a subcommand followed by that subcommand's options, and the help is tuned for each subcommand in the same way as for a single dispatch. The repository layout shows the scale of the project for a library of this kind, with a `cligen/` directory holding the modules, a `cligen.nim` entry file, a `cligen.nimble` and a `cligen.nim.cfg` configuration, a `docs/` directory, an `examples/` directory with a couple of dozen small programs, a `test/` directory with a test script, a `configs/` directory for the user config files, a `util/` directory, and separate `MOTIVATION.md`, `RELEASE-NOTES.md` and `TODO.md` files at the root alongside a `GNUmakefile`.

## Where this is the wrong choice

The limits follow from the premise. First, the API is the specification, so a program whose command line surface is defined by configuration, by a data file, or by a schema rather than by proc signatures cannot use this approach, and a tool with a stable external CLI contract that must not move when the internal API moves is the classic counterexample. Second, the permissive parsing has costs. Unambiguous prefix matching means that adding an option later can turn a working command line into an ambiguous one, and kebab-case insensitivity means two options that differ only in case style cannot coexist, which is a constraint on the design of the interface, not just on the parser. Third, the help text quality is inherited from the doc comments, so a proc without a doc comment produces a bare help entry, and a project with a house style for help strings has to enforce that by convention. Fourth, the layout details such as column spacing and help colourisation are run-time adjustable through a user config file, which is a nice feature and also means the rendered help can differ between machines, which matters if you are scripting against the help text. Finally, this is a Nim library for a niche language, so the pool of people who can maintain a fork is small.

## Conclusion

cligen fits a Nim developer whose program is a set of procs and who wants a usable command line without hand writing a parser or learning a specification language, since the interface is derived from the signature and the doc comment, and the help output is generated rather than maintained. It is the wrong choice if you want a configuration-driven or declarative CLI, because the whole point is that the API is the specification, and it is the wrong choice if your program is not a collection of Nim procs. Before you adopt it, read the MOTIVATION.md document the README links, and look at the examples directory, which contains a couple of dozen small programs each exercising one feature. The last push was on 2026-08-29, the newest release is 1.11.0 from 2026-07-21, and the licence is ISC.

## FAQ

### How do I add a command line interface to a Nim proc with cligen?

Import cligen and call dispatch on the proc. The signature and the doc comment are enough, so a proc with defaults, a bool flag and a trailing sequence becomes a working CLI with a generated help table, and the help strings can be overridden per parameter with a help dictionary passed to dispatch.

### Does cligen support subcommands?

Yes. The dispatchMulti variant exposes two or more procs as subcommands, in the style of git or nimble, and each subcommand is dispatched with its own argument list and its own help settings, tuned the same way as a single dispatch.

### Can I parse a custom type in cligen?

Yes, through the argcvt mechanism, by supplying in-dispatch scope overloads of the argument parse and argument help procs for your type. The README's example parses a timestamp with a fixed format, and notes that the same mechanism can override the built-in parsers for the standard types as well.

## Sources

- [c-blake/cligen on GitHub](https://github.com/c-blake/cligen)
- [License: ISC](https://github.com/c-blake/cligen/blob/master/LICENSE)
- [Project website](https://c-blake.github.io/cligen/)
- [README](https://github.com/c-blake/cligen/blob/master/README.md)
- [Releases](https://github.com/c-blake/cligen/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/c-blake-cligen
