CLI tool
dotnet/command-line-api avatar
dotnet/command-line-api

System.CommandLine: a parser, invocation layer and completion source for .NET console apps

Command line parsing, invocation, and rendering of terminal output.

3,675 stars431 forksC#MIT

At a glance

What is it?
System.CommandLine is the .NET library behind command line parsing, model binding, invocation and shell completions, plus the dotnet-suggest global tool. It is for C# developers who want a single package to define a CLI, and it is a poor fit for anyone who needs a stable API across major versions or support for a runtime other than .NET.
Who is it for?
Adopt System.CommandLine if you are writing a .NET console application and want parsing, binding, invocation and completion in one package rather than assembling them yourself. Do not adopt it if you ship on a non-.NET runtime, or if you cannot absorb a breaking rewrite between major versions, since v2.0.0 followed v2.0.1 and v2.0.2 within roughly two months of each other and the API surface moved.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 6 days ago.
What is it written in?
Mainly C#, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 27, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What System.CommandLine actually replaces in a .NET console app

A .NET console application gets args as a string array. Everything after that point is manual work: splitting tokens, deciding which token is a subcommand and which is an option value, converting strings to typed values, detecting missing required arguments, and printing usage text when the input is wrong. System.CommandLine exists to take that work off the application author. The README describes the System.CommandLine package as "Command line parser, model binding, invocation, shell completions", which is a fair summary of four distinct jobs bundled into one library.

The audience is narrow and specific. This is a library for people building .NET command line tools, not for people consuming them. If you are writing a CLI in C#, defining commands, options and arguments as objects and letting the library route input to handlers is the intended workflow. If you are writing in Go, Rust or Python, nothing here applies. The project's primary language is C# and the packages ship through NuGet, so the audience is the .NET ecosystem and stops there.

The second deliverable is easy to overlook. dotnet-suggest is a separate global tool, described in the README as "A command-line tool to provide shell completions for apps built using System.CommandLine". That means the library does not just parse input; it can also tell a shell what the valid next token is. Completion support is often bolted on late or skipped entirely in hand-rolled parsers, and here it is part of the package surface.

How parsing, binding and invocation fit together

The architecture visible in the repository is a library plus a tool. The library defines a command model, parses incoming tokens against it, binds the results to typed values, and invokes a handler. The tool, dotnet-suggest, sits outside the application and answers completion queries for applications that expose the command model.

The repository layout supports this split. There is a src/ directory holding the implementation, a docs/ directory, and a solution file, System.CommandLine.slnx. Build entry points are build.cmd on Windows and build.sh elsewhere, with eng/ holding the shared build infrastructure and Directory.Build.props and Directory.Packages.props centralising compiler and package settings. A global.json pins the SDK, which matters because a pinned SDK version is the difference between a build that reproduces on another machine and one that does not.

What the README does not do is explain the command model itself. It points readers to Microsoft Learn for the System.CommandLine documentation, and that is where the actual API guidance lives. The README is a package table, a daily-build feed URL, a documentation link, and project governance. Anyone trying to learn the API from the repository root will not get far, and that is a deliberate choice rather than an oversight: the documentation moved to a separate site.

Daily builds are available by adding a specific feed to nuget.config, at https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-libraries/nuget/v3/index.json, with versions listed on the corresponding Azure DevOps artifacts page. That feed is for people who need a fix before the next release, and it carries the usual risk of tracking an unreleased build.

Installing System.CommandLine and running a first command

The library is distributed as a NuGet package named System.CommandLine. The README presents a package table listing System.CommandLine and dotnet-suggest, and it does not include install commands, so the exact invocation to add the package has to come from the Microsoft Learn documentation the README links to. The current release line shown in the repository is v2.0.2, published on 2026-01-13, following v2.0.0 on 2025-11-11 and v2.0.1 on 2025-12-09.

The package table is the whole of the README's installation guidance:

text
Package                          | Description
---------------------------------| ----------------------------------------------------------------------------
System.CommandLine               | Command line parser, model binding, invocation, shell completions
dotnet-suggest                   | A command-line tool to provide shell completions for apps built using System.CommandLine.

After the package restores, the project references the parser, the binding layer and the invocation layer in one dependency. The README does not include a code sample, so the exact API to define a command and attach a handler has to come from the Microsoft Learn documentation. That is a real friction point: the repository tells you the package exists and where the docs are, and nothing more.

If you need a build that is not yet released, the README gives the feed to add to nuget.config:

text
https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-libraries/nuget/v3/index.json

