Help us shape the future of Go! Take the 2026 Go Developer Survey

cmdkit

package
v0.12.0 Latest Latest
Warning

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

Go to latest
Published: Oct 18, 2026 License: Apache-2.0 Imports: 27 Imported by: 0

Documentation

Overview

Package cmdkit is what every workflow command is built from, beneath the command tree internal/cli spells: the scriptable contract — the artifact on a checked stdout, what is said about it on stderr, --yes and --dry-run around a write, a mistake in the call as a usage error — and the connection to where a command runs, the environment it reads the process through, the flags every command shares and the prompt that asks the person at the terminal. It imports no command, and not the web server: the root hands it the port --web serves on unless told otherwise.

Index

Constants

View Source
const ConfirmationHelp = "go ahead without the confirmation"

ConfirmationHelp is --yes's help on a write that asks one question.

Variables

View Source
var ErrMessagingNotConfigured = errors.New("no messaging transport is configured")

ErrMessagingNotConfigured refuses announcing when no messaging transport is set up.

View Source
var ErrNoTerminal = errors.New("no terminal to answer on")

ErrNoTerminal reports a question asked with nothing to answer it: stdin was closed, or piped and already read to its end.

View Source
var ErrOutputUnwritten = errors.New("the output could not be written")

ErrOutputUnwritten reports an artifact that could not be written to stdout — a pipe whose reader has gone, a full disk. It fails the command however its work went, since a script reading stdout would otherwise take an artifact cut short, or none, for a run that succeeded.

View Source
var ErrUsage = errors.New("invalid argument")

ErrUsage marks a mistake in how a command was called — a flag or an argument it does not take — as distinct from a command that ran and failed. Its words are cobra's own for a flag value it refuses, so a value this tree refuses reads the same.

Functions

func AddJSONFlag

func AddJSONFlag(cmd *cobra.Command, asJSON *bool, help string)

AddJSONFlag declares --json, or -j, on cmd, held in asJSON, with help that says what it prints: the one way every command's data is asked for.

func CheckPort

func CheckPort(cmd *cobra.Command, web bool, port int) error

CheckPort refuses, as a mistake in the call, --port without --web, where nothing listens on it, and a port no listener can take.

func CompleteAssignedIssues

func CompleteAssignedIssues(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective)

CompleteAssignedIssues completes an issue command's key with the keys of the issues assigned to you, for `workflow branch <tab>` and every command that names an issue first. Past the key it offers nothing and asks nothing, and a tracker that cannot be reached offers nothing rather than an error.

func ConfigHome

func ConfigHome(cmd *cobra.Command) string

ConfigHome is the home directory a configuration falls back to, or "" for none. A machine without a home directory is unusual but not a failure: the working directory alone is still a valid place to find a configuration.

func Confirm

func Confirm(prompt Prompt, question string) (bool, error)

Confirm asks a yes/no question, defaulting to no, so a bare enter is the safe answer. With nothing to read an answer from, it says so rather than passing on a bare end-of-file.

func DryRunRequested

func DryRunRequested(cmd *cobra.Command) bool

DryRunRequested reports whether --dry-run was given, before or after the command's name. The root declares it for every command, so it is always there to read.

func EncodeJSON

func EncodeJSON(out io.Writer, value any) error

EncodeJSON writes value as indented JSON: the one encoder every command's data goes out through, so each one's output is shaped and worded the same.

func Examples

func Examples(lines ...string) string

Examples is a command's Example text: one invocation a line, indented as cobra indents its other sections, so --help and the reference page show what scripting.md shows.

func ForgeNoun

func ForgeNoun(kind forge.Kind) string

ForgeNoun names a forge mid-sentence: by its own name, or as the forge when the remote does not say which.

func FromWorkingDir

func FromWorkingDir(cmd *cobra.Command, path string) (string, error)

FromWorkingDir is path, or where it leads from the directory the command cmd runs in was run from when it is relative to that, as a shell would read it there.

func HandOver

func HandOver(cmd *cobra.Command, env Environment) error

HandOver puts env in the context of the command cmd runs, which every command under the root reads it from: the root does this before any command's own work starts, once it has refused --quiet beside --verbose. The working directory is read at the first ask and answered from then on as it was then. The context also carries itself as the lifetime of what the command detaches from its asker (a Slack directory read, a token's refresh), so a signal ends that too.

