cli/docs/pkg/cli/prompts.md
Snider 7be4e243f2
Some checks failed
Deploy / build (push) Failing after 4s
Security Scan / security (push) Failing after 13s
docs: add human-friendly documentation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 13:02:39 +00:00

3.9 KiB

title description
Interactive Prompts Text prompts, confirmations, single and multi-select menus, and type-safe generic selectors.

Interactive Prompts

The framework provides several prompt functions, ranging from simple text input to type-safe generic selectors.

Text Prompt

Basic text input with an optional default:

name, err := cli.Prompt("Project name", "my-app")
// Project name [my-app]: _
// Returns default if user presses Enter

Question (Enhanced Prompt)

Question extends Prompt with validation and required-input support:

name := cli.Question("Enter your name:")
name := cli.Question("Enter your name:", cli.WithDefault("Anonymous"))
name := cli.Question("Enter your name:", cli.RequiredInput())

With validation:

port := cli.Question("Port:", cli.WithValidator(func(s string) error {
    n, err := strconv.Atoi(s)
    if err != nil || n < 1 || n > 65535 {
        return fmt.Errorf("must be 1-65535")
    }
    return nil
}))

Grammar-composed question:

name := cli.QuestionAction("rename", "old.txt")
// Rename old.txt? _

Confirmation

Yes/no confirmation with sensible defaults:

if cli.Confirm("Delete file?") { ... }             // Default: no  [y/N]
if cli.Confirm("Save?", cli.DefaultYes()) { ... }   // Default: yes [Y/n]
if cli.Confirm("Destroy?", cli.Required()) { ... }  // Must type y or n [y/n]

With auto-timeout:

if cli.Confirm("Continue?", cli.Timeout(30*time.Second)) { ... }
// Continue? [y/N] (auto in 30s)
// Auto-selects default after timeout

Combine options:

if cli.Confirm("Deploy?", cli.DefaultYes(), cli.Timeout(10*time.Second)) { ... }
// Deploy? [Y/n] (auto in 10s)

Grammar-Composed Confirmation

if cli.ConfirmAction("delete", "config.yaml") { ... }
// Delete config.yaml? [y/N]

if cli.ConfirmAction("save", "changes", cli.DefaultYes()) { ... }
// Save changes? [Y/n]

Dangerous Action (Double Confirmation)

if cli.ConfirmDangerousAction("delete", "production database") { ... }
// Delete production database? [y/n]    (must type y/n)
// Really delete production database? [y/n]

Single Select

Numbered menu, returns the selected string:

choice, err := cli.Select("Choose backend:", []string{"metal", "rocm", "cpu"})
// Choose backend:
//   1. metal
//   2. rocm
//   3. cpu
// Choose [1-3]: _

Multi Select

Space-separated number input, returns selected strings:

tags, err := cli.MultiSelect("Enable features:", []string{"auth", "api", "admin", "mcp"})
// Enable features:
//   1. auth
//   2. api
//   3. admin
//   4. mcp
// Choose (space-separated) [1-4]: _

Type-Safe Generic Select (Choose)

For selecting from typed slices with custom display:

type File struct {
    Name string
    Size int64
}

files := []File{{Name: "a.go", Size: 1024}, {Name: "b.go", Size: 2048}}

choice := cli.Choose("Select a file:", files,
    cli.Display(func(f File) string {
        return fmt.Sprintf("%s (%d bytes)", f.Name, f.Size)
    }),
)

With a default selection:

choice := cli.Choose("Select:", items, cli.WithDefaultIndex[Item](0))
// Items marked with * are the default when Enter is pressed

Grammar-composed:

file := cli.ChooseAction("select", "file", files)
// Select file:
//   1. ...

Type-Safe Generic Multi-Select (ChooseMulti)

Select multiple items with ranges:

selected := cli.ChooseMulti("Select files:", files,
    cli.Display(func(f File) string { return f.Name }),
)
// Select files:
//   1. a.go
//   2. b.go
//   3. c.go
// Enter numbers (e.g., 1 3 5 or 1-3) or empty for none: _

Input formats:

  • 1 3 5 -- select items 1, 3, and 5
  • 1-3 -- select items 1, 2, and 3
  • 1 3-5 -- select items 1, 3, 4, and 5
  • (empty) -- select none

Grammar-composed:

files := cli.ChooseMultiAction("select", "files", allFiles)

Testing Prompts

Override stdin for testing:

cli.SetStdin(strings.NewReader("test input\n"))
defer cli.SetStdin(os.Stdin)