Adding that source makes daily builds resolvable. Versions are listed at the Azure DevOps artifacts page for the dotnet-libraries feed. It also means your restore now depends on an external feed, which is a supply chain decision, not just a convenience.

The v2 breaking-change problem is the main adoption risk

The release history is short and dense. v2.0.0 shipped on 2025-11-11, v2.0.1 on 2025-12-09, and v2.0.2 on 2026-01-13. A major version bump in that window means the API surface changed in a way that is not drop-in compatible with what came before. For a library that sits at the entry point of an application, that cost lands on every handler signature, every option definition and every place the parse result is read.

This is the limitation to weigh honestly. The library is not a thin wrapper over args; it owns the shape of your command definitions. When that shape changes, the migration is mechanical but it is not free, and it touches the part of the codebase most likely to be under-tested. Teams that pinned a pre-2.0 version and wrote handlers against it will need to rework them.

There is a second, quieter limitation. The README documents the packages, the daily feed, the license and the code of conduct, and it delegates all usage guidance to Microsoft Learn. That means the repository itself is not a self-contained reference. If the external documentation and the installed package version drift apart, the repository offers no fallback. For a library whose whole value is a specific API, that is a meaningful gap. It is also a maintenance signal in the other direction: the last push to the repository was on 2026-09-23, so the code is current even though the README is thin.

System.CommandLine versus writing your own argument handling

The realistic alternative is not another parser library; it is not using one. A small tool with two or three flags can read args directly, and that code will be shorter than the command model needed to describe the same interface. The difference in approach is where the complexity sits. Hand-rolled parsing puts token handling in your application, where you control it completely and where every edge case is your problem. System.CommandLine moves that into a dependency, where edge cases are handled once and where completions come along with parsing.

The crossover point is roughly when you add subcommands. Once a tool has nested commands, options that only apply to some of them, and required arguments, hand-rolled parsing turns into a state machine that nobody wants to maintain. That is the point at which a command model earns its dependency. Below that point, the library is more machinery than the problem requires.

A second alternative worth naming is the daily-build feed. It is not a different tool, but it is a different approach to the same dependency: instead of waiting for a release, you consume an unreleased build. That buys earlier fixes at the cost of tracking a moving target. The README presents it as an option, not a recommendation, and it should be treated that way.

License, build cost and what upgrading involves

The project is licensed under the MIT license, per the README and the LICENSE.md file at the repository root. MIT is permissive: it allows use in closed-source software and places few conditions on redistribution. This is a description of the license text, not legal advice, and anyone with specific obligations should read LICENSE.md and consult their own counsel.

Build cost is low for consumers. The library is a NuGet dependency, and the pinned SDK in global.json, the central package versions in Directory.Packages.props and the build scripts in the repository are concerns for people building the project itself, not for people referencing the package. If you only consume the package, you never touch build.cmd or build.sh.

Upgrade cost is the part to budget for. Because v2.0.0 was a major release, moving from an earlier line means reviewing command definitions and handler signatures against the Microsoft Learn documentation for the version you are moving to. The repository does not document a rollback path, and the README says nothing about supported version ranges or a deprecation policy. Before pinning a version, check the release notes for that version and confirm the documentation you are reading matches it. The gap between the three v2 releases is about two months end to end, so a pinned version can fall behind quickly.

Editorial conclusion

Adopt System.CommandLine if you are writing a .NET console application and want parsing, binding, invocation and completion in one package rather than assembling them yourself. Do not adopt it if you ship on a non-.NET runtime, or if you cannot absorb a breaking rewrite between major versions, since v2.0.0 followed v2.0.1 and v2.0.2 within roughly two months of each other and the API surface moved. Before committing, read the Microsoft Learn command line documentation against the exact version you pin, and check the release notes for the version you intend to use rather than assuming v1 guidance still applies.

Frequently asked questions

What is System.CommandLine used for?

It is the .NET library for command line parsing, model binding, invocation and shell completions, according to the README's package table. It is aimed at developers building .NET console applications, not at people consuming them.

How do I install System.CommandLine in a C# project?

The README lists System.CommandLine in a package table but gives no install command, so the package has to be added through NuGet and the API guidance comes from the Microsoft Learn documentation the README links to.

What is dotnet-suggest?

dotnet-suggest is a command-line tool described in the README as providing shell completions for apps built using System.CommandLine. It is listed as a separate package from the library itself.

What license does System.CommandLine use?

The project is licensed under the MIT license, as stated in the README and in the LICENSE.md file at the repository root. That is a description of the license, not legal advice.

Official sources

  1. dotnet/command-line-api on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/dotnet-command-line-api.svg)](https://hysenlabs.com/projects/dotnet-command-line-api)