# How the Aqua CLI Command Structure Is Organized

> Understand the aqua CLI command structure. Discover how urfave/cli v3 and the central Runner assemble individual commands, learn about package organization, and command constructors within the aqua CLI.

- Repository: [aquaproj/aqua](https://github.com/aquaproj/aqua)
- Tags: internals
- Published: 2026-02-25

---

**The aqua CLI command structure relies on the urfave/cli v3 framework, with a central `Runner` in [`pkg/cli/runner.go`](https://github.com/aquaproj/aqua/blob/main/pkg/cli/runner.go) that assembles individual commands from dedicated packages under `pkg/cli/`, each exposing a `New` constructor returning a `*cli.Command` and receiving shared runtime parameters via `util.Param` and global arguments via `cliargs.GlobalArgs`.**

The aquaproj/aqua repository implements a highly modular aqua CLI command structure designed for scalability and testing. By delegating each subcommand to its own package and standardizing the constructor pattern, the codebase enables developers to add or modify CLI functionality without affecting unrelated components.

## Central Command Assembly in pkg/cli/runner.go

The entry point for all CLI operations resides in [`pkg/cli/runner.go`](https://github.com/aquaproj/aqua/blob/main/pkg/cli/runner.go), specifically within the `Run` function. This function acts as the composition root, instantiating shared dependencies before delegating to individual command packages.

First, `Run` creates a `util.Param` struct containing runtime essentials:

```go
// pkg/cli/runner.go
param := &util.Param{
    Stdin:   env.Stdin,
    Stdout:  env.Stdout,
    Stderr:  env.Stderr,
    Logger:  logger,
    Runtime: runtime.New(),
    Version: env.Version,
}

```

It then initializes `cliargs.GlobalArgs` to capture flags available across all commands. These dependencies are passed to a `commands()` helper that invokes each package's `New` constructor:

```go
return urfave.Command(env, &cli.Command{
    Name:  "aqua",
    Usage: "Version Manager of CLI. https://aquaproj.github.io/",
    Flags: cliargs.GlobalFlags(globalArgs),
    Commands: commands(
        param,
        globalArgs,
        initcmd.New,
        install.New,
        generate.New,
        updateaqua.New,
        upc.New,
        update.New,
        which.New,
        info.New,
        remove.New,
        vacuum.New,
        token.New,
        cp.New,
        cpolicy.New,
        cpolicy.NewInitPolicy,
        exec.New,
        list.New,
        genr.New,
        root.New,
    ),
}).Run(ctx, env.Args)

```

This centralized registration pattern ensures the aqua CLI command structure remains explicit and discoverable, with all top-level commands enumerated in a single location.

## Modular Command Packages Under pkg/cli/

Each subcommand lives in its own package beneath `pkg/cli/`, following a strict naming convention. The `commands()` function in [`runner.go`](https://github.com/aquaproj/aqua/blob/main/runner.go) accepts constructor functions that return `*cli.Command` instances. The current modular structure includes:

- **`init`** (`pkg/cli/initcmd`): Initializes a new [`aqua.yaml`](https://github.com/aquaproj/aqua/blob/main/aqua.yaml) configuration
- **`install`** (`pkg/cli/install`): Downloads tools and creates symlinks
- **`generate`** (`pkg/cli/generate`): Generates shell completion scripts
- **`updateaqua`** (`pkg/cli/updateaqua`): Self-updates the aqua binary
- **`upc`** (`pkg/cli/upc`): Updates aqua configuration file(s)
- **`update`** (`pkg/cli/update`): Updates package versions and registries
- **`which`** (`pkg/cli/which`): Displays the installed path of a command
- **`info`** (`pkg/cli/info`): Prints detailed package information
- **`remove`** (`pkg/cli/remove`): Removes installed tools
- **`vacuum`** (`pkg/cli/vacuum`): Cleans up unused files
- **`token`** (`pkg/cli/token`): Manages GitHub token storage
- **`cp`** (`pkg/cli/cp`): Copies files from a tool's install directory
- **`policy`** (`pkg/cli/cpolicy`): Manages security policies with constructors `New` and `NewInitPolicy`
- **`exec`** (`pkg/cli/exec`): Executes commands within the tool-specific environment
- **`list`** (`pkg/cli/list`): Lists installed tools
- **`genr`** (`pkg/cli/genr`): Generates a JSON schema for [`aqua.yaml`](https://github.com/aquaproj/aqua/blob/main/aqua.yaml)
- **`root-dir`** (`pkg/cli/root`): Prints the Aqua root directory (`AQUA_ROOT_DIR`)

This package-per-command approach isolates business logic and allows independent testing of each CLI surface.

### Standard Command Implementation Pattern

Every command package follows an identical four-step implementation pattern to maintain consistency across the aqua CLI command structure:

1. **Define an `Args` struct** embedding `*cliargs.GlobalArgs` to inherit global flags
2. **Define a `command` struct** holding `*util.Param` for runtime access
3. **Implement `New(r *util.Param, globalArgs *cliargs.GlobalArgs) *cli.Command`** registering flags, usage text, and the action function
4. **Implement an `action` method** handling profiling setup, parameter conversion via `util.SetParam`, and controller invocation

The `install` command exemplifies this pattern in [`pkg/cli/install/command.go`](https://github.com/aquaproj/aqua/blob/main/pkg/cli/install/command.go):

```go
func New(r *util.Param, globalArgs *cliargs.GlobalArgs) *cli.Command {
    args := &Args{GlobalArgs: globalArgs}
    i := &command{r: r}
    return &cli.Command{
        Name:    "install",
        Aliases: []string{"i"},
        Usage:   "Install tools",
        Action: func(ctx context.Context, _ *cli.Command) error {
            return i.action(ctx, args)
        },
        Flags: []cli.Flag{ /* bool/string flags */ },
    }
}

```

The `action` method typically initializes a controller from the `pkg/controller` layer and executes core logic, keeping the CLI adapter thin and focused on argument parsing and I/O handling.

## Shared Infrastructure Across Commands

Two critical abstractions enable dependency injection throughout the aqua CLI command structure:

- **`util.Param`** (defined in [`pkg/cli/util/param.go`](https://github.com/aquaproj/aqua/blob/main/pkg/cli/util/param.go)): A shared runtime context containing `Stdin`, `Stdout`, `Stderr`, `Logger`, `Runtime`, and `Version`. Every command receives this via its `New` constructor, ensuring consistent I/O and logging without global state.
- **`cliargs.GlobalArgs`** (defined in [`pkg/cli/cliargs/global.go`](https://github.com/aquaproj/aqua/blob/main/pkg/cli/cliargs/global.go)): A struct capturing flags applicable to all commands, such as configuration file paths or log levels. The `Args` struct in each command embeds this to automatically inherit global flag parsing.

This design eliminates tight coupling between commands and the environment, facilitating unit testing through mock `util.Param` injection.

## Key Files Defining the Command Structure

| Path | Responsibility |
|------|----------------|
| [`pkg/cli/runner.go`](https://github.com/aquaproj/aqua/blob/main/pkg/cli/runner.go) | Central assembly point and entry point for the CLI; contains the `Run` function and command registration |
| `pkg/cli/<command>/command.go` | Individual command implementations (e.g., [`pkg/cli/install/command.go`](https://github.com/aquaproj/aqua/blob/main/pkg/cli/install/command.go)) |
| [`pkg/cli/cliargs/global.go`](https://github.com/aquaproj/aqua/blob/main/pkg/cli/cliargs/global.go) | Definition of global flags and `GlobalArgs` struct |
| [`pkg/cli/util/param.go`](https://github.com/aquaproj/aqua/blob/main/pkg/cli/util/param.go) | Shared runtime parameters passed to every command constructor |
| `pkg/controller/*` | Business logic layer invoked by command `action` methods |
| [`cmd/aqua/main.go`](https://github.com/aquaproj/aqua/blob/main/cmd/aqua/main.go) | Minimal wrapper that calls `cli.Run` with the environment |

## Summary

- The aqua CLI command structure uses **urfave/cli v3** as its foundational framework.
- **Centralized registration** occurs in [`pkg/cli/runner.go`](https://github.com/aquaproj/aqua/blob/main/pkg/cli/runner.go), where the `Run` function assembles all commands via a `commands()` helper.
- **Modular packages** under `pkg/cli/` isolate each command, with directories like `install/`, `update/`, and `info/` each exporting a `New` constructor.
- Standardized patterns include an `Args` struct embedding `GlobalArgs`, a `command` struct holding `util.Param`, and an `action` method delegating to controllers.
- **Shared infrastructure** (`util.Param` and `cliargs.GlobalArgs`) provides consistent runtime context and global flag handling across all subcommands.

## Frequently Asked Questions

### What framework does the aqua CLI use for its command structure?

The aqua CLI is built on **urfave/cli v3**, a popular Go framework for building command-line interfaces. This framework provides the `cli.Command` struct and flag-parsing primitives that the aqua CLI command structure extends through its modular package organization.

### How can I add a new command to the aqua CLI?

To add a new command, create a package under `pkg/cli/<name>/` containing a [`command.go`](https://github.com/aquaproj/aqua/blob/main/command.go) file. Implement the standard pattern: define an `Args` struct embedding `*cliargs.GlobalArgs`, a `command` struct with `*util.Param`, and a `New` function returning `*cli.Command`. Finally, add your package's `New` function to the `commands()` slice in [`pkg/cli/runner.go`](https://github.com/aquaproj/aqua/blob/main/pkg/cli/runner.go) and rebuild the binary.

### What is the purpose of `util.Param` in the command structure?

`util.Param` (located in [`pkg/cli/util/param.go`](https://github.com/aquaproj/aqua/blob/main/pkg/cli/util/param.go)) serves as a dependency injection container for runtime resources including standard I/O streams, the structured logger, runtime information, and version metadata. Every command receives this parameter through its constructor, enabling isolated testing and consistent resource access without global variables.

### How does the aqua CLI handle global flags across all subcommands?

Global flags are defined in `cliargs.GlobalArgs` (within [`pkg/cli/cliargs/global.go`](https://github.com/aquaproj/aqua/blob/main/pkg/cli/cliargs/global.go)) and exposed via `cliargs.GlobalFlags()`. Each command's local `Args` struct embeds `*cliargs.GlobalArgs`, automatically inheriting these flags. The `New` constructor in each package binds these arguments to the urfave command definition, ensuring uniform flag availability throughout the aqua CLI command structure.