# Custom Commands in gastownhall/gastown: How to Extend the gt CLI

> Extend the gt CLI with custom commands in gastownhall/gastown. Discover how to add new subcommands easily by leveraging the pluggable command architecture in the cmd directory.

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

---

**The gastown repository implements a pluggable command architecture where each Go file in `internal/cmd` auto-registers with the Cobra CLI via `init()` functions, enabling developers to add custom subcommands to the `gt` tool without modifying existing source code.**

The Gastown project provides a multi-agent workflow platform centered around the `gt` CLI. Rather than using hard-coded command strings, the codebase employs a dynamic registration pattern that treats every file in the `internal/cmd` directory as a potential custom command extension point.

## How Custom Commands Work in Gastown

The architecture follows a simple but powerful pattern: individual command files declare their own initialization logic, and the root CLI discovers them at runtime. This eliminates the need for a central registry that must be updated manually.

### CLI Entry Point in cmd/gt/main.go

The journey begins at [`cmd/gt/main.go`](https://github.com/gastownhall/gastown/blob/main/cmd/gt/main.go), which serves as the entry point for the `gt` binary. This file creates the Cobra root command and immediately delegates to `cmd.Execute()`. According to the gastownhall/gastown source code, this execution path triggers the registration of all subcommands found in the `internal/cmd` package.

### Command Registration Pattern

Each custom command follows a consistent registration contract. Files in `internal/cmd` expose an `init()` function that calls `RootCmd.AddCommand()` with a constructor function. For example, [`internal/cmd/wl.go`](https://github.com/gastownhall/gastown/blob/main/internal/cmd/wl.go) registers the work-list command using this exact pattern:

```go
func init() {
    RootCmd.AddCommand(NewWLCmd())
}

// NewWLCmd builds the cobra.Command for `gt wl`.
func NewWLCmd() *cobra.Command {
    return &cobra.Command{
        Use:   "wl",
        Short: "Show the work‑list for the current session",
        RunE:  runWLCmd,
    }
}

```

When the binary starts, Go automatically executes all `init()` functions, making the command available immediately.

## Existing Custom Command Examples

The `cmd` directory contains several production commands that demonstrate this extensibility pattern. These files illustrate how the platform implements complex functionality through the same custom command interface:

- **[`internal/cmd/wl.go`](https://github.com/gastownhall/gastown/blob/main/internal/cmd/wl.go)** – Implements `gt wl` for displaying the current work-list
- **[`internal/cmd/witness.go`](https://github.com/gastownhall/gastown/blob/main/internal/cmd/witness.go)** – Provides the `gt witness` command for witness operations  
- **[`internal/cmd/start.go`](https://github.com/gastownhall/gastown/blob/main/internal/cmd/start.go)** – Handles `gt start` to boot new sessions
- **[`internal/cmd/tap_guard.go`](https://github.com/gastownhall/gastown/blob/main/internal/cmd/tap_guard.go)** – Implements the complex `gt tap guard` subcommand
- **[`internal/cmd/upgrade.go`](https://github.com/gastownhall/gastown/blob/main/internal/cmd/upgrade.go)** – Contains the `gt upgrade` maintenance command

Each file follows the identical structure: an `init()` hook calling `RootCmd.AddCommand(NewXCommand())`, followed by the command constructor and execution logic.

## Adding a New Custom Command

Creating a custom command requires only a new Go file in `internal/cmd`. The following example demonstrates adding a `gt hello` command:

Create [`internal/cmd/hello.go`](https://github.com/gastownhall/gastown/blob/main/internal/cmd/hello.go):

```go
package cmd

import (
    "fmt"
    "github.com/spf13/cobra"
)

func init() {
    RootCmd.AddCommand(NewHelloCmd())
}

func NewHelloCmd() *cobra.Command {
    return &cobra.Command{
        Use:   "hello",
        Short: "Print a friendly greeting",
        Run: func(cmd *cobra.Command, args []string) {
            fmt.Println("👋 Hello from Gastown!")
        },
    }
}

```

After recompiling the binary, the command becomes immediately available:

```bash
$ gt hello
👋 Hello from Gastown!

```

No changes to [`cmd/gt/main.go`](https://github.com/gastownhall/gastown/blob/main/cmd/gt/main.go) or existing files are required. The `init()` function automatically registers the new command with the root CLI at startup.

## Summary

- **Auto-registration**: Commands in `internal/cmd` register themselves via `init()` functions, eliminating central registry maintenance.
- **Cobra framework**: The `gt` CLI uses the Cobra library, with each command returning a `*cobra.Command` struct from its constructor function.
- **Entry point**: [`cmd/gt/main.go`](https://github.com/gastownhall/gastown/blob/main/cmd/gt/main.go) bootstraps the CLI but delegates command discovery to the `internal/cmd` package.
- **Extensibility**: Adding custom commands requires only creating a new file with `init()` and `NewCommand()` functions following the established pattern.

## Frequently Asked Questions

### How do I add a custom command to the gastown CLI?

Create a new Go file in `internal/cmd/` containing an `init()` function that calls `RootCmd.AddCommand(NewMyCommand())`, plus a constructor function returning a `*cobra.Command`. The Go runtime automatically registers the command when the binary starts, requiring no changes to existing code.

### What is the role of the init() function in gastown commands?

The `init()` function serves as the registration hook. Each command file uses `init()` to add its command constructor to the root command. Go executes these functions automatically during package initialization, enabling the modular command architecture found in gastownhall/gastown.

### Where are custom commands stored in the gastown repository?

Custom commands reside in the `internal/cmd/` directory. While the entry point lives at [`cmd/gt/main.go`](https://github.com/gastownhall/gastown/blob/main/cmd/gt/main.go), all subcommand implementations—including [`wl.go`](https://github.com/gastownhall/gastown/blob/main/wl.go), [`witness.go`](https://github.com/gastownhall/gastown/blob/main/witness.go), and [`start.go`](https://github.com/gastownhall/gastown/blob/main/start.go)—are located in `internal/cmd/`.

### Does gastown require manual registration of new commands?

No. The gastown CLI uses runtime discovery via Go's `init()` mechanism. Simply placing a properly structured Go file in `internal/cmd/` automatically makes the command available to users after recompilation, without manual registry updates.