Skip to content

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.

swarm.yaml
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
Every key, its default, and what it does

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.

swarm.yaml
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.

swarm.yaml
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.