Open-Source Wikis

/

GitHub CLI

/

GitHub CLI

/

Architecture

cli/cli

Architecture

gh is a single Go binary built from cmd/gh/main.go. The main function delegates immediately to internal/ghcmd.Main, which constructs the configuration, telemetry service, and Cobra command tree before dispatching to the subcommand selected by os.Args.

Top-level layout

cmd/gh/main.go              # Entry point (calls ghcmd.Main)
internal/ghcmd/cmd.go       # Bootstraps config, telemetry, factory, root cmd
pkg/cmd/root/root.go        # Builds the Cobra root command and registers all top-level commands
pkg/cmd/factory/default.go  # Builds the cmdutil.Factory (HTTP client, prompter, git client, etc.)
pkg/cmd/<command>/...       # Implementations of every gh subcommand
api/                        # GraphQL + REST client and queries
git/                        # Wrapper around the local git executable
internal/                   # Project-internal Go packages
pkg/                        # Reusable building blocks (cmdutil, iostreams, httpmock, ...)

The non-vendored Go source totals roughly 241k lines across 854 .go files, with 339 _test.go files. The bulk of the code lives under pkg/cmd/ (about 20 MB on disk) which holds one directory per command.

Boot sequence

graph TD
    A[cmd/gh/main.go] -->|os.Exit| B[internal/ghcmd.Main]
    B --> C[config.NewConfig]
    B --> D[iostreams.System]
    B --> E[agents.Detect]
    B --> F[telemetry.Service]
    B --> G[pkg/cmd/factory.New]
    G --> H[cmdutil.Factory]
    B --> I[root.NewCmdRoot]
    I --> J[Register every subcommand]
    I --> K[Register help topics & extensions]
    I --> L[Register aliases]
    B --> M[rootCmd.ExecuteC]
    M -->|RunE| N[Subcommand handler]
    N --> O[api.Client / git.Client / IOStreams]
  1. Main (internal/ghcmd/cmd.go) loads config from disk, builds an IOStreams wired to the user's terminal, detects whether the CLI was launched by an AI agent, and configures telemetry (real or no-op).
  2. The cmdutil.Factory from pkg/cmd/factory/default.go exposes the dependencies every command needs: IOStreams, Config, HttpClient, GitClient, Prompter, Browser, BaseRepo, Branch, and ExtensionManager.
  3. pkg/cmd/root.NewCmdRoot registers every top-level command, help topics, installed extensions, user aliases, and "official extension stubs" that suggest installing first-party extensions.
  4. Cobra parses os.Args, dispatches to the matching RunE, and the command performs its work using only the dependencies it pulled from the factory.
  5. On exit, the parent Main translates Go errors into one of five exit codes: exitOK, exitError, exitCancel, exitAuth, or exitPending.

The factory pattern

Every command is constructed as NewCmdFoo(f *cmdutil.Factory, runF func(*FooOptions) error) *cobra.Command. The runF parameter is the test injection point: tests call NewCmdFoo with a stub runF to assert flag parsing, while the real binary uses the package's own run function.

The Factory type is defined in pkg/cmdutil/factory.go:

type Factory struct {
    AppVersion       string
    ExecutablePath   string
    InvokingAgent    string

    Browser          browser.Browser
    ExtensionManager extensions.ExtensionManager
    GitClient        *git.Client
    IOStreams        *iostreams.IOStreams
    Prompter         prompter.Prompter

    BaseRepo        func() (ghrepo.Interface, error)
    Branch          func() (string, error)
    Config          func() (gh.Config, error)
    HttpClient      func() (*http.Client, error)
    PlainHttpClient func() (*http.Client, error)
    Remotes         func() (context.Remotes, error)
}

BaseRepo, Branch, Config, and friends are functions rather than values because they may fail or do work that should not happen during construction. Commands lazily call them inside RunE.

Repo-resolving commands

Most commands use the simple BaseRepoFunc, which returns the first eligible git remote. A subset (pr, issue, repo, release, org, ruleset, run, workflow, label, cache, api, agent-task, browse) need the "smart" resolver in pkg/cmd/factory/remote_resolver.go and default.go. The smart resolver:

  • Reads the resolved git config key set by gh repo set-default or by clone-of-fork.
  • Resolves repository networks via the GitHub API to pick a base repo when multiple remotes match.
  • Falls back to interactive prompting when ambiguity remains and a TTY is attached.

pkg/cmd/root/root.go constructs a copy of the factory with BaseRepo: factory.SmartBaseRepoFunc(f) and registers those commands against it.

Networking and feature detection

  • api/client.go wraps the go-gh REST and GraphQL clients with Client.REST, Client.GraphQL, Client.Mutate, and pagination helpers.
  • The HTTP transport stack adds the user agent, OAuth scope hints, telemetry headers, and an SSO redirect interceptor.
  • internal/featuredetection/feature_detection.go probes the GraphQL schema at runtime to decide whether features such as Issues v2, ProjectsV2, or merge queues are available on the host (GitHub.com vs GitHub Enterprise Server).

Git, prompts, and I/O

  • git/client.go shells out to the git executable through cli/safeexec. The client returns typed objects (commits, refs, remotes, statuses) and is used wherever the CLI needs to inspect or mutate the local repo.
  • pkg/iostreams/iostreams.go abstracts stdin, stdout, stderr, color, and pager handling. It is the canonical place to query IsStdoutTTY(), start spinners, and manage the pager process.
  • internal/prompter/ provides a Prompter interface (mocked via moq) for interactive selects, confirms, and authentication prompts.

Extensions

User extensions follow the gh-<name> naming convention and live under ~/.local/share/gh/extensions/. The extension manager in pkg/cmd/extension/manager.go supports git-cloned scripts, precompiled binaries, and Go-built source trees. Installed extensions are discovered at gh startup and registered as Cobra commands beside the built-ins, but only if their name does not collide with a core command.

Where to drill down

  • Commands for the user-facing surface.
  • Systems for the shared internal libraries.
  • Reference for configuration files, environment variables, and dependency lists.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Architecture – GitHub CLI wiki | Factory