yargs

package module
v1.0.1 Latest Latest
Warning

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

Go to latest
Published: Jan 4, 2026 License: MIT Imports: 10 Imported by: 0

README

yargs

A reflection-based, generic, type-safe CLI parser for Go that reads struct tags and generates help text, with subcommands, grouped commands, and LLM-friendly output.

Table of Contents

Features

  • Type-safe flag parsing with Go generics.
  • Global + subcommand flag separation (including mixed ordering).
  • Positional argument schemas with validation and auto-population.
  • Automatic help text (human and LLM optimized).
  • Command groups ("docker run"-style) and aliases.
  • Flags can appear anywhere; supports -- passthrough.
  • Optional flags via pointer types; defaults via struct tags.
  • Built-in Port type with optional range validation.
  • Partial parsing for "known flags" and consume-only workflows.
  • Registry-based command introspection for advanced dispatching.

Install

go get github.com/shayne/yargs@latest

Quick Start (Single Command)

Use ParseFlags when you have no subcommands.

package main

import (
    "fmt"
    "log"
    "os"

    "github.com/shayne/yargs"
)

type Flags struct {
    Verbose bool   `flag:"verbose" short:"v" help:"Enable verbose output"`
    Output  string `flag:"output" short:"o" help:"Output file"`
}

func main() {
    result, err := yargs.ParseFlags[Flags](os.Args[1:])
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("verbose=%v output=%s args=%v\n", result.Flags.Verbose, result.Flags.Output, result.Args)
}

Subcommands (Global + Command Flags)

Use ParseWithCommand or ParseAndHandleHelp when you have subcommands.

package main

import (
    "context"
    "errors"
    "fmt"
    "os"

    "github.com/shayne/yargs"
)

type GlobalFlags struct {
    Verbose bool `flag:"verbose" short:"v" help:"Enable verbose output"`
}

type StatusFlags struct {
    Short bool `flag:"short" short:"s" help:"Show short status"`
}

type CommitFlags struct {
    Message string `flag:"message" short:"m" help:"Commit message"`
    Amend   bool   `flag:"amend" help:"Amend the last commit"`
}

type CloneFlags struct {
    Depth  int    `flag:"depth" help:"Create a shallow clone with history truncated"`
    Branch string `flag:"branch" short:"b" help:"Checkout a specific branch"`
}

type CloneArgs struct {
    Repo string `pos:"0" help:"Repository URL"`
    Dir  string `pos:"1?" help:"Target directory"`
}

var helpConfig = yargs.HelpConfig{
    Command: yargs.CommandInfo{
        Name:        "git",
        Description: "The stupid content tracker",
    },
    SubCommands: map[string]yargs.SubCommandInfo{
        "status": {Name: "status", Description: "Show working tree status"},
        "commit": {Name: "commit", Description: "Record changes to the repository"},
        "clone":  {Name: "clone", Description: "Clone a repository into a new directory"},
    },
}

func handleStatus(args []string) error {
    return runWithParse[StatusFlags, struct{}](args, func(result *yargs.TypedParseResult[GlobalFlags, StatusFlags, struct{}]) error {
        fmt.Printf("short=%v verbose=%v\n", result.SubCommandFlags.Short, result.GlobalFlags.Verbose)
        return nil
    })
}

func handleCommit(args []string) error {
    return runWithParse[CommitFlags, struct{}](args, func(result *yargs.TypedParseResult[GlobalFlags, CommitFlags, struct{}]) error {
        fmt.Printf("message=%q amend=%v\n", result.SubCommandFlags.Message, result.SubCommandFlags.Amend)
        return nil
    })
}

func handleClone(args []string) error {
    return runWithParse[CloneFlags, CloneArgs](args, func(result *yargs.TypedParseResult[GlobalFlags, CloneFlags, CloneArgs]) error {
        fmt.Printf("repo=%s dir=%s depth=%d branch=%s\n",
            result.Args.Repo,
            result.Args.Dir,
            result.SubCommandFlags.Depth,
            result.SubCommandFlags.Branch,
        )
        return nil
    })
}

func runWithParse[S any, A any](args []string, fn func(*yargs.TypedParseResult[GlobalFlags, S, A]) error) error {
    result, err := yargs.ParseAndHandleHelp[GlobalFlags, S, A](args, helpConfig)
    if errors.Is(err, yargs.ErrShown) {
        return nil
    }
    if err != nil {
        return err
    }
    return fn(result)
}

