cli

package
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: May 1, 2026 License: MIT Imports: 22 Imported by: 0

Documentation

Overview

Package cli wires cobra commands, config loading, env/flag overrides, and top-level error-to-exit-code mapping. Thin transport layer: business logic lives in internal/config, internal/prompt, internal/client, and internal/render.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrUsage   = errors.New("usage error")
	ErrConfig  = errors.New("config error")
	ErrFile    = errors.New("file error")
	ErrNetwork = errors.New("network error")
	ErrAPI     = errors.New("api error")
	ErrTimeout = errors.New("timeout error")
)

Sentinel base errors for each category — wrapped so callers can use errors.Is.

Functions

func APIBody

func APIBody(err error) string

APIBody walks the error chain and returns the first available API response body.

func FormatError

func FormatError(w io.Writer, err error, verbose int)

FormatError renders an error for stderr in the canonical `askit: <category>: <detail> (exit <N>)` form (FR-060/061). When verbose > 0, an additional indented "hint:" line is emitted with any hint supplied via Hint(err). When verbose >= 2 and the error is in the API category, the truncated body from APIBody(err) is also emitted.

func Hint

func Hint(err error) string

Hint walks the error chain and returns the first available hint string.

func NewAPIErr

func NewAPIErr(format string, args ...any) error

NewAPIErr wraps a non-2xx API response (after retry exhaustion) as exit-6.

func NewConfigErr

func NewConfigErr(format string, args ...any) error

NewConfigErr wraps a configuration-level failure (malformed YAML, failed validation, unknown preset, unresolved required field) as exit-3.

func NewFileErr

func NewFileErr(format string, args ...any) error

NewFileErr wraps a file-reference failure (missing, oversize, unreadable under the active unknown-strategy) as exit-4.

func NewLogger

func NewLogger(verbose int) *slog.Logger

NewLogger builds a stderr slog.Logger whose level maps from the stacked `-v` count: 0 → Error, 1 → Info, 2+ → Debug (FR-090).

func NewLoggerTo

func NewLoggerTo(w io.Writer, verbose int) *slog.Logger

NewLoggerTo is the same as NewLogger but writes to an arbitrary io.Writer (primarily for tests).

func NewNetworkErr

func NewNetworkErr(format string, args ...any) error

NewNetworkErr wraps an HTTP transport failure (connection refused, DNS, TLS, connection reset) as exit-5.

func NewTimeoutErr

func NewTimeoutErr(format string, args ...any) error

NewTimeoutErr wraps a deadline or cancel event as exit-7.

func NewUsageErr

func NewUsageErr(format string, args ...any) error

NewUsageErr wraps a usage violation (unknown flag, missing prompt, bad -o target, TTY required, …) as exit-2.

func ReadPrompt

func ReadPrompt(args []string, stdin io.Reader, stdinIsTTY bool) (string, error)

ReadPrompt resolves the user prompt from the three documented sources per FR-002:

  • A positional argument (possibly the explicit `-` stdin marker).
  • stdin, when stdin is not a terminal.
  • Both: stdin prepended to the positional argument, separated by a blank line.

Using `-` AND a separate positional prompt AND piped stdin at the same time is ambiguous and returns a usage error.

isTTY is injected so tests can exercise every branch without touching a real terminal.

func StdinIsTTY

func StdinIsTTY() bool

StdinIsTTY reports whether os.Stdin is connected to a terminal. The real CLI uses this as the isTTY argument to ReadPrompt.

func WrapCategorized

func WrapCategorized(cat Category, code ExitCode, err error) error

WrapCategorized lets callers attach a Category/ExitCode to an already-typed error without double-wrapping when it's already a *CategorizedError.

Types

type AtomicWriter

type AtomicWriter struct {
	// contains filtered or unexported fields
}

AtomicWriter writes to a temporary sibling file and, on Commit, atomically renames it over the target (FR-006). Writer failures trigger automatic cleanup via Rollback.

func OpenOutput

func OpenOutput(path string, force bool) (*AtomicWriter, error)

OpenOutput creates an AtomicWriter for path. If the target already exists as a regular file and force is false, returns a usage-level error (exit 2). Parent directories are created automatically.

func (*AtomicWriter) Close

func (a *AtomicWriter) Close() error

Close is an io.Closer helper that defers to Rollback. Callers should pair OpenOutput with `defer w.Close()` and an explicit Commit.

func (*AtomicWriter) Commit

func (a *AtomicWriter) Commit() error

Commit closes the temp file and atomically renames it over the target. After a successful Commit, subsequent calls are no-ops.

func (*AtomicWriter) Rollback

func (a *AtomicWriter) Rollback() error

