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.
| Variable | Value |
|---|---|
SPOTLIGHT_HANDOVER_URL | The address of the run's loopback server, as http://127.0.0.1:<port>/. |
SPOTLIGHT_HANDOVER_TOKEN | A random token for this run, sent as Authorization: Bearer. |
SPOTLIGHT_STATE_DIR | The harness's state directory, /workspace/.spotlight/state. |
Without SPOTLIGHT_HANDOVER_URL and SPOTLIGHT_HANDOVER_TOKEN, each command
fails:
| Command | Message |
|---|---|
handover | no run to hand over: spotlight runs inside a Spotlight workspace |
reply | no run to answer for: spotlight runs inside a Spotlight workspace |
trace | no run to read a trace from: spotlight runs inside a Spotlight workspace |
history | no run to read history from: spotlight runs inside a Spotlight workspace |
recall | no run to recall from: spotlight runs inside a Spotlight workspace |
remember | no run to remember from: spotlight runs inside a Spotlight workspace |
candidate | no run to propose a candidate from: spotlight runs inside a Spotlight workspace |
cause | no run to record a cause from: spotlight runs inside a Spotlight workspace |
tools, tools search, tools describe | no 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 success | 0 |
| Exit code on any error | 1, with the message on standard error |
| Request timeout | 2 minutes |
| A refusal from the server | Printed as the server wrote it. |
| A token that does not match | not this run's token |
| Spotlight unreachable | reach 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.
| Flag | Required | Description |
|---|---|---|
--next | yes | Where the task goes. Matched without regard to case. |
--note | no | What 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.
| Output | When |
|---|---|
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 |
| Message | Cause |
|---|---|
--next is required | No --next. The usage follows. |
"<next>" is not one of: <options> | --next names nothing on offer. The options are listed. |
unreadable handover | The 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.mdWithout 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.
| Flag | Required | Description |
|---|---|---|
--to | yes | The remark's handle. |
--body | yes | The answer. Omitted, - or /dev/stdin: read from standard input. |
--resolve | no | Resolves the thread as well. |
| Output | When |
|---|---|
Answered <handle>. | Without --resolve. |
Answered <handle>, and resolved it. | With --resolve. |
| Message | Cause |
|---|---|
--to is required | No --to. |
--body is required | No body, and nothing on standard input. |
say which remark | The handle is blank. |
say something back | The body is blank. |
unreadable answer | The 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.mdspotlight trace
spotlight trace --task <id> [--out <path>]Writes a task's trace to a file as indented JSON.
| Flag | Required | Description |
|---|---|---|
--task | yes | The task whose trace to write. |
--out | no | The 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.
| Message | Cause |
|---|---|
--task is required | No --task. |
say which task | The 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.
| Flag | Required | Description |
|---|---|---|
--station | yes | The station's key, matched exactly. |
--line | no | The line's key. Needed when more than one of the team's lines has a station with that key. |
--fingerprint | no | Only runs of this config: its full config fingerprint. |
--outcome | no | Only runs whose outcome holds this word. See Outcome words. |
--since | no | Only runs queued at or after this date or time, such as 2026-09-01. A value that is not a date is ignored. |
--last | no | How many runs. Default: 30. At most 50; a larger number counts as 50, and 0 as the default. |
--out | no | The 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.
| File | Holds |
|---|---|
index.md | A 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>.yml | One 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.md | The 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.json | runId, taskId, fingerprint and outcome, which is null for a run with no recorded outcome. |
runs/<run>/diff.patch | The 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:
| File | Holds |
|---|---|
current.json | The station block the search started from, the live config when it began. |
index.md | After 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.json | The candidate's station block. |
candidates/<name>/changes.diff | How block.json differs from current.json, as a unified diff. Left out when they are the same. |
candidates/<name>/hypothesis.md | What the agent that proposed it expected it to improve. |
candidates/<name>/score.json | succeeded 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.md | The 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.json | runId, original, repeat (which copy, from 1), taskId, fingerprint, state, cost, succeeded, reasons and verdict (pass, fail or null). |
candidates/<name>/runs/<original run>/diff.patch | The 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.
| Part | Words |
|---|---|
| Ending | completed, failed, stopped, abandoned, withdrawn, superseded |
| Failure code | A failed or superseded run's code, such as exited. See Failure codes. |
| Next person act | started, parked, closed, answered, approved, refused, sent-back |
| Delivery | open, merged, closed |
| Checks | checks-passed, checks-failed, checks-skipped |
Station outcomes says what each part means.
| Message | Cause |
|---|---|
--station is required | No --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 team | The 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 10spotlight 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-21The last line says where the memory came from:
| Last line | The 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.
| Message | Cause |
|---|---|
say what to look for | No 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-12spotlight 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.
| Flag | Required | Description |
|---|---|---|
--kind | yes | rationale, rejected-approach, failure, environment, preference or correction. |
--body | yes | What to remember, at most 1,000 characters. Omitted, - or /dev/stdin: read from standard input. |
--path | no | A 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. |
--reach | no | Where it applies: team, line or station. Default: line. |
--supersedes | no | The 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.
| Message | Cause |
|---|---|
--kind is required | No --kind. The usage follows. |
--body is required | No 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.mdspotlight 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.
| Flag | Required | Description |
|---|---|---|
--name | yes | A slug unique in the search: lowercase letters, digits and hyphens, starting with a letter or digit, at most 63 characters. Not seed. |
--hypothesis | yes | What 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.
| Message | Cause |
|---|---|
--name is required | No --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 required | No --hypothesis. |
name one file holding the station block, or - to read it from stdin | No file, or more than one. |
<path> is HCL; give the station block as JSON, as the line resource holds a station | The file ends in .tf or .hcl. |
<path> holds no JSON object; it holds the station block, as the line resource holds a station | The 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 do | The 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 it | At station.<field>: the block changes another field. |
name cannot be reserved name seed | At name: the name is seed, which names the live config the search started from. |
This search has a candidate named <name> already; name it otherwise | Another iteration took the name. |
This search iteration has proposed its <n> candidates, or tried to too often; end the run | The 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 cause | The run is a reflection's. |
The search no longer holds this run | The 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.jsonspotlight 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.
| Flag | Required | Description |
|---|---|---|
--task | yes | The task's id, as the briefing lists it. |
--cause | yes | config, task, judge, check or infra. See Causes. |
--evidence | yes | What in the runs shows the cause, in 1 to 4,000 characters. @<file> reads it from a file. |
--suggestion | no | What 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.| Message | Cause |
|---|---|
--task is required | No --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 required | No --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 do | The run is not a search's. |
This search iteration proposes candidates; only an iteration that looks into why the search stalled records causes | The 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 characters | The evidence is over 4,000 characters. |
A cause's suggestion says what to fix in at most 4,000 characters | The 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 it | The 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 toolsLists 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 part | Meaning |
|---|---|
--<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.
| Message | Cause |
|---|---|
no such command "tools <word>" | A word after tools other than search or describe. The usage follows. |
spotlight tools search
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.
| Flag | Default | Description |
|---|---|---|
--limit | 10 | How 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:
| Match | Points |
|---|---|
| A word of the field equals it | 10 |
| A word of the field starts with it | 6 |
| It starts with a word of the field of 3 letters or more | 4 |
| A word of the field contains it | 2 |
| The field contains it anywhere | 1 |
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| Message | Cause |
|---|---|
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 does | No 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 3spotlight 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:
| Part | Content |
|---|---|
| Heading | The 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.
| Schema | TypeScript |
|---|---|
string, number, integer, boolean, null | string, number, boolean, null |
enum, const | A union of the literal values, as in "open" | "closed" |
anyOf, oneOf, a list of types | A union, as in number | null |
allOf | An intersection, as in A & B |
array with items | T[], or unknown[] without items |
object with properties | { name: T; … }, with [key: string]: T for a schema in additionalProperties |
object without properties | Record<string, unknown> |
| Anything else, or nested deeper than 6 levels | unknown |
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 "…"| Message | Cause |
|---|---|
say which tool | No 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-statusLearned 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.
| Rule | Value |
|---|---|
| Depth | 5 levels. Deeper values are unknown. |
| Items of a list | The first 20. |
| Parts | 400 per answer. Past that, unknown. |
| Keyed by data | An 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 keys | When 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 out | Optional, marked ?. |
| Different kinds for one field | A 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.
| Input | How 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.
| Message | Cause |
|---|---|
no such command "<name>" | No granted tool has that name. The usage and the run's tools follow. |
--<input> is required | A 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 flag | A 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 is | A 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 JSON | The 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"