huddle-cli 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +357 -2
- package/dist/adapters/amp.js +40 -0
- package/dist/adapters/antigravity.js +52 -0
- package/dist/adapters/claude.js +70 -0
- package/dist/adapters/codex.js +59 -0
- package/dist/adapters/copilot.js +69 -0
- package/dist/adapters/cursor.js +85 -0
- package/dist/adapters/custom.js +340 -0
- package/dist/adapters/droid.js +57 -0
- package/dist/adapters/gemini.js +61 -0
- package/dist/adapters/goose.js +55 -0
- package/dist/adapters/index.js +135 -0
- package/dist/adapters/kiro.js +41 -0
- package/dist/adapters/opencode.js +63 -0
- package/dist/adapters/qwen.js +67 -0
- package/dist/adapters/types.js +6 -0
- package/dist/adapters/usage.js +141 -0
- package/dist/attach/connect.js +409 -0
- package/dist/attach/guard.js +64 -0
- package/dist/attach/live.js +222 -0
- package/dist/attach/plug.js +174 -0
- package/dist/attach/session.js +172 -0
- package/dist/cli.js +176 -0
- package/dist/mcp/server.js +240 -0
- package/dist/orchestrator/ask.js +51 -0
- package/dist/orchestrator/brain.js +164 -0
- package/dist/orchestrator/council.js +274 -0
- package/dist/orchestrator/handoff.js +117 -0
- package/dist/orchestrator/launcher.js +155 -0
- package/dist/orchestrator/planner.js +194 -0
- package/dist/orchestrator/qa.js +350 -0
- package/dist/orchestrator/scheduler.js +748 -0
- package/dist/orchestrator/takeover.js +85 -0
- package/dist/preflight.js +31 -0
- package/dist/relay/server.js +113 -0
- package/dist/room/db.js +617 -0
- package/dist/room/identity.js +122 -0
- package/dist/room/live.js +293 -0
- package/dist/room/progress.js +110 -0
- package/dist/room/relaylink.js +85 -0
- package/dist/room/server.js +752 -0
- package/dist/room/sqlite.js +156 -0
- package/dist/room/transport.js +107 -0
- package/dist/room/tunnel.js +85 -0
- package/dist/sandbox/sync.js +104 -0
- package/dist/sandbox/worktree.js +261 -0
- package/dist/types.js +4 -0
- package/dist/ui/app.js +1534 -0
- package/dist/ui/catalog.js +75 -0
- package/dist/ui/dashboard.js +192 -0
- package/dist/ui/doctor.js +54 -0
- package/dist/ui/logo.js +40 -0
- package/dist/ui/remote.js +332 -0
- package/dist/ui/story.js +932 -0
- package/dist/ui/term.js +114 -0
- package/dist/ui/watch.js +174 -0
- package/install.sh +44 -0
- package/package.json +60 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Preet37
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,358 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# 🏈 Huddle
|
|
4
|
+
|
|
5
|
+
### The multiplayer layer for AI coding agents.
|
|
6
|
+
|
|
7
|
+
**One command. Every agent. One repo. Zero collisions.**
|
|
8
|
+
|
|
9
|
+
`npm install -g huddle-cli` → `huddle`
|
|
10
|
+
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Get started (any computer)
|
|
16
|
+
|
|
17
|
+
You need **Node.js 20+** and **git**. Then:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install -g huddle-cli # puts `huddle` on your PATH
|
|
21
|
+
huddle doctor # checks node, git, and which agents you have
|
|
22
|
+
|
|
23
|
+
cd ~/some-git-repo
|
|
24
|
+
huddle # welcome → connects your agents → huddle ›
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Prefer a script? `install.sh` checks Node, npm, git and a root-owned npm folder first, then runs the same `npm install -g` (works once the GitHub repo is public):
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
curl -fsSL https://raw.githubusercontent.com/Preet37/Huddle/main/install.sh | sh
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Agents.** Huddle drives the coding-agent CLIs you already use — Claude Code (`claude`), Codex (`codex`), Antigravity (`agy`), Cursor (`cursor-agent`), and Gemini CLI if it's installed. You need at least one. `huddle` offers to install any that are missing and to sign you in, **always asking first**; `huddle doctor` prints the exact install commands. Each agent signs in with its own account and bills to it — Huddle never sees or stores a credential.
|
|
34
|
+
|
|
35
|
+
**Not a git repo?** `huddle` offers to set one up (git init + a first commit). One-shot `huddle join "<goal>"` won't touch a non-repo folder.
|
|
36
|
+
|
|
37
|
+
**Updating:** `npm install -g huddle-cli` again · **Version:** `huddle --version` · **Remove:** `npm uninstall -g huddle-cli` (your repos keep their work; delete a repo's `.huddle/` folder to clear its board).
|
|
38
|
+
|
|
39
|
+
<details>
|
|
40
|
+
<summary>Troubleshooting a fresh install</summary>
|
|
41
|
+
|
|
42
|
+
- **`EACCES` from npm** — your Node's global folder is root-owned. Use a version manager ([nvm](https://github.com/nvm-sh/nvm)), or `npm config set prefix ~/.npm-global` and add `~/.npm-global/bin` to `PATH`. Don't `sudo npm`.
|
|
43
|
+
- **"couldn't open its room database (SQLite)"** — the room db uses `better-sqlite3`, a native module that normally downloads a prebuilt binary. If it couldn't (offline, unusual platform, Node upgraded after install), reinstall with `npm install -g huddle-cli`. On **Node 22.13+** Huddle falls back to Node's built-in SQLite automatically, so nothing needs building.
|
|
44
|
+
- **`huddle needs Node.js 20 or newer`** — update Node (`nvm install 22`), then reinstall.
|
|
45
|
+
- **An agent shows "not signed in"** — run the command `huddle doctor` prints next to it (e.g. `codex login`), then `huddle doctor` again.
|
|
46
|
+
- Anything else: `HUDDLE_DEBUG=1 huddle …` prints the full stack.
|
|
47
|
+
|
|
48
|
+
</details>
|
|
49
|
+
|
|
50
|
+
### From source (contributors)
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
git clone https://github.com/Preet37/Huddle.git && cd Huddle
|
|
54
|
+
npm install && npm link # builds dist/ and links `huddle`
|
|
55
|
+
npm test # no tokens spent — mock agents (test/README.md)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Using it
|
|
59
|
+
|
|
60
|
+
At the `huddle ›` prompt, type what you want built. Huddle plans it into parallel tasks, runs your agents at the same time (each in its own sandbox), merges finished work when tests pass, and ends with a "what you got" report. Ask a question (`where are we with this project?`) and it answers read-only instead.
|
|
61
|
+
|
|
62
|
+
- **shift+tab** switches mode: `manual` (approve the plan and each merge) → `auto` → `plan` (dry run)
|
|
63
|
+
- **`approvals safe|auto|yolo`** sets what agents may do without asking
|
|
64
|
+
- keep typing while agents work: `board`, questions, and new goals (queued) all work mid-run
|
|
65
|
+
- `cd`, `!cmd`, `clear` behave like a shell; not a git repo? Huddle offers to set it up
|
|
66
|
+
|
|
67
|
+
**Running claude, codex and cursor yourself, in one repo at once (attach mode):**
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
huddle connect # shows the project config files it will write per agent, asks, then wires each one in
|
|
71
|
+
# (git-excluded, backed up, never your global configs; `huddle disconnect` undoes it)
|
|
72
|
+
claude # open your agents as usual, in as many terminals as you like
|
|
73
|
+
huddle peers # who's live, what each is doing, which files each one holds
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Each session checks in before editing, claims the files it's about to change, and gets warned (Claude: asked through its own permission prompt) when another session holds them. Sessions can leave each other notes. Want each session on its own branch instead? `huddle attach codex` opens it in a worktree, and `huddle land` tests and merges it. Live-verified for Claude and Codex; Cursor checks in but hasn't finished a live attach run yet (see STATUS).
|
|
77
|
+
|
|
78
|
+
**With a team:** `invite @harsh` in your Huddle; Harsh installs Huddle the same way and runs the `huddle join …` line it prints, from his clone of the repo. His agents take tasks off your board, work on his machine with his accounts, and push branches you test, review and merge. Different networks? `huddle relay` somewhere and `relay <url>`.
|
|
79
|
+
|
|
80
|
+
What's real today vs. designed: **[docs/STATUS.md](docs/STATUS.md)**.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## The one-sentence pitch
|
|
85
|
+
|
|
86
|
+
You already pay for Claude Code, Codex, Gemini CLI, and Cursor — but you run them **one at a time**, because they can't see each other and would trash the same repo. **Huddle puts all of them in the same room**: they split the work, each codes in its own invisible sandbox, they watch each other's progress live, and nothing merges until the tests pass. You stop waiting. You stop babysitting. You never touch git.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## The problem (in your words)
|
|
91
|
+
|
|
92
|
+
> "I have Claude, I have Codex, I have Gemini, I have Cursor. I tend not to use Gemini or Cursor if Claude is running, and I try not to use Codex when Claude is running because it's doing something. I don't know what Cursor is going to do versus Codex versus Claude."
|
|
93
|
+
|
|
94
|
+
That's the whole problem in one paragraph. It isn't a *team* problem. It's **one person with four agents who are blind to each other.** So you serialize them by hand — which means you're paying for four engines and running one.
|
|
95
|
+
|
|
96
|
+
The deeper version, for teams:
|
|
97
|
+
|
|
98
|
+
> "I'm person A working on one portion. Person B is on another. I don't know what B is doing until B pushes to git. I could be done and just *waiting*."
|
|
99
|
+
|
|
100
|
+
Both are the **same coordination problem**: no shared awareness, no shared plan, no shared memory. Git only tells you what happened *after* it happened.
|
|
101
|
+
|
|
102
|
+
```mermaid
|
|
103
|
+
%%{init: {'theme':'dark'}}%%
|
|
104
|
+
flowchart LR
|
|
105
|
+
subgraph today["❌ Today — blind & serialized"]
|
|
106
|
+
direction TB
|
|
107
|
+
You1["👤 You"] -->|"run one,<br/>wait, run next"| C1["Claude"]
|
|
108
|
+
C1 -.->|"finishes"| X1["Codex"]
|
|
109
|
+
X1 -.->|"finishes"| G1["Gemini"]
|
|
110
|
+
G1 -.->|"finishes"| U1["Cursor"]
|
|
111
|
+
note1["Agents can't see each other.<br/>You are the message bus.<br/>3 engines idle at all times."]
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
subgraph huddle["✅ With Huddle — parallel & aware"]
|
|
115
|
+
direction TB
|
|
116
|
+
Room(("🏈 Huddle<br/>Room"))
|
|
117
|
+
Ca["Claude"] <--> Room
|
|
118
|
+
Xa["Codex"] <--> Room
|
|
119
|
+
Ga["Gemini"] <--> Room
|
|
120
|
+
Ua["Cursor"] <--> Room
|
|
121
|
+
note2["Shared plan · shared memory · live progress.<br/>All engines running. No collisions."]
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
today ==> huddle
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## What Huddle actually is
|
|
130
|
+
|
|
131
|
+
Huddle is **not** a new editor, and **not** a new agent. It's a thin neutral layer that sits *above* the agents you already use. Three moving parts:
|
|
132
|
+
|
|
133
|
+
```mermaid
|
|
134
|
+
%%{init: {'theme':'dark'}}%%
|
|
135
|
+
flowchart TB
|
|
136
|
+
subgraph laptop["Your laptop"]
|
|
137
|
+
CLI["🧰 huddle CLI<br/><i>the daemon</i>"]
|
|
138
|
+
Room[("🏈 The Room<br/>SQLite + WebSocket<br/><i>tasks · progress · shared memory</i>")]
|
|
139
|
+
subgraph agents["Your agents — unchanged"]
|
|
140
|
+
A1["Claude Code"]
|
|
141
|
+
A2["Codex"]
|
|
142
|
+
A3["Gemini CLI"]
|
|
143
|
+
A4["Cursor Agent"]
|
|
144
|
+
end
|
|
145
|
+
MCP["🔌 The Plug<br/><i>one MCP server</i>"]
|
|
146
|
+
subgraph boxes["Isolated sandboxes (git worktrees)"]
|
|
147
|
+
W1["worktree/claude"]
|
|
148
|
+
W2["worktree/codex"]
|
|
149
|
+
W3["worktree/gemini"]
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
CLI --> Room
|
|
154
|
+
CLI --> MCP
|
|
155
|
+
A1 & A2 & A3 & A4 -->|"speak MCP"| MCP
|
|
156
|
+
MCP <--> Room
|
|
157
|
+
CLI -->|"spawns each agent in"| boxes
|
|
158
|
+
A1 --> W1
|
|
159
|
+
A2 --> W2
|
|
160
|
+
A3 --> W3
|
|
161
|
+
boxes -->|"tests pass → merge"| main["🌳 main branch"]
|
|
162
|
+
|
|
163
|
+
style Room fill:#3b1f6e,stroke:#a78bfa,color:#fff
|
|
164
|
+
style MCP fill:#1e3a5f,stroke:#60a5fa,color:#fff
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**1. The Room** — a tiny local server + a single-file SQLite database. It holds the task board, who claimed what, live progress, and *shared memory* (decisions + the "why" behind them). Think group chat + whiteboard + kanban, but for agents.
|
|
168
|
+
|
|
169
|
+
**2. The Plug** — one [MCP](https://modelcontextprotocol.io) server. MCP is the "USB-C for AI tools": Claude, Codex, Gemini, and Cursor **all already speak it**. We build the plug once, and every agent instantly gains new verbs: *see the board, claim a task, announce a plan, post progress, read shared memory, check what everyone else is doing, mark done.* This is why Huddle works **without switching tools**.
|
|
170
|
+
|
|
171
|
+
**3. The Sandboxes** — when an agent claims a task, Huddle silently gives it its own [git worktree](https://git-scm.com/docs/git-worktree): a separate working copy of the repo. Agents physically cannot corrupt each other's files. Git is the plumbing; **you never see it**. Work only rejoins `main` when tests are green.
|
|
172
|
+
|
|
173
|
+
> **The reframe that matters:** we don't kill git — we *hide* it. "Git is dead" is a claim engineers argue with. **"You never have to touch git again"** is a claim they pay for.
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## The magic loop
|
|
178
|
+
|
|
179
|
+
```mermaid
|
|
180
|
+
%%{init: {'theme':'dark'}}%%
|
|
181
|
+
sequenceDiagram
|
|
182
|
+
autonumber
|
|
183
|
+
participant You
|
|
184
|
+
participant Orch as 🧠 Orchestrator<br/>(Opus 5.5)
|
|
185
|
+
participant Room as 🏈 Room
|
|
186
|
+
participant Claude
|
|
187
|
+
participant Codex
|
|
188
|
+
participant Gemini
|
|
189
|
+
|
|
190
|
+
You->>Orch: "Build the checkout flow"
|
|
191
|
+
Orch->>Orch: decompose into tasks +<br/>route by strength
|
|
192
|
+
Orch->>Room: post task board
|
|
193
|
+
par Everyone reads the same board
|
|
194
|
+
Claude->>Room: claim "payment UI" (best at frontend)
|
|
195
|
+
Codex->>Room: claim "Stripe integration"
|
|
196
|
+
Gemini->>Room: claim "order-confirmation email"
|
|
197
|
+
end
|
|
198
|
+
Note over Claude,Gemini: each works in its own sandbox
|
|
199
|
+
Claude->>Room: announce plan → "renaming Cart type"
|
|
200
|
+
Codex->>Room: reads memory → "Cart type is changing, I'll wait / adapt"
|
|
201
|
+
Note over Room: ⚠️ collision predicted BEFORE it happens
|
|
202
|
+
Codex->>Room: progress 40% · 60% · done ✅
|
|
203
|
+
Codex->>Room: tests green → merged to main
|
|
204
|
+
Claude->>Room: I'm free — what's backlogged?
|
|
205
|
+
Room-->>Claude: steal "email template" (Gemini is slow)
|
|
206
|
+
Note over You,Gemini: You watch one dashboard.<br/>No engine sits idle.
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Five behaviors fall out of this loop — the ones you called for:
|
|
210
|
+
|
|
211
|
+
| Behavior | What it does | Where it lives |
|
|
212
|
+
|---|---|---|
|
|
213
|
+
| **Talk it out first** | Agents announce plans before coding: "I'll take auth, you take the DB." | Room + Plug |
|
|
214
|
+
| **Live awareness** | Your agent sees a teammate's piece is 70% done and lines up the next step. No waiting on a push. | Room (WebSocket) |
|
|
215
|
+
| **Collision radar** | Two agents about to edit the same symbol? Flagged *before* a line is written. | Overlap detector |
|
|
216
|
+
| **Shared memory** | My agent knows *why* your agent renamed that function, not just *what* changed. | Room (`memory` table) |
|
|
217
|
+
| **Work-stealing** | An idle agent grabs backlogged tasks from a busy one — the bottleneck removed automatically. | Scheduler |
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## The account problem — designed for from day one
|
|
222
|
+
|
|
223
|
+
Real setups are messy: some agents are on a personal account, one comes through school or work, and subscriptions start and lapse month to month.
|
|
224
|
+
|
|
225
|
+
**Huddle never handles a single credential.** That's the core design decision, and it's what makes it survive every subscription change.
|
|
226
|
+
|
|
227
|
+
Each vendor CLI (`claude`, `codex`, `gemini`, `cursor-agent`) already logs in **its own way, to its own account** — Claude to your personal Anthropic login, Cursor to your school or work SSO, etc. Huddle treats each as a **black box it launches**, not an account it owns. It only ever asks two questions: *are you installed?* and *are you logged in?*
|
|
228
|
+
|
|
229
|
+
```mermaid
|
|
230
|
+
%%{init: {'theme':'dark'}}%%
|
|
231
|
+
flowchart TB
|
|
232
|
+
Roster["📇 roster.toml<br/><i>which agents exist,<br/>which model each maps to</i>"]
|
|
233
|
+
Doctor["🩺 huddle doctor<br/><i>runs on every join</i>"]
|
|
234
|
+
|
|
235
|
+
Roster --> Doctor
|
|
236
|
+
Doctor --> Q1{"claude<br/>installed +<br/>authed?"}
|
|
237
|
+
Doctor --> Q2{"codex?"}
|
|
238
|
+
Doctor --> Q3{"gemini?"}
|
|
239
|
+
Doctor --> Q4{"cursor-agent?"}
|
|
240
|
+
|
|
241
|
+
Q1 -->|"✅ personal acct"| Pool["🏈 Active roster<br/>for this session"]
|
|
242
|
+
Q2 -->|"✅ personal acct"| Pool
|
|
243
|
+
Q3 -->|"✅ personal acct"| Pool
|
|
244
|
+
Q4 -->|"✅ school / work SSO"| Pool
|
|
245
|
+
Q4 -.->|"❌ expired?"| Skip["gracefully skipped<br/>— session still runs"]
|
|
246
|
+
|
|
247
|
+
Pool --> Session["huddle join 🚀"]
|
|
248
|
+
|
|
249
|
+
style Roster fill:#3b1f6e,stroke:#a78bfa,color:#fff
|
|
250
|
+
style Pool fill:#14532d,stroke:#4ade80,color:#fff
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
**Why this survives all of that:**
|
|
254
|
+
|
|
255
|
+
- **Different accounts?** Each CLI carries its own login. Huddle doesn't care that one agent is on a work account and the rest are personal — it just shells out to `cursor-agent` and that binary knows who it is.
|
|
256
|
+
- **Subscription expires next month?** `huddle doctor` sees the auth check fail, drops that agent from the roster, and the session runs with whoever's left. Nothing breaks.
|
|
257
|
+
- **You get GPT/Codex back later?** Reinstall the CLI, log in, and it reappears in the roster automatically. Zero Huddle config change.
|
|
258
|
+
- **A vendor ships a new CLI or renames flags?** Only that one **adapter file** changes (~30 lines). The Room, Plug, and everything else are untouched. See [`docs/ADAPTERS.md`](docs/ADAPTERS.md).
|
|
259
|
+
|
|
260
|
+
Each agent is described by a small **adapter** — a declaration of "how do I launch you, and how do I hand you the Plug?" That's the entire vendor-specific surface area.
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## The agent architecture (Opus 5.5 orchestrator + Fable/Opus workers)
|
|
265
|
+
|
|
266
|
+
You wanted every agent to be a top-tier model — an **Opus 5.5 orchestrator** with **Fable / Opus 5.5** workers, not cheap subagents. That's the design: a strategist that never writes code, and a pool of elite executors.
|
|
267
|
+
|
|
268
|
+
```mermaid
|
|
269
|
+
%%{init: {'theme':'dark'}}%%
|
|
270
|
+
flowchart TB
|
|
271
|
+
You["👤 You: one goal"] --> Orch
|
|
272
|
+
|
|
273
|
+
subgraph brain["🧠 Orchestrator — Opus 5.5"]
|
|
274
|
+
Orch["Decompose goal → tasks<br/>Route by model strength<br/>Predict conflicts<br/>Rebalance the queue"]
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
Orch --> Board[("🏈 Task Board")]
|
|
278
|
+
|
|
279
|
+
subgraph workers["⚡ Worker pool — Opus 5.5 / Fable, routed by strength"]
|
|
280
|
+
direction LR
|
|
281
|
+
Wf["Fable<br/><i>fast execution,<br/>breadth</i>"]
|
|
282
|
+
Wo["Opus 5.5<br/><i>hard reasoning,<br/>architecture</i>"]
|
|
283
|
+
Wr["Opus 5.5 (reviewer)<br/><i>adversarial check</i>"]
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
Board --> Wf & Wo
|
|
287
|
+
Wf & Wo --> Wr
|
|
288
|
+
Wr -->|"✅ approved + tests green"| Merge["merge → main"]
|
|
289
|
+
Wr -->|"❌ found a bug"| Board
|
|
290
|
+
|
|
291
|
+
style Orch fill:#3b1f6e,stroke:#a78bfa,color:#fff
|
|
292
|
+
style Wr fill:#5b2121,stroke:#f87171,color:#fff
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
The orchestrator maps to Claude Code's native pattern (Opus 5.5 orchestrating, spawning workers), and the *same* Room lets **external** agents (Codex, Gemini, Cursor) join the exact same board as first-class workers. One brain, many hands, mixed vendors.
|
|
296
|
+
|
|
297
|
+
> **Guardrail:** agents advising each other raises quality, but two models can also talk each other *into* the same wrong answer. So **tests — not agent consensus — are the referee.** Nothing merges on vibes.
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
## Why nobody has shipped this (and why now)
|
|
302
|
+
|
|
303
|
+
- **Real-time shared coding was tried for *humans*** — VS Code Live Share, Replit multiplayer, Zed — and never became the default, because humans coordinate fine by *talking*. A daily pull request was good enough.
|
|
304
|
+
- **Agents broke that math.** One person now runs four agents that type faster than any human and are blind to each other. Coordination went from *annoying* to *the actual bottleneck* — and only in the last year, once agents got trustworthy enough to run in parallel.
|
|
305
|
+
- **The labs won't build the neutral version.** Anthropic wants you on Claude; OpenAI wants you on Codex. Neither is motivated to make its agent cooperate with a rival's. **A neutral middle layer is exactly the gap a small team can own.**
|
|
306
|
+
|
|
307
|
+
So it's not that it was tried and failed. **The problem is new.**
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
## Honest hard parts (we're not hiding these)
|
|
312
|
+
|
|
313
|
+
- **Agents forget to check the room.** Mitigation: the Plug injects "check the board before you start and after each step" into the launch prompt, and the daemon nudges idle agents.
|
|
314
|
+
- **Merges can still break** when two "separate" tasks were secretly coupled. Mitigation: the overlap detector + tests-as-referee catch most; a human approves the rest.
|
|
315
|
+
- **Shared resources outside code** — same port, same dev DB, same package version. Mitigation: the Room brokers a lease on shared resources (ports, migrations).
|
|
316
|
+
- **Cost & rate limits multiply** with four agents. Mitigation: per-agent budgets + a live spend meter; the orchestrator throttles.
|
|
317
|
+
- **Token savings are a *maybe*, not a promise.** Fewer duplicated efforts and less re-reading save tokens; checking the room and cross-advising spend them. **We pitch "less wasted work," and we'll only claim token savings once we've measured them.**
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
321
|
+
## Where this is going
|
|
322
|
+
|
|
323
|
+
```mermaid
|
|
324
|
+
%%{init: {'theme':'dark'}}%%
|
|
325
|
+
timeline
|
|
326
|
+
title Huddle roadmap
|
|
327
|
+
V0 · You + your laptop : Claude + Codex, one repo : split a task, see each other, auto-merge : (you are your own first customer)
|
|
328
|
+
V1 · Full local roster : Gemini + Cursor adapters : work-stealing + collision radar : live TUI dashboard
|
|
329
|
+
V2 · Two humans : Room moves to the cloud : your agents + your teammate's, same board : no more "waiting on a push"
|
|
330
|
+
V3 · Teams & trust : self-hosted Room for companies : replay log + budgets + approvals : the coordination layer for agentic teams
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
**V0 is the wedge**: just *you*, on one laptop, Claude and Codex splitting a task in the same repo without colliding. If that feels magical to you, adding a second human is mostly moving the Room to the cloud.
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
## Repo map
|
|
338
|
+
|
|
339
|
+
| Path | What's inside |
|
|
340
|
+
|---|---|
|
|
341
|
+
| [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) | Deep dive: the Room schema, the Plug's MCP verbs, the sandbox/merge model, data flow diagrams. |
|
|
342
|
+
| [`docs/ADAPTERS.md`](docs/ADAPTERS.md) | The account/auth strategy in full — how Huddle stays alive as your subscriptions change. |
|
|
343
|
+
| [`docs/STATUS.md`](docs/STATUS.md) | **What's built right now**, verified status per component, and the changelog. Updated with every change. |
|
|
344
|
+
| [`docs/MULTIPLAYER.md`](docs/MULTIPLAYER.md) | V2 design: crews on different laptops, the Room relay, security, invite/join/leave, no-idle agents. |
|
|
345
|
+
| [`docs/ROADMAP.md`](docs/ROADMAP.md) | Build order, V0→V3, and what's a demo vs. what's real. |
|
|
346
|
+
| `src/` | The runnable prototype — app (`src/ui`), Room, Plug, scheduler, sandboxes, adapters. |
|
|
347
|
+
| `install.sh` | One-line installer for the `huddle` CLI. |
|
|
348
|
+
| `teaser/` | The cinematic teaser page (screen-record → video). |
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
<div align="center">
|
|
353
|
+
|
|
354
|
+
**Huddle** — your agents, in the same room.
|
|
355
|
+
|
|
356
|
+
*Built as a working prototype + full design. Not vaporware.*
|
|
357
|
+
|
|
358
|
+
</div>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ampUsage } from "./usage.js";
|
|
2
|
+
// Amp (Sourcegraph) — headless via execute mode, `amp -x`. The Plug is passed
|
|
3
|
+
// inline with --mcp-config '{"huddle": {...}}', which "specif[ies] an MCP server
|
|
4
|
+
// without changing your settings file". Auth is Amp's own (`amp login`, or
|
|
5
|
+
// AMP_API_KEY).
|
|
6
|
+
//
|
|
7
|
+
// Sources (checked 2026-10):
|
|
8
|
+
// https://ampcode.com/docs/cli install script, amp login, AMP_API_KEY
|
|
9
|
+
// https://ampcode.com/docs/cli/execute-mode -x/--execute, --mcp-config '{"name": {"command", "args"}}'
|
|
10
|
+
// https://ampcode.com/docs/cli/streaming-json --stream-json (needs --execute), message schema with usage
|
|
11
|
+
// https://ampcode.com/news/amp-x -x runs tools (renames files) without stopping to ask
|
|
12
|
+
// Amp picks models by mode, not by a model flag, so Huddle passes none.
|
|
13
|
+
// Approvals: Amp doesn't ask before running tools by default, so all three
|
|
14
|
+
// Huddle modes run the same; the worktree is the fence. Unverified: whether
|
|
15
|
+
// --mcp-config honors "env" (it's sent anyway; the agent's own env has the
|
|
16
|
+
// same values, which stdio servers normally inherit). Not yet run live.
|
|
17
|
+
export const ampAdapter = {
|
|
18
|
+
id: "amp",
|
|
19
|
+
vendor: "sourcegraph",
|
|
20
|
+
bin: "amp",
|
|
21
|
+
roles: ["worker", "reviewer"],
|
|
22
|
+
prefers: ["refactor", "backend", "frontend"],
|
|
23
|
+
models: { orchestrator: "", worker: "", fast: "", reviewer: "" },
|
|
24
|
+
accountNote: "your Amp account",
|
|
25
|
+
homepage: "https://ampcode.com/docs/cli",
|
|
26
|
+
optional: true,
|
|
27
|
+
install: { cmd: "curl -fsSL https://ampcode.com/install.sh | bash", login: ["amp", "login"] },
|
|
28
|
+
authProbe: {
|
|
29
|
+
cmd: ["amp", "--version"],
|
|
30
|
+
ok: (r) => r.code === 0,
|
|
31
|
+
fixHint: "run `amp login` (or set AMP_API_KEY)",
|
|
32
|
+
},
|
|
33
|
+
plug: {},
|
|
34
|
+
usage: ampUsage,
|
|
35
|
+
launch: ({ worktree, prompt, plug }) => ({
|
|
36
|
+
cmd: ["amp", "--mcp-config", JSON.stringify({ huddle: plug }), "-x", prompt, "--stream-json"],
|
|
37
|
+
cwd: worktree,
|
|
38
|
+
}),
|
|
39
|
+
};
|
|
40
|
+
//# sourceMappingURL=amp.js.map
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { agyUsage } from "./usage.js";
|
|
5
|
+
// Antigravity CLI (`agy`) — Google's terminal agent, which replaced Gemini CLI
|
|
6
|
+
// for personal Google accounts in June 2026. Headless via `-p`; MCP servers are
|
|
7
|
+
// read from .agents/mcp_config.json in the workspace, so the launcher writes
|
|
8
|
+
// the Plug there (and the merge engine keeps it out of commits).
|
|
9
|
+
// NOTE: flags taken from Antigravity docs; not yet verified on a real run.
|
|
10
|
+
const agyAuthed = () => Boolean(process.env.GEMINI_API_KEY) || fs.existsSync(path.join(os.homedir(), ".gemini", "antigravity-cli"));
|
|
11
|
+
export const antigravityAdapter = {
|
|
12
|
+
id: "antigravity",
|
|
13
|
+
vendor: "google",
|
|
14
|
+
bin: "agy",
|
|
15
|
+
roles: ["worker", "fast"],
|
|
16
|
+
prefers: ["research", "breadth", "docs", "data"],
|
|
17
|
+
models: { orchestrator: "", worker: "", fast: "", reviewer: "" },
|
|
18
|
+
accountNote: "personal Google",
|
|
19
|
+
install: {
|
|
20
|
+
cmd: "curl -fsSL https://antigravity.google/cli/install.sh | bash",
|
|
21
|
+
login: ["agy"],
|
|
22
|
+
// agy has no login subcommand — signing in happens inside its own chat UI.
|
|
23
|
+
loginNote: "Antigravity opens its own chat to sign you in — once your email shows at the top, press ctrl-c twice to come back to Huddle",
|
|
24
|
+
},
|
|
25
|
+
authProbe: {
|
|
26
|
+
cmd: ["agy", "--version"],
|
|
27
|
+
ok: (r) => r.code === 0,
|
|
28
|
+
check: agyAuthed,
|
|
29
|
+
fixHint: "run `agy` once and sign in with Google (or set GEMINI_API_KEY)",
|
|
30
|
+
},
|
|
31
|
+
plug: {
|
|
32
|
+
file: { path: ".agents/mcp_config.json", in: "worktree" },
|
|
33
|
+
entry: (plug) => ({ mcpServers: { huddle: plug } }),
|
|
34
|
+
},
|
|
35
|
+
ask: ({ prompt, cwd, model }) => ({ cmd: ["agy", "-p", prompt, "--mode", "plan", ...(model ? ["--model", model] : [])], cwd }),
|
|
36
|
+
usage: agyUsage,
|
|
37
|
+
launch: ({ worktree, prompt, model, approvals }) => ({
|
|
38
|
+
cmd: [
|
|
39
|
+
"agy",
|
|
40
|
+
"-p",
|
|
41
|
+
prompt,
|
|
42
|
+
"--output-format",
|
|
43
|
+
"stream-json", // so Huddle can count tokens
|
|
44
|
+
...(model ? ["--model", model] : []),
|
|
45
|
+
...(approvals === "safe" ? ["--mode", "accept-edits"] : []),
|
|
46
|
+
...(approvals === "auto" ? ["--sandbox", "--dangerously-skip-permissions"] : []),
|
|
47
|
+
...(approvals === "yolo" ? ["--dangerously-skip-permissions"] : []),
|
|
48
|
+
],
|
|
49
|
+
cwd: worktree,
|
|
50
|
+
}),
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=antigravity.js.map
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { claudeUsage } from "./usage.js";
|
|
2
|
+
// Claude Code — launched headless with `-p`. The Plug is handed over with
|
|
3
|
+
// --mcp-config pointing at a file in .huddle/run/, so nothing lands in the
|
|
4
|
+
// agent's worktree. Auth is Claude's own (OAuth or ANTHROPIC_API_KEY); Huddle
|
|
5
|
+
// never touches it.
|
|
6
|
+
const PERMISSIONS = {
|
|
7
|
+
safe: ["--permission-mode", "acceptEdits", "--allowedTools", "mcp__huddle"],
|
|
8
|
+
// Claude's own auto mode: a safety check approves routine actions.
|
|
9
|
+
auto: ["--permission-mode", "auto", "--allowedTools", "mcp__huddle"],
|
|
10
|
+
yolo: ["--dangerously-skip-permissions"],
|
|
11
|
+
};
|
|
12
|
+
export const claudeAdapter = {
|
|
13
|
+
id: "claude",
|
|
14
|
+
vendor: "anthropic",
|
|
15
|
+
bin: "claude",
|
|
16
|
+
roles: ["orchestrator", "worker", "reviewer"],
|
|
17
|
+
prefers: ["frontend", "design", "product", "refactor"],
|
|
18
|
+
// CLI aliases, not dated ids — Claude Code resolves them to the latest model.
|
|
19
|
+
models: { orchestrator: "opus", worker: "opus", fast: "haiku", reviewer: "opus" },
|
|
20
|
+
accountNote: "personal Anthropic",
|
|
21
|
+
install: { cmd: "curl -fsSL https://claude.ai/install.sh | bash" },
|
|
22
|
+
authProbe: {
|
|
23
|
+
// `claude auth status` (JSON by default, verified on 2.1.300): exit 0 +
|
|
24
|
+
// "loggedIn": true when signed in (OAuth or ANTHROPIC_API_KEY), exit 1 +
|
|
25
|
+
// "loggedIn": false when not. `--version` used to be the probe, which showed
|
|
26
|
+
// a signed-out Claude as ready.
|
|
27
|
+
cmd: ["claude", "auth", "status"],
|
|
28
|
+
ok: (r) => r.code === 0 && !/"loggedIn"\s*:\s*false/.test(r.stdout),
|
|
29
|
+
fixHint: "run `claude` once and sign in (OAuth) or set ANTHROPIC_API_KEY",
|
|
30
|
+
},
|
|
31
|
+
plug: {
|
|
32
|
+
file: { path: "claude-mcp.json", in: "run-dir" },
|
|
33
|
+
entry: (plug) => ({ mcpServers: { huddle: { type: "stdio", ...plug } } }),
|
|
34
|
+
},
|
|
35
|
+
// Read-only questions never need your personal MCP servers (Gmail, Drive, …).
|
|
36
|
+
// Self-contained prompts go further: safe mode + no tools ≈ 15k tokens of
|
|
37
|
+
// context instead of ~38k (or 200k+ on Haiku, which loads every tool eagerly).
|
|
38
|
+
ask: ({ prompt, cwd, model, noTools }) => ({
|
|
39
|
+
cmd: [
|
|
40
|
+
"claude",
|
|
41
|
+
"-p",
|
|
42
|
+
prompt,
|
|
43
|
+
"--model",
|
|
44
|
+
model || "opus",
|
|
45
|
+
...(noTools ? ["--safe-mode", "--tools", ""] : ["--permission-mode", "plan", "--strict-mcp-config"]),
|
|
46
|
+
],
|
|
47
|
+
cwd,
|
|
48
|
+
}),
|
|
49
|
+
usage: claudeUsage,
|
|
50
|
+
launch: ({ worktree, prompt, model, mcpConfigPath, approvals }) => ({
|
|
51
|
+
cmd: [
|
|
52
|
+
"claude",
|
|
53
|
+
"-p",
|
|
54
|
+
prompt,
|
|
55
|
+
...(model ? ["--model", model] : []),
|
|
56
|
+
...PERMISSIONS[approvals],
|
|
57
|
+
"--mcp-config",
|
|
58
|
+
mcpConfigPath,
|
|
59
|
+
// Only Huddle's Plug — not your personal MCP servers. Smaller context
|
|
60
|
+
// every turn, and a headless agent can't reach your Gmail/Drive/etc.
|
|
61
|
+
"--strict-mcp-config",
|
|
62
|
+
// JSON events so Huddle can count tokens live (logs stay in .huddle/logs)
|
|
63
|
+
"--output-format",
|
|
64
|
+
"stream-json",
|
|
65
|
+
"--verbose",
|
|
66
|
+
],
|
|
67
|
+
cwd: worktree,
|
|
68
|
+
}),
|
|
69
|
+
};
|
|
70
|
+
//# sourceMappingURL=claude.js.map
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { codexUsage } from "./usage.js";
|
|
2
|
+
// OpenAI Codex CLI — runs non-interactively via `codex exec`. The Plug is
|
|
3
|
+
// passed as `-c mcp_servers.huddle.*` overrides, so neither ~/.codex/config.toml
|
|
4
|
+
// nor the worktree is touched. Auth is `codex login` (ChatGPT) or OPENAI_API_KEY.
|
|
5
|
+
const toml = (v) => JSON.stringify(v); // strings/arrays are valid TOML as JSON
|
|
6
|
+
const tomlTable = (o) => `{${Object.entries(o).map(([k, v]) => `${k}=${toml(v)}`).join(",")}}`;
|
|
7
|
+
export const codexAdapter = {
|
|
8
|
+
id: "codex",
|
|
9
|
+
vendor: "openai",
|
|
10
|
+
bin: "codex",
|
|
11
|
+
roles: ["worker", "reviewer"],
|
|
12
|
+
prefers: ["systems", "algorithms", "refactor", "backend"],
|
|
13
|
+
models: { orchestrator: "", worker: "", fast: "", reviewer: "" },
|
|
14
|
+
accountNote: "personal OpenAI",
|
|
15
|
+
install: { cmd: "npm install -g @openai/codex", login: ["codex", "login"] },
|
|
16
|
+
authProbe: {
|
|
17
|
+
// `codex login status` exits non-zero when not authenticated.
|
|
18
|
+
cmd: ["codex", "login", "status"],
|
|
19
|
+
ok: (r) => r.code === 0,
|
|
20
|
+
fixHint: "run `codex login` (or set OPENAI_API_KEY)",
|
|
21
|
+
},
|
|
22
|
+
plug: {},
|
|
23
|
+
// exec streams its work to stdout; -o captures just the final answer.
|
|
24
|
+
ask: ({ prompt, cwd, outFile, model }) => ({
|
|
25
|
+
cmd: ["codex", "exec", "--sandbox", "read-only", ...(model ? ["-m", model] : []), "-o", outFile, prompt],
|
|
26
|
+
cwd,
|
|
27
|
+
usesOutFile: true,
|
|
28
|
+
}),
|
|
29
|
+
usage: codexUsage,
|
|
30
|
+
launch: ({ worktree, prompt, model, plug, approvals }) => ({
|
|
31
|
+
cmd: [
|
|
32
|
+
"codex",
|
|
33
|
+
"exec",
|
|
34
|
+
"--json", // JSON events so Huddle can count tokens live
|
|
35
|
+
...(model ? ["--model", model] : []),
|
|
36
|
+
"-c",
|
|
37
|
+
`mcp_servers.huddle.command=${toml(plug.command)}`,
|
|
38
|
+
"-c",
|
|
39
|
+
`mcp_servers.huddle.args=${toml(plug.args)}`,
|
|
40
|
+
"-c",
|
|
41
|
+
`mcp_servers.huddle.env=${tomlTable(plug.env)}`,
|
|
42
|
+
// Headless: nobody can click "allow", and Codex refuses unapproved MCP
|
|
43
|
+
// calls under exec. The Plug is Huddle's own server, so pre-approve it.
|
|
44
|
+
"-c",
|
|
45
|
+
`mcp_servers.huddle.default_tools_approval_mode="approve"`,
|
|
46
|
+
// `codex exec` never stops to ask; its sandbox confines commands to the
|
|
47
|
+
// worktree. Without network, the sandbox also refuses to listen on
|
|
48
|
+
// localhost: seen live on the capstone, Codex couldn't run its own HTTP
|
|
49
|
+
// API tests ("26 HTTP tests blocked by sandbox listen EPERM"). `auto`
|
|
50
|
+
// runs commands freely, so it gets network; `safe` stays offline.
|
|
51
|
+
...(approvals === "yolo"
|
|
52
|
+
? ["--dangerously-bypass-approvals-and-sandbox"]
|
|
53
|
+
: ["--sandbox", "workspace-write", ...(approvals === "auto" ? ["-c", "sandbox_workspace_write.network_access=true"] : [])]),
|
|
54
|
+
prompt,
|
|
55
|
+
],
|
|
56
|
+
cwd: worktree,
|
|
57
|
+
}),
|
|
58
|
+
};
|
|
59
|
+
//# sourceMappingURL=codex.js.map
|