cli

package
v0.0.17 Latest Latest
Warning

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

Go to latest
Published: Oct 7, 2026 License: MIT Imports: 25 Imported by: 0

Documentation

Overview

Package cli runs the tools of an agenttool.Set as the commands of one program, for a host that reaches tools through a shell rather than in process or over MCP. Each tool is a command named as the tool is, and its arguments are flags read from its schema, a JSON object, or both:

file-tools read_file --path go.mod
file-tools read_file '{"path": "go.mod"}'
file-tools read_file - <<'EOF'
{"path": "go.mod"}
EOF

Nothing here is part of the contract, and a tool needs nothing to be run this way. The package is a translation: Commands describes a set as commands, Runner parses a command line into a agenttool.Call and runs it, and Markdown renders the usage a model reads, from the same description the parser uses so that the two cannot drift.

The translation loses what one call per process cannot hold. A agenttool.Resource or agenttool.Sequential tool orders nothing against a call in another process, and a tool that keeps state across calls, a persistent shell or a container session, loses it at exit. Such a tool is better left out of the set than run here.

Index

Constants

View Source
const (
	// ExitOK is a call that succeeded; its output is on stdout.
	ExitOK = 0
	// ExitFailed is the tool's error, written to stderr as the model
	// would see it in process, "Error: <message>". Invalid arguments
	// are the tool's to report, so they end here too: a tool from
	// [agenttool.New] validates them against its schema, and one from
	// [agenttool.NewFunc] checks only what its own function checks, as
	// in process, where mcpserver would validate them for it.
	ExitFailed = 1
	// ExitUsage is a call that did not run: a command line the program
	// did not understand, such as an unknown command or option, a flag
	// value of the wrong type or a JSON argument that is not an object,
	// or a call the program could not start, such as a --record file it
	// cannot open or a set [Commands] refuses.
	ExitUsage = 2
	// ExitNeedsAnswer is a call that asked a question no --answer
	// answered and nobody was there to ask. The tool was answered
	// [agenttool.ActionCancel], as the contract says a harness with
	// nobody to ask answers, and did what it does with that; stdout
	// holds the question as JSON, with the tool's output or error. A
	// caller that has the answer runs the call again, which is safe for
	// a tool that asks before it acts and repeats what was done for one
	// that does not.
	ExitNeedsAnswer = 3
)

The exit statuses of Runner.Run. They carry the contract's error convention through a shell, which shows a model the status as well as the output.

Variables

This section is empty.

Functions

func Markdown

func Markdown(program string, cmds []Command) string

Markdown renders how to call program and each of cmds, for a model to read: the body of a skill, or a section of AGENTS.md. Headings start at level two, so it nests under the title of whatever holds it. It is rendered from the same Command values Runner parses against, so what it teaches is what the program accepts.

func Prompt

func Prompt(in io.Reader, out io.Writer) agenttool.Elicitor

Prompt returns an elicitor that asks a person at a terminal, reading from in and writing to out, for Runner.Ask. A question with a URL shows it and waits for Enter; a form asks for each field in turn, converting each answer by the field's type; any other question, including a form with no fields, which is how an MCP server asks for a confirmation, is yes or no. An end of input is agenttool.ActionCancel, since nobody answered.

Questions are asked one at a time, in the order they arrive. A question whose context ends while it waits for a line, as it does on an interrupt, returns the context's error without waiting.

Types

type Command

type Command struct {
	// Name is the tool's name, which is the command's.
	Name        string
	Description string
	// Annotations are the tool's, the zero value when it carries none.
	Annotations agenttool.Annotations
	// Params are the top-level properties of the tool's schema, in
	// schema order.
	Params []Param
	// Schema is the tool's parameters schema, [agenttool.NoArgsSchema]
	// when it has none.
	Schema json.RawMessage
}

Command is one tool as a command: what Runner parses and Markdown renders.

func Commands

func Commands(set agenttool.Set) ([]Command, error)

