cli

package
v0.0.0-...-1defb3b Latest Latest
Warning

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

Go to latest
Published: Oct 2, 2026 License: MIT Imports: 21 Imported by: 0

Documentation

Index

Constants

View Source
const (
	ActionCreated      = "created"
	ActionUpdated      = "updated"
	ActionSkipped      = "skipped"
	ActionCommented    = "commented"
	ActionTransitioned = "transitioned"
	ActionLinked       = "linked"
	ActionUnlinked     = "unlinked"
	ActionWatched      = "watched"
	ActionUnwatched    = "unwatched"
	ActionAttached     = "attached"
	ActionDownloaded   = "downloaded"
	ActionUploaded     = "uploaded"
	ActionLabeled      = "labeled"
	ActionUnlabeled    = "unlabeled"
	ActionWikiLinked   = "wiki-linked"
	ActionWikiUnlinked = "wiki-unlinked"
)

Action constants identify mutations in JSON output.

View Source
const (
	SummaryTruncate    = 55  // issue summary next to key + status/due columns
	CommentBriefTrunc  = 120 // one-line comment in smart-view / `jcli comments`
	CommentInlineTrunc = 60  // `jcli recent` reason rows (tighter — beside date + author)
	LinkSummaryTrunc   = 50  // printLinks summary column
	HistoryValueTrunc  = 40  // printHistory before/after values
	WikiExcerptTrunc   = 80  // wiki search result snippet
)

Shared display truncation widths.

View Source
const DefaultBoardFilter = jira.NotDoneJQL

DefaultBoardFilter applies when defaults.board_filter is empty or absent.

View Source
const EnvelopeVersion = "2"

EnvelopeVersion identifies the JSON schema; bump it for breaking changes.

View Source
const MaxParallelFetches = 10

MaxParallelFetches limits concurrent API calls within a fetch operation.

Variables

View Source
var ErrHelpShown = errors.New("help shown")

ErrHelpShown means help was printed to stderr. Return it unchanged so main exits successfully.

View Source
var ErrUnknownCommand = errors.New("unknown command")

ErrUnknownCommand means neither the command index nor Fallback matched. Dispatch has already printed usage in human mode.

Functions

func ApplyEnvOverrides

func ApplyEnvOverrides(paths Paths, cc *jira.ClientConfig) error

func Fatal

func Fatal(format string, args ...any)

Fatal prints "error: <msg>" to stderr and exits with status 1.

func HelpFirstLine

func HelpFirstLine(help, fallback string) string

HelpFirstLine returns the first line of help, or fallback when help is empty.

func LoadConfig

func LoadConfig(paths Paths) (jira.ClientConfig, AppConfig, *ConfigFile, error)

LoadConfig reads configuration, applies environment overrides, and validates credentials. The returned raw config is nil when no file exists.

func LooksLikeIssueKey

func LooksLikeIssueKey(s string) bool

LooksLikeIssueKey reports whether s is shaped like a Jira issue key.

func MarshalConfig

func MarshalConfig(cf ConfigFile) ([]byte, error)

MarshalConfig serializes cf as pretty-printed JSON with a trailing newline.

func NewGlobalFlagSet

func NewGlobalFlagSet(jsonMode, debug, version, helpRequested *bool) *flag.FlagSet

NewGlobalFlagSet defines leading global flags and discards parser output.

func Parse

func Parse(env *Env, args []string, setup func(*flag.FlagSet)) (*flag.FlagSet, error)

Parse accepts flags anywhere before "--" and enforces the dispatcher's minimum positional count. Read positionals from FlagSet.Args. setup may be nil for commands without flags. Help returns ErrHelpShown.

func PersistCloudID

func PersistCloudID(paths Paths, effectiveSite, cloudID string, raw *ConfigFile)

PersistCloudID saves a discovered ID for the configured site only. It does nothing if raw is nil or already has a cloud ID.

func PrintJSON

func PrintJSON(w io.Writer, v any) error

PrintJSON writes indented JSON with deterministic key ordering.

func ReadTextInput

func ReadTextInput(bodyFile string, tail []string, stdin io.Reader) (text string, provided bool, err error)

ReadTextInput reads a body file, explicit or piped stdin, or joined positional text. Files and stdin are trimmed. A file and positional text conflict; provided=false means there was no input source.

func ReadTokenFile

func ReadTokenFile(paths Paths, tokenFile string) (string, error)

ReadTokenFile reads and trims a token, warning if group or other permissions are set.

