Agent commands

An agent runs these commands from anywhere in its sandbox. Each one talks to a server the runner starts on the loopback interface for that run. They work only inside a Spotlight run.

spotlight handover --next <where> [--note <text>]
spotlight reply --to <handle> [--body <text>] [--resolve]
spotlight trace --task <id> [--out <path>]
spotlight history --station <key> [--line <key>] [--fingerprint <fp>] [--outcome <word>] [--since <date>] [--last <n>] [--out <dir>]
spotlight recall "<words>" | <handle>
spotlight remember --kind <kind> --body <text> [--path <path>]... [--reach <reach>] [--supersedes <handle>]
spotlight candidate --name <name> --hypothesis <text | @file> <station.json | ->
spotlight cause --task <task> --cause config|task|judge|check|infra --evidence <text | @file> [--suggestion <text | @file>]
spotlight tools
spotlight tools search "<words>" [--limit <n>]
spotlight tools describe <tool>
spotlight <tool> [--<input> <value>...] [--json <object> | --json -]

In a search's agent run, reply, recall and remember are refused with a search may not <answer a remark, recall what the team remembers or propose a memory>: it studies the station's history and proposes station configs, nothing more.

The tools Spotlight itself gives are listed in Built-in tools and skills. Why agents talk back through a command line gives the reasons.

Environment

The runner sets these in the harness's environment. Every other SPOTLIGHT_ variable is removed from it.

VariableValue
SPOTLIGHT_HANDOVER_URLThe address of the run's loopback server, as http://127.0.0.1:<port>/.
SPOTLIGHT_HANDOVER_TOKENA random token for this run, sent as Authorization: Bearer.
SPOTLIGHT_STATE_DIRThe harness's state directory, /workspace/.spotlight/state.

Without SPOTLIGHT_HANDOVER_URL and SPOTLIGHT_HANDOVER_TOKEN, each command fails:

CommandMessage
handoverno run to hand over: spotlight runs inside a Spotlight workspace
replyno run to answer for: spotlight runs inside a Spotlight workspace
traceno run to read a trace from: spotlight runs inside a Spotlight workspace
historyno run to read history from: spotlight runs inside a Spotlight workspace
recallno run to recall from: spotlight runs inside a Spotlight workspace
rememberno run to remember from: spotlight runs inside a Spotlight workspace
candidateno run to propose a candidate from: spotlight runs inside a Spotlight workspace
causeno run to record a cause from: spotlight runs inside a Spotlight workspace
tools, tools search, tools describeno run to reach for tools in: spotlight runs inside a Spotlight workspace
<tool>could not read this run's tools to find "<name>": no run to reach for tools in: spotlight runs inside a Spotlight workspace

Common behaviour

Value
Exit code on success0
Exit code on any error1, with the message on standard error
Request timeout2 minutes
A refusal from the serverPrinted as the server wrote it.
A token that does not matchnot this run's token
Spotlight unreachablereach Spotlight: <error>

spotlight handover

spotlight handover --next <where> [--note <text>]

Says where the task goes when the run ends. The run carries on until the agent's turn ends.

FlagRequiredDescription
--nextyesWhere the task goes. Matched without regard to case.
--notenoWhat the next station or person needs to know. Omitted, - or /dev/stdin: read from standard input to end of file, with surrounding whitespace trimmed.

--next takes one of the values the run is offered: the keys of the stations a run at the current station may pass to, in line order, then review, then done where the current station ends the line. When each is offered, and what it does to the task, is in Handover.

Running handover again replaces the earlier choice. The last accepted one counts.

OutputWhen
Handed over to <next>.A station was named.
Put to a person. The task waits here until someone answers.--next review
Closed where it stands.--next done
MessageCause
--next is requiredNo --next. The usage follows.
"<next>" is not one of: <options>--next names nothing on offer. The options are listed.
unreadable handoverThe request body was not JSON.
read the note: <error>Standard input could not be read.
spotlight handover --next code-review --note "Added retries to the webhook client."
spotlight handover --next review < question.md

Without a handover

If the agent never runs handover, the runner reads /workspace/.spotlight/outcome.json when the harness exits:

{ "next": "code-review", "note": "Added retries to the webhook client." }

The file is ignored when it is missing, is not JSON, or has an empty next. A file an earlier run left is removed when a run starts. With neither a handover nor the file, the task goes to a person. See Handover.

