Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion internal/cli/command_center.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,30 @@ type modelSummary = zerocommands.ModelSnapshot
type providerCatalogSummary = zerocommands.ProviderCatalogSnapshot

func runConfig(args []string, stdout io.Writer, stderr io.Writer, deps appDeps) int {
// The first non-flag argument is a subcommand. With no positional argument
// (or only flag arguments), the read-only summary path runs — this keeps
// `zero config` and `zero config --json` working unchanged.
command := "summary"
rest := args
if len(args) > 0 && !strings.HasPrefix(args[0], "-") {
command = strings.ToLower(strings.TrimSpace(args[0]))
rest = args[1:]
}
switch command {
case "summary":
return runConfigSummary(rest, stdout, stderr, deps)
case "notify":
return runConfigNotify(rest, stdout, stderr, deps)
case "help":
if err := writeConfigHelp(stdout); err != nil {
return exitCrash
}
return exitSuccess
}
return writeExecUsageError(stderr, fmt.Sprintf("unknown config command %q", command))
}

func runConfigSummary(args []string, stdout io.Writer, stderr io.Writer, deps appDeps) int {
options, help, err := parseCommandCenterArgs(args, false, false)
if err != nil {
return writeExecUsageError(stderr, err.Error())
Expand Down Expand Up @@ -462,8 +486,11 @@ func formatProviderCatalogValue(value string, fallback string) string {
func writeConfigHelp(w io.Writer) error {
_, err := fmt.Fprint(w, `Usage:
zero config [flags]
zero config notify [flags]

Inspects resolved Go configuration without printing secrets.
Inspects resolved Go configuration without printing secrets. The notify
subcommand reads or updates the permission-prompt alert preference —
run "zero config notify --help" for details.

Flags:
--json Print JSON summary
Expand Down
169 changes: 169 additions & 0 deletions internal/cli/config_notify.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
package cli

import (
"fmt"
"io"
"strings"

"github.com/Gitlawb/zero/internal/config"
)

// runConfigNotify implements `zero config notify`: with no flags it prints the
// current mode/focusMode; --mode/--focus update them via the same
// config.SetNotify writer the TUI /notify command uses, so all surfaces stay
// in lockstep; --reset blanks both fields so the resolver defaults apply.
func runConfigNotify(args []string, stdout io.Writer, stderr io.Writer, deps appDeps) int {
options, help, err := parseConfigNotifyArgs(args)
if err != nil {
return writeExecUsageError(stderr, err.Error())
}
if help {
if err := writeConfigNotifyHelp(stdout); err != nil {
return exitCrash
}
return exitSuccess
}

resolved, exitCode := resolveCommandCenterConfig(stderr, deps)
if exitCode != exitSuccess {
return exitCode
}

if options.mode != "" || options.focus != "" || options.reset {
configPath, err := deps.userConfigPath()
if err != nil {
return writeAppError(stderr, err.Error(), exitCrash)
}
// Seed omitted fields from the USER'S OWN file, never from the
// resolved view: resolved merges project config (so a repo's
// mode:off would be copied into the user's global settings) and
// carries no defaults here, but seeding from it would also pin
// defaults as explicit choices. Blank stays blank — blank means
// "use the built-in defaults". --reset is the only path that
// clears both fields.
current, err := config.UserNotify(configPath)
if err != nil {
return writeAppError(stderr, err.Error(), exitUsage)
}
notify := current
if options.reset {
notify = config.NotifyConfig{}
} else {
if options.mode != "" {
notify.Mode = options.mode
}
if options.focus != "" {
notify.FocusMode = options.focus
}
}
if _, err := config.SetNotify(configPath, notify); err != nil {
return writeAppError(stderr, err.Error(), exitUsage)
}
// Re-resolve so the printed value reflects what the next launch will
// actually use (e.g. a reset shows the built-in defaults).
resolved, exitCode = resolveCommandCenterConfig(stderr, deps)
if exitCode != exitSuccess {
return exitCode
}
}

if options.json {
if err := writePrettyJSON(stdout, map[string]any{
"mode": resolved.Notify.Mode,
"focusMode": resolved.Notify.FocusMode,
}); err != nil {
return exitCrash
}
return exitSuccess
}
lines := []string{
"Notify",
"mode: " + displayCLIValue(resolved.Notify.Mode, "(default)"),
"focusMode: " + displayCLIValue(resolved.Notify.FocusMode, "(default)"),
}
if _, err := fmt.Fprintln(stdout, strings.Join(lines, "\n")); err != nil {
return exitCrash
}
return exitSuccess
}

type configNotifyOptions struct {
mode string
focus string
reset bool
json bool
}

func parseConfigNotifyArgs(args []string) (configNotifyOptions, bool, error) {
options := configNotifyOptions{}
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 arg == "--reset":
options.reset = true
case arg == "--mode":
value, next, err := nextFlagValue(args, index, arg)
if err != nil {
return options, false, err
}
options.mode = value
index = next
case strings.HasPrefix(arg, "--mode="):
value, err := requiredInlineFlagValue(arg, "--mode")
if err != nil {
return options, false, err
}
options.mode = value
case arg == "--focus":
value, next, err := nextFlagValue(args, index, arg)
if err != nil {
return options, false, err
}
options.focus = value
index = next
case strings.HasPrefix(arg, "--focus="):
value, err := requiredInlineFlagValue(arg, "--focus")
if err != nil {
return options, false, err
}
options.focus = value
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 writeConfigNotifyHelp(w io.Writer) error {
_, err := fmt.Fprint(w, "Usage:\n"+
" zero config notify [flags]\n"+
"\n"+
"Print or update the permission-prompt notify preference.\n"+
"\n"+
"When run with no flag, prints the current mode and focusMode; a field you\n"+
"never set shows as (default) — the TUI alerts with bell + notification,\n"+
"firing only when the terminal is unfocused. Omitted flags preserve the\n"+
"values stored in YOUR config file; --reset clears both so the defaults\n"+
"apply again.\n"+
"\n"+
"Examples:\n"+
" zero config notify\n"+
" zero config notify --json\n"+
" zero config notify --mode both --focus unfocused\n"+
" zero config notify --mode off\n"+
" zero config notify --reset # clear config so the resolver defaults apply\n"+
"\n"+
"Flags:\n"+
" --mode <off|bell|notify|both> Notification mechanism\n"+
" --focus <unfocused|always|focused> When the alert fires\n"+
" --reset Clear both fields so the resolver defaults apply\n"+
" --json Machine-readable output\n"+
" -h, --help Show this help\n")
return err
}
Loading