// Package cliflag is the single home of the carlos CLI's claimed // short-flag namespace (spec 2026-08-19-cli-flag-conventions-design.md): // the alias table, the one registrar that cannot get the default-value // trap wrong, and the usage printer that folds an alias into its // primary. It is a package rather than a corner of package main so that // verbs living outside main (internal/vet) register through the same // table instead of open-coding an alias pair. package cliflag import ( "flag" "fmt" "strings" ) // Aliases maps a primary flag name to its short alias. Five letters, // and nothing else (-d for --destination joined the original four // in 2026-09): -vv is reserved for a future verbose mode so it can // never quietly claim -v and collide with --version; -console stays // long-only by ruling; -f, -n, -y are claimed by logs/update as // primaries and are not aliases. var Aliases = map[string]string{ "app": "a", "channel": "c", "destination": "d", "environment": "e", "version": "v", } // String registers name — and its short form, when the table claims one // — against one variable. The alias's StringVar assigns through the // shared pointer, so both registrations MUST pass the same default; // repeating def here is the reason this registrar exists rather than // two open-coded calls (a mismatched alias default silently clobbers // the primary's). func String(fs *flag.FlagSet, name, def, usage string) *string { p := fs.String(name, def, usage) if short := Aliases[name]; short != "" { fs.StringVar(p, short, def, "alias for --"+name) } return p } // Usage replaces the stock PrintDefaults face for every carlos flag // set: the alias folds into its primary ("--app, -a"), long flags render // with the documented double dash, single-letter primaries (-f, -n, -y) // keep their one dash, and alias registrations never print a row of // their own. Output derives from the live flag set, so help cannot // drift from what the parser accepts — the reason to generate rather // than hand-write 90 flag lists. header is the command's synopsis // ("usage: carlos ..."); empty derives one from the set's name. func Usage(fs *flag.FlagSet, header string) func() { return func() { w := fs.Output() if header == "" { header = "usage: carlos " + fs.Name() + " [flags]" } fmt.Fprintln(w, strings.TrimRight(header, "\n")) aliasOf := make(map[string]string, len(Aliases)) for primary, short := range Aliases { aliasOf[short] = primary } type row struct{ head, usage string } var rows []row width := 0 isAlias := func(short, primary string) bool { f := fs.Lookup(short) return f != nil && f.Usage == "alias for --"+primary } fs.VisitAll(func(f *flag.Flag) { // Fold only pairs String itself registered — recognised by // the alias's marker usage, not by name alone, so a command // registering a plain -a of its own is neither hidden nor // folded into a primary it does not share a value with. if p, ok := aliasOf[f.Name]; ok && isAlias(f.Name, p) && fs.Lookup(p) != nil { return } head := "--" + f.Name if len(f.Name) == 1 { head = "-" + f.Name } if short := Aliases[f.Name]; short != "" && isAlias(short, f.Name) { head += ", -" + short } arg, usage := flag.UnquoteUsage(f) if arg != "" { head += " <" + arg + ">" } switch f.DefValue { case "", "false", "0", "0s", "[]": default: usage += " (default: " + f.DefValue + ")" } if len(head) > width { width = len(head) } rows = append(rows, row{head, usage}) }) if len(rows) > 0 { fmt.Fprintln(w, "flags:") } for _, r := range rows { fmt.Fprintf(w, " %-*s %s\n", width, r.head, r.usage) } } }