func IsAny

func IsAny(err error, targets ...error) bool

IsAny reports whether err is, or wraps, any of targets.

func LiveLook

func LiveLook(ui config.UI) console.Look

LiveLook is how a live line is drawn under ui: in ASCII when ui.ascii asks, and its mark still when ui.motion reduces motion.

func LoadFromEnvironment

func LoadFromEnvironment(cmd *cobra.Command) (config.Config, error)

LoadFromEnvironment loads the configuration that applies where cmd runs, searching up from its working directory to the repository root and then the home directory. A missing or unreadable file, or a working directory that cannot be named, is reported through the error.

func MarkMisuse

func MarkMisuse(root *cobra.Command)

MarkMisuse makes every command in the tree report its flag and argument errors as usage errors: a flag it does not know, a wrong argument count, and an unknown command, which is an argument to a command that takes none.

func MessagingNotConfigured

func MessagingNotConfigured() error

MessagingNotConfigured refuses to announce with no messaging transport set up, saying how to set one up.

func NotesOf

func NotesOf(cmd *cobra.Command) console.Notes

NotesOf is what the command cmd runs says on its stderr, as the root's flags ask, colored as the variables of the environment it runs in allow.

func OnlineDoer

func OnlineDoer(cfg config.Config) httpx.Doer

OnlineDoer is the transport the tracker and messaging checks travel over: the redirect-refusing client, bounded by RequestTimeout.

func PostingIn

func PostingIn(settings config.Messaging, channel string) (config.Messaging, error)

PostingIn is settings posting in channel, as --channel asks, in place of messaging.channel; a blank channel leaves them as they are. A webhook cannot post anywhere but where it is bound, so asking it to is refused as usage. With no messaging set up the channel is set all the same, for the command's own refusal to say what to set up.

func RequestLogFor

func RequestLogFor(cmd *cobra.Command) (*wiring.RequestLog, func(), error)

RequestLogFor opens the request log --request-log names and, under --verbose, says each outline as a debug line too, or keeps none when neither asks for one, warning on the command's stderr at close when the file was not fully written. The root declares both flags for every command, so they are always there to read.

func RequestTimeout

func RequestTimeout(cfg config.Config) time.Duration

RequestTimeout is the configured request timeout, or the default when none is set: it bounds each of doctor's --online checks, so a hung service does not hang doctor, and how long a quit waits for the writes in flight.

func ServicesUnreachable

func ServicesUnreachable() []error

ServicesUnreachable are the clients' errors for a service that never answered the request itself: it could not be reached, answered only to say wait, answered with a redirect the client refused to follow, or took too long to finish its answer. A script may try again later. The exit family adds doctor.ErrUnreachable, which says the same of a check doctor made.

func TerminalOut

func TerminalOut(cmd *cobra.Command) io.Writer

TerminalOut is the stdout cmd writes to as the stream it was handed, without the check over it: the interface draws on it itself, and Bubble Tea knows a terminal only as the file it is.

func VoiceOf

func VoiceOf(root *cobra.Command, getenv func(string) string) console.Options

VoiceOf is how the notes of a run of root are said, as its flags ask: in the --log-format given, leaving the notes out under --quiet and adding debug lines under --verbose, colored as getenv's variables allow.

func Waiting

func Waiting(
	cmd *cobra.Command, prompt Prompt, look console.Look, wait func(out Output, live console.Wait) error,
) error

Waiting runs wait, a slow command's reads, keeping its live line on stderr, drawn as look asks, while it does when stderr is a terminal a person watches, and keeping none in a pipe or a log, where only what it says prints. Its notes are said above the line, and its artifact goes to stdout, unless stdout is the terminal too.

func WorkingDir

func WorkingDir(cmd *cobra.Command) (string, error)

WorkingDir is the directory the command cmd runs in was run from, or why it cannot be named: the one place a command reads it.

Types

type CheckedOutput

type CheckedOutput struct {
	// Out is the stdout the writes go to.
	Out io.Writer
	// Err is why the first write that failed could not be made, an
	// ErrOutputUnwritten, or nil while none has.
	Err error
}

CheckedOutput is stdout, remembering the first write it could not make and refusing every write after it, so every command's artifact — prose written line by line as much as JSON — fails the command when it went nowhere.

