@hivefloor/cli 0.1.3 → 0.1.5

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 (38) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +2 -1
  3. package/dist/{chunk-HT5KXWXM.js → chunk-3N5JYP5O.js} +7 -3
  4. package/dist/{chunk-TH4ANHSG.js → chunk-4DRHR7LR.js} +365 -129
  5. package/dist/{chunk-DQV6FAJU.js → chunk-6MDJB3BV.js} +97 -26
  6. package/dist/{chunk-NLZXB26Y.js → chunk-6XVIEX75.js} +293 -71
  7. package/dist/{chunk-KHHSRIMU.js → chunk-C627OKTW.js} +2 -2
  8. package/dist/chunk-H6NYMF2D.js +50 -0
  9. package/dist/{chunk-T7TG6WA4.js → chunk-IJRHCDIG.js} +1 -1
  10. package/dist/{chunk-3YOTXIFP.js → chunk-IWIDKNWC.js} +109 -29
  11. package/dist/{chunk-E4V47VEV.js → chunk-J36OIJBM.js} +41 -23
  12. package/dist/{chunk-O6VSA2JT.js → chunk-MZGGJ6ZS.js} +87 -13
  13. package/dist/{chunk-YRNMECUF.js → chunk-RYGL6XU4.js} +5 -3
  14. package/dist/chunk-SOXRITAW.js +139 -0
  15. package/dist/{chunk-VNCQFFQO.js → chunk-TPMFQUAW.js} +102 -1
  16. package/dist/chunk-U6RRUAC2.js +229 -0
  17. package/dist/chunk-VX7SCIF2.js +65 -0
  18. package/dist/{chunk-O2TC3WSS.js → chunk-X6B6R32A.js} +1 -1
  19. package/dist/cli-JCBZICR3.js +243 -0
  20. package/dist/connect-2HLVPEOL.js +23 -0
  21. package/dist/{disconnect-FI76IEZJ.js → disconnect-WQMDCUXT.js} +41 -27
  22. package/dist/{hook-N4ZTQEWM.js → hook-AYJNRGIY.js} +25 -9
  23. package/dist/index.js +2 -2
  24. package/dist/{link-5WSZHKBE.js → link-ORT4LID6.js} +29 -16
  25. package/dist/{machine-connect-GQJ4SB3D.js → machine-connect-N52CKQEW.js} +21 -21
  26. package/dist/{mcp-EUBTVJNX.js → mcp-2SF6Y73M.js} +27 -30
  27. package/dist/{run-BEE2ATEM.js → run-ZAZ45MKQ.js} +22 -10
  28. package/dist/{status-SMAVPW4D.js → status-GOCFNO7K.js} +25 -13
  29. package/dist/{token-AUEAWQDH.js → token-EMM4AJAU.js} +15 -6
  30. package/dist/voice-F5MIS3IZ.js +262 -0
  31. package/dist/{where-7NI7UEEF.js → where-XFXRTP7E.js} +16 -7
  32. package/package.json +2 -2
  33. package/dist/chunk-2IMJECYD.js +0 -71
  34. package/dist/chunk-DWHMBUCA.js +0 -30
  35. package/dist/chunk-NKMVO5BP.js +0 -132
  36. package/dist/chunk-PY74LT2W.js +0 -41
  37. package/dist/cli-MAHYTCYV.js +0 -103
  38. package/dist/connect-QA3QIEGL.js +0 -23
@@ -117,6 +117,100 @@ function serializeJson(data, style) {
117
117
  }
118
118
  var isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
119
119
 
