agentainer 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -29
- package/{swarm.sh → agentainer} +9 -9
- package/agents.example.yaml +2 -2
- package/bin/agentainer.js +1 -1
- package/examples/bug-hunt.yaml +2 -2
- package/examples/existing-repo.yaml +3 -3
- package/examples/research-swarm.yaml +8 -8
- package/examples/software-company.yaml +2 -2
- package/hooks/claude_stop.sh +2 -2
- package/hooks/codex_notify.sh +1 -1
- package/lib/config.py +1 -1
- package/lib/swarm.py +7 -6
- package/llms.txt +15 -15
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -4,8 +4,8 @@ Run a team of coding agents — **Claude Code, Codex, Gemini CLI, Hermes** — s
|
|
|
4
4
|
side in tmux, each in its own directory, each able to message the others only if
|
|
5
5
|
your YAML file says it may.
|
|
6
6
|
|
|
7
|
-
> Formerly **AgentSwarm**. Installed globally
|
|
8
|
-
>
|
|
7
|
+
> Formerly **AgentSwarm**. Installed globally the command is `agentainer`; from a
|
|
8
|
+
> clone, the `./agentainer` script in the repo root is the same thing.
|
|
9
9
|
|
|
10
10
|
```
|
|
11
11
|
agents.yaml tmux
|
|
@@ -31,7 +31,7 @@ the agent's CLI, and types each agent's first prompt into it.
|
|
|
31
31
|
|
|
32
32
|
- `tmux` (3.0+)
|
|
33
33
|
- `python3` — PyYAML is used if present, otherwise a bundled parser handles the config
|
|
34
|
-
- `node` (16+) — only for the global `agentainer` command; not needed if you run `./
|
|
34
|
+
- `node` (16+) — only for the global `agentainer` command; not needed if you run `./agentainer` from a clone
|
|
35
35
|
- whichever agent CLIs you reference: `claude`, `codex`, `gemini`, `hermes` — install only the one(s) you actually use
|
|
36
36
|
|
|
37
37
|
## Install
|
|
@@ -51,7 +51,7 @@ Or from a clone (no npm needed):
|
|
|
51
51
|
|
|
52
52
|
```bash
|
|
53
53
|
git clone https://github.com/mehmetcanfarsak/AgentSwarm.git && cd AgentSwarm
|
|
54
|
-
./
|
|
54
|
+
./agentainer --help # same commands as the global `agentainer`, straight from the repo
|
|
55
55
|
```
|
|
56
56
|
|
|
57
57
|
## Quickstart
|
|
@@ -69,15 +69,15 @@ agentainer down # stop everything
|
|
|
69
69
|
Give the swarm its actual work:
|
|
70
70
|
|
|
71
71
|
```bash
|
|
72
|
-
|
|
72
|
+
agentainer send --to orchestrator "Build a CLI that converts CSV to Parquet."
|
|
73
73
|
```
|
|
74
74
|
|
|
75
75
|
Watch the traffic between agents:
|
|
76
76
|
|
|
77
77
|
```bash
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
agentainer logs -f # whole swarm, live
|
|
79
|
+
agentainer logs reviewer -n 20 # one agent
|
|
80
|
+
agentainer inbox developer # messages an agent received
|
|
81
81
|
```
|
|
82
82
|
|
|
83
83
|
---
|
|
@@ -236,8 +236,8 @@ conversation instead of starting a fresh one — it does not re-send the first
|
|
|
236
236
|
prompt, and it keeps any mail still queued for that agent:
|
|
237
237
|
|
|
238
238
|
```bash
|
|
239
|
-
|
|
240
|
-
|
|
239
|
+
agentainer sessions # what is recorded, and the command that would resume it
|
|
240
|
+
agentainer up --resume # reattach; agents without a recorded id start fresh
|
|
241
241
|
```
|
|
242
242
|
|
|
243
243
|
Claude is resumed with `--resume <id>`, codex with `resume <id>`. Set
|
|
@@ -497,9 +497,9 @@ Ready-to-run swarms in [`examples/`](examples/):
|
|
|
497
497
|
| [`existing-repo.yaml`](examples/existing-repo.yaml) | Pairing | Two agents in one **existing** checkout, with `create_workdirs: false` |
|
|
498
498
|
|
|
499
499
|
```bash
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
500
|
+
agentainer validate -c examples/research-swarm.yaml # look before you leap
|
|
501
|
+
agentainer up -c examples/research-swarm.yaml
|
|
502
|
+
agentainer send --to lead "Research the state of WebGPU compute shaders."
|
|
503
503
|
```
|
|
504
504
|
|
|
505
505
|
`existing-repo.yaml` intentionally refuses to start until you point `workdir` at
|
|
@@ -509,28 +509,28 @@ a repository that exists.
|
|
|
509
509
|
|
|
510
510
|
| Command | Purpose |
|
|
511
511
|
|---|---|
|
|
512
|
-
| `
|
|
513
|
-
| `
|
|
514
|
-
| `
|
|
515
|
-
| `
|
|
516
|
-
| `
|
|
517
|
-
| `
|
|
518
|
-
| `
|
|
519
|
-
| `
|
|
520
|
-
| `
|
|
521
|
-
| `
|
|
522
|
-
| `
|
|
523
|
-
| `
|
|
524
|
-
| `
|
|
525
|
-
|
|
526
|
-
|
|
512
|
+
| `agentainer up` | Start the swarm. `--only a,b`, `--restart`, `--resume`, `--no-prompt`, `--attach` |
|
|
513
|
+
| `agentainer down` | Kill sessions and watchers. `--only a,b` |
|
|
514
|
+
| `agentainer restart` | `down` then `up` |
|
|
515
|
+
| `agentainer status` | Table of agents, sessions, capture mode, permissions |
|
|
516
|
+
| `agentainer attach <agent>` | Attach to an agent's tmux session |
|
|
517
|
+
| `agentainer send --to <agent> "msg"` | Deliver a message (`--from`, `--file`, `--queue`, `--wait`, `--ignore-busy`, `--force`) |
|
|
518
|
+
| `agentainer broadcast "msg"` | Message everyone the sender may talk to |
|
|
519
|
+
| `agentainer sessions` | Show each agent's recorded conversation id (`--raw`) |
|
|
520
|
+
| `agentainer queue <agent>` | Show what is waiting for a busy agent (`--clear`) |
|
|
521
|
+
| `agentainer idle <agent>` | Force an agent back to idle, then drain its queue |
|
|
522
|
+
| `agentainer inbox <agent>` | Print archived messages |
|
|
523
|
+
| `agentainer logs [agent] [-f]` | Event log: prompts, responses, messages |
|
|
524
|
+
| `agentainer validate` | Parse the config. `--show-prompts` renders final prompts |
|
|
525
|
+
|
|
526
|
+
`agentainer my-swarm.yaml` is shorthand for `agentainer up -c my-swarm.yaml`.
|
|
527
527
|
`-c` and `$SWARM_CONFIG` both select a config; `-c` wins.
|
|
528
528
|
|
|
529
529
|
## Layout
|
|
530
530
|
|
|
531
531
|
```
|
|
532
532
|
AgentSwarm/
|
|
533
|
-
├──
|
|
533
|
+
├── agentainer # entrypoint
|
|
534
534
|
├── agents.example.yaml # annotated config
|
|
535
535
|
├── llms.txt # reference for agents configuring this tool
|
|
536
536
|
├── hooks/
|
package/{swarm.sh → agentainer}
RENAMED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
#
|
|
3
|
-
#
|
|
3
|
+
# Agentainer -- launch a configurable swarm of coding agents in tmux.
|
|
4
4
|
#
|
|
5
|
-
# ./
|
|
6
|
-
# ./
|
|
7
|
-
# ./
|
|
8
|
-
# ./
|
|
9
|
-
# ./
|
|
10
|
-
# ./
|
|
11
|
-
# ./
|
|
5
|
+
# ./agentainer up start every agent in agents.yaml
|
|
6
|
+
# ./agentainer up -c my-swarm.yaml ...from a different config
|
|
7
|
+
# ./agentainer agents.yaml shorthand for `up -c agents.yaml`
|
|
8
|
+
# ./agentainer status see who is running
|
|
9
|
+
# ./agentainer send --to dev "hi" message an agent
|
|
10
|
+
# ./agentainer attach dev jump into an agent's tmux session
|
|
11
|
+
# ./agentainer down stop everything
|
|
12
12
|
#
|
|
13
13
|
# See README.md for the full reference, or llms.txt if you are an LLM.
|
|
14
14
|
set -euo pipefail
|
|
@@ -27,7 +27,7 @@ if [[ -z "$PYTHON" ]]; then
|
|
|
27
27
|
fi
|
|
28
28
|
|
|
29
29
|
if [[ -z "$PYTHON" ]]; then
|
|
30
|
-
echo "xx
|
|
30
|
+
echo "xx Agentainer needs python3 on PATH (or set SWARM_PYTHON)" >&2
|
|
31
31
|
exit 1
|
|
32
32
|
fi
|
|
33
33
|
|
package/agents.example.yaml
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
# AgentSwarm example configuration
|
|
3
3
|
#
|
|
4
4
|
# cp agents.example.yaml agents.yaml
|
|
5
|
-
#
|
|
6
|
-
#
|
|
5
|
+
# agentainer validate --show-prompts # check it without launching anything
|
|
6
|
+
# agentainer up # launch the swarm
|
|
7
7
|
#
|
|
8
8
|
# Every key is documented inline. Anything marked (optional) can be omitted.
|
|
9
9
|
# =============================================================================
|
package/bin/agentainer.js
CHANGED
|
@@ -21,7 +21,7 @@ if (process.argv[2] === "doctor") {
|
|
|
21
21
|
process.exit(r.status === null ? 1 : r.status);
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
// Locate a Python interpreter, mirroring
|
|
24
|
+
// Locate a Python interpreter, mirroring ./agentainer (SWARM_PYTHON, then python3,
|
|
25
25
|
// then python).
|
|
26
26
|
function findPython() {
|
|
27
27
|
const candidates = process.env.SWARM_PYTHON
|
package/examples/bug-hunt.yaml
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# =============================================================================
|
|
2
2
|
# Bug hunt -- an automatic pipeline: reproduce -> diagnose -> fix -> verify.
|
|
3
3
|
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
4
|
+
# agentainer up -c examples/bug-hunt.yaml
|
|
5
|
+
# agentainer send --to reproducer "Uploads over 2MB fail with a 500 in prod."
|
|
6
6
|
#
|
|
7
7
|
# This is the one shape where `forward_responses_to` earns its keep: each stage
|
|
8
8
|
# hands its finished turn to the next stage automatically, with no human in the
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
# writes the code and a navigator who reads it as it lands.
|
|
7
7
|
#
|
|
8
8
|
# 1. Point `workdir` below at a real repository.
|
|
9
|
-
# 2.
|
|
10
|
-
# 3.
|
|
11
|
-
# 4.
|
|
9
|
+
# 2. agentainer validate -c examples/existing-repo.yaml
|
|
10
|
+
# 3. agentainer up -c examples/existing-repo.yaml
|
|
11
|
+
# 4. agentainer send --to driver "Add retry-with-backoff to the HTTP client."
|
|
12
12
|
#
|
|
13
13
|
# Until you edit the paths, `validate` will refuse to run and tell you so --
|
|
14
14
|
# that is the point of `create_workdir: false`.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# =============================================================================
|
|
2
2
|
# Research swarm -- a small team that investigates a topic and writes it up.
|
|
3
3
|
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
4
|
+
# agentainer up -c examples/research-swarm.yaml
|
|
5
|
+
# agentainer send --to lead "Research the state of WebGPU compute shaders."
|
|
6
6
|
#
|
|
7
7
|
# Shape: hub and spoke. The lead hands out work and assembles the result; the
|
|
8
8
|
# specialists only ever report back to the lead (and to each other where it
|
|
@@ -21,7 +21,7 @@ agents:
|
|
|
21
21
|
|
|
22
22
|
- name: lead
|
|
23
23
|
type: claude
|
|
24
|
-
command: "
|
|
24
|
+
command: "claude --dangerously-skip-permissions"
|
|
25
25
|
can_talk_to: ["scout", "analyst", "writer"]
|
|
26
26
|
|
|
27
27
|
# The lead waits for the human to supply the research question.
|
|
@@ -51,8 +51,8 @@ agents:
|
|
|
51
51
|
Never accept "it depends" as a final answer. Force a recommendation.
|
|
52
52
|
|
|
53
53
|
- name: scout
|
|
54
|
-
type:
|
|
55
|
-
command: "
|
|
54
|
+
type: gemini
|
|
55
|
+
command: "gemini --yolo"
|
|
56
56
|
can_talk_to: ["lead", "analyst"]
|
|
57
57
|
|
|
58
58
|
# Gemini has no turn-completion hook, so its output would have to be scraped
|
|
@@ -79,8 +79,8 @@ agents:
|
|
|
79
79
|
of guessing. Say plainly when you could not find something.
|
|
80
80
|
|
|
81
81
|
- name: analyst
|
|
82
|
-
type:
|
|
83
|
-
command: "
|
|
82
|
+
type: codex
|
|
83
|
+
command: "codex --yolo"
|
|
84
84
|
can_talk_to: ["lead", "scout"]
|
|
85
85
|
|
|
86
86
|
first_prompt: |
|
|
@@ -101,7 +101,7 @@ agents:
|
|
|
101
101
|
|
|
102
102
|
- name: writer
|
|
103
103
|
type: claude
|
|
104
|
-
command: "
|
|
104
|
+
command: "claude --dangerously-skip-permissions"
|
|
105
105
|
can_talk_to: ["lead"]
|
|
106
106
|
|
|
107
107
|
# A custom folder: the report lands somewhere predictable, outside the
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
# Software company -- a product team with a CTO, an architect, two developers,
|
|
3
3
|
# a QA reviewer and a technical writer.
|
|
4
4
|
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
5
|
+
# agentainer up -c examples/software-company.yaml
|
|
6
|
+
# agentainer send --to cto "Build a URL shortener with an API and a web UI."
|
|
7
7
|
#
|
|
8
8
|
# The communication graph is deliberately not a free-for-all: developers talk to
|
|
9
9
|
# their architect and to QA, but not to each other, so design decisions go
|
package/hooks/claude_stop.sh
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# Claude Code `Stop` hook: fires when Claude finishes responding.
|
|
3
3
|
# Claude passes a JSON payload on stdin containing `transcript_path`.
|
|
4
|
-
# Installed automatically into <agent-workdir>/.claude/settings.json by `
|
|
4
|
+
# Installed automatically into <agent-workdir>/.claude/settings.json by `agentainer up`.
|
|
5
5
|
#
|
|
6
6
|
# A hook must never break the agent it is attached to, so every failure here is
|
|
7
7
|
# swallowed and the script always exits 0.
|
|
@@ -13,5 +13,5 @@ if [[ -n "${SWARM_ROOT:-}" ]] && mkdir -p "$SWARM_ROOT/.swarm/logs" 2>/dev/null;
|
|
|
13
13
|
log="$SWARM_ROOT/.swarm/logs/hooks.log"
|
|
14
14
|
fi
|
|
15
15
|
|
|
16
|
-
"$HERE/
|
|
16
|
+
"$HERE/agentainer" hook claude >>"$log" 2>&1
|
|
17
17
|
exit 0
|
package/hooks/codex_notify.sh
CHANGED
package/lib/config.py
CHANGED
|
@@ -312,7 +312,7 @@ def load(path: str | os.PathLike) -> SwarmConfig:
|
|
|
312
312
|
raise ConfigError(
|
|
313
313
|
f"config file not found: {cfg_path}\n"
|
|
314
314
|
" Create one with: cp agents.example.yaml agents.yaml\n"
|
|
315
|
-
" Or point at it:
|
|
315
|
+
" Or point at it: agentainer -c /path/to/swarm.yaml up"
|
|
316
316
|
)
|
|
317
317
|
|
|
318
318
|
try:
|
package/lib/swarm.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
2
|
+
"""Agentainer -- run a swarm of coding agents in tmux and let them talk.
|
|
3
3
|
|
|
4
|
-
Invoked through ``
|
|
4
|
+
Invoked through ``agentainer`` (or ``./agentainer`` from a clone); see
|
|
5
|
+
``agentainer --help`` and README.md.
|
|
5
6
|
"""
|
|
6
7
|
|
|
7
8
|
from __future__ import annotations
|
|
@@ -1609,8 +1610,8 @@ def write_shim(cfg: SwarmConfig) -> None:
|
|
|
1609
1610
|
shim = cfg.bin_dir / "swarm"
|
|
1610
1611
|
shim.write_text(
|
|
1611
1612
|
"#!/usr/bin/env bash\n"
|
|
1612
|
-
"# Generated by
|
|
1613
|
-
f'exec {shlex.quote(str(SWARM_HOME / "
|
|
1613
|
+
"# Generated by Agentainer. Lets an agent run `swarm send ...` from its shell.\n"
|
|
1614
|
+
f'exec {shlex.quote(str(SWARM_HOME / "agentainer"))} "$@"\n'
|
|
1614
1615
|
)
|
|
1615
1616
|
shim.chmod(0o755)
|
|
1616
1617
|
|
|
@@ -1803,7 +1804,7 @@ def cmd_up(args) -> int:
|
|
|
1803
1804
|
print()
|
|
1804
1805
|
info(f"swarm {cfg.name!r} is up with {len(started)} agent(s)")
|
|
1805
1806
|
info(f"attach with: tmux attach -t {started[0].session}")
|
|
1806
|
-
info(f"or: {SWARM_HOME / '
|
|
1807
|
+
info(f"or: {SWARM_HOME / 'agentainer'} attach {started[0].name}")
|
|
1807
1808
|
|
|
1808
1809
|
if args.attach:
|
|
1809
1810
|
os.execvp("tmux", ["tmux", "attach", "-t", started[0].session])
|
|
@@ -2129,7 +2130,7 @@ def select_agents(cfg: SwarmConfig, only: str | None) -> list[Agent]:
|
|
|
2129
2130
|
|
|
2130
2131
|
|
|
2131
2132
|
def default_config() -> str:
|
|
2132
|
-
"""SWARM_CONFIG, else ./agents.yaml, else the agents.yaml beside
|
|
2133
|
+
"""SWARM_CONFIG, else ./agents.yaml, else the agents.yaml beside agentainer."""
|
|
2133
2134
|
from_env = os.environ.get("SWARM_CONFIG")
|
|
2134
2135
|
if from_env:
|
|
2135
2136
|
return from_env
|
package/llms.txt
CHANGED
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
>
|
|
8
8
|
> This file is the reference for LLMs asked to write or repair an AgentSwarm config.
|
|
9
9
|
> If you are configuring a swarm, everything you need is below. Verify your work with
|
|
10
|
-
>
|
|
10
|
+
> `agentainer validate --show-prompts`, which parses the config and launches nothing.
|
|
11
11
|
|
|
12
12
|
## Mental model
|
|
13
13
|
|
|
14
|
-
- One YAML file describes the whole swarm.
|
|
14
|
+
- One YAML file describes the whole swarm. `agentainer up` reads it and, for each agent:
|
|
15
15
|
creates `<root>/<name>/`, installs a turn-completion hook in that folder, opens a tmux
|
|
16
16
|
session named `<session_prefix><name>`, runs the agent's `command` inside the folder,
|
|
17
17
|
waits for its input box to become responsive, then types the assembled first prompt in.
|
|
@@ -223,23 +223,23 @@ Final prompt order: `first_prompt`, then the comms block, then the task notice.
|
|
|
223
223
|
## Commands
|
|
224
224
|
|
|
225
225
|
```
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
226
|
+
agentainer up [--only a,b] [--restart] [--no-prompt] [--attach]
|
|
227
|
+
agentainer down [--only a,b]
|
|
228
|
+
agentainer restart
|
|
229
|
+
agentainer status
|
|
230
|
+
agentainer attach <agent>
|
|
231
|
+
agentainer send --to <agent> [--from <name>] [--file F] [--queue] [--wait]
|
|
232
232
|
[--wait-timeout S] [--ignore-busy] [--force] "message"
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
233
|
+
agentainer broadcast "message"
|
|
234
|
+
agentainer queue <agent> [--clear]
|
|
235
|
+
agentainer idle <agent> [--no-drain]
|
|
236
|
+
agentainer inbox <agent> [-n N]
|
|
237
|
+
agentainer logs [agent] [-n N] [-f]
|
|
238
|
+
agentainer validate [--show-prompts]
|
|
239
239
|
```
|
|
240
240
|
|
|
241
241
|
Config resolution: `-c PATH`, else `$SWARM_CONFIG`, else `./agents.yaml`, else the
|
|
242
|
-
`agents.yaml` beside `
|
|
242
|
+
`agents.yaml` beside `agentainer`. `agentainer x.yaml` is shorthand for `up -c x.yaml`.
|
|
243
243
|
|
|
244
244
|
## What an agent sees at runtime
|
|
245
245
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentainer",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Run a configurable swarm of coding-agent CLIs (Claude Code, Codex, Gemini) in tmux and let them talk to each other.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agents",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"lib/*.py",
|
|
33
33
|
"hooks/*.sh",
|
|
34
34
|
"scripts/check-deps.js",
|
|
35
|
-
"
|
|
35
|
+
"agentainer",
|
|
36
36
|
"examples/*.yaml",
|
|
37
37
|
"agents.example.yaml",
|
|
38
38
|
"README.md",
|