Guide
Swarms as an agent tool
Your agent hands a task to a swarm and applies the patch that comes back. It does this by running
the acyclic CLI, the same way it runs git or your tests.
Use the CLI when the agent works on a local checkout: --dir . sends the
swarm your working files. If your agent has no shell and the code is in a public repository, connect it over MCP instead.
1. Set up once
Do this yourself, not through the agent: the login prints a link and a code, and a person has to approve it. The session lasts 90 days.
npm install -g @acyclic-labs/cli acyclic swarm login --site https://acyclic.dev --no-browser acyclic swarm whoami
For CI, log in once and give the job your ~/.acyclic directory as a secret.
It holds a login to your account, so revoke it from the dashboard if it leaks.
2a. Agents with a shell
Claude Code, Codex and Cursor need no wrapper. Add this to the project's CLAUDE.md or AGENTS.md. llms.txt walks the agent through the rest.
## Swarms For a large task that splits into parts (a migration, a failing test suite, a wide refactor), hand it to an Acyclic swarm by following https://acyclic.dev/llms.txt. Before uploading, run `acyclic swarm run --dir . --dry-run` and check the file list. After `acyclic swarm pull <id> --apply`, read `git diff --cached` and run the tests before committing.
2b. Your own harness
Wrap five commands as tools. Each one runs acyclic swarm … and hands
its output to the model, which passes the swarm id from one call to the next.
| Tool | acyclic swarm … | Gives back |
|---|---|---|
| swarm_start | run --task … --dir . --idempotency-key … | The swarm id, sw_… |
| swarm_wait | watch <id> | Exits when the swarm ends: 0 succeeded, 2 failed, 3 canceled, 1 CLI error |
| swarm_status | show <id> --json | State, agent tree, open questions, spend, result |
| swarm_answer | answer <id> --option <n> | Answers a question the swarm asked |
| swarm_apply | pull <id> --apply | Stages the patch in the checkout |
The definition the model sees for the first tool:
{
"name": "swarm_start",
"description": "Hand a coding task to an Acyclic swarm: several agents, each in its own cloud sandbox, working on a copy of the current directory. Returns a swarm id. Call swarm_wait with it next.",
"input_schema": {
"type": "object",
"required": ["task"],
"properties": {
"task": {
"type": "string",
"maxLength": 4000,
"description": "The goal, the constraints, and how to check the result."
}
}
}
} And the code behind the first two. Pass arguments as a list, not through a shell, so a task with
quotes in it arrives intact. A swarm takes minutes, so give the wait a long timeout and let the
model fall back to swarm_status.
import { execFile } from "node:child_process";
import { promisify } from "node:util";
const acyclic = (args: string[], timeout = 60_000) =>
promisify(execFile)("acyclic", ["swarm", ...args], { timeout, maxBuffer: 16 << 20 });
// key: your tool call's id, so retrying the same call reuses the swarm it started.
export async function swarmStart({ task }: { task: string }, key: string) {
const { stdout } = await acyclic(["run", "--task", task, "--dir", ".", "--idempotency-key", key]);
return stdout.trim(); // sw_...
}
export async function swarmWait({ swarm_id }: { swarm_id: string }) {
try {
await acyclic(["watch", swarm_id], 30 * 60_000);
return "succeeded";
} catch (error: any) {
if (error.killed) return "still running: call swarm_status";
if (error.code === 2) return "failed";
if (error.code === 3) return "canceled";
return `error: ${error.stderr}`;
}
}3. Write the task like a ticket
Give the goal, what not to touch, and how to check the result, in at most 4,000 characters. For
example: "Fix the failing tests in tests/api. Don't edit the tests. Done when pytest passes."
Longer instructions go in a SKILL.md passed with --skill.
Good to know
--dirleaves out.git,.env*, keys and ignored files.run --dir . --dry-runlists what would go up without sending anything.- Retrying a start after a timeout? Reuse its
--idempotency-key. You get the swarm that already started, not a second bill. - A swarm may ask a question, but it never waits for the answer. It carries on with the option it thinks best, so your harness can answer or ignore it.
pull --applystages the change: review it withgit diff --cached, since a plaingit diffshows nothing.--pull-requestopens a GitHub pull request instead. It needs--repoand a linked GitHub account.--toolgives the swarm one of your saved MCP servers.- Every extra agent is another sandbox you pay for. Leave
--widthat the default unless the task really splits into parts. - On acyclic.dev the CLI refuses
--model,--verify,--from,--boxesand--merge-requires. Those only work against a host you run yourself. - Errors end with
[X-Trace-Id: …]. Log it, and quote it if you contact us.