func main() {
    handlers := map[string]yargs.SubcommandHandler{
        "status": func(_ context.Context, args []string) error { return handleStatus(args) },
        "commit": func(_ context.Context, args []string) error { return handleCommit(args) },
        "clone":  func(_ context.Context, args []string) error { return handleClone(args) },
    }
    if err := yargs.RunSubcommands(context.Background(), os.Args[1:], helpConfig, GlobalFlags{}, handlers); err != nil {
        fmt.Fprintf(os.Stderr, "Error: %v\n", err)
        os.Exit(1)
    }
}

Command Groups ("gh repo" style)

Use RunSubcommandsWithGroups and Group/GroupInfo for grouped commands.

config := yargs.HelpConfig{
    Command: yargs.CommandInfo{Name: "gh", Description: "GitHub CLI"},
    Groups: map[string]yargs.GroupInfo{
        "repo": {
            Name:        "repo",
            Description: "Manage repositories",
            Commands: map[string]yargs.SubCommandInfo{
                "create": {Name: "create", Description: "Create a repository"},
                "view":   {Name: "view", Description: "View a repository"},
            },
        },
        "issue": {
            Name:        "issue",
            Description: "Manage issues",
            Commands: map[string]yargs.SubCommandInfo{
                "list":   {Name: "list", Description: "List issues"},
                "create": {Name: "create", Description: "Create an issue"},
            },
        },
    },
}

groups := map[string]yargs.Group{
    "repo": {
        Description: "Manage repositories",
        Commands: map[string]yargs.SubcommandHandler{
            "create": handleRepoCreate,
            "view":   handleRepoView,
        },
    },
    "issue": {
        Description: "Manage issues",
        Commands: map[string]yargs.SubcommandHandler{
            "list":   handleIssueList,
            "create": handleIssueCreate,
        },
    },
}

_ = yargs.RunSubcommandsWithGroups(context.Background(), os.Args[1:], config, GlobalFlags{}, nil, groups)

Help Generation (Human + LLM)

Yargs can emit human help or LLM-optimized help from the same metadata.

Human help
  • Global: GenerateGlobalHelp
  • Group: GenerateGroupHelp
  • Subcommand: GenerateSubCommandHelp
  • Dispatcher: RunSubcommands and RunSubcommandsWithGroups
LLM help
  • Global: GenerateGlobalHelpLLM
  • Group: GenerateGroupHelpLLM
  • Subcommand: GenerateSubCommandHelpLLM
  • Flags: --help-llm
Parse-and-handle help

ParseWithCommandAndHelp and ParseAndHandleHelp will detect help, -h, --help, and --help-llm and return the right error sentinel. ParseAndHandleHelp prints help automatically and returns ErrShown. The help subcommand is supported as app help or app help <command>.

Help metadata fields

You control help output with these fields:

  • CommandInfo: Name, Description, Examples, LLMInstructions
  • SubCommandInfo: Name, Description, Usage, Examples, Aliases, Hidden, LLMInstructions
  • GroupInfo: Name, Description, Commands, Hidden, LLMInstructions

Flag Tags

type Flags struct {
    Verbose bool   `flag:"verbose" short:"v" help:"Enable verbose output"`
    Output  string `flag:"output" default:"out.txt" help:"Output path"`
    Rate    int    `flag:"rate" help:"Requests per second"`
}

Supported struct tags:

  • flag:"name" - long flag name. Defaults to lowercased field name.
  • short:"x" - single-character alias.
  • help:"text" - help text for auto-generation.
  • default:"value" - default if flag not provided.
  • port:"min-max" - range validation for Port fields.
  • pos:"N" - positional argument schema (see below).

Supported Flag Types

  • string, bool
  • int, int8, int16, int32, int64
  • uint, uint8, uint16, uint32, uint64
  • float32, float64
  • time.Duration
  • url.URL, *url.URL
  • yargs.Port (uint16 alias with optional range validation)
  • Pointers to any of the above (for optional flags)
  • []string (comma-separated or repeated flags)
Optional flags via pointers
type Flags struct {
    Token *string `flag:"token" help:"Optional auth token"`
}

// Token is nil if not provided.
Port validation
type Flags struct {
    HTTPPort  yargs.Port  `flag:"http" port:"1-65535" help:"HTTP port"`
    AdminPort *yargs.Port `flag:"admin" port:"8000-9000" help:"Admin port"`
}