spotlight reply

spotlight reply --to <handle> [--body <text>] [--resolve]

Answers a remark on the task's pull request. The run's briefing lists the open remarks and their handles, such as c12.

FlagRequiredDescription
--toyesThe remark's handle.
--bodyyesThe answer. Omitted, - or /dev/stdin: read from standard input.
--resolvenoResolves the thread as well.
OutputWhen
Answered <handle>.Without --resolve.
Answered <handle>, and resolved it.With --resolve.
MessageCause
--to is requiredNo --to.
--body is requiredNo body, and nothing on standard input.
say which remarkThe handle is blank.
say something backThe body is blank.
unreadable answerThe request body was not JSON.
read the answer: <error>Standard input could not be read.
spotlight reply --to c12 --body "Renamed, thanks." --resolve
spotlight reply --to c14 < answer.md

spotlight trace

spotlight trace --task <id> [--out <path>]

Writes a task's trace to a file as indented JSON.

FlagRequiredDescription
--taskyesThe task whose trace to write.
--outnoThe file to write. Default: a new file spotlight-trace-*.json in the temporary directory.

On success it prints one line: Wrote the trace to <path>, the number of nodes, the number of faults, the time the trace spans, and advice to search the file rather than load it whole.

MessageCause
--task is requiredNo --task.
say which taskThe task id is blank.
open <path>: <error>--out could not be created.

spotlight history

spotlight history --station <key> [--line <key>] [--fingerprint <fp>] [--outcome <word>] [--since <date>] [--last <n>] [--out <dir>]

Writes a station's recent runs to files: the config each ran with, its journal, its outcome and its file changes. Only stations on the lines of the run's own team are read. Runs are the station's ended task runs, newest first.

FlagRequiredDescription
--stationyesThe station's key, matched exactly.
--linenoThe line's key. Needed when more than one of the team's lines has a station with that key.
--fingerprintnoOnly runs of this config: its full config fingerprint.
--outcomenoOnly runs whose outcome holds this word. See Outcome words.
--sincenoOnly runs queued at or after this date or time, such as 2026-09-01. A value that is not a date is ignored.
--lastnoHow many runs. Default: 30. At most 50; a larger number counts as 50, and 0 as the default.
--outnoThe folder to write into. Default: .spotlight/history in the current directory.

The runs are written to <out>/<station>/. Anything already in that folder is removed first.

FileHolds
index.mdA table of the runs: each run, linked to its journal, then its task's title, config, ending, checks, the next person act, delivery, round, cost, steps, duration and start. A run with no recorded outcome shows its state as its ending. Then each config, oldest first: its fingerprint, its file, how many of the runs used it, what changed since the config before it, and the prompt's change as a diff.
configs/<fingerprint>.ymlOne config, as YAML: fingerprint, harness, model, effort, image, profile, repositories, skills, tools, decisions, memories, judge and prompt. judge holds rubricDigest, sample and trust, or is null.
runs/<run>/journal.mdThe run's steps and spans: each step's text, what a tool was given and returned, and each file change as a diff.
runs/<run>/outcome.jsonrunId, taskId, fingerprint and outcome, which is null for a run with no recorded outcome.
runs/<run>/diff.patchThe run's file changes as a unified diff. Written only for a run that changed files. Files are taken in the order they were first changed. A file whose text before and after is over 256,000 characters together, or does not fit in what is left of the run's 1,000,000 characters of changed-file text, is listed as Omitted <path>: too large to send whole.

index.md shows a fingerprint by its first 12 characters, and none for a run with no recorded config.

On success it prints three lines: Wrote the history to <folder>, the number of runs and configs, and a grep -r command to search the folder. In a search's run, a line before the last reads <n> candidates proposed so far, under <folder>/candidates.

In a search's run

In a search's agent run, only the searched station is read; any other is answered as a station no line has. The runs of the search's held-out tasks are left out, and every run of the searched tasks is written, beyond --last if need be. The folder also holds:

