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

tool

package
v1.3.15 Latest Latest
Warning

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

Go to latest
Published: Oct 15, 2026 License: MIT Imports: 19 Imported by: 0

Documentation

Overview

Package tool is the one shape of a nova command. A Tool is its verbs; a Verb declares its flags and returns one value, Out, which is rendered either as typed lines or as the JSON of the same value (out.go). Everything a command writes for itself lives here once: the verb dispatch, the banner (what the tool is, how it works, its usage lines, its exit codes, a runnable example block), `help` and `<verb> -h`, the `version` verb, the standard flags (--json on every verb; --max and --dry-run where a verb opts in), refusing to guess (every problem of one invocation named at once), and the refusal line with its remedy: an unknown verb or flag is answered with the nearest name and the ones there are, and a verb group's -h lists its verbs. A row carries a prose tail (Out.ItemText): a reason or a command renders plain after the row's typed fields and as the `text` field of the row's JSON, so the line and the object stay one value. A tool whose exit 0 already means CLEAR sets HelpRefused, and `<verb> -h` is then a refusal at exit 2 naming `help`, never an answer at exit 0. A tool may name a default verb (`<tool> <file>`) and its own status words (STALE beside FAIL). A verb may be hidden (Verb.Hidden): it runs and answers `-h`, and the banner, the usage block and the unknown-verb list do not show it, a probe step verb a user never types. A long-running verb prints each item as it goes; the Out it returns is the closing line. Call.Ctx is cancelled when the run's context ends and on interrupt (RunContext). A command holds only what its verbs do.

Index

Constants

View Source
const (
	HowLines = 5
	HowWidth = 100
)

HowLines and HowWidth bound the banner's how-it-works text: a reader takes in five lines at a glance, and a line past 100 characters wraps.

View Source
const HowLabel = "how it works: "

HowLabel opens the how-it-works text in the banner (docs/ONBOARDING.md point 6), so a tool's How is the paragraph without it; the width bound counts it on the first line, where it is printed.

View Source
const MaxRemedy = "--max <n> raises the ceiling, --max 0 lists all"

MaxRemedy is the remedy every MORE line names.

View Source
const MaxWords = 6

MaxWords bounds a tool's own status words: a reader learns them all at once.

Variables

This section is empty.

Functions

This section is empty.

Types

type Call

type Call struct {
	// Ctx is the run's context, cancelled when that context ends and on
	// interrupt (skeleton contract 2.4). A verb that runs for a while selects
	// on it; a context that has ended is the closing line, not the verb's Out.
	Ctx            context.Context
	Stdin          io.Reader
	Stdout, Stderr io.Writer // written by a verb that Prints
	// contains filtered or unexported fields
}

Call is one invocation of a verb: its streams, its context and its parsed flags.

func (*Call) Bool

func (c *Call) Bool(name string) bool

func (*Call) DryRun

func (c *Call) DryRun() bool

DryRun reports whether --dry-run was given (only a Verb with DryRun takes it). A verb reads it before it writes and, when it is set, returns the plan the real run would carry out, from the same code path, and writes nothing; the skeleton adds dry_run=true to the OK line unless the verb set it.

func (*Call) Dur

func (c *Call) Dur(name string) time.Duration

func (*Call) Get

func (c *Call) Get(name string) any

Get is a declared flag's value, for a flag.Value of the verb's own (a flag.Getter).

func (*Call) Given

func (c *Call) Given(name string) bool

Given reports whether the flag was on the command line.

func (*Call) Int

func (c *Call) Int(name string) int

func (*Call) Problem

func (c *Call) Problem(what string)

Problem records one reason the invocation cannot run.

func (*Call) ProblemAs

func (c *Call) ProblemAs(reason, what string)

ProblemAs records one reason with a stable code (skeleton contract 2.10, STANDARD §2). The refusal line carries reason=<code> before the colon, and the JSON result adds "reasons" beside "why": a program reads the code, a person reads the sentence.

func (*Call) Refused

func (c *Call) Refused() *Out

Refused is the refusal naming every problem recorded, or nil when there is none.

func (*Call) Str

func (c *Call) Str(name string) string

Str, Int, Bool and Dur read a declared flag's value.

func (*Call) Want

func (c *Call) Want(name, wants string) string

Want reads a required string flag, recording a problem that says what it wants when it is empty. Every Want is read before Refused, so one run names every missing flag (docs/ONBOARDING.md point 2).

