@dirwin517/bot-farm 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 +667 -449
- package/imgs/ApplyChanges.png +0 -0
- package/imgs/BotConfig.png +0 -0
- package/imgs/BotRouting.png +0 -0
- package/imgs/FARM.png +0 -0
- package/imgs/Harvest.png +0 -0
- package/imgs/KanBan.png +0 -0
- package/imgs/KanBanChat.png +0 -0
- package/imgs/MultiTeamRuns.png +0 -0
- package/imgs/SetLimits.png +0 -0
- package/imgs/StartWorkStream.png +0 -0
- package/imgs/WorkStreams.png +0 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,437 +1,299 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
watch what they are burning, steer them without entering them, and — if you allow it — let them talk
|
|
5
|
-
to each other.
|
|
3
|
+
# 🌾 BotFarm
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
npm link # or: node src/cli.mjs up
|
|
9
|
-
botfarm up # starts opencode serve if needed, opens the dashboard
|
|
10
|
-
```
|
|
5
|
+
### Run a whole team of AI coding agents — and watch them work the field.
|
|
11
6
|
|
|
12
|
-
|
|
7
|
+
A kanban-style process manager and dashboard for [opencode](https://opencode.ai) sessions.
|
|
8
|
+
Run several agents at once, see what they're burning, steer them without jumping in,
|
|
9
|
+
and — if you allow it — let them talk to each other.
|
|
13
10
|
|
|
14
|
-
|
|
11
|
+
[](https://www.npmjs.com/package/@dirwin517/bot-farm)
|
|
12
|
+
[](https://nodejs.org)
|
|
13
|
+
[](package.json)
|
|
14
|
+
[](LICENSE)
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
named after its id:
|
|
25
|
-
- `botfarm/<id>.agent.botfarm.yml` — one agent: `{ title, role, prompt, model?, variant?, tools?, tiers?, may_spawn? }`
|
|
26
|
-
- `botfarm/<id>.pipeline.botfarm.yml` — one pipeline: `{ title, description?, limits?, stages: [{ id, title?, persona | human: true, receives: [stage ids], after?: [stage ids], split?: tasks, parallel?, difficulty?, limits?: { minutes, tokens, usd }, prompt }] }`
|
|
27
|
-
- `botfarm/routing.botfarm.yml` — model routing by difficulty (off until `enabled: true`)
|
|
28
|
-
- `botfarm/mcps/*.js` — your own tools, served to every bot in the workspace (see below)
|
|
29
|
-
|
|
30
|
-
Older `botfarm-agents.yml` / `botfarm-pipeline.yml` files are split into these on first start and kept as
|
|
31
|
-
`.bak`. Edit them in the app (Pipelines / Agents in the sidebar, as a form or as that one file's YAML) or
|
|
32
|
-
by hand; hand edits are picked up without a restart.
|
|
33
|
-
- **Workstream** — one story going through one pipeline. Starting a pipeline always makes a new
|
|
34
|
-
workstream with its own branch and worktree, its own team of bots, its own channel and its own board.
|
|
35
|
-
Two DoUserStory runs are two workstreams side by side. A workstream keeps the pipeline definition it
|
|
36
|
-
started with, so editing the pipeline later changes only new workstreams.
|
|
37
|
-
|
|
38
|
-
**Parallel work.** A stage normally starts when the one before it hands off. Give it
|
|
39
|
-
`after: [stage ids]` to start as soon as those are done instead — dev and QA both `after: [analyse]`
|
|
40
|
-
build and write tests side by side (TDD), and a stage `after: [build, tests]` waits for both.
|
|
41
|
-
`split: tasks` on a stage fans it out: the stage it waits on is asked for a `tasks` list
|
|
42
|
-
(`[{ title, detail }]`) on its handoff, each item becomes its own card, and up to `parallel: N` bots of
|
|
43
|
-
that kind (default 3, max 10; overridable when you start the workstream) work them at once in the same
|
|
44
|
-
worktree — extra bots are added to the team as needed. The next stage waits for every piece and gets
|
|
45
|
-
their handoffs joined. Use `{{#item}}{{title}} {{detail}}{{/item}}` in the split stage's prompt, or let
|
|
46
|
-
BotFarm add "Your part — n of N" itself. See the `story-parallel` example.
|
|
47
|
-
|
|
48
|
-
**Limits.** `limits: { minutes, tokens, usd }` on a stage applies to each of its cards (each piece of a
|
|
49
|
-
split); `limits:` on a pipeline, or the Limits box when you start one, applies to the whole workstream.
|
|
50
|
-
When a card reaches a limit its bot is stopped and the card waits in Needs you (Continue gives half as
|
|
51
|
-
much again, Set limits… for exact numbers, or Stop); when the workstream reaches one every bot on it
|
|
52
|
-
stops until you raise it. Limits can be changed any time from the workstream header.
|
|
53
|
-
|
|
54
|
-
The workstream page has the team down the left (grouped by kind, live status and spend), the board
|
|
55
|
-
with the chat under it, and a **Farm** view of the same board: cards are crops moving from the shed to
|
|
56
|
-
the field to the silo, and each bot walks to the card it is working on.
|
|
57
|
-
|
|
58
|
-
**BotFarm's MCP server** is called `botfarm`: bots see `botfarm_task_complete`, `botfarm_room_post`,
|
|
59
|
-
`botfarm_notes_read` and so on (it used to be `botfarm`; old worktrees are renamed automatically).
|
|
60
|
-
|
|
61
|
-
**Your own tools.** Every `.js` file in `botfarm/mcps/` exports `name`, `description`, `inputSchema` and
|
|
62
|
-
`execute(args, ctx)` and becomes `botfarm_<name>` for the workspace's bots. `ctx.repoRoot` is the calling
|
|
63
|
-
bot's worktree, `ctx.exec(cmd, args)` runs a program there, `ctx.log(...)` writes to the test bench. Saving
|
|
64
|
-
a file reloads it; bots get the new list on their next idle moment. **Tools** in the sidebar is the test
|
|
65
|
-
bench: a form from the schema (or raw JSON), where to run it, the result and log, and the source to edit.
|
|
16
|
+
#### Install as easy as
|
|
17
|
+
```
|
|
18
|
+
npm i -g @dirwin517/bot-farm
|
|
19
|
+
```
|
|
20
|
+
#### Running as easy as
|
|
21
|
+
```
|
|
22
|
+
bot-farm
|
|
23
|
+
```
|
|
66
24
|
|
|
67
|
-
|
|
68
|
-
from its next turn (Opus for a hard fix, Sonnet or Haiku to save money), kept across restarts, without
|
|
69
|
-
touching the agent file.
|
|
70
|
-
|
|
71
|
-
**Keeping it cheap — no paid model involved in any of these:**
|
|
72
|
-
- *Routing by difficulty*: each card is sized easy / normal / hard from its words, length and criteria
|
|
73
|
-
(or `difficulty:` on a stage or split piece) and gets the model from `routing.botfarm.yml` or the agent's
|
|
74
|
-
`tiers:`. The card shows its size and model. All of it is editable in the app: **Model routing** in
|
|
75
|
-
the sidebar, **Difficulty** on each stage in the pipeline editor, **Model by difficulty** in the agent editor.
|
|
76
|
-
A tier can cap its model — *Quota and fallback*: e.g. up to $5 a day on Opus, then Sonnet (also tokens or
|
|
77
|
-
minutes, per day / week / month / workstream). Spend is counted per workspace per model; once it runs out,
|
|
78
|
-
new cards take the fallback and bots already on it move over, at once or on their next turn.
|
|
79
|
-
*Per agent*: an agent's `tiers:` override the workspace rules size by size (give only the reviewer its
|
|
80
|
-
own `hard:` and it still uses the workspace's easy/normal), and apply even with workspace routing off.
|
|
81
|
-
An agent's own quota counts only that agent's spend (e.g. "the reviewer gets $3/day of Opus"); a
|
|
82
|
-
workspace quota counts everyone's. **Model routing → What each agent gets** shows the effective model,
|
|
83
|
-
reasoning, quota and where each came from, per agent and size. One-line YAML maps
|
|
84
|
-
(`quota: { usd: 3, per: day }`) work in hand-edited files.
|
|
85
|
-
- *Apply to my repos*: from the harvest (**Apply to my repos…**) or any time after (**Apply to repos…** in the
|
|
86
|
-
workstream header), every repo in the workstream — parent, services, linked repos — becomes a patch of what
|
|
87
|
-
its worktree has that your checkout does not (edits, new files, commits on the branch, from the merge-base),
|
|
88
|
-
leaving out BotFarm's own files. The dialog shows, per repo, where it goes (path and current branch), the
|
|
89
|
-
files, whether it applies cleanly / needs a 3-way merge / will conflict / is already there, and warns about
|
|
90
|
-
your own uncommitted edits to the same files. Applying never commits or stages a clean patch: it lands as
|
|
91
|
-
working-tree changes to review and commit yourself. Otherwise it falls back to a 3-way apply (conflict
|
|
92
|
-
markers) and then `--reject` (.rej files). **Undo last apply** reverses the clean ones. Patches are kept in
|
|
93
|
-
`.botfarm/workstreams/<id>.patches/` and can be downloaded. API: `GET /api/projects/:id/patches`,
|
|
94
|
-
`…/patches/file?rel=`, `POST …/patches/apply {repos?}`, `POST …/patches/undo`.
|
|
95
|
-
- *Repos from anywhere*: a workstream's worktree can hold repos from outside the workspace. **Add repo**
|
|
96
|
-
in the new-workstream dialog (e.g. `~/workspace/spt`, optional folder name) links it to the workspace;
|
|
97
|
-
each workstream it is ticked for gets a worktree of it on the same branch, mounted at `<worktree>/spt/`
|
|
98
|
-
(hidden from the parent's git status via `info/exclude`). **+ Repo** on a running workstream adds one after
|
|
99
|
-
the fact; the bots are told in the chat, the team notes and every card ("Repos in this worktree").
|
|
100
|
-
Card diffs, snapshots, PR stats and deleting the workstream (worktrees and `botfarm/` branches) cover them.
|
|
101
|
-
API: `POST /api/workspaces/:id/repos {path, as?, running?}`, `DELETE …/repos?path=`, `POST /api/projects/:id/repos {path, as?}`.
|
|
102
|
-
Docker MCPs see it only if the path is under a mounted folder (`~/workspace`, `~/worktrees`).
|
|
103
|
-
- *Send back (fix loops)*: Plan → Code → Verify → Fail → Code → Verify → Pass. A checker that finds a
|
|
104
|
-
failing test, broken build or missed criterion calls `botfarm_send_back { task_id, to, reason, failures, files }`
|
|
105
|
-
instead of fixing it. The earlier card goes back to To do with what failed and its last handoff kept
|
|
106
|
-
("↩ round 2"), preferring the bot that did it (another bot of that kind takes it after ~90s if that one is
|
|
107
|
-
busy or gone); the checker's card waits and comes back to the same checker to verify ("↻ verify 2"). For a
|
|
108
|
-
split stage only the pieces that touched the named files go back, or a new "Fix:" piece is added. Any
|
|
109
|
-
earlier stage is a valid target unless the stage says `send_back: [build]` or `send_back: false`;
|
|
110
|
-
`max_rounds` (per stage or pipeline, default 3, also in the pipeline editor as *Fix rounds*) caps it, and
|
|
111
|
-
the same failure twice stops too — then you choose *One more round*, *Accept as is* or *Stop*. You can
|
|
112
|
-
send a done card back yourself: drag it to To do, or *Send back…* on the card. Fix rounds earn little XP;
|
|
113
|
-
the checker gets a little for catching them.
|
|
114
|
-
- *Loop detection* counts a call as repeated only when the tool **and every argument** match (a fingerprint of
|
|
115
|
-
the whole input, key order ignored) — three greps for different things are work. An edit in between resets
|
|
116
|
-
the count, so build → fix → build is fine.
|
|
117
|
-
- *Trimmed handoffs*: the next bot reads a brief (long code blocks become pointers, repeats go, cut at
|
|
118
|
-
~1800 characters); `botfarm_handoff(stage)` fetches the full text. For a local model instead, set
|
|
119
|
-
`"handoffs": { "local": { "url": "http://localhost:11434", "model": "qwen2.5:3b" } }` in
|
|
120
|
-
`~/.botfarm/config.json` (falls back to the rules).
|
|
121
|
-
- *Team notes*: `botfarm_notes_write` / `botfarm_notes_read`; every card lists the notes and the files
|
|
122
|
-
teammates already read.
|
|
123
|
-
- *Loop stop*: the same call three times, five failures in a row, or ~250k tokens without an edit pauses
|
|
124
|
-
the card and asks you (`"loops": { "tokensWithoutEdits": N }` to tune).
|
|
25
|
+
<img src="imgs/FARM.png" alt="The Farm view: bots walk from the farmhouse to the crops they're working on" width="100%">
|
|
125
26
|
|
|
126
|
-
|
|
127
|
-
took, tokens, cost, model, XP, every file that changed on disk while it ran, with the diff from where the file stood when the card
|
|
128
|
-
began — whatever changed it: opencode's tools, an MCP server, a script (files it did not list in its
|
|
129
|
-
handoff are marked) — open *Changes*, and any screenshots (images among its files, or image results from its tools; click to
|
|
130
|
-
enlarge). Split stages post one more when all their pieces are in. These never wake the other bots.
|
|
131
|
-
|
|
132
|
-
**Pause / Resume** in a workstream's header stops every bot on it (mid-card) until you resume; resuming
|
|
133
|
-
tells each stopped bot to pick its card up again.
|
|
134
|
-
|
|
135
|
-
**When a workstream finishes**: a *PR packet* (`.botfarm/workstreams/<id>.pr.md` — criteria with the tests
|
|
136
|
-
behind them, files, commits, how to test, open questions, cost; **Open PR…** pushes and runs `gh pr
|
|
137
|
-
create`), a *harvest* (a small celebration and a line in the home page's harvest log) and XP.
|
|
27
|
+
<sub>The <b>Farm</b> view — every card is a crop, every bot walks to the one it's working on.</sub>
|
|
138
28
|
|
|
139
|
-
|
|
140
|
-
budget, nothing left open. Levels bring hats on the farm and small perks: level 3 +10% card limits,
|
|
141
|
-
level 5 the first limit hit extends itself once, level 8 +20%.
|
|
29
|
+
</div>
|
|
142
30
|
|
|
143
|
-
|
|
144
|
-
replay file gives one portable JSON; open it from the home page of any BotFarm.
|
|
31
|
+
---
|
|
145
32
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
**
|
|
33
|
+
> ## ⚠️ Big, important, please-actually-read-this disclaimer
|
|
34
|
+
>
|
|
35
|
+
> **BotFarm drives AI agents. AI makes mistakes.** Sometimes small ones, sometimes confident,
|
|
36
|
+
> creative, spectacular ones.
|
|
37
|
+
>
|
|
38
|
+
> These bots can run commands, edit and delete files, push branches and talk to each other,
|
|
39
|
+
> all with whatever access *you* give them. If a bot:
|
|
40
|
+
>
|
|
41
|
+
> - 🔥 deletes everything you own (your repo, your home folder, your will to live on a Friday afternoon),
|
|
42
|
+
> - 🤗 decides to "just quickly" hack into Hugging Face, or anywhere else,
|
|
43
|
+
> - 💸 burns through your cloud budget chasing a flaky test for six hours,
|
|
44
|
+
> - 🐐 orders 400 goats to your house because the acceptance criteria said "herd the requests",
|
|
45
|
+
> - 🤖 or does anything else unexpected, unwise or illegal,
|
|
46
|
+
>
|
|
47
|
+
> …**that is not my fault.** You started the farm; you own what it harvests.
|
|
48
|
+
>
|
|
49
|
+
> **Use it at your own discretion, and with supervision:**
|
|
50
|
+
>
|
|
51
|
+
> - Keep an eye on your bots. Don't leave them unattended with access you'd regret.
|
|
52
|
+
> - Run them in worktrees, containers or sandboxes, never with production credentials.
|
|
53
|
+
> - Set [limits](#limits) so they stop and ask before spending too much.
|
|
54
|
+
> - Review every change before it reaches your real repos. That's why [Apply to my repos](#apply-to-my-repos) never commits for you.
|
|
55
|
+
>
|
|
56
|
+
> BotFarm is provided **"as is", without warranty of any kind**. See the [MIT License](LICENSE).
|
|
57
|
+
> By using it you accept all responsibility for what your bots do.
|
|
149
58
|
|
|
150
|
-
|
|
151
|
-
stage's open questions) can offer choices per question — pick one, or several — and every question can
|
|
152
|
-
also be answered in your own words; a question without choices is open-ended. They appear at the bottom
|
|
153
|
-
of the chat and on the card, answerable in place.
|
|
59
|
+
---
|
|
154
60
|
|
|
155
|
-
|
|
156
|
-
tools stay on). The built-in product agent is read-only and told to polish the criteria it is given,
|
|
157
|
-
not to study the code — that is the dev's job. Unedited built-ins in an existing workspace are moved to
|
|
158
|
-
the current ones on start; anything you changed is left alone.
|
|
61
|
+
## ✨ Why BotFarm?
|
|
159
62
|
|
|
160
|
-
A
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
63
|
+
- 🧑🤝🧑 **A team, not a chat window.** Product, dev, QA and reviewer bots work one story together, each on its own card, handing off to the next.
|
|
64
|
+
- 🗂️ **One board per story.** Every workstream gets its own branch, worktree, team, chat and kanban.
|
|
65
|
+
- 💸 **Always know the bill.** Live tokens, cost and limits per card and per workstream — bots stop and ask before they overspend.
|
|
66
|
+
- 🧠 **Cheap by default.** Route easy cards to cheap models and save the big one for hard work. No paid model decides.
|
|
67
|
+
- 🔁 **Real fix loops.** QA can send work back to dev with what failed — Plan → Code → Verify → Fail → Code → Pass.
|
|
68
|
+
- 🙋 **You stay in charge.** Questions land in *Needs you*; stages can wait for your sign-off; nothing is applied to your real repo until you say so.
|
|
69
|
+
- 🪶 **Zero dependencies.** Node 20+ and `git`. That's it.
|
|
165
70
|
|
|
166
|
-
|
|
167
|
-
locally): its branch, worktree, story, stage statuses, and the opencode session id of every bot — plus
|
|
168
|
-
the ones they replaced. On start botfarm re-adopts any bot its own state has forgotten, and recreates a
|
|
169
|
-
workstream it has no record of, so a lost `~/.botfarm` or a new machine gets the team back.
|
|
71
|
+
---
|
|
170
72
|
|
|
171
|
-
|
|
172
|
-
global board).
|
|
73
|
+
## 🚀 Quick start
|
|
173
74
|
|
|
174
|
-
|
|
75
|
+
```bash
|
|
76
|
+
npm install -g @dirwin517/bot-farm # or, from a clone: npm link
|
|
77
|
+
botfarm up # starts opencode serve if needed and opens the dashboard
|
|
78
|
+
```
|
|
175
79
|
|
|
176
|
-
|
|
177
|
-
archive browser — 250 cards, none of them what you are working on. botfarm shows only sessions it
|
|
178
|
-
manages: ones it started, plus ones you explicitly add with **Add existing…**. The rest sit behind a
|
|
179
|
-
one-line banner offering them. `Remove` puts a session back in that pool; it never deletes anything.
|
|
80
|
+
Then open **http://127.0.0.1:4777** and hit **Start a workstream**.
|
|
180
81
|
|
|
181
|
-
|
|
182
|
-
|
|
82
|
+
> **Want to try it without spending tokens?** Run `node test/mock-opencode.mjs` in one terminal and
|
|
83
|
+
> `botfarm up` in another — you'll get a fully populated board driven by a fake opencode server.
|
|
183
84
|
|
|
184
|
-
|
|
85
|
+
---
|
|
185
86
|
|
|
186
|
-
|
|
187
|
-
are working through, and the conversation they are having about it. Those used to be four separate
|
|
188
|
-
ideas here — a group, a worktree, a board and a room — which meant four things to create before
|
|
189
|
-
anything could happen. They are one object now.
|
|
87
|
+
## 🖼️ A quick tour
|
|
190
88
|
|
|
191
|
-
|
|
192
|
-
project's kanban board, and that project's chat with a composer, side by side. Creating a project
|
|
193
|
-
creates its chat; moving a session into a project moves it into that chat; the project's board is
|
|
194
|
-
its tasks. The global board is still there as a view across every project, rather than as the
|
|
195
|
-
primary one.
|
|
89
|
+
### 1. Pick a workspace, see all your workstreams
|
|
196
90
|
|
|
197
|
-
A
|
|
91
|
+
A **workspace** is a folder and its opencode config. Each **workstream** is one story on its own branch,
|
|
92
|
+
with its own bots, chat and board — run as many side by side as you like.
|
|
198
93
|
|
|
199
|
-
|
|
94
|
+
<img src="imgs/MultiTeamRuns.png" alt="Workspace home page showing four workstreams, each with its own team of bots" width="100%">
|
|
200
95
|
|
|
201
|
-
|
|
202
|
-
hour), the closing sentences of the last message so you can see what you would be continuing, and the
|
|
203
|
-
controls you actually reach for. Click one and you get a centred modal: the full scrollable
|
|
204
|
-
transcript with a composer on the left, stats, cost, tools and mesh settings on the right.
|
|
96
|
+
### 2. Start a workstream
|
|
205
97
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
- **Abort** — one session, or all running sessions from the header.
|
|
209
|
-
- **Inspect** — slides open the usage breakdown, tool timeline with durations, changed files, peers
|
|
210
|
-
and transcript.
|
|
211
|
-
- Cards sort by attention: running, then waiting on a permission, then errored, then idle.
|
|
98
|
+
Choose a pipeline, paste the story (with its acceptance criteria), pick which repos go in the worktree,
|
|
99
|
+
and optionally set a budget. Every bot gets the full story.
|
|
212
100
|
|
|
213
|
-
|
|
101
|
+
<img src="imgs/StartWorkStream.png" alt="Start a workstream dialog: pipeline, story, branch, repos and limits" width="100%">
|
|
214
102
|
|
|
215
|
-
|
|
216
|
-
the socket will not connect. Neither polls. Actions stay on plain HTTP: they are one-shot, they want
|
|
217
|
-
status codes and retries, and multiplexing them over the socket would mean reinventing request ids
|
|
218
|
-
and error handling that `fetch` already has.
|
|
103
|
+
### 3. Watch the board
|
|
219
104
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
quiet or a session looks stale. A board with nothing happening makes **zero** requests per second;
|
|
223
|
-
with the stream down the polling loop comes back automatically.
|
|
105
|
+
The team runs down the left with live status and spend. The board shows every card moving through
|
|
106
|
+
**To do → Doing → Needs you → Done**, with the team's group chat underneath. Flip to **Farm** for the fun version.
|
|
224
107
|
|
|
225
|
-
|
|
226
|
-
`{parts: [...]}` to `{prompt: {text}}`, so botfarm tries the known shapes, keeps the index of the one
|
|
227
|
-
that worked, and reports clearly if none is accepted.
|
|
108
|
+
<img src="imgs/KanBan.png" alt="Kanban board with the team list, cards per stage and the group chat" width="100%">
|
|
228
109
|
|
|
229
|
-
|
|
230
|
-
experimental and event names have already changed once; assistant messages always carry their own
|
|
231
|
-
usage and tool calls are always parts with stable ids. The event stream only says *something changed
|
|
232
|
-
in session X* — the message diff does the counting. The client also sniffs the v1/v2 path dialect at
|
|
233
|
-
connect time, so a rename of `/session` to `/api/session` doesn't break it.
|
|
110
|
+
### 4. Look over a bot's shoulder
|
|
234
111
|
|
|
235
|
-
|
|
112
|
+
Click any bot to see its live transcript, tokens, cost, MCP servers and worktree. Talk to it directly,
|
|
113
|
+
switch its model, stop it, or move its card.
|
|
236
114
|
|
|
237
|
-
|
|
115
|
+
<img src="imgs/KanBanChat.png" alt="A bot's live transcript panel with model, tokens, tool calls and a direct-message box" width="100%">
|
|
238
116
|
|
|
239
|
-
|
|
240
|
-
Before anything is created, the dialogs probe the path and tell you what is there — a missing
|
|
241
|
-
directory and a directory that is not a repository are different problems and say so.
|
|
117
|
+
### 5. Shape your pipelines and agents
|
|
242
118
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
worktree of the parent, and a worktree of each selected service at the same relative path inside it.
|
|
246
|
-
Every repo ends up on its own branch and the agent sees the directory structure it expects. The
|
|
247
|
-
dialog lists the nested repositories it found (git will not mention them, since the parent ignores
|
|
248
|
-
them) and you tick the ones in scope; leaving them all unticked gives you the parent alone.
|
|
119
|
+
Pipelines and agents are plain YAML files in your repo — edit them as forms in the app, or flip to the
|
|
120
|
+
YAML tab for anything the form doesn't cover.
|
|
249
121
|
|
|
250
|
-
|
|
251
|
-
|
|
122
|
+
<table>
|
|
123
|
+
<tr>
|
|
124
|
+
<td width="50%"><img src="imgs/WorkStreams.png" alt="Pipeline editor with stages, owners, difficulty, limits and prompt"></td>
|
|
125
|
+
<td width="50%"><img src="imgs/BotConfig.png" alt="Agent editor with name, role, model, reasoning, tools and instructions"></td>
|
|
126
|
+
</tr>
|
|
127
|
+
<tr>
|
|
128
|
+
<td align="center"><sub><b>Pipeline editor</b> — stages, who does them, what they receive</sub></td>
|
|
129
|
+
<td align="center"><sub><b>Agent editor</b> — model, reasoning, tools and brief</sub></td>
|
|
130
|
+
</tr>
|
|
131
|
+
</table>
|
|
252
132
|
|
|
253
|
-
|
|
254
|
-
botfarm new ~/code/app --branch fix/auth-redirect --task "Fix the redirect loop after SSO login"
|
|
255
|
-
```
|
|
133
|
+
### 6. Keep it cheap
|
|
256
134
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
`+142 −18 · 7 files` from `git status`. `botfarm rm <id> --worktree` removes both.
|
|
135
|
+
Route each card to a model by difficulty, cap spend with quotas and fallbacks, and set hard limits
|
|
136
|
+
on any workstream. When a limit is hit, the bots stop and wait for you.
|
|
260
137
|
|
|
261
|
-
|
|
138
|
+
<table>
|
|
139
|
+
<tr>
|
|
140
|
+
<td width="60%"><img src="imgs/BotRouting.png" alt="Model routing: easy, normal and hard tiers with model, reasoning and quota"></td>
|
|
141
|
+
<td width="40%"><img src="imgs/SetLimits.png" alt="Workstream limits dialog: dollars, tokens and minutes"></td>
|
|
142
|
+
</tr>
|
|
143
|
+
<tr>
|
|
144
|
+
<td align="center"><sub><b>Model routing</b> by difficulty</sub></td>
|
|
145
|
+
<td align="center"><sub><b>Limits</b> per workstream or per card</sub></td>
|
|
146
|
+
</tr>
|
|
147
|
+
</table>
|
|
262
148
|
|
|
263
|
-
|
|
149
|
+
### 7. Harvest the work
|
|
264
150
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
making one (stacking, moulding, polishing, welding), product from a shaping one (framing, scoping,
|
|
269
|
-
sketching), reviewers from a weighing one (tallying, auditing, squaring). `@prying-heron` reads as a
|
|
270
|
-
tester before you have looked at anything else on the card, and in a four-bot room the handle is
|
|
271
|
-
doing the work a job title would. A persona can supply its own `adjectives:` list. The handle isn't decoration — it's the
|
|
272
|
-
addressing scheme. A model copies a three-syllable handle reliably and a 26-character ulid
|
|
273
|
-
unreliably, and you can hold six handles in your head at once.
|
|
151
|
+
When a workstream finishes you get a PR packet, a small celebration 🎉, and XP for the bots.
|
|
152
|
+
**Apply to my repos** turns each worktree into a patch and lands it in your real checkout as
|
|
153
|
+
unstaged changes — ready to review in your IDE. Nothing is committed for you.
|
|
274
154
|
|
|
275
|
-
|
|
276
|
-
cards scannable — sixty-four creatures against sixteen backgrounds, so a thousand sessions come and
|
|
277
|
-
go before two of them wear the same face. They are generated locally, instantly, and
|
|
278
|
-
deterministically — no model, no download, no network.
|
|
155
|
+
<img src="imgs/ApplyChanges.png" alt="Apply the work to your repos dialog showing each repo, target branch and patch status" width="100%">
|
|
279
156
|
|
|
280
|
-
|
|
157
|
+
---
|
|
281
158
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
159
|
+
## 📚 Table of contents
|
|
160
|
+
|
|
161
|
+
- [Core concepts](#-core-concepts)
|
|
162
|
+
- [Pipelines](#-pipelines)
|
|
163
|
+
- [Keeping it cheap](#-keeping-it-cheap)
|
|
164
|
+
- [Working with your repos](#-working-with-your-repos)
|
|
165
|
+
- [Needs you: questions & sign-offs](#-needs-you-questions--sign-offs)
|
|
166
|
+
- [Fun stuff: XP, levels & replays](#-fun-stuff-xp-levels--replays)
|
|
167
|
+
- [The mesh: bots talking to bots](#-the-mesh-bots-talking-to-bots)
|
|
168
|
+
- [Extending BotFarm](#-extending-botfarm)
|
|
169
|
+
- [CLI](#-cli)
|
|
170
|
+
- [Configuration](#%EF%B8%8F-configuration)
|
|
171
|
+
- [Under the hood](#-under-the-hood)
|
|
172
|
+
- [Development & tests](#-development--tests)
|
|
173
|
+
- [Known limits](#%EF%B8%8F-known-limits)
|
|
288
174
|
|
|
289
|
-
|
|
175
|
+
---
|
|
290
176
|
|
|
291
|
-
|
|
292
|
-
the whole authentication story: a tool call arrives already bound to one caller, so an agent cannot
|
|
293
|
-
claim to be a peer. When you enable the mesh for a session, botfarm registers the server with opencode
|
|
294
|
-
at runtime and also writes it into `<worktree>/.opencode/opencode.json`:
|
|
177
|
+
## 🧭 Core concepts
|
|
295
178
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
179
|
+
| Concept | What it is |
|
|
180
|
+
| --- | --- |
|
|
181
|
+
| **Workspace** | A folder and its opencode config. Its bots and pipelines live in a `botfarm/` folder at its root. |
|
|
182
|
+
| **Workstream** | One story going through one pipeline — its own branch, worktree, team, chat and board. |
|
|
183
|
+
| **Pipeline** | A recipe of stages (e.g. `analyse → build → verify → check → signoff`). |
|
|
184
|
+
| **Agent** | A bot definition: role, model, tools and brief. |
|
|
185
|
+
| **Card** | One task on the board — a pipeline stage, a piece of a split stage, or ad-hoc work. |
|
|
303
186
|
|
|
304
|
-
|
|
305
|
-
is the reliable fallback (restart that session). `.opencode/` is added to `.git/info/exclude` so
|
|
306
|
-
flipping a toggle never dirties the branch the agent is about to commit from.
|
|
187
|
+
### Workspaces
|
|
307
188
|
|
|
308
|
-
|
|
189
|
+
The first workspace is the folder you start BotFarm in. Change it with `"defaultWorkspace"` in
|
|
190
|
+
`~/.botfarm/config.json` or `botfarm up --workspace <dir>`, and open more with the folder browser under
|
|
191
|
+
the workspace name.
|
|
309
192
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
not see a `send` tool and then get refused, it simply has no such tool.
|
|
193
|
+
A workspace's definitions live at its root in `botfarm/` (created with the built-in set if missing),
|
|
194
|
+
one file per definition, named after its id:
|
|
313
195
|
|
|
314
|
-
|
|
196
|
+
| File | Holds |
|
|
197
|
+
| --- | --- |
|
|
198
|
+
| `botfarm/<id>.agent.botfarm.yml` | One agent: `{ title, role, prompt, model?, variant?, tools?, tiers?, may_spawn? }` |
|
|
199
|
+
| `botfarm/<id>.pipeline.botfarm.yml` | One pipeline: `{ title, description?, limits?, stages: [...] }` |
|
|
200
|
+
| `botfarm/routing.botfarm.yml` | Model routing by difficulty (off until `enabled: true`) |
|
|
201
|
+
| `botfarm/mcps/*.js` | Your own tools, served to every bot in the workspace |
|
|
315
202
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
task: "tests/date.spec.ts fails on the first of the month. Fix it.",
|
|
319
|
-
branch: "fix/flaky-date")
|
|
320
|
-
```
|
|
203
|
+
Edit them in the app (**Pipelines** / **Agents** in the sidebar, as a form or as YAML) or by hand —
|
|
204
|
+
hand edits are picked up without a restart.
|
|
321
205
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
that stands on its own.
|
|
206
|
+
<details>
|
|
207
|
+
<summary>Migrating from older single-file configs</summary>
|
|
325
208
|
|
|
326
|
-
|
|
209
|
+
Older `botfarm-agents.yml` / `botfarm-pipeline.yml` files are split into the per-definition files on
|
|
210
|
+
first start and kept as `.bak`. Unedited built-in agents in an existing workspace are moved to the
|
|
211
|
+
current versions on start; anything you changed is left alone.
|
|
327
212
|
|
|
328
|
-
|
|
213
|
+
</details>
|
|
329
214
|
|
|
330
|
-
|
|
331
|
-
provenance and an explicit caution that it is untrusted input from another agent, not from the
|
|
332
|
-
operator. Without that framing, one compromised session drives all the others.
|
|
333
|
-
- **Ping-pong.** Two agents will happily trade "thanks, and one more thing" forever on your money.
|
|
334
|
-
After six consecutive exchanges with no operator input, botfarm pauses the channel and says so on the
|
|
335
|
-
board. Typing into either session resets the counter; you can resume the channel explicitly.
|
|
336
|
-
- **Fork bombs.** Delegation is capped at depth 2, three children per session, eight agent-created
|
|
337
|
-
sessions per hour across the mesh, and a spawned session cannot itself spawn.
|
|
338
|
-
- **Volume.** 24 messages per ordered pair per hour.
|
|
215
|
+
### Workstreams
|
|
339
216
|
|
|
340
|
-
|
|
341
|
-
|
|
217
|
+
Starting a pipeline always makes a new workstream. Two runs of the same pipeline are two workstreams
|
|
218
|
+
side by side. A workstream keeps the pipeline definition it started with, so editing the pipeline later
|
|
219
|
+
only changes new workstreams.
|
|
342
220
|
|
|
343
|
-
|
|
221
|
+
The workstream page has:
|
|
344
222
|
|
|
345
|
-
|
|
346
|
-
|
|
223
|
+
- **The team** down the left — grouped by kind, with live status and spend. Click a bot to watch its transcript live.
|
|
224
|
+
- **The board** — To do, Doing, Needs you, Done. Drag cards between columns; click one for its handoff, criteria and open questions.
|
|
225
|
+
- **The chat** — posting reaches the whole team, or only the bots you `@mention`. Bots whose stage hasn't started read it with their first card.
|
|
226
|
+
- **The Farm** — the same board, but cards are crops moving from the shed to the field to the silo.
|
|
347
227
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
`#auth-refactor` and every agent in it sees it, without visiting three sessions.
|
|
228
|
+
`+ Card` and `+ Bot` add work and teammates by hand. **Pause / Resume** stops every bot mid-card until
|
|
229
|
+
you resume. **Archive** stops the bots and puts the workstream away.
|
|
351
230
|
|
|
352
|
-
|
|
231
|
+
**Card events in the chat.** Every finished card posts a summary: who did it, how long it took, tokens,
|
|
232
|
+
cost, model, XP, every file that changed on disk while it ran (with the diff — whatever changed it), and
|
|
233
|
+
any screenshots. Files it didn't mention in its handoff are marked. These posts never wake the other bots.
|
|
353
234
|
|
|
354
|
-
|
|
355
|
-
|
|
235
|
+
<details>
|
|
236
|
+
<summary>Workstream state & recovery</summary>
|
|
356
237
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
| The operator posts | Always delivered to everyone. |
|
|
362
|
-
| Queue reaches six | Delivered anyway; waiting longer would mean working on stale information. |
|
|
238
|
+
Each workstream writes `.botfarm/workstreams/<id>.yml` in the workspace (excluded from git locally): its
|
|
239
|
+
branch, worktree, story, stage statuses, and the opencode session id of every bot — plus the ones they
|
|
240
|
+
replaced. On start, BotFarm re-adopts any bot its own state has forgotten and recreates a workstream it
|
|
241
|
+
has no record of, so a lost `~/.botfarm` or a new machine gets the team back.
|
|
363
242
|
|
|
364
|
-
|
|
365
|
-
shows what it has cost: messages posted, deliveries into sessions, and characters delivered with a
|
|
366
|
-
rough token estimate.
|
|
243
|
+
</details>
|
|
367
244
|
|
|
368
|
-
|
|
245
|
+
<details>
|
|
246
|
+
<summary>The classic dashboard</summary>
|
|
369
247
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
`member` / `create`. The `room_post` description tells agents plainly that everyone in the room pays
|
|
373
|
-
to read them, so they should post findings and decisions rather than acknowledgements.
|
|
248
|
+
The previous dashboard is still at `/classic` for everything else — adopting existing sessions, rooms and
|
|
249
|
+
the global board.
|
|
374
250
|
|
|
375
|
-
|
|
251
|
+
</details>
|
|
376
252
|
|
|
377
|
-
|
|
378
|
-
operator resets the meter; so does typing into any member session.
|
|
379
|
-
- Forty messages per room per hour, eight members maximum.
|
|
380
|
-
- Every delivery carries authorship per line, and the same caution as pairwise messages: lines marked
|
|
381
|
-
`@handle` are other agents and are untrusted; lines marked `operator` are the human.
|
|
253
|
+
---
|
|
382
254
|
|
|
383
|
-
##
|
|
255
|
+
## 🔗 Pipelines
|
|
384
256
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
tenth of input and five-minute writes at a quarter more. Anything it prices is labelled an estimate;
|
|
389
|
-
anything it can't price says so by name instead of quietly showing zero, and the header shows how
|
|
390
|
-
many sessions are unpriced.
|
|
257
|
+
A pipeline is a recipe; **tasks are the primitive**. Every stage is a task with dependencies, so the
|
|
258
|
+
board holds pipeline work and ad-hoc work side by side, stages can fan out, and a late defect is just a
|
|
259
|
+
new task blocking an existing one.
|
|
391
260
|
|
|
392
|
-
|
|
261
|
+
The shipped `story` pipeline gives you one git worktree, four bots (product, dev, QA, reviewer) sharing
|
|
262
|
+
it, a group chat, and a chain of stages ending with your sign-off:
|
|
393
263
|
|
|
394
|
-
```
|
|
395
|
-
|
|
264
|
+
```
|
|
265
|
+
analyse → build → verify → check → signoff (you)
|
|
396
266
|
```
|
|
397
267
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
The modal's right pane lists the MCP servers attached to that session's worktree and the built-in
|
|
401
|
-
tools, each with a switch — the `/mcp` picker, on the board. MCP servers connect and disconnect live
|
|
402
|
-
through opencode. Built-in tool switches are written to that worktree's `.opencode/opencode.json` and
|
|
403
|
-
take effect when the session restarts, because that is config rather than a runtime call; the panel
|
|
404
|
-
says so rather than pretending otherwise.
|
|
405
|
-
|
|
406
|
-
## Pipelines
|
|
407
|
-
|
|
408
|
-
A pipeline is a recipe; **tasks are the primitive**. That is the one design decision worth knowing
|
|
409
|
-
about, because everything else follows from it: if stages were their own thing, a QA bot finding an
|
|
410
|
-
unrelated defect would have nowhere to put it and a pipeline could only ever be a straight line.
|
|
411
|
-
Since every stage is a task with dependencies, the board holds pipeline work and ad-hoc work side by
|
|
412
|
-
side, stages can fan out, and a late defect is just a new task blocking an existing one.
|
|
268
|
+
Only the first stage is queued; the rest wait until a handoff lands.
|
|
413
269
|
|
|
414
|
-
|
|
415
|
-
reviewer) sharing it, a group chat between them, and four chained tasks.
|
|
270
|
+
### Stage options
|
|
416
271
|
|
|
272
|
+
```yaml
|
|
273
|
+
stages:
|
|
274
|
+
- id: build
|
|
275
|
+
title: Build the pieces
|
|
276
|
+
persona: dev # or `human: true` for a stage you do yourself
|
|
277
|
+
receives: [analyse] # which earlier handoffs it gets
|
|
278
|
+
after: [analyse] # start as soon as these are done (optional)
|
|
279
|
+
split: tasks # fan out into one card per task (optional)
|
|
280
|
+
parallel: 3 # how many bots at once for a split stage (default 3, max 10)
|
|
281
|
+
difficulty: hard # easy / normal / hard — overrides auto-sizing
|
|
282
|
+
limits: { minutes: 30, tokens: 500000, usd: 2 }
|
|
283
|
+
max_rounds: 3 # fix-loop cap
|
|
284
|
+
send_back: [build] # which stages a checker here may send work back to (or false)
|
|
285
|
+
hold_on_questions: true # wait for you if it has open questions
|
|
286
|
+
prompt: |
|
|
287
|
+
...
|
|
417
288
|
```
|
|
418
|
-
analyse → build → verify → check
|
|
419
|
-
```
|
|
420
|
-
|
|
421
|
-
Only the first is queued; the rest are blocked until a handoff lands.
|
|
422
289
|
|
|
423
290
|
### Handoffs
|
|
424
291
|
|
|
425
|
-
A stage ends with `botfarm_task_complete(task_id, summary, acceptance_criteria, artifacts,
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
question parks the task in **review** instead of `done`, so it reaches you before it reaches the next
|
|
429
|
-
bot.
|
|
292
|
+
A stage ends with `botfarm_task_complete(task_id, summary, acceptance_criteria, artifacts, open_questions)`.
|
|
293
|
+
It's structured on purpose — prose alone gives the next bot nothing to template against, and "what did
|
|
294
|
+
you actually change?" is the first question every downstream stage asks.
|
|
430
295
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
Each stage declares `receives`, and its prompt is a mustache template over those handoffs. Dropping
|
|
434
|
-
QA's write-up from the reviewer's prompt is a one-line YAML edit:
|
|
296
|
+
Each stage declares what it `receives`, and its prompt is a mustache template over those handoffs:
|
|
435
297
|
|
|
436
298
|
```yaml
|
|
437
299
|
- id: verify
|
|
@@ -450,135 +312,491 @@ QA's write-up from the reviewer's prompt is a one-line YAML edit:
|
|
|
450
312
|
{{/handoffs.build}}
|
|
451
313
|
```
|
|
452
314
|
|
|
453
|
-
|
|
454
|
-
on load rather than
|
|
455
|
-
|
|
456
|
-
### Tasks are yours too
|
|
315
|
+
Also available: `{{story}}`, and `{{#received}}…{{/received}}` for everything a stage receives. A stage
|
|
316
|
+
that receives a nonexistent stage, or uses handoffs it never gets, is reported on load rather than
|
|
317
|
+
silently rendering empty.
|
|
457
318
|
|
|
458
|
-
|
|
459
|
-
assignee. Work you raise and work a bot raises land in the same column, and clicking any card opens
|
|
460
|
-
the task next to the assignee's live conversation — what was asked on the left, what is happening on
|
|
461
|
-
the right, with a composer, rather than one being a click away from the other.
|
|
319
|
+
### Parallel work
|
|
462
320
|
|
|
463
|
-
|
|
321
|
+
- **`after:`** — a stage normally starts when the one before it hands off. With `after: [ids]` it starts
|
|
322
|
+
as soon as those are done. Dev and QA both `after: [analyse]` build and write tests side by side (TDD);
|
|
323
|
+
a stage `after: [build, tests]` waits for both.
|
|
324
|
+
- **`split: tasks`** — the stage it waits on is asked for a `tasks` list (`[{ title, detail }]`); each item
|
|
325
|
+
becomes its own card, and up to `parallel: N` bots work them at once in the same worktree. The next stage
|
|
326
|
+
waits for every piece and gets their handoffs joined. Use `{{#item}}{{title}} {{detail}}{{/item}}` in the
|
|
327
|
+
prompt, or let BotFarm add "Your part — n of N" itself. See the `story-parallel` example.
|
|
464
328
|
|
|
465
|
-
|
|
466
|
-
digests. Two exceptions, because waiting for an idle event is not always right: the first stage of a
|
|
467
|
-
fresh pipeline goes out immediately (its bot is only "busy" because it was just briefed), and a task
|
|
468
|
-
nobody has picked up for 45 seconds is delivered anyway, since opencode queues prompts durably and a
|
|
469
|
-
missed idle event should not strand a run. An agent mid-thought is never derailed by new work. `botfarm_task_create` lets a bot raise work
|
|
470
|
-
for someone else (by handle, or by persona name within its own run) and optionally block an existing
|
|
471
|
-
task on it, which is how a defect found at QA reaches the dev bot and holds up the reviewer.
|
|
329
|
+
### Fix loops (send back)
|
|
472
330
|
|
|
473
|
-
|
|
331
|
+
Plan → Code → Verify → **Fail** → Code → Verify → **Pass**.
|
|
474
332
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
into `~/.botfarm/botfarm.db` via `node:sqlite`, falling back to a JSON file on older runtimes. The split is
|
|
478
|
-
by kind, so there is never a question of which copy is authoritative. A finished run exports back to
|
|
479
|
-
pipeline YAML for sharing.
|
|
333
|
+
A checker that finds a failing test, broken build or missed criterion calls
|
|
334
|
+
`botfarm_send_back { task_id, to, reason, failures, files }` instead of fixing it itself.
|
|
480
335
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
336
|
+
- The earlier card goes back to To do with what failed and its last handoff (`↩ round 2`), preferring the
|
|
337
|
+
bot that did it (another bot of that kind takes it after ~90s if that one is busy or gone).
|
|
338
|
+
- The checker's card waits and comes back to the same checker to verify (`↻ verify 2`).
|
|
339
|
+
- For a split stage, only the pieces that touched the named files go back — or a new "Fix:" piece is added.
|
|
340
|
+
- `max_rounds` (default 3, *Fix rounds* in the editor) caps it, and the same failure twice stops too — then
|
|
341
|
+
you choose **One more round**, **Accept as is** or **Stop**.
|
|
342
|
+
- You can send a done card back yourself: drag it to To do, or **Send back…** on the card.
|
|
484
343
|
|
|
485
|
-
|
|
344
|
+
<details>
|
|
345
|
+
<summary>How tasks are dispatched & owned</summary>
|
|
486
346
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
347
|
+
**Tasks are enqueued, never interrupting.** A queued task is handed to its assignee when that session
|
|
348
|
+
next goes idle. Two exceptions: the first stage of a fresh pipeline goes out immediately, and a task nobody
|
|
349
|
+
has picked up for 45 seconds is delivered anyway (opencode queues prompts durably, so a missed idle event
|
|
350
|
+
shouldn't strand a run). An agent mid-thought is never derailed by new work.
|
|
491
351
|
|
|
492
|
-
|
|
352
|
+
**Bots can raise work too.** `botfarm_task_create` lets a bot raise work for someone else (by handle, or by
|
|
353
|
+
persona name within its run) and optionally block an existing task on it — that's how a defect found at QA
|
|
354
|
+
reaches the dev bot and holds up the reviewer. **New task** on any board does exactly the same for you.
|
|
493
355
|
|
|
494
|
-
|
|
356
|
+
**Who owns a task:**
|
|
495
357
|
|
|
496
|
-
| | |
|
|
358
|
+
| Owner | Meaning |
|
|
497
359
|
| --- | --- |
|
|
498
|
-
| **session** |
|
|
499
|
-
| **role** |
|
|
500
|
-
| **human** |
|
|
360
|
+
| **session** | One named session does this. |
|
|
361
|
+
| **role** | Whoever is playing that part picks it up — a dev task doesn't die because one dev session was closed. |
|
|
362
|
+
| **human** | You do it, or you answer it; it's never dispatched. |
|
|
363
|
+
|
|
364
|
+
A role task becomes a session's task the moment it's picked up, so two bots playing the same part can't both take it.
|
|
365
|
+
|
|
366
|
+
**Tool restrictions.** An agent's `tools:` list is enforced on every turn (everything else built in is
|
|
367
|
+
switched off; the BotFarm tools stay on). The built-in product agent is read-only and told to polish the
|
|
368
|
+
criteria, not study the code — that's the dev's job.
|
|
369
|
+
|
|
370
|
+
</details>
|
|
371
|
+
|
|
372
|
+
---
|
|
373
|
+
|
|
374
|
+
## 💰 Keeping it cheap
|
|
375
|
+
|
|
376
|
+
None of these involve a paid model making decisions.
|
|
377
|
+
|
|
378
|
+
### Limits
|
|
379
|
+
|
|
380
|
+
`limits: { minutes, tokens, usd }` on a stage applies to each of its cards (each piece of a split).
|
|
381
|
+
`limits:` on a pipeline — or the Limits box when you start one — applies to the whole workstream.
|
|
501
382
|
|
|
502
|
-
|
|
503
|
-
|
|
383
|
+
- When a **card** hits a limit, its bot stops and the card waits in *Needs you*: **Continue** gives half as
|
|
384
|
+
much again, **Set limits…** for exact numbers, or **Stop**.
|
|
385
|
+
- When a **workstream** hits one, every bot on it stops until you raise it.
|
|
504
386
|
|
|
505
|
-
|
|
387
|
+
Limits can be changed any time from the workstream header.
|
|
506
388
|
|
|
507
|
-
|
|
508
|
-
asker's face on it and the choices it offered as buttons. Answering delivers the answer straight back
|
|
509
|
-
into that session, marked as coming from the human running it rather than from a peer. By default
|
|
510
|
-
the bot does not block: it asks, carries on with what it can, and the answer arrives as a message.
|
|
389
|
+
### Model routing by difficulty
|
|
511
390
|
|
|
512
|
-
|
|
513
|
-
|
|
391
|
+
Each card is sized **easy / normal / hard** from its words, length and criteria (or `difficulty:` on a
|
|
392
|
+
stage or split piece) and gets the model from `routing.botfarm.yml` or the agent's `tiers:`. The card shows
|
|
393
|
+
its size and model. Edit it all in the app: **Model routing** in the sidebar, **Difficulty** on each stage,
|
|
394
|
+
**Model by difficulty** in the agent editor.
|
|
514
395
|
|
|
515
|
-
|
|
396
|
+
- **Quota and fallback** — cap a tier's model, e.g. up to $5/day on Opus, then Sonnet (also tokens or
|
|
397
|
+
minutes; per day / week / month / workstream). Once it runs out, new cards take the fallback and bots
|
|
398
|
+
already on it move over — at once or on their next turn.
|
|
399
|
+
- **Per agent** — an agent's `tiers:` override the workspace rules size by size, and apply even with
|
|
400
|
+
workspace routing off. An agent's own quota counts only that agent's spend ("the reviewer gets $3/day of
|
|
401
|
+
Opus"). **Model routing → What each agent gets** shows the effective model, reasoning and quota and where
|
|
402
|
+
each came from.
|
|
403
|
+
- One-line YAML maps (`quota: { usd: 3, per: day }`) work in hand-edited files.
|
|
516
404
|
|
|
517
|
-
|
|
518
|
-
which earlier handoffs it receives, and edit its prompt. Personas have their own form — model, tools
|
|
519
|
-
it may use, mesh permissions, handle vocabulary, and the brief it gets before any task.
|
|
405
|
+
### Switching a bot's model
|
|
520
406
|
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
receiving a phase that does not exist, a persona that is not defined) are reported on save rather
|
|
524
|
-
than swallowed.
|
|
407
|
+
**Model…** in a bot's panel picks another model and reasoning level for that bot from its next turn (Opus
|
|
408
|
+
for a hard fix, Sonnet or Haiku to save money). It's kept across restarts and doesn't touch the agent file.
|
|
525
409
|
|
|
526
|
-
|
|
410
|
+
### Smaller context, fewer wasted turns
|
|
527
411
|
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
412
|
+
- **Trimmed handoffs** — the next bot reads a brief (long code blocks become pointers, repeats go, cut at
|
|
413
|
+
~1800 characters); `botfarm_handoff(stage)` fetches the full text. To use a local model for trimming, set
|
|
414
|
+
`"handoffs": { "local": { "url": "http://localhost:11434", "model": "qwen2.5:3b" } }` in
|
|
415
|
+
`~/.botfarm/config.json` (falls back to the rules).
|
|
416
|
+
- **Team notes** — `botfarm_notes_write` / `botfarm_notes_read`; every card lists the notes and the files
|
|
417
|
+
teammates already read.
|
|
418
|
+
- **Loop detection** — a call counts as repeated only when the tool **and every argument** match. An edit in
|
|
419
|
+
between resets the count, so build → fix → build is fine.
|
|
420
|
+
- **Loop stop** — the same call three times, five failures in a row, or ~250k tokens without an edit pauses
|
|
421
|
+
the card and asks you (tune with `"loops": { "tokensWithoutEdits": N }`).
|
|
422
|
+
|
|
423
|
+
<details>
|
|
424
|
+
<summary>How cost is calculated</summary>
|
|
532
425
|
|
|
533
|
-
|
|
426
|
+
Bedrock returns usage but no price, so opencode reports `$0.00` and every Bedrock session looks free.
|
|
427
|
+
BotFarm prices the tokens itself from a table in `src/pricing.mjs`, normalising ids like
|
|
428
|
+
`us.anthropic.claude-sonnet-4-20250514-v1:0` down to the model family, counting cache reads at a tenth of
|
|
429
|
+
input and five-minute writes at a quarter more. Anything it prices is labelled an estimate; anything it
|
|
430
|
+
can't price says so by name instead of quietly showing zero, and the header shows how many sessions are
|
|
431
|
+
unpriced.
|
|
432
|
+
|
|
433
|
+
Prices go stale and Bedrock/Vertex vary by region, so treat the table as a floor and override it:
|
|
534
434
|
|
|
435
|
+
```json
|
|
436
|
+
{ "pricing": { "claude-opus-5": { "input": 15, "output": 75 } } }
|
|
535
437
|
```
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
438
|
+
|
|
439
|
+
</details>
|
|
440
|
+
|
|
441
|
+
---
|
|
442
|
+
|
|
443
|
+
## 🌳 Working with your repos
|
|
444
|
+
|
|
445
|
+
### Worktrees
|
|
446
|
+
|
|
447
|
+
Every workstream gets its own git worktree (under `~/worktrees/<repo>/<branch>` unless you set
|
|
448
|
+
`BOT_FARM_WORKTREE_ROOT`), so parallel agents never fight over one checkout. Paths are expanded the way a
|
|
449
|
+
shell would — `~/workspace/app`, `$HOME/app` and relative paths all work — and dialogs tell you up front if a
|
|
450
|
+
path is missing or isn't a repository.
|
|
451
|
+
|
|
452
|
+
**Parent repos with nested services** (services cloned into a gitignored folder) get one worktree per repo,
|
|
453
|
+
laid out exactly like the original tree, each on its own branch. The dialog lists the nested repos it found
|
|
454
|
+
and you tick the ones in scope. Changed files and diffs aggregate across all of them, tagged by repo.
|
|
455
|
+
|
|
456
|
+
### Repos from anywhere
|
|
457
|
+
|
|
458
|
+
A workstream can include repos from outside the workspace. **Add repo** in the new-workstream dialog
|
|
459
|
+
(e.g. `~/workspace/spt`) links it to the workspace; each workstream it's ticked for gets a worktree of it on
|
|
460
|
+
the same branch, mounted at `<worktree>/spt/`. **+ Repo** adds one to a running workstream, and the bots are
|
|
461
|
+
told in the chat, team notes and every card.
|
|
462
|
+
|
|
463
|
+
<details>
|
|
464
|
+
<summary>API & Docker notes</summary>
|
|
465
|
+
|
|
466
|
+
- `POST /api/workspaces/:id/repos {path, as?, running?}`
|
|
467
|
+
- `DELETE /api/workspaces/:id/repos?path=`
|
|
468
|
+
- `POST /api/projects/:id/repos {path, as?}`
|
|
469
|
+
|
|
470
|
+
Linked repos are hidden from the parent's git status via `info/exclude`. Docker MCPs see them only if the
|
|
471
|
+
path is under a mounted folder (`~/workspace`, `~/worktrees`).
|
|
472
|
+
|
|
473
|
+
</details>
|
|
474
|
+
|
|
475
|
+
### Apply to my repos
|
|
476
|
+
|
|
477
|
+
From the harvest (**Apply to my repos…**) or any time after (**Apply to repos…** in the workstream header),
|
|
478
|
+
every repo in the workstream becomes a patch of what its worktree has that your checkout doesn't — edits,
|
|
479
|
+
new files, commits on the branch — leaving out BotFarm's own files.
|
|
480
|
+
|
|
481
|
+
- The dialog shows per repo where it goes (path and current branch), the files, and whether it applies
|
|
482
|
+
cleanly, needs a 3-way merge, will conflict, or is already there. It warns about your own uncommitted edits
|
|
483
|
+
to the same files.
|
|
484
|
+
- A clean patch lands as **unstaged working-tree changes** — never committed for you. Otherwise it falls back
|
|
485
|
+
to a 3-way apply (conflict markers), then `--reject` (`.rej` files).
|
|
486
|
+
- **Undo last apply** reverses the clean ones.
|
|
487
|
+
- Patches are kept in `.botfarm/workstreams/<id>.patches/` and can be downloaded.
|
|
488
|
+
|
|
489
|
+
<details>
|
|
490
|
+
<summary>Patches API</summary>
|
|
491
|
+
|
|
492
|
+
- `GET /api/projects/:id/patches`
|
|
493
|
+
- `GET /api/projects/:id/patches/file?rel=`
|
|
494
|
+
- `POST /api/projects/:id/patches/apply {repos?}`
|
|
495
|
+
- `POST /api/projects/:id/patches/undo`
|
|
496
|
+
|
|
497
|
+
</details>
|
|
498
|
+
|
|
499
|
+
### When a workstream finishes
|
|
500
|
+
|
|
501
|
+
- A **PR packet** (`.botfarm/workstreams/<id>.pr.md`) — criteria with the tests behind them, files, commits,
|
|
502
|
+
how to test, open questions and cost. **Open PR…** pushes and runs `gh pr create`.
|
|
503
|
+
- A **harvest** — a small celebration and a line in the home page's harvest log.
|
|
504
|
+
- **XP** for the bots.
|
|
505
|
+
|
|
506
|
+
---
|
|
507
|
+
|
|
508
|
+
## 🙋 Needs you: questions & sign-offs
|
|
509
|
+
|
|
510
|
+
- **Bots can ask you things.** `botfarm_ask_human(question, options, wait_seconds)` puts a question at the top
|
|
511
|
+
of your board with the asker's face on it. Questions can offer choices (pick one or several) and can always
|
|
512
|
+
be answered in your own words. The answer goes straight back into that session, marked as coming from you.
|
|
513
|
+
By default the bot doesn't block — it carries on and the answer arrives as a message.
|
|
514
|
+
- **Stages can be yours.** `human: true` on a stage — like the sign-off at the end of the `story` pipeline —
|
|
515
|
+
lands it in *Needs you* and the run waits.
|
|
516
|
+
- **Open questions travel.** A stage with open questions still hands off; the questions go to the next stage
|
|
517
|
+
with its card. Set `hold_on_questions: true` (or "wait for me" in the editor) to stop there until you press
|
|
518
|
+
**Continue**, optionally with answers every later stage receives.
|
|
519
|
+
|
|
520
|
+
---
|
|
521
|
+
|
|
522
|
+
## 🎮 Fun stuff: XP, levels & replays
|
|
523
|
+
|
|
524
|
+
- **Levels.** Each kind of bot earns XP per workspace for clean work only — a handoff, no pauses, under budget,
|
|
525
|
+
nothing left open. Fix rounds earn little XP; the checker gets a little for catching them. Levels bring hats on
|
|
526
|
+
the farm 🎩 and small perks: level 3 = +10% card limits, level 5 = the first limit hit extends itself once,
|
|
527
|
+
level 8 = +20%.
|
|
528
|
+
- **Replays.** **Replay** on a workstream scrubs through everything that happened (farm, team, chat).
|
|
529
|
+
**Save replay file** gives one portable JSON you can open from the home page of any BotFarm.
|
|
530
|
+
- **Handles & avatars.** Every bot gets a handle like `@prying-heron` and an avatar that *is* the creature in the
|
|
531
|
+
handle. The adjective carries the job — testers are *prying, nervous, squinting*; builders are *stacking,
|
|
532
|
+
welding, polishing*; reviewers are *tallying, auditing* — so you can tell who's who at a glance. A persona can
|
|
533
|
+
supply its own `adjectives:` list.
|
|
534
|
+
|
|
535
|
+
---
|
|
536
|
+
|
|
537
|
+
## 🕸️ The mesh: bots talking to bots
|
|
538
|
+
|
|
539
|
+
Sessions are isolated by default. **Nothing is shared until you turn it on, per session.**
|
|
540
|
+
|
|
541
|
+
| Setting | Effect |
|
|
542
|
+
| --- | --- |
|
|
543
|
+
| `Off` (default) | Invisible to other sessions and unreachable by them. |
|
|
544
|
+
| `Receive only` | Appears in other sessions' rosters and can be messaged; can't initiate. |
|
|
545
|
+
| `Send and receive` | Full participant. |
|
|
546
|
+
| `Can open sessions` | May create new sessions for unrelated topics. Off by default. |
|
|
547
|
+
|
|
548
|
+
**Tools a bot sees:** `botfarm_whoami`, `botfarm_roster`, `botfarm_send`, `botfarm_ask`, `botfarm_reply`,
|
|
549
|
+
`botfarm_inbox`, `botfarm_spawn`, `botfarm_notify`. Tools you've switched off aren't listed at all.
|
|
550
|
+
|
|
551
|
+
`spawn` is for "I found a defect that doesn't belong in this conversation":
|
|
552
|
+
|
|
553
|
+
```
|
|
554
|
+
botfarm_spawn(title: "flaky date test",
|
|
555
|
+
task: "tests/date.spec.ts fails on the first of the month. Fix it.",
|
|
556
|
+
branch: "fix/flaky-date")
|
|
543
557
|
```
|
|
544
558
|
|
|
545
|
-
|
|
559
|
+
A new pod appears on the board with its own worktree, marked *opened by @velvet-shrew*. The child starts
|
|
560
|
+
empty, so the tool refuses a spawn without a brief that stands on its own.
|
|
561
|
+
|
|
562
|
+
### Group chats (rooms)
|
|
563
|
+
|
|
564
|
+
Work that spans several sessions — a migration, a contract between two services — gets a room. **You're a
|
|
565
|
+
full member**, so one line in `#auth-refactor` reaches every agent in it.
|
|
566
|
+
|
|
567
|
+
Because every delivery costs real input tokens, rooms don't broadcast by default:
|
|
568
|
+
|
|
569
|
+
| | What happens |
|
|
570
|
+
| --- | --- |
|
|
571
|
+
| You're `@mentioned` | Delivered straight into your next step, with any backlog riding along. |
|
|
572
|
+
| You're not mentioned | Queued — one batched digest the moment you go idle, never mid-task. |
|
|
573
|
+
| The operator posts | Always delivered to everyone. |
|
|
574
|
+
| Queue reaches six | Delivered anyway, so nobody works on stale information. |
|
|
575
|
+
|
|
576
|
+
Room tools: `botfarm_rooms`, `botfarm_room_read`, `botfarm_room_post`, `botfarm_room_create`,
|
|
577
|
+
`botfarm_room_invite`, `botfarm_room_leave` — gated by a rooms policy of `off` / `member` / `create`.
|
|
578
|
+
|
|
579
|
+
### Guardrails
|
|
580
|
+
|
|
581
|
+
| Risk | Guard |
|
|
582
|
+
| --- | --- |
|
|
583
|
+
| **Smuggled instructions** | Peer messages arrive framed with their provenance and marked as untrusted input from another agent, not the operator. |
|
|
584
|
+
| **Ping-pong** | After six exchanges with no operator input, the channel pauses. Typing into either session resets it. |
|
|
585
|
+
| **Fork bombs** | Delegation capped at depth 2, three children per session, eight agent-created sessions per hour; spawned sessions can't spawn. |
|
|
586
|
+
| **Volume** | 24 messages per ordered pair per hour; 40 messages per room per hour, 8 members max. |
|
|
587
|
+
| **Runaway rooms** | Twelve agent posts with no operator input mutes the room. Posting as the operator resets it. |
|
|
588
|
+
|
|
589
|
+
Everything that crosses between sessions is logged to the activity feed and both mailboxes — the mesh never
|
|
590
|
+
does something you can't see afterwards.
|
|
591
|
+
|
|
592
|
+
<details>
|
|
593
|
+
<summary>How a session gets the mesh tools</summary>
|
|
594
|
+
|
|
595
|
+
BotFarm exposes an MCP server at `/mcp/<token>`, with **a distinct token per session**. That token is the whole
|
|
596
|
+
authentication story: a tool call arrives already bound to one caller, so an agent can't claim to be a peer.
|
|
597
|
+
Enabling the mesh registers the server with opencode at runtime and writes it into
|
|
598
|
+
`<worktree>/.opencode/opencode.json`:
|
|
546
599
|
|
|
547
600
|
```json
|
|
548
|
-
{
|
|
601
|
+
{
|
|
602
|
+
"mcp": {
|
|
603
|
+
"botfarm": { "type": "remote", "url": "http://127.0.0.1:4777/mcp/<token>", "enabled": true }
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
Runtime registration lets an open session pick the tools up immediately; the project config is the reliable
|
|
609
|
+
fallback (restart that session). `.opencode/` is added to `.git/info/exclude`, so flipping a toggle never dirties
|
|
610
|
+
the branch.
|
|
611
|
+
|
|
612
|
+
</details>
|
|
613
|
+
|
|
614
|
+
---
|
|
615
|
+
|
|
616
|
+
## 🧩 Extending BotFarm
|
|
617
|
+
|
|
618
|
+
### Your own tools
|
|
619
|
+
|
|
620
|
+
Every `.js` file in `botfarm/mcps/` becomes a tool called `botfarm_<name>` for the workspace's bots:
|
|
621
|
+
|
|
622
|
+
```js
|
|
623
|
+
export const name = "worktree_status"
|
|
624
|
+
export const description = "Show git status for the calling bot's worktree"
|
|
625
|
+
export const inputSchema = { type: "object", properties: {} }
|
|
626
|
+
|
|
627
|
+
export async function execute(args, ctx) {
|
|
628
|
+
// ctx.repoRoot – the calling bot's worktree
|
|
629
|
+
// ctx.exec(cmd, args) – run a program there
|
|
630
|
+
// ctx.log(...) – write to the test bench
|
|
631
|
+
return ctx.exec("git", ["status", "--short"])
|
|
632
|
+
}
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
Saving a file reloads it; bots get the new list on their next idle moment. **Tools** in the sidebar is a test
|
|
636
|
+
bench: a form from the schema (or raw JSON), where to run it, the result and log, and the source to edit.
|
|
637
|
+
|
|
638
|
+
### BotFarm's MCP server
|
|
639
|
+
|
|
640
|
+
It's called `botfarm`, so bots see `botfarm_task_complete`, `botfarm_room_post`, `botfarm_notes_read` and so
|
|
641
|
+
on. Old worktrees are renamed automatically.
|
|
642
|
+
|
|
643
|
+
### Per-session tools
|
|
644
|
+
|
|
645
|
+
A bot's panel lists the MCP servers attached to its worktree and the built-in tools, each with a switch — the
|
|
646
|
+
`/mcp` picker, on the board. MCP servers connect and disconnect live; built-in tool switches are written to the
|
|
647
|
+
worktree's `.opencode/opencode.json` and take effect when the session restarts.
|
|
648
|
+
|
|
649
|
+
---
|
|
650
|
+
|
|
651
|
+
## ⌨️ CLI
|
|
652
|
+
|
|
653
|
+
```
|
|
654
|
+
bot-farm up [--port 4777] [--server URL] [--repo PATH]... dashboard (default)
|
|
655
|
+
bot-farm ls list sessions
|
|
656
|
+
bot-farm new <repo> [task] --branch NAME [--agent A] new session in a worktree
|
|
657
|
+
bot-farm send <id> <text...> queue a prompt
|
|
658
|
+
bot-farm stop [id...] interrupt (all busy if omitted)
|
|
659
|
+
bot-farm rm <id> [--worktree] [--force] delete session and its worktree
|
|
660
|
+
bot-farm attach <id> open the session in the opencode TUI
|
|
549
661
|
```
|
|
550
662
|
|
|
551
|
-
|
|
552
|
-
|
|
663
|
+
Example — spin up a single bot on a fresh branch:
|
|
664
|
+
|
|
665
|
+
```bash
|
|
666
|
+
bot-farm new ~/code/app --branch fix/auth-redirect --task "Fix the redirect loop after SSO login"
|
|
667
|
+
```
|
|
668
|
+
|
|
669
|
+
That creates the worktree, opens a session there and sends the first instruction. The card shows the branch and
|
|
670
|
+
a live `+142 −18 · 7 files`. `botfarm rm <id> --worktree` removes both.
|
|
671
|
+
|
|
672
|
+
---
|
|
673
|
+
|
|
674
|
+
## ⚙️ Configuration
|
|
553
675
|
|
|
554
|
-
|
|
676
|
+
Global config lives in `~/.botfarm/config.json`:
|
|
555
677
|
|
|
678
|
+
```json
|
|
679
|
+
{
|
|
680
|
+
"server": "http://127.0.0.1:4096",
|
|
681
|
+
"port": 4777,
|
|
682
|
+
"repos": ["~/code/app"],
|
|
683
|
+
"defaultWorkspace": "~/code/app"
|
|
684
|
+
}
|
|
556
685
|
```
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
686
|
+
|
|
687
|
+
| Path | What's there |
|
|
688
|
+
| --- | --- |
|
|
689
|
+
| `~/.botfarm/config.json` | Server, port, repos, pricing overrides, handoff & loop tuning |
|
|
690
|
+
| `~/.botfarm/registry.json` | Sessions BotFarm manages, plus groups, labels, mesh policy, lineage, disabled tools |
|
|
691
|
+
| `~/.botfarm/metrics.json` | Rolling metrics, snapshotted every 30s so a restart keeps the last hour |
|
|
692
|
+
| `~/.botfarm/botfarm.db` | Runtime state (runs, tasks, handoffs) via `node:sqlite`, or JSON on older runtimes |
|
|
693
|
+
| `<workspace>/botfarm/` | Agents, pipelines, routing and your tools |
|
|
694
|
+
| `<workspace>/.botfarm/workstreams/` | Workstream state, PR packets and patches |
|
|
695
|
+
|
|
696
|
+
| Env var | Effect |
|
|
697
|
+
| --- | --- |
|
|
698
|
+
| `BOT_FARM_WORKTREE_ROOT` | Where worktrees are created (default `~/worktrees`) |
|
|
699
|
+
| `CHROME_PATH` | Chrome binary for `test/shot.mjs` |
|
|
700
|
+
|
|
701
|
+
---
|
|
702
|
+
|
|
703
|
+
## 🔍 Under the hood
|
|
704
|
+
|
|
705
|
+
<details>
|
|
706
|
+
<summary>The board is a registry, not a listing</summary>
|
|
707
|
+
|
|
708
|
+
opencode accumulates every session you've ever opened. Listing them all turns the dashboard into an archive
|
|
709
|
+
browser. BotFarm shows only sessions it manages — ones it started, plus ones you explicitly add with
|
|
710
|
+
**Add existing…**. The rest sit behind a one-line banner. `Remove` puts a session back in that pool; it never
|
|
711
|
+
deletes anything.
|
|
712
|
+
|
|
713
|
+
</details>
|
|
714
|
+
|
|
715
|
+
<details>
|
|
716
|
+
<summary>Transport</summary>
|
|
717
|
+
|
|
718
|
+
The dashboard is pushed to over a websocket (`/api/socket`), falling back to server-sent events. Neither polls.
|
|
719
|
+
Actions stay on plain HTTP — they're one-shot and want status codes and retries.
|
|
720
|
+
|
|
721
|
+
BotFarm's own traffic to opencode is event-driven too. Periodic checks are reconciliation, not the mechanism:
|
|
722
|
+
a board with nothing happening makes **zero** requests per second, and if the event stream goes down the polling
|
|
723
|
+
loop comes back automatically.
|
|
724
|
+
|
|
725
|
+
Request bodies are negotiated rather than pinned: opencode's prompt endpoint moved from `{parts: [...]}` to
|
|
726
|
+
`{prompt: {text}}`, so BotFarm tries the known shapes and remembers the one that worked. Metrics come from each
|
|
727
|
+
session's message list rather than event payloads, and the client sniffs the v1/v2 path dialect at connect time,
|
|
728
|
+
so a rename of `/session` to `/api/session` doesn't break it.
|
|
729
|
+
|
|
730
|
+
</details>
|
|
731
|
+
|
|
732
|
+
<details>
|
|
733
|
+
<summary>Config is YAML, state is a database</summary>
|
|
734
|
+
|
|
735
|
+
Definitions people edit and share in git are YAML; runtime state goes in `~/.botfarm/botfarm.db`. The split is by
|
|
736
|
+
kind, so there's never a question of which copy is authoritative. A finished run exports back to pipeline YAML for
|
|
737
|
+
sharing.
|
|
738
|
+
|
|
739
|
+
The YAML parser is a deliberate subset — maps, lists, scalars and `|` block scalars — and throws with a line number
|
|
740
|
+
on anything else rather than misreading it.
|
|
741
|
+
|
|
742
|
+
</details>
|
|
743
|
+
|
|
744
|
+
<details>
|
|
745
|
+
<summary>Colour & accessibility</summary>
|
|
746
|
+
|
|
747
|
+
Status is carried by amber, violet and blue — never red against green, the one pair a red-green colour-blind
|
|
748
|
+
operator can't separate. Red appears only for failure. Diffs use `+`/`-` prefixes with blue and amber, toggles say
|
|
749
|
+
"on"/"off" beside the switch, and every status is spelled out in words next to its colour.
|
|
750
|
+
|
|
751
|
+
</details>
|
|
752
|
+
|
|
753
|
+
---
|
|
754
|
+
|
|
755
|
+
## 🧪 Development & tests
|
|
756
|
+
|
|
757
|
+
No build step, no dependencies. The tests run against `test/mock-opencode.mjs`, a fake server that streams
|
|
758
|
+
plausible sessions, so you can develop without burning tokens.
|
|
759
|
+
|
|
760
|
+
```bash
|
|
761
|
+
node test/mock-opencode.mjs # fake opencode server
|
|
762
|
+
botfarm up # a populated board to play with
|
|
570
763
|
```
|
|
571
764
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
765
|
+
| Test | Covers |
|
|
766
|
+
| --- | --- |
|
|
767
|
+
| `node test/smoke.mjs` | Discovery, metrics, abort, continue, SSE, persistence |
|
|
768
|
+
| `node test/board.mjs` | Registry, adoption, groups, cost estimation, tool toggles |
|
|
769
|
+
| `node test/repos.mjs` | Path expansion, nested repositories, multi-repo worktrees |
|
|
770
|
+
| `node test/transport.mjs` | Websocket handshake, pushes, idle traffic to opencode |
|
|
771
|
+
| `node test/roles.mjs` | Role-flavoured handles, role and human ownership, editing definitions |
|
|
772
|
+
| `node test/flow.mjs` | Late briefing, held mentions, handoff watchdog, shared worktrees, live transcripts |
|
|
773
|
+
| `node test/workspaces.mjs` | Workspace files, one workstream per run, archive, folder browser |
|
|
774
|
+
| `node test/handoff.mjs` | Open questions in handoffs, holding stages, continue with answers |
|
|
775
|
+
| `node test/parallel.mjs` | Split stages, `after:` (TDD), time/token/$ limits |
|
|
776
|
+
| `node test/loop.mjs` | Send-back fix loops |
|
|
777
|
+
| `node test/craft.mjs` | Routing, trimmed handoffs, notes, loop detection, XP, harvests, PR packets, replays |
|
|
778
|
+
| `node test/extend.mjs` | Per-file definitions, workspace tools, switching a bot's model |
|
|
779
|
+
| `node test/linked.mjs` | Repos from anywhere in a workstream |
|
|
780
|
+
| `node test/patches.mjs` | Applying the work as patches to real checkouts |
|
|
781
|
+
| `node test/shot.mjs` | Renders the dashboard in headless Chrome (needs `CHROME_PATH`) |
|
|
782
|
+
|
|
783
|
+
---
|
|
784
|
+
|
|
785
|
+
## ⚠️ Known limits
|
|
786
|
+
|
|
787
|
+
- **Identity is per location.** The MCP token is registered against a directory, so two sessions sharing one
|
|
788
|
+
directory share a token. Pods with their own worktrees — the intended setup — are cleanly separated.
|
|
789
|
+
- **Tested mostly against a mock.** Usage fields are checked in several shapes, but a new opencode version may
|
|
790
|
+
need a tweak.
|
|
791
|
+
- `botfarm attach` assumes `opencode --session <id>` is right for your opencode version.
|
|
792
|
+
- Cost is only as good as what the provider reports back.
|
|
793
|
+
|
|
794
|
+
---
|
|
795
|
+
|
|
796
|
+
<div align="center">
|
|
797
|
+
|
|
798
|
+
Made with 🌱 for people who'd rather watch the crops grow than babysit a terminal.
|
|
575
799
|
|
|
576
|
-
|
|
800
|
+
**[MIT License](LICENSE)** · Provided as is. Supervise your bots. Not responsible for goats. 🐐
|
|
577
801
|
|
|
578
|
-
|
|
579
|
-
sharing one directory share a token; botfarm resolves the caller to whichever of them is running.
|
|
580
|
-
Pods with their own worktrees — the intended setup — are cleanly separated.
|
|
581
|
-
- **Tested against a mock**, not yet against a live opencode server. Field names in the usage object
|
|
582
|
-
are checked in several shapes before giving up, but the first real run may need a tweak.
|
|
583
|
-
- `botfarm attach` assumes `opencode --session <id>` is the right incantation for your version.
|
|
584
|
-
- Cost is only as good as what the provider reports back in the message.
|
|
802
|
+
</div>
|