Positional Argument Schemas

Define positional arguments using pos:"N" tags. Yargs will validate counts and populate the struct.

type Args struct {
    Service string   `pos:"0" help:"Service name"`
    Image   string   `pos:"1" help:"Image or payload"`
    Tags    []string `pos:"2*" help:"Optional tags"`
}

Positional tag variants:

  • pos:"0" required argument at index 0
  • pos:"0?" optional argument at index 0
  • pos:"0*" variadic (0 or more) at index 0
  • pos:"0+" variadic (1 or more) at index 0

Aliases

Use Aliases on SubCommandInfo to register alternative command names. ApplyAliases will rewrite the args to canonical names and is used by the built-in dispatchers.

config := yargs.HelpConfig{
    SubCommands: map[string]yargs.SubCommandInfo{
        "status": {Name: "status", Aliases: []string{"st", "stat"}},
    },
}

args := yargs.ApplyAliases(os.Args[1:], config)

Aliases also work for grouped commands (group-local aliases).

Partial Parsing (Known Flags)

When you only want a subset of flags and want to preserve unknown args:

type Flags struct {
    Host string   `flag:"host"`
    Tags []string `flag:"tags" short:"t"`
}

res, err := yargs.ParseKnownFlags[Flags](os.Args[1:], yargs.KnownFlagsOptions{
    SplitCommaSlices: true,
})
// res.Flags contains only known flags; res.RemainingArgs preserves the rest.

Or use the lower-level ConsumeFlagsBySpec:

specs := map[string]yargs.ConsumeSpec{
    "host": {Kind: reflect.String},
    "tags": {Kind: reflect.Slice, SplitComma: true},
}
remaining, values := yargs.ConsumeFlagsBySpec(os.Args[1:], specs)

Registry & Introspection

Use Registry for schema-aware command resolution and positional metadata.

reg := yargs.Registry{
    Command: yargs.CommandInfo{Name: "app"},
    SubCommands: map[string]yargs.CommandSpec{
        "run": {
            Info:       yargs.SubCommandInfo{Name: "run"},
            ArgsSchema: RunArgs{},
        },
    },
}

resolved, ok, err := yargs.ResolveCommandWithRegistry(os.Args[1:], reg)
if ok {
    if spec, ok := resolved.PArg(0); ok {
        fmt.Printf("arg0 name=%s required=%v\n", spec.Name, spec.Required)
    }
}

Remaining Args (--)

Everything after -- is preserved in RemainingArgs.

result, _ := yargs.ParseFlags[Flags]([]string{"-v", "arg1", "--", "--raw", "x"})
// result.Args == []string{"arg1"}
// result.RemainingArgs == []string{"--raw", "x"}

Errors

Yargs exposes structured errors for control flow and diagnostics:

  • ErrHelp, ErrSubCommandHelp, ErrHelpLLM for help requests.
  • ErrShown from ParseAndHandleHelp when it already printed output.
  • InvalidFlagError for unknown flags.
  • InvalidArgsError for bad positional args.
  • FlagValueError for type conversion or validation issues.

ParseAndHandleHelp will print a clean message for users, and if the global flags struct has a Verbose bool field set to true, it will also print the full error chain for FlagValueError.

API Reference

For full, generated API docs, see:

https://pkg.go.dev/github.com/shayne/yargs

Development

This repo uses mise for tool and task management.

mise install
mise run test
Common tasks
  • mise run fmt
  • mise run tidy
  • mise run test
  • mise run check

License

MIT. See LICENSE.

Documentation

Overview

Package yargs provides a flexible, type-safe command-line argument parser with automatic help generation.

The library is designed for building CLIs with subcommands and follows these principles:

  • Type-safe flag parsing using Go generics
  • Zero boilerplate for common use cases
  • Automatic help generation from struct tags
  • Flags can appear anywhere in the command line
  • Consistent and predictable parsing behavior

Basic Usage (Simple CLI)

For a simple CLI without subcommands, use ParseFlags:

type Flags struct {
    Verbose bool   `flag:"verbose" short:"v" help:"Enable verbose output"`
    Output  string `flag:"output" short:"o" help:"Output file"`
}

result, err := yargs.ParseFlags[Flags](os.Args[1:])
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Verbose: %v, Output: %s\n", result.Flags.Verbose, result.Flags.Output)

