hivecode-mcp 0.14.1 → 0.14.2

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 (72) hide show
  1. package/HIVE_RULES.md +98 -98
  2. package/LICENSE +21 -21
  3. package/README.md +421 -421
  4. package/core.js +91 -91
  5. package/docs/ALWAYS_ON.md +310 -310
  6. package/docs/GITHUB_WORKFLOW.md +394 -394
  7. package/docs/HIVE_RULES.md +41 -41
  8. package/docs/MCP.md +157 -157
  9. package/docs/QUICKSTART.md +266 -266
  10. package/docs/SELF_HOSTING.md +256 -256
  11. package/docs/SLACK_SETUP.md +474 -474
  12. package/docs/TROUBLESHOOTING.md +105 -105
  13. package/hive-activity.js +336 -336
  14. package/hive-authors.js +46 -44
  15. package/hive-await.js +230 -230
  16. package/hive-branch.js +608 -607
  17. package/hive-busy.js +88 -88
  18. package/hive-coord.js +110 -110
  19. package/hive-gate.js +135 -135
  20. package/hive-github.js +3 -1
  21. package/hive-hook-install.js +179 -179
  22. package/hive-instructions.js +74 -74
  23. package/hive-land.js +695 -691
  24. package/hive-listen.js +81 -81
  25. package/hive-mailbox.js +520 -520
  26. package/hive-mcp-http.js +591 -591
  27. package/hive-mcp.js +33 -6
  28. package/hive-mux-proto.js +58 -58
  29. package/hive-mux.js +351 -351
  30. package/hive-notify.js +234 -233
  31. package/hive-obligations.js +247 -247
  32. package/hive-poll-proto.js +50 -50
  33. package/hive-pr-staleness.js +189 -189
  34. package/hive-pr.js +600 -512
  35. package/hive-privacy.js +327 -327
  36. package/hive-receipts.js +233 -233
  37. package/hive-relay-health.js +234 -234
  38. package/hive-repos.js +430 -430
  39. package/hive-room-key.js +210 -210
  40. package/hive-room-mode.js +1668 -1668
  41. package/hive-room-watcher.js +562 -561
  42. package/hive-secrets.js +89 -89
  43. package/hive-session.js +114 -114
  44. package/hive-setup.js +560 -560
  45. package/hive-slack-agent.js +408 -408
  46. package/hive-slack.js +2384 -2367
  47. package/hive-stdio.js +172 -172
  48. package/hive-stop-hook.js +293 -293
  49. package/hive-throttle.js +118 -118
  50. package/hive-time.js +71 -71
  51. package/hive-transport.js +578 -578
  52. package/hive-update.js +58 -58
  53. package/hive-version.js +110 -110
  54. package/hive-webhook.js +55 -54
  55. package/icr-merge.js +58 -58
  56. package/icr.js +851 -851
  57. package/lang-brace.js +305 -305
  58. package/lang-js.js +651 -651
  59. package/lang-json.js +211 -211
  60. package/lang-python.js +588 -588
  61. package/lang-ruby.js +230 -230
  62. package/lang-toml.js +136 -136
  63. package/lang-yaml.js +183 -183
  64. package/limits.js +413 -413
  65. package/package.json +1 -1
  66. package/reconcile.cjs +621 -621
  67. package/slack-app-manifest-agent.json +61 -61
  68. package/slack-app-manifest-inbound.json +36 -36
  69. package/slack-app-manifest.json +27 -27
  70. package/substrate.js +252 -252
  71. package/sync.js +169 -11
  72. package/token.js +201 -201
package/hive-listen.js CHANGED
@@ -1,81 +1,81 @@
1
- // hive-listen.js -- how an agent stays reachable after its turn ends, in one place.
2
- //
3
- // THE PROBLEM (Ganesh, 4 Oct 2026): agents join a room, answer once, and go deaf. The room already
4
- // had the answer, a background listener (hive-await.js): it blocks on the room's chat at no token
5
- // cost and EXITS when the agent is addressed, and Claude Code and Antigravity both start a new turn
6
- // when a background task ends. But it only works while one is running, and nothing made sure one
7
- // was. The agent was told the command once, on its first quiet hive_wait, and had to remember to
8
- // start it again after every wake. Agents forget, and a forgotten listener is invisible: the agent
9
- // still looks present.
10
- //
11
- // ARMED is the fact the rest of the system can now check instead of trusting memory: this machine
12
- // has a hive-await process for <name> in <room>, alive now, in the agent's own session. The listener
13
- // writes it to a file and refreshes it; it is never a network question, because the Stop hook that
14
- // reads it has a hard timeout and must work with the relay down.
15
- //
16
- // "CAN'T TELL" READS AS NOT ARMED. A false "armed" is a deaf agent nobody knows is deaf. A false
17
- // "not armed" costs one extra start, and the newest listener replaces the old one (hive-await.js),
18
- // so it never becomes two.
19
- import fs from 'fs'
20
- import os from 'os'
21
- import path from 'path'
22
- import { fileURLToPath } from 'url'
23
-
24
- // Claude Code stops a background task after about 30 minutes (measured 2 Oct: started 03:46,
25
- // killed 04:16) and then tells the agent not to restart it; a listener that ends itself on idle
26
- // can be restarted.
27
- export const LISTEN_IDLE_MIN = 25
28
- export const AWAIT_JS = path.join(path.dirname(fileURLToPath(import.meta.url)), 'hive-await.js').replace(/\\/g, '/')
29
-
30
- // TEMP on Windows, TMPDIR on macOS. On Linux usually NEITHER is set, and the listener used to fall
31
- // back to '.', the folder it was started in: its cursor landed in the agent's project folder, and a
32
- // hook started from a different folder could never find it. os.tmpdir() is the same for both.
33
- export function awaitDir(name, env = process.env) {
34
- return (env.TEMP || env.TMPDIR || os.tmpdir()) + '/hive-await-' + name
35
- }
36
- const safe = (s) => String(s).replace(/[^\w.-]/g, '_')
37
- export function armedFile(name, room, env = process.env) {
38
- return path.join(awaitDir(name, env), `armed-${safe(room)}.json`)
39
- }
40
-
41
- // The listener refreshes the file every BEAT_MS and deletes it when it exits. A process killed
42
- // outright cannot delete it, so a file older than FRESH_MS is not believed either. A listener that
43
- // has not reached the relay within SYNC_GRACE_MS is not believed: one refused for want of a token
44
- // is alive, refreshed, and hears nothing.
45
- export const BEAT_MS = Number(process.env.HIVE_AWAIT_BEAT_MS) || 10000
46
- export const FRESH_MS = Math.max(45000, BEAT_MS * 4)
47
- export const SYNC_GRACE_MS = 30000
48
-
49
- export function readArmed(name, room, env = process.env) {
50
- try { return JSON.parse(fs.readFileSync(armedFile(name, room, env), 'utf8')) } catch { return null }
51
- }
52
-
53
- const alive = (pid) => { try { process.kill(pid, 0); return true } catch (e) { return e.code === 'EPERM' } }
54
-
55
- /** Is a listener for `name` in `room` running for this agent? Pass `session` when the caller knows
56
- * its app's session id: a listener started in another session wakes that session, not this one. */
57
- export function isArmed({ name, room, session = '' }, { env = process.env, now = Date.now() } = {}) {
58
- const a = readArmed(name, room, env)
59
- if (!a || !Number(a.pid)) return false
60
- if (now - (Number(a.beat) || 0) > FRESH_MS) return false
61
- if (!a.synced && now - (Number(a.at) || 0) > SYNC_GRACE_MS) return false
62
- if (session && a.session && a.session !== session) return false
63
- return alive(Number(a.pid))
64
- }
65
-
66
- /** The exact command an agent runs to listen. Forward slashes: bash and PowerShell both accept them. */
67
- export function listenCommand({ relay, room, name, idleMin = LISTEN_IDLE_MIN, app = '', node = process.execPath, awaitJs = AWAIT_JS }) {
68
- return `"${String(node).replace(/\\/g, '/')}" "${awaitJs}" "${relay}|${room}" "${name}" ${idleMin}${app ? ` "${app}"` : ''}`
69
- }
70
-
71
- // Each app is told in its own tool's words; an app nobody has measured gets no order, because an
72
- // order it cannot carry out is a false "listening".
73
- export function listenHow(app) {
74
- if (/claude-code/i.test(app)) return 'Call your Bash tool now, with run_in_background: true, on this command.'
75
- if (/antigravity/i.test(app)) return 'Call your run_command tool now on this command, with WaitMsBeforeAsync: 1000, so it runs in the background.'
76
- return ''
77
- }
78
-
79
- export function listenOrder(how, cmd, idleMin = LISTEN_IDLE_MIN) {
80
- return `[BEFORE YOU END THIS TURN] Ending it stops you hearing this room. ${how} It ends, and that wakes you, when someone addresses you or after ${idleMin} quiet minutes; when it ends, run it again:\n${cmd}`
81
- }
1
+ // hive-listen.js -- how an agent stays reachable after its turn ends, in one place.
2
+ //
3
+ // THE PROBLEM (Ganesh, 4 Oct 2026): agents join a room, answer once, and go deaf. The room already
4
+ // had the answer, a background listener (hive-await.js): it blocks on the room's chat at no token
5
+ // cost and EXITS when the agent is addressed, and Claude Code and Antigravity both start a new turn
6
+ // when a background task ends. But it only works while one is running, and nothing made sure one
7
+ // was. The agent was told the command once, on its first quiet hive_wait, and had to remember to
8
+ // start it again after every wake. Agents forget, and a forgotten listener is invisible: the agent
9
+ // still looks present.
10
+ //
11
+ // ARMED is the fact the rest of the system can now check instead of trusting memory: this machine
12
+ // has a hive-await process for <name> in <room>, alive now, in the agent's own session. The listener
13
+ // writes it to a file and refreshes it; it is never a network question, because the Stop hook that
14
+ // reads it has a hard timeout and must work with the relay down.
15
+ //
16
+ // "CAN'T TELL" READS AS NOT ARMED. A false "armed" is a deaf agent nobody knows is deaf. A false
17
+ // "not armed" costs one extra start, and the newest listener replaces the old one (hive-await.js),
18
+ // so it never becomes two.
19
+ import fs from 'fs'
20
+ import os from 'os'
21
+ import path from 'path'
22
+ import { fileURLToPath } from 'url'
23
+
24
+ // Claude Code stops a background task after about 30 minutes (measured 2 Oct: started 03:46,
25
+ // killed 04:16) and then tells the agent not to restart it; a listener that ends itself on idle
26
+ // can be restarted.
27
+ export const LISTEN_IDLE_MIN = 25
28
+ export const AWAIT_JS = path.join(path.dirname(fileURLToPath(import.meta.url)), 'hive-await.js').replace(/\\/g, '/')
29
+
30
+ // TEMP on Windows, TMPDIR on macOS. On Linux usually NEITHER is set, and the listener used to fall
31
+ // back to '.', the folder it was started in: its cursor landed in the agent's project folder, and a
32
+ // hook started from a different folder could never find it. os.tmpdir() is the same for both.
33
+ export function awaitDir(name, env = process.env) {
34
+ return (env.TEMP || env.TMPDIR || os.tmpdir()) + '/hive-await-' + name
35
+ }
36
+ const safe = (s) => String(s).replace(/[^\w.-]/g, '_')
37
+ export function armedFile(name, room, env = process.env) {
38
+ return path.join(awaitDir(name, env), `armed-${safe(room)}.json`)
39
+ }
40
+
41
+ // The listener refreshes the file every BEAT_MS and deletes it when it exits. A process killed
42
+ // outright cannot delete it, so a file older than FRESH_MS is not believed either. A listener that
43
+ // has not reached the relay within SYNC_GRACE_MS is not believed: one refused for want of a token
44
+ // is alive, refreshed, and hears nothing.
45
+ export const BEAT_MS = Number(process.env.HIVE_AWAIT_BEAT_MS) || 10000
46
+ export const FRESH_MS = Math.max(45000, BEAT_MS * 4)
47
+ export const SYNC_GRACE_MS = 30000
48
+
49
+ export function readArmed(name, room, env = process.env) {
50
+ try { return JSON.parse(fs.readFileSync(armedFile(name, room, env), 'utf8')) } catch { return null }
51
+ }
52
+
53
+ const alive = (pid) => { try { process.kill(pid, 0); return true } catch (e) { return e.code === 'EPERM' } }
54
+
55
+ /** Is a listener for `name` in `room` running for this agent? Pass `session` when the caller knows
56
+ * its app's session id: a listener started in another session wakes that session, not this one. */
57
+ export function isArmed({ name, room, session = '' }, { env = process.env, now = Date.now() } = {}) {
58
+ const a = readArmed(name, room, env)
59
+ if (!a || !Number(a.pid)) return false
60
+ if (now - (Number(a.beat) || 0) > FRESH_MS) return false
61
+ if (!a.synced && now - (Number(a.at) || 0) > SYNC_GRACE_MS) return false
62
+ if (session && a.session && a.session !== session) return false
63
+ return alive(Number(a.pid))
64
+ }
65
+
66
+ /** The exact command an agent runs to listen. Forward slashes: bash and PowerShell both accept them. */
67
+ export function listenCommand({ relay, room, name, idleMin = LISTEN_IDLE_MIN, app = '', node = process.execPath, awaitJs = AWAIT_JS }) {
68
+ return `"${String(node).replace(/\\/g, '/')}" "${awaitJs}" "${relay}|${room}" "${name}" ${idleMin}${app ? ` "${app}"` : ''}`
69
+ }
70
+
71
+ // Each app is told in its own tool's words; an app nobody has measured gets no order, because an
72
+ // order it cannot carry out is a false "listening".
73
+ export function listenHow(app) {
74
+ if (/claude-code/i.test(app)) return 'Call your Bash tool now, with run_in_background: true, on this command.'
75
+ if (/antigravity/i.test(app)) return 'Call your run_command tool now on this command, with WaitMsBeforeAsync: 1000, so it runs in the background.'
76
+ return ''
77
+ }
78
+
79
+ export function listenOrder(how, cmd, idleMin = LISTEN_IDLE_MIN) {
80
+ return `[BEFORE YOU END THIS TURN] Ending it stops you hearing this room. ${how} It ends, and that wakes you, when someone addresses you or after ${idleMin} quiet minutes; when it ends, run it again:\n${cmd}`
81
+ }