Documentation
¶
Overview ¶
Package output writes command results and errors for humans and machines.
It is the one writer of contract:cli.json-envelope v1 and the one renderer of the human error block (EPIC P7-REG decisions D7-D9). Every surface that prints a machine result (help --json, the pilot commands, main, and later MCP and HTTP) goes through it, so the envelope cannot drift.
Success envelope: {"schema_version":"1","command":"<path>","data":<value>} Error envelope: {"schema_version":"1","command":"<path>","error":<Detail>} Exactly one of data/error is present. An optional last member "meta" carries deprecation notices and warnings recorded with AddDeprecation/AddWarning and is omitted when empty. Encoding is 2-space indent, trailing newline, HTML escaping off.
Files: envelope.go (types), writer.go (Writer, EmitData, EmitError), legacy.go (EmitLegacyCompatible, ADR 0021), redact.go (error text redaction), state.go (invocation and meta process state), args.go (JSONRequestedFromArgs), render.go (RenderError).
Error text is redacted before it is written (redact.go), in the envelope and in the human block.
Layer: L0. Imports the standard library, golang.org/x/term, internal/errs, internal/ui, internal/compat and internal/observability (Redact; it imports no other cli package). It never imports cmd/, cobra or a domain package and never calls os.Exit.
Index ¶
- Constants
- func AddDeprecation(old, new, removalAt string)
- func AddWarning(msg string)
- func EmitData(w Writer, command string, data any) error
- func EmitError(w Writer, command string, err error) error
- func EmitLegacyCompatible(w Writer, command string, data any) error
- func Invocation() (command string, json bool, known bool)
- func IsolateStdout() (restore func())
- func JSONRequestedFromArgs(args []string) bool
- func RenderError(w io.Writer, err error)
- func ResetState()
- func SetInvocation(command string, json bool)
- func StreamEnd(w io.Writer, command string, result any, err error) error
- func StreamRecord(w io.Writer, command string, rec any) error
- type Deprecation
- type Envelope
- type ErrorEnvelope
- type Meta
- type Writer
Constants ¶
const LegacyEnvVar = "NSELF_JSON_LEGACY"
LegacyEnvVar restores the pre-contract bare JSON in v1.5 mode for commands that printed one (EmitLegacyCompatible only). Removal at v1.6.0.
const ResultType = "result"
ResultType is the data.type of the last line of a successful stream.
const SchemaVersion = "1"
SchemaVersion is the envelope version, the string "1". It versions the envelope only; the shape of Data is each command's own schema.
Variables ¶
This section is empty.
Functions ¶
func AddDeprecation ¶
func AddDeprecation(old, new, removalAt string)
AddDeprecation records a deprecation notice for the envelope meta member. A notice identical to one already recorded is ignored; order of first insertion is kept.
func AddWarning ¶
func AddWarning(msg string)
AddWarning records a warning for the envelope meta member. A message already recorded is ignored; order of first insertion is kept.
func EmitData ¶
EmitData writes the v1 success envelope for command to w.Out and nothing to w.Err. data nil is written as `"data": null`. The document is built in full before anything is written, so a marshal failure writes nothing and is returned. Recorded meta notices (AddDeprecation, AddWarning) are attached.
func EmitError ¶
EmitError writes the v1 error envelope for err to w.Out, using errs.Describe for the error object, with message, cause and remediation redacted (see redactText). The envelope has no data key. A nil err is a caller bug and is returned as an error without writing a document.
func EmitLegacyCompatible ¶
EmitLegacyCompatible writes the result of a command that printed a pre-contract JSON shape (status, doctor, config show --format json).
v1.4 mode: data bare, byte-identical to ui.PrintJSON, nothing on w.Err. v1.5 mode: the v1 envelope (same as EmitData). v1.5 mode with NSELF_JSON_LEGACY=1 (or "true"): data bare as above, plus the deprecation warning once per process on w.Err. In v1.4 mode NSELF_JSON_LEGACY has no effect. EmitData and EmitError never consult it.
func Invocation ¶
Invocation returns what SetInvocation recorded. known is false when no invocation was recorded yet; callers then fall back to JSONRequestedFromArgs on the raw arguments.
func IsolateStdout ¶
func IsolateStdout() (restore func())
IsolateStdout redirects incidental stdout to stderr until restore is called. Call only in a decorator's single-threaded command body window, and defer restore in that same invocation. No goroutine may read or write os.Stdout across the window: swapping this process-global pointer is not synchronized. The first call retains the real stdout for Default; nested calls are no-ops.
func JSONRequestedFromArgs ¶
JSONRequestedFromArgs reports whether raw command-line arguments ask for JSON mode: `--json`, `--json=true` and `--json=1` are true, `--json=false` is false. Scanning stops at a `--` terminator; the last occurrence wins, as it does for a pflag bool. An unparsable value counts as false.
It exists for failures that happen before cobra resolves a command (an unknown command or flag), where Invocation has nothing recorded yet.
func RenderError ¶
RenderError writes the human form of err to w (stderr in practice).
An error that carries a code other than E400 (a *errs.CLIError or a wrapped coded sentinel) prints
Error: [CODE] message Why: <cause> Fix: <remediation> Docs: <url>
where the three indented lines appear only when non-empty. Every other error prints exactly `Error: <message>`, as main did before. Message, cause and remediation are redacted (URL credentials, tokens, e-mail, IPs; see redactText), so a plain error changes only when it carries such a value. Colour is decided from w's own terminal state (colorFor); the plain line is never coloured. A nil err writes nothing. Write errors are ignored: there is no better stream to report a failure to write the error stream.
func ResetState ¶
func ResetState()
ResetState clears the invocation, the recorded meta notices and the once-per-process legacy warning. Tests call it between cases; a host that serves many invocations in one process (MCP, HTTP) calls it between them.
func SetInvocation ¶
SetInvocation records the resolved command path (without "nself ", "" when none) and whether JSON mode is on, so a failure reported later by main knows how to render. Calling it again replaces the earlier value.
func StreamEnd ¶ added in v1.4.15
StreamEnd writes the last line of a stream. With err nil it is a data envelope whose data object is {"type":"result", <members of result>}; result may be nil (a bare result) and must not carry its own "type" member, and recorded meta notices are attached. With err non-nil it is the compact v1 error envelope for err (same error object and redaction as EmitError) and result is ignored. Nothing is written when it returns an error.
func StreamRecord ¶ added in v1.4.15
StreamRecord writes one record line: {"schema_version":"1","command":..., "data":rec} followed by a newline. rec must marshal to a JSON object whose first member is "type" with a non-empty string value other than "result" (a struct with Type as its first field does). Nothing is written on error.
Types ¶
type Deprecation ¶
type Deprecation struct {
Old string `json:"old"`
New string `json:"new"`
RemovalAt string `json:"removal_at"`
}
Deprecation is one deprecation notice: the old invocation, its replacement and the release at which the old form is removed.
type Envelope ¶
type Envelope struct {
SchemaVersion string `json:"schema_version"`
Command string `json:"command"`
Data any `json:"data"`
Meta *Meta `json:"meta,omitempty"`
}
Envelope is the v1 success document: schema_version, command, data, then the optional meta. Data is written as null when the command's data is nil. Exported so tools/schemagen can generate the schema from it.
type ErrorEnvelope ¶
type ErrorEnvelope struct {
SchemaVersion string `json:"schema_version"`
Command string `json:"command"`
Error *errs.Detail `json:"error"`
Meta *Meta `json:"meta,omitempty"`
}
ErrorEnvelope is the v1 error document: schema_version, command, error, then the optional meta. It has no data key, so exactly one of data/error exists in any document. Exported so tools/schemagen can generate the schema.
type Meta ¶
type Meta struct {
Deprecations []Deprecation `json:"deprecations,omitempty"`
Warnings []string `json:"warnings,omitempty"`
}
Meta is the optional last member of an envelope. Both lists are omitted when empty and the whole member is omitted when both are empty.