Skip to content

Framework-free core, opt-in cobra

output is a member of the phpboyscout Go toolkit: small modules that carry no framework weight, so any project can adopt one without inheriting a CLI framework, a config system, or a cloud SDK. For an output formatter that principle has a specific edge: it must not force cobra on its importers.

The tension

The predecessor package's JSON-envelope helpers took a *cobra.Command:

func Emit(cmd *cobra.Command, resp Response) error

…but they only used two things off the command: the --output flag value and cmd.OutOrStdout(). Neither needs cobra — or even pflag. Taking the whole command coupled the entire package to spf13, so anyone importing it for a table dragged in a CLI framework.

The resolution: plain parameters, opt-in sugar

The core takes no flag library at all. Output goes to an io.Writer; the format is a plain Format. Both are configured on the Renderer:

r := output.New(output.WithWriter(w), output.WithFormat(f))
r.Emit(resp)

That makes the core usable by anything — a cobra CLI, a urfave/cli app, a raw HTTP handler, a test — and if a tool ever swaps its CLI framework, the core never changes.

The convenience of pulling (writer, format) off a *cobra.Command still exists, but as an opt-in subpackage, output/cobra:

r := ocobra.NewRenderer(cmd) // reads cmd.OutOrStdout() + the --output flag

cobra appears in the module's go.mod, but Go's package-level dead-code elimination means it is never linked into a binary that does not import the subpackage.

Guarded, not just intended

A depfootprint_test.go asserts that the root package's dependency graph (go list -deps .) contains no cobra, no pflag, no Viper, no OpenTelemetry, no cloud SDK, and no go-tool-base. The cobra subpackage legitimately imports cobra and is excluded from that assertion. The promise "importing output stays framework-free" is therefore enforced by CI, not left to discipline.

What the core does depend on

The interactive renderers (spinner, styled status) genuinely need the Charm stack — lipgloss, glamour, bubbles/bubbletea — and charmbracelet/x/ansi for Unicode-correct widths. Those are intrinsic to what the module is (a terminal output formatter), plus cockroachdb/errors, yaml, and the standard library. That is the whole graph.