wizard

package module
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 15, 2026 License: MIT Imports: 13 Imported by: 1

README

cli-wizard-core

CI Release Go Version License

Reusable core primitives for interactive CLI wizards in Go.

Behavioral Contract

This library defines shared wizard behavior contracts used by consumer repos.

What it provides

  • Session: draft lifecycle orchestration (load/start/stop/finalize)
  • RunSteps: deterministic step runner with start/done callbacks
  • Manage: generic create/edit/delete/default action flow for list-based resources
  • FormatMenuLabel: aligned two-column menu labels ([tag] + text)
  • FormatActionLabel / ActionVerb: aligned action-menu labels (Create/Edit/...) + robust action dispatch
  • Colorize: ANSI color wrapper for labels/messages
  • BackLabel / BackMenuLabel / IsBackChoice: consistent Back UX primitives
  • ExitLabel / ExitMenuLabel / IsExitChoice: consistent Exit UX primitives
  • SelectHint: standard selector hint ([Use arrows to move, type to filter, Esc=Back, Ctrl+C=Exit])
  • NewUserError / WithHint: attach actionable hints to user-facing errors
  • FormatCLIError: consistent colored error+hint output for CLIs
  • ReconcileWithTemplate: generic template sync for config objects (added/removed/missing-required report)

The package is intentionally transport-agnostic: no direct dependency on survey/readline/TTY, no provider-specific logic (VMware, Talos, etc.).

Install

go get github.com/infrakit-io/cli-wizard-core

Usage

import wizard "github.com/infrakit-io/cli-wizard-core"

session := wizard.NewSession(
    targetPath,
    draftPath,
    &state,
    isEmpty,
    loadDraftFn,
    startDraftFn,
    finalizeFn,
)

_ = wizard.RunSteps([]wizard.Step{
    {Name: "Step 1", Run: step1},
    {Name: "Step 2", Run: step2},
}, onStart, onDone)

template := map[string]any{
    "vm": map[string]any{
        "name":       "",
        "profile":    "talos",
        "ip_address": "",
    },
}
current := map[string]any{
    "vm": map[string]any{
        "name":     "cp-01",
        "username": "",
    },
}

out, report, err := wizard.ReconcileWithTemplate(current, template, wizard.ReconcileOptions{
    DropUnknown:   true,
    RequiredPaths: []string{"vm.name", "vm.ip_address"},
})
_ = out
_ = report
_ = err

Documentation

Index

Constants

View Source
const (
	ANSIReset  = "\033[0m"
	ANSIRed    = "\033[31m"
	ANSIYellow = "\033[33m"
	ANSICyan   = "\033[36m"
)

Variables

View Source
var ErrInterrupted = errors.New("wizard interrupted")

ErrInterrupted is the shared sentinel used by wizard consumers to propagate Ctrl+C / prompt interruption consistently.

Functions

func ActionVerb

func ActionVerb(text string) string

ActionVerb returns the first word from an action label (after ANSI normalization).

func BackLabel

func BackLabel() string

BackLabel returns a consistently colored "Back" label.

func BackMenuLabel

func BackMenuLabel(width int) string

BackMenuLabel renders a colored Back label aligned like menu entries.

func CleanupDrafts added in v0.3.0

func CleanupDrafts(targetPath string) error

CleanupDrafts removes all draft files for the given target path.

func Colorize

func Colorize(text, color string) string

Colorize wraps a string with an ANSI color code and resets formatting. Pass empty color to keep the text unchanged.

func DefaultRunSteps added in v0.3.0

func DefaultRunSteps(steps []Step) error

DefaultRunSteps executes steps with standard N/M progress formatting. Named steps print "N/M name" on start; a blank line separates non-final steps.

func DraftTargetToken added in v0.3.0

func DraftTargetToken(targetPath string) string

DraftTargetToken converts a target file path to a safe filename token. Uses path→"__" conversion to preserve directory context. Example: "configs/prod/node.yaml" → "configs__prod__node.yaml"

func DrainStdin added in v0.3.0

func DrainStdin()