Subcommand CLI

For CLIs with subcommands, use RunSubcommands with ParseAndHandleHelp in handlers:

type GlobalFlags struct {
    Verbose bool `flag:"verbose" short:"v" help:"Enable verbose output"`
}

type RunFlags struct {
    Detach bool `flag:"detach" short:"d" help:"Run in background"`
}

func handleRun(args []string) error {
    result, err := yargs.ParseAndHandleHelp[GlobalFlags, RunFlags](args, helpConfig)
    if errors.Is(err, yargs.ErrShown) {
        return nil
    }
    if err != nil {
        return err
    }
    // Use result.GlobalFlags and result.SubCommandFlags
    return nil
}

func main() {
    handlers := map[string]yargs.SubcommandHandler{
        "run": handleRun,
    }
    if err := yargs.RunSubcommands(os.Args[1:], helpConfig, GlobalFlags{}, handlers); err != nil {
        fmt.Fprintf(os.Stderr, "Error: %v\n", err)
        os.Exit(1)
    }
}

Flag Syntax

Flags support both long and short forms:

  • Boolean flags: -v, --verbose
  • Flags with values (equals): -o=file.txt, --output=file.txt
  • Flags with values (space): -o file.txt, --output file.txt, -n 10, --lines -5

When using ParseWithCommand or ParseFlags with typed structs, the parser automatically consumes the next argument as the flag's value for non-boolean flags (unless the next argument starts with "-"). Boolean flags never consume the next argument

Supported Types

The following types are supported for flag fields:

  • string, bool
  • int, int8, int16, int32, int64
  • uint, uint8, uint16, uint32, uint64
  • float32, float64
  • time.Duration
  • url.URL, *url.URL
  • Port (uint16 with optional range validation via `port:"min-max"` tag)
  • Pointer types to any of the above (e.g., *int, *bool, *string, *Port)

Pointer types allow distinguishing between "flag not provided" (nil) and "flag provided with value" (non-nil). This is useful for optional settings where you need to preserve existing values if the flag is omitted.

The Port type is a uint16 alias that supports optional range validation. Use the `port:"min-max"` struct tag to specify allowed port ranges:

type Flags struct {
    HTTPPort  Port  `flag:"port" port:"1-65535" help:"HTTP port"`
    AdminPort *Port `flag:"admin-port" port:"8000-9000" help:"Admin port"`
}

Command Registry and Introspection

Use Registry to declare command schemas once and resolve commands without hardcoded lists. This is useful for checking positional requirements.

type RunArgs struct {
    Service string `pos:"0" help:"Service name"`
    Payload string `pos:"1" help:"Payload (file/image)"`
}

type StatusArgs struct {
    Service string `pos:"0?" help:"Optional service name"`
}

reg := yargs.Registry{
    Command: yargs.CommandInfo{Name: "app"},
    SubCommands: map[string]yargs.CommandSpec{
        "run": {Info: yargs.SubCommandInfo{Name: "run"}, ArgsSchema: RunArgs{}},
        "status": {Info: yargs.SubCommandInfo{Name: "status"}, ArgsSchema: StatusArgs{}},
    },
}

res, ok, err := yargs.ResolveCommandWithRegistry(os.Args[1:], reg)
if err != nil {
    log.Fatal(err)
}
if ok {
    if arg0, ok := res.PArg(0); ok {
        fmt.Printf("arg0 name=%s required=%v\n", arg0.Name, arg0.Required)
    }
}

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrHelp is returned when global help is requested (--help, -h, or "help" subcommand)
	ErrHelp = errors.New("help requested")

	// ErrSubCommandHelp is returned when subcommand-specific help is requested
	ErrSubCommandHelp = errors.New("subcommand help requested")

	// ErrHelpLLM is returned when LLM-optimized help is requested (--help-llm)
	ErrHelpLLM = errors.New("llm help requested")

	// ErrShown is returned by ParseAndHandleHelp when help or an error message was displayed.
	// This allows callers to distinguish between "message displayed successfully" and other errors.
	// Callers should treat this as a signal to exit and return nil to the user.
	ErrShown = errors.New("help or error displayed")
)

Sentinel errors for help handling

Functions

func ApplyAliases

func ApplyAliases(args []string, config HelpConfig) []string

ApplyAliases rewrites args to replace any command aliases with their canonical names. It handles both flat subcommands and grouped commands.