FileHolds
current.jsonThe station block the search started from, the live config when it began.
index.mdAfter the runs and configs, a "Candidates" table: each candidate, linked to its score.json, its config, how many of its replay runs succeeded and were scored, its repeats, its rate, and its hypothesis. When a candidate replayed each run more than once, a line under the table says where its copies are and how its rate is worked out.
candidates/<name>/block.jsonThe candidate's station block.
candidates/<name>/changes.diffHow block.json differs from current.json, as a unified diff. Left out when they are the same.
candidates/<name>/hypothesis.mdWhat the agent that proposed it expected it to improve.
candidates/<name>/score.jsonsucceeded and scored (replay runs, copies included), repeats (copies of each run), tasksScored (runs of the set with a scored copy), rate (for each of those, the share of its scored copies that succeeded, averaged; null while none was scored), failed (the tasks it failed, each once), verdicts (how many runs the judge passed and failed, as pass and fail), spent (per currency) and unpriced (runs with no cost).
candidates/<name>/runs/<original run>/journal.mdThe replay run's steps, as for a run. When the candidate replayed each run more than once, each copy is under runs/<original run>/<copy>/ instead, numbered from 1, and so are the two files below.
candidates/<name>/runs/<original run>/result.jsonrunId, original, repeat (which copy, from 1), taskId, fingerprint, state, cost, succeeded, reasons and verdict (pass, fail or null).
candidates/<name>/runs/<original run>/diff.patchThe replay run's file changes, when it changed files.

The candidate named seed is the live config the search started from, replayed on the searched tasks. A candidate's runs are its replay's runs on the searched tasks, one per task run of the set and copy.

Outcome words

--outcome keeps a run when the word is one of these parts of its outcome. A run with no recorded outcome never matches. With --outcome, the latest 500 ended runs are searched, and --last of the matches are kept.

PartWords
Endingcompleted, failed, stopped, abandoned, withdrawn, superseded
Failure codeA failed or superseded run's code, such as exited. See Failure codes.
Next person actstarted, parked, closed, answered, approved, refused, sent-back
Deliveryopen, merged, closed
Checkschecks-passed, checks-failed, checks-skipped

Station outcomes says what each part means.

MessageCause
--station is requiredNo --station. The usage follows.
"<key>" is not a station key--station holds / or \, or is . or ...
--last counts runs, so it cannot be <n>--last is negative.
no line in this team has a station "<key>"No line of the run's team has the station.
line "<line>" has no station "<key>" in this teamThe named line has no such station, or is not the team's.
Several lines have a station by that key; name one with --line: <lines>More than one of the team's lines has the station, and no --line.
clear <folder>: <error>The earlier history could not be removed.
make <folder>: <error>A folder could not be made.
write <path>: <error>A file could not be written.
spotlight history --station implementation
spotlight history --line delivery --station implementation --outcome sent-back --last 10

spotlight recall

spotlight recall "<words>"
spotlight recall <handle>

Reads what the run's team remembers, beyond the memories its briefing lists. Only memories that stand in the run's own team are read, never proposed, rejected or retired ones, and never another team's.

Given words, it searches the bodies of the team's standing memories and prints at most ten, the best match first. Words are matched by their English stem, so retry finds retries. Each is printed as:

M-12 · rejected approach · line, in acme/api at src/uploads
  Retrying uploads from a queue needs a worker we do not run.
  From a run on task <task id>, approved by Ada Lovelace on 2026-09-21

The last line says where the memory came from:

Last lineThe memory
From a run on task <task id>, approved by <name> on <date>Was drawn from a run of the task.
From what a person said on task <task id>, approved by <name> on <date>Was drawn only from what people said on the task, or was edited on the Memory page from one drawn from the task.
From a run on input from outside the team on task <task id>, approved by <name> on <date>Was drawn from a task that came from outside the team, or from what another tool said on the task.
Written by <name> on <date>Was written on the Memory page.

<name> is someone when the person is not known. With no match it prints The team remembers nothing about "<words>". Try other words.

Every memory a recall returns is recorded against the run, and shows under "Given to the agent" in "Remembered" on its Run page; the recall itself is a spotlight.recall span in the task's trace.

Given a handle, such as M-12 or m-12, it prints that one memory in full: its kind, whether it stands or was retired, its body, where it applies, the steps and notes it rests on, the spotlight trace command for its task, the memory it replaced, and, once retired, why and what replaced it.

MessageCause
say what to look forNo words. The usage follows.
Memory <handle> not found.No memory has that handle in the run's team.
A memory is recalled by at most 500 characters of words it holds, or by its handle, like M-12.The words are more than 500 characters.
spotlight recall upload retries
spotlight recall M-12

