Agent orchestration · Go CLI · MIT
Orchestrate every model your agent runs on.
One CLI for the whole supply line. Point opencode, Pi or lucinate at a hosted or local model, serve it yourself, watch every engine you run from one board — and wake a cloud GPU only for as long as you use it.
brew install spinloop-ai/tap/spinloop ╭──────────────────────────────────────────╮ │● studio running (up 1h 2m 5s) │ │llamacpp Qwen3.6-35B-A3B-GGUF │ │ last active 12s ago │ │ CPU ██████████░░░░░░░░░░░░░░░ 41% │ │ RAM ████████████░░░░░░░░░░░░░ 48% │ │ GPU util ███████████████░░░░░░░░░░ 61% │ │ GPU mem ████████████░░░░░░░░░░░░░ 50% │ │ │ │ running: 1 │ │ prompt tokens: 184320 │ │ generation tokens: 20481 │ │ requests: 37 │ ╰──────────────────────────────────────────╯
╭──────────────────────────────────────────╮ │● gpu-box running (up 22m 41s) │ │vllm Qwen3.8-27B-Instruct │ │ last active 3s ago │ │ CPU ████████████████░░░░░░░░░ 66% │ │ RAM ████████████████████░░░░░ 83% │ │ GPU util ██████████████████████░░░ 91% │ │ GPU mem █████████████████████░░░░ 87% │ │ │ │ running: 3 │ │ prompt tokens: 902144 │ │ generation tokens: 118307 │ │ requests: 214 │ ╰──────────────────────────────────────────╯
╭──────────────────────────────────────────╮
│● aws-h100 starting │
│instance starting; retrying in 42s │
│stopped │
│vllm Qwen3.8-27B-Instruct │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
╰──────────────────────────────────────────╯╭──────────────────────────────────────────╮
│● mac-studio unreachable │
│dial tcp 10.0.0.9:4242: connect: connectio│
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
╰──────────────────────────────────────────╯ ↑↓←→ move s start x stop r refresh q quit
Nine providers built in — name one, spinloop fills in the rest
- OpenRouter
- AWS Bedrock
- Google Vertex · Gemini
- Google Vertex · Claude
- Ollama
- llama.cpp
- oMLX
- vLLM
- Any OpenAI-compatible endpoint
Adding one that isn't here is a data change, not code.
The problem
Changing model is never just changing the model.
Every agent keeps its config somewhere different, in a shape of its own. Pointing one at a new provider means getting the base URL, the model id, the package it loads and the name of the environment variable holding your key all correct at once. One stray brace and the agent won't start.
And the model you want changes by the day — a frontier model on OpenRouter for the hard problems, a local Qwen when you're offline or cost-conscious, Claude on Bedrock for work.
-
Local models are the worst of it
Each runtime has its own ports, model refs and quirks — llama.cpp, vLLM, oMLX, Ollama — and none of it is written down where you need it.
-
Then there's the box it runs on
A workstation under the desk, a GPU box on the LAN, a cloud instance you'd rather not leave running. Three machines, three ways to start, stop and check them.
-
Switching should take a second
It usually doesn't. Which is the whole reason spinloop exists.
How it works
It scales with what you're actually running.
Start at the first command and stop there if that's all you need. Each step up reuses the file you already wrote.
- 01
Point
spinloop add -p ollama -m qwen3.6Configures the agent and points it at the model. Everything else in the config stays exactly where you left it.
- 02
Serve
spinloop serveThe same file that configures the agent launches the engine behind it — llama-server, vLLM or oMLX, flags and all.
- 03
Fleet
spinloop fleet dashboardEvery machine runs a daemon behind one small control API. One board watches them all, and starts or stops any of them.
- 04
Cloud
spinloop remote startA GPU instance that only exists while you're using it, boots to a stable address, and stops itself once you've stopped.
The Spinloop file
Like a Dockerfile, but for your coding agent.
One flat, line-oriented file describing one provider selection. Commit it beside the
project, give it a short name with spinloop alias, or publish it
at a URL and let your team fetch it by name.
- The same file applies the config, serves the model, and names the fleet or cloud endpoint behind it.
- The agent is never named in it — pick your harness when you run the command, not when you write the file.
- No secrets in it. Keys are read from an adjacent .env, written 0600, so it stays committable.
PROVIDER llamacpp MODEL unsloth/Qwen3.6-35B-A3B-GGUF:UD-Q4_K_XL ALIAS qwen3.6 CONTEXT 128k # context window OUTPUT 32k # max output tokens PARALLEL 2 # concurrent slots PRESET preset.ini # llama.cpp flags this can't model FLEET fleet.yaml # route the launch to a node
$ spinloop apply # configure the agent $ spinloop serve # run the engine it names $ spinloop harness -O # apply, then launch the agent $ spinloop export > Spinloop # capture what you already have
What you get
Careful with your config, your keys and your machines.
A Go binary with no runtime dependencies. Nothing to run in the background until you ask for it.
-
Your config survives
Settings are merged into what you already have. Other providers, your theme, even your comments stay exactly where you left them.
-
Keys stay where they belong
Read from a local
.env, written owner-only, or kept as an environment reference — never hard-coded somewhere they'll leak. -
Model ids from the source
spinloop list --modelsasks the provider's own endpoint for what it currently serves. Nothing to memorise, nothing to look up. -
Routing picks the machine
prefer: idlespreads work across the fleet;activeconsolidates it. A launch that names a fleet needs no address of its own. -
Logs that outlive the box
Engine and boot output ship off the instance, so you can still read what a cloud machine said after it's gone — which is when you want it most.
-
A published contract
The daemon's control API ships an OpenAPI description with every release, so anything you build against it works from the spec, not guesswork.
Harnesses
Late-bound by design: the file picks the model, the command picks the agent.
-
opencode
DefaultAn in-place JSONC merge that keeps your comments.
addalso sets the chosen model as the default. -
Pi
Registers the provider in Pi's own model catalogue and tells you which model to pick with
/model. -
lucinate
Writes one managed connection and points the startup default at it, so it opens straight onto the model you chose.
$ spinloop add -p ollama -m llama3.2 --harness pi # this command only
See it first
Run a three-node fleet without owning three machines.
The dockerised example brings up real daemons with real auth and a stand-in engine, so you can drive the whole thing before setting up a single machine. No GPU, no cloud account, about a minute.
Read the fleet guide$ cd examples/fleet-docker && cp .env.example .env $ docker compose up -d --build $ set -a && . ./.env && set +a $ spinloop fleet status --fleet ./fleet.yaml NODE STATE SERVING studio running llamacpp org/fake-model (up 1m 4s) gpu-box idle laptop idle
Point your agent at anything. Then run what's behind it.
One Go binary, no runtime dependencies, MIT licensed.
brew install spinloop-ai/tap/spinloop
or build from source — go build -o spinloop ./cmd/spinloop