func ConsumeFlagsBySpec

func ConsumeFlagsBySpec(args []string, specs map[string]ConsumeSpec) ([]string, map[string][]string)

ConsumeFlagsBySpec removes known flags from args and returns remaining args plus parsed values. Unknown flags (and their following values) are left intact. The "--" separator stops parsing and is preserved in remaining args.

func ExtractGroupAndSubcommand

func ExtractGroupAndSubcommand(args []string) (group, subcommand string)

ExtractGroupAndSubcommand extracts both group and subcommand from command-line arguments. It returns the first two non-flag arguments (excluding "help"). Returns empty strings if not found. Example: ["docker", "run"] -> ("docker", "run") Example: ["status"] -> ("", "status")

func ExtractSubcommand

func ExtractSubcommand(args []string) string

ExtractSubcommand extracts the subcommand name from command-line arguments. It returns the first non-flag argument that isn't "help". Returns empty string if no subcommand is found.

func GenerateGlobalHelp

func GenerateGlobalHelp[G any](config HelpConfig, globalFlagsExample G) string

GenerateGlobalHelp generates and prints the global help message.

func GenerateGlobalHelpLLM

func GenerateGlobalHelpLLM[G any](config HelpConfig, globalFlagsExample G) string

GenerateGlobalHelpLLM generates LLM-optimized help for the entire CLI as structured markdown. This format is designed for LLMs to understand the CLI structure, commands, and their usage.

func GenerateGroupHelp

func GenerateGroupHelp[G any](config HelpConfig, groupName string, globalFlagsExample G) string

GenerateGroupHelp generates and prints help for a specific command group.

func GenerateGroupHelpLLM

func GenerateGroupHelpLLM[G any](config HelpConfig, groupName string, globalFlagsExample G) string

GenerateGroupHelpLLM generates LLM-optimized help for a specific command group as structured markdown.

func GenerateSubCommandHelp

func GenerateSubCommandHelp[G any, S any, A any](config HelpConfig, subCmdName string, globalFlagsExample G, subCmdFlagsExample S, argsExample A) string

GenerateSubCommandHelp generates and prints help for a specific subcommand.

func GenerateSubCommandHelpFromConfig

func GenerateSubCommandHelpFromConfig[G any](config HelpConfig, subCmdName string, globalFlagsExample G) string

GenerateSubCommandHelpFromConfig generates subcommand help using only HelpConfig metadata. It omits subcommand-specific flags and arguments when no structured types are available.

func GenerateSubCommandHelpLLM

func GenerateSubCommandHelpLLM[G any, S any, A any](config HelpConfig, subCmdName string, globalFlagsExample G, subCmdFlagsExample S, argsExample A) string

GenerateSubCommandHelpLLM generates LLM-optimized help for a specific subcommand as structured markdown.

func GenerateSubCommandHelpLLMFromConfig

func GenerateSubCommandHelpLLMFromConfig[G any](config HelpConfig, subCmdName string, globalFlagsExample G) string

GenerateSubCommandHelpLLMFromConfig generates LLM-optimized help for a subcommand using only HelpConfig metadata.

func RunSubcommands

func RunSubcommands[G any](ctx context.Context, args []string, config HelpConfig, globalFlagsExample G, handlers map[string]SubcommandHandler) error

RunSubcommands automatically routes to the appropriate subcommand handler. It handles:

  • Empty args: shows global help
  • Global help flags (--help, -h, help): shows global help
  • Unknown subcommands: returns error with helpful message
  • Known subcommands: calls the registered handler

This eliminates all the boilerplate of manual help handling and routing.

Example:

handlers := map[string]yargs.SubcommandHandler{
    "status": handleStatus,
    "run":    handleRun,
}
if err := yargs.RunSubcommands(os.Args[1:], helpConfig, GlobalFlags{}, handlers); err != nil {
    fmt.Fprintf(os.Stderr, "Error: %v\n", err)
    os.Exit(1)
}

func RunSubcommandsWithGroups

func RunSubcommandsWithGroups[G any](ctx context.Context, args []string, config HelpConfig, globalFlagsExample G, commands map[string]SubcommandHandler, groups map[string]Group) error