func ResolveEnvToken

func ResolveEnvToken(paths Paths) (string, error)

ResolveEnvToken prefers JIRA_TOKEN to JIRA_TOKEN_FILE; neither set returns ("", nil).

func ResolveToken

func ResolveToken(paths Paths, cf ConfigFile) (string, error)

ResolveToken prefers the inline token to TokenFile. It returns "" if neither is set.

func StdinIsPiped

func StdinIsPiped() bool

StdinIsPiped reports whether stdin is a pipe or redirected file, not a TTY.

func Usage

func Usage(w io.Writer, paths Paths, d *Dispatcher)

Usage prints top-level help using the dispatcher's command list.

func ValidateSDConfig

func ValidateSDConfig(cfg AppConfig, paths Paths) error

ValidateSDConfig checks the settings required by RequireSD commands.

Types

type AppConfig

type AppConfig struct {
	SDProjects      []string
	BoardID         string
	BoardFilter     string
	ChecklistField  string
	FormsCountField string
}

AppConfig holds command settings, separate from HTTP client configuration.

func (AppConfig) ChecklistAvailable

func (cfg AppConfig) ChecklistAvailable() bool

ChecklistAvailable reports whether a checklist field is configured.

func (AppConfig) ChecklistFields

func (cfg AppConfig) ChecklistFields() string

ChecklistFields returns "summary" plus the configured checklist field, if any.

func (AppConfig) IsSDProject

func (cfg AppConfig) IsSDProject(issueKey string) bool

IsSDProject matches the issue key's project against SDProjects, ignoring case.

type Cmd

type Cmd struct {
	MinArgs int // minimum positionals the handler requires
	Flags   CmdFlags
	Desc    string // one-line description for Usage listing
	Help    string // help text; first line is used in usage errors

	// Subcommands routes a command group; when set, Run is ignored.
	Subcommands *Dispatcher

	Run func(ctx context.Context, env *Env, args []string) error
}

Cmd describes a subcommand. Run receives raw arguments and must call Parse to parse flags and enforce MinArgs.

func (Cmd) IsGroup

func (c Cmd) IsGroup() bool

IsGroup reports whether cmd carries a nested subcommand table.

type CmdFlags

type CmdFlags uint8

CmdFlags declares leaf-command requirements. Zero requires a client but no SD config.

const (
	// NoClient skips connection setup.
	NoClient CmdFlags = 1 << iota
	// RequireSD requires valid service-desk configuration after connecting.
	RequireSD
)

type ConfigDefaults

type ConfigDefaults struct {
	Board           string   `json:"board,omitzero"`
	BoardFilter     string   `json:"board_filter,omitzero"`
	SDProjects      []string `json:"sd_projects,omitzero"`
	ChecklistField  string   `json:"checklist_field,omitzero"`
	FormsCountField string   `json:"forms_count_field,omitzero"`
}

ConfigDefaults holds board and service-desk settings.

type ConfigFile

type ConfigFile struct {
	Site    string `json:"site"`
	CloudID string `json:"cloud_id,omitzero"`
	Email   string `json:"email"`
	// Token stores an inline API token. init defaults to a separate token file.
	Token     string `json:"token,omitzero"`
	TokenFile string `json:"token_file,omitzero"`
	// Scoped selects gateway URLs and Bearer auth for a scoped API token.
	Scoped   bool           `json:"scoped,omitzero"`
	Defaults ConfigDefaults `json:"defaults,omitzero"`
}

ConfigFile is the config.json format.

func LoadConfigFile

func LoadConfigFile(paths Paths) (*ConfigFile, error)

LoadConfigFile reads the on-disk configuration without opening token files. Credential precedence is resolved separately by LoadConfig.

type Dispatcher

type Dispatcher struct {
	Name      string         // for errors and usage ("jcli" or "jcli wiki")
	Prefix    string         // Env.Command prefix ("" or "wiki ")
	Index     map[string]Cmd // subcommand name → spec
	Shortcuts []ShortcutDoc  // rendered in usage before Index

	Fallback Fallback
	Usage    func(w io.Writer)
}

Dispatcher routes top-level or nested commands using a shared Env.

func (*Dispatcher) Dispatch

func (d *Dispatcher) Dispatch(ctx context.Context, env *Env, args []string) error

Dispatch matches a command case-insensitively, connects if needed, checks requirements, and invokes its handler or nested dispatcher.

func (*Dispatcher) WriteCommandList