DrainStdin discards bytes pending in stdin (e.g. cursor-position responses left by interactive rendering in raw mode). Safe to call from non-TTY contexts.

func ErrorHint

func ErrorHint(err error) string

ErrorHint extracts a hint from an error if available.

func ExitLabel

func ExitLabel() string

ExitLabel returns a normalized "Exit" label.

func ExitMenuLabel

func ExitMenuLabel(width int) string

ExitMenuLabel renders an Exit label aligned like menu entries.

func FormatActionLabel

func FormatActionLabel(text string, verbWidth int) string

FormatActionLabel aligns action menus as two columns: verb + description. "Back"/"Exit" remain plain.

func FormatCLIError

func FormatCLIError(err error) string

FormatCLIError renders a colored CLI error and optional hint.

func FormatMenuLabel

func FormatMenuLabel(tag, text string, width int) string

FormatMenuLabel renders a two-column menu label: [tag] + aligned text. width controls the fixed width for the [tag] column; values <= 0 default to 12.

func IsBackChoice

func IsBackChoice(value string) bool

IsBackChoice checks whether a selected value maps to Back.

func IsCancelChoice

func IsCancelChoice(value string) bool

IsCancelChoice checks whether a selected value maps to Cancel.

func IsExitChoice

func IsExitChoice(value string) bool

IsExitChoice checks whether a selected value maps to Exit.

func IsInterrupted

func IsInterrupted(err error) bool

IsInterrupted reports whether err is or wraps ErrInterrupted.

func IsPlaceholderValue added in v0.2.3

func IsPlaceholderValue(v any) bool

IsPlaceholderValue returns true if v is empty, nil, or contains "CHANGE_ME".

func LatestDraftForTarget added in v0.3.0

func LatestDraftForTarget(targetPath string) string

LatestDraftForTarget returns the path of the most-recently-modified draft for the given target, or empty string if none exist.

func LoadDraftYAML added in v0.3.0

func LoadDraftYAML(draftPath string, out any) (bool, error)

LoadDraftYAML reads a draft file and unmarshals it into out. Returns (true, nil) on success, (false, nil) if draftPath is empty, and (false, err) on read or parse failure.

func MultiSelectHint added in v0.3.0

func MultiSelectHint() string

MultiSelectHint returns the standard keyboard hint for multi-select prompts.

func NewUserError

func NewUserError(message, hint string) error

NewUserError creates a user-facing error message with an optional hint.

func NormalizeChoice

func NormalizeChoice(value string) string

NormalizeChoice strips ANSI colors and trims whitespace for robust comparisons.

func RestoreTTY added in v0.3.0

func RestoreTTY()

RestoreTTY best-effort restores terminal state. Call from signal handlers or os.Exit paths that bypass deferred term.Restore calls.

func RunSteps

func RunSteps(steps []Step, onStepStart func(index, total int, name string), onStepDone func(index, total int)) error

RunSteps executes steps in order and reports transitions through callbacks.

func SelectHint

func SelectHint() string

SelectHint returns the standard selector help hint used across wizard UIs.

func WithHint

func WithHint(err error, hint string) error

WithHint wraps an existing error with a user-facing hint.

func WriteDraft added in v0.3.0

func WriteDraft(targetPath string, plaintext []byte) (string, error)

WriteDraft writes plaintext YAML to tmp/<token>.draft.<timestamp>.yaml. Creates the tmp/ directory if needed. Returns the draft file path.

Types

type ManageAction

type ManageAction string
const (
	ManageCreate     ManageAction = "create"
	ManageEdit       ManageAction = "edit"
	ManageDelete     ManageAction = "delete"
	ManageSetDefault ManageAction = "set_default"
	ManageCancel     ManageAction = "cancel"
)

type ManageOptions

type ManageOptions[T any] struct {
	Items         []T
	ItemLabel     func(item T) string
	Select        func(options []string, defaultOption, message string) string
	MainMessage   string
	ItemMessage   string
	DefaultLabel  string
	CreateLabel   string
	EditLabel     string
	DeleteLabel   string
	SetDefaultLbl string
	CancelLabel   string
}

type ManageSelection

