@hivefloor/cli 0.1.5 → 0.1.8

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 (48) hide show
  1. package/LICENSE +6 -20
  2. package/README.md +29 -8
  3. package/dist/chunk-2ERPDCOT.js +38 -0
  4. package/dist/chunk-4G362W6G.js +19 -0
  5. package/dist/chunk-5OJUJCE3.js +817 -0
  6. package/dist/{chunk-U6RRUAC2.js → chunk-5XXYQQG6.js} +53 -8
  7. package/dist/chunk-AGIYVFDQ.js +10984 -0
  8. package/dist/{chunk-RYGL6XU4.js → chunk-BLLPDPTL.js} +4 -2
  9. package/dist/{chunk-3N5JYP5O.js → chunk-FPJNGKT5.js} +5 -3
  10. package/dist/chunk-HW3KRNCO.js +201 -0
  11. package/dist/{chunk-4DRHR7LR.js → chunk-HWBUESB4.js} +1158 -196
  12. package/dist/{chunk-C627OKTW.js → chunk-K2IRTZH5.js} +7 -6
  13. package/dist/chunk-KQMPCV57.js +122 -0
  14. package/dist/{chunk-J36OIJBM.js → chunk-L6EFW2SJ.js} +14 -10
  15. package/dist/{chunk-IJRHCDIG.js → chunk-LX5A25O6.js} +4 -2
  16. package/dist/{chunk-MZGGJ6ZS.js → chunk-MYJCU5JL.js} +4 -2
  17. package/dist/{chunk-H6NYMF2D.js → chunk-NMUH5NXN.js} +6 -4
  18. package/dist/{chunk-VX7SCIF2.js → chunk-NRJONTFT.js} +3 -1
  19. package/dist/chunk-QBBKGORP.js +327 -0
  20. package/dist/{chunk-SOXRITAW.js → chunk-QGULSOQ2.js} +3 -0
  21. package/dist/{chunk-6MDJB3BV.js → chunk-QLMLB7RB.js} +17 -9
  22. package/dist/{chunk-TKMIV7BG.js → chunk-QPAFN47B.js} +3 -1
  23. package/dist/chunk-SBOEAOHD.js +48 -0
  24. package/dist/{chunk-IWIDKNWC.js → chunk-TIR4CV3P.js} +21 -17
  25. package/dist/{chunk-6XVIEX75.js → chunk-X2KH6AR4.js} +25 -303
  26. package/dist/chunk-XAXKZ572.js +106 -0
  27. package/dist/{chunk-TPMFQUAW.js → chunk-XPGL7VYE.js} +12 -0
  28. package/dist/{cli-JCBZICR3.js → cli-BFOJWJNG.js} +56 -20
  29. package/dist/connect-ZJ4JXBAJ.js +27 -0
  30. package/dist/control-6GHTZQJV.js +25 -0
  31. package/dist/{disconnect-WQMDCUXT.js → disconnect-3T25NTKX.js} +24 -17
  32. package/dist/{hook-AYJNRGIY.js → hook-TPSHBCWU.js} +277 -17
  33. package/dist/index.js +9 -6
  34. package/dist/{link-ORT4LID6.js → link-EAS5F5PC.js} +14 -11
  35. package/dist/{machine-connect-N52CKQEW.js → machine-connect-KEI4BRWF.js} +23 -17
  36. package/dist/mcp-LVIYR3EN.js +1882 -0
  37. package/dist/orch-KVRL2YA2.js +137 -0
  38. package/dist/{run-ZAZ45MKQ.js → run-JL2OZWK3.js} +1920 -28
  39. package/dist/{status-GOCFNO7K.js → status-QXOOJGGN.js} +14 -11
  40. package/dist/{token-EMM4AJAU.js → token-BFWWQIDA.js} +10 -7
  41. package/dist/{voice-F5MIS3IZ.js → voice-QXHFXRWG.js} +5 -3
  42. package/dist/{where-XFXRTP7E.js → where-CMUORQIO.js} +11 -8
  43. package/package.json +5 -6
  44. package/dist/chunk-JCLWCMEM.js +0 -8
  45. package/dist/chunk-K3NQKI34.js +0 -10
  46. package/dist/chunk-X6B6R32A.js +0 -303
  47. package/dist/connect-2HLVPEOL.js +0 -23
  48. package/dist/mcp-2SF6Y73M.js +0 -118
@@ -1,4 +1,9 @@
1
1
  #!/usr/bin/env node