spotlight remember

spotlight remember --kind <kind> --body <text> [--path <path>]... [--reach <reach>] [--supersedes <handle>]

Proposes a memory from inside the run. It rests on the step the agent ran the command from; where that step can't be found, on the last thing the agent said. It waits in the inbox beside the run, with anything drafted from the run, until a teammate approves, edits or rejects it, and is kept only once approved. A memory from a task that came from outside the team cannot be approved together with other memories. Proposing the same memory twice from one run keeps one.

FlagRequiredDescription
--kindyesrationale, rejected-approach, failure, environment, preference or correction.
--bodyyesWhat to remember, at most 1,000 characters. Omitted, - or /dev/stdin: read from standard input.
--pathnoA file or folder the memory bears on, relative to the current directory or absolute. It must be inside one of the run's checkouts, and every --path in one repository. Within its repository, at most 500 characters. Repeat for more, up to 20 different paths.
--reachnoWhere it applies: team, line or station. Default: line.
--supersedesnoThe handle of a standing memory of the team that this one corrects. Approving it retires that one.

On success it prints Proposed <kind> for the <reach>: <body>, and that the memory waits for a person's approval.

MessageCause
--kind is requiredNo --kind. The usage follows.
--body is requiredNo body, and nothing on standard input.
put everything in flags, not "<word>"A word outside any flag.
A memory kind must be one of rationale, rejected-approach, failure, environment, preference, correction, not <kind>.--kind names no kind.
A memory reach must be one of team, line, station, not <reach>.--reach names no reach.
<path> is outside the repositories this run checked out; name a path inside one of: <directories>A --path is outside every checkout.
A memory bears on paths of one repository at a time.The paths are in different repositories.
A memory must be at most 1000 characters.The body is more than 1,000 characters.
A memory path must be at most 500 characters.A --path is more than 500 characters within its repository.
A memory can cover at most 20 paths.More than 20 different paths.
A memory supersedes only a memory standing in the run's team, and <handle> is not one.--supersedes names a retired memory of the team.
Memory <handle> not found.--supersedes names no memory of the run's team.
A memory is remembered only from a task's run; in a chat, ask the person to write it on the team's Memory page.The run is a chat's.
spotlight remember --kind rationale --body "Uploads retry in the request, not from a queue." --path src/uploads
spotlight remember --kind correction --supersedes M-7 < correction.md

spotlight candidate

spotlight candidate --name <name> --hypothesis <text | @file> <station.json | ->

Proposes a station config from a search's agent run. The file holds the station block as JSON, in the shape of one entry of a line's stations; - reads it from standard input. Spotlight checks it, then replays it on the search's searched tasks. Only a search's runs may propose a candidate.

FlagRequiredDescription
--nameyesA slug unique in the search: lowercase letters, digits and hyphens, starting with a letter or digit, at most 63 characters. Not seed.
--hypothesisyesWhat the change should improve, and why, in 1 to 4,000 characters. @<file> reads it from a file.

The block may change only the station's prompt, harness, model, effort, profile, skills, tools, decisions, memories and description, from the block the search started from. Proposing the same name again from the same run answers with the candidate it proposed.

On success it prints:

Proposed <name> as candidate <id>.
Spotlight replays it as <replay id>; its results show in spotlight history once the replay ends.

A refused block prints Spotlight refused the candidate:, one line per problem as <path>: <message>, a warning marked (<severity>, not why it was refused), and Fix the station block and propose it again. A refused proposal does not count toward the iteration's candidates, but an iteration may try at most three times as many as it may propose.

MessageCause
--name is requiredNo --name. The usage follows.
"<name>" cannot name a candidate: a name is a slug of lowercase letters, digits and hyphens, starting with a letter or digit, at most 63 characters--name is no slug.
--hypothesis is requiredNo --hypothesis.
name one file holding the station block, or - to read it from stdinNo file, or more than one.
<path> is HCL; give the station block as JSON, as the line resource holds a stationThe file ends in .tf or .hcl.
<path> holds no JSON object; it holds the station block, as the line resource holds a stationThe file is not a JSON object.
<file> holds no hypothesis; say what you expect the change to do@<file> is empty.
this run cannot propose a candidate: only a search's runs doThe run is not a search's.
A search may change only the station's prompt, harness, model, effort, profile, skills, tools, decisions, memories, description; keep <field> as the search's starting config has itAt station.<field>: the block changes another field.
name cannot be reserved name seedAt name: the name is seed, which names the live config the search started from.
This search has a candidate named <name> already; name it otherwiseAnother iteration took the name.
This search iteration has proposed its <n> candidates, or tried to too often; end the runThe iteration may propose no more.
This search iteration looks into why the search stalled, so it proposes no candidates; record a cause for each failing task with spotlight causeThe run is a reflection's.
The search no longer holds this runThe search stopped, or moved on from this iteration.

