huddle-cli 0.1.1 → 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 CHANGED
@@ -1,5 +1,43 @@
1
1
  # Changelog
2
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
+
3
41
  ## 0.1.1 — 2026-10-16
4
42
 
5
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).
package/QUICKSTART.md CHANGED
@@ -53,7 +53,21 @@ One-shot instead of the prompt: `huddle join "build a REST API for todos with te
53
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
54
  One-shot: `huddle join "fix this" --with error.log --with screenshot.png`, or `cat error.log | huddle join "fix this"`.
55
55
 
56
- **While it runs** (second terminal, same folder):
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):
57
71
 
58
72
  ```bash
59
73
  huddle view # live: who's doing what, messages, merges
@@ -64,8 +78,14 @@ huddle view # live: who's doing what, messages, merges
64
78
  ```bash
65
79
  huddle story # replay: plan, messages between agents, reviews, merges
66
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
67
85
  ```
68
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
+
69
89
  ## 3. Working with teammates (e.g. a hackathon)
70
90
 
71
91
  Everyone installs Huddle as in step 1, on their own machine, with their own agents.
@@ -77,6 +97,16 @@ Everyone installs Huddle as in step 1, on their own machine, with their own agen
77
97
 
78
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.
79
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
+
80
110
  ## Everyday commands
81
111
 
82
112
  | Command | What it does |
@@ -86,8 +116,11 @@ Not on the same Wi-Fi? Install [Tailscale](https://tailscale.com) on both machin
86
116
  | `huddle setup` | install and sign in to your agents (asks first) |
87
117
  | `huddle doctor` | check Node, git and your agents |
88
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) |
89
120
  | `huddle view` | watch a run live |
90
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`) |
91
124
  | `huddle board` | print the task board |
92
125
  | `add <file>` / `ctx` | (at the prompt) put a file on the shared screen / see what's on it |
93
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 |
package/README.md CHANGED
@@ -65,6 +65,7 @@ At the `huddle ›` prompt, type what you want built. Huddle plans it into paral
65
65
 
66
66
  - **shift+tab** switches mode: `manual` (approve the plan and each merge) → `auto` → `plan` (dry run)
67
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.
68
69
  - keep typing while agents work: `board`, questions, and new goals (queued) all work mid-run
69
70
  - `cd`, `!cmd`, `clear` behave like a shell; not a git repo? Huddle offers to set it up
70
71
 
@@ -78,6 +79,8 @@ At the `huddle ›` prompt, type what you want built. Huddle plans it into paral
78
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.
79
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.
80
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`.
81
84
 
82
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.
83
86
 
