Files
commandkit/docs/adr/0002-cobra-backend.md

96 lines
5.2 KiB
Markdown

# ADR 0002: Use Cobra and pflag as the initial internal CLI backend
## Status
Accepted.
## Date
2026-10-06
## Context
CommandKit's selected dispatcher model needs nested routing, flags and operands,
generated help, usage diagnostics, and invocation execution. Reimplementing this
generic infrastructure would add work beyond the suite-specific inspection and
consistency behavior that motivates the library.
Cobra and urfave/cli v3 were the finalists. The latter's destination bindings and
duplicate controls offered a close fit for the explicit-input model. The author
strongly preferred Cobra because of its broader adoption. Familiarity and the
availability of contributors who know the framework are useful maintenance
considerations across several related applications.
The selection was based on documentation and API review. Ecosystem adoption is
the selection rationale, not evidence that every backend default is an appropriate
CommandKit contract.
## Decision
Use Cobra with pflag as the sole initial CLI backend. CommandKit translates its
registrations into backend command trees, flag bindings, and execution callbacks.
Reuse their routing, option parsing, help, context, and stream mechanisms and add
adaptation where selected CommandKit semantics require it.
CommandKit retains its own public registration API, typed inputs, standard
families, and observable contracts. Applications do not construct Cobra commands
or use pflag getters in handlers. [ADR 0003](0003-public-facade.md) isolates the
backend behind an internal adapter and root-owned public contracts.
Applications retain process lifecycle and domain execution. ConfigKit and
PromptKit retain configuration and inspection semantics. Framework selection
does not transfer those responsibilities to Cobra or CommandKit.
Build fresh backend command/flag state and input storage per invocation. A fresh
command tree constructed from registered definitions is the initial approach.
Tree construction must not load configuration or create application runtime.
Implement one concrete backend. There is no initial public backend selector or
pluggable framework. A future replacement can change internal implementation
while preserving the public API and selected behavioral contracts.
## Alternatives considered
| Alternative | Advantages | Reason not selected |
| --- | --- | --- |
| urfave/cli v3 | Destination bindings for flags and named operands, presence queries, and duplicate controls fit the chosen input model closely. | Credible alternative; Cobra's broader adoption is the preferred maintenance tradeoff. |
| pflag alone | Established parsing with a smaller integration surface. | Leaves more routing, help integration, and command infrastructure to implement. |
| Kong | Compact typed-struct and tag-driven declarations. | Its main declaration model differs from the explicit-binding API; adaptation reduces its benefit. |
| Standard `flag` alone | Familiar variable binding and custom value parsing. | First-operand stopping requires adaptation for the intended syntax, with more custom command infrastructure. |
| Expose Cobra as the public API | Less adapter code and the full framework surface. | Couples consumers to backend structures and framework-specific configuration. |
| Interchangeable backends immediately | Future integration flexibility. | Adds abstraction and behavior surfaces without a current requirement. |
## Consequences
The framework supplies established generic CLI machinery. CommandKit still owns
registration conflicts, reserved families, typed bindings and presence, standard
help and diagnostics, provider integration, and selected reports and outcomes.
The adapter must accommodate explicit empty/false/zero values, fresh state,
selected repetition and operand rules, and help without configuration. Backend
features do not expand the standard automatically: for example, completion
commands remain deferred unless deliberately selected.
Backend error handling must preserve application outcomes, avoid duplicate
diagnostics, and return control to the application's entry point. Native YAML
inspection and multi-document scope display remain integration work using the
owning libraries. Choosing a framework introduces no JSON report requirement.
Exact dependency versions and attribution are selected when dependencies are
incorporated. Meaningful integration evidence must exercise the real adapter
against selected contracts. A dependency update or replacement can change CLI
behavior even with unchanged Go signatures and is subject to compatibility policy.
The [architecture policy](../policy/architecture.md) owns normative boundaries;
the [backend roadmap](../roadmap/03-backend.md) owns implementation tasks and status.
[ADR 0001](0001-command-dispatch-and-inputs.md) records the dispatcher and input
model. Remaining command and grammar proposals belong to the CLI roadmap.
Reviewed primary references from the selection:
- [Cobra API](https://pkg.go.dev/github.com/spf13/cobra).
- [Cobra command and help guide](https://github.com/spf13/cobra/blob/main/site/content/user_guide.md).
- [Cobra flag bindings](https://cobra.dev/docs/how-to-guides/working-with-flags/).
- [pflag syntax and extension points](https://github.com/spf13/pflag).
- [urfave/cli v3 API](https://pkg.go.dev/github.com/urfave/cli/v3).