func (d *Dispatcher) WriteCommandList(w io.Writer)

WriteCommandList writes aligned shortcuts followed by alphabetical commands.

type EmitOpts

type EmitOpts struct {
	Data       any            // payload; nil for pure mutations
	Action     string         // "commented", "created", "skipped", ...
	Key        string         // target resource identifier
	Meta       map[string]any // non-essential context
	Pagination *Pagination    // cursor-paginated results
	Human      func()         // human-mode closure (writes to env.Stdout)
}

EmitOpts configures a single emission. Zero value is valid.

type Env

type Env struct {
	Client   *jira.Client // nil until Connect runs; NoClient handlers never get one
	Cfg      AppConfig
	Paths    Paths
	Stdout   io.Writer
	Stderr   io.Writer
	JSONMode bool
	// Connect loads Client and Cfg when the dispatcher first needs them.
	Connect func(ctx context.Context) error
	// Command is the dispatched path (e.g. "wiki page"), included in JSON envelopes.
	Command string
	// contains filtered or unexported fields
}

Env holds per-run state and routes command output to text or JSON.

func (*Env) Emit

func (e *Env) Emit(opts EmitOpts) error

Emit writes a JSON envelope to Stdout or calls opts.Human in text mode.

func (*Env) EmitJSON

func (e *Env) EmitJSON(opts EmitOpts) error

EmitJSON unconditionally writes the v2 envelope to env.Stdout. Use for commands that always emit JSON regardless of --json (schema, forms raw).

func (*Env) EmitJSONError

func (e *Env) EmitJSONError(err error)

EmitJSONError writes an error envelope, validation details, and pending warnings to Stderr. The caller sets the exit code.

func (*Env) EmitList

func (e *Env) EmitList(items any, key string, meta map[string]any, human func()) error

EmitList is the shorthand for a list response.

func (*Env) EmitListOrEmpty

func (e *Env) EmitListOrEmpty[T any](items []T, key, resource string, render func([]T)) error

EmitListOrEmpty renders non-empty lists in human mode and reports empty ones on stderr. JSON mode always emits an array, including [] for empty lists.

func (*Env) EmitMutation

func (e *Env) EmitMutation(action, key string, meta map[string]any, human func()) error

EmitMutation is the shorthand for a mutation with no refetched data.

func (*Env) EmitRefreshed

func (e *Env) EmitRefreshed[T RawJSONer](ctx context.Context, action, key, humanMsg string, meta map[string]any, fetch func(context.Context) (T, error)) error

EmitRefreshed refetches and emits raw JSON in JSON mode. Text mode prints humanMsg only.

func (*Env) EmitSkipped

func (e *Env) EmitSkipped(key, reason string) error

EmitSkipped reports a no-op with action="skipped" and a successful exit.

func (*Env) Empty

func (e *Env) Empty(msg string)

Empty writes an empty-result message to stderr.

func (*Env) EmptyKey

func (e *Env) EmptyKey(key, resource string)

EmptyKey writes "KEY: no RESOURCE" to stderr for commands scoped to a specific issue, page, or project.

func (*Env) Notice

func (e *Env) Notice(format string, args ...any)

Notice records a warning for the next JSON envelope or prints it immediately in human mode. Calls are mutex-protected; handlers normally call it after joins.

type Envelope

type Envelope struct {
	Version    string         `json:"version"`
	Command    string         `json:"command"`
	Data       jsontext.Value `json:"data,omitzero"`
	Action     string         `json:"action,omitzero"`
	Key        string         `json:"key,omitzero"`
	Pagination *Pagination    `json:"pagination,omitzero"`
	Meta       map[string]any `json:"meta,omitzero"`
	Warnings   []string       `json:"warnings,omitzero"`
	Error      *ErrorDetail   `json:"error,omitzero"`
}

Envelope wraps JSON command output. Error identifies failure; Data is omitted on failure and for mutations without a payload.

type ErrKind

type ErrKind string

ErrKind is the envelope error taxonomy. Consumers should treat unknown values as ErrKindClient (non-retryable).

const (
	ErrKindNotFound    ErrKind = "not_found"
	ErrKindAuth        ErrKind = "auth"
	ErrKindRateLimited ErrKind = "rate_limited"
	ErrKindServer      ErrKind = "server"
	ErrKindClient      ErrKind = "client"
	ErrKindNetwork     ErrKind = "network"
	ErrKindValidation  ErrKind = "validation"
	ErrKindCancelled   ErrKind = "cancelled"
)