Other problems with the block are those of a replay candidate, at their path under station.

spotlight candidate --name terse-prompt --hypothesis @why.md station.json

spotlight cause

spotlight cause --task <task> --cause config|task|judge|check|infra --evidence <text | @file> [--suggestion <text | @file>]

Records why a failing task failed, from the agent run of a search's reflection. Only the tasks its briefing lists take a cause. Recording a task's cause again replaces the one before.

FlagRequiredDescription
--taskyesThe task's id, as the briefing lists it.
--causeyesconfig, task, judge, check or infra. See Causes.
--evidenceyesWhat in the runs shows the cause, in 1 to 4,000 characters. @<file> reads it from a file.
--suggestionnoWhat a person, or the search's next iteration, should change, in at most 4,000 characters. @<file> reads it from a file.

On success it prints:

Recorded <cause> as the cause for task <task>, whose replay run <run> failed.
Recording another cause for the task replaces this one.
MessageCause
--task is requiredNo --task. The usage follows.
--cause is one of config, task, judge, check, infra--cause is missing or names no cause. The usage follows.
--evidence is requiredNo --evidence. The usage follows.
read the evidence: <error>@<file> for --evidence could not be read; read the suggestion for --suggestion.
<file> holds no evidence@<file> is empty; holds no suggestion for --suggestion.
this run cannot record a cause: only a search's runs doThe run is not a search's.
This search iteration proposes candidates; only an iteration that looks into why the search stalled records causesThe run's iteration is not a reflection.
No failing task <task> among the tasks this reflection looks into; record causes only for the tasks its briefing lists--task names a task the briefing does not list.
A cause's evidence says what in the runs shows the cause, in 1 to 4,000 charactersThe evidence is over 4,000 characters.
A cause's suggestion says what to fix in at most 4,000 charactersThe suggestion is over 4,000 characters.
The search no longer holds this run, or The search no longer holds the run <run>, so it takes no more candidates from itThe search stopped, or moved on from this iteration.
spotlight cause --task a17c4e02-9b3d-4f61-8e5a-0c2d7b9f4a13 --cause judge --evidence @evidence.md --suggestion "Drop the rubric line that asks for tests."

spotlight tools

spotlight tools

Lists the tools the run was given, up to 30 of them. Each entry is the tool's signature, its description, and a line for each input that has a description. For a tool acme.comment with a required input issue and an optional piped input body:

spotlight acme comment --issue <string> [--body <string> (or piped in)] [--json <object> | --json -]
  <description>
    --issue  <input description>
Signature partMeaning
--<input> <string>An input that takes text, or one of a list of values.
--<input> <number>An input that takes a number or a whole number.
--<input> <json>An input that takes a list, an object or anything else, written as JSON.
--<input>A boolean input, given as a bare flag.
(or piped in)The input may come from standard input.
[...]Optional, or has a default.
[--json <object> | --json -]Every input at once, as one JSON object. Shown for a tool with inputs.

A tool that requires approval lists [--intent <string>] after its own inputs, and its description ends with the reason a person approves it and Say what you want to do with --intent; the person reads it first.

A run given more than 30 tools prints a summary instead: how many tools it was given, each integration's name with its number of tools, and the search and describe commands. Spotlight's own tools are counted under Spotlight, and catalog tools under the Spotlight catalog.

This run was given 214 tools, too many to list:
  Linear  195
  Petstore  19

Find one with: spotlight tools search "<words>"
Read one with: spotlight tools describe <tool>

A run given no tools prints This run was given no tools.

MessageCause
no such command "tools <word>"A word after tools other than search or describe. The usage follows.
spotlight tools search "<words>" [--limit <n>]

Ranks the run's tools by how well they match the words, and prints the best first. The search runs in the command over the run's tool list. Nothing is sent anywhere.

