byob-go-cli

--yes / -y destructive-action override

byob-prompter.5 command-shapeprompter

Problem: destructive actions (delete, purge, overwrite) need confirmation when run interactively and must be bypassable in CI scripts. Tools that invent their own flag (--force, --no-confirm, --non-interactive) end up with three inconsistent flags across subcommands.

Idea: one flag, --yes (short -y), on any command that does a destructive action. When set, Confirm() returns true without prompting. Documented convention across the tool — every destroyer reads the same flag.

--yes is NOT a persistent root flag, because not every command is destructive. It's a per-command flag, but the flag name and semantics are template-mandated so users see a uniform surface.

Tradeoffs: users who want a single "assume yes to everything" knob have to learn that it's per-command. That's correct — a persistent --yes would silently confirm destructive side-effects in subcommands the user didn't intend to flag.

Relationship to ErrNotTTY (byob-prompter.3): --yes short-circuits the TTY check. A non-interactive run with --yes is the supported, correct flow. A non-interactive run without --yes returns ErrNotTTY and fails with a clear message.

Design

type DeleteOptions struct {
    // ...
    Prompter prompt.Prompter
    Yes      bool
}

func NewCmdDelete(f *Factory, runF func(context.Context, *DeleteOptions) error) *cobra.Command {
    opts := &DeleteOptions{Prompter: f.Prompter}
    cmd := &cobra.Command{
        Use:   "delete <name>",
        Short: "Delete an item (prompts unless --yes)",
        RunE: func(c *cobra.Command, args []string) error {
            if runF != nil { return runF(c.Context(), opts) }
            return deleteRun(c.Context(), opts)
        },
    }
    cmd.Flags().BoolVarP(&opts.Yes, "yes", "y", false, "skip confirmation prompt")
    return cmd
}

func deleteRun(ctx context.Context, opts *DeleteOptions) error {
    if !opts.Yes {
        ok, err := opts.Prompter.Confirm(ctx, "Delete permanently?", false)
        if errors.Is(err, prompt.ErrNotTTY) {
            return fmt.Errorf("refusing to delete without --yes in non-interactive mode")
        }
        if err != nil { return err }
        if !ok { return ErrCancel }
    }
    // ...perform delete
}