# Golang CLI

> Design or review the framework-independent behavior and architecture of Go command-line applications: command UX, flags, configuration, output and exit contracts, version/build metadata, cancellation, completion, distribution, and testability. Use for cross-cutting CLI behavior and combine with a framework skill when Cobra or Viper APIs are central.

- Skill: `reagin/golang-cli` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add reagin/golang-cli`
- Raw SKILL.md: https://api.skillmd.com/api/skills/reagin/golang-cli/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- License: MIT
- Author: reagin (https://skillmd.com/u/reagin)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/reagin/golang-cli

---


# Go CLI Engineering

Build a predictable interface for both people and automation. Preserve the project's existing command framework and conventions unless the user asks to change them.

## Start with the current contract

Before editing:

1. Read `go.mod`, command entry points, existing command and configuration packages, tests, release scripts, completion/docs generation, and user-facing docs.
2. Identify supported platforms, installation channels, whether output is consumed by scripts, and compatibility promises for flags, exit codes, version output, and output formats.
3. Separate the command layer from domain work. Commands should parse input, call testable application logic, render results, and map failures at one outer boundary.
4. Treat renaming a flag, changing a default, moving output between streams, or changing JSON fields as an API change.

Do not add a CLI or configuration dependency merely to follow a preferred pattern. The standard library flag package is often enough for a small, flat command. A framework can be justified by an existing codebase or requirements such as nested commands, completion, shared flags, and extensive help generation.

When implementing the boundary, read [command recipes](references/command-recipes.md) for testable standard-library and Cobra shapes, explicit flag/config precedence, signal ownership, and separate output capture. Use only the branch matching the existing project.

## Design the command surface

- Make command and flag names consistent, unsurprising, and stable.
- Distinguish required input from optional configuration. Validate combinations and ranges close to parsing, then keep business validation in the domain layer.
- Keep configuration precedence explicit and tested. Preserve the project's chosen ordering among flags, environment variables, files, and defaults; do not assume one universal order.
- Distinguish an omitted value from an explicit zero, false, or empty value when that difference matters.
- Keep secrets out of command lines when process listings or shell history would expose them. Prefer an existing secret input mechanism.
- Generate completions only when the selected framework supports them and the product needs them. Completion must not perform surprising network calls or mutations.

## Preserve Unix and automation semantics

| Concern | Guidance |
| --- | --- |
| stdout | Successful primary data intended for piping |
| stderr | Diagnostics, warnings, progress, and error details |
| exit status | Zero on success; stable nonzero classes only when callers benefit |
| help and usage | Concise help on request; avoid flooding runtime failures with full usage |
| machine output | Stable schema, no color or progress mixed into the data stream |
| non-interactive runs | Never block on a prompt unless explicitly enabled |

Do not impose BSD sysexits or a custom status taxonomy on an established tool. Map errors once at the executable boundary, and avoid `os.Exit` inside command or domain functions so defers and cleanup still run.

When human and machine output coexist, provide an explicit format switch or preserve the existing one. Encode structured output directly rather than constructing JSON or YAML by hand.

Terminal detection and color must follow the stream actually selected by the command, not assume process-global `os.Stdout`. Respect the project's non-color convention, such as `NO_COLOR`, and make redirected or machine-readable output deterministic.

## Cancellation and cleanup

- Propagate the command context into I/O and long-running operations.
- Let the executable boundary own process signals. If it already passes a cancellable context into commands, do not register duplicate signal handlers inside each subcommand.
- For daemons or watch commands, translate supported process signals into cancellation and give cleanup a bounded deadline. Isolate platform-specific signals when the CLI is cross-platform.
- Stop accepting new work before closing shared resources.

## Version, completion, and distribution

- Keep version output stable enough for scripts and support diagnostics. Use the repository's existing build metadata mechanism, such as VCS build information or linker-injected values; do not hard-code release versions in source.
- Treat missing development-build metadata as a valid state with an explicit fallback. Do not assume a Git checkout exists at runtime.
- Write generated completion scripts to the command's configured output stream. Completion callbacks should be fast, side-effect-free, and avoid uncontrolled network access.
- Treat generated completions, manual pages, package-manager formulas, and release archives as derived artifacts. Regenerate only those in scope through the repository's pinned release workflow.
- Do not introduce GoReleaser, Homebrew packaging, or another distribution system merely because the CLI lacks one; distribution is a separate product decision.

When the task includes version injection, generated manuals or completions, release archives, package-manager metadata, or release handoff, read [distribution and generated documentation](references/distribution-and-generated-docs.md).

## Make commands testable

Prefer a command constructor or runner that accepts dependencies and I/O streams. Tests should cover:

- parsing, defaults, aliases, and invalid combinations;
- stdout and stderr independently;
- exit classification at the outer boundary;
- cancellation and cleanup for long-running commands;
- version and build-info fallbacks;
- completion output and callbacks when changed;
- stable structured output and help examples.

Use the framework's injectable streams when available instead of writing to process globals. Reset mutable command state between tests; reusing a global command tree can leak flags and buffers across cases.

## Review checklist

- The implementation matches existing architecture and dependency policy.
- Public flags, defaults, output, and status changes are documented as compatibility changes.
- Diagnostics cannot corrupt piped output.
- Errors retain useful causes internally without leaking secrets.
- Commands do not hide domain logic, global mutable state, or process termination.
- Tests execute the command through the same parsing path users exercise.
- User documentation and completion behavior match the implemented surface.

Run the repository's existing focused format, test, lint, and documentation checks for the changed command surface.