FlagDefaultDescription
--limit10How many tools to print at most. At least 1.

The words and every tool's name, integration and description are split into lower case words at spaces, punctuation and changes of case, so linear.issueCreate and linear.issue-create both read as linear issue create. Each word of the search scores against each of the three:

MatchPoints
A word of the field equals it10
A word of the field starts with it6
It starts with a word of the field of 3 letters or more4
A word of the field contains it2
The field contains it anywhere1

Only the best match in each field counts. Points in the name count three times, in the integration's name twice, and in the description once. Where the search has two words or more and they appear together, in order, in a field, that field adds 8 points, weighted the same way. A tool that matches every word of the search scores a quarter more. Tools that match nothing are left out. Equal scores rank the tool with fewer words in its name first, then by name. Words of one letter in the search are ignored.

Each result is the tool's signature, the first line of its description (cut at 160 characters), and Needs a person's approval. for a tool that requires approval. The last line names the describe command for the best match.

spotlight linear issue-create --title <string> --team-id <string> [--json <object> | --json -]
  Create an issue.

Read one with: spotlight tools describe linear.issue-create
MessageCause
No tool this run was given matches "<words>". Try other words, or run spotlight tools.No tool matches any word. Printed to standard output, with exit code 0.
say what the tool doesNo words were given. The usage follows.
--limit is how many tools to show, at least 1--limit is below 1. The usage follows.
spotlight tools search "create issue"
spotlight tools search pets by status --limit 3

spotlight tools describe

spotlight tools describe <tool>

Prints one tool the run was given, named as <integration>.<tool> or as two words, as in linear.issue-create or linear issue-create. In order, it prints:

PartContent
HeadingThe tool's name, and (from <integration>) for an integration's tool. Its description follows, with the reason a person approves it for a tool that requires approval.
Takes:The inputs as a TypeScript object type, or Takes nothing. for a tool without inputs.
Answers with (learned from earlier calls, may be incomplete):The answer's shape as a TypeScript type, for a tool with a learned output. Left out otherwise.
For example:One call to start from.

The type marks an optional input, or one with a default, with ?. Each description becomes a /** */ comment above its input. Names that are not JavaScript identifiers are quoted, as in "team-id"?: string.

SchemaTypeScript
string, number, integer, boolean, nullstring, number, boolean, null
enum, constA union of the literal values, as in "open" | "closed"
anyOf, oneOf, a list of typesA union, as in number | null
allOfAn intersection, as in A & B
array with itemsT[], or unknown[] without items
object with properties{ name: T; … }, with [key: string]: T for a schema in additionalProperties
object without propertiesRecord<string, unknown>
Anything else, or nested deeper than 6 levelsunknown

The example call gives every required input without a default, and --intent for a tool that requires approval. Each value is the schema's first examples entry, else its default, const or first enum value, else "…" for text, 0 for a number and true for a boolean. An object gives its required properties the same way, and a list holds one item. Where every input given is text, a number or a boolean, the call uses flags. Otherwise it gives every input as one object with --json.

linear.issue-create (from Linear)
Create an issue. Needs a person's approval before it runs: It posts publicly under the team's name. Using it asks them and does nothing else, so hand over and wait for their answer. Say what you want to do with --intent; the person reads it first.

Takes:
{
  /** What it is about */
  title: string;
  "team-id": string;
  labels?: string[];
  intent?: string;
}

For example:
spotlight linear issue-create --title "…" --team-id "…" --intent "…"
MessageCause
say which toolNo tool was named. The usage follows.
this run was given no tool "<name>"; the nearest are <tools>No granted tool has that name. Up to three that match it best follow, by the search ranking.
this run was given no tool "<name>"No granted tool has that name, and none matches it.
spotlight tools describe linear.issue-create
spotlight tools describe petstore find-pets-by-status

Learned output

After a call to an integration's tool succeeds, Spotlight records the shape of what it answered: field names and types, never values. It does this in the background, and never slows or fails the call. Each later successful call is merged into it. Every shape spotlight tools describe prints is learned.

