Browse documentation

Overview

Overview

Start here

Your first CLIRunnable examplesGuide index

How do I...?

Common recipesShell completionProject generator

Learn

Commands and subcommandsOptions and validationOutput, errors, and progress

Reference

Public APICurrent limitations

Inside cli-fp

Technical design

Project

Contributing and support

Commands and subcommands

Start here · How do I...? · Options · Limitations

In normal v1.4.x code, a command is an object you define by subclassing TBaseCommand. The object owns its registered options; the application owns the command tree and calls the selected object's Execute method.

TBaseCommand
  └── your command class (for example, TGreetCommand)
      ├── Execute
      └── options registered on its instance
your command instance
  └── registered with ICLIApplication

Choose the smallest shape that matches how people invoke the program.

ShapeInvocationUse it when
Root commandhello --name AdaThe program has one default action.
Named commandtool greet --name AdaThe program is a small toolbox.
Nested commandtool repo clone --url …Actions have useful groups, like Git.

Root command

An unnamed descendant is the default action. This complete command pattern defines TRootCommand; its empty name is supplied when the instance is made.

uses
  CLI.Command;

type
  TRootCommand = class(TBaseCommand)
  public
    function Execute: Integer; override;
  end;

function TRootCommand.Execute: Integer;
begin
  WriteLn('Running the default action');
  Result := 0;
end;

This program-setup fragment needs CLI.Interfaces and CLI.Application. It creates the Root object, registers its option on that object, and passes the object to the three-argument factory:

var
  App: ICLIApplication;
  Root: TRootCommand;
begin
  Root := TRootCommand.Create('', 'Run the default action');
  Root.AddFlag('-v', '--verbose', 'Show detailed output');
  App := CreateCLIApplication('hello', '1.0.0', Root);
  Halt(App.Execute);
end.

The root's Execute runs for hello [options]. Its options only belong to the root action; they are not global options inherited by named commands.

Named command

A named descendant is a separately selectable action. This complete command pattern defines the TGreetCommand class and its Execute method:

uses
  CLI.Command;

type
  TGreetCommand = class(TBaseCommand)
  public
    function Execute: Integer; override;
  end;

function TGreetCommand.Execute: Integer;
begin
  WriteLn('Hello from greet');
  Result := 0;
end;

This program-setup fragment creates the Greet instance, gives that instance its --name option, and registers it with App. It needs CLI.Interfaces and CLI.Application:

var
  App: ICLIApplication;
  Greet: TGreetCommand;
begin
  App := CreateCLIApplication('tool', '1.0.0');
  Greet := TGreetCommand.Create('greet', 'Print a greeting');
  Greet.AddStringParameter('-n', '--name', 'Name to greet', False, 'World');
  App.RegisterCommand(Greet);
  Halt(App.Execute);
end.

This accepts tool greet --name Ada. With no root command, tool by itself shows application help instead of running an action.

Subcommand

A nested CLI has a parent command object and a child command object. Both descend from TBaseCommand and must implement Execute; the parent can act as a group even when its method only returns success:

uses
  CLI.Command;

type
  TRepoCommand = class(TBaseCommand)
  public
    function Execute: Integer; override;
  end;

  TCloneCommand = class(TBaseCommand)
  public
    function Execute: Integer; override;
  end;

function TRepoCommand.Execute: Integer;
begin
  Result := 0;
end;

function TCloneCommand.Execute: Integer;
begin
  WriteLn('Cloning a repository');
  Result := 0;
end;

This program-setup fragment builds tool repo clone. It needs CLI.Interfaces and CLI.Application; Clone, not Repo, owns --url:

var
  App: ICLIApplication;
  Repo: TRepoCommand;
  Clone: TCloneCommand;
begin
  App := CreateCLIApplication('tool', '1.0.0');
  Repo := TRepoCommand.Create('repo', 'Repository operations');
  Clone := TCloneCommand.Create('clone', 'Clone a repository');
  Clone.AddUrlParameter('-u', '--url', 'Repository URL', True);
  Repo.AddSubCommand(Clone);
  App.RegisterCommand(Repo);
  Halt(App.Execute);
end.

This accepts tool repo clone --url https://example.com/project.git. A group does not receive options from its children. For a runnable nested example, see SubCommandDemo.

Help and exit codes

tool --help shows application help. tool greet --help shows the selected command. --version is an application-level request when used as the first argument. Return 0 from a command's Execute for success and a non-zero integer for an application failure; the setup fragments use Halt(App.Execute) to forward that result to the shell.

See How do I return a non-zero exit code? for a focused method pattern.