Files
zero/internal/cli/command_center.go
KRATOSandClaude Opus 4.8 30545b4417 Slice 3: model registry depth — regex aliases, deprecation fallback, reasoning defaults
Resolve()/ResolveWithFallback()/FallbackFor() + DefaultReasoningEffort/EffectiveReasoningEffort wired into CLI --model and TUI /model (deprecated models auto-redirect with a notice; unknown ids like minimax-m3 pass through unchanged). zero models renders an aligned table; catalog entries decorated with match patterns/default efforts/deprecation rules. Provider reasoning-effort propagation deferred (needs request-schema change). Build/vet/test green.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-06 22:16:33 +05:30

387 lines
10 KiB
Go

package cli
import (
"fmt"
"io"
"sort"
"strings"
"text/tabwriter"
"github.com/Gitlawb/zero/internal/config"
"github.com/Gitlawb/zero/internal/modelregistry"
"github.com/Gitlawb/zero/internal/zerocommands"
)
type commandCenterOptions struct {
json bool
provider string
includeDeprecated bool
}
type configSummary = zerocommands.ConfigSnapshot
type providerSummary = zerocommands.ProviderSnapshot
type modelSummary = zerocommands.ModelSnapshot
func runConfig(args []string, stdout io.Writer, stderr io.Writer, deps appDeps) int {
options, help, err := parseCommandCenterArgs(args, false)
if err != nil {
return writeExecUsageError(stderr, err.Error())
}
if help {
if err := writeConfigHelp(stdout); err != nil {
return exitCrash
}
return exitSuccess
}
resolved, exitCode := resolveCommandCenterConfig(stderr, deps)
if exitCode != exitSuccess {
return exitCode
}
summary := summarizeConfig(resolved)
if options.json {
if err := writePrettyJSON(stdout, summary); err != nil {
return exitCrash
}
return exitSuccess
}
if _, err := fmt.Fprintln(stdout, formatConfigSummary(summary)); err != nil {
return exitCrash
}
return exitSuccess
}
func runProviders(args []string, stdout io.Writer, stderr io.Writer, deps appDeps) int {
command := "list"
if len(args) > 0 && !strings.HasPrefix(args[0], "-") {
command = strings.ToLower(strings.TrimSpace(args[0]))
args = args[1:]
}
if command == "help" {
if err := writeProvidersHelp(stdout); err != nil {
return exitCrash
}
return exitSuccess
}
if command != "list" && command != "current" {
return writeExecUsageError(stderr, fmt.Sprintf("unknown providers command %q", command))
}
options, help, err := parseCommandCenterArgs(args, false)
if err != nil {
return writeExecUsageError(stderr, err.Error())
}
if help {
if err := writeProvidersHelp(stdout); err != nil {
return exitCrash
}
return exitSuccess
}
resolved, exitCode := resolveCommandCenterConfig(stderr, deps)
if exitCode != exitSuccess {
return exitCode
}
summary := summarizeConfig(resolved)
providers := summary.Providers
if command == "current" {
providers = []providerSummary{}
for _, provider := range summary.Providers {
if provider.Active {
providers = append(providers, provider)
break
}
}
}
if options.json {
if command == "current" {
var provider any
if len(providers) > 0 {
provider = providers[0]
}
if err := writePrettyJSON(stdout, map[string]any{"provider": provider}); err != nil {
return exitCrash
}
return exitSuccess
}
if err := writePrettyJSON(stdout, map[string]any{"providers": providers}); err != nil {
return exitCrash
}
return exitSuccess
}
if _, err := fmt.Fprintln(stdout, formatProviderSummaries(command, providers)); err != nil {
return exitCrash
}
return exitSuccess
}
func runModels(args []string, stdout io.Writer, stderr io.Writer) int {
if len(args) > 0 && (args[0] == "list" || args[0] == "ls") {
args = args[1:]
}
options, help, err := parseCommandCenterArgs(args, true)
if err != nil {
return writeExecUsageError(stderr, err.Error())
}
if help {
if err := writeModelsHelp(stdout); err != nil {
return exitCrash
}
return exitSuccess
}
registry, err := modelregistry.DefaultRegistry()
if err != nil {
return writeAppError(stderr, err.Error(), exitCrash)
}
models, err := listModelSummaries(registry, options)
if err != nil {
return writeExecUsageError(stderr, err.Error())
}
if options.json {
if err := writePrettyJSON(stdout, map[string]any{"models": models}); err != nil {
return exitCrash
}
return exitSuccess
}
if _, err := fmt.Fprintln(stdout, formatModelSummaries(models)); err != nil {
return exitCrash
}
return exitSuccess
}
func resolveCommandCenterConfig(stderr io.Writer, deps appDeps) (config.ResolvedConfig, int) {
workspaceRoot, err := resolveWorkspaceRoot("", deps)
if err != nil {
return config.ResolvedConfig{}, writeExecUsageError(stderr, err.Error())
}
resolved, err := deps.resolveConfig(workspaceRoot, config.Overrides{})
if err != nil {
return config.ResolvedConfig{}, writeAppError(stderr, err.Error(), exitProvider)
}
return resolved, exitSuccess
}
func parseCommandCenterArgs(args []string, allowModelFilters bool) (commandCenterOptions, bool, error) {
options := commandCenterOptions{}
for index := 0; index < len(args); index++ {
arg := args[index]
switch {
case arg == "-h" || arg == "--help" || arg == "help":
return options, true, nil
case arg == "--json":
options.json = true
case allowModelFilters && arg == "--include-deprecated":
options.includeDeprecated = true
case allowModelFilters && arg == "--provider":
value, next, err := nextFlagValue(args, index, arg)
if err != nil {
return options, false, err
}
options.provider = value
index = next
case allowModelFilters && strings.HasPrefix(arg, "--provider="):
options.provider = strings.TrimSpace(strings.TrimPrefix(arg, "--provider="))
case strings.HasPrefix(arg, "-"):
return options, false, execUsageError{fmt.Sprintf("unknown flag %q", arg)}
default:
return options, false, execUsageError{fmt.Sprintf("unexpected argument %q", arg)}
}
}
return options, false, nil
}
func summarizeConfig(resolved config.ResolvedConfig) configSummary {
summary := zerocommands.ConfigSnapshotFromResolved(resolved)
sort.SliceStable(summary.Providers, func(i int, j int) bool {
if summary.Providers[i].Active != summary.Providers[j].Active {
return summary.Providers[i].Active
}
return summary.Providers[i].Name < summary.Providers[j].Name
})
return summary
}
func listModelSummaries(registry modelregistry.Registry, options commandCenterOptions) ([]modelSummary, error) {
summaries, err := zerocommands.ModelSnapshots(registry, zerocommands.ModelSnapshotOptions{
Provider: modelregistry.ProviderKind(strings.TrimSpace(strings.ToLower(options.provider))),
IncludeDeprecated: options.includeDeprecated,
})
if err != nil {
return nil, execUsageError{err.Error()}
}
sort.SliceStable(summaries, func(i int, j int) bool {
if summaries[i].Provider == summaries[j].Provider {
return summaries[i].ID < summaries[j].ID
}
return summaries[i].Provider < summaries[j].Provider
})
return summaries, nil
}
func formatConfigSummary(summary configSummary) string {
lines := []string{
"Config",
"runtime: " + summary.Runtime,
"active provider: " + displayCLIValue(summary.ActiveProvider, "none"),
fmt.Sprintf("max turns: %d", summary.MaxTurns),
"providers:",
}
if len(summary.Providers) == 0 {
lines = append(lines, " (none)")
}
for _, provider := range summary.Providers {
lines = append(lines, " "+formatProviderLine(provider))
}
return strings.Join(lines, "\n")
}
func formatProviderSummaries(command string, providers []providerSummary) string {
title := "Providers"
if command == "current" {
title = "Provider"
}
lines := []string{title}
if len(providers) == 0 {
lines = append(lines, " (none)")
return strings.Join(lines, "\n")
}
for _, provider := range providers {
if command == "current" {
lines = append(lines,
"name: "+displayCLIValue(provider.Name, "none"),
"kind: "+displayCLIValue(provider.ProviderKind, "unknown"),
"model: "+displayCLIValue(provider.Model, "none"),
"api model: "+displayCLIValue(provider.APIModel, "unknown"),
"base url: "+displayCLIValue(provider.BaseURL, "default"),
"api key: "+apiKeyState(provider.APIKeySet),
)
if provider.Message != "" {
lines = append(lines, "status: "+provider.Status+" - "+provider.Message)
}
continue
}
lines = append(lines, " "+formatProviderLine(provider))
}
return strings.Join(lines, "\n")
}
func formatProviderLine(provider providerSummary) string {
marker := " "
if provider.Active {
marker = "*"
}
line := fmt.Sprintf("%s %s [%s] model=%s apiModel=%s api key: %s", marker, displayCLIValue(provider.Name, "none"), displayCLIValue(provider.ProviderKind, "unknown"), displayCLIValue(provider.Model, "none"), displayCLIValue(provider.APIModel, "unknown"), apiKeyState(provider.APIKeySet))
if provider.Message != "" {
line += " (" + provider.Status + ": " + provider.Message + ")"
}
return line
}
func formatModelSummaries(models []modelSummary) string {
if len(models) == 0 {
return "Models\n (none)"
}
var builder strings.Builder
builder.WriteString("Models\n")
writer := tabwriter.NewWriter(&builder, 0, 0, 2, ' ', 0)
fmt.Fprintln(writer, "ID\tPROVIDER\tSTATUS\tCONTEXT\tREASONING\t$IN/1M\t$OUT/1M")
for _, model := range models {
fmt.Fprintf(writer, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
model.ID,
model.Provider,
model.Status,
formatTokenCount(model.ContextWindow),
formatReasoningEfforts(model.ReasoningEfforts),
formatRate(model.InputPerMillion),
formatRate(model.OutputPerMillion),
)
}
writer.Flush()
return strings.TrimRight(builder.String(), "\n")
}
func formatReasoningEfforts(efforts []string) string {
if len(efforts) == 0 {
return "-"
}
return strings.Join(efforts, "/")
}
func formatRate(rate float64) string {
if rate <= 0 {
return "-"
}
return fmt.Sprintf("%.2f", rate)
}
func formatTokenCount(value int) string {
if value <= 0 {
return "-"
}
digits := fmt.Sprintf("%d", value)
var grouped strings.Builder
for index, char := range digits {
if index > 0 && (len(digits)-index)%3 == 0 {
grouped.WriteByte(',')
}
grouped.WriteRune(char)
}
return grouped.String()
}
func apiKeyState(set bool) string {
if set {
return "set"
}
return "not set"
}
func displayCLIValue(value string, fallback string) string {
if strings.TrimSpace(value) == "" {
return fallback
}
return value
}
func writeConfigHelp(w io.Writer) error {
_, err := fmt.Fprint(w, `Usage:
zero config [flags]
Inspects resolved Go configuration without printing secrets.
Flags:
--json Print JSON summary
-h, --help Show this help
`)
return err
}
func writeModelsHelp(w io.Writer) error {
_, err := fmt.Fprint(w, `Usage:
zero models list [flags]
Lists Zero model registry entries.
Flags:
--json Print JSON model list
--provider <provider> Filter by openai, anthropic, google, or openai-compatible
--include-deprecated Include deprecated models
-h, --help Show this help
`)
return err
}
func writeProvidersHelp(w io.Writer) error {
_, err := fmt.Fprint(w, `Usage:
zero providers current [flags]
zero providers list [flags]
Inspects resolved provider profiles without printing secrets.
Flags:
--json Print JSON summary
-h, --help Show this help
`)
return err
}