func (*CheckedOutput) Failed

func (o *CheckedOutput) Failed(err error) error

Failed is a command's outcome err, joined with why its output could not be written when it could not and err does not already say so.

func (*CheckedOutput) Stream

func (o *CheckedOutput) Stream() io.Writer

Stream is the checked stdout as a command writes it: over a file, the terminal it may be, so fang can tell whether to color its help.

func (*CheckedOutput) Write

func (o *CheckedOutput) Write(p []byte) (int, error)

Write writes p, or refuses it once a write has failed.

type Connection

type Connection struct {
	// Config is the configuration in effect.
	Config config.Config
	// LoadErr is why the configuration did not load — config.ErrNotFound
	// when there is no file — or nil.
	LoadErr error
	// Where is the repository the command runs in, or the directory when
	// there is none.
	Where wiring.Workspace
	// Deps are the seams over the configuration and the repository.
	Deps tui.Deps
	// Controls are the wiring's own: finding tokens ahead, and applying
	// settings the web's Settings saves.
	Controls wiring.Controls
	// RequestLog records each request, or is nil; a switch wires the next
	// directory to the same log.
	RequestLog *wiring.RequestLog
	// CloseLog closes the request log, which the caller defers.
	CloseLog func()
	// Keychain keeps a token typed into the first run's form in the OS
	// keychain; nil where none is wired, or under a dry run.
	Keychain func(ctx context.Context, service, secret string) error
	// Process is what the wiring read of the process: the home, variables,
	// store and programs the command runs with.
	Process wiring.Environment
}

Connection is a command wired to where it runs: the configuration in effect and, when it did not load, why; the repository; the seams over both, and what finds their tokens ahead of first use, which only the interface and the web server call; and the close of the request log, which the caller defers.

func Connect

func Connect(cmd *cobra.Command) (Connection, error)

Connect wires a command to its working directory, recording each request in the --request-log file when one is named. A configuration file that is there but cannot be read is refused rather than replaced by the defaults, which would act on settings nobody chose; no file at all is not an error.

func ConnectAt

func ConnectAt(ctx context.Context, cmd *cobra.Command, dir string, requestLog *wiring.RequestLog) Connection

ConnectAt wires the directory at dir, which reads its own configuration, recording each request in requestLog unless it is nil, and finding where dir is under ctx. Under --dry-run its store is only read, and only when it is already on disk. It opens nothing, so its close is a no-op.

func ConnectLeniently

func ConnectLeniently(cmd *cobra.Command) (Connection, error)

ConnectLeniently is Connect keeping a configuration that did not load, for the interface and the web server, which each show the user why.

func (Connection) UnreadConfiguration

func (c Connection) UnreadConfiguration() error

UnreadConfiguration is why the configuration file in effect could not be read, or nil when it was read or there is none.

func (Connection) WithSetup

func (c Connection) WithSetup(
	cmd *cobra.Command, keychain func(ctx context.Context, service, secret string) error,
) Connection

WithSetup is the connection offering a first run where it works, with the token kept through keychain, which a dry run never stores in.

type Environment

type Environment struct {
	// WorkingDir is the directory the command was run from, or why the
	// operating system cannot name it. A command reads it once, whatever
	// reads it after: its sections, its request log and each directory it
	// names relatively are all read from the one directory.
	WorkingDir func() (string, error)
	// Chdir makes dir the process's working directory, so whatever a session
	// switched there runs — an editor, a hook — starts there.
	Chdir func(dir string) error
	// Process is what the wiring reads of the process.
	Process wiring.Environment
}

Environment is what a command reads of the process it runs in: the directory it was run from and how to move the process to another, and what the wiring reads — the home directory, the environment's variables, the store's directory and where a program is found. cli.Execute's caller builds it from the operating system, so no command reads the process's own, and a test hands each run one of its own.

func EnvironmentOf

func EnvironmentOf(cmd *cobra.Command) Environment

EnvironmentOf is the Environment the command cmd runs in, or, when none was handed over, one that answers every question with errNoEnvironment.

func NoEnvironment

func NoEnvironment() Environment

NoEnvironment is the environment of a command that has none: it names no directory, home or variable, finds no program, and moves nowhere.

type Output

type Output struct {
	// Artifact is where the artifact goes: stdout, or the notes' own stream
	// for an aside.
	Artifact io.Writer
	// Notes are what the command says about it, on stderr.
	Notes console.Notes
}

Output is where a command writes: the artifact — the preview, the JSON, what it created — on stdout, and what it says about it — a notice, a dry-run line, a warning — through the notes on stderr, so a script capturing stdout gets data alone.

func OutputOf

func OutputOf(cmd *cobra.Command) Output

OutputOf is the command's stdout and the notes it says on stderr.

func (Output) Aside

func (o Output) Aside() Output

Aside is out with its artifact said as notes too, for a command whose stdout carries another artifact: what it previews, printed beside the JSON a script reads.

type Prompt

type Prompt struct {
	// Line prints prompt and reads one visible line, for an address or a choice.
	Line func(prompt string) (string, error)
	// Secret prints prompt and reads one line without echoing it, for a token;
	// with no terminal to read it from, it answers io.EOF.
	Secret func(prompt string) (string, error)
	// StoreSecret saves secret in the OS keychain under the item service names,
	// under ctx. It is nil where storing is not wired for the platform, and the
	// guided flow then keeps the token in the file.
	StoreSecret func(ctx context.Context, service, secret string) error
	// Input is standard input, read to its end for a comment's text, as `git
	// commit -F -` reads a message, so a script never quotes it. It shares Line's
	// reader, so nothing read for one is lost to the other. Nil reads as empty.
	Input io.Reader
	// IsTerminal reports whether a stream is a terminal, so a slow command keeps
	// a live line on stderr only where a person is watching it, never in a
	// pipe or a log, and prints its artifact beside the line only where stdout
	// is no terminal. Nil reads every stream as no terminal.
	IsTerminal func(stream io.Writer) bool
}

Prompt is how a command asks the person at the terminal: reading an answer, for the guided init and every scriptable write's yes/no question; reading standard input whole, for a comment's text; keeping a secret in the operating system's keychain; and composing a note in the editor. Each is a seam so a test can drive the conversation without a terminal, a real keychain or an editor. The main package wires the two terminal reads, which no test can make, and takes the keychain and the editor from their own packages.

A zero Prompt is enough for a command that never asks anything — the doctor, or the reference generator that only walks the tree.

type RootFlags

type RootFlags struct {
	// DryRun is --dry-run: show what would change, and change nothing.
	DryRun bool
	// Web is --web: serve the web app rather than open the interface.
	Web bool
	// Port is --port, the port --web serves on.
	Port int
}

RootFlags are the root's flags: a dry run, which every command reads, and --web and --port, which choose what bare workflow opens.

func (*RootFlags) Declare

func (f *RootFlags) Declare(root *cobra.Command, defaultPort int)

Declare declares the flags on root: --dry-run, --request-log and how stderr is said once, for every command, so a script passes them before the command's name or after it, and each command reads them back by name; --web and --port, serving on defaultPort unless given, on the root alone. --log, the request log's old name, is kept hidden only to refuse it with the new one.

type WriteOptions

type WriteOptions struct {
	// DryRun is a run that changes nothing.
	DryRun bool
	// Yes skips the confirmation.
	Yes bool
}

WriteOptions are the flags every scriptable write shares: a dry run that changes nothing, and a yes that skips the confirmation for unattended use.

func (*WriteOptions) AddFlags

func (o *WriteOptions) AddFlags(cmd *cobra.Command, yesHelp string)

AddFlags declares --yes, or -y, on a scriptable write command, with help that says what it answers, and has it read the root's --dry-run — one flag every command inherits — before it runs.

func (*WriteOptions) Proceed

func (o *WriteOptions) Proceed(notes console.Notes, confirm func(string) (bool, error), say WritePrompt) (bool, error)

Proceed reports whether to go ahead with a write, the preview already printed. A dry run says what it would do and stops; --yes goes ahead without asking; otherwise it asks, and a declined answer says so and stops. What it says is commentary, written to notes.

type WritePrompt

type WritePrompt struct {
	// Question is what the confirmation asks.
	Question string
	// DryRun is what a dry run says the write would do.
	DryRun string
	// Declined is what the write prints when the answer is no.
	Declined string
}

WritePrompt is what a scriptable write says around its confirmation: the question it asks, what a dry run would do, and what it prints when declined.

Jump to

Keyboard shortcuts

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