@@ -48,6 +48,9 @@ export const codexAdapter = {
48
48
  // calls under exec. The Plug is Huddle's own server, so pre-approve it.
49
49
  "-c",
50
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",
51
54
  // `codex exec` never stops to ask; its sandbox confines commands to the
52
55
  // worktree. Without network, the sandbox also refuses to listen on
53
56
  // localhost: seen live on the capstone, Codex couldn't run its own HTTP
@@ -0,0 +1,142 @@
1
+ // The bridge between a run and the dashboard (`huddle app`), which may be
2
+ // another process: the run can start in a terminal and be watched, answered
3
+ // and stopped from the browser, or start from the browser itself.
4
+ //
5
+ // .huddle/live.json the run's live state, rewritten every second: phase,
6
+ // narrator, each agent's task / % / tokens, the
7
+ // council so far, a question waiting on you
8
+ // .huddle/inbox/ what the dashboard sends the run: your answer to a
9
+ // question, a line for the agents, stop. One JSON file
10
+ // each; the run takes them within a second
11
+ // .huddle/app.json the dashboard is open (pid, url): a run with no
12
+ // terminal still asks you its questions there
13
+ //
14
+ // Files, not a socket: the council runs before the Room exists, and either
15
+ // side may start first. Everything here is local to the repo's .huddle/.
16
+ import fs from "node:fs";
17
+ import path from "node:path";
18
+ const dir = (root) => path.join(root, ".huddle");
19
+ export const livePath = (root) => path.join(dir(root), "live.json");
20
+ export const inboxDir = (root) => path.join(dir(root), "inbox");
21
+ export const appFile = (root) => path.join(dir(root), "app.json");
22
+ /** Is `pid` a process on this machine? */
23
+ export function alive(pid) {
24
+ if (!Number.isInteger(pid) || pid <= 0)
25
+ return false;
26
+ try {
27
+ process.kill(pid, 0);
28
+ return true;
29
+ }
30
+ catch (e) {
31
+ return e?.code === "EPERM"; // exists, owned by someone else
32
+ }
33
+ }
34
+ /** Write atomically: a reader never sees half a file. */
35
+ function writeJson(file, value) {
36
+ fs.mkdirSync(path.dirname(file), { recursive: true });
37
+ const tmp = `${file}.${process.pid}.tmp`;
38
+ fs.writeFileSync(tmp, JSON.stringify(value));
39
+ fs.renameSync(tmp, file);
40
+ }
41
+ function readJson(file) {
42
+ try {
43
+ return JSON.parse(fs.readFileSync(file, "utf8"));
44
+ }
45
+ catch {
46
+ return null;
47
+ }
48
+ }
49
+ export function writeLive(root, s) {
50
+ try {
51
+ writeJson(livePath(root), s);
52
+ }
53
+ catch {
54
+ /* a read-only or vanished .huddle: the dashboard just won't see this run */
55
+ }
56
+ }
57
+ /** The run in progress, or null: none, finished, or its process is gone (a crash, a kill -9). */
58
+ export function readLive(root) {
59
+ const s = readJson(livePath(root));
60
+ if (!s || s.done || !alive(s.pid))
61
+ return null;
62
+ return s;
63
+ }
64
+ let seq = 0;
65
+ /** The dashboard's side: queue a command for the run. */
66
+ export function postCommand(root, c) {
67
+ const d = inboxDir(root);
68
+ fs.mkdirSync(d, { recursive: true });
69
+ // Sorted by name = oldest first: two commands in one millisecond keep their order (seen on Linux).
70
+ const name = `${String(Date.now()).padStart(15, "0")}-${String(seq++).padStart(6, "0")}-${process.pid}.json`;
71
+ writeJson(path.join(d, name), c);
72
+ }
73
+ /** The run's side: every queued command, oldest first, each taken once. */
74
+ export function takeCommands(root) {
75
+ const d = inboxDir(root);
76
+ let names = [];
77
+ try {
78
+ names = fs.readdirSync(d).filter((n) => n.endsWith(".json")).sort();
79
+ }
80
+ catch {
81
+ return [];
82
+ }
83
+ const out = [];
84
+ for (const n of names) {
85
+ const f = path.join(d, n);
86
+ const c = readJson(f);
87
+ try {
88
+ fs.unlinkSync(f);
89
+ }
90
+ catch {
91
+ continue; // another taker got it
92
+ }
93
+ if (c && typeof c === "object" && ["answer", "say", "stop"].includes(c.type))
94
+ out.push(c);
95
+ }
96
+ return out;
97
+ }
98
+ /** Commands left from an earlier run must not steer this one. */
99
+ export function clearCommands(root) {
100
+ try {
101
+ for (const n of fs.readdirSync(inboxDir(root)))
102
+ fs.rmSync(path.join(inboxDir(root), n), { force: true });
103
+ }
104
+ catch {
105
+ /* no inbox yet */
106
+ }
107
+ }
108
+ export function writeAppInfo(root, info) {
109
+ writeJson(appFile(root), info);
110
+ }
111
+ export function clearAppInfo(root) {
112
+ const cur = readJson(appFile(root));
113
+ if (cur?.pid === process.pid)
114
+ fs.rmSync(appFile(root), { force: true });
115
+ }
116
+ /** The dashboard that's open on this repo, if any. */
117
+ export function openApp(root) {
118
+ const a = readJson(appFile(root));
119
+ return a && alive(a.pid) ? a : null;
120
+ }
121
+ /** The run's WHAT YOU GOT write-up, kept for the dashboard: .huddle/reports/<session>.md */
122
+ const reportFile = (root, session) => path.join(dir(root), "reports", `${session.replace(/[^\w-]/g, "_")}.md`);
123
+ export function saveReport(root, session, text) {
124
+ try {
125
+ fs.mkdirSync(path.dirname(reportFile(root, session)), { recursive: true });
126
+ fs.writeFileSync(reportFile(root, session), text);
127
+ }
128
+ catch {
129
+ /* the terminal had it; the dashboard just won't */
130
+ }
131
+ }
132
+ export function readReport(root, session) {
133
+ if (!session)
134
+ return null;
135
+ try {
136
+ return fs.readFileSync(reportFile(root, session), "utf8");
137
+ }
138
+ catch {
139
+ return null;
140
+ }
141
+ }
142
+ //# sourceMappingURL=live.js.map