RunSubcommandsWithGroups automatically routes to the appropriate subcommand handler, supporting both flat commands and grouped commands (e.g., "docker run"). It handles:

  • Empty args: shows global help
  • Global help flags (--help, -h, help): shows global help
  • Group without subcommand (e.g., "docker"): shows group help
  • Group help flags (e.g., "docker --help"): shows group help
  • Grouped subcommands (e.g., "docker run"): calls the handler from the group
  • Flat subcommands (e.g., "status"): calls the handler from commands map
  • Unknown groups/commands: returns error with helpful message

This allows mixing flat commands with grouped commands for better organization.

Example:

commands := map[string]yargs.SubcommandHandler{
    "status": handleStatus,
    "whoami": handleWhoAmI,
}
groups := map[string]yargs.Group{
    "docker": {
        Description: "Docker-related commands",
        Commands: map[string]yargs.SubcommandHandler{
            "run":  handleDockerRun,
            "ps":   handleDockerPs,
        },
    },
}
if err := yargs.RunSubcommandsWithGroups(ctx, os.Args[1:], config, GlobalFlags{}, commands, groups); err != nil {
    fmt.Fprintf(os.Stderr, "Error: %v\n", err)
    os.Exit(1)
}

Types

type ArgSpec

type ArgSpec struct {
	Position    int
	Name        string
	Type        string
	GoType      reflect.Type
	Description string
	Required    bool
	Variadic    bool
	MinCount    int
	IsSlice     bool
}

ArgSpec describes a positional argument defined by struct tags.

func ArgSpecAt

func ArgSpecAt(schema any, pos int) (ArgSpec, bool)

ArgSpecAt returns the ArgSpec for the given position in the schema, if any.

func ExtractArgSpecs

func ExtractArgSpecs(schema any) []ArgSpec

ExtractArgSpecs extracts positional argument metadata from a struct with `pos` tags. It returns an empty slice if schema is nil or not a struct.

type CommandInfo

type CommandInfo struct {
	Name            string
	Description     string
	Examples        []string
	LLMInstructions string // Additional instructions for LLMs (only shown in --help-llm)
}

CommandInfo contains metadata about the CLI command for help generation.

type CommandSpec

type CommandSpec struct {
	Info       SubCommandInfo
	ArgsSchema any
}

CommandSpec describes a subcommand with optional positional-argument schema.

type ConsumeSpec

type ConsumeSpec struct {
	Kind       reflect.Kind
	SplitComma bool
}

ConsumeSpec describes how to consume a flag and its values from args. Kind is the expected type; bool flags never consume the next argument. SplitComma splits comma-separated values into multiple entries.

type FlagValueError

type FlagValueError struct {
	FlagName   string // The flag that had an invalid value (e.g., "http-port")
	FieldName  string // The struct field name (e.g., "HTTPPort")
	Value      string // The invalid value that was provided
	SubCommand string // The subcommand (if any)
	UserMsg    string // User-friendly error message for display
	Err        error  // Full wrapped error chain for debugging/verbose logging
}

FlagValueError is returned when a flag value cannot be parsed or is invalid. It provides a user-friendly error message while preserving the underlying error for debugging. UserMsg contains the clean user-facing error message, while Err contains the full wrapped error chain.

func (*FlagValueError) Error

func (e *FlagValueError) Error() string

func (*FlagValueError) Unwrap

func (e *FlagValueError) Unwrap() error

type Group

type Group struct {
	Description string
	Commands    map[string]SubcommandHandler // Handlers for commands in this group
}

Group represents a collection of related subcommands with their runtime handlers. A group itself has no handler - it only organizes commands under a common prefix. Groups are used at runtime to route commands like "docker run" where "docker" is the group.

Example:

groups := map[string]yargs.Group{
    "docker": {
        Description: "Docker container management",
        Commands: map[string]yargs.SubcommandHandler{
            "run":  handleDockerRun,
            "ps":   handleDockerPs,
        },
    },
}

type GroupInfo

type GroupInfo struct {
	Name            string
	Description     string
	Commands        map[string]SubCommandInfo // Commands within this group
	Hidden          bool                      // Hidden groups don't appear in help but still work
	LLMInstructions string                    // Additional instructions for LLMs (only shown in --help-llm)
}

GroupInfo contains metadata about a command group for help generation. Unlike Group, GroupInfo contains SubCommandInfo structs instead of handlers, and is used purely for generating help text. The GroupInfo should be included in HelpConfig.Groups and must match the structure of the Group.

Example:

Groups: map[string]yargs.GroupInfo{
    "docker": {
        Name:        "docker",
        Description: "Docker container management",
        Commands: map[string]yargs.SubCommandInfo{
            "run": {Name: "run", Description: "Run a container"},
            "ps":  {Name: "ps", Description: "List containers"},
        },
    },
}

type GroupSpec

type GroupSpec struct {
	Info     GroupInfo
	Commands map[string]CommandSpec
}

GroupSpec describes a group of commands with optional positional-argument schemas.

type HelpConfig

type HelpConfig struct {
	Command     CommandInfo
	SubCommands map[string]SubCommandInfo // Flat subcommands (status, whoami, etc.)
	Groups      map[string]GroupInfo      // Command groups (docker, etc.)
}

HelpConfig contains all metadata needed for help generation.

type InvalidArgsError

type InvalidArgsError struct {
	Expected   string // "1", "1-3", "at least 1", "0 or more"
	Got        int
	SubCommand string // The subcommand that had invalid args (if any)
}

InvalidArgsError is returned when the wrong number of positional arguments is provided.

func (*InvalidArgsError) Error

func (e *InvalidArgsError) Error() string

type InvalidFlagError

type InvalidFlagError struct {
	Flag       string
	SubCommand string // The subcommand that had an invalid flag (if any). Empty if the flag appeared before the subcommand was identified.
}

InvalidFlagError is returned when an unknown/undefined flag is encountered.

func (*InvalidFlagError) Error

func (e *InvalidFlagError) Error() string

type KnownFlagsOptions

type KnownFlagsOptions struct {
	SplitCommaSlices bool
}

KnownFlagsOptions controls how ParseKnownFlags handles slices.

type KnownFlagsResult

type KnownFlagsResult[T any] struct {
	Flags         T
	RemainingArgs []string
}

KnownFlagsResult contains parsed flags plus remaining args.

func ParseKnownFlags

func ParseKnownFlags[T any](args []string, opts KnownFlagsOptions) (*KnownFlagsResult[T], error)

ParseKnownFlags parses only the flags defined by T and leaves unknown flags intact. It returns the remaining args with known flags removed.

type ParseResult

type ParseResult[T any] struct {
	*Parser
	Flags T
}

ParseResult combines the parsed flags with the Parser for access to other fields.

func ParseFlags

func ParseFlags[T any](args []string) (*ParseResult[T], error)

ParseFlags parses command-line arguments and returns a typed struct with parsed flags. The struct fields should have a `flag:"name"` tag to specify the flag name. Optionally use `short:"x"` tag for short flag aliases. Supported types: string, bool, int, int64, uint, uint64, float64, time.Duration, url.URL, *url.URL Pointer types (*int, *bool, *string, etc.) are also supported for optional flags - they remain nil if not specified

Example:

type Flags struct {
    Verbose    bool   `flag:"verbose" short:"v"`
    ControlURL string `flag:"control-url"`
    Port       int    `flag:"port"`
}

result, err := yargs.ParseFlags[Flags](os.Args[1:])

type Parser

type Parser struct {
	// SubCommand is the sub-command name (first non-flag argument)
	SubCommand string
	// Args contains positional arguments to the sub-command
	Args []string
	// GlobalFlags contains global flags parsed from anywhere in the command line
	GlobalFlags map[string]string
	// SubCommandFlags contains sub-command specific flags
	SubCommandFlags map[string]string
	// Flags contains all flags (global and sub-command) for backward compatibility
	Flags map[string]string
	// RemainingArgs contains all arguments after "--" as a slice (preserves argument boundaries)
	RemainingArgs []string
}

Parser represents a parsed command line.

func (*Parser) GetFlag

func (p *Parser) GetFlag(name string) string

GetFlag returns the value of a flag, or an empty string if not set.

func (*Parser) HasFlag

func (p *Parser) HasFlag(name string) bool

HasFlag returns true if a flag was set.

type Port

type Port uint16

Port is a uint16 type alias for IP ports with optional range validation. When used in struct fields, you can optionally specify allowed port ranges using the `port:"min-max"` struct tag. If no range is specified, all ports 0-65535 are allowed.

Examples:

type Flags struct {
    HTTPPort  Port  `flag:"port" port:"1-65535" help:"HTTP port (excludes port 0)"`
    AdminPort *Port `flag:"admin" port:"8000-9000" help:"Admin port (restricted range)"`
}