120
+ // src/tools.ts
121
+ import fs2 from "fs";
122
+ import path2 from "path";
123
+ var REAL_TOOLS = ["claude", "codex", "cursor", "gemini", "grok"];
124
+ var TOOL_LABEL = {
125
+ claude: "Claude Code",
126
+ codex: "Codex",
127
+ cursor: "Cursor",
128
+ gemini: "Gemini CLI",
129
+ grok: "Grok CLI",
130
+ other: "Other"
131
+ };
132
+ var BINARIES = {
133
+ claude: ["claude"],
134
+ codex: ["codex"],
135
+ gemini: ["gemini"],
136
+ grok: ["grok"]
137
+ };
138
+ function isTool(s) {
139
+ return s === "other" || REAL_TOOLS.includes(s);
140
+ }
141
+ function findBin(name, env) {
142
+ const exts = process.platform === "win32" ? ["", ".exe", ".cmd", ".bat"] : [""];
143
+ for (const dir of (env.PATH ?? "").split(path2.delimiter)) {
144
+ if (!dir) continue;
145
+ for (const ext of exts) {
146
+ const p = path2.join(dir, name + ext);
147
+ try {
148
+ if (fs2.statSync(p).isFile()) {
149
+ fs2.accessSync(p, fs2.constants.X_OK);
150
+ return p;
151
+ }
152
+ } catch {
153
+ }
154
+ }
155
+ }
156
+ return void 0;
157
+ }
158
+ function firstBin(names, env) {
159
+ for (const name of names) {
160
+ const bin = findBin(name, env);
161
+ if (bin) return bin;
162
+ }
163
+ return void 0;
164
+ }
165
+ function sameFile(a, b) {
166
+ try {
167
+ return fs2.realpathSync(a) === fs2.realpathSync(b);
168
+ } catch {
169
+ return false;
170
+ }
171
+ }
172
+ function agentIsGrok(agentPath, env) {
173
+ const grok = findBin("grok", env);
174
+ if (grok && sameFile(agentPath, grok)) return true;
175
+ return /(?:^|\/)\.grok(?:\/|$)/.test(agentPath.replace(/\\/g, "/").toLowerCase());
176
+ }
177
+ function hasCursorSignal(agentPath) {
178
+ let resolved = agentPath;
179
+ try {
180
+ resolved = fs2.realpathSync(agentPath);
181
+ } catch {
182
+ }
183
+ return resolved.replace(/\\/g, "/").toLowerCase().includes("cursor");
184
+ }
185
+ function agentIsCursor(agentPath, env) {
186
+ if (agentIsGrok(agentPath, env)) return false;
187
+ const cursor = findBin("cursor-agent", env);
188
+ if (cursor && sameFile(agentPath, cursor)) return true;
189
+ return hasCursorSignal(agentPath);
190
+ }
191
+ function cursorBin(env) {
192
+ const linked = findBin("cursor-agent", env);
193
+ if (linked) return linked;
194
+ const agent = findBin("agent", env);
195
+ return agent && agentIsCursor(agent, env) ? agent : void 0;
196
+ }
197
+ function detectTools(env) {
198
+ const found = [];
199
+ for (const tool of REAL_TOOLS) {
200
+ const bin = tool === "cursor" ? cursorBin(env) : firstBin(BINARIES[tool], env);
201
+ if (bin) found.push({ tool, bin });
202
+ }
203
+ return found;
204
+ }
205
+ function toolFromEnv(env) {
206
+ if (env.GROK_SESSION_ID) return "grok";
207
+ if (env.CLAUDECODE || env.CLAUDE_CODE_ENTRYPOINT) return "claude";
208
+ if (env.CODEX_SANDBOX || env.CODEX_SESSION_ID || env.CODEX_THREAD_ID || env.CODEX_CI) return "codex";
209
+ if (env.CURSOR_AGENT || env.CURSOR_TRACE_ID || env.CURSOR_SESSION_ID) return "cursor";
210
+ if (env.GEMINI_CLI || env.GEMINI_SESSION_ID) return "gemini";
211
+ return void 0;
212
+ }
213
+
120
214
  export {
121
215
  home,
122
216
  configDir,
@@ -139,5 +233,12 @@ export {
139
233
  ConfigError,
140
234
  readJsonFile,
141
235
  serializeJson,
142
- isRecord
236
+ isRecord,
237
+ REAL_TOOLS,
238
+ TOOL_LABEL,
239
+ isTool,
240
+ findBin,
241
+ agentIsCursor,
242
+ detectTools,
243
+ toolFromEnv
143
244
  };
@@ -0,0 +1,229 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ CLAUDE_EVENTS,
4
+ CODEX_EVENTS,
5
+ addCreated,
6
+ addHooks,
7
+ editJson,
8
+ enableFeatureHooks,
9
+ forgetCreated,
10
+ removeHooks,
11
+ restoreFeatureHooks,
12
+ wasCreated
13
+ } from "./chunk-6XVIEX75.js";
14
+ import {
15
+ claudeConfigDirs,
16
+ codexConfigDir,
17
+ profileKeys
18
+ } from "./chunk-J36OIJBM.js";
19
+ import {
20
+ atomicWrite,
21
+ backupOnce,
22
+ cliDir,
23
+ cliEntry,
24
+ dropBackupIfSame,
25
+ home,
26
+ isDir,
27
+ readText,
28
+ removeFile
29
+ } from "./chunk-TPMFQUAW.js";
30
+
31
+ // src/machine-hooks.ts
32
+ import fs from "fs";
33
+ import path from "path";
34
+ function installCliCopy(srcDir) {
35
+ if (!fs.existsSync(path.join(srcDir, "index.js"))) throw new Error(`cannot find the CLI bundle in ${srcDir}`);
36
+ const dest = cliDir();
37
+ fs.mkdirSync(dest, { recursive: true, mode: 448 });
38
+ for (const f of fs.readdirSync(srcDir)) {
39
+ if (!/\.(?:js|mjs)$/.test(f)) continue;
40
+ const tmp = path.join(dest, `${f}.${process.pid}.tmp`);
41
+ fs.copyFileSync(path.join(srcDir, f), tmp);
42
+ fs.renameSync(tmp, path.join(dest, f));
43
+ }
44
+ fs.writeFileSync(path.join(dest, "package.json"), '{"type":"module"}\n');
45
+ }
46
+ var removeCliCopy = () => fs.rmSync(cliDir(), { recursive: true, force: true });
47
+ function claudeMachineSpecs(node, profile) {
48
+ return CLAUDE_EVENTS.map((event) => ({
49
+ event,
50
+ ...event === "Notification" ? { matcher: "permission_prompt|idle_prompt" } : {},
51
+ // Exec form: no shell, no quoting. async: the hook never slows the agent down.
52
+ entry: { type: "command", command: node, args: [cliEntry(), "hook", "claude", event, "--profile", profile], async: true }
53
+ }));
54
+ }
55
+ var q = (s) => `"${s.replace(/(["\\$`])/g, "\\$1")}"`;
56
+ var CODEX_HOOK_CMD = "codex-hook.cmd";
57
+ var CMD_META = /[&|<>^%!]/;
58
+ var PS_META = /[$`;,']/;
59
+ function isWinPath(s) {
60
+ return /^[A-Za-z]:[\\/]|^\\\\|\\/.test(s);
61
+ }
62
+ function windowsCmdRules(paths) {
63
+ return process.platform === "win32" || paths.some(isWinPath);
64
+ }
65
+ function pathMod(s) {
66
+ return isWinPath(s) ? path.win32 : path;
67
+ }
68
+ function normalizeCmdPath(s) {
69
+ return isWinPath(s) ? path.win32.normalize(s) : s;
70
+ }
71
+ var hasCmdMeta = (s) => CMD_META.test(s);
72
+ var hasPsMeta = (s) => PS_META.test(s);
73
+ var bareClean = (s) => !/\s/.test(s) && !hasCmdMeta(s) && !hasPsMeta(s);
74
+ var doubleQuotedClean = (s) => !hasCmdMeta(s) && !hasPsMeta(s);
75
+ var qWinBody = (s) => `"${normalizeCmdPath(s).replace(/%/g, "%%").replace(/"/g, '""')}"`;
76
+ function shimCandidates(cliDirectory, homeDir) {
77
+ const m = pathMod(cliDirectory);
78
+ const cli = normalizeCmdPath(cliDirectory);
79
+ return [m.join(cli, CODEX_HOOK_CMD), m.join(m.dirname(cli), "codex-hook", CODEX_HOOK_CMD), m.join(homeDir, ".hivefloor", CODEX_HOOK_CMD)].map(normalizeCmdPath);
80
+ }
81
+ function codexShimPath(cliDirectory, homeDir) {
82
+ const ranked = shimCandidates(cliDirectory, homeDir);
83
+ const bare = ranked.find(bareClean);
84
+ if (bare) return bare;
85
+ const doubleOk = ranked.find(doubleQuotedClean);
86
+ if (doubleOk) return doubleOk;
87
+ const singleOk = ranked.find((p) => !hasCmdMeta(p));
88
+ if (singleOk) return singleOk;
89
+ if (windowsCmdRules(ranked)) {
90
+ throw new Error(
91
+ `cannot write the Codex Windows hook shim: every candidate contains a cmd metacharacter (& | < > ^ % !): ${ranked.join(", ")}. The shim was not written`
92
+ );
93
+ }
94
+ const beside = ranked[0];
95
+ if (beside !== void 0) return beside;
96
+ throw new Error("no Codex shim candidate");
97
+ }
98
+ function shimBesideCli(shim, cliDirectory) {
99
+ const m = pathMod(cliDirectory);
100
+ return m.normalize(m.dirname(shim)) === m.normalize(normalizeCmdPath(cliDirectory));
101
+ }
102
+ function codexHookCmd(node, script) {
103
+ const target = script ? qWinBody(script) : '"%~dp0index.js"';
104
+ return `@echo off\r
105
+ ${qWinBody(node)} ${target} %*\r
106
+ `;
107
+ }
108
+ function codexHookBody(node, cliDirectory, homeDir) {
109
+ const shim = codexShimPath(cliDirectory, homeDir);
110
+ const m = pathMod(cliDirectory);
111
+ const script = shimBesideCli(shim, cliDirectory) ? void 0 : m.join(normalizeCmdPath(cliDirectory), "index.js");
112
+ return codexHookCmd(node, script);
113
+ }
114
+ function codexCommandWindows(shim, event, profile) {
115
+ const win = normalizeCmdPath(shim);
116
+ if (hasCmdMeta(win) && !windowsCmdRules([win])) return void 0;
117
+ const tail = `hook codex ${event} --profile ${profile}`;
118
+ if (bareClean(win)) return `${win} ${tail}`;
119
+ if (!hasPsMeta(win)) return `& "${win.replace(/"/g, '""')}" ${tail}`;
120
+ return `& '${win.replace(/'/g, "''")}' ${tail}`;
121
+ }
122
+ function codexMachineSpecs(node, profile, shim = codexShimPath(cliDir(), home())) {
123
+ const tail = (event) => `hook codex ${event} --profile ${profile}`;
124
+ return CODEX_EVENTS.map((event) => {
125
+ const commandWindows = codexCommandWindows(shim, event, profile);
126
+ return {
127
+ event,
128
+ entry: {
129
+ type: "command",
130
+ command: `${q(node)} ${q(cliEntry())} ${tail(event)}`,
131
+ ...commandWindows ? { commandWindows } : {}
132
+ }
133
+ };
134
+ });
135
+ }
136
+ function installMachineHooks(node, opts) {
137
+ const res = { claude_dirs: [], lines: [], warnings: [] };
138
+ const dirs = claudeConfigDirs(opts.processDirs).filter(isDir);
139
+ const keys = profileKeys("claude", dirs);
140
+ for (const dir of dirs) {
141
+ const file = path.join(dir, "settings.json");
142
+ try {
143
+ editJson(file, (d) => (addHooks(d, "nested", claudeMachineSpecs(node, keys.get(dir)), "machine"), true));
144
+ res.claude_dirs.push(dir);
145
+ res.lines.push(`Claude Code hooks (${keys.get(dir)}): ${file}`);
146
+ } catch (e) {
147
+ res.warnings.push(e.message);
148
+ }
149
+ }
150
+ if (opts.codex && isDir(codexConfigDir(opts.env))) {
151
+ const dir = codexConfigDir(opts.env);
152
+ const key = profileKeys("codex", [dir]).get(dir);
153
+ try {
154
+ const dirCli = cliDir();
155
+ const shim = codexShimPath(dirCli, home());
156
+ fs.mkdirSync(path.dirname(shim), { recursive: true, mode: 448 });
157
+ atomicWrite(shim, codexHookBody(node, dirCli, home()));
158
+ const specs = codexMachineSpecs(node, key, shim);
159
+ editJson(path.join(dir, "hooks.json"), (d) => (addHooks(d, "nested", specs, "machine"), true));
160
+ editToml(path.join(dir, "config.toml"), (c) => enableFeatureHooks(c));
161
+ res.codex_dir = dir;
162
+ res.lines.push(`Codex hooks (${key}): ${path.join(dir, "hooks.json")}`);
163
+ if (specs.some((s) => s.entry.commandWindows === void 0)) {
164
+ res.warnings.push("Codex commandWindows was omitted: every shim path contains a cmd metacharacter (& | < > ^ % !). The POSIX hook command was written");
165
+ }
166
+ } catch (e) {
167
+ res.warnings.push(e.message);
168
+ }
169
+ }
170
+ return res;
171
+ }
172
+ function editToml(file, fn) {
173
+ const before = readText(file);
174
+ const next = fn(before ?? "");
175
+ if (next === (before ?? "")) return;
176
+ if (before === void 0) addCreated(file);
177
+ else if (!wasCreated(file)) backupOnce(file);
178
+ atomicWrite(file, next);
179
+ }
180
+ function removeShimFile(file) {
181
+ removeFile(file);
182
+ const dir = path.dirname(file);
183
+ const base = path.basename(dir);
184
+ if (base !== ".hivefloor" && base !== "codex-hook") return;
185
+ try {
186
+ if (fs.readdirSync(dir).length === 0) fs.rmdirSync(dir);
187
+ } catch {
188
+ }
189
+ }
190
+ function removeMachineHooks(inst) {
191
+ const lines = [];
192
+ for (const dir of inst.claude_dirs) {
193
+ const file = path.join(dir, "settings.json");
194
+ try {
195
+ if (editJson(file, (d) => removeHooks(d, "nested", "machine") > 0, { drop: true })) lines.push(`Claude Code hooks: removed from ${file}`);
196
+ } catch {
197
+ }
198
+ }
199
+ if (inst.codex_dir) {
200
+ const file = path.join(inst.codex_dir, "hooks.json");
201
+ try {
202
+ if (editJson(file, (d) => removeHooks(d, "nested", "machine") > 0, { drop: true })) lines.push(`Codex hooks: removed from ${file}`);
203
+ } catch {
204
+ }
205
+ for (const file2 of shimCandidates(cliDir(), home())) removeShimFile(file2);
206
+ const cfg = path.join(inst.codex_dir, "config.toml");
207
+ const before = readText(cfg);
208
+ if (before !== void 0) {
209
+ const next = restoreFeatureHooks(before);
210
+ if (next !== before) {
211
+ if (wasCreated(cfg) && next.trim() === "") {
212
+ removeFile(cfg);
213
+ forgetCreated(cfg);
214
+ } else {
215
+ atomicWrite(cfg, next);
216
+ dropBackupIfSame(cfg);
217
+ }
218
+ }
219
+ }
220
+ }
221
+ return lines;
222
+ }
223
+
224
+ export {
225
+ installCliCopy,
226
+ removeCliCopy,
227
+ installMachineHooks,
228
+ removeMachineHooks
229
+ };
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ assertTokenTransport
4
+ } from "./chunk-SOXRITAW.js";
5
+
6
+ // src/api.ts
7
+ var ApiError = class extends Error {
8
+ constructor(status, code, message) {
9
+ super(message);
10
+ this.status = status;
11
+ this.code = code;
12
+ }
13
+ status;
14
+ code;
15
+ };
16
+ async function apiRequest(ctx, host, path, opts = {}) {
17
+ if (opts.token) assertTokenTransport(host);
18
+ const headers = { accept: "application/json", ...opts.headers };
19
+ if (opts.token) headers.authorization = `Bearer ${opts.token}`;
20
+ if (opts.body !== void 0) headers["content-type"] = "application/json";
21
+ let res;
22
+ try {
23
+ res = await ctx.fetch(`${host}${path}`, {
24
+ method: opts.method ?? (opts.body !== void 0 ? "POST" : "GET"),
25
+ headers,
26
+ body: opts.body !== void 0 ? JSON.stringify(opts.body) : void 0,
27
+ redirect: "error",
28
+ signal: AbortSignal.timeout(opts.timeoutMs ?? 15e3)
29
+ });
30
+ } catch (e) {
31
+ throw networkError(host, e);
32
+ }
33
+ const text = await res.text();
34
+ let json = null;
35
+ try {
36
+ json = text ? JSON.parse(text) : null;
37
+ } catch {
38
+ json = null;
39
+ }
40
+ return { status: res.status, json };
41
+ }
42
+ function networkError(host, e) {
43
+ const err = e;
44
+ let why;
45
+ if (err?.name === "TimeoutError" || err?.name === "AbortError") why = "timed out";
46
+ else if (err?.cause?.code) why = err.cause.code;
47
+ else if (err?.code) why = err.code;
48
+ else why = err?.cause?.message ?? err?.message ?? "network error";
49
+ let name = host;
50
+ try {
51
+ name = new URL(host).host;
52
+ } catch {
53
+ }
54
+ return new ApiError(0, "network", `cannot reach ${name}: ${why}. Check --host or HIVEFLOOR_URL.`);
55
+ }
56
+ function errorOf(r) {
57
+ const j = r.json ?? {};
58
+ return new ApiError(r.status, typeof j.error === "string" ? j.error : "error", typeof j.message === "string" ? j.message : `HTTP ${r.status}`);
59
+ }
60
+
61
+ export {
62
+ ApiError,
63
+ apiRequest,
64
+ errorOf
65
+ };
@@ -5,7 +5,7 @@ import {
5
5
  removeFile,
6
6
  serviceLogFile,
7
7
  spoolFile
8
- } from "./chunk-VNCQFFQO.js";
8
+ } from "./chunk-TPMFQUAW.js";
9
9
 