Rollback removes the temp file. Safe to call after Commit (it's a no-op then) or before any Write (removes an empty file).

func (*AtomicWriter) Write

func (a *AtomicWriter) Write(p []byte) (int, error)

Write writes data to the temp file.

type BodyCarrier

type BodyCarrier interface {
	APIResponseBody() string
}

BodyCarrier is implemented by API errors that carry an upstream response body for inclusion under -vv.

type CategorizedError

type CategorizedError struct {
	Cat  Category
	Code ExitCode
	Err  error
}

CategorizedError is an error with an attached ExitCode and Category so the top-level mapper can render the correct stderr line and choose the correct exit code.

func (*CategorizedError) Error

func (c *CategorizedError) Error() string

Error renders the wrapped error's message; category / exit code are applied by the outermost renderer in errout.go.

func (*CategorizedError) Unwrap

func (c *CategorizedError) Unwrap() error

Unwrap exposes the wrapped error for errors.Is / errors.As.

type Category

type Category string

Category is the short tag ("config", "file", "endpoint", "api", …) that appears after "askit:" in stderr output (FR-060).

const (
	CatUsage    Category = "usage"
	CatConfig   Category = "config"
	CatFile     Category = "file"
	CatEndpoint Category = "endpoint"
	CatAPI      Category = "api"
	CatTimeout  Category = "timeout"
	CatGeneric  Category = ""
)

Canonical categories.

func CategoryOf

func CategoryOf(err error) Category

CategoryOf walks an error chain and returns the first embedded Category. Errors without a category map to CatGeneric.

type ExitCode

type ExitCode int

ExitCode is the process exit code taxonomy defined by contracts/cli-surface.md.

const (
	ExitOK      ExitCode = 0
	ExitGeneric ExitCode = 1
	ExitUsage   ExitCode = 2
	ExitConfig  ExitCode = 3
	ExitFile    ExitCode = 4
	ExitNetwork ExitCode = 5
	ExitAPI     ExitCode = 6
	ExitTimeout ExitCode = 7
)

Stable exit codes — part of the tool's public contract.

func CodeOf

func CodeOf(err error) ExitCode

CodeOf walks an error chain and returns the first embedded ExitCode. Unrecognized errors map to ExitGeneric.

func Execute

func Execute(args []string) ExitCode

Execute parses argv and dispatches to the matching subcommand, returning the ExitCode the process should surface. Centralizing the error→exit mapping here keeps cmd/askit/main.go a one-liner.

type Globals

type Globals struct {
	ConfigPath string // from -c or ASKIT_CONFIG
	Endpoint   string // from -e or ASKIT_ENDPOINT
	Model      string // from -m or ASKIT_MODEL
	APIKey     string // from --api-key or ASKIT_API_KEY
	NoColor    bool   // from --no-color or NO_COLOR
	Verbose    int    // from -v (stackable)
	// contains filtered or unexported fields
}

Globals holds the values of the global (root-level) flags plus the env vars that parallel them. Populated by [harvestGlobals] at PersistentPreRun time so that downstream subcommands all see the same resolved view.

func (*Globals) EnvOverrides

func (g *Globals) EnvOverrides() config.Overrides

EnvOverrides returns the Overrides view of env-var values that should be layered after the explicit config file but before flag overrides.

func (*Globals) FlagOverrides

func (g *Globals) FlagOverrides() config.Overrides

FlagOverrides returns the Overrides view of flag-supplied values that should be layered last.

type Hinter

type Hinter interface {
	Hint() string
}

Hinter is implemented by errors that carry a verbose-only hint line.

type PresetFlags

type PresetFlags struct {
	System      *string
	Temperature *float64
	TopP        *float64
	MaxTokens   *int
	Seed        *int
	Stream      *bool
	Output      *config.OutputFormat
	Model       *string
}

PresetFlags is the subset of explicit CLI flags relevant to preset resolution. Pointer fields distinguish "flag was set" from "default zero value"; nil fields fall through to the preset or config.

type ResolvedPreset

type ResolvedPreset struct {
	Name        string
	System      string
	Temperature float64
	TopP        float64
	MaxTokens   int
	Seed        *int
	Stream      bool
	Output      config.OutputFormat
	Model       string
}

ResolvedPreset is the effective set of overrides after merging a named preset on top of config.Defaults and below explicit flags. All fields are concrete (no pointers) so the downstream request builder doesn't need to re-check layered nils.

func ResolvePreset

func ResolvePreset(cfg *config.Config, name string, flags PresetFlags) (ResolvedPreset, error)

ResolvePreset composes the effective ResolvedPreset for a request by layering: config.Defaults → preset[name] → flags. Passing an empty name skips the preset layer entirely.

When name is non-empty but not found in cfg.Presets, returns a typed ConfigError enumerating the available preset names (FR-033).

Jump to

Keyboard shortcuts

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