Note: Range validation only applies to flag fields, not positional arguments.

type Registry

type Registry struct {
	Command     CommandInfo
	SubCommands map[string]CommandSpec
	Groups      map[string]GroupSpec
}

Registry provides a schema-aware command registry independent of help config.

func (Registry) CommandSpec

func (r Registry) CommandSpec(path []string) (CommandSpec, bool)

CommandSpec returns the CommandSpec for the given command path.

func (Registry) HelpConfig

func (r Registry) HelpConfig() HelpConfig

HelpConfig returns a HelpConfig derived from the registry.

type ResolvedCommand

type ResolvedCommand struct {
	Path []string
	Info SubCommandInfo
	Args []string
	// ArgsSchema is an optional schema (struct with `pos` tags) used for introspection.
	ArgsSchema any
}

ResolvedCommand describes a resolved subcommand and the remaining args. Path is ["subcommand"] or ["group", "subcommand"] and Args excludes the command tokens.

func ResolveCommand

func ResolveCommand(args []string, config HelpConfig) (ResolvedCommand, bool, error)

ResolveCommand resolves a subcommand (including grouped commands) using the same alias and parsing rules as the dispatcher. It returns the resolved command path and the remaining args with the command tokens removed.

func ResolveCommandWithRegistry

func ResolveCommandWithRegistry(args []string, reg Registry) (ResolvedCommand, bool, error)

ResolveCommandWithRegistry resolves a command using the registry and attaches the args schema.

func (ResolvedCommand) PArg

func (r ResolvedCommand) PArg(pos int) (ArgSpec, bool)

PArg returns the positional argument spec at the given index, if available.

type SubCommandInfo

type SubCommandInfo struct {
	Name            string
	Description     string
	Usage           string // e.g., "SERVICE" or "SERVICE [SERVICE...]"
	Examples        []string
	Aliases         []string
	Hidden          bool   // Hidden subcommands don't appear in help but still work
	LLMInstructions string // Additional instructions for LLMs (only shown in --help-llm)
}

SubCommandInfo contains metadata about a subcommand for help generation.

type SubcommandHandler

type SubcommandHandler func(context.Context, []string) error

SubcommandHandler is a function that handles a subcommand. It receives the original args (including the subcommand name) and returns an error if the command fails.

type TypedParseResult

type TypedParseResult[G any, S any, A any] struct {
	*Parser
	GlobalFlags     G
	SubCommandFlags S
	Args            A
	HelpText        string // Contains generated help text if help was requested
}

TypedParseResult combines the parsed flags and args with the Parser for access to other fields.

func ParseAndHandleHelp

func ParseAndHandleHelp[G any, S any, A any](args []string, config HelpConfig) (*TypedParseResult[G, S, A], error)

ParseAndHandleHelp parses command-line arguments and automatically handles help output. If help is requested, it prints the help text to stdout and returns (nil, ErrShown). This eliminates the need for manual help checking in command handlers.

When a FlagValueError occurs and the global flags struct has a "Verbose" field set to true, detailed error information is displayed including the flag name, value, subcommand, and full error chain.

Returns:

  • (result, nil) on successful parse
  • (nil, ErrShown) if help was displayed (caller should treat as success)
  • (nil, error) if parsing failed

Usage:

result, err := yargs.ParseAndHandleHelp[GlobalFlags, RunFlags, RunArgs](args, config)
if errors.Is(err, yargs.ErrShown) {
    return nil // Help was shown, exit successfully
}
if err != nil {
    return err // Actual error
}
// Use result.GlobalFlags, result.SubCommandFlags, and result.Args

func ParseWithCommand

func ParseWithCommand[G any, S any, A any](args []string) (*TypedParseResult[G, S, A], error)

ParseWithCommand parses command-line arguments and returns typed structs for global flags, subcommand flags, and positional args. The generic type parameters define which flags are global vs subcommand-specific via struct tags. Panics if the same flag name appears in both G and S types.

func ParseWithCommandAndHelp

func ParseWithCommandAndHelp[G any, S any, A any](args []string, config HelpConfig) (*TypedParseResult[G, S, A], error)

ParseWithCommandAndHelp parses command-line arguments with automatic help generation. If help is requested (via --help, -h, or help subcommand), it generates the help text and returns it in the HelpText field along with the appropriate error (ErrHelp or ErrSubCommandHelp).

Jump to

Keyboard shortcuts

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