# Internal Architecture of the internal/cli Package in JetBrains/go-modern-guidelines

> Explore the internal architecture of the internal/cli package in JetBrains/go-modern-guidelines. Understand how it parses arguments, dispatches commands, and delegates resolution.

- Repository: [JetBrains/go-modern-guidelines](https://github.com/jetbrains/go-modern-guidelines)
- Tags: internals
- Published: 2026-08-30

---

**The `internal/cli` package implements a thin command-line front-end that parses arguments, dispatches to sub-commands, and delegates guideline resolution to specialized internal packages.**

The `internal/cli` package serves as the primary user interface for the `go-modern-guidelines` tool developed by JetBrains. Understanding the internal architecture of the `internal/cli` package reveals a deliberately simple design that prioritizes testability and clear separation of concerns. The entire implementation resides in a single file, [`internal/cli/cli.go`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/cli/cli.go), supported by comprehensive unit tests in [`internal/cli/cli_test.go`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/cli/cli_test.go).

## Architectural Layers

The architecture is organized into distinct functional layers, each handling specific aspects of command-line interaction.

### Entry Point and Command Dispatch

The **entry point** is the exported `Run` function, defined as `func Run(args []string, stdout io.Writer) error` in [`internal/cli/cli.go`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/cli/cli.go). This function receives command-line arguments and an output writer, then dispatches to appropriate handlers based on the first argument. A simple switch statement routes to `runList`, `runExplain`, or utility functions like `printUsage` and `printVersion` depending on whether the user invoked the `list`, `explain`, or help/version commands.

### Sub-Command Implementations

Each sub-command manages its own `flag.FlagSet` to prevent option leakage between operations. The `runList` function creates a flag set accepting `--go-version` and `--file-path` parameters, then calls `goversion.Resolve` to determine the target Go version before formatting output via `guidelines.ListText`. The `runExplain` function uses the custom `stringListFlag` type to collect one or more guideline IDs, then retrieves detailed descriptions through `guidelines.ExplainText`.

### Flag Handling Infrastructure

Centralized helpers reduce boilerplate and ensure consistent behavior across commands. The `newFlagSet` function creates preconfigured flag sets with standardized naming, while `parseFlagSet` normalizes parsing errors and automatically surfaces help text when requested. These utilities capture help output and surface parsing errors without exposing implementation details to the sub-command logic.

### Utility Functions and Types

Supporting functions include `detectedVersion` for identifying the build's Go version, `printUsage` for displaying static help text, and `printVersion` for outputting the tool's version. The `stringListFlag` type implements `flag.Value` to enable repeated or CSV-style flag values, supporting complex query patterns required by the `explain` command.

## Execution Flow

The typical execution path begins when `Run` receives arguments and an output writer. If no arguments are provided, the system immediately invokes `printUsage`. When a valid command is detected, the dispatcher routes to the appropriate handler. Both `runList` and `runExplain` follow a consistent three-phase pattern: instantiate flags using `newFlagSet`, parse input via `parseFlagSet`, resolve dependencies through `internal/goversion` or `internal/guidelines`, and return formatted text to the provided writer.

## External Dependencies

The package maintains minimal coupling by relying on two internal dependencies. The `internal/goversion` package resolves version strings from command-line flags, filesystem paths, or defaults to the latest known version. The `internal/guidelines` package supplies domain logic through `ListText` and `ExplainText` functions, ensuring the CLI layer remains strictly concerned with presentation and input validation.

## Usage Example

Developers integrate the package by invoking the `Run` function with appropriate arguments. The following driver program demonstrates common operations against the `internal/cli` package API:

```go
package main

import (
	"os"

	"github.com/JetBrains/go-modern-guidelines/internal/cli"
)

func main() {
	// Example 1: Show usage (no arguments)
	_ = cli.Run([]string{}, os.Stdout)

	// Example 2: List guidelines for Go 1.22
	_ = cli.Run([]string{"list", "--go-version", "1.22"}, os.Stdout)

	// Example 3: Explain a specific guideline
	_ = cli.Run([]string{"explain", "--guideline-id", "G001"}, os.Stdout)

	// Example 4: Print the tool’s own version
	_ = cli.Run([]string{"--version"}, os.Stdout)
}

```

This pattern enables programmatic invocation of CLI functionality while maintaining clean separation between the command layer and business logic.

## Summary

- The `internal/cli` package provides a thin wrapper around core functionality, implementing only presentation logic in [`internal/cli/cli.go`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/cli/cli.go).
- The **entry point** `Run` acts as a central dispatcher, routing to `runList`, `runExplain`, or utility functions based on the first argument.
- Each sub-command maintains its own `flag.FlagSet` using helpers like `newFlagSet` and `parseFlagSet` for consistent error handling.
- The custom `stringListFlag` type enables flexible repeated flag parsing for complex guideline queries.
- Dependencies on `internal/goversion` and `internal/guidelines` ensure the CLI remains focused on I/O operations while delegating domain logic to specialized packages.

## Frequently Asked Questions

### What is the main entry point of the internal/cli package?

The `Run` function serves as the sole public entry point, defined as `func Run(args []string, stdout io.Writer) error` in [`internal/cli/cli.go`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/cli/cli.go). This design allows both production code and test suites to inject arguments and capture output through the writer interface, facilitating deterministic testing without subprocess overhead.

### How does the internal/cli package handle command-line flags?

Each sub-command constructs its own `flag.FlagSet` using the `newFlagSet` helper, ensuring isolation between commands. The `parseFlagSet` utility normalizes error handling by either printing help text for usage errors or returning clean error strings, while the custom `stringListFlag` type supports repeated or comma-separated values for complex inputs.

### Which internal packages does internal/cli depend on?

The package depends exclusively on `internal/goversion` for resolving target Go versions from flags or files, and `internal/guidelines` for retrieving guideline content. This dependency structure maintains a strict separation between the presentation layer and domain logic, keeping the CLI implementation lightweight and focused.

### Where is the internal/cli package located in the repository?

All implementation logic resides in [`internal/cli/cli.go`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/cli/cli.go), with corresponding tests in [`internal/cli/cli_test.go`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/cli/cli_test.go). These two files constitute the complete architecture, making the package easy to navigate and modify without cross-referencing multiple directories.