Commands describes every tool of set, in order. A set the program could not run is an error: two tools of one name, a tool with none, one named as a command of the program's own, help or schema, or one whose name begins with a dash, which the command line would read as an option. A schema is never an error, so one odd tool cannot take the program's other commands down with it; see Describe.

func Describe

func Describe(t agenttool.Tool) Command

Describe describes one tool as a command. Its parameters are read from the schema the tool serves, so a tool from agenttool.NewFunc or mcpclient is described as one from agenttool.New is. Only the keywords the parser needs are read, and leniently: a property whose schema is not an object, or whose keywords are not the shape expected, has no flag and arrives in the JSON argument; a schema with no properties object gives no flags at all; of a name written twice the first is described. The tool remains what validates its arguments, and the schema is served as the tool gave it.

type Param

type Param struct {
	Name        string
	Description string
	// Type is the property's JSON Schema type, with a nullable union
	// reduced to the type it admits besides null; empty when the schema
	// names none or several.
	Type string
	// Items is the type of an array's elements, read as Type is; empty
	// for anything but an array.
	Items string
	// Fields are the properties of an object that declares them, read
	// as the top-level ones are, in schema order; nil for anything else.
	// Each field with a flag has one of its own, named by its path from
	// the top: --name.field.
	Fields []Param
	// Values is the type of a map's values, read as Type is: an object
	// that declares additionalProperties and no properties. Empty for
	// anything else.
	Values string
	// Required is whether the object that holds the property requires
	// it.
	Required bool
	Enum     []any
}

Param is one property of a tool's arguments: a top-level one, or a field of an object that is one.

func (Param) Flag

func (p Param) Flag() bool

Flag reports whether the parameter has a flag of its own: a string, integer, number or boolean has one, an array of them has one that repeats, and a map of them has one given once per entry, as key=value. An object with fields has none of its own, and its fields may have theirs; see Param.Fields. Anything else, an array of objects or a value of any type, arrives only in the JSON argument, as does a property whose name a flag cannot carry: empty, starting with a dash, or holding an equals sign.

func (Param) Map added in v0.0.17

func (p Param) Map() bool

Map reports whether the parameter is a map whose flag is given once per entry, as key=value.

func (Param) Repeated

func (p Param) Repeated() bool

Repeated reports whether the parameter's flag is given once per element of an array.

type Runner

type Runner struct {
	// Name is the program's name, as usage and errors print it.
	Name  string
	Tools agenttool.Set
	// Ask answers a question no --answer answered: [Prompt] for a
	// person at a terminal. Nil, the question is answered
	// [agenttool.ActionCancel] and the call ends with [ExitNeedsAnswer]
	// and the question on stdout, for the caller to run again with the
	// answer, which is what a model calling through a shell can do. A
	// run again is a second call, so the protocol suits a tool that asks
	// before it acts.
	// A program run by people and models alike sets it only when it
	// knows a person is there, since a terminal does not say who is
	// at it.
	Ask agenttool.Elicitor

	Stdin  io.Reader
	Stdout io.Writer
	Stderr io.Writer
}

Runner runs one command line against a set of tools: one call of one tool, or the program's own help. The zero value of each stream is the process's own.

The command line is the program's options, then the command, then the command's flags, then at most one JSON argument:

program [--answer <reply>]... [--record <file>] [--out <dir>] <command> [--<param> <value>]... [<json> | -]

The program's options come before the command, so that a tool's flags are named by its schema alone and no parameter is shadowed. --answer answers the questions the call asks, in order; --record appends every agenttool.Record the call writes to a file as JSON lines; --out is where files the output carries are written. The commands help and schema print a command's usage and its JSON Schema.

Run is one call, so it closes nothing: the program that built the tools closes them when Run returns, with agenttool.Set.Close.

func (Runner) Run

func (r Runner) Run(ctx context.Context, args []string) int

Run runs args, the command line without the program's name, and returns the exit status. ctx is the call's: cancelling it, as signal.NotifyContext does on an interrupt, is the interrupt the tool receives.

Jump to

Keyboard shortcuts

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