Start

First run in the terminal

What the first launch asks, how a turn is shaped, and which keys drive it.

The first launch picks a model

Run shoal in a terminal with nothing configured and it asks before it starts. When the binary is loose, it offers to install itself first. Then it lists the models it found on this machine, numbered, with their sizes:

  models on this machine:

    1  gpt-oss-20b-MXFP4.gguf                     12.0 GB

  moor which model? [1]

The choice is saved as your user-wide default, so every launch after this one runs on it without asking. Change it any time with shoal config set model <name>. When the machine has no models at all, the wizard prints where to get llama.cpp and where to put a GGUF, then stops.

The wizard only runs when somebody is there to answer it, meaning an interactive terminal with no model already resolved and no -p prompt.

Starting unmoored is a real state

Every other launch with nothing configured starts anyway. No dock is detected and no server starts. The agent runs on a provider that refuses every model call with one sentence:

no model is moored: pick one from the mooring form, or pass --model

Nothing crashes. The session is live, commands work, and the fleet lists. This is the state a piped or headless launch lands in, and the message names the flag that fixes it.

The terminal keeps every finished line

Shoal does not take over the screen. A small live region at the bottom holds the status lines, the input box, and any approval prompt. Everything that finishes is printed above it into the terminal’s own scrollback, so completed output has native selection, native search, and it is still there after you quit.

A turn moves through phases and each one gets a live line while it runs. The lines say sending to model, planning tools, inferencing with its loop number, and delegating. A tool call opens its own line and, when it returns, that line is replaced by a block showing what went in and what came out. A sub-agent’s work renders indented under the parent. The turn ends with elapsed time and a step count.

When stdin is not a terminal, the surface degrades to plain lines. Each input line is one turn, streamed text prints as it arrives, and write and shell tools stay denied unless the run was started with --auto-approve.

Approvals take a single key

Before a write, an edit, or a patch, Shoal shows the diff it computed from the tool arguments and the file on disk, then waits for one key.

KeyEffect
yAllow this call
aAllow this tool for the rest of the session
nDeny this call

ESC, Ctrl+C, and Enter also deny. Any other key is ignored while the prompt is up. A tool a sub-agent asks for comes to the same prompt. A denied call never runs, and the transcript records the denial in place of the call.

Enter steers and ESC interrupts

Typing while a turn is running and pressing Enter queues your text for the loop’s next iteration instead of starting a new turn. The interjection is printed into the transcript so it stays visible in the history. ESC interrupts the running turn. Ctrl+C is ignored mid-turn and quits when idle, and Ctrl+D quits on an empty input line. The up and down arrows cycle earlier input.

/help prints the slash command menu and /shortcuts prints the key list. Both read from the same tables the reference pages on this site are built from.

Sessions and servers outlive a run

Every message is stored as it happens, and a session is titled from your first line. shoal sessions lists what is stored, newest first. /sessions lists the recent ones from inside a session. Pick one back up with --resume <id> at launch or /resume <id> in the session, and an unambiguous id prefix is enough.

A persistent server means the model loads once instead of once per run:

shoal up --model <path>        # start a server and leave it running
shoal --model <path>           # this run adopts the healthy server
shoal down                     # the deliberate stop

A run that finds a healthy server on the port uses it and leaves it running on exit. A run that finds nothing spawns its own server and stops it when the session ends. The server binds to 127.0.0.1 and is never exposed to the network.