2
+ import { createRequire as __hfCreateRequire } from 'node:module';
3
+ const require = __hfCreateRequire(import.meta.url);
4
+ import {
5
+ userBinDirs
6
+ } from "./chunk-XAXKZ572.js";
2
7
  import {
3
8
  atomicWrite,
4
9
  atomicWriteUtf16,
@@ -8,15 +13,15 @@ import {
8
13
  isRecord,
9
14
  readText,
10
15
  removeFile
11
- } from "./chunk-TPMFQUAW.js";
16
+ } from "./chunk-XPGL7VYE.js";
12
17
  import {
13
18
  normalizeHost,
14
19
  runCmd,
15
20
  sameOrigin
16
- } from "./chunk-SOXRITAW.js";
21
+ } from "./chunk-QGULSOQ2.js";
17
22
  import {
18
23
  cliNpxCommand
19
- } from "./chunk-4DRHR7LR.js";
24
+ } from "./chunk-HWBUESB4.js";
20
25
 
21
26
  // src/service/units.ts
22
27
  import os from "os";
@@ -27,8 +32,11 @@ var SCHTASKS_TASK = "Hivefloor Machine";
27
32
  var xml = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
28
33
  var sdEscape = (s) => s.replace(/(["\\$])/g, "\\$1").replace(/%/g, "%%");
29
34
  var sdQuote = (s) => /^[\w@+=:,./-]+$/.test(s) ? s : `"${sdEscape(s)}"`;
35
+ function unitPath(plan, system, homeDir = home()) {
36
+ if (/[\r\n\0]/.test(homeDir)) throw new Error("refusing to write a service unit: HOME contains a control character");
37
+ return [path.dirname(plan.node), ...userBinDirs(homeDir), ...system].filter((d, i, a) => a.indexOf(d) === i).join(":");
38
+ }
30
39
  function launchdPlist(plan) {
31
- const bin = path.dirname(plan.node);
32
40
  return `<?xml version="1.0" encoding="UTF-8"?>
33
41
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
34
42
  <plist version="1.0">
@@ -54,7 +62,7 @@ function launchdPlist(plan) {
54
62
  <key>EnvironmentVariables</key>
55
63
  <dict>
56
64
  <key>PATH</key>
57
- <string>${xml(`${bin}:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin`)}</string>
65
+ <string>${xml(unitPath(plan, ["/usr/bin", "/bin", "/usr/sbin", "/sbin"]))}</string>
58
66
  </dict>
59
67
  <key>StandardOutPath</key>
60
68
  <string>/dev/null</string>
@@ -66,14 +74,14 @@ function launchdPlist(plan) {
66
74
  }
67
75
  function systemdUnit(plan) {
68
76
  return `[Unit]
69
- Description=Hivefloor machine service
77
+ Description=HiveFloor machine service
70
78
  After=network-online.target
71
79
 
72
80
  [Service]
73
81
  ExecStart=${sdQuote(plan.node)} ${sdQuote(plan.script)} service run
74
82
  Restart=on-failure
75
83
  RestartSec=30
76
- Environment="PATH=${sdEscape(`${path.dirname(plan.node)}:/usr/local/bin:/usr/bin:/bin`)}"
84
+ Environment="PATH=${sdEscape(unitPath(plan, ["/usr/bin", "/bin"]))}"
77
85
 
78
86
  [Install]
79
87
  WantedBy=default.target
@@ -95,7 +103,7 @@ function windowsTaskXml(plan, user) {
95
103
  return `<?xml version="1.0" encoding="UTF-16"?>
96
104
  <Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
97
105
  <RegistrationInfo>
98
- <Description>Hivefloor machine service</Description>
106
+ <Description>HiveFloor machine service</Description>
99
107
  </RegistrationInfo>
100
108
  <Triggers>
101
109
  <LogonTrigger>
@@ -278,7 +286,7 @@ function writeCredentials(c) {
278
286
  }
279
287
  atomicWrite(file, JSON.stringify(c, null, 2) + "\n", 384);
280
288
  }
281
- var windowsAclError = () => new Error("could not restrict the Hivefloor config to your Windows user with icacls; the token was not kept");
289
+ var windowsAclError = () => new Error("could not restrict the HiveFloor config to your Windows user with icacls; the token was not kept");
282
290
  async function secureWindowsCredentials(run, username) {
283
291
  if (!username) throw new Error("could not determine the Windows user; the token was not kept");
284
292
  const dir = prepareConfigDir();
@@ -1,7 +1,9 @@
1
1
  #!/usr/bin/env node
2
+ import { createRequire as __hfCreateRequire } from 'node:module';
3
+ const require = __hfCreateRequire(import.meta.url);
2
4
 
3
5
  // src/args.ts
4
- var BOOLEAN = /* @__PURE__ */ new Set(["no-open", "yes", "y", "reveal", "help", "h", "json", "no-cli", "agent", "no-service"]);
6
+ var BOOLEAN = /* @__PURE__ */ new Set(["no-open", "yes", "y", "reveal", "help", "h", "json", "no-cli", "agent", "no-service", "permission-prompt", "full", "no-full"]);
5
7
  function parseArgs(argv) {
6
8
  const positionals = [];
7
9
  const flags = {};
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire as __hfCreateRequire } from 'node:module';
3
+ const require = __hfCreateRequire(import.meta.url);
4
+
5
+ // src/service/control/private.ts
6
+ import fs from "fs";
7
+ var uid = () => typeof process.getuid === "function" ? process.getuid() : void 0;
8
+ function ensurePrivateDir(dir) {
9
+ fs.mkdirSync(dir, { recursive: true, mode: 448 });
10
+ if (process.platform !== "win32") fs.chmodSync(dir, 448);
11
+ }
12
+ function privateDirTrouble(dir) {
13
+ let st;
14
+ try {
15
+ st = fs.lstatSync(dir);
16
+ } catch {
17
+ return `${dir} does not exist`;
18
+ }
19
+ if (st.isSymbolicLink() || !st.isDirectory()) return `${dir} is not a plain folder`;
20
+ if (process.platform === "win32") return void 0;
21
+ if (uid() !== void 0 && st.uid !== uid()) return `${dir} is owned by another user`;
22
+ if ((st.mode & 63) !== 0) return `${dir} is open to group or others`;
23
+ return void 0;
24
+ }
25
+ function privateFileTrouble(file) {
26
+ let st;
27
+ try {
28
+ st = fs.lstatSync(file);
29
+ } catch {
30
+ return `${file} does not exist`;
31
+ }
32
+ if (st.isSymbolicLink() || !st.isFile()) return `${file} is not a regular file`;
33
+ if (process.platform === "win32") return void 0;
34
+ if (uid() !== void 0 && st.uid !== uid()) return `${file} is owned by another user`;
35
+ if ((st.mode & 63) !== 0) return `${file} is open to group or others`;
36
+ return void 0;
37
+ }
38
+ function safeName(id) {
39
+ const s = id.replace(/[^A-Za-z0-9_.-]/g, "_").replace(/^\.+/, "_").slice(0, 120);
40
+ return s === "" ? "_" : s;
41
+ }
42
+
43
+ export {
44
+ ensurePrivateDir,
45
+ privateDirTrouble,
46
+ privateFileTrouble,
47
+ safeName
48
+ };
@@ -1,44 +1,48 @@
1
1
  #!/usr/bin/env node
2
+ import { createRequire as __hfCreateRequire } from 'node:module';
3
+ const require = __hfCreateRequire(import.meta.url);
2
4
  import {
3
5
  deviceLogin
4
- } from "./chunk-3N5JYP5O.js";
6
+ } from "./chunk-FPJNGKT5.js";
5
7
  import {
6
8
  RC_LINE,
7
9
  addRcLine,
8
- emptyReport,
9
- envTokenSharedWith,
10
10
  hasRcLine,
11
- installClaude,
12
- installCodex,
13
11
  installCursor,
14
12
  installGemini,
15
13
  installGrok,
16
- rcFileFor,
14
+ rcFileFor
15
+ } from "./chunk-QBBKGORP.js";
16
+ import {
17
+ emptyReport,
18
+ envTokenSharedWith,
19
+ installClaude,
20
+ installCodex,
17
21
  readManifest,
18
22
  toolReadsEnvToken,
19
23
  updateManifest
20
- } from "./chunk-6XVIEX75.js";
24
+ } from "./chunk-X2KH6AR4.js";
21
25
  import {
22
26
  detectRepo
23
- } from "./chunk-RYGL6XU4.js";
27
+ } from "./chunk-BLLPDPTL.js";
24
28
  import {
25
29
  defaultHost,
26
30
  loadToken,
27
31
  saveToken
28
- } from "./chunk-6MDJB3BV.js";
32
+ } from "./chunk-QLMLB7RB.js";
29
33
  import {
30
34
  checkUnknownFlags,
31
35
  flagStr
32
- } from "./chunk-TKMIV7BG.js";
36
+ } from "./chunk-QPAFN47B.js";
33
37
  import {
34
38
  VERSION
35
- } from "./chunk-C627OKTW.js";
39
+ } from "./chunk-K2IRTZH5.js";
36
40
  import {
37
41
  readMachine
38
- } from "./chunk-IJRHCDIG.js";
42
+ } from "./chunk-LX5A25O6.js";
39
43
  import {
40
44
  ApiError
41
- } from "./chunk-VX7SCIF2.js";
45
+ } from "./chunk-NRJONTFT.js";
42
46
  import {
43
47
  ConfigError,
44
48
  REAL_TOOLS,
@@ -47,19 +51,19 @@ import {
47
51
  findBin,
48
52
  isTool,
49
53
  toolFromEnv
50
- } from "./chunk-TPMFQUAW.js";
54
+ } from "./chunk-XPGL7VYE.js";
51
55
  import {
52
56
  hostFromEnv,
53
57
  hostFromFlag,
54
58
  machineName,
55
59
  normalizeHost,
56
60
  sameOrigin
57
- } from "./chunk-SOXRITAW.js";
61
+ } from "./chunk-QGULSOQ2.js";
58
62
  import {
59
63
  CLI_BIN_NAME,
60
64
  CLI_INSTALL_COMMAND,
61
65
  cliNpxCommand
62
- } from "./chunk-4DRHR7LR.js";
66
+ } from "./chunk-HWBUESB4.js";
63
67
 
64
68
  // src/connect.ts
65
69
  var INSTALLERS = {
@@ -252,7 +256,7 @@ async function connect(ctx, p) {
252
256
  return 2;
253
257
  }
254
258
  if (p.flags.agent || p.flags.tool !== void 0) return connectAgent(ctx, p);
255
- return (await import("./machine-connect-N52CKQEW.js")).connectMachine(ctx, p);
259
+ return (await import("./machine-connect-KEI4BRWF.js")).connectMachine(ctx, p);
256
260
  }
257
261
 
258
262
  export {
@@ -1,7 +1,9 @@
1
1
  #!/usr/bin/env node
2
+ import { createRequire as __hfCreateRequire } from 'node:module';
3
+ const require = __hfCreateRequire(import.meta.url);
2
4
  import {
3
5
  ApiError
4
- } from "./chunk-VX7SCIF2.js";
6
+ } from "./chunk-NRJONTFT.js";
5
7
  import {
6
8
  atomicWrite,
7
9
  backupOnce,
@@ -14,14 +16,11 @@ import {
14
16
  readText,
15
17
  removeFile,
16
18
  serializeJson
17
- } from "./chunk-TPMFQUAW.js";
19
+ } from "./chunk-XPGL7VYE.js";
18
20
  import {
19
21
  runCmd,
20
22
  sameOrigin
21
- } from "./chunk-SOXRITAW.js";
22
- import {
23
- cliNpxCommand
24
- } from "./chunk-4DRHR7LR.js";
23
+ } from "./chunk-QGULSOQ2.js";
25
24
 
26
25
  // src/manifest.ts
27
26
  function readManifest() {
@@ -71,54 +70,8 @@ function envTokenSharedWith(host, tool, shellExportsToken) {
71
70
  });
72
71
  }
73
72
 
74
- // src/shellrc.ts
75
- import fs from "fs";
76
- import path from "path";
77
- var RC_MARK = "# hivefloor";
78
- var RC_LINE = `export HIVEFLOOR_TOKEN="$(hivefloor token)" ${RC_MARK}`;
79
- function rcFileFor(env) {
80
- const shell = path.basename(env.SHELL ?? "");
81
- if (shell === "zsh") return path.join(home(), ".zshrc");
82
- if (shell === "bash") {
83
- const profile = path.join(home(), ".bash_profile");
84
- return process.platform === "darwin" && fs.existsSync(profile) ? profile : path.join(home(), ".bashrc");
85
- }
86
- return void 0;
87
- }
88
- function isRcLine(line) {
89
- const t = line.trim();
90
- return !t.startsWith("#") && t.includes("HIVEFLOOR_TOKEN") && t.includes("hivefloor token") && t.endsWith(RC_MARK);
91
- }
92
- function hasRcLine(file) {
93
- return (readText(file) ?? "").split("\n").some(isRcLine);
94
- }
95
- function addRcLine(file) {
96
- if (hasRcLine(file)) return false;
97
- const cur = readText(file);
98
- if (cur === void 0) addCreated(file);
99
- const sep = cur && !cur.endsWith("\n") ? "\n" : "";
100
- atomicWrite(file, `${cur ?? ""}${sep}${RC_LINE}
101
- `);
102
- return true;
103
- }
104
- function removeRcLine(file) {
105
- const cur = readText(file);
106
- if (cur === void 0) return false;
107
- const lines = cur.split("\n");
108
- const kept = lines.filter((l) => !isRcLine(l));
109
- if (kept.length === lines.length) return false;
110
- const next = kept.join("\n");
111
- if (next.trim() === "" && wasCreated(file)) {
112
- fs.rmSync(file, { force: true });
113
- forgetCreated(file);
114
- } else {
115
- atomicWrite(file, next);
116
- }
117
- return true;
118
- }
119
-
120
73
  // src/writers/common.ts
121
- import fs2 from "fs";
74
+ import fs from "fs";
122
75
  var emptyReport = () => ({ lines: [], warnings: [] });
123
76
  var CLI_COPY_RE = /hivefloor[/\\]cli[/\\]index\.js$/;
124
77
  var MACHINE_CMD_RE = /hivefloor[/\\]+cli[/\\]+index\.js"?\s+hook\s/;
@@ -197,7 +150,7 @@ function editJson(file, mutate, opts = {}) {
197
150
  function secureFile(file) {
198
151
  for (const f of [file, `${file}.hivefloor.bak`]) {
199
152
  try {
200
- fs2.chmodSync(f, 384);
153
+ fs.chmodSync(f, 384);
201
154
  } catch {
202
155
  }
203
156
  }
@@ -214,10 +167,10 @@ function dropBackupWithEntry(file, name) {
214
167
  }
215
168
 
216
169
  // src/writers/claude.ts
217
- import path2 from "path";
170
+ import path from "path";
218
171
  var CLAUDE_EVENTS = ["SessionStart", "SessionEnd", "UserPromptSubmit", "Stop", "Notification", "SubagentStart", "SubagentStop"];
219
- var claudeJson = () => path2.join(home(), ".claude.json");
220
- var claudeSettings = () => path2.join(home(), ".claude", "settings.json");
172
+ var claudeJson = () => path.join(home(), ".claude.json");
173
+ var claudeSettings = () => path.join(home(), ".claude", "settings.json");
221
174
  var MCP_NAME = "hivefloor";
222
175
  function claudeHookSpecs() {
223
176
  return CLAUDE_EVENTS.map((event) => ({
@@ -295,7 +248,7 @@ async function removeClaude(ctx, viaCli, keepHooks = false, viaFile = false) {
295
248
  }
296
249
 
297
250
  // src/writers/codex.ts
298
- import path3 from "path";
251
+ import path2 from "path";
299
252
 
300
253
  // src/writers/toml.ts
301
254
  var BEGIN = (name, sep) => `# hivefloor:begin ${name} sep=${sep}`;
@@ -423,9 +376,9 @@ function restoreFeatureHooks(content) {
423
376
 
424
377
  // src/writers/codex.ts
425
378
  var CODEX_EVENTS = ["SessionStart", "SessionEnd", "UserPromptSubmit", "Stop", "SubagentStart", "SubagentStop"];
426
- var codexDir = () => path3.join(process.env.CODEX_HOME || path3.join(home(), ".codex"));
427
- var codexConfig = () => path3.join(codexDir(), "config.toml");
428
- var codexHooks = () => path3.join(codexDir(), "hooks.json");
379
+ var codexDir = () => path2.join(process.env.CODEX_HOME || path2.join(home(), ".codex"));
380
+ var codexConfig = () => path2.join(codexDir(), "config.toml");
381
+ var codexHooks = () => path2.join(codexDir(), "hooks.json");
429
382
  function codexHookSpecs() {
430
383
  return CODEX_EVENTS.map((event) => ({ event, entry: { type: "command", command: `hivefloor hook codex ${event}` } }));
431
384
  }
@@ -530,7 +483,7 @@ async function installCodex(i) {
530
483
  report.lines.push(`Codex hooks: ${codexHooks()} (${CODEX_EVENTS.join(", ")})`);
531
484
  editToml(enableFeatureHooks);
532
485
  report.lines.push(`Codex: features.hooks = true in ${codexConfig()}`);
533
- report.warnings.push("Codex asks you to trust new hooks: run codex, open /hooks and approve the Hivefloor hooks once.");
486
+ report.warnings.push("Codex asks you to trust new hooks: run codex, open /hooks and approve the HiveFloor hooks once.");
534
487
  }
535
488
  }
536
489
  return { report, mcp };
@@ -552,234 +505,6 @@ async function removeCodex(ctx, viaCli, keepHooks = false) {
552
505
  return report;
553
506
  }
554
507
 
555
- // src/writers/cursor.ts
556
- import path4 from "path";
557
- var CURSOR_EVENTS = ["sessionStart", "sessionEnd", "beforeSubmitPrompt", "stop", "subagentStart", "subagentStop"];
558
- var cursorMcp = () => path4.join(home(), ".cursor", "mcp.json");
559
- var cursorHooks = () => path4.join(home(), ".cursor", "hooks.json");
560
- function cursorHookSpecs() {
561
- return CURSOR_EVENTS.map((event) => ({ event, entry: { command: `hivefloor hook cursor ${event}` } }));
562
- }
563
- async function installCursor(i) {
564
- const report = emptyReport();
565
- const auth = i.useEnvToken ? "Bearer ${env:HIVEFLOOR_TOKEN}" : `Bearer ${i.token}`;
566
- editJson(
567
- cursorMcp(),
568
- (d) => {
569
- const servers = isRecord(d.mcpServers) ? d.mcpServers : d.mcpServers = {};
570
- servers[MCP_NAME] = { url: i.mcpUrl, headers: { Authorization: auth } };
571
- return true;
572
- },
573
- i.useEnvToken ? {} : { mode: 384 }
574
- );
575
- report.lines.push(`Cursor MCP: ${cursorMcp()} (mcpServers.${MCP_NAME})`);
576
- i.onMcpRegistered?.("file");
577
- if (!i.mcpOnly) {
578
- editJson(cursorHooks(), (d) => {
579
- if (d.version === void 0) d.version = 1;
580
- addHooks(d, "flat", cursorHookSpecs());
581
- return true;
582
- });
583
- report.lines.push(`Cursor hooks: ${cursorHooks()} (${CURSOR_EVENTS.join(", ")})`);
584
- }
585
- return { report, mcp: "file" };
586
- }
587
- async function removeCursor() {
588
- const report = emptyReport();
589
- const changed = editJson(
590
- cursorMcp(),
591
- (d) => {
592
- if (!isRecord(d.mcpServers) || !(MCP_NAME in d.mcpServers)) return false;
593
- delete d.mcpServers[MCP_NAME];
594
- if (Object.keys(d.mcpServers).length === 0) delete d.mcpServers;
595
- return true;
596
- },
597
- { drop: true }
598
- );
599
- if (changed) report.lines.push(`Cursor MCP: removed from ${cursorMcp()}`);
600
- if (editJson(cursorHooks(), (d) => removeHooks(d, "flat") > 0, { drop: true })) report.lines.push(`Cursor hooks: removed from ${cursorHooks()}`);
601
- return report;
602
- }
603
-
604
- // src/writers/gemini.ts
605
- import path5 from "path";
606
- var GEMINI_EVENTS = ["SessionStart", "SessionEnd", "AfterAgent"];
607
- var geminiSettings = () => path5.join(home(), ".gemini", "settings.json");
608
- function geminiHookSpecs() {
609
- return GEMINI_EVENTS.map((event) => ({
610
- event,
611
- // Gemini hook entries carry a name: ours is "hivefloor", which is also how disconnect finds them.
612
- entry: { name: "hivefloor", type: "command", command: `hivefloor hook gemini ${event}` }
613
- }));
614
- }
615
- async function installGemini(i) {
616
- const report = emptyReport();
617
- const auth = i.useEnvToken ? "Bearer $HIVEFLOOR_TOKEN" : `Bearer ${i.token}`;
618
- let mcp = "file";
619
- const priorCli = !i.useEnvToken && readManifest().tools.gemini?.mcp === "cli";
620
- const bin = i.noCli ? void 0 : findBin("gemini", i.ctx.env);
621
- if (bin && (i.useEnvToken || priorCli)) {
622
- await runCmd(bin, ["mcp", "remove", MCP_NAME, "-s", "user"], { env: i.ctx.env });
623
- if (i.useEnvToken) {
624
- const r = await runCmd(bin, ["mcp", "add", MCP_NAME, i.mcpUrl, "-t", "http", "-s", "user", "-H", `Authorization: ${auth}`], { env: i.ctx.env });
625
- if (r.code === 0) {
626
- mcp = "cli";
627
- report.lines.push(`Gemini MCP: gemini mcp add (user scope) -> ${i.mcpUrl}`);
628
- }
629
- }
630
- }
631
- if (mcp === "file") {
632
- editJson(
633
- geminiSettings(),
634
- (d) => {
635
- const servers = isRecord(d.mcpServers) ? d.mcpServers : d.mcpServers = {};
636
- servers[MCP_NAME] = { httpUrl: i.mcpUrl, headers: { Authorization: auth } };
637
- return true;
638
- },
639
- i.useEnvToken ? {} : { mode: 384 }
640
- );
641
- if (!i.useEnvToken) secureFile(geminiSettings());
642
- report.lines.push(`Gemini MCP: ${geminiSettings()} (mcpServers.${MCP_NAME})`);
643
- }
644
- i.onMcpRegistered?.(mcp);
645
- if (!i.mcpOnly) {
646
- editJson(geminiSettings(), (d) => (addHooks(d, "nested", geminiHookSpecs()), true), i.useEnvToken ? {} : { mode: 384 });
647
- report.lines.push(`Gemini hooks: ${geminiSettings()} (${GEMINI_EVENTS.join(", ")})`);
648
- }
649
- return { report, mcp };
650
- }
651
- async function removeGemini(ctx, viaCli, viaFile = false) {
652
- const report = emptyReport();
653
- if (viaCli) {
654
- const bin = findBin("gemini", ctx.env);
655
- if (bin && (await runCmd(bin, ["mcp", "remove", MCP_NAME, "-s", "user"], { env: ctx.env })).code === 0) report.lines.push("Gemini MCP: gemini mcp remove");
656
- }
657
- const changed = editJson(
658
- geminiSettings(),
659
- (d) => {
660
- let did = removeHooks(d, "nested") > 0;
661
- if (viaFile && isRecord(d.mcpServers) && MCP_NAME in d.mcpServers) {
662
- delete d.mcpServers[MCP_NAME];
663
- if (Object.keys(d.mcpServers).length === 0) delete d.mcpServers;
664
- did = true;
665
- }
666
- return did;
667
- },
668
- { drop: true }
669
- );
670
- if (changed) report.lines.push(`Gemini: removed our entries from ${geminiSettings()}`);
671
- dropBackupWithEntry(geminiSettings(), MCP_NAME);
672
- return report;
673
- }
674
-
675
- // src/writers/grok.ts
676
- import path6 from "path";
677
- var grokConfig = () => path6.join(home(), ".grok", "config.toml");
678
- var HOOKS_LINE = "Grok hooks: none needed (Grok reads the Claude Code hooks in ~/.claude/settings.json; connect Claude Code too for live sessions)";
679
- function editGrokToml(fn, opts = {}) {
680
- const file = grokConfig();
681
- const before = readText(file);
682
- const next = fn(before ?? "");
683
- if (next === (before ?? "")) return false;
684
- if (opts.drop && wasCreated(file) && next.trim() === "") {
685
- removeFile(file);
686
- forgetCreated(file);
687
- return true;
688
- }
689
- if (before === void 0) addCreated(file);
690
- else if (!wasCreated(file)) backupOnce(file);
691
- atomicWrite(file, next, opts.mode);
692
- if (opts.drop) dropBackupIfSame(file);
693
- return true;
694
- }
695
- function grokMcpBlock(mcpUrl, auth) {
696
- return `[mcp_servers.${MCP_NAME}]
697
- url = ${tomlString(mcpUrl)}
698
- enabled = true
699
-
700
- [mcp_servers.${MCP_NAME}.headers]
701
- Authorization = ${tomlString(auth)}`;
702
- }
703
- var GROK_TABLE = `mcp_servers.${MCP_NAME}`;
704
- var ENV_AUTH = "Bearer ${HIVEFLOOR_TOKEN}";
705
- function outsideHasRawAuthorization(content) {
706
- const stripped = content.replace(/# hivefloor:begin[\s\S]*?# hivefloor:end [^\n]*(?:\n|$)/g, "");
707
- let inTable = false;
708
- for (const line of stripped.split("\n")) {
709
- const bare = line.replace(/\r$/, "");
710
- const header = /^\s*\[\s*([^\]]+?)\s*\]/.exec(bare);
711
- if (header) {
712
- const name = header[1].replace(/\s+/g, "");
713
- inTable = name === GROK_TABLE || name.startsWith(`${GROK_TABLE}.`);
714
- continue;
715
- }
716
- if (!inTable) continue;
717
- const auth = /Authorization\s*=\s*["']([^"']*)["']/.exec(bare);
718
- if (auth && auth[1] !== ENV_AUTH) return true;
719
- }
720
- return false;
721
- }
722
- async function dropOutsideServer(env, bin) {
723
- const file = grokConfig();
724
- const bak = `${file}.hivefloor.bak`;
725
- const before = readText(file);
726
- const beforeBak = readText(bak);
727
- const staleLive = before !== void 0 && hasOutsideTable(before, GROK_TABLE);
728
- const staleBak = beforeBak !== void 0 && hasOutsideTable(beforeBak, GROK_TABLE);
729
- if (!staleLive && !staleBak) return false;
730
- if (bin && staleLive) await runCmd(bin, ["mcp", "remove", MCP_NAME, "-s", "user"], { env });
731
- for (const f of [file, bak]) {
732
- const cur = readText(f);
733
- if (cur === void 0 || !hasOutsideTable(cur, GROK_TABLE)) continue;
734
- const next = removeOutsideTable(cur, GROK_TABLE);
735
- if (next.trim() === "") removeFile(f);
736
- else atomicWrite(f, next);
737
- }
738
- const left = readText(file);
739
- if (left !== void 0 && left.trim() === "") removeFile(file);
740
- return true;
741
- }
742
- async function installGrok(i) {
743
- const report = emptyReport();
744
- const auth = i.useEnvToken ? ENV_AUTH : `Bearer ${i.token}`;
745
- const bin = i.noCli ? void 0 : findBin("grok", i.ctx.env);
746
- const liveBefore = readText(grokConfig());
747
- const bakBefore = readText(`${grokConfig()}.hivefloor.bak`);
748
- const hadRaw = [liveBefore, bakBefore].some((t) => t !== void 0 && outsideHasRawAuthorization(t));
749
- const removed = await dropOutsideServer(i.ctx.env, bin);
750
- if (removed && hadRaw) report.warnings.push(`${grokConfig()} had [${GROK_TABLE}] outside our block; removed it so an older token does not stay on disk`);
751
- if (bin && i.useEnvToken) {
752
- await runCmd(bin, ["mcp", "remove", MCP_NAME, "-s", "user"], { env: i.ctx.env });
753
- const r = await runCmd(bin, ["mcp", "add", "--transport", "http", MCP_NAME, i.mcpUrl, "-s", "user", "--header", `Authorization: ${auth}`], { env: i.ctx.env });
754
- if (r.code === 0) {
755
- report.lines.push(`Grok MCP: grok mcp add (user scope) -> ${i.mcpUrl}`);
756
- if (!i.mcpOnly) report.lines.push(HOOKS_LINE);
757
- i.onMcpRegistered?.("cli");
758
- return { report, mcp: "cli" };
759
- }
760
- }
761
- const file = grokConfig();
762
- const changed = editGrokToml((c) => setBlock(c, "mcp", grokMcpBlock(i.mcpUrl, auth)), i.useEnvToken ? {} : { mode: 384 });
763
- if (!i.useEnvToken && (changed || (readText(file) ?? "").includes(i.token))) secureFile(file);
764
- if (changed || hasBlock(readText(file) ?? "", "mcp")) {
765
- report.lines.push(`Grok MCP: ${file} ([${GROK_TABLE}])`);
766
- if (!i.mcpOnly) report.lines.push(HOOKS_LINE);
767
- i.onMcpRegistered?.("file");
768
- return { report, mcp: "file" };
769
- }
770
- report.warnings.push(`Could not run the grok CLI. Add the server by hand: grok mcp add --transport http ${MCP_NAME} ${i.mcpUrl} -s user --header "Authorization: Bearer <token from ${cliNpxCommand("token --reveal")}>"`);
771
- return { report, mcp: "manual" };
772
- }
773
- async function removeGrok(ctx, viaCli, viaFile = false) {
774
- const report = emptyReport();
775
- if (viaCli) {
776
- const bin = findBin("grok", ctx.env);
777
- if (bin && (await runCmd(bin, ["mcp", "remove", MCP_NAME, "-s", "user"], { env: ctx.env })).code === 0) report.lines.push("Grok MCP: grok mcp remove");
778
- }
779
- if (viaFile && editGrokToml((c) => removeBlock(c, "mcp"), { drop: true })) report.lines.push(`Grok MCP: removed from ${grokConfig()}`);
780
- return report;
781
- }
782
-
783
508
  export {
784
509
  readManifest,
785
510
  updateManifest,
@@ -788,29 +513,26 @@ export {
788
513
  forgetCreated,
789
514
  toolReadsEnvToken,
790
515
  envTokenSharedWith,
791
- RC_LINE,
792
- rcFileFor,
793
- hasRcLine,
794
- addRcLine,
795
- removeRcLine,
796
516
  emptyReport,
797
517
  addHooks,
798
518
  removeHooks,
799
519
  editJson,
520
+ secureFile,
521
+ dropBackupWithEntry,
800
522
  CLAUDE_EVENTS,
801
523
  claudeJson,
524
+ MCP_NAME,
802
525
  installClaude,
803
526
  removeClaude,
527
+ tomlString,
528
+ setBlock,
529
+ removeBlock,
530
+ hasBlock,
531
+ removeOutsideTable,
532
+ hasOutsideTable,
804
533
  enableFeatureHooks,
805
534
  restoreFeatureHooks,
806
535
  CODEX_EVENTS,
807
536
  installCodex,
808
- removeCodex,
809
- installCursor,
810
- removeCursor,
811
- geminiSettings,
812
- installGemini,
813
- removeGemini,
814
- installGrok,
815
- removeGrok
537
+ removeCodex
816
538
  };