type ManageSelection[T any] struct {
	Action ManageAction
	Item   *T
}

func Manage

func Manage[T any](opts ManageOptions[T]) (ManageSelection[T], error)

type ReconcileOptions

type ReconcileOptions struct {
	// DropUnknown removes keys not present in template.
	DropUnknown bool
	// RequiredPaths are dot-notation keys that must exist after reconcile.
	RequiredPaths []string
	// CheckPlaceholders, when true, checks required paths for empty/CHANGE_ME values.
	CheckPlaceholders bool
}

ReconcileOptions controls template sync behavior.

type ReconcileReport

type ReconcileReport struct {
	Added             []string
	Removed           []string
	MissingRequired   []string
	PlaceholderValues []string
}

ReconcileReport summarizes changes and validation findings.

func ReconcileWithTemplate

func ReconcileWithTemplate(current, template map[string]any, opts ReconcileOptions) (map[string]any, ReconcileReport, error)

ReconcileWithTemplate syncs a config object against a template object. It keeps existing values for keys defined in template, fills missing keys from template defaults, and optionally removes unknown keys.

type Selector

type Selector struct {
	// MaxVisible limits items visible at once. 0 means show all.
	MaxVisible int
	// contains filtered or unexported fields
}

Selector provides an interactive arrow-key selection widget for terminal UIs.

func NewSelector

func NewSelector() *Selector

NewSelector creates a Selector with sensible defaults (MaxVisible = 10).

func (*Selector) Confirm added in v0.3.0

func (s *Selector) Confirm(message string, defaultYes bool) bool

Confirm displays an interactive Y/N prompt. Returns true for yes, false for no. On Ctrl+C, returns false and sets the interrupted flag (check WasInterrupted()). On ESC, returns false (not interrupted). On non-TTY stdin, returns defaultYes without prompting.

func (*Selector) MultiSelect added in v0.3.0

func (s *Selector) MultiSelect(items []string, defaults []string, message string) []string

MultiSelect displays an interactive checkbox list. Arrow keys navigate, Space toggles selection, Enter confirms. Ctrl+C/ESC returns defaults and sets interrupted (Ctrl+C only). Items are returned in original (not selection) order. On non-TTY stdin, returns defaults without prompting.

func (*Selector) Select

func (s *Selector) Select(items []string, defaultItem, message string) string

Select displays an interactive menu with arrow-key navigation, type-to-filter, and optional scrolling. Returns the selected item. On ESC or Ctrl+C, returns the preferred cancel option (Back > Exit > Cancel) if present, or empty string. Call WasInterrupted() to check for Ctrl+C.

Signature is compatible with ManageOptions.Select.

func (*Selector) WasInterrupted

func (s *Selector) WasInterrupted() bool

WasInterrupted returns true if the last Select ended with Ctrl+C.

type Session

type Session struct {
	TargetPath string
	DraftPath  string
	State      any
	IsEmpty    func() bool
	// contains filtered or unexported fields
}

Session manages a wizard draft lifecycle with pluggable IO handlers.

func NewSession

func NewSession(
	targetPath, draftPath string,
	state any,
	isEmpty func() bool,
	loadDraftFn func(draftPath string, state any) (bool, error),
	startFn func(targetPath, draftPath string, state any, isEmpty func() bool) func(),
	finalizeFn func(targetPath string) error,
) *Session

NewSession creates a reusable wizard session orchestrator.

func (*Session) Finalize

func (s *Session) Finalize() error

Finalize removes stale drafts for target.

func (*Session) LoadDraft

func (s *Session) LoadDraft() (bool, error)

LoadDraft loads draft state when available.

func (*Session) Start

func (s *Session) Start()

Start installs draft handling for this session.

func (*Session) Stop

func (s *Session) Stop()

Stop restores process state after Start.

type Step

type Step struct {
	Name string
	Run  func() error
}

Step defines one wizard action.

type UserError

type UserError struct {
	Message string
	HintMsg string
}

UserError is a user-facing error with an optional actionable hint.

func (*UserError) Error

func (e *UserError) Error() string

func (*UserError) Hint

func (e *UserError) Hint() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL