# How to Parse Arguments for Commands in gastownhall/gastown

> Learn how to parse command-line arguments in gastownhall/gastown using Cobra. Discover structured definitions, flag registration, and positional argument validation.

- Repository: [Gas Town Hall/gastown](https://github.com/gastownhall/gastown)
- Tags: how-to-guide
- Published: 2026-07-07

---

**Gastown uses the Cobra library to parse command-line arguments through structured command definitions, flag registration, and positional argument validators in `internal/cmd/*.go` files.**

The gastownhall/gastown repository implements a hierarchical CLI (`gt`) built on the Cobra framework, which provides a robust pattern for parsing arguments for commands. Understanding how this architecture handles flags, positional arguments, and external command forwarding is essential for extending the tool or debugging command execution.

## Cobra-Based Command Architecture

Gastown’s CLI relies on the standard Cobra pattern where each command is defined as a `*cobra.Command` struct. This structure determines how to parse arguments for commands by declaring four key elements: `Use` for syntax documentation, `Args` for positional argument validation, `Flags()` for named options, and `Run` for execution logic.

### Command Structure and File Locations

In [`internal/cmd/witness.go`](https://github.com/gastownhall/gastown/blob/main/internal/cmd/witness.go), the `witness start` command demonstrates the standard declaration pattern:

```go
var witnessStartCmd = &cobra.Command{
    Use:   "witness start",
    Short: "Start the witness agent",
    Args:  cobra.NoArgs, // Validates zero positional arguments
    Run: func(cmd *cobra.Command, args []string) error {
        return runWitnessStart(cmd, args)
    },
}

```

The entry point in [`cmd/gt/main.go`](https://github.com/gastownhall/gastown/blob/main/cmd/gt/main.go) initializes the command tree and executes the parser, while individual command definitions reside in files like [`internal/cmd/witness.go`](https://github.com/gastownhall/gastown/blob/main/internal/cmd/witness.go), [`internal/cmd/version.go`](https://github.com/gastownhall/gastown/blob/main/internal/cmd/version.go), and others within the `internal/cmd/` directory.

## Registering and Retrieving Flags

Flags are registered before command execution and automatically populated by Cobra during the parsing phase. This separates definition from consumption and ensures type safety.

### Boolean and String Flags

In [`internal/cmd/witness.go`](https://github.com/gastownhall/gastown/blob/main/internal/cmd/witness.go), flags attach directly to Go variables through pointer binding:

```go
var witnessForeground bool
var witnessAgentOverride string

func init() {
    witnessStartCmd.Flags().BoolVar(&witnessForeground, "foreground", false,
        "Run in foreground (default: background)")
    witnessStartCmd.Flags().StringVar(&witnessAgentOverride, "agent", "", 
        "Agent alias to run the Witness with (overrides town default)")
}

```

When `runWitnessStart` executes, these variables are already populated with parsed values from the command line.

### String Array Flags for Repeated Values

For flags that accept multiple values, such as environment variable overrides, Gastown uses `StringArrayVar`:

```go
var witnessEnvOverrides []string

witnessStartCmd.Flags().StringArrayVar(&witnessEnvOverrides, "env", nil,
    "Environment variable override (KEY=VALUE, can be repeated)")

```

Users can invoke this with multiple `--env` flags, and Cobra aggregates them into the slice before the handler runs.

## Handling Positional Arguments

Positional arguments are collected after flags and validated according to the `Args` field configuration.

### Argument Validation

Cobra provides built-in validators that enforce argument count during the parsing phase:

- **cobra.ExactArgs(n)** – Requires exactly n arguments
- **cobra.NoArgs** – Requires zero arguments  
- **cobra.MinimumNArgs(n)** – Requires at least n arguments

The validation occurs automatically before the `Run` function executes, ensuring handlers receive the expected argument slice length.

## Forwarding Arguments to External Tools

Gastown frequently forwards flags to downstream tools like `bd` or `git`, requiring careful argument construction to prevent flag misinterpretation.

### The Flags-First Convention

According to the source code in [`internal/web/setup.go`](https://github.com/gastownhall/gastown/blob/main/internal/web/setup.go) (lines 147-150), Gastown follows a **"flags-first, then `--`, then positional path"** pattern. This mirrors the POSIX `--` separator that stops flag parsing.

The comment explicitly states: *"Build gt install command. Flags go first, then `--` to end flag parsing, then the positional path (prevents paths like `--help` being parsed as flags)."*

### Building External Commands

When constructing commands for external execution, Gastown assembles slices in the required order:

```go
func buildInstallCmd(flags []string, path string) []string {
    // flags (e.g. --json) must precede the `--` separator
    // after `--` everything is treated as a literal argument.
    return append(append([]string{"gt", "install"}, flags...), "--", path)
}

```

This pattern ensures that paths starting with hyphens (like `--help`) are treated as literal arguments rather than flags. The assembled slice is then passed to `exec.Command` via utilities in [`internal/util/exec.go`](https://github.com/gastownhall/gastown/blob/main/internal/util/exec.go).

## Wrapper Binary Handling

For agents launched through wrappers like `env`, `sudo`, or `nohup`, Gastown must extract the real binary name from the wrapper's argument list.

### Parsing Wrapped Arguments

The function `extractWrappedBinary` in [`internal/config/agents.go`](https://github.com/gastownhall/gastown/blob/main/internal/config/agents.go) implements custom flag parsing logic that mirrors Cobra's behavior:

```go
func extractWrappedBinary(wrapper string, args []string) string {
    flagsTakeValue := wrapperFlagsTakeValue(wrapper)
    for i := 0; i < len(args); i++ {
        a := args[i]
        if a == "--" {               // end-of-options marker
            if i+1 < len(args) {
                return filepath.Base(args[i+1])
            }
            return ""
        }
        if strings.HasPrefix(a, "-") && a != "-" {
            if flagsTakeValue[a] && i+1 < len(args) {
                i++ // skip the flag's argument
            }
            continue
        }
        return filepath.Base(a) // first non-flag token = real binary
    }
    return ""
}

```

This logic handles short flags that take values (skipping the next token), respects the `--` separator, and identifies the first non-flag argument as the actual binary name.

## Summary

- **Cobra command structs** in `internal/cmd/*.go` declare command syntax, validators, and flag bindings.
- **Flag registration** via `Flags().*Var` methods automatically populates Go variables during parsing.
- **Argument validation** uses `cobra.ExactArgs`, `cobra.NoArgs`, and similar validators to enforce positional argument requirements.
- **External command builders** in [`internal/web/setup.go`](https://github.com/gastownhall/gastown/blob/main/internal/web/setup.go) use the "flags `--` path" pattern to safely forward arguments to sub-tools.
- **Wrapper handling** in [`internal/config/agents.go`](https://github.com/gastownhall/gastown/blob/main/internal/config/agents.go) parses complex argument lists to identify real binary names behind wrapper commands.

## Frequently Asked Questions

### How does Gastown handle flags that can be repeated multiple times?

Gastown uses `StringArrayVar` for flags that accept multiple values, such as the `--env` flag in `witness start`. This binds repeated flag instances to a `[]string` slice, allowing users to specify `--env KEY1=VAL1 --env KEY2=VAL2`. The Cobra library automatically aggregates these into the bound variable before the command handler executes.

### What prevents file paths from being parsed as flags when forwarding commands?

Gastown inserts a `--` separator between flags and positional arguments when building external commands (as seen in [`internal/web/setup.go`](https://github.com/gastownhall/gastown/blob/main/internal/web/setup.go)). This POSIX-compliant separator forces the parser to treat all subsequent tokens as positional arguments, ensuring paths like `--help` or `--config` are passed literally to the downstream tool rather than being interpreted as flags.

### Where does argument validation occur in the Gastown CLI?

Argument validation occurs in the `Args` field of the `cobra.Command` struct definition within files like [`internal/cmd/witness.go`](https://github.com/gastownhall/gastown/blob/main/internal/cmd/witness.go). Validators such as `cobra.ExactArgs(1)` or `cobra.NoArgs` execute automatically after Cobra parses the command line but before the `Run` function begins, ensuring handlers receive the correct argument count.

### How does Gastown extract the real binary when using wrapper commands like sudo?

The `extractWrappedBinary` function in [`internal/config/agents.go`](https://github.com/gastownhall/gastown/blob/main/internal/config/agents.go) scans the wrapper's argument list, skips any flags (and their values if applicable), stops processing if it encounters a `--` separator, and returns the first non-flag argument as the actual binary name. This allows process monitoring to identify the real application regardless of wrapper invocations.