Documentation
¶
Index ¶
- Constants
- Variables
- func ColumnWidths(rows [][]string, minWidths []int, maxWidths []int) []int
- func ConfirmDangerousOperation(operationName, exactMatch string, force bool) error
- func ConfirmUninstall(repoName string, force bool) error
- func ConfirmYesNo(prompt string, defaultYes bool) bool
- func DangerousOperationWarning(operation, target string)
- func FormatRow(row []string, widths []int) string
- func FormatSlogLine(line string) string
- func FormatTipText(tip string) string
- func HasMultipleEndpoints(sageoxDir string) bool
- func InputWithDefault(title, defaultVal string) (string, error)
- func IsHeadless() bool
- func IsInteractive() bool
- func IsSilent(err error) bool
- func OpenInBrowser(url string) error
- func PrintActionHint(command, description string, step int)
- func PrintDisclaimer()
- func PrintError(msg string)
- func PrintHint(msg string)
- func PrintInfo(msg string)
- func PrintJSON(v any)
- func PrintPreserved(msg string)
- func PrintSectionBreak()
- func PrintSubsectionBreak()
- func PrintSuccess(msg string)
- func PrintSuggestionBox(title, message, fix string)
- func PrintTip(tip string)
- func PrintWarning(msg string)
- func SelectEndpoint(endpoints []EndpointInfo, defaultEndpoint, flagEndpoint string) (string, error)
- func SelectOne(title string, options []string, defaultIdx int) (int, error)
- func SelectOneValue[T comparable](title string, options []SelectOption[T], defaultIdx int) (T, error)
- func SetJSONMode(enabled bool)
- func SetNoInteractive(enabled bool)
- func ShowUninstallPreview(items []RemovalItem, dryRun bool)
- func StreamFormattedLogs(r io.Reader, w io.Writer)
- func SuggestionBox(title, message, fix string) string
- func TruncateID(id string, verbose bool) string
- func TruncateUUID(uuid string, verbose bool) string
- func WithSpinner[T any](message string, fn func() (T, error)) (T, error)
- func WithSpinnerNoResult(message string, fn func() error) error
- func Wordmark() string
- type Context
- func (c *Context) Fprintf(w *os.File, format string, args ...any)
- func (c *Context) IsJSON() bool
- func (c *Context) IsNoInteractive() bool
- func (c *Context) IsQuiet() bool
- func (c *Context) IsReview() bool
- func (c *Context) IsText() bool
- func (c *Context) IsVerbose() bool
- func (c *Context) LogDebug(msg string, args ...any)
- func (c *Context) LogError(msg string, args ...any)
- func (c *Context) LogInfo(msg string, args ...any)
- func (c *Context) LogWarn(msg string, args ...any)
- func (c *Context) PrintError(msg string)
- func (c *Context) PrintPreserved(msg string)
- func (c *Context) PrintSuccess(msg string)
- func (c *Context) PrintWarning(msg string)
- func (c *Context) Printf(format string, args ...any)
- func (c *Context) Println(msg string)
- func (c *Context) Shutdown()
- func (c *Context) TrackCommandCompletion(cmd *cobra.Command)
- func (c *Context) TrackCommandError(cmd *cobra.Command, err error)
- type EndpointInfo
- type RemovalItem
- type SelectOption
- type SilentError
- type SlogLine
- type SpinnerResult
Examples ¶
Constants ¶
const ( // SectionBreak adds visual separation between major sections SectionBreak = "\n" // SubsectionBreak adds minimal separation within sections SubsectionBreak = "" )
Section spacing for CLI output
Variables ¶
var ( ColorPrimary = theme.ColorPrimary ColorSecondary = theme.ColorSecondary ColorAccent = theme.ColorAccent ColorSuccess = theme.ColorSuccess ColorWarning = theme.ColorWarning ColorError = theme.ColorError ColorInfo = theme.ColorInfo ColorDim = theme.ColorDim ColorPublic = theme.ColorPublic ColorPrivate = theme.ColorPrivate )
Re-export theme colors for backward compatibility
var ( StyleBrand = lipgloss.NewStyle(). Foreground(ColorPrimary). Bold(true) StyleSecondary = lipgloss.NewStyle(). Foreground(ColorSecondary) StyleAccent = lipgloss.NewStyle(). Foreground(ColorAccent) StyleSuccess = lipgloss.NewStyle(). Foreground(ColorSuccess) StyleWarning = lipgloss.NewStyle(). Foreground(ColorWarning) StyleError = lipgloss.NewStyle(). Foreground(ColorError) StyleInfo = lipgloss.NewStyle(). Foreground(ColorInfo) StyleDim = lipgloss.NewStyle(). Foreground(ColorDim) StyleBold = lipgloss.NewStyle(). Bold(true) StyleCommand = lipgloss.NewStyle(). Foreground(ColorSecondary) StyleFlag = lipgloss.NewStyle(). Foreground(ColorInfo) StyleGroupHeader = lipgloss.NewStyle(). Foreground(ColorPrimary). Bold(true) // StyleFile for file/directory paths (uses accent color for visual distinction) StyleFile = lipgloss.NewStyle(). Foreground(ColorAccent) // SpinnerStyle for spinner animations SpinnerStyle = lipgloss.NewStyle(). Foreground(ColorPrimary) // StyleCallout for contextual highlights (stars, step indicators, guided actions) // Used to draw attention to recommended next actions based on user state StyleCallout = lipgloss.NewStyle(). Foreground(ColorAccent) // StyleCalloutBold for emphasized callout elements (command names in guided actions) StyleCalloutBold = lipgloss.NewStyle(). Foreground(ColorSecondary). Bold(true) // Visibility styles (public=teal, private=amber) StylePublic = lipgloss.NewStyle(). Foreground(ColorPublic) StylePrivate = lipgloss.NewStyle(). Foreground(ColorPrivate) )
Text styles
var ErrHeadless = errors.New("cannot open browser: no graphical display available (SSH or headless environment)")
ErrHeadless is returned by OpenInBrowser when the environment has no graphical display (SSH session, no DISPLAY). Callers should handle this to provide context-appropriate guidance (e.g., upload to view online, or copy the file).
var ErrSilent = SilentError{}
ErrSilent is a sentinel error indicating output was already printed.
var Styles = struct { Success lipgloss.Style Preserved lipgloss.Style Error lipgloss.Style Warning lipgloss.Style Info lipgloss.Style Hint lipgloss.Style Code lipgloss.Style }{ Success: successStyle, Preserved: preservedStyle, Error: errorStyle, Warning: warningStyle, Info: infoStyle, Hint: hintStyle, Code: codeStyle, }
Styles exposes lipgloss styles for use in commands
Functions ¶
func ColumnWidths ¶
ColumnWidths calculates optimal widths for columns based on content
func ConfirmDangerousOperation ¶
ConfirmDangerousOperation is a generic confirmation prompt for destructive operations Requires user to type exactMatch string to proceed Returns error if user cancels or input doesn't match
Example ¶
Example demonstrates generic dangerous operation confirmation
package main
import (
"fmt"
"github.com/sageox/ox/internal/cli"
)
func main() {
// for operations where you want user to type a specific string
operation := "delete all production data"
confirmText := "DELETE-PROD"
if err := cli.ConfirmDangerousOperation(operation, confirmText, false); err != nil {
fmt.Println("Operation canceled")
return
}
fmt.Println("User confirmed, proceeding...")
}
Output:
func ConfirmUninstall ¶
ConfirmUninstall prompts user to confirm a destructive uninstall operation Returns error if user cancels or input fails, nil if confirmed Bypasses prompt if force is true
Example ¶
Example demonstrates how to use the uninstall confirmation flow
package main
import (
"fmt"
"github.com/sageox/ox/internal/cli"
)
func main() {
// in actual use, these would come from scanning the repository
items := []cli.RemovalItem{
{
Type: "directory",
Path: ".sageox",
Description: "SageOx state and configuration",
},
{
Type: "file",
Path: "AGENTS.md",
Description: "agent guidance document",
},
{
Type: "file",
Path: ".claude/CLAUDE.md",
Description: "Claude-specific guidance",
},
{
Type: "hook",
Path: ".git/hooks/pre-commit",
Description: "git pre-commit hook",
},
}
// show what will be removed
cli.ShowUninstallPreview(items, false)
// display warning
cli.DangerousOperationWarning("UNINSTALL SAGEOX", "my-awesome-repo")
// get confirmation (would use force flag from command line)
force := false // in real code: cmd.Flags().GetBool("force")
if err := cli.ConfirmUninstall("my-awesome-repo", force); err != nil {
fmt.Println("Uninstall canceled")
return
}
fmt.Println("Proceeding with uninstall...")
// ... actual uninstall logic here ...
}
Output:
func ConfirmYesNo ¶
ConfirmYesNo displays a yes/no prompt and loops until valid input. Returns true if user confirms with y/yes, false for n/no. Empty input uses the default specified by defaultYes.
Example ¶
Example demonstrates yes/no prompts
package main
import (
"fmt"
"github.com/sageox/ox/internal/cli"
)
func main() {
// simple yes/no with default to No (safer for destructive ops)
if cli.ConfirmYesNo("Remove user-level config too?", false) {
fmt.Println("Will remove user config")
}
// with default to Yes (for non-destructive confirmations)
if cli.ConfirmYesNo("Install recommended plugins?", true) {
fmt.Println("Will install plugins")
}
}
Output:
func DangerousOperationWarning ¶
func DangerousOperationWarning(operation, target string)
DangerousOperationWarning displays a styled warning box for destructive operations
func FormatSlogLine ¶ added in v0.4.0
FormatSlogLine parses a raw slog log line and returns a colorized version. Malformed lines are returned as-is.
func FormatTipText ¶
FormatTipText formats tip text by highlighting backtick-wrapped commands
func HasMultipleEndpoints ¶
HasMultipleEndpoints is a quick check for whether endpoint selection is needed.
func InputWithDefault ¶
InputWithDefault prompts for text input with a placeholder showing the default. If user enters empty string, returns the default value. Falls back to simple text prompt when stdin is not a TTY or in non-interactive mode.
func IsHeadless ¶
func IsHeadless() bool
IsHeadless returns true when no graphical display is available. Detects SSH sessions and missing display servers so callers can skip browser-open calls that would launch broken text-mode browsers (lynx, w3m).
func IsInteractive ¶
func IsInteractive() bool
IsInteractive returns true if interactive mode is enabled. Interactive mode is disabled when --no-interactive flag is set, CI=true, or stdin is not a terminal (e.g., running inside an AI agent).
func OpenInBrowser ¶
OpenInBrowser opens a URL in the user's default browser. Returns ErrHeadless if the environment is headless (SSH, no display). Silently returns nil when SKIP_BROWSER=1 (used by automated tests and demo scripts to suppress browser popups during non-interactive runs). This is the single entry point for browser-opening across the CLI.
func PrintActionHint ¶
PrintActionHint prints a prominent actionable hint with star, command, and optional step. Matches the visual styling used in help output for contextual next-action guidance. Example: "★ ox login Authenticate with SageOx (Step 1)"
func PrintDisclaimer ¶
func PrintDisclaimer()
PrintDisclaimer prints the Claude Code compatibility disclaimer in brand gold.
func PrintError ¶
func PrintError(msg string)
func PrintHint ¶
func PrintHint(msg string)
PrintHint prints a dimmed hint message (e.g., "Run 'ox login' to authenticate")
func PrintPreserved ¶
func PrintPreserved(msg string)
PrintPreserved prints a message indicating an existing file was preserved (not overwritten). Uses cyan color to distinguish from green success (created) messages.
func PrintSectionBreak ¶
func PrintSectionBreak()
PrintSectionBreak prints consistent spacing between sections
func PrintSubsectionBreak ¶
func PrintSubsectionBreak()
PrintSubsectionBreak prints minimal spacing within sections
func PrintSuccess ¶
func PrintSuccess(msg string)
func PrintSuggestionBox ¶
func PrintSuggestionBox(title, message, fix string)
PrintSuggestionBox prints a bordered suggestion box to stderr.
func PrintTip ¶
func PrintTip(tip string)
PrintTip prints a helpful tip message with command highlighting
func PrintWarning ¶
func PrintWarning(msg string)
func SelectEndpoint ¶
func SelectEndpoint(endpoints []EndpointInfo, defaultEndpoint, flagEndpoint string) (string, error)
SelectEndpoint prompts the user to select an endpoint when multiple are available. If only one endpoint exists, returns it without prompting. If flagEndpoint is provided, validates it exists and returns it. Returns the selected endpoint URL.
func SelectOne ¶
SelectOne displays an interactive selection menu with arrow key navigation. Returns the index of the selected option (0-based) or -1 if canceled. Falls back to numbered prompt when stdin is not a TTY or in non-interactive mode.
func SelectOneValue ¶
func SelectOneValue[T comparable](title string, options []SelectOption[T], defaultIdx int) (T, error)
SelectOneValue displays an interactive selection menu and returns the selected value.
func SetJSONMode ¶
func SetJSONMode(enabled bool)
func SetNoInteractive ¶
func SetNoInteractive(enabled bool)
SetNoInteractive sets the global non-interactive mode flag. When enabled, spinners and TUI elements are disabled.
func ShowUninstallPreview ¶
func ShowUninstallPreview(items []RemovalItem, dryRun bool)
ShowUninstallPreview displays what will be removed, grouped by type Shows summary first, then details if items exist
Example (DryRun) ¶
Example demonstrates dry-run mode
package main
import (
"github.com/sageox/ox/internal/cli"
)
func main() {
items := []cli.RemovalItem{
{Type: "directory", Path: ".sageox", Description: "state directory"},
{Type: "file", Path: "AGENTS.md", Description: "guidance"},
}
// dry-run mode shows what would be removed without doing it
cli.ShowUninstallPreview(items, true)
}
Output:
func StreamFormattedLogs ¶ added in v0.4.0
StreamFormattedLogs reads log lines from r, colorizes each, and writes to w. Blocks until r is closed or an error occurs.
func SuggestionBox ¶
SuggestionBox renders a bordered box with a title, message, and fix command. Used for actionable suggestions that need visual emphasis.
func TruncateID ¶
TruncateID shortens an ID for display, keeping prefix and suffix Examples:
- "oxsid_01JBCDEFGHIJKLMNOPQRSTUVWXYZ" -> "oxsid_01JB...WXYZ" (verbose=false)
- "oxsid_01JBCDEFGHIJKLMNOPQRSTUVWXYZ" -> "oxsid_01JBCDEFGHIJKLMNOPQRSTUVWXYZ" (verbose=true)
- "Oxa7b3" -> "Oxa7b3" (already short)
Returns full ID if verbose=true or ID is already short (<=16 chars).
func TruncateUUID ¶
TruncateUUID shortens a UUID for display Examples:
- "a1b2c3d4-e5f6-7890-abcd-ef1234567890" -> "a1b2c3d4..." (verbose=false)
- "a1b2c3d4-e5f6-7890-abcd-ef1234567890" -> "a1b2c3d4-e5f6-7890-abcd-ef1234567890" (verbose=true)
Returns full UUID if verbose=true or UUID is already short (<=12 chars).
func WithSpinner ¶
WithSpinner runs an async function with a spinner displayed. Shows spinner after a short delay to avoid flicker for fast operations. In non-interactive mode (CI or --no-interactive), runs without spinner. Returns the result of the function.
func WithSpinnerNoResult ¶
WithSpinnerNoResult runs an async function with a spinner displayed. Use when the function returns only an error.
Types ¶
type Context ¶
type Context struct {
// Config holds the loaded configuration
Config *config.Config
// Logger provides structured logging
Logger *slog.Logger
// Telemetry client for tracking command usage
TelemetryClient *telemetry.Client
// CommandStartTime tracks when the command started (for telemetry)
CommandStartTime time.Time
// Ctx provides the Go context for cancellation and timeouts
Ctx context.Context
}
Context holds common dependencies for CLI commands. It provides centralized initialization and access to shared resources like configuration, logging, and telemetry.
func NewContext ¶
NewContext creates a new CLI context from a Cobra command. This centralizes all the initialization logic that was previously scattered across PersistentPreRunE in root.go.
func (*Context) IsNoInteractive ¶
IsNoInteractive returns true if non-interactive mode is enabled (CI or --no-interactive)
func (*Context) PrintError ¶
PrintError prints an error message
func (*Context) PrintPreserved ¶
PrintPreserved prints a preserved/unchanged message (unless quiet mode)
func (*Context) PrintSuccess ¶
PrintSuccess prints a success message (unless quiet mode)
func (*Context) PrintWarning ¶
PrintWarning prints a warning message (unless quiet mode)
func (*Context) Shutdown ¶
func (c *Context) Shutdown()
Shutdown performs cleanup operations. Should be called in PersistentPostRunE or deferred.
func (*Context) TrackCommandCompletion ¶
TrackCommandCompletion tracks a successful command completion via telemetry
type EndpointInfo ¶
EndpointInfo holds information about an endpoint from a repo marker
func DiscoverEndpoints ¶
func DiscoverEndpoints(sageoxDir string) ([]EndpointInfo, error)
DiscoverEndpoints scans .sageox/ for .repo_* marker files and returns unique endpoints found. Returns endpoints sorted by their first appearance.
type RemovalItem ¶
type RemovalItem struct {
Type string // e.g., "directory", "file", "hook"
Path string // relative path from repo root
Description string // brief description of what this is
}
RemovalItem represents an item to be removed during uninstall
type SelectOption ¶
SelectOption represents a single option in a selection menu.
type SilentError ¶
type SilentError struct{}
SilentError is an error type that signals the error was already displayed. main.go checks for this and skips printing "Error:" prefix.
func (SilentError) Error ¶
func (e SilentError) Error() string
type SlogLine ¶ added in v0.4.0
type SlogLine struct {
Time string // raw timestamp value
Level string // DEBUG, INFO, WARN, ERROR
Message string // msg value (unquoted)
Attrs string // remaining key=value pairs
}
SlogLine holds parsed fields from a slog TextHandler line.
func ParseSlogLine ¶ added in v0.4.0
ParseSlogLine parses a slog TextHandler line into its components. Returns false if the line doesn't match the expected format.
type SpinnerResult ¶
SpinnerResult holds the result of an async operation