type ErrorDetail

type ErrorDetail struct {
	Kind      ErrKind `json:"kind"`
	Retryable bool    `json:"retryable"`
	Hint      string  `json:"hint,omitzero"`
	Message   string  `json:"message"`
}

ErrorDetail classifies a failure. Retryable is advisory; automatic retries have already finished when this is emitted.

func ClassifyError

func ClassifyError(err error) ErrorDetail

ClassifyError checks wrapped errors, defaulting to non-retryable client errors. Validation takes precedence over HTTP errors; context and HTTP checks precede net.Error because their types can also satisfy that interface.

type Fallback

type Fallback func(args []string) (rewritten []string, handled bool, err error)

Fallback rewrites an unknown command. handled=true dispatches the new arguments; false leaves the command unknown.

func KeyShortcut

func KeyShortcut(detect func(string) bool, prepend, appendTo []string) Fallback

KeyShortcut prepends and appends tokens when detect matches args[0], preserving all input arguments. For example, a bare key becomes "view KEY --full".

type FieldRecord

type FieldRecord struct {
	AllowedValues []string `json:"allowed_values,omitzero"`
	FieldID       string   `json:"field_id"`
	Name          string   `json:"name,omitzero"`
	Required      bool     `json:"required,omitzero"`
	SetExample    string   `json:"set_example,omitzero"`
	Supplied      any      `json:"supplied,omitzero"`
	Type          string   `json:"type,omitzero"`
}

FieldRecord describes a validation failure and, when available, a suggested fix.

type InputSource

type InputSource int

InputSource identifies where ReadTextInput will read from.

const (
	InputNone  InputSource = iota
	InputFile              // --body-file
	InputStdin             // explicit "-" arg or piped/redirected stdin
	InputArgs              // positional args joined with spaces
)

func ClassifyInput

func ClassifyInput(bodyFile string, nArg int, arg0 string, piped bool) (InputSource, error)

ClassifyInput selects an input source without I/O. A file and positional text conflict.

type MultiFlag

type MultiFlag []string

MultiFlag implements flag.Value for repeatable string flags (e.g. --set).

func (*MultiFlag) Set

func (m *MultiFlag) Set(v string) error

func (MultiFlag) String

func (m MultiFlag) String() string

String uses a value receiver so flag can safely print a zero-value default.

type Pagination

type Pagination struct {
	NextCursor string `json:"next_cursor,omitzero"`
	HasMore    bool   `json:"has_more"`
}

Pagination carries cursor-based pagination state for list commands.

type Paths

type Paths struct {
	Home      string
	ConfigDir string
}

Paths holds the home and configuration directories resolved at startup.

func DefaultPaths

func DefaultPaths() (Paths, error)

DefaultPaths resolves Home and ConfigDir (honouring XDG_CONFIG_HOME on Linux via os.UserConfigDir). Falls back to $HOME/.config.

func (Paths) ConfigPath

func (p Paths) ConfigPath() string

ConfigPath is the canonical on-disk location of jcli's config JSON.

func (Paths) ContractHome

func (p Paths) ContractHome(path string) string

ContractHome replaces the home-directory prefix with "~".

func (Paths) ExpandHome

func (p Paths) ExpandHome(path string) (string, error)

ExpandHome replaces a leading ~ or ~/ with the resolved home directory.

type RawJSONer

type RawJSONer interface {
	RawJSON() jsontext.Value
}

RawJSONer exposes a resource's original server JSON.

type ShortcutDoc

type ShortcutDoc struct {
	Label string // e.g. "<ISSUE-KEY>" or "<PAGE-ID-OR-URL>"
	Desc  string
}

ShortcutDoc describes a Fallback shortcut in command help.

type ValidationError

type ValidationError struct {
	Err           error         // joined per-issue errors (errors.Join)
	Hint          string        // short cue for ErrorDetail.Hint
	MissingFields []FieldRecord // required but absent → meta.missing_fields
	UnknownFields []FieldRecord // not in editmeta/createmeta → meta.unknown_fields
	InvalidValues []FieldRecord // value not in allowedValues → meta.invalid_values
}

ValidationError carries local validation failures and field-specific details for JSON error metadata.

func (*ValidationError) Error

func (v *ValidationError) Error() string

func (*ValidationError) Unwrap

func (v *ValidationError) Unwrap() error

Jump to

Keyboard shortcuts

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