huddle-cli 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/QUICKSTART.md +102 -0
  3. package/README.md +23 -4
  4. package/dist/adapters/amp.js +1 -1
  5. package/dist/adapters/antigravity.js +2 -0
  6. package/dist/adapters/claude.js +19 -3
  7. package/dist/adapters/codex.js +6 -1
  8. package/dist/adapters/cursor.js +12 -5
  9. package/dist/adapters/custom.js +4 -2
  10. package/dist/adapters/droid.js +1 -0
  11. package/dist/adapters/goose.js +2 -0
  12. package/dist/adapters/index.js +44 -18
  13. package/dist/adapters/kiro.js +1 -1
  14. package/dist/adapters/opencode.js +1 -1
  15. package/dist/attach/connect.js +4 -1
  16. package/dist/attach/guard.js +21 -2
  17. package/dist/attach/live.js +5 -1
  18. package/dist/attach/plug.js +14 -3
  19. package/dist/attach/screen.js +150 -0
  20. package/dist/attach/session.js +7 -2
  21. package/dist/cli.js +108 -40
  22. package/dist/mcp/context-tools.js +79 -0
  23. package/dist/mcp/server.js +38 -11
  24. package/dist/orchestrator/ask.js +13 -10
  25. package/dist/orchestrator/brain.js +13 -3
  26. package/dist/orchestrator/council.js +427 -86
  27. package/dist/orchestrator/goal.js +196 -0
  28. package/dist/orchestrator/handoff.js +2 -2
  29. package/dist/orchestrator/launcher.js +29 -13
  30. package/dist/orchestrator/learn.js +105 -0
  31. package/dist/orchestrator/planner.js +25 -7
  32. package/dist/orchestrator/qa.js +62 -14
  33. package/dist/orchestrator/radar.js +719 -0
  34. package/dist/orchestrator/scheduler.js +498 -39
  35. package/dist/orchestrator/takeover.js +155 -0
  36. package/dist/platform.js +273 -0
  37. package/dist/room/context.js +1285 -0
  38. package/dist/room/contract.js +132 -0
  39. package/dist/room/db.js +79 -16
  40. package/dist/room/followup.js +233 -0
  41. package/dist/room/identity.js +2 -2
  42. package/dist/room/lessons.js +275 -0
  43. package/dist/room/live.js +75 -20
  44. package/dist/room/mail.js +212 -0
  45. package/dist/room/progress.js +40 -1
  46. package/dist/room/server.js +169 -39
  47. package/dist/room/tunnel.js +6 -5
  48. package/dist/sandbox/sync.js +4 -4
  49. package/dist/sandbox/worktree.js +145 -14
  50. package/dist/ui/app.js +554 -133
  51. package/dist/ui/catalog.js +62 -24
  52. package/dist/ui/dashboard.js +194 -51
  53. package/dist/ui/doctor.js +15 -11
  54. package/dist/ui/lessons.js +81 -0
  55. package/dist/ui/paste.js +515 -0
  56. package/dist/ui/remote.js +11 -5
  57. package/dist/ui/setup.js +152 -0
  58. package/dist/ui/story.js +666 -69
  59. package/dist/ui/term.js +151 -16
  60. package/dist/ui/watch.js +145 -48
  61. package/package.json +6 -3
