# Where to Find the Main CLI Runner in the Aqua Project

> Discover the location of the main CLI runner in the Aqua project. Find the entry point at cmd/aqua/main.go and understand its initialization process.

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

---

**The main CLI runner in the Aqua project is located at [`cmd/aqua/main.go`](https://github.com/aquaproj/aqua/blob/main/cmd/aqua/main.go), which initializes the application using the urfave-cli v3 framework and delegates execution to the command tree defined in [`pkg/cli/run.go`](https://github.com/aquaproj/aqua/blob/main/pkg/cli/run.go).**

The Aqua project is a declarative CLI version manager written in Go. Understanding where the main CLI runner resides is essential for contributors looking to extend functionality or debug execution flow. This guide pinpoints the entry point and explains how control flows from the binary initialization to the individual command handlers.

## Locating the Main CLI Runner Entry Point

The absolute entry point for the Aqua binary is **[`cmd/aqua/main.go`](https://github.com/aquaproj/aqua/blob/main/cmd/aqua/main.go)**. This file contains the `main()` function that the Go runtime executes when the `aqua` command is invoked.

```go
// File: cmd/aqua/main.go
package main

import (
    "github.com/aquaproj/aqua/v2/pkg/cli"
    "github.com/suzuki-shunsuke/urfave-cli-v3-util/urfave"
)

func main() {
    // "aqua" is the command name, version is injected at build time,
    // and cli.Run holds the full command tree.
    urfave.Main("aqua", version, cli.Run)
}

```

This design keeps the main package minimal, containing only the necessary imports and a single function call to bootstrap the CLI framework.

## How the Main CLI Runner Delegates to the Command Tree

After the `main()` function executes, control passes to **`urfave.Main`**, a utility wrapper around the urfave-cli v3 library. This wrapper handles flag parsing, help generation, and command routing.

The third argument, **`cli.Run`**, is the critical bridge to Aqua's business logic. Defined in **[`pkg/cli/run.go`](https://github.com/aquaproj/aqua/blob/main/pkg/cli/run.go)**, this function constructs the root command and registers all sub-commands such as `install`, `update`, `exec`, and `which`.

When a user runs `aqua install`, the execution flow follows this path:

1. [`cmd/aqua/main.go`](https://github.com/aquaproj/aqua/blob/main/cmd/aqua/main.go) → `main()`
2. `urfave.Main()` parses global flags and identifies the sub-command
3. `cli.Run()` routes to the specific command handler in `pkg/cli/commands/`
4. The command implementation executes the tool installation logic

## Exploring the CLI Package Structure

The **`pkg/cli`** directory contains the modular architecture that supports the main CLI runner. Understanding these files helps trace how commands are registered and executed.

**Key Files in the CLI Package:**

- **[`pkg/cli/run.go`](https://github.com/aquaproj/aqua/blob/main/pkg/cli/run.go)** – Contains the `Run` function that instantiates the root command and attaches all sub-commands using the urfave-cli API.
- **`pkg/cli/commands/*.go`** – Individual command implementations. Each file typically exports a function that returns a `*cli.Command` struct defining flags, arguments, and the action function.
- **[`pkg/cli/flags.go`](https://github.com/aquaproj/aqua/blob/main/pkg/cli/flags.go)** – Defines shared flag variables and parsing logic used across multiple commands to ensure consistent behavior for options like `--config` or `--log-level`.

This separation of concerns allows the main runner to remain agnostic of specific command details while providing a clean extension point for new functionality.

## Running the Aqua CLI Locally

To verify the main CLI runner behavior or test modifications, you can build and execute the binary from source.

**Build the binary:**

```bash

# Clone the repository

git clone https://github.com/aquaproj/aqua.git
cd aqua

# Build the main CLI runner

go build -o aqua ./cmd/aqua

```

**Execute commands:**

```bash

# Show version information (injected at build time)

./aqua version

# Install tools defined in aqua.yaml

./aqua install

# Execute a managed tool

./aqua exec -- go version

# Locate the installation path of a specific tool

./aqua which golangci-lint

```

For programmatic integration, you can embed Aqua's CLI within another Go application by importing the `pkg/cli` package and invoking the `Run` function directly with custom arguments.

## Summary

- The **main CLI runner** entry point is located at **[`cmd/aqua/main.go`](https://github.com/aquaproj/aqua/blob/main/cmd/aqua/main.go)**, containing the `main()` function that bootstraps the application.
- Execution delegates immediately to **`urfave.Main`**, which wraps the urfave-cli v3 framework for flag parsing and command routing.
- The **`cli.Run`** function in **[`pkg/cli/run.go`](https://github.com/aquaproj/aqua/blob/main/pkg/cli/run.go)** constructs the command tree and registers all sub-commands like `install`, `exec`, and `which`.
- Individual command implementations reside in **`pkg/cli/commands/`**, with shared flags defined in **[`pkg/cli/flags.go`](https://github.com/aquaproj/aqua/blob/main/pkg/cli/flags.go)**.
- This architecture separates binary initialization from business logic, enabling easy testing and extension of the Aqua CLI.

## Frequently Asked Questions

### Where is the main function located in the Aqua project?

The `main` function is located in **[`cmd/aqua/main.go`](https://github.com/aquaproj/aqua/blob/main/cmd/aqua/main.go)**. This file serves as the entry point for the compiled binary, importing the CLI package and invoking the bootstrap utility to start the command-line interface.

### How does Aqua handle command routing from the main entry point?

Aqua uses the **`urfave.Main`** wrapper from the `urfave-cli-v3-util` package to handle command routing. This utility parses command-line arguments, matches them against the command tree defined in [`pkg/cli/run.go`](https://github.com/aquaproj/aqua/blob/main/pkg/cli/run.go), and dispatches execution to the appropriate handler in `pkg/cli/commands/`.

### Can I extend Aqua by adding new sub-commands to the main CLI runner?

Yes, you can extend Aqua by adding new sub-commands. Create a new command implementation in **`pkg/cli/commands/`** following the existing pattern, then register it in **[`pkg/cli/run.go`](https://github.com/aquaproj/aqua/blob/main/pkg/cli/run.go)** by adding it to the command slice passed to the root command constructor. The modular design keeps the main runner in [`cmd/aqua/main.go`](https://github.com/aquaproj/aqua/blob/main/cmd/aqua/main.go) unchanged.

### What is the difference between cmd/aqua/main.go and pkg/cli/run.go?

**[`cmd/aqua/main.go`](https://github.com/aquaproj/aqua/blob/main/cmd/aqua/main.go)** is the binary entry point that initializes the application and imports the CLI framework. It contains only the `main` function and minimal bootstrap logic. **[`pkg/cli/run.go`](https://github.com/aquaproj/aqua/blob/main/pkg/cli/run.go)** contains the actual command tree construction, sub-command registration, and the `Run` function that defines how the CLI behaves. This separation allows the core logic to be tested independently of the binary entry point.