RuleValue
Depth5 levels. Deeper values are unknown.
Items of a listThe first 20.
Parts400 per answer. Past that, unknown.
Keyed by dataAn object with more than 40 keys, or with a key that is not a plain name (a letter, _ or $, then up to 63 letters, digits, _, $ or -) or that holds 3 digits or 8 hexadecimal characters in a row, is kept as [key: string]: T. Its keys are not stored.
Changing keysWhen a later answer brings an object whose keys are mostly new (more than half of them not seen before), the object is kept as [key: string]: T too, so keys that vary between calls, such as user names or labels, are not stored.
A field one answer leaves outOptional, marked ?.
Different kinds for one fieldA union, as in string | null. More than 4 kinds become unknown.
Answers with (learned from earlier calls, may be incomplete):
{
  id: string;
  url?: string;
}
  • The shape is kept per team and per tool. It is read when a run starts, so it shows in runs that start after a successful call.
  • Redefining or removing the integration forgets it, and so does disbanding the team.

spotlight <tool>

spotlight <integration> <tool> [--<input> <value>...] [--json <object> | --json -]
spotlight <integration>.<tool> [--<input> <value>...] [--json <object> | --json -]

Calls a tool the run was given. A tool named acme.comment is called as spotlight acme comment or spotlight acme.comment. Its flags are its inputs, by the names the tool gives them, as in --team_id or --petId. A built-in tool, such as pull-request, is called by its name alone, as in spotlight pull-request. See spotlight_integration for how an integration defines tools and their inputs.

InputHow it is given
Text or a number--<input> <value>. A number is written in decimal, so 0x10 is refused.
boolean--<input>, present or absent, or --<input>=false
A list, an object or anything else--<input> '<JSON>', as in --filter '{"team": "ENG"}'
Piped--<input> <value>, or --<input> -, or --<input> /dev/stdin, or omitted with standard input not a terminal. Empty piped input counts as not given.

--json <object> gives every input at once as one JSON object, keyed by input name, with each value in its JSON type. --json - or --json /dev/stdin reads the object from standard input. A flag given beside --json replaces that field of the object. When --json reads standard input, no piped input does: an input left out stays out, and naming one with - or /dev/stdin is refused.

Spotlight checks the inputs against the tool's schemas before it calls the host, and refuses a call that does not fit, saying where, as in linear.issue-create takes input, and input.teamId is not text. See Input schemas.

For a tool that requires approval, --intent <phrase> says what the agent wants to do, completing "Spotlight would like to …". It is optional, shown to the person who decides, and never passed to the tool. See What the agent wants.

The tool's result is printed to standard output. When the tool returns {"output": ...}, only the output is printed, as indented JSON with a string included, as in "https://linear.app/acme/issue/ENG-12". Who the tool acted as is recorded on the run and not printed. Any other result is printed as it came. Built-in tools answer with an object, as in {"url": "…"} from pull-request.

MessageCause
no such command "<name>"No granted tool has that name. The usage and the run's tools follow.
--<input> is requiredA required input without a default was not given. The tool's signature follows.
<command>: <flag error>A flag the tool does not take, or a missing value. The tool's signature follows.
<command>: "<value>" follows --<input>, which takes no value; write --<input>=<value>A value after a boolean flag, as in --notify false. The tool's signature follows.
<command>: "<argument>" is not a flagA word outside any flag. The tool's signature follows.
--json takes one JSON object, as --json '{"name": "value"}'The --json value, or what it read from standard input, is not a JSON object. The tool's signature follows.
read the --json piped in: <error>Standard input could not be read for --json -.
--<input> cannot be read from stdin when --json isA piped input was named with - or /dev/stdin while --json read standard input. The tool's signature follows.
<tool> takes <input>, and <where> <why>An input does not fit its schema. See Input schemas.
this run was not given "<name>"; it was given: <tools>The tool is not granted to the run.
unreadable input: hand the tool JSONThe input is over 8 MiB, or is not JSON.
could not read this run's tools to find "<name>": <error>The tool list could not be read.

Other failures are the tool's own error as Spotlight reports it.

spotlight acme comment --issue <issue> --body "Deployed to staging."
git diff | spotlight acme comment --issue <issue>
spotlight petstore find-pets-by-status --status sold
spotlight linear issue-create --input '{"teamId": "<team id>", "title": "Flaky upload test"}'
spotlight linear issue-create --json '{"input": {"teamId": "<team id>", "title": "Flaky upload test"}}'
spotlight linear issue-create --json - < issue.json
spotlight acme comment --issue <issue> --body "Deployed to staging." \
  --intent "post the staging deploy on the issue, publicly under the team's name"

On this page