agentainer 0.1.7 → 2.0.0
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 +248 -677
- package/agentainer +16 -18
- package/agentainer.example.yaml +86 -0
- package/bin/agentainer.js +9 -8
- package/examples/brainstorm.yaml +27 -128
- package/examples/bug-hunt.yaml +51 -96
- package/examples/code-review.yaml +73 -0
- package/examples/debate.yaml +16 -90
- package/examples/incident-response.yaml +52 -109
- package/examples/localization.yaml +56 -123
- package/examples/quickstart.yaml +48 -0
- package/examples/research.yaml +25 -0
- package/examples/software-company.yaml +71 -128
- package/examples/tdd-pingpong.yaml +36 -68
- package/examples/writers-room.yaml +49 -111
- package/hooks/claude_stop.sh +5 -3
- package/hooks/codex_notify.sh +4 -3
- package/lib/cli.py +929 -0
- package/lib/config.py +247 -305
- package/lib/hooks.py +246 -0
- package/lib/lock.py +75 -0
- package/lib/log.py +64 -0
- package/lib/mail.py +634 -0
- package/lib/minyaml.py +1 -39
- package/lib/reconcile.py +473 -0
- package/lib/sessions.py +223 -0
- package/lib/supervisor.py +216 -0
- package/lib/telegram.py +372 -0
- package/lib/tmux.py +355 -0
- package/lib/turn.py +159 -0
- package/lib/ui.py +1020 -0
- package/llms.txt +145 -429
- package/package.json +9 -7
- package/scripts/check-deps.js +18 -61
- package/ui/app.js +869 -0
- package/ui/index.html +348 -0
- package/agents.example.yaml +0 -257
- package/examples/code-review-broadcast.yaml +0 -109
- package/examples/existing-repo.yaml +0 -74
- package/examples/multi-language-broadcast.yaml +0 -127
- package/examples/ping-pong.yaml +0 -89
- package/examples/red-team.yaml +0 -117
- package/examples/research-swarm.yaml +0 -129
- package/lib/swarm.py +0 -2461
package/agentainer
CHANGED
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
#
|
|
3
|
-
# Agentainer -- launch a configurable
|
|
3
|
+
# Agentainer -- launch a configurable team of coding agents in tmux.
|
|
4
4
|
#
|
|
5
|
-
# ./agentainer up
|
|
6
|
-
# ./agentainer up -c my-swarm.yaml
|
|
7
|
-
# ./agentainer
|
|
8
|
-
# ./agentainer
|
|
9
|
-
# ./agentainer
|
|
10
|
-
# ./agentainer
|
|
11
|
-
# ./agentainer
|
|
5
|
+
# ./agentainer up start every agent in agentainer.yaml
|
|
6
|
+
# ./agentainer up -c my-swarm.yaml ...from a different config
|
|
7
|
+
# ./agentainer my-swarm.yaml shorthand for `up -c my-swarm.yaml`
|
|
8
|
+
# ./agentainer validate print the resolved config, launch nothing
|
|
9
|
+
# ./agentainer status see who is running
|
|
10
|
+
# ./agentainer send --to dev "hi" mail an agent
|
|
11
|
+
# ./agentainer attach dev jump into an agent's tmux session
|
|
12
|
+
# ./agentainer down stop everything
|
|
12
13
|
#
|
|
13
14
|
# See README.md for the full reference, or llms.txt if you are an LLM.
|
|
14
15
|
set -euo pipefail
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
export
|
|
17
|
+
AGENTAINER_HOME="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
18
|
+
export AGENTAINER_HOME
|
|
18
19
|
|
|
19
|
-
PYTHON="${
|
|
20
|
+
PYTHON="${AGENTAINER_PYTHON:-}"
|
|
20
21
|
if [[ -z "$PYTHON" ]]; then
|
|
21
22
|
for candidate in python3 python; do
|
|
22
23
|
if command -v "$candidate" >/dev/null 2>&1; then
|
|
@@ -27,7 +28,7 @@ if [[ -z "$PYTHON" ]]; then
|
|
|
27
28
|
fi
|
|
28
29
|
|
|
29
30
|
if [[ -z "$PYTHON" ]]; then
|
|
30
|
-
echo "xx Agentainer needs python3 on PATH (or set
|
|
31
|
+
echo "xx Agentainer needs python3 on PATH (or set AGENTAINER_PYTHON)" >&2
|
|
31
32
|
exit 1
|
|
32
33
|
fi
|
|
33
34
|
|
|
@@ -35,9 +36,6 @@ if ! command -v tmux >/dev/null 2>&1; then
|
|
|
35
36
|
echo "!! tmux was not found on PATH; every command except 'validate' will fail" >&2
|
|
36
37
|
fi
|
|
37
38
|
|
|
38
|
-
# The config is resolved in lib/
|
|
39
|
-
# ./
|
|
40
|
-
|
|
41
|
-
# discovers from the agent's working directory.
|
|
42
|
-
|
|
43
|
-
exec "$PYTHON" "$SWARM_HOME/lib/swarm.py" "$@"
|
|
39
|
+
# The config is resolved in lib/cli.py: -c, then $AGENTAINER_CONFIG, then
|
|
40
|
+
# ./agentainer.yaml, then the agentainer.yaml next to this script.
|
|
41
|
+
exec "$PYTHON" "$AGENTAINER_HOME/lib/cli.py" "$@"
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# agentainer.example.yaml — reference configuration for Agentainer v2.
|
|
3
|
+
#
|
|
4
|
+
# Copy it to start your own swarm:
|
|
5
|
+
#
|
|
6
|
+
# cp agentainer.example.yaml my-swarm.yaml
|
|
7
|
+
# agentainer up -c my-swarm.yaml
|
|
8
|
+
# agentainer status -c my-swarm.yaml
|
|
9
|
+
# agentainer send -c my-swarm.yaml --to orchestrator "Build a CSV->Parquet CLI."
|
|
10
|
+
# agentainer down -c my-swarm.yaml
|
|
11
|
+
#
|
|
12
|
+
# This file is FULLY COMMENTED and KEY-FREE: every `command:` runs a tiny
|
|
13
|
+
# `bash` loop instead of a real agent CLI, so the swarm comes up and routes
|
|
14
|
+
# mail with NO API keys and NO model calls. To run real agents, swap each
|
|
15
|
+
# `command` for the actual CLI you installed and drop `capture: none` so turns
|
|
16
|
+
# get detected.
|
|
17
|
+
#
|
|
18
|
+
# The v2 model: agents RECEIVE by reading a file (inbox/) and SEND by writing a
|
|
19
|
+
# file (outbox/<name>/). The orchestrator owns routing, ACL, message IDs,
|
|
20
|
+
# read-state, queueing, retries, and the durable log.
|
|
21
|
+
# =============================================================================
|
|
22
|
+
|
|
23
|
+
swarm:
|
|
24
|
+
name: my-swarm # any label; shown in `status` / logs
|
|
25
|
+
root: ./my-swarm-workspace # disposable working dir; agents get <root>/<name>/ subdirs
|
|
26
|
+
supervise: true # run the liveness supervisor (heartbeat) at up
|
|
27
|
+
supervise_interval_ms: 15000 # how often the supervisor reconciles agents
|
|
28
|
+
ready_timeout_ms: 30000 # give up waiting for an agent's input prompt after this
|
|
29
|
+
busy_timeout_ms: 120000 # mark a silent agent idle after this long
|
|
30
|
+
user_available: false # the human mailbox starts away (set `agentainer user available`)
|
|
31
|
+
|
|
32
|
+
# Optional Telegram bridge (off by default). When enabled, the orchestrator
|
|
33
|
+
# mirrors mail to a Telegram chat over HTTPS (stdlib only, no dependency), and a
|
|
34
|
+
# Telegram reply to a mirrored message routes back into the swarm as `user` mail.
|
|
35
|
+
# Create a bot with @BotFather for the token; get your chat id from @userinfobot.
|
|
36
|
+
# It is easiest to set all of this from the UI (`agentainer serve` -> Settings).
|
|
37
|
+
# telegram:
|
|
38
|
+
# enabled: true
|
|
39
|
+
# bot_token: "123456:ABC-DEF..." # keep this secret; it is a credential
|
|
40
|
+
# chat_id: "123456789" # the chat/user id to send to & accept replies from
|
|
41
|
+
# mirror: "*" # "*"/all, or a list of agent names to mirror
|
|
42
|
+
# mirror_user: true # also mirror mail addressed to you (default true)
|
|
43
|
+
# mirror_system: false # mirror system pings/bounces too (default false)
|
|
44
|
+
|
|
45
|
+
defaults:
|
|
46
|
+
# capture: how the orchestrator knows a turn finished.
|
|
47
|
+
# hook -> per-type hook/notify (claude Stop hook, codex `notify`) [default for those types]
|
|
48
|
+
# pane -> poll the tmux pane (gemini / hermes, or any CLI without a hook)
|
|
49
|
+
# none -> no turn detection (mock agents; the agent never "finishes")
|
|
50
|
+
# `capture: none` is auto-upgraded to `hook` on hook-backed types at load time.
|
|
51
|
+
capture: none
|
|
52
|
+
can_talk_to: [] # tightened per agent below
|
|
53
|
+
|
|
54
|
+
agents:
|
|
55
|
+
# The orchestrator fans work out to the workers. It may also talk to the human.
|
|
56
|
+
- name: orchestrator
|
|
57
|
+
type: claude
|
|
58
|
+
can_talk_to: [developer, reviewer, user] # may also talk to the human mailbox
|
|
59
|
+
# Real agent: command: "claude --dangerously-skip-permissions"
|
|
60
|
+
command: "claude --dangerously-skip-permissions"
|
|
61
|
+
role: "You are the orchestrator. Wait for the user's task, then delegate to workers."
|
|
62
|
+
|
|
63
|
+
# A worker that can only reply to the orchestrator (enforced by the ACL).
|
|
64
|
+
- name: developer
|
|
65
|
+
type: codex
|
|
66
|
+
can_talk_to: [orchestrator]
|
|
67
|
+
# Real agent: command: "codex"
|
|
68
|
+
command: "codex --yolo"
|
|
69
|
+
role: "You are a developer. Implement what the orchestrator asks."
|
|
70
|
+
|
|
71
|
+
# A reviewer that checks the developer's work.
|
|
72
|
+
- name: reviewer
|
|
73
|
+
type: claude
|
|
74
|
+
can_talk_to: [developer]
|
|
75
|
+
# Real agent: command: "claude"
|
|
76
|
+
command: "claude --dangerously-skip-permissions"
|
|
77
|
+
role: "You are a reviewer. Read the developer's output and critique it."
|
|
78
|
+
|
|
79
|
+
# An optional idle pinger: every N seconds, if idle, the orchestrator drops a
|
|
80
|
+
# `system` nudge into this agent's queue (idle-only, no pile-up, min-cadence).
|
|
81
|
+
# - name: researcher
|
|
82
|
+
# type: gemini
|
|
83
|
+
# can_talk_to: [orchestrator]
|
|
84
|
+
# command: "bash -c 'while true; do read -r l || sleep 1; done'"
|
|
85
|
+
# periodically_ping_seconds: 600
|
|
86
|
+
# periodically_ping_message: "Any progress to report? Reply, or stay quiet."
|
package/bin/agentainer.js
CHANGED
|
@@ -4,14 +4,15 @@
|
|
|
4
4
|
// npm symlinks this file into a bin directory (e.g. /usr/local/bin/agentainer).
|
|
5
5
|
// Node resolves that symlink before setting __dirname, so __dirname always
|
|
6
6
|
// points at the real bin/ inside the installed package -- which makes the
|
|
7
|
-
// package root, and therefore
|
|
7
|
+
// package root, and therefore AGENTAINER_HOME, reliable no matter where npm
|
|
8
|
+
// puts us.
|
|
8
9
|
"use strict";
|
|
9
10
|
|
|
10
11
|
const path = require("path");
|
|
11
12
|
const { spawnSync } = require("child_process");
|
|
12
13
|
|
|
13
14
|
const PKG_ROOT = path.resolve(__dirname, "..");
|
|
14
|
-
const ENTRY = path.join(PKG_ROOT, "lib", "
|
|
15
|
+
const ENTRY = path.join(PKG_ROOT, "lib", "cli.py");
|
|
15
16
|
|
|
16
17
|
// `agentainer doctor` re-runs the dependency check without touching Python.
|
|
17
18
|
if (process.argv[2] === "doctor") {
|
|
@@ -21,11 +22,11 @@ if (process.argv[2] === "doctor") {
|
|
|
21
22
|
process.exit(r.status === null ? 1 : r.status);
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
// Locate a Python interpreter, mirroring ./agentainer (
|
|
25
|
-
// then python).
|
|
25
|
+
// Locate a Python interpreter, mirroring ./agentainer (AGENTAINER_PYTHON, then
|
|
26
|
+
// python3, then python).
|
|
26
27
|
function findPython() {
|
|
27
|
-
const candidates = process.env.
|
|
28
|
-
? [process.env.
|
|
28
|
+
const candidates = process.env.AGENTAINER_PYTHON
|
|
29
|
+
? [process.env.AGENTAINER_PYTHON]
|
|
29
30
|
: ["python3", "python"];
|
|
30
31
|
for (const cand of candidates) {
|
|
31
32
|
const probe = spawnSync(cand, ["--version"], { stdio: "ignore" });
|
|
@@ -42,7 +43,7 @@ function has(cmd, args) {
|
|
|
42
43
|
const python = findPython();
|
|
43
44
|
if (!python) {
|
|
44
45
|
process.stderr.write(
|
|
45
|
-
"xx Agentainer needs python3 on PATH (or set
|
|
46
|
+
"xx Agentainer needs python3 on PATH (or set AGENTAINER_PYTHON).\n" +
|
|
46
47
|
" Run `agentainer doctor` for install hints.\n"
|
|
47
48
|
);
|
|
48
49
|
process.exit(1);
|
|
@@ -59,7 +60,7 @@ if (!has("tmux", ["-V"])) {
|
|
|
59
60
|
|
|
60
61
|
const result = spawnSync(python, [ENTRY, ...process.argv.slice(2)], {
|
|
61
62
|
stdio: "inherit",
|
|
62
|
-
env: { ...process.env,
|
|
63
|
+
env: { ...process.env, AGENTAINER_HOME: PKG_ROOT },
|
|
63
64
|
});
|
|
64
65
|
|
|
65
66
|
if (result.error) {
|
package/examples/brainstorm.yaml
CHANGED
|
@@ -1,147 +1,46 @@
|
|
|
1
1
|
# =============================================================================
|
|
2
|
-
# Brainstorm --
|
|
2
|
+
# 💡 Brainstorm -- two agents riff on a prompt, each extending the other, with no
|
|
3
|
+
# orchestrator in the middle. A pure ping-pong of ideas via the mail model.
|
|
3
4
|
#
|
|
4
|
-
#
|
|
5
|
-
# agentainer
|
|
5
|
+
# cp examples/brainstorm.yaml my-session.yaml
|
|
6
|
+
# agentainer up -c my-session.yaml
|
|
7
|
+
# agentainer send -c my-session.yaml --to a "We need a name for a CLI tool."
|
|
8
|
+
# agentainer down -c my-session.yaml
|
|
6
9
|
#
|
|
7
|
-
# Shape:
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
# who clusters, de-dupes and ranks. The generators talk only to the facilitator.
|
|
10
|
+
# Shape: A <--> B, two-way, and that's the whole graph. Useful for ideation,
|
|
11
|
+
# rubber-ducking, or adversarial "yes, and..." loops. Either agent may also
|
|
12
|
+
# talk to `user` (the human can drop in).
|
|
11
13
|
#
|
|
12
|
-
#
|
|
13
|
-
# \ | /
|
|
14
|
-
# `----- facilitator -----'
|
|
15
|
-
# |
|
|
16
|
-
# synthesiser
|
|
14
|
+
# a <----> b
|
|
17
15
|
#
|
|
18
|
-
#
|
|
19
|
-
# answer back). Ideas diverge across the three generators, then converge
|
|
20
|
-
# through the facilitator to the synthesiser.
|
|
21
|
-
#
|
|
22
|
-
# Deliberately divergent roles keep the three from producing the same list three
|
|
23
|
-
# times. This swarm creates its own folders under `root` -- no repo needed.
|
|
16
|
+
# Real agents: commands launch the actual CLIs (claude / codex / gemini / hermes). For a key-free demo, swap each `command` for a mock bash loop.
|
|
24
17
|
# =============================================================================
|
|
25
18
|
|
|
26
19
|
swarm:
|
|
27
20
|
name: brainstorm
|
|
28
21
|
root: ./brainstorm-workspace
|
|
29
|
-
session_prefix: "bs-"
|
|
30
|
-
|
|
31
|
-
# Fan-out then one funnel step: a single hop is enough.
|
|
32
|
-
max_forward_hops: 1
|
|
33
22
|
|
|
34
23
|
defaults:
|
|
35
|
-
|
|
24
|
+
capture: none
|
|
25
|
+
can_talk_to: []
|
|
36
26
|
|
|
37
27
|
agents:
|
|
38
|
-
|
|
39
|
-
- name: facilitator
|
|
40
|
-
type: claude
|
|
41
|
-
command: "claude --dangerously-skip-permissions --model opus"
|
|
42
|
-
can_talk_to: ["wildcard", "pragmatist", "contrarian", "synthesiser"]
|
|
43
|
-
|
|
44
|
-
# Waits for the human to supply the actual prompt.
|
|
45
|
-
in_first_prompt_append_your_task_will_be_sent_in_the_next_prompt: true
|
|
46
|
-
|
|
47
|
-
first_prompt: |
|
|
48
|
-
You are the FACILITATOR. You run a divergent-then-convergent session.
|
|
49
|
-
|
|
50
|
-
You do not generate ideas or judge them yourself. You frame the prompt,
|
|
51
|
-
keep the three generators pulling in different directions, then hand the
|
|
52
|
-
raw pile to the synthesiser and relay the result.
|
|
53
|
-
|
|
54
|
-
Your room:
|
|
55
|
-
- wildcard: unconstrained, ambitious, "what if budget were no object"
|
|
56
|
-
- pragmatist: ships this quarter with what we already have
|
|
57
|
-
- contrarian: attacks the framing and the obvious answers
|
|
58
|
-
- synthesiser: clusters, de-dupes and ranks everything at the end
|
|
59
|
-
|
|
60
|
-
How to run it:
|
|
61
|
-
1. Sharpen the human's prompt into one clear question with a success
|
|
62
|
-
metric. Send it to all three generators, and tell each to lean fully
|
|
63
|
-
into ITS angle -- no self-censoring, no converging yet.
|
|
64
|
-
2. Do NOT show generators each other's ideas mid-round; that collapses
|
|
65
|
-
the diversity you are paying for.
|
|
66
|
-
3. When all three have reported, forward the combined, unedited list to
|
|
67
|
-
the synthesiser.
|
|
68
|
-
4. Relay the synthesiser's ranked shortlist to the human, with the one
|
|
69
|
-
bold idea and the one safe idea called out.
|
|
70
|
-
|
|
71
|
-
In the diverge phase, quantity and range beat quality. Save judgement for
|
|
72
|
-
the synthesiser.
|
|
73
|
-
|
|
74
|
-
- name: wildcard
|
|
75
|
-
type: claude
|
|
76
|
-
command: "claude --dangerously-skip-permissions"
|
|
77
|
-
can_talk_to: ["facilitator"]
|
|
78
|
-
|
|
79
|
-
first_prompt: |
|
|
80
|
-
You are the WILDCARD. Your job is range and ambition, not feasibility.
|
|
81
|
-
|
|
82
|
-
Given the question, produce 8-12 genuinely different ideas, including ones
|
|
83
|
-
that sound expensive, weird, or premature. Assume constraints can be
|
|
84
|
-
renegotiated. Chase the idea that would matter most if it worked.
|
|
85
|
-
|
|
86
|
-
For each: a one-line pitch and the single reason it could be a big deal.
|
|
87
|
-
Do not filter for practicality -- the pragmatist and synthesiser do that.
|
|
88
|
-
Avoid the obvious answer everyone already thought of; go past it.
|
|
89
|
-
|
|
90
|
-
- name: pragmatist
|
|
91
|
-
type: codex
|
|
92
|
-
command: "codex --yolo"
|
|
93
|
-
can_talk_to: ["facilitator"]
|
|
94
|
-
|
|
95
|
-
first_prompt: |
|
|
96
|
-
You are the PRAGMATIST. Your job is ideas that ship soon, cheaply.
|
|
97
|
-
|
|
98
|
-
Given the question, produce 8-12 ideas that a small team could start this
|
|
99
|
-
quarter with tools it already has. Favour reversible, low-risk moves with a
|
|
100
|
-
clear owner and a visible payoff.
|
|
101
|
-
|
|
102
|
-
For each: a one-line pitch, the rough effort, and the expected payoff. Bias
|
|
103
|
-
toward things measurable within a few weeks. Leave the moonshots to the
|
|
104
|
-
wildcard; you own "what can we actually do on Monday".
|
|
105
|
-
|
|
106
|
-
- name: contrarian
|
|
28
|
+
- name: a
|
|
107
29
|
type: claude
|
|
30
|
+
can_talk_to: [b, user]
|
|
108
31
|
command: "claude --dangerously-skip-permissions"
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
First, challenge the framing: is the stated goal the real goal? Is the
|
|
115
|
-
metric measuring the right thing? Name the assumption everyone is making.
|
|
32
|
+
role: |
|
|
33
|
+
You are BRAINSTORMER A. You receive an idea in your inbox/ and extend it
|
|
34
|
+
-- build on it, add a twist, or sharpen it -- then send your riff back to
|
|
35
|
+
agent b (write a file into outbox/b/). Keep the energy up; one concrete
|
|
36
|
+
improvement per message. If the human (user) writes to you, answer them.
|
|
116
37
|
|
|
117
|
-
|
|
118
|
-
do the opposite, remove the thing instead of optimising it, or solve the
|
|
119
|
-
problem one level up so it disappears.
|
|
120
|
-
|
|
121
|
-
For each: the conventional answer you are rejecting, and your alternative.
|
|
122
|
-
Be provocative but honest -- the goal is better options, not just dissent.
|
|
123
|
-
|
|
124
|
-
- name: synthesiser
|
|
38
|
+
- name: b
|
|
125
39
|
type: claude
|
|
40
|
+
can_talk_to: [a, user]
|
|
126
41
|
command: "claude --dangerously-skip-permissions"
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
first_prompt: |
|
|
133
|
-
You are the SYNTHESISER. You turn a messy pile of ideas into a decision.
|
|
134
|
-
|
|
135
|
-
You receive every idea from all three generators. Write the result to
|
|
136
|
-
SHORTLIST.md.
|
|
137
|
-
|
|
138
|
-
Do this:
|
|
139
|
-
1. Cluster near-duplicates into single themes; note where two angles
|
|
140
|
-
independently converged (that is a signal).
|
|
141
|
-
2. Score each theme on impact vs effort. Be explicit about the criteria.
|
|
142
|
-
3. Produce a ranked shortlist of 3-5 things worth doing, each with why it
|
|
143
|
-
made the cut and the first concrete step.
|
|
144
|
-
4. Call out the single boldest bet and the single safest quick win, and
|
|
145
|
-
name any idea that is a trap (looks great, fails in practice).
|
|
146
|
-
|
|
147
|
-
Do not just list everything back. The value you add is cutting.
|
|
42
|
+
role: |
|
|
43
|
+
You are BRAINSTORMER B. You receive A's riff in your inbox/ and push it
|
|
44
|
+
further -- a new angle, a risk, a concrete next step -- then send it back
|
|
45
|
+
to agent a (write a file into outbox/a/). One concrete improvement per
|
|
46
|
+
message. If the human (user) writes to you, answer them.
|
package/examples/bug-hunt.yaml
CHANGED
|
@@ -1,112 +1,67 @@
|
|
|
1
1
|
# =============================================================================
|
|
2
|
-
# Bug hunt --
|
|
2
|
+
# 🐛 Bug hunt -- one orchestrator fans a bug report out to several hunters who
|
|
3
|
+
# each investigate a different area, then report findings back.
|
|
3
4
|
#
|
|
4
|
-
#
|
|
5
|
-
# agentainer
|
|
5
|
+
# cp examples/bug-hunt.yaml my-hunt.yaml
|
|
6
|
+
# agentainer up -c my-hunt.yaml
|
|
7
|
+
# agentainer send -c my-hunt.yaml --to orchestrator "Intermittent 500s on /api/login."
|
|
8
|
+
# agentainer down -c my-hunt.yaml
|
|
6
9
|
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
+
# Shape: orchestrator is the hub. Hunters never talk to each other (they'd
|
|
11
|
+
# double up or step on each other's fixes); they report only to the orchestrator,
|
|
12
|
+
# who de-duplicates and assigns the fix.
|
|
10
13
|
#
|
|
11
|
-
#
|
|
14
|
+
# orchestrator <--> hunter-a, hunter-b, hunter-c (spokes silent among selves)
|
|
12
15
|
#
|
|
13
|
-
#
|
|
14
|
-
# they have something to say, delete `forward_responses_to` and let them call
|
|
15
|
-
# `swarm send` themselves.
|
|
16
|
+
# Real agents: commands launch the actual CLIs (claude / codex / gemini / hermes). For a key-free demo, swap each `command` for a mock bash loop.
|
|
16
17
|
# =============================================================================
|
|
17
18
|
|
|
18
19
|
swarm:
|
|
19
|
-
name:
|
|
20
|
-
root: ./
|
|
21
|
-
session_prefix: "bug-"
|
|
20
|
+
name: bug-hunt
|
|
21
|
+
root: ./bug-hunt-workspace
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
defaults:
|
|
24
|
+
capture: none
|
|
25
|
+
can_talk_to: []
|
|
25
26
|
|
|
26
27
|
agents:
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
- name: orchestrator
|
|
29
|
+
type: claude
|
|
30
|
+
can_talk_to: [hunter_a, hunter_b, hunter_c, user]
|
|
31
|
+
command: "claude --dangerously-skip-permissions"
|
|
32
|
+
role: |
|
|
33
|
+
You are the HUNT COORDINATOR. You receive a bug report (as mail in your
|
|
34
|
+
inbox/) and split it into disjoint leads -- one per hunter. Each hunter gets a
|
|
35
|
+
sharp, independent question and the file/area to focus on. Collect their
|
|
36
|
+
findings, de-duplicate, and decide who fixes what. Never let two hunters
|
|
37
|
+
investigate the same thing.
|
|
38
|
+
|
|
39
|
+
- name: hunter_a
|
|
29
40
|
type: codex
|
|
41
|
+
can_talk_to: [orchestrator]
|
|
30
42
|
command: "codex --yolo"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
Given a bug report, your only job is to turn it into a deterministic,
|
|
38
|
-
minimal reproduction: a script or test that fails now and would pass if
|
|
39
|
-
the bug were fixed.
|
|
40
|
-
|
|
41
|
-
Report, in this order:
|
|
42
|
-
1. The exact command to reproduce.
|
|
43
|
-
2. The observed output, verbatim.
|
|
44
|
-
3. The expected output.
|
|
45
|
-
4. What you had to assume, because the report did not say.
|
|
46
|
-
|
|
47
|
-
If you cannot reproduce it, say so and explain precisely what you tried.
|
|
48
|
-
Do not attempt a fix -- the next stage does that. Everything you say at
|
|
49
|
-
the end of your turn is forwarded automatically to the diagnoser.
|
|
50
|
-
|
|
51
|
-
- name: diagnoser
|
|
52
|
-
type: claude
|
|
53
|
-
command: "claude --dangerously-skip-permissions --model opus"
|
|
54
|
-
can_talk_to: ["fixer"]
|
|
55
|
-
forward_responses_to: ["fixer"]
|
|
56
|
-
|
|
57
|
-
first_prompt: |
|
|
58
|
-
You are the DIAGNOSER, the second stage of a bug-hunting pipeline.
|
|
59
|
-
|
|
60
|
-
You receive a reproduction. Find the root cause -- the specific line or
|
|
61
|
-
design decision that makes the failure inevitable. Not the symptom, and
|
|
62
|
-
not the first suspicious thing you see.
|
|
63
|
-
|
|
64
|
-
Report:
|
|
65
|
-
1. Root cause, at file:line, in one paragraph.
|
|
66
|
-
2. The causal chain from input to wrong output.
|
|
67
|
-
3. Why it was not caught: the missing test, the wrong assumption.
|
|
68
|
-
4. The smallest correct fix, and one alternative you rejected (say why).
|
|
69
|
-
|
|
70
|
-
Do not write the fix. Describe it. Your turn is forwarded to the fixer.
|
|
43
|
+
role: |
|
|
44
|
+
You are HUNTER A (backend/logic). When the orchestrator sends you a lead,
|
|
45
|
+
read the code and reproduce the failure with evidence (the log line, the
|
|
46
|
+
input, file:line). Report back to the orchestrator: what you found and how
|
|
47
|
+
confident you are. Do not fix it -- just find it.
|
|
71
48
|
|
|
72
|
-
- name:
|
|
49
|
+
- name: hunter_b
|
|
73
50
|
type: codex
|
|
51
|
+
can_talk_to: [orchestrator]
|
|
74
52
|
command: "codex --yolo"
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
- name: verifier
|
|
92
|
-
type: claude
|
|
93
|
-
command: "claude --dangerously-skip-permissions"
|
|
94
|
-
|
|
95
|
-
# End of the line: it forwards to nobody, so the pipeline stops here.
|
|
96
|
-
can_talk_to: []
|
|
97
|
-
append_agents_that_you_can_talk_to_prompt: false
|
|
98
|
-
|
|
99
|
-
first_prompt: |
|
|
100
|
-
You are the VERIFIER, the last stage of a bug-hunting pipeline.
|
|
101
|
-
|
|
102
|
-
You receive a proposed fix. Assume it is wrong until you have checked.
|
|
103
|
-
|
|
104
|
-
Do all of these:
|
|
105
|
-
1. Run the original reproduction. Does it now pass?
|
|
106
|
-
2. Run the whole test suite. Did anything else break?
|
|
107
|
-
3. Read the fix. Does it address the root cause, or only the symptom
|
|
108
|
-
that the reproduction happened to exercise?
|
|
109
|
-
4. Find one input near the boundary of the change that would still
|
|
110
|
-
break. If you find one, the fix is not done.
|
|
111
|
-
|
|
112
|
-
Print a verdict: SHIPPABLE or NOT SHIPPABLE, then the evidence. Be blunt.
|
|
53
|
+
role: |
|
|
54
|
+
You are HUNTER B (frontend/UX). When the orchestrator sends you a lead,
|
|
55
|
+
trace the user-facing path that triggers the bug and capture the exact
|
|
56
|
+
steps + screenshots/state. Report to the orchestrator with evidence. Do not
|
|
57
|
+
fix it -- just find it.
|
|
58
|
+
|
|
59
|
+
- name: hunter_c
|
|
60
|
+
type: gemini
|
|
61
|
+
can_talk_to: [orchestrator]
|
|
62
|
+
capture: pane
|
|
63
|
+
command: "gemini --yolo"
|
|
64
|
+
role: |
|
|
65
|
+
You are HUNTER C (dependencies/config). Check recent dependency bumps,
|
|
66
|
+
config changes, and environment drift that could explain the symptom. Report
|
|
67
|
+
to the orchestrator with evidence. Do not fix it -- just find it.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# 🧐 Code review -- several developers each build a slice and a single REVIEWER
|
|
3
|
+
# checks every one of them. (v1 had a `broadcast` primitive; v2 has no
|
|
4
|
+
# broadcast -- instead the reviewer's `can_talk_to` simply lists all the
|
|
5
|
+
# developers, so it can address each one directly.)
|
|
6
|
+
#
|
|
7
|
+
# cp examples/code-review.yaml my-repo.yaml
|
|
8
|
+
# agentainer up -c my-repo.yaml
|
|
9
|
+
# agentainer send -c my-repo.yaml --to reviewer "Review the open PRs, report per dev."
|
|
10
|
+
# agentainer down -c my-repo.yaml
|
|
11
|
+
#
|
|
12
|
+
# Shape: developers never talk to each other; each talks only to the reviewer,
|
|
13
|
+
# who is the hub. This keeps every critique flowing through one consistent
|
|
14
|
+
# reviewer voice instead of N pairwise arguments.
|
|
15
|
+
#
|
|
16
|
+
# dev-1 \
|
|
17
|
+
# dev-2 > reviewer (hub)
|
|
18
|
+
# dev-3 /
|
|
19
|
+
#
|
|
20
|
+
# Key-free: commands are bash loops. Swap for real CLIs to review real code.
|
|
21
|
+
# =============================================================================
|
|
22
|
+
|
|
23
|
+
swarm:
|
|
24
|
+
name: code-review
|
|
25
|
+
root: ./code-review-workspace
|
|
26
|
+
|
|
27
|
+
defaults:
|
|
28
|
+
capture: none
|
|
29
|
+
can_talk_to: []
|
|
30
|
+
|
|
31
|
+
agents:
|
|
32
|
+
- name: reviewer
|
|
33
|
+
type: claude
|
|
34
|
+
can_talk_to: [dev_1, dev_2, dev_3, user]
|
|
35
|
+
command: "claude --dangerously-skip-permissions"
|
|
36
|
+
role: |
|
|
37
|
+
You are the REVIEWER -- the hub. When a developer sends you their work
|
|
38
|
+
(mail in your inbox/), read it and try to break it: wrong logic,
|
|
39
|
+
unhandled errors, race conditions, data loss, auth holes. Cite file:line.
|
|
40
|
+
Send each critique back to the specific developer who wrote it (write a file
|
|
41
|
+
into outbox/<their-name>/). Never let two developers argue with each other
|
|
42
|
+
directly -- all feedback goes through you. If the human (user) writes to
|
|
43
|
+
you, answer them.
|
|
44
|
+
|
|
45
|
+
- name: dev_1
|
|
46
|
+
type: codex
|
|
47
|
+
can_talk_to: [reviewer]
|
|
48
|
+
command: "codex --yolo"
|
|
49
|
+
role: |
|
|
50
|
+
You are DEVELOPER 1. Build your slice in your own directory. When a unit
|
|
51
|
+
of work is done, send the reviewer a short summary (write a file into
|
|
52
|
+
outbox/reviewer/): what changed, why, and what you are unsure about.
|
|
53
|
+
Ask for review. Act only on the reviewer's feedback.
|
|
54
|
+
|
|
55
|
+
- name: dev_2
|
|
56
|
+
type: codex
|
|
57
|
+
can_talk_to: [reviewer]
|
|
58
|
+
command: "codex --yolo"
|
|
59
|
+
role: |
|
|
60
|
+
You are DEVELOPER 2. Build your slice in your own directory. When a unit
|
|
61
|
+
of work is done, send the reviewer a short summary (write a file into
|
|
62
|
+
outbox/reviewer/): what changed, why, and what you are unsure about.
|
|
63
|
+
Ask for review. Act only on the reviewer's feedback.
|
|
64
|
+
|
|
65
|
+
- name: dev_3
|
|
66
|
+
type: codex
|
|
67
|
+
can_talk_to: [reviewer]
|
|
68
|
+
command: "codex --yolo"
|
|
69
|
+
role: |
|
|
70
|
+
You are DEVELOPER 3. Build your slice in your own directory. When a unit
|
|
71
|
+
of work is done, send the reviewer a short summary (write a file into
|
|
72
|
+
outbox/reviewer/): what changed, why, and what you are unsure about.
|
|
73
|
+
Ask for review. Act only on the reviewer's feedback.
|