miller/pkg/cli/flag_json.go
John Kerl 2dd94cb181
Add machine-readable help catalog: mlr help --json (#2098) (#2099)
Emit Miller's existing help catalog (verbs, functions, flags, keywords)
as structured JSON so AI agents and tooling can model Miller's surface
without scraping prose. The --json token may appear anywhere on a
`mlr help ...` command line; plain text help is unchanged.

  mlr help --json                  # full catalog
  mlr help verb cat --json         # one or more verbs
  mlr help function splitax --json # one or more functions
  mlr help flag --ifs --json       # one or more flags
  mlr help keyword ENV --json      # one or more keywords

Functions and flags serialize fully (name/class/arity/help/examples;
section/name/alt_names/arg/help). Verbs carry a summary, ignores_input,
and captured raw usage_text as a Tier-1 fallback, since per-verb options
are prose-only today (each verb hand-writes its UsageFunc). Structured
verb options are a planned follow-on (see #2098).

This is a serialization layer over the existing registries -- no
refactor of the text-help path.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 16:55:54 -04:00

54 lines
1.9 KiB
Go

// Machine-readable (JSON) accessors over the command-line flag table, for
// `mlr help --json` and similar tooling. Unlike verb options (which are
// prose-only), the flag table is already fully structured since it drives the
// actual command-line parser -- so this is a thin serialization layer.
package cli
// FlagInfoForJSON is the structured view of a single command-line flag. Section
// is the human-readable flag-section name (e.g. "CSV-only flags"); Arg is the
// argument placeholder in curly braces (e.g. "{filename}") or "" for boolean
// flags; AltNames carries alternate spellings (e.g. "--csv" alongside "-c").
type FlagInfoForJSON struct {
Section string `json:"section"`
Name string `json:"name"`
AltNames []string `json:"alt_names,omitempty"`
Arg string `json:"arg,omitempty"`
Help string `json:"help"`
}
func makeFlagInfoForJSON(sectionName string, flag *Flag) *FlagInfoForJSON {
return &FlagInfoForJSON{
Section: sectionName,
Name: flag.name,
AltNames: flag.altNames,
Arg: flag.arg,
Help: flag.help,
}
}
// GetFlagInfosForJSON returns the full flag catalog, grouped by section in
// table order, flattened into a single list with each flag tagged by section.
func (ft *FlagTable) GetFlagInfosForJSON() []*FlagInfoForJSON {
infos := make([]*FlagInfoForJSON, 0)
for _, section := range ft.sections {
for i := range section.flags {
infos = append(infos, makeFlagInfoForJSON(section.name, &section.flags[i]))
}
}
return infos
}
// GetFlagInfoForJSON returns the structured view of a single flag by name
// (matching either its primary name or any alternate spelling), or nil if there
// is no such flag.
func (ft *FlagTable) GetFlagInfoForJSON(name string) *FlagInfoForJSON {
for _, section := range ft.sections {
for i := range section.flags {
if section.flags[i].Owns(name) {
return makeFlagInfoForJSON(section.name, &section.flags[i])
}
}
}
return nil
}