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]Main(internal/ghcmd/cmd.go) loads config from disk, builds anIOStreamswired to the user's terminal, detects whether the CLI was launched by an AI agent, and configures telemetry (real or no-op).- The
cmdutil.Factoryfrompkg/cmd/factory/default.goexposes the dependencies every command needs:IOStreams,Config,HttpClient,GitClient,Prompter,Browser,BaseRepo,Branch, andExtensionManager. pkg/cmd/root.NewCmdRootregisters every top-level command, help topics, installed extensions, user aliases, and "official extension stubs" that suggest installing first-party extensions.- Cobra parses
os.Args, dispatches to the matchingRunE, and the command performs its work using only the dependencies it pulled from the factory. - On exit, the parent
Maintranslates Go errors into one of five exit codes:exitOK,exitError,exitCancel,exitAuth, orexitPending.
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
resolvedgit config key set bygh repo set-defaultor 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.gowraps thego-ghREST and GraphQL clients withClient.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.goprobes 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.goshells out to thegitexecutable throughcli/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.goabstracts stdin, stdout, stderr, color, and pager handling. It is the canonical place to queryIsStdoutTTY(), start spinners, and manage the pager process.internal/prompter/provides aPrompterinterface (mocked viamoq) 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
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.