Configuration
One file, and most of it optional#
swarm init writes a starter swarm.yaml: one agent, nothing listening on a port, and every other setting present as a commented example. Uncomment what you need.
defaults: # inherited by every agent
idle_after: 3s # quiet this long → "idle"
delivery: defer # follows the work, never cuts it
groups: # usable as @dev anywhere
dev: [dev-1, dev-2]
agents:
- name: dev-1
role: dev # a role is a target too: @dev
command: [claude] # any argv; the only required field
workspace: clone # its own copy of the repository
- name: dev-2
role: dev
command: [codex]
- name: review-1
role: review
command: [codex]
delivery: pull # do not interrupt; it runs swarm inbox
Found by walking up
No -c flag? swarm walks up from the working directory trying swarm.yaml, swarm.yml, .swarm.yaml, .swarm.yml — so any subdirectory of your project works. There is no global config: a swarm belongs to a project.
Groups and roles are targets
A role is declared on the agent, a group in one line; both become targets. swarm send @dev "…" reaches the pair without you naming them, and adding a third agent to the group changes nothing else.
Patterns give an agent a state
A regexp matched against the tail of the agent’s screen. When it matches, the agent gets a badge, optionally raises an event, and can even be answered — only for prompts you would always answer the same way.
patterns:
- match: "Run this command\\? \\(y/n\\)"
state: approval
notify: true
reply: "y" # auto-answer — trusted prompts only
Webhooks
It listens, and it reports back#
swarm knows nothing about GitHub, Telegram or your CI, and does not want to. A rule names conditions on paths into an event and renders a body from the same paths — in both directions.
Incoming: rules, not integrations
A path addresses the decoded body, or a header. Conditions are ANDed, and every matching rule fires — so a delivery can wake a reviewer and a triage agent at once.
hooks:
enabled: true
addr: 127.0.0.1:7778
secret_path: .swarm/hook-secret
signature_header: X-Hub-Signature-256
rules:
- name: review-requested
when:
event: pull_request.review_requested
data.member_id: "6aa593d4-…"
to: review-1
message: "a review was asked of you on {data.repository}"
to: is deliberately not templated. A payload must never choose which agent it wakes up — route by member with one rule per member, so the config decides what an identifier means and an unknown one wakes nobody.
Outgoing: the same rules, backwardssince v0.3.0
Conditions on paths into a fleet event, a body rendered from the same paths, a signed POST that is retried. What is at the far end is none of swarm’s business: Telegram, a CI job and a script behind a proxy are the same thing to it.
outgoing:
enabled: true
url: https://ci.example/swarm
secret_path: .swarm/out-secret
signature_header: X-Swarm-Signature
rules:
- name: finished
when: {event: agent.done}
body: "{agent} finished on {data.branch}"
Seven events: agent.started, agent.exited, agent.idle, agent.done, agent.stalled, agent.attention, agent.error.
Why nothing happened
A webhook that does nothing looks the same from outside whether it never arrived, was refused, matched no rule, or reached a stopped agent. Every delivery is recorded in full — and each rule says which condition failed and what was there instead.
Both directions are recorded in the same file, so a delivery in and a notice out sit side by side. The body is on one line: paste it into a file and swarm hook test replays it offline, through the same matching code the listener uses.