huddle-cli 0.1.0 → 0.1.3
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/CHANGELOG.md +69 -0
- package/QUICKSTART.md +135 -0
- package/README.md +26 -4
- package/dist/adapters/amp.js +1 -1
- package/dist/adapters/antigravity.js +2 -0
- package/dist/adapters/claude.js +19 -3
- package/dist/adapters/codex.js +9 -1
- package/dist/adapters/cursor.js +12 -5
- package/dist/adapters/custom.js +4 -2
- package/dist/adapters/droid.js +1 -0
- package/dist/adapters/goose.js +2 -0
- package/dist/adapters/index.js +44 -18
- package/dist/adapters/kiro.js +1 -1
- package/dist/adapters/opencode.js +1 -1
- package/dist/app/live.js +142 -0
- package/dist/app/page.js +701 -0
- package/dist/app/server.js +262 -0
- package/dist/args.js +18 -0
- package/dist/attach/connect.js +7 -4
- package/dist/attach/guard.js +21 -2
- package/dist/attach/live.js +5 -1
- package/dist/attach/plug.js +21 -3
- package/dist/attach/screen.js +150 -0
- package/dist/attach/session.js +7 -2
- package/dist/cli.js +134 -42
- package/dist/mcp/context-tools.js +79 -0
- package/dist/mcp/server.js +57 -11
- package/dist/orchestrator/ask.js +13 -10
- package/dist/orchestrator/brain.js +16 -4
- package/dist/orchestrator/budget.js +283 -0
- package/dist/orchestrator/busy.js +106 -0
- package/dist/orchestrator/council.js +434 -87
- package/dist/orchestrator/goal.js +196 -0
- package/dist/orchestrator/handoff.js +2 -2
- package/dist/orchestrator/launcher.js +31 -13
- package/dist/orchestrator/learn.js +101 -0
- package/dist/orchestrator/planner.js +54 -9
- package/dist/orchestrator/qa.js +71 -15
- package/dist/orchestrator/radar.js +719 -0
- package/dist/orchestrator/scheduler.js +575 -50
- package/dist/orchestrator/takeover.js +155 -0
- package/dist/platform.js +377 -0
- package/dist/room/context.js +1285 -0
- package/dist/room/contract.js +132 -0
- package/dist/room/db.js +79 -16
- package/dist/room/followup.js +233 -0
- package/dist/room/identity.js +2 -2
- package/dist/room/lessons.js +275 -0
- package/dist/room/live.js +75 -20
- package/dist/room/mail.js +225 -0
- package/dist/room/progress.js +40 -1
- package/dist/room/server.js +220 -39
- package/dist/room/tunnel.js +6 -5
- package/dist/room/user.js +54 -0
- package/dist/sandbox/sync.js +4 -4
- package/dist/sandbox/worktree.js +145 -14
- package/dist/ui/app.js +963 -159
- package/dist/ui/brief.js +494 -0
- package/dist/ui/catalog.js +62 -24
- package/dist/ui/dashboard.js +269 -55
- package/dist/ui/doctor.js +15 -11
- package/dist/ui/export.js +1194 -0
- package/dist/ui/lessons.js +81 -0
- package/dist/ui/narrator.js +56 -0
- package/dist/ui/paste.js +515 -0
- package/dist/ui/remote.js +11 -5
- package/dist/ui/setup.js +152 -0
- package/dist/ui/story.js +716 -94
- package/dist/ui/talk.js +176 -0
- package/dist/ui/term.js +182 -16
- package/dist/ui/transcript.js +434 -0
- package/dist/ui/watch.js +149 -48
- package/package.json +6 -3
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.3 — 2026-10-16
|
|
4
|
+
|
|
5
|
+
You can follow a run, and take part in it, without being an engineer. Live capstone: 29/29 with real Claude, Codex and Cursor (13 merged, 0 failed, 22 min, 5.6M tokens, down from 6.6M).
|
|
6
|
+
|
|
7
|
+
- **The plan in plain words.** The plan now opens with "What you'll get" (one or two sentences, no jargon) and "For example" (one real use and what you'd see). The technical decisions and risks fold into one line; `details` shows them.
|
|
8
|
+
- **A narrator.** The top line of a live run says what's happening now ("the agents are each sketching how they'd build it", "building — 2 of 8 parts done · claude: argv parser") and ends with "nothing needed from you", unless something is.
|
|
9
|
+
- **Agents can ask you.** For a big call the goal doesn't settle, an agent asks with 2–4 numbered options, its recommendation first (`huddle_ask_user`). Type a number, press Enter for the recommended one, or answer in your own words. With no answer in 90 seconds (`HUDDLE_ASK_USER_SEC`), it goes with its pick. At most 2 questions per agent per run; the answer goes into shared memory and shows in `huddle story` / `huddle export`.
|
|
10
|
+
- **Talk to the agents mid-run.** Anything you type during a run goes to every agent working now and to every task that starts later, marked as from you; it wins over the plan and the contract. `@codex …` sends it to one agent. This works in `huddle` and in `huddle join` on a terminal (a typing line under the live status). `next <goal>` queues another run.
|
|
11
|
+
- **`huddle build a temperature converter`**: plain words are a goal, with no `join` and no quotes.
|
|
12
|
+
- **Names for the parts**: Room, Screen, Whiteboard, Board, Brief (see QUICKSTART).
|
|
13
|
+
- **`huddle app`, the dashboard.** Huddle in your browser, for the repo you're in:
|
|
14
|
+
- **Live view:** the narrator, the overall bar, one card per agent (task, %, tokens).
|
|
15
|
+
- **Questions:** an agent's question shows as buttons, with a box for your own words.
|
|
16
|
+
- **Talk:** a composer to start a run or talk to the agents, with hold-to-talk voice (hold Space or the mic; the browser's own speech recognition).
|
|
17
|
+
- **History:** every past run as a conversation, with filters, the plan (who did what, what they agreed, the contract), cost per agent, "What you got", and one-click exports.
|
|
18
|
+
- **Any run:** it works for runs started in a terminal too (through `.huddle/live.json` and `.huddle/inbox/`).
|
|
19
|
+
- **Local and private:** it listens on 127.0.0.1 only, behind a per-session key. Runs started there keep going if you close the page.
|
|
20
|
+
|
|
21
|
+
- **The dashboard reads as a story**: what you'll get (or got) in prose; the live status as one sentence, four steps and a chip per agent; the design as idea cards with who borrowed what; the plan part by part, each with its thread (reviews, mid-way checks, messages) in plain words. The raw log has its own tab. Serif headings, real icons, light and dark.
|
|
22
|
+
- **Huddle for macOS** (`desktop/macos`): a ~1 MB native app around the dashboard. ⌘O picks a project, standard menus, exports to Downloads, hold-to-talk. `desktop/macos/build.sh` makes a universal `Huddle.app` and `Huddle.dmg`; see `desktop/README.md` for signing and notarizing.
|
|
23
|
+
|
|
24
|
+
### Fixed (found in a live 0.1.2 run)
|
|
25
|
+
- The design council waited 2 minutes on one agent's ratify that then timed out; ratifying (an OK or a few objections) now has 75 seconds (`HUDDLE_RATIFY_TIMEOUT_S`).
|
|
26
|
+
- The budget's 90% line fired at 108%: Codex reports tokens only when its turn ends. Work in flight is now estimated at that agent's own pace for the 90% line (the 120% line still waits for real numbers).
|
|
27
|
+
- Agents required Node 22 on a Node 20 machine: the planner and council now see the machine's OS and Node version.
|
|
28
|
+
- A sent-back review's notes were stored as a "lesson" word for word; they're about one task, so they no longer are.
|
|
29
|
+
- Two dashboard commands sent in the same millisecond could reach the run out of order.
|
|
30
|
+
|
|
31
|
+
## 0.1.2 — 2026-10-16
|
|
32
|
+
|
|
33
|
+
Memory that outlives the run, chat exports, progress bars, and a cost guard. Live capstone: 29/29 with real claude, codex and cursor.
|
|
34
|
+
|
|
35
|
+
- **Quiet isn't stuck.** Before taking over an agent that has printed nothing for the silence window, Huddle checks whether it is working: a child process using CPU (a long test run, a build), files changing in its worktree, or its log growing. A busy agent gets more time and the run shows "⧗ running a long command" (in `huddle view` and `huddle story` too). It is still taken over after a hard cap, 3× the window by default (`HUDDLE_SILENCE_MAX_MIN`). This happened live: Claude was taken over in the middle of an 8-minute test suite.
|
|
36
|
+
- **Cost guard.** The plan now comes with an estimate (`≈ 4–6M tokens, ~15–20 min with 3 agents`), based on this repo's past runs or on built-in defaults. `--budget=5M` (or `"budget"` in prefs) sets a limit. At 90% Huddle starts no new tasks, takeovers, QA rounds, buddy checks or pairings, and lets running tasks finish. At 120% it stops the agents, as Ctrl-C does. RESULT shows how much of the budget was used and lists what was left undone.
|
|
37
|
+
- **Progress bars.** The live run pins an overall bar (`BUILD ███████░░░░░ 6/9 tasks · 12m`) that moves through the council's four steps, the build (fix tasks QA files included) and each QA round; every agent's status line has a mini bar from its own reported % (a dotted track and "no report yet" until it reports — never a made-up number). `huddle view` and ctrl-t show the same bars in the top bar, the agent panes and the board, ✓ when done and red when failed. Everything fits 60/80/120 columns; with NO_COLOR or piped output it's plain words ("BUILD 6/9 tasks"), one log line each time the numbers move.
|
|
38
|
+
- **Hand-off brief.** Every run writes `.huddle/BRIEF.md` (goal, who built what, decisions, contract, open issues, how to continue) and points to it from a marked, uncommitted block in `CLAUDE.md` / `AGENTS.md`, so solo agents opened afterwards know what the team did. `huddle brief [--run n]`, `huddle brief off|on`.
|
|
39
|
+
- **Run history and chat exports.** `huddle runs` lists every run in the repo (goal, agents, merged/failed, time, tokens, QA). `huddle export [<run>|latest]` saves a run like a chat app keeps a conversation: `--level summary` (result, who did what, decisions, contract, teamwork, cost), `chat` (every message between agents, buddy notes and replies, radar alerts, reviews, QA bugs and Huddle's notes, in full and in time order) or `full` (plus each agent's own session per task, read from the claude / codex / cursor logs), as Markdown, a self-contained HTML page with an agent filter, or JSON with a stable schema. Likely secrets are redacted (`--no-redact` keeps them).
|
|
40
|
+
|
|
41
|
+
## 0.1.1 — 2026-10-16
|
|
42
|
+
|
|
43
|
+
The agents now work *together*, not just side by side. Verified live with real Claude, Codex and Cursor building an app from an empty repo (capstone: 29/29 checks, rounds 4 and 6).
|
|
44
|
+
|
|
45
|
+
### Collaboration
|
|
46
|
+
- **Design council v2.** Every agent proposes a design, then reviews the others: what it *adopts* from each teammate and what *flaw* it sees. The agent whose ideas teammates adopted most writes the plan (not always Claude). Everyone else ratifies it or objects; objections are taken before anyone codes.
|
|
47
|
+
- **Shared contract.** The plan comes with `HUDDLE_CONTRACT.md` — file owners, function names, data fields, exact error messages — committed before work starts. `huddle_amend_contract` changes it and tells every working agent.
|
|
48
|
+
- **The goal reaches every agent verbatim** and wins over the plan and the contract; a check makes sure every exact string in the goal lands in some task.
|
|
49
|
+
- **Mail mid-task.** Messages arrive inside every Huddle tool result, and Claude also gets them after *every* tool call (a hook). Agents check in at ~25/50/75%.
|
|
50
|
+
- **Buddy checks.** At ~50% (or after 2 minutes without a progress report) another agent looks at the work in progress and sends notes. Notes must be answered — fixed, or "won't fix, because…" — before the task can finish.
|
|
51
|
+
- **Radar.** Every 20s Huddle compares the agents' unfinished work and warns both sides about overlapping edits or name mismatches (`getItems` vs `listItems`).
|
|
52
|
+
- **Pairing.** A free agent takes a separable part of a task that's behind (or writes its tests) unless the author says no.
|
|
53
|
+
- **QA checks the goal item by item**, and a suspiciously quick "clean" round gets a second opinion from another agent.
|
|
54
|
+
- **Reviews say what they verified**; a bare APPROVE is marked shallow.
|
|
55
|
+
|
|
56
|
+
### New
|
|
57
|
+
- `huddle setup` — checks Node and git, installs the agents you pick, walks you through each sign-in.
|
|
58
|
+
- `huddle agents use claude,codex` / `huddle agents skip cursor` — choose your agents, even ones that are installed.
|
|
59
|
+
- **The shared screen.** Paste a long log or doc and it becomes one item (`ctx-1`) instead of one command per line; `add <file|folder>` or drag a file in; screenshots and PDFs too. Agents read, search and pin notes on it. Works in attach mode: `huddle add <file>`, `pbpaste | huddle add -`, `huddle ctx`. Secrets are caught before anything goes to an agent.
|
|
60
|
+
- `huddle join "<goal>" --with <file>` and piped input (`cat log | huddle join "fix this"`).
|
|
61
|
+
- **Lessons.** Huddle remembers what went wrong in earlier runs and tells the next run's agents (`huddle lessons`).
|
|
62
|
+
- `npm run evolve` — Huddle runs on its own repo to fix its weaknesses, kept only if the capstone score holds.
|
|
63
|
+
- The planner (brain) defaults to Claude Opus.
|
|
64
|
+
|
|
65
|
+
### Better
|
|
66
|
+
- Live output in clear phases (COUNCIL → PLAN → BUILD → QA → RESULT), one status line per agent, a TEAMWORK summary; redesigned `huddle view` and `huddle story --html`.
|
|
67
|
+
- Windows: PowerShell install commands, agent CLIs launched correctly, Windows paths, process cleanup, no duplicated rows in `huddle doctor`. `HUDDLE_ASCII=1` for old consoles.
|
|
68
|
+
- Fixed: lost reviewer answers and agent output when Huddle was busy; a log-stream crash on Linux; flaky tests.
|
|
69
|
+
- `QUICKSTART.md`: install, first run, teammates, on one page.
|
package/QUICKSTART.md
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Huddle quickstart
|
|
2
|
+
|
|
3
|
+
Two commands on any computer, then you type what you want built.
|
|
4
|
+
|
|
5
|
+
## 1. Install (once per computer)
|
|
6
|
+
|
|
7
|
+
You need **Node.js 20+** and **git**. Check with `node -v` and `git --version`.
|
|
8
|
+
Missing one? Node: [nodejs.org](https://nodejs.org) (or `nvm install 22`). git on macOS: `xcode-select --install`. On Windows, see [below](#windows).
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install -g huddle-cli && huddle setup
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
`huddle setup` checks Node and git, asks which agents you want (say no to any you don't use — e.g. skip Cursor), installs the missing ones with the right command for your computer, then opens each one's own sign-in, one at a time. You need at least one agent. `huddle setup --yes` skips the questions (sign-ins still need you: they open a browser).
|
|
15
|
+
|
|
16
|
+
By hand instead:
|
|
17
|
+
|
|
18
|
+
| Agent | Install (macOS / Linux) | Install (Windows PowerShell) | Sign in (once) |
|
|
19
|
+
|---|---|---|---|
|
|
20
|
+
| Claude Code | `curl -fsSL https://claude.ai/install.sh \| bash` | `irm https://claude.ai/install.ps1 \| iex` | `claude auth login` |
|
|
21
|
+
| Codex | `npm install -g @openai/codex` | same | `codex login` |
|
|
22
|
+
| Cursor | `curl https://cursor.com/install -fsS \| bash` | `irm 'https://cursor.com/install?win32=true' \| iex` | `cursor-agent login` |
|
|
23
|
+
| Antigravity | `curl -fsSL https://antigravity.google/cli/install.sh \| bash` | `irm https://antigravity.google/cli/install.ps1 \| iex` | run `agy`, sign in, then ctrl-c twice |
|
|
24
|
+
|
|
25
|
+
Each agent uses its own account; Huddle never sees a password or key. Changed your mind? `huddle agents skip cursor` keeps Cursor out of every run (even when it's installed and signed in); `huddle agents use cursor` brings it back; `huddle agents` shows who's ready and who's skipped.
|
|
26
|
+
|
|
27
|
+
### Windows
|
|
28
|
+
|
|
29
|
+
Huddle runs natively in PowerShell (Windows Terminal or VS Code's terminal):
|
|
30
|
+
|
|
31
|
+
```powershell
|
|
32
|
+
winget install OpenJS.NodeJS.LTS # if `node -v` fails (then open a new terminal)
|
|
33
|
+
winget install --id Git.Git -e --source winget # if `git --version` fails
|
|
34
|
+
npm install -g huddle-cli
|
|
35
|
+
huddle setup
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`huddle setup` uses the PowerShell installers above. If PowerShell says "running scripts is disabled", run `Set-ExecutionPolicy -Scope CurrentUser RemoteSigned` once.
|
|
39
|
+
**If anything fails on Windows, WSL is the fallback:** `wsl --install`, restart, open "Ubuntu", then follow the macOS / Linux steps inside it (install Node there too, and keep your repo inside the Linux file system, e.g. `~/code`).
|
|
40
|
+
|
|
41
|
+
## 2. Use it
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
cd my-project # any git repo (not one? huddle offers to set it up)
|
|
45
|
+
huddle
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
At the `huddle ›` prompt, type the goal, e.g. `build a REST API for todos with tests`.
|
|
49
|
+
Huddle plans it, every agent works at once in its own copy of the repo, and work merges only when tests pass.
|
|
50
|
+
|
|
51
|
+
One-shot instead of the prompt: `huddle join "build a REST API for todos with tests"`.
|
|
52
|
+
|
|
53
|
+
**Pasting and files.** Paste anything long (a stack trace, a spec, code) at the prompt: it goes on the *shared screen* as one item, every agent can read it, and what you type next is the goal. Drag a file or screenshot into the terminal, or type `add notes.md` / `add src/` / `add *.png`. `ctx` shows what's on the screen; `ctx rm ctx-2` takes one off. Prompts only carry a short index, so a huge paste doesn't get copied to every agent. Keys and tokens in a paste are caught first: Huddle asks before showing them to agents, and redacts them unless you say no.
|
|
54
|
+
One-shot: `huddle join "fix this" --with error.log --with screenshot.png`, or `cat error.log | huddle join "fix this"`.
|
|
55
|
+
|
|
56
|
+
Or skip the prompt: `huddle build a temperature converter with tests` (no quotes needed).
|
|
57
|
+
|
|
58
|
+
**While it runs** the top line says what's happening in plain words, and whether you're needed:
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
Now: building — 2 of 8 parts done · claude: argv parser · codex: conversion core 25% · nothing needed from you
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
- **Talk to the agents:** just type. `use two decimals` goes to every agent working now (and every one that starts later); `@codex use two decimals` to one. Your words win over the plan.
|
|
65
|
+
- **When they need you:** for a big call the goal doesn't settle, an agent asks with numbered options, its pick first. Type a number, press Enter for the recommended one, or answer in your own words. No answer in 90 seconds and it goes with its pick, so a run never waits on you.
|
|
66
|
+
- `details` shows the technical decisions behind the plan; `stop` (or ctrl-c) stops the agents and still shows what got done.
|
|
67
|
+
|
|
68
|
+
**The dashboard:** `huddle app` opens Huddle in your browser. You get the run going now (who's on what, how far along, a question waiting for you), every past run as a conversation, the plan and what each agent cost. Type there to start a run or talk to the agents; hold Space (or the mic) to say it instead. It works for runs started in a terminal too. Runs keep going if you close the page.
|
|
69
|
+
|
|
70
|
+
**A second view** (another terminal, same folder):
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
huddle view # live: who's doing what, messages, merges
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**After it runs:**
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
huddle story # replay: plan, messages between agents, reviews, merges
|
|
80
|
+
huddle story --html story.html # the same as a page you can open or share
|
|
81
|
+
huddle brief # the hand-off brief your next solo claude/codex/cursor reads
|
|
82
|
+
huddle runs # every run in this repo: goal, agents, result, cost
|
|
83
|
+
huddle export # the last run as a chat: who said what to whom (Markdown)
|
|
84
|
+
huddle export 2 --level full --format html # run 2 with each agent's own session, as a page
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Each run leaves `.huddle/BRIEF.md` (decisions, contract, open issues) and a short pointer to it at the top of `CLAUDE.md` and `AGENTS.md`, so an agent you open on your own afterwards knows what the team did. The pointer isn't committed; `huddle brief off` removes it.
|
|
88
|
+
|
|
89
|
+
## 3. Working with teammates (e.g. a hackathon)
|
|
90
|
+
|
|
91
|
+
Everyone installs Huddle as in step 1, on their own machine, with their own agents.
|
|
92
|
+
|
|
93
|
+
1. Put the repo on GitHub and have each teammate clone it.
|
|
94
|
+
2. In your `huddle`, type `sync on` so merged work gets pushed for them.
|
|
95
|
+
3. Type `invite @their-github-name`. Huddle prints a one-time `huddle join <address> <CODE>` line (valid 10 minutes).
|
|
96
|
+
4. They run that line inside their clone. Their agents now take tasks off your board, using their own accounts.
|
|
97
|
+
|
|
98
|
+
Not on the same Wi-Fi? Install [Tailscale](https://tailscale.com) on both machines, or run `huddle relay` on a server and type `relay <url>` in your huddle.
|
|
99
|
+
|
|
100
|
+
## The words Huddle uses
|
|
101
|
+
|
|
102
|
+
| Word | What it is |
|
|
103
|
+
|---|---|
|
|
104
|
+
| **Room** | Where your agents (and teammates' agents) meet for a goal. `huddle` opens yours; `invite` lets people in. |
|
|
105
|
+
| **Screen** | The shared screen: what you paste, drag in or `add` — logs, docs, screenshots. Every agent reads it. |
|
|
106
|
+
| **Whiteboard** | Where they design it together before coding: each one's idea, what they borrow from each other, the plan, and the contract (`HUDDLE_CONTRACT.md`, the exact names everyone codes to). `details` and `huddle story` show it. |
|
|
107
|
+
| **Board** | Who's doing which part, how far along, what's merged. `board`, `huddle view`, ctrl-t. |
|
|
108
|
+
| **Brief** | What a finished run leaves for your next solo agent (`.huddle/BRIEF.md`). |
|
|
109
|
+
|
|
110
|
+
## Everyday commands
|
|
111
|
+
|
|
112
|
+
| Command | What it does |
|
|
113
|
+
|---|---|
|
|
114
|
+
| `huddle` | open Huddle in this repo |
|
|
115
|
+
| `huddle join "<goal>" --plan` | dry run: show who would do what |
|
|
116
|
+
| `huddle setup` | install and sign in to your agents (asks first) |
|
|
117
|
+
| `huddle doctor` | check Node, git and your agents |
|
|
118
|
+
| `huddle agents` · `huddle agents skip <id>` · `use <id>` | who's ready, and which agents Huddle uses |
|
|
119
|
+
| `huddle app` | the dashboard in your browser: live run, history, talk to the agents (or speak) |
|
|
120
|
+
| `huddle view` | watch a run live |
|
|
121
|
+
| `huddle story` | replay the last run |
|
|
122
|
+
| `huddle brief` · `huddle brief off` | what the last run left for your next solo agent · stop pointing CLAUDE.md / AGENTS.md at it |
|
|
123
|
+
| `huddle runs` · `huddle export [run]` | list past runs · save one as a chat (`--level summary\|chat\|full`, `--format md\|html\|json`) |
|
|
124
|
+
| `huddle board` | print the task board |
|
|
125
|
+
| `add <file>` / `ctx` | (at the prompt) put a file on the shared screen / see what's on it |
|
|
126
|
+
| `huddle add <file\|->` / `huddle ctx` | running claude/codex/cursor yourself (`huddle connect`): put a file, folder or stdin (`cat err.log \| huddle add -`) on the shared screen every session reads / see it |
|
|
127
|
+
| `npm install -g huddle-cli` | update to the newest version |
|
|
128
|
+
| `npm uninstall -g huddle-cli` | remove it (your repos keep their work) |
|
|
129
|
+
|
|
130
|
+
## If something goes wrong
|
|
131
|
+
|
|
132
|
+
- **`EACCES` when installing:** your npm global folder is owned by root. Use [nvm](https://github.com/nvm-sh/nvm), or run `npm config set prefix ~/.npm-global` and add `~/.npm-global/bin` to your `PATH`. Don't use `sudo`.
|
|
133
|
+
- **An agent says "not signed in":** `huddle setup` walks you through it, or run the sign-in command `huddle doctor` shows next to it.
|
|
134
|
+
- **"Node.js 20 or newer":** `nvm install 22`, then install Huddle again.
|
|
135
|
+
- **Anything else:** `HUDDLE_DEBUG=1 huddle …` prints the full error.
|
package/README.md
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
`npm install -g huddle-cli` → `huddle`
|
|
10
10
|
|
|
11
|
+
**New here? [QUICKSTART.md](QUICKSTART.md)** — install, first run, teammates, in one page.
|
|
12
|
+
|
|
11
13
|
</div>
|
|
12
14
|
|
|
13
15
|
---
|
|
@@ -18,19 +20,21 @@ You need **Node.js 20+** and **git**. Then:
|
|
|
18
20
|
|
|
19
21
|
```bash
|
|
20
22
|
npm install -g huddle-cli # puts `huddle` on your PATH
|
|
21
|
-
huddle
|
|
23
|
+
huddle setup # checks node + git, installs and signs in the agents you pick
|
|
22
24
|
|
|
23
25
|
cd ~/some-git-repo
|
|
24
26
|
huddle # welcome → connects your agents → huddle ›
|
|
25
27
|
```
|
|
26
28
|
|
|
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):
|
|
29
|
+
Prefer a script? `install.sh` checks Node, npm, git and a root-owned npm folder first, then runs the same `npm install -g` (this URL works only once the GitHub repo is public; until then use the npm command above):
|
|
28
30
|
|
|
29
31
|
```bash
|
|
30
32
|
curl -fsSL https://raw.githubusercontent.com/Preet37/Huddle/main/install.sh | sh
|
|
31
33
|
```
|
|
32
34
|
|
|
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
|
|
35
|
+
**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 setup` (and the first `huddle`) offers to install any that are missing and to sign you in, **always asking first**; `huddle doctor` prints the exact install commands for your OS. Don't want one? `huddle agents skip cursor` keeps it off every run, even when installed; `huddle agents use cursor` undoes it.
|
|
36
|
+
|
|
37
|
+
**Windows:** works natively in PowerShell — the same two commands; `huddle setup` uses each agent's PowerShell installer. Fallback if anything fails: WSL (`wsl --install`, then the Linux steps). Details in [QUICKSTART.md](QUICKSTART.md#windows). Each agent signs in with its own account and bills to it — Huddle never sees or stores a credential.
|
|
34
38
|
|
|
35
39
|
**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
40
|
|
|
@@ -61,9 +65,27 @@ At the `huddle ›` prompt, type what you want built. Huddle plans it into paral
|
|
|
61
65
|
|
|
62
66
|
- **shift+tab** switches mode: `manual` (approve the plan and each merge) → `auto` → `plan` (dry run)
|
|
63
67
|
- **`approvals safe|auto|yolo`** sets what agents may do without asking
|
|
68
|
+
- **`--budget=5M`** (typed with the goal, on `huddle join`, or `"budget": "5M"` in `~/.huddle/prefs.json`) caps the tokens a run may use. The plan shows an estimate first. At 90% no new work starts and running tasks finish; at 120% the agents stop. RESULT lists what was left undone. Cached tokens count in full, so the budget runs out before the bill does.
|
|
64
69
|
- keep typing while agents work: `board`, questions, and new goals (queued) all work mid-run
|
|
65
70
|
- `cd`, `!cmd`, `clear` behave like a shell; not a git repo? Huddle offers to set it up
|
|
66
71
|
|
|
72
|
+
**How the agents work together on a big goal:**
|
|
73
|
+
|
|
74
|
+
- **Council.** Each agent proposes a design, then reads the others' and answers `ADOPT` (what a teammate got right that it missed), `FLAW` (what a teammate got wrong, and why) and `REVISED`. The agent whose ideas were adopted by the most teammates writes the plan (ties: the most adoptions in all, then the fewest flaws found, then whoever has written fewest plans); the brain writes it if nobody adopted anything. Each task records whose idea it was.
|
|
75
|
+
- **Contract.** The plan comes with a contract: the exact shared names (files and owners, signatures, data fields, routes, error strings). The other agents reply `OK` or `OBJECT: <change>`, with one revision round. It's committed as `HUDDLE_CONTRACT.md` and included in every task prompt. To change a name mid-task an agent calls `huddle_amend_contract`, and everyone still working is told.
|
|
76
|
+
- **Mid-task mail.** Every Plug call result includes the agent's new messages. Claude also gets them after any tool use, through a hook. Agents report progress at about 25/50/75%.
|
|
77
|
+
- **Radar.** About every 20s Huddle compares the uncommitted work of the agents in flight. It looks for edits to the same lines, and for imports of a name the other agent's module doesn't export (`getItems` vs `listItems`). It tells both agents while they can still fix it. Turn it off with `HUDDLE_RADAR_SEC=off`.
|
|
78
|
+
- **Buddy checks.** At about 40% (and again at 75% if there were notes), another agent reads the work in progress. It checks it against the task, the contract and the other tasks in flight, and its notes go to the author's inbox. An agent that finishes while a check is still being written waits for it. An agent that never reports progress gets its check after 2 minutes instead (`HUDDLE_BUDDY_AFTER_SEC`). Turn it off with `HUDDLE_BUDDY=off`.
|
|
79
|
+
- **Notes need an answer.** A buddy note or a radar finding stays open until the author fixes the file it names or replies to it (`won't fix: <why>` counts). `huddle_done` with notes still open is refused once, with the list; the second `huddle_done` always goes through, and the note is recorded as ignored.
|
|
80
|
+
- **Pairing.** When an agent is free and a task is under 50% done (one that never reports progress only after twice the help wait, with little of its file list started), the free agent proposes taking a separate part of it, or its tests. The author has 60s to say no.
|
|
81
|
+
- `huddle story` shows all of this afterwards: who adopted whose idea, who objected, contract versions, what the radar caught, buddy notes (addressed or ignored), and pairs.
|
|
82
|
+
- **Hand-off brief.** Every run ends with `.huddle/BRIEF.md`: the goal, what was built and by whom, the decisions and why, the contract, open issues and how to continue — from the Room's records, no model call. A short marked block at the top of `CLAUDE.md` and `AGENTS.md` points to it, so a `claude`, `codex` or `cursor` you open on your own afterwards reads it first. It's never committed: a file Huddle creates is git-excluded, and in a file you own the block is lifted out while each run works and put back after. `huddle brief` prints it (`--run n`); `huddle brief off` removes the pointers; `HUDDLE_NO_BRIEF=1` turns it all off.
|
|
83
|
+
- `huddle runs` lists every run in the repo (goal, agents, result, time, tokens, QA). `huddle export [<run>|latest]` saves one as a document you can read or share, like a chat app's history: `--level summary` (result, who did what, decisions, contract, teamwork, cost), `chat` (the default: every message between agents, buddy notes and replies, radar alerts, reviews, QA bugs and Huddle's notes, in time order and in full), or `full` (also each agent's own session per task: what it said, read, ran and wrote, parsed from its log). `--format md|html|json` (HTML is one offline file with an agent filter; JSON is a stable schema, documented in `src/ui/export.ts`). It goes to `.huddle/exports/` unless you pass `--out <file|dir|->`. Likely secrets are redacted unless you pass `--no-redact`.
|
|
84
|
+
|
|
85
|
+
None of these steps blocks the run. If an agent doesn't answer or doesn't follow the format, the run continues as it would without that step.
|
|
86
|
+
|
|
87
|
+
**Pasting and files: one shared screen, not N copies.** Paste a stack trace, a doc or a block of code at the prompt and it becomes one item on the Room's *shared screen* (`📎 pasted 214 lines (ctx-3 'TypeError…')`) instead of 214 commands; what you type next is the goal. Drag a file or screenshot into the terminal, or `add <file|folder|glob>`. Prompts carry only a one-line index per item; the planner links items to the tasks that need them, and agents open them with `huddle_context_read` (400 lines a call, images and PDFs as a file path; Codex also gets linked screenshots via `--image`), search them, and pin notes on them that everyone sees. `ctx` lists the screen (`ctx show|rm|note|clear`). Likely secrets (API keys, tokens, private keys, `.env` lines) are flagged before anything is shown to agents, and redacted unless you say no. One-shot: `huddle join "fix this" --with error.log --with shot.png`, or `cat log.txt | huddle join "fix this"`. Items live in `.huddle/context/` (git-ignored); caps: 2 MB of text, 25 MB per file, 200 MB in all (`HUDDLE_CTX_MAX_*_MB`).
|
|
88
|
+
|
|
67
89
|
**Running claude, codex and cursor yourself, in one repo at once (attach mode):**
|
|
68
90
|
|
|
69
91
|
```bash
|
|
@@ -73,7 +95,7 @@ claude # open your agents as usual, in as many terminals as you lik
|
|
|
73
95
|
huddle peers # who's live, what each is doing, which files each one holds
|
|
74
96
|
```
|
|
75
97
|
|
|
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).
|
|
98
|
+
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. The shared screen works here too: `huddle add err.log` (or `cat err.log | huddle add -`) from any terminal puts it up, every session hears `📎 new on the screen: ctx-4 …` on its next Huddle call and reads it with `huddle_context_read`; `huddle ctx` lists it (`show|rm|note|clear`). 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
99
|
|
|
78
100
|
**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
101
|
|
package/dist/adapters/amp.js
CHANGED
|
@@ -24,7 +24,7 @@ export const ampAdapter = {
|
|
|
24
24
|
accountNote: "your Amp account",
|
|
25
25
|
homepage: "https://ampcode.com/docs/cli",
|
|
26
26
|
optional: true,
|
|
27
|
-
install: { cmd: "curl -fsSL https://ampcode.com/install.sh | bash", login: ["amp", "login"] },
|
|
27
|
+
install: { cmd: "curl -fsSL https://ampcode.com/install.sh | bash", win: null, login: ["amp", "login"] },
|
|
28
28
|
authProbe: {
|
|
29
29
|
cmd: ["amp", "--version"],
|
|
30
30
|
ok: (r) => r.code === 0,
|
|
@@ -17,7 +17,9 @@ export const antigravityAdapter = {
|
|
|
17
17
|
models: { orchestrator: "", worker: "", fast: "", reviewer: "" },
|
|
18
18
|
accountNote: "personal Google",
|
|
19
19
|
install: {
|
|
20
|
+
// https://antigravity.google/docs/getting-started?tab=cli
|
|
20
21
|
cmd: "curl -fsSL https://antigravity.google/cli/install.sh | bash",
|
|
22
|
+
win: "irm https://antigravity.google/cli/install.ps1 | iex",
|
|
21
23
|
login: ["agy"],
|
|
22
24
|
// agy has no login subcommand — signing in happens inside its own chat UI.
|
|
23
25
|
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",
|
package/dist/adapters/claude.js
CHANGED
|
@@ -18,7 +18,12 @@ export const claudeAdapter = {
|
|
|
18
18
|
// CLI aliases, not dated ids — Claude Code resolves them to the latest model.
|
|
19
19
|
models: { orchestrator: "opus", worker: "opus", fast: "haiku", reviewer: "opus" },
|
|
20
20
|
accountNote: "personal Anthropic",
|
|
21
|
-
|
|
21
|
+
// https://code.claude.com/docs/en/setup · sign-in: https://code.claude.com/docs/en/cli-reference
|
|
22
|
+
install: {
|
|
23
|
+
cmd: "curl -fsSL https://claude.ai/install.sh | bash",
|
|
24
|
+
win: "irm https://claude.ai/install.ps1 | iex",
|
|
25
|
+
login: ["claude", "auth", "login"],
|
|
26
|
+
},
|
|
22
27
|
authProbe: {
|
|
23
28
|
// `claude auth status` (JSON by default, verified on 2.1.300): exit 0 +
|
|
24
29
|
// "loggedIn": true when signed in (OAuth or ANTHROPIC_API_KEY), exit 1 +
|
|
@@ -26,7 +31,7 @@ export const claudeAdapter = {
|
|
|
26
31
|
// a signed-out Claude as ready.
|
|
27
32
|
cmd: ["claude", "auth", "status"],
|
|
28
33
|
ok: (r) => r.code === 0 && !/"loggedIn"\s*:\s*false/.test(r.stdout),
|
|
29
|
-
fixHint: "run `claude
|
|
34
|
+
fixHint: "run `claude auth login` (or set ANTHROPIC_API_KEY)",
|
|
30
35
|
},
|
|
31
36
|
plug: {
|
|
32
37
|
file: { path: "claude-mcp.json", in: "run-dir" },
|
|
@@ -46,12 +51,22 @@ export const claudeAdapter = {
|
|
|
46
51
|
],
|
|
47
52
|
cwd,
|
|
48
53
|
}),
|
|
54
|
+
// `claude -p` with no prompt argument reads it from stdin.
|
|
55
|
+
stdinPrompt: "",
|
|
49
56
|
usage: claudeUsage,
|
|
50
|
-
|
|
57
|
+
// A PostToolUse hook, loaded with --settings (a file in .huddle/run/, never
|
|
58
|
+
// the user's settings): after any tool call, `huddle mail --hook` prints
|
|
59
|
+
// {hookSpecificOutput:{hookEventName:"PostToolUse",additionalContext}} when
|
|
60
|
+
// a teammate wrote, and nothing otherwise. Schema per
|
|
61
|
+
// https://code.claude.com/docs/en/hooks; --settings per `claude --help` (2.1.300).
|
|
62
|
+
mailHook: (command) => ({ hooks: { PostToolUse: [{ matcher: "*", hooks: [{ type: "command", command, timeout: 5 }] }] } }),
|
|
63
|
+
launch: ({ worktree, prompt, model, mcpConfigPath, approvals, context, hookSettingsPath }) => ({
|
|
51
64
|
cmd: [
|
|
52
65
|
"claude",
|
|
53
66
|
"-p",
|
|
54
67
|
prompt,
|
|
68
|
+
// The shared screen lives outside the worktree; Read opens its images and PDFs.
|
|
69
|
+
...(context ? ["--add-dir", context.dir] : []),
|
|
55
70
|
...(model ? ["--model", model] : []),
|
|
56
71
|
...PERMISSIONS[approvals],
|
|
57
72
|
"--mcp-config",
|
|
@@ -59,6 +74,7 @@ export const claudeAdapter = {
|
|
|
59
74
|
// Only Huddle's Plug — not your personal MCP servers. Smaller context
|
|
60
75
|
// every turn, and a headless agent can't reach your Gmail/Drive/etc.
|
|
61
76
|
"--strict-mcp-config",
|
|
77
|
+
...(hookSettingsPath ? ["--settings", hookSettingsPath] : []),
|
|
62
78
|
// JSON events so Huddle can count tokens live (logs stay in .huddle/logs)
|
|
63
79
|
"--output-format",
|
|
64
80
|
"stream-json",
|
package/dist/adapters/codex.js
CHANGED
|
@@ -26,11 +26,16 @@ export const codexAdapter = {
|
|
|
26
26
|
cwd,
|
|
27
27
|
usesOutFile: true,
|
|
28
28
|
}),
|
|
29
|
+
// `codex exec -`: "instructions are read from stdin".
|
|
30
|
+
stdinPrompt: "-",
|
|
29
31
|
usage: codexUsage,
|
|
30
|
-
launch: ({ worktree, prompt, model, plug, approvals }) => ({
|
|
32
|
+
launch: ({ worktree, prompt, model, plug, approvals, context }) => ({
|
|
31
33
|
cmd: [
|
|
32
34
|
"codex",
|
|
33
35
|
"exec",
|
|
36
|
+
// `codex exec -i/--image <FILE>`: screenshots the planner linked to this task, attached to the prompt.
|
|
37
|
+
// One flag per file, before the other flags, so the list never swallows the prompt.
|
|
38
|
+
...(context?.images ?? []).flatMap((f) => ["--image", f]),
|
|
34
39
|
"--json", // JSON events so Huddle can count tokens live
|
|
35
40
|
...(model ? ["--model", model] : []),
|
|
36
41
|
"-c",
|
|
@@ -43,6 +48,9 @@ export const codexAdapter = {
|
|
|
43
48
|
// calls under exec. The Plug is Huddle's own server, so pre-approve it.
|
|
44
49
|
"-c",
|
|
45
50
|
`mcp_servers.huddle.default_tools_approval_mode="approve"`,
|
|
51
|
+
// huddle_ask_user waits up to 90s for the person at the keyboard; Codex's default tool timeout is 60s.
|
|
52
|
+
"-c",
|
|
53
|
+
"mcp_servers.huddle.tool_timeout_sec=150",
|
|
46
54
|
// `codex exec` never stops to ask; its sandbox confines commands to the
|
|
47
55
|
// worktree. Without network, the sandbox also refuses to listen on
|
|
48
56
|
// localhost: seen live on the capstone, Codex couldn't run its own HTTP
|
package/dist/adapters/cursor.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { spawnToolSync } from "../platform.js";
|
|
2
2
|
import { cursorUsage } from "./usage.js";
|
|
3
3
|
/**
|
|
4
4
|
* The model for quick prompt-only answers (council turns, cross-reviews), or
|
|
@@ -24,7 +24,7 @@ export function cursorFastModel(list = listCursorModels) {
|
|
|
24
24
|
catch {
|
|
25
25
|
/* not installed / signed out: keep the default */
|
|
26
26
|
}
|
|
27
|
-
const ids = out.split(
|
|
27
|
+
const ids = out.split(/\r?\n/).map((l) => /^\s*([\w.-]+)\s+-\s/.exec(l)?.[1]).filter((x) => !!x);
|
|
28
28
|
fastPick = { model: ids.find((id) => /^composer-[\d.]+$/.test(id)) };
|
|
29
29
|
return fastPick.model;
|
|
30
30
|
}
|
|
@@ -33,7 +33,7 @@ export function resetCursorFastModel() {
|
|
|
33
33
|
fastPick = undefined;
|
|
34
34
|
}
|
|
35
35
|
function listCursorModels() {
|
|
36
|
-
const r =
|
|
36
|
+
const r = spawnToolSync(["cursor-agent", "--list-models"], { encoding: "utf8", timeout: 15_000 });
|
|
37
37
|
return r.status === 0 ? r.stdout : "";
|
|
38
38
|
}
|
|
39
39
|
// Cursor CLI (cursor-agent) — headless coding agent. Personal, team or SSO
|
|
@@ -47,7 +47,12 @@ export const cursorAdapter = {
|
|
|
47
47
|
prefers: ["in-editor-edits", "frontend", "quickfix"],
|
|
48
48
|
models: { orchestrator: "", worker: "", fast: "", reviewer: "" },
|
|
49
49
|
accountNote: "your Cursor account (SSO handled by cursor-agent itself)",
|
|
50
|
-
|
|
50
|
+
// https://cursor.com/docs/cli/installation (native Windows: PowerShell)
|
|
51
|
+
install: {
|
|
52
|
+
cmd: "curl https://cursor.com/install -fsS | bash",
|
|
53
|
+
win: "irm 'https://cursor.com/install?win32=true' | iex",
|
|
54
|
+
login: ["cursor-agent", "login"],
|
|
55
|
+
},
|
|
51
56
|
authProbe: {
|
|
52
57
|
cmd: ["cursor-agent", "status"],
|
|
53
58
|
ok: (r) => r.code === 0 && !/not logged in/i.test(r.stdout + r.stderr),
|
|
@@ -64,11 +69,13 @@ export const cursorAdapter = {
|
|
|
64
69
|
return { cmd: ["cursor-agent", "-p", prompt, "--mode", "ask", "--trust", ...(m ? ["--model", m] : [])], cwd };
|
|
65
70
|
},
|
|
66
71
|
usage: cursorUsage,
|
|
67
|
-
launch: ({ worktree, prompt, model, approvals }) => ({
|
|
72
|
+
launch: ({ worktree, prompt, model, approvals, context }) => ({
|
|
68
73
|
cmd: [
|
|
69
74
|
"cursor-agent",
|
|
70
75
|
"-p",
|
|
71
76
|
prompt,
|
|
77
|
+
// No image flag on cursor-agent: the shared screen's folder, so its tools can open items by path.
|
|
78
|
+
...(context ? ["--add-dir", context.dir] : []),
|
|
72
79
|
...(model ? ["--model", model] : []),
|
|
73
80
|
// headless: trust the worktree and the Plug without prompting
|
|
74
81
|
"--trust",
|
package/dist/adapters/custom.js
CHANGED
|
@@ -28,7 +28,7 @@ export const USAGE_PARSERS = {
|
|
|
28
28
|
qwen: qwenUsage,
|
|
29
29
|
};
|
|
30
30
|
const SPEC_KEYS = new Set([
|
|
31
|
-
"id", "bin", "launch", "vendor", "approvals", "ask", "askOutFile", "plug", "env", "install", "login", "loginNote",
|
|
31
|
+
"id", "bin", "launch", "vendor", "approvals", "ask", "askOutFile", "plug", "env", "install", "installWin", "login", "loginNote",
|
|
32
32
|
"auth", "usage", "roles", "prefers", "models", "accountNote", "homepage", "optional", "$comment",
|
|
33
33
|
]);
|
|
34
34
|
const ROLES = new Set(["orchestrator", "worker", "reviewer", "fast"]);
|
|
@@ -124,6 +124,8 @@ export function validateSpec(raw) {
|
|
|
124
124
|
errs.push(`"login" must be an array of strings`);
|
|
125
125
|
if (s.install !== undefined && typeof s.install !== "string")
|
|
126
126
|
errs.push(`"install" must be a shell command string`);
|
|
127
|
+
if (s.installWin !== undefined && typeof s.installWin !== "string")
|
|
128
|
+
errs.push(`"installWin" must be a PowerShell command string`);
|
|
127
129
|
if (s.auth !== undefined) {
|
|
128
130
|
const a = s.auth;
|
|
129
131
|
if (!a || typeof a !== "object")
|
|
@@ -216,7 +218,7 @@ export function specToAdapter(spec, source) {
|
|
|
216
218
|
roles: spec.roles ?? (spec.ask ? ["worker", "reviewer"] : ["worker"]),
|
|
217
219
|
prefers: spec.prefers ?? [],
|
|
218
220
|
models: { orchestrator: "", worker: "", fast: "", reviewer: "", ...spec.models },
|
|
219
|
-
install: { cmd: spec.install ?? "", login: spec.login, loginNote: spec.loginNote },
|
|
221
|
+
install: { cmd: spec.install ?? "", ...(spec.installWin !== undefined ? { win: spec.installWin } : {}), login: spec.login, loginNote: spec.loginNote },
|
|
220
222
|
authProbe: {
|
|
221
223
|
cmd: authCmd,
|
|
222
224
|
ok: (r) => r.code === 0 && !(fail && fail.test(r.stdout + r.stderr)),
|
package/dist/adapters/droid.js
CHANGED
|
@@ -34,6 +34,7 @@ export const droidAdapter = {
|
|
|
34
34
|
optional: true,
|
|
35
35
|
install: {
|
|
36
36
|
cmd: "curl -fsSL https://app.factory.ai/cli | sh",
|
|
37
|
+
win: "irm https://app.factory.ai/cli/windows | iex",
|
|
37
38
|
login: ["droid"],
|
|
38
39
|
loginNote: "Droid signs you in from its own screen — once you're in, press ctrl-c to come back to Huddle",
|
|
39
40
|
},
|
package/dist/adapters/goose.js
CHANGED
|
@@ -37,6 +37,8 @@ export const gooseAdapter = {
|
|
|
37
37
|
optional: true,
|
|
38
38
|
install: {
|
|
39
39
|
cmd: "curl -fsSL https://github.com/aaif-goose/goose/releases/download/stable/download_cli.sh | CONFIGURE=false bash",
|
|
40
|
+
// Windows has a download_cli.ps1 that saves itself into the current folder: point at the docs instead.
|
|
41
|
+
win: null,
|
|
40
42
|
login: ["goose", "configure"],
|
|
41
43
|
loginNote: "pick a provider and paste its key in goose's own setup — Huddle never sees it",
|
|
42
44
|
},
|
package/dist/adapters/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// uses to decide who's in the roster for a session. Because we only ever ask
|
|
3
3
|
// "installed?" and "authed?", expiring subscriptions and account swaps are a
|
|
4
4
|
// non-event: a failing probe just drops that agent, the session runs on.
|
|
5
|
-
import {
|
|
5
|
+
import { spawn } from "node:child_process";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { pathToFileURL } from "node:url";
|
|
8
8
|
import { claudeAdapter } from "./claude.js";
|
|
@@ -18,6 +18,7 @@ import { qwenAdapter } from "./qwen.js";
|
|
|
18
18
|
import { droidAdapter } from "./droid.js";
|
|
19
19
|
import { kiroAdapter } from "./kiro.js";
|
|
20
20
|
import { loadAgentsFiles } from "./custom.js";
|
|
21
|
+
import { onPath, spawnToolSync, toolCommand } from "../platform.js";
|
|
21
22
|
export const ADAPTERS = [
|
|
22
23
|
claudeAdapter,
|
|
23
24
|
codexAdapter,
|
|
@@ -80,23 +81,18 @@ export function registerAdapter(adapter) {
|
|
|
80
81
|
else if (adapter.id !== "gemini")
|
|
81
82
|
LEGACY.delete(adapter.id);
|
|
82
83
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
84
|
+
/** Mark the agents you skipped, keeping what the probe found in `was`. */
|
|
85
|
+
export function applySkips(results, skip) {
|
|
86
|
+
const s = new Set(skip);
|
|
87
|
+
return results.map((r) => s.has(r.adapter.id) && r.state !== "skipped" ? { ...r, state: "skipped", was: r.state, detail: `skipped by you — huddle agents use ${r.adapter.id}` } : r);
|
|
88
|
+
}
|
|
89
|
+
/** Undo applySkips for `ids` — `--agents=` on a run overrides a skip for that run. */
|
|
90
|
+
export function unskip(results, ids) {
|
|
91
|
+
return results.map((r) => (r.state === "skipped" && r.was && ids.includes(r.adapter.id) ? { ...r, state: r.was, was: undefined } : r));
|
|
91
92
|
}
|
|
92
93
|
function run(cmd) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
return { code: 0, stdout, stderr: "" };
|
|
96
|
-
}
|
|
97
|
-
catch (e) {
|
|
98
|
-
return { code: e?.status ?? 1, stdout: e?.stdout?.toString() ?? "", stderr: e?.stderr?.toString() ?? "" };
|
|
99
|
-
}
|
|
94
|
+
const r = spawnToolSync(cmd, { encoding: "utf8", stdio: "pipe", timeout: 8000 });
|
|
95
|
+
return { code: r.error ? 1 : (r.status ?? 1), stdout: String(r.stdout ?? ""), stderr: String(r.stderr ?? "") };
|
|
100
96
|
}
|
|
101
97
|
/** Detect + auth-probe a single adapter. */
|
|
102
98
|
export function probe(adapter) {
|
|
@@ -109,9 +105,21 @@ export function probe(adapter) {
|
|
|
109
105
|
}
|
|
110
106
|
return { adapter, state: "ready", detail: `${adapter.vendor} · ${adapter.accountNote ?? "ready"}` };
|
|
111
107
|
}
|
|
112
|
-
function runAsync(cmd) {
|
|
108
|
+
function runAsync(cmd, timeoutMs = 8000) {
|
|
113
109
|
return new Promise((resolve) => {
|
|
114
|
-
|
|
110
|
+
const t = toolCommand(cmd);
|
|
111
|
+
const child = spawn(t.file, t.args, { stdio: ["ignore", "pipe", "pipe"], windowsVerbatimArguments: t.verbatim });
|
|
112
|
+
let stdout = "";
|
|
113
|
+
let stderr = "";
|
|
114
|
+
child.stdout.on("data", (d) => (stdout += d));
|
|
115
|
+
child.stderr.on("data", (d) => (stderr += d));
|
|
116
|
+
const timer = setTimeout(() => child.kill(), timeoutMs);
|
|
117
|
+
const done = (code) => {
|
|
118
|
+
clearTimeout(timer);
|
|
119
|
+
resolve({ code, stdout, stderr });
|
|
120
|
+
};
|
|
121
|
+
child.on("error", () => done(1));
|
|
122
|
+
child.on("close", (code) => done(code ?? 1));
|
|
115
123
|
});
|
|
116
124
|
}
|
|
117
125
|
/** Non-blocking probe, so the welcome screen can animate while agents connect. */
|
|
@@ -125,6 +133,24 @@ export async function probeAsync(adapter) {
|
|
|
125
133
|
}
|
|
126
134
|
return { adapter, state: "ready", detail: `${adapter.vendor} · ${adapter.accountNote ?? "ready"}` };
|
|
127
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* The install command for this platform, or null when there's no native one.
|
|
138
|
+
* An `npm install -g …` works everywhere; curl-pipe-bash doesn't on Windows.
|
|
139
|
+
*/
|
|
140
|
+
export function installCommand(a, platform = process.platform) {
|
|
141
|
+
if (platform !== "win32")
|
|
142
|
+
return a.install.cmd || null;
|
|
143
|
+
if (a.install.win !== undefined)
|
|
144
|
+
return a.install.win;
|
|
145
|
+
return /^npm (install|i) /.test(a.install.cmd) ? a.install.cmd : null;
|
|
146
|
+
}
|
|
147
|
+
/** What to say when installCommand() is null. */
|
|
148
|
+
export function noInstallHint(a, platform = process.platform) {
|
|
149
|
+
const docs = a.homepage ? `see ${a.homepage}` : `install ${a.bin} the way its docs say`;
|
|
150
|
+
return platform === "win32" && a.install.cmd ? `no Windows installer — ${docs}, or use it from WSL` : docs;
|
|
151
|
+
}
|
|
152
|
+
/** The sign-in command as one line, for messages ("codex login"). */
|
|
153
|
+
export const loginLine = (a) => (a.install.login ? a.install.login.join(" ") : null);
|
|
128
154
|
/** Probe the whole roster. */
|
|
129
155
|
export function probeAll() {
|
|
130
156
|
return ADAPTERS.map(probe);
|
package/dist/adapters/kiro.js
CHANGED
|
@@ -23,7 +23,7 @@ export const kiroAdapter = {
|
|
|
23
23
|
accountNote: "your Kiro login",
|
|
24
24
|
homepage: "https://kiro.dev/docs/cli/",
|
|
25
25
|
optional: true,
|
|
26
|
-
install: { cmd: "curl -fsSL https://cli.kiro.dev/install | bash", login: ["kiro-cli", "login"] },
|
|
26
|
+
install: { cmd: "curl -fsSL https://cli.kiro.dev/install | bash", win: null, login: ["kiro-cli", "login"] },
|
|
27
27
|
authProbe: {
|
|
28
28
|
cmd: ["kiro-cli", "whoami"],
|
|
29
29
|
ok: (r) => r.code === 0 && !/not logged in/i.test(r.stdout + r.stderr),
|
|
@@ -35,7 +35,7 @@ export const opencodeAdapter = {
|
|
|
35
35
|
accountNote: "your OpenCode providers",
|
|
36
36
|
homepage: "https://opencode.ai/docs/",
|
|
37
37
|
optional: true,
|
|
38
|
-
install: { cmd: "curl -fsSL https://opencode.ai/install | bash", login: ["opencode", "auth", "login"] },
|
|
38
|
+
install: { cmd: "curl -fsSL https://opencode.ai/install | bash", win: "npm install -g opencode-ai", login: ["opencode", "auth", "login"] },
|
|
39
39
|
authProbe: {
|
|
40
40
|
cmd: ["opencode", "--version"],
|
|
41
41
|
ok: (r) => r.code === 0,
|