@@ -0,0 +1,152 @@
1
+ // `huddle setup`: everything a person would otherwise do by hand before the
2
+ // first goal, in one command — check Node and git, pick which agents you use,
3
+ // install the missing ones with this platform's command, then hand the
4
+ // terminal to each one's own sign-in, and check again.
5
+ //
6
+ // huddle setup asks: use each agent? install the missing ones?
7
+ // huddle setup --yes no questions for choices and installs
8
+ // (sign-ins still need you: they open a browser)
9
+ // huddle setup --agents=claude,codex only these (and they're marked "used")
10
+ //
11
+ // Huddle never sees a credential: sign-in is always the agent's own command.
12
+ import readline from "node:readline/promises";
13
+ import { spawnSync } from "node:child_process";
14
+ import { ADAPTERS, LEGACY, installCommand, loginLine } from "../adapters/index.js";
15
+ import { MIN_NODE } from "../preflight.js";
16
+ import { gitFix, nodeFix } from "../platform.js";
17
+ import { connectAgents, installAgent, installLine, runInteractive, setAgentChoice, signIn, skippedAgents } from "./app.js";
18
+ import { C, dot } from "./term.js";
19
+ function realDeps(rl) {
20
+ return {
21
+ ask: async (q, def = true) => {
22
+ if (!rl)
23
+ return def;
24
+ const a = (await rl.question(`${q} ${C.faint(def ? "[Y/n]" : "[y/N]")} `)).trim().toLowerCase();
25
+ return a === "" ? def : a.startsWith("y");
26
+ },
27
+ run: (cmd) => runInteractive(rl, cmd),
28
+ print: (l = "") => console.log(l),
29
+ probe: () => connectAgents(),
30
+ git: () => {
31
+ const r = spawnSync("git", ["--version"], { encoding: "utf8" });
32
+ return r.status === 0 ? (r.stdout.match(/\d+\.\d+(\.\d+)?/)?.[0] ?? "?") : null;
33
+ },
34
+ node: process.versions.node,
35
+ interactive: !!process.stdin.isTTY,
36
+ };
37
+ }
38
+ export async function runSetup(args, deps) {
39
+ const rl = deps ? null : process.stdin.isTTY ? readline.createInterface({ input: process.stdin, output: process.stdout }) : null;
40
+ try {
41
+ return await setup(args, deps ?? realDeps(rl));
42
+ }
43
+ finally {
44
+ rl?.close();
45
+ }
46
+ }
47
+ /** The flow, with every side effect in `d` (tests pass fakes). Exit code: 0 = at least one agent ready. */
48
+ export async function setup(args, d) {
49
+ const yesAll = args.includes("--yes") || args.includes("-y");
50
+ const pick = args.find((a) => a.startsWith("--agents="))?.split("=")[1];
51
+ const only = pick?.split(",").map((s) => s.trim()).filter(Boolean);
52
+ const ask = (q, def = true) => (yesAll || !d.interactive ? Promise.resolve(def) : d.ask(q, def));
53
+ // 1. Huddle's own needs. Node is checked by preflight before we get here, but say it.
54
+ d.print(`\n${C.bold("Huddle setup")}\n`);
55
+ d.print(` ${C.green("✓")} node ${d.node}${Number(d.node.split(".")[0]) < MIN_NODE ? C.red(` — needs ${MIN_NODE}+: ${nodeFix()}`) : ""}`);
56
+ const git = d.git();
57
+ if (git)
58
+ d.print(` ${C.green("✓")} git ${git}`);
59
+ else
60
+ d.print(` ${C.red("✗")} git is missing — Huddle gives every agent its own git worktree. Install it:\n ${gitFix()}`);
61
+ if (only) {
62
+ const known = new Set(ADAPTERS.map((a) => a.id));
63
+ const unknown = only.filter((id) => !known.has(id));
64
+ if (unknown.length) {
65
+ d.print(C.red(`✗ --agents: no agent called ${unknown.join(", ")}.`) + ` Known: ${[...known].join(", ")}`);
66
+ return 1;
67
+ }
68
+ setAgentChoice(only, true);
69
+ }
70
+ // 2. Which agents you use. Asked once per agent; --yes keeps your earlier choices.
71
+ let results = await d.probe();
72
+ const inScope = (r) => (only ? only.includes(r.adapter.id) : true);
73
+ if (!only && d.interactive && !yesAll) {
74
+ d.print(C.faint("\n Pick the agents you want Huddle to use. Skipped ones are never installed, asked about, or put to work."));
75
+ const skipped = skippedAgents();
76
+ const use = [];
77
+ const skip = [];
78
+ for (const r of results) {
79
+ const id = r.adapter.id;
80
+ const was = r.state === "skipped" ? r.was : r.state;
81
+ const note = was === "ready" ? "ready" : was === "not_authed" ? "installed, not signed in" : "not installed";
82
+ (await d.ask(` Use ${dot(id)} ${C.bold(id)} ${C.faint(`(${note})`)}?`, !skipped.has(id)) ? use : skip).push(id);
83
+ }
84
+ setAgentChoice(use, true);
85
+ setAgentChoice(skip, false);
86
+ if (skip.some((id) => !skipped.has(id)) || use.some((id) => skipped.has(id)))
87
+ results = await d.probe();
88
+ }
89
+ // 3. Install what's missing, with this platform's command.
90
+ const missing = results.filter((r) => r.state === "not_installed" && inScope(r) && (only || !LEGACY.has(r.adapter.id)));
91
+ if (missing.length) {
92
+ d.print(`\n ${C.bold("Not installed:")}`);
93
+ for (const r of missing)
94
+ d.print(` ${dot(r.adapter.id)} ${C.bold(r.adapter.id.padEnd(12))} ${C.faint(installCommand(r.adapter) ? "$ " : "")}${installLine(r.adapter)}`);
95
+ const installable = missing.filter((r) => installCommand(r.adapter));
96
+ let chosen = [];
97
+ if (installable.length) {
98
+ if (!d.interactive && !yesAll) {
99
+ d.print(C.faint("\n Run those yourself, or `huddle setup --yes` to have Huddle run them."));
100
+ }
101
+ else if (await ask(`\n Install all missing (${installable.map((r) => r.adapter.id).join(", ")})?`)) {
102
+ chosen = installable.map((r) => r.adapter);
103
+ }
104
+ else {
105
+ for (const r of installable)
106
+ if (await d.ask(` Install ${r.adapter.id}?`, false))
107
+ chosen.push(r.adapter);
108
+ }
109
+ }
110
+ let installed = false;
111
+ for (const a of chosen)
112
+ installed = installAgent(d, a) || installed;
113
+ if (installed)
114
+ results = await d.probe();
115
+ }
116
+ // 4. Sign in, one agent at a time: its own login, with the terminal handed over.
117
+ const signedOut = results.filter((r) => r.state === "not_authed" && inScope(r));
118
+ let signedIn = false;
119
+ for (const r of signedOut) {
120
+ const a = r.adapter;
121
+ const line = loginLine(a);
122
+ if (!line) {
123
+ d.print(`\n ${dot(a.id)} ${C.bold(a.id)} isn't signed in — ${r.detail}`);
124
+ continue;
125
+ }
126
+ if (!d.interactive) {
127
+ d.print(`\n ${dot(a.id)} ${C.bold(a.id)} isn't signed in — run: ${C.bold(line)}`);
128
+ continue;
129
+ }
130
+ // Even with --yes: a sign-in opens a browser, so the human is needed anyway — ask.
131
+ if (await d.ask(`\n ${dot(a.id)} ${C.bold(a.id)} isn't signed in. Sign in now? ${C.faint(`($ ${line})`)}`)) {
132
+ signIn(d, a);
133
+ signedIn = true;
134
+ }
135
+ }
136
+ if (signedIn)
137
+ results = await d.probe();
138
+ // 5. Where that leaves you.
139
+ const ready = results.filter((r) => r.state === "ready");
140
+ const rest = results.filter((r) => inScope(r) && (r.state === "not_authed" || (r.state === "not_installed" && !LEGACY.has(r.adapter.id))));
141
+ d.print("");
142
+ if (ready.length)
143
+ d.print(`${C.green("✓")} ${ready.length} agent${ready.length === 1 ? "" : "s"} ready (${ready.map((r) => r.adapter.id).join(", ")}) — cd into a repo and run ${C.bold("huddle")}`);
144
+ else
145
+ d.print(`${C.yellow("No agents ready yet.")} Install or sign in to one, then run ${C.bold("huddle setup")} again.`);
146
+ if (rest.length)
147
+ d.print(C.faint(` not ready: ${rest.map((r) => r.adapter.id).join(", ")} — run huddle setup again any time`));
148
+ if (!git)
149
+ d.print(C.red(` git is still needed: ${gitFix()}`));
150
+ return ready.length && git ? 0 : 1;
151
+ }
152
+ //# sourceMappingURL=setup.js.map