10
10
  // src/spool.ts
11
11
  import fs from "fs";
@@ -0,0 +1,243 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ parseArgs
4
+ } from "./chunk-TKMIV7BG.js";
5
+ import {
6
+ VERSION
7
+ } from "./chunk-C627OKTW.js";
8
+ import {
9
+ ApiError
10
+ } from "./chunk-VX7SCIF2.js";
11
+ import {
12
+ defaultCtx
13
+ } from "./chunk-SOXRITAW.js";
14
+ import {
15
+ CLI_BIN_NAME,
16
+ CLI_INSTALL_COMMAND,
17
+ cliNpxCommand
18
+ } from "./chunk-4DRHR7LR.js";
19
+ import "./chunk-K3NQKI34.js";
20
+
21
+ // src/help.ts
22
+ var HOST_NOTE = `Host: --host URL, else HIVEFLOOR_URL, else the hosted instance. Self-hosting: pass --host or set HIVEFLOOR_URL.`;
23
+ var COMMAND_HELP = {
24
+ connect: `Usage: ${CLI_BIN_NAME} connect [options]
25
+
26
+ Connect this computer once: every agent on it (Claude profiles, Codex, ...) shows up.
27
+
28
+ Options:
29
+ --host URL Hivefloor server (default: HIVEFLOOR_URL, else the hosted instance)
30
+ --name NAME Display name for this machine (default: the computer's hostname)
31
+ --no-open Print the sign-in link instead of opening a browser
32
+ --no-service Do not install the background service
33
+ --yes, -y Skip confirmation prompts and accept defaults
34
+ --agent Connect one agent for the current git repo (MCP and tasks) instead of the machine
35
+ --tool NAME Like --agent for one tool: claude, codex, cursor, gemini, grok or other
36
+ --no-cli With --agent or --tool: write the tool's config files directly instead of running its own CLI (claude mcp add, ...)
37
+
38
+ ${HOST_NOTE}
39
+ `,
40
+ disconnect: `Usage: ${CLI_BIN_NAME} disconnect [options]
41
+
42
+ Remove everything connect added: hooks, MCP entries, service, stored tokens, and the machine on the server.
43
+
44
+ Options:
45
+ --host URL Only remove credentials for this server (a different server leaves this machine in place)
46
+ --tool NAME Remove one agent tool only: claude, codex, cursor, gemini or grok
47
+ `,
48
+ status: `Usage: ${CLI_BIN_NAME} status [options]
49
+
50
+ This machine and its service; the per-repo agent, its projects and live sessions.
51
+
52
+ Options:
53
+ --host URL Server to query (default: HIVEFLOOR_URL, else the last connected host)
54
+ --tool NAME Look at one agent tool's token
55
+
56
+ ${HOST_NOTE}
57
+ `,
58
+ where: `Usage: ${CLI_BIN_NAME} where [tool] [options]
59
+
60
+ Which account to run next on. Read only: nothing is switched.
61
+
62
+ Arguments:
63
+ tool claude, codex, ... (a tool or provider name)
64
+
65
+ Options:
66
+ --json Print the raw answer
67
+ --host URL Server to query (default: HIVEFLOOR_URL, else the last connected host)
68
+ --tool NAME Look at one agent tool's token
69
+
70
+ ${HOST_NOTE}
71
+ `,
72
+ service: `Usage: ${CLI_BIN_NAME} service run
73
+
74
+ Run the background service in the foreground. Your OS starts it for you after connect; you rarely run it by hand.
75
+ `,
76
+ link: `Usage: ${CLI_BIN_NAME} link [options]
77
+
78
+ Link this repo to another project. With neither --project nor --new, your browser opens to pick one.
79
+
80
+ Options:
81
+ --project ID Link to an existing project
82
+ --new NAME Create a project and link to it (not together with --project)
83
+ --path PREFIX Repo path inside the project (monorepo subfolder)
84
+ --host URL Server (default: HIVEFLOOR_URL, else the stored host)
85
+ --tool NAME Which agent tool's token to use
86
+ --no-open Print the sign-in link instead of opening a browser
87
+
88
+ ${HOST_NOTE}
89
+ `,
90
+ token: `Usage: ${CLI_BIN_NAME} token [options]
91
+
92
+ Print the stored token for shell use, for example: export HIVEFLOOR_TOKEN=$(${CLI_BIN_NAME} token)
93
+ It refuses to print to a terminal unless you pass --reveal.
94
+
95
+ Options:
96
+ --reveal Print to a terminal anyway
97
+ --host URL Host to look up the token for (default: HIVEFLOOR_URL, else the last connected host)
98
+ --tool NAME Which agent tool's token to print
99
+ `,
100
+ mcp: `Usage: ${CLI_BIN_NAME} mcp [options]
101
+
102
+ Stdio MCP server that proxies to Hivefloor, for tools that cannot send HTTP headers. Needs a stored token (run ${cliNpxCommand("connect")} first).
103
+
104
+ Options:
105
+ --url URL MCP endpoint (default: <host>/mcp)
106
+ --host URL Host that resolves the stored token
107
+ --tool NAME Which agent tool's token to use
108
+
109
+ ${HOST_NOTE}
110
+ `,
111
+ voice: `Usage: ${CLI_BIN_NAME} voice status
112
+ ${CLI_BIN_NAME} voice list [--agent-id ID]
113
+ ${CLI_BIN_NAME} voice say "<text>" --out FILE.wav [--voice ID | --agent-id AGENT_ID] [--speed N]
114
+
115
+ Speech from TalkBack's Kokoro voice, which runs on this computer (https://github.com/Errr0rr404/talkback).
116
+
117
+ status Is TalkBack's voice running? (reads ~/.talkback/voiced.json and pings it)
118
+ list Print the Kokoro voice ids. Does not contact TalkBack.
119
+ say Synthesize the text to a 24 kHz mono WAV file. Nothing is played.
120
+
121
+ Options for list:
122
+ --agent-id ID Print the voice and speed hivefloor gives that agent id
123
+
124
+ Options for say:
125
+ --out FILE Where to write the WAV (required)
126
+ --voice ID A Kokoro voice id, for example af_heart or bf_emma
127
+ --agent-id ID Use the stable voice and speed hivefloor gives that agent id
128
+ --speed N 0.5 to 2 (default: TalkBack's setting)
129
+
130
+ If TalkBack is not installed, status and say print the install line and exit 1. list does not contact TalkBack. Text for say is limited to 2000 characters.
131
+ `,
132
+ hook: `Usage: ${CLI_BIN_NAME} hook <tool> <event> [--profile KEY]
133
+
134
+ The hook runner that Claude Code, Codex and other tools call. It reads the event JSON on stdin, reports it to Hivefloor, and always exits 0 so a hook never fails the agent. You do not run it by hand; ${cliNpxCommand("connect")} installs the hook entries.
135
+
136
+ Arguments:
137
+ tool claude, codex, ...
138
+ event the tool's native event name, for example PreToolUse
139
+ --profile KEY Use this machine profile's token and host (set by machine connect)
140
+ `
141
+ };
142
+ function commandHelp(cmd) {
143
+ return Object.hasOwn(COMMAND_HELP, cmd) ? COMMAND_HELP[cmd] : void 0;
144
+ }
145
+
146
+ // src/cli.ts
147
+ var HELP = `hivefloor ${VERSION}
148
+
149
+ Usage: ${CLI_BIN_NAME} <command> [options]
150
+ ${cliNpxCommand("<command> [options]")} (no install)
151
+
152
+ Commands:
153
+ connect Connect this computer once: every agent on it shows up (Claude profiles, Codex, ...)
154
+ --host URL --name NAME --no-open --no-service --yes
155
+ --agent | --tool claude|codex|cursor|gemini|grok|other connect one agent for one repo (MCP and tasks) instead
156
+ disconnect Remove everything connect added, the machine on the server included (--tool <name> for one agent tool)
157
+ status This machine and its service; the per-repo agent, its projects and live sessions
158
+ where [tool] Which account to run next on (read only; --json for the raw answer)
159
+ service run: the background service (started by your OS; you rarely run it by hand)
160
+ link Link this repo to another project (--project ID | --new NAME [--path PREFIX])
161
+ token Print the stored token for shell use (--reveal to print to a terminal)
162
+ voice status | list [--agent-id ID] | say "<text>" --out file.wav: TalkBack's spoken voice on this computer
163
+ mcp Stdio MCP server that proxies to Hivefloor (for tools without HTTP header support)
164
+ hook <tool> <event>: the hook runner the tools call (reads JSON on stdin)
165
+ help Show this help (hivefloor <command> --help, or help <command>, for one command)
166
+ --version Print the version
167
+
168
+ Install once: ${CLI_INSTALL_COMMAND}
169
+ Host: https://hivefloor.worldofz.info. Self-hosting: pass --host URL or set HIVEFLOOR_URL.
170
+ `;
171
+ async function run(argv, ctx = defaultCtx()) {
172
+ const [cmd, ...rest] = argv;
173
+ if (cmd === "--version" || cmd === "-v" || cmd === "version") {
174
+ ctx.out(`${VERSION}
175
+ `);
176
+ return 0;
177
+ }
178
+ if (cmd === "help" && rest[0] !== void 0 && !rest[0].startsWith("-")) {
179
+ const h = rest[0] === "help" || rest[0] === "version" ? HELP : commandHelp(rest[0]);
180
+ if (h === void 0) {
181
+ ctx.err(`hivefloor: unknown command "${rest[0]}". Run "hivefloor help".
182
+ `);
183
+ return 2;
184
+ }
185
+ ctx.out(h);
186
+ return 0;
187
+ }
188
+ if (cmd === void 0 || cmd === "help" || cmd === "--help" || cmd === "-h") {
189
+ ctx.out(HELP);
190
+ return 0;
191
+ }
192
+ const p = parseArgs(rest);
193
+ if (p.flags.help) {
194
+ ctx.out(commandHelp(cmd) ?? HELP);
195
+ return 0;
196
+ }
197
+ try {
198
+ switch (cmd) {
199
+ case "connect":
200
+ return await (await import("./connect-2HLVPEOL.js")).connect(ctx, p);
201
+ case "disconnect":
202
+ return await (await import("./disconnect-WQMDCUXT.js")).disconnect(ctx, p);
203
+ case "status":
204
+ return await (await import("./status-GOCFNO7K.js")).status(ctx, p);
205
+ case "where":
206
+ return await (await import("./where-XFXRTP7E.js")).where(ctx, p);
207
+ case "service":
208
+ if (p.positionals[0] !== "run") {
209
+ ctx.err("hivefloor: usage: hivefloor service run\n");
210
+ return 2;
211
+ }
212
+ return await (await import("./run-ZAZ45MKQ.js")).runService({ ctx });
213
+ case "link":
214
+ return await (await import("./link-ORT4LID6.js")).link(ctx, p);
215
+ case "token":
216
+ return await (await import("./token-EMM4AJAU.js")).token(ctx, p);
217
+ case "voice":
218
+ return await (await import("./voice-F5MIS3IZ.js")).voice(ctx, p);
219
+ case "mcp":
220
+ return await (await import("./mcp-2SF6Y73M.js")).runMcpProxy(ctx, p);
221
+ case "hook": {
222
+ const { runHook, readStdin } = await import("./hook-AYJNRGIY.js");
223
+ await runHook(rest, { env: ctx.env, cwd: ctx.cwd, fetch: ctx.fetch, now: ctx.now, readStdin });
224
+ return 0;
225
+ }
226
+ default:
227
+ ctx.err(`hivefloor: unknown command "${cmd}". Run "hivefloor help".
228
+ `);
229
+ return 2;
230
+ }
231
+ } catch (e) {
232
+ if (e instanceof ApiError) ctx.err(`hivefloor: ${e.message}
233
+ `);
234
+ else ctx.err(`hivefloor: ${e.message}
235
+ `);
236
+ return 1;
237
+ }
238
+ }
239
+ export {
240
+ HELP,
241
+ VERSION,
242
+ run
243
+ };
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ connect,
4
+ connectAgent,
5
+ resolveHost
6
+ } from "./chunk-IWIDKNWC.js";
7
+ import "./chunk-3N5JYP5O.js";
8
+ import "./chunk-6XVIEX75.js";
9
+ import "./chunk-RYGL6XU4.js";
10
+ import "./chunk-6MDJB3BV.js";
11
+ import "./chunk-TKMIV7BG.js";
12
+ import "./chunk-C627OKTW.js";
13
+ import "./chunk-IJRHCDIG.js";
14
+ import "./chunk-VX7SCIF2.js";
15
+ import "./chunk-TPMFQUAW.js";
16
+ import "./chunk-SOXRITAW.js";
17
+ import "./chunk-4DRHR7LR.js";
18
+ import "./chunk-K3NQKI34.js";
19
+ export {
20
+ connect,
21
+ connectAgent,
22
+ resolveHost
23
+ };