type Effect

type Effect string

Effect is what running a verb does beyond printing: one of the three below, optionally with a clause after it; a verb whose flags change it states the strongest and says which flag (Problems holds every verb to one of the three).

const (
	Inspection Effect = "inspection: reads, writes nothing"
	LocalWrite Effect = "local write: writes files on this machine"
	Delivery   Effect = "delivery: sends beyond this machine"
)

type Field

type Field struct {
	K string
	V any
}

Field is one key=value.

type Fields

type Fields []Field

Fields keeps its keys in the order they were added, in both renderings.

func (Fields) MarshalJSON

func (fs Fields) MarshalJSON() ([]byte, error)

MarshalJSON writes the fields as one object in the order they were added.

type Flags

type Flags struct {
	*flag.FlagSet
	// contains filtered or unexported fields
}

Flags is one verb's flag set: package flag's own, with its two mouths closed (verbflag.New), and the standard flags a verb opts into.

func (*Flags) Check

func (f *Flags) Check(rule func(c *Call))

Check adds a rule over the parsed flags (c.Problem, c.Want), run with the required flags, --max and the arguments before the verb runs.

func (*Flags) Max

func (f *Flags) Max()

Max adds --max: the items listed before one MORE line stands for the rest.

func (*Flags) Prints

func (f *Flags) Prints()

Prints marks a verb that writes its own output (a payload a program reads, a child's stream, or a body shared with a tool not yet on this package): it gets no --json, and returns Exit(code) after writing to c.Stdout and c.Stderr.

func (*Flags) Required

func (f *Flags) Required(name, wants string)

Required declares a string flag the verb cannot run without: empty, it is a problem that says what it wants, named with every other problem at once.

type Item

type Item struct {
	Kind   string `json:"kind"`
	Fields Fields `json:"fields"`
	Text   string `json:"text,omitempty"`
}

Item is one typed row: `<TOKEN> <KIND> k=v ...[: <text>]`, where the text is the row's prose tail (ItemText): a reason or a command, printed plain after the typed fields and carried as the `text` field of the JSON, so the line and the object stay one value.

type More

type More struct {
	Kind   string `json:"kind"`
	Shown  int    `json:"shown"`
	Total  int    `json:"total"`
	Remedy string `json:"remedy"`
}

More stands for the items of one kind that --max did not list.

type Out

type Out struct {
	Verb    string
	Status  Status
	Exit    int
	Word    string   // the tool's own status word in place of OK or FAILED (Out.As); "" is the plain one
	Remedy  string   // what to run next: the tool's help on a refusal unless the verb names better
	Why     []string // every reason it failed or was refused
	Reasons []string // a stable code per Why, parallel to it; a program reads the code (skeleton contract 2.10)
	Facts   Fields
	Items   []Item
	More    []More
	Notes   []string
	Payload string
	// contains filtered or unexported fields
}

Out is the one value every verb returns. Render writes it as typed lines:

<TOKEN> OK|FAILED|REFUSED [reason=<r>] k=v ...[: <why>][; run: <remedy>]   one line per why
<TOKEN> <KIND> k=v ...[: <text>]                            one line per item, the text its prose tail
<TOKEN> MORE kind=<kind> shown=<n> total=<n> <remedy>       one per capped kind
<TOKEN> NOTE <text>                                         one per note

or as the JSON of the same value:

{"result":{"verb","status","exit","remedy","why","reasons"},"facts":{},"items":[{"kind","fields","text"}],
 "more":[{"kind","shown","total","remedy"}],"notes":[],"payload":""}

A payload (the version line, a document a program reads) is printed as it is, last, in the text form, and alone when the result carries nothing else; it is the "payload" key of the JSON. Values are strings, integers, booleans or Text; an empty value is "-" in the text form. The JSON is not HTML-escaped: `<dir>` is `<dir>` in both renderings.

func Done

func Done() *Out

Done is an OK result, Refuse one that could not run, Fail one that ran and said no.

func Exit

func Exit(code int) *Out

Exit is the result of a verb that printed its own output (Flags.Prints).

func Fail

func Fail(why ...string) *Out

func Payload

func Payload(text string) *Out

func Refuse

func Refuse(why ...string) *Out

func (*Out) As

func (o *Out) As(word string) *Out

As puts one of the tool's own status words (Tool.Words) in place of OK or FAILED on the first line; the status and the exit stay: `Done().As("UNCHANGED")` exits 0, and a gate that says no is `Fail(why).As("STALE")`, exit 1, apart from a refusal's 2. A word the tool does not declare, or one on a refusal, is turned into a FAILED naming the bug.

func (*Out) Cap

func (o *Out) Cap(max int) *Out

Cap keeps the first max items of each kind (0 keeps all) and records a More for each kind with items left over, counted by pkg/bounded: the `MORE kind= shown= total=` cut. A verb with Flags.Max is capped by the skeleton; a verb that bounds a listing of its own calls Cap.

func (*Out) Fact

func (o *Out) Fact(k string, v any) *Out

Fact adds one key=value to the first line.

func (*Out) Findings

func (o *Out) Findings(kinds ...string) *Out

Findings names the item kinds that are a verb's findings: in the text form their lines go to stderr, and the rest of a verb that ran (its first line, its other items, MORE and NOTE) to stdout, whether it said OK or FAILED. A refusal stays whole on stderr; JSON stays one object on stdout.

func (*Out) Item

func (o *Out) Item(kind string, kv ...any) *Out

Item adds one row of a kind, its fields given as key, value, key, value.

func (*Out) ItemText

func (o *Out) ItemText(kind, text string, kv ...any) *Out

ItemText adds one row of a kind with a prose tail: the typed fields render as key=value tokens and the text renders plain after them, `: <text>` (oneline.Escape), while the JSON carries it as the `text` field beside the typed fields (STANDARD §2, one output structure, two renderings). A reason or a command lives in the tail, never escaped into one key=value field.

func (*Out) MarshalJSON

func (o *Out) MarshalJSON() ([]byte, error)

MarshalJSON is the JSON rendering: the result, then the value's parts.

func (*Out) Note

func (o *Out) Note(text string) *Out

func (*Out) Render

func (o *Out) Render(w io.Writer, asJSON bool) int

Render writes o as typed lines, or as one JSON object when json is set, and returns the exit that stands: o.Exit, or 1 when o is no JSON (a NaN or an infinite float, a value of the verb's own that JSON cannot carry), which is then a FAILED line naming the verb, never an empty line and a success. Every value goes through pkg/oneline.

type Status

type Status string

Status is how a verb ended: ok (exit 0), failed (it ran and said no, exit 1) or refused (it could not run, exit 2).

const (
	OK      Status = "ok"
	Failed  Status = "failed"
	Refused Status = "refused"
)

type Text

type Text string

Text is a value of free text, a reason, a sentence or a command to run, as a fact or an item's field: the text form prints it after the line's typed fields, its prose tail, quoted (oneline.Quote) so it keeps its spaces and is not hex-escaped, where a typed value (a name, a path, a count) is one token (oneline.Field): `REPORT UNKNOWN name=x path=p reason="no version line" run="nova-version report -h"`. JSON carries it as a string under its key.

type Tool

type Tool struct {
	Name string // the binary: nova-<name>
	What string // line 1 of the banner: what the tool is for
	// Stage, when set, is one sentence on how ready the tool is ("nova-x is
	// pre-alpha: not ready for production use."): the banner's line 2, the
	// second line of every verb's -h, and an indented NOTE line under a bare
	// command's refusal, so no reader meets the tool without it.
	Stage     string
	How       string // how it works: the paragraph under line 1
	Verbs     []Verb // in banner order; version and help are added here
	ExitTable string // "0 ..., 1 ..., 2 ...": the banner's exit-codes line
	Stamp     string // the build stamp (-ldflags -X main.version), for version
	// Default is the verb run when the first word is no verb: a flag, a path
	// (a word with a separator), or a word naming a file that is there
	// (`<tool> <file>...`); any other word is refused as no verb and no file.
	// The default verb accepts positional arguments, also when named explicitly.
	// "" makes every first word a verb and leaves every verb flags-only.
	Default string
	// Exists reports whether path is a file or directory that is there: the
	// one seam the skeleton reads the filesystem through (skeleton contract
	// 2.1). Nil defaults to checking with os.Stat. Tests pass a map.
	Exists func(path string) bool
	// Words are the tool's own status words (STALE, MISSING, UNCHANGED), the
	// only ones Out.As may put in place of OK or FAILED: at most MaxWords,
	// upper case, none of OK, FAILED, REFUSED, MORE or NOTE (Problems).
	Words []string
	// HelpRefused refuses `<verb> -h` (and --help) at exit 2 instead of
	// answering it at exit 0: a tool sets it when its exit 0 already means
	// CLEAR, so a `-h` answer could read as CLEAR (STANDARD §3 names the one
	// exception). The refusal names `help` as the door. No tool sets it yet.
	HelpRefused bool
	// NoJSON, when set, replaces the banner's standard --json sentence's
	// clause after the colon: a tool whose verbs print their own prose (the
	// note body a bus carries) says there why they take no --json and pastes
	// a line they print, so the help and the output cannot drift (ONBOARDING
	// point 6). Empty keeps the standard sentence.
	NoJSON string
	// UsageNote, when set, is printed under the usage block, before the
	// standard flags sentence: a tool states once, where a reader has just
	// read the usage lines, the shape of a value several of them name (the
	// manifest of --file), so no usage line carries it. Empty prints none.
	UsageNote string
	// Topics are the tool's help topics: `help <topic>` prints the topic's
	// text at exit 0, and the banner lists the topic names on one line
	// (skeleton contract 2.7). A tool's reference text lives here, never in
	// the banner, which a reader takes in at a glance (STANDARD §3 point 6).
	// A topic's name is none of the tool's verbs, since `help <name>` is one
	// door (Problems).
	Topics []Topic
	// GroupNotes are notes a verb group's help prints under its examples
	// (`<tool> <group> -h`), keyed by the group's word: what a reader of only
	// that group needs, such as where an older set of its verbs went.
	GroupNotes map[string]string
}

Tool is one command.

func (*Tool) Banner

func (t *Tool) Banner() string

Banner is what `help` prints: what, how, usage, the standard flags, the exit codes, and the example block last (docs/ONBOARDING.md point 1).

func (*Tool) Main

func (t *Tool) Main() int

Main runs the tool over the process's arguments and streams and returns the exit code.

func (*Tool) Problems

func (t *Tool) Problems() []string

Problems is where t falls short of the standard its banner and help carry by construction only when the definition is complete: a what line, an exit table, a how text of at most HowLines lines of at most HowWidth characters, and every verb's effect one of inspection, local write or delivery. A tool on this package runs it in its own tests (internal/ci holds every such package to that).

func (*Tool) Run

func (t *Tool) Run(args []string, stdin io.Reader, stdout, stderr io.Writer) int

Run dispatches one invocation: help, version, or a verb. `<verb> -h` and `help <verb>` print that verb's help on stdout at exit 0 before anything is read or written (the CLI style's rule (b)); a tool that refuses help (HelpRefused) still answers `help <verb>` by name, while `<verb> -h` is a refusal at exit 2, since its exit 0 would read as CLEAR.

func (*Tool) RunContext

func (t *Tool) RunContext(ctx context.Context, args []string, stdin io.Reader, stdout, stderr io.Writer) int

RunContext is Run on ctx. Call.Ctx is ctx, cancelled also on interrupt, so a long-running verb can stop (skeleton contract 2.4, STANDARD §2). A context that has already ended does not run the verb: the closing line names the context's error. One cancelled while the verb runs replaces that closing line the same way.

type Topic

type Topic struct {
	Name string
	Text string
}

Topic is one help topic: `<tool> help <name>` prints Text on stdout at exit 0. It is where a tool's reference text lives, so the banner stays short (skeleton contract 2.7, STANDARD §3 point 6).

type Verb

type Verb struct {
	Name      string
	Usage     string         // the usage line(s) after the tool's name, one form per line
	Example   string         // runnable line(s) after the tool's name, for the banner's example block
	Effect    Effect         // what running it does to the world, stated in `help <verb>`
	Detail    string         // lines `help <verb>` prints above its flags: a format, a worked example
	ExitTable string         // this verb's exit codes, quoted by its -h; "" quotes the tool's
	DryRun    bool           // the verb takes --dry-run and honours it (Call.DryRun): it plans and writes nothing
	Hidden    bool           // the verb runs and answers -h and `help <it>`, but the banner, the usage block and the verb lists a refusal names do not show it: a probe step verb a user never types (STANDARD §3, help is never a refusal; §2, a list names the verbs there are for the reader)
	Flags     func(f *Flags) // declares the verb's flags; nil declares none
	Run       func(c *Call) *Out
}

Verb is one verb of a tool. A name of two words ("fn load") puts the verb in a group ("fn"): `<tool> fn -h` lists the group's verbs at exit 0.

Jump to

Keyboard shortcuts

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