hivecode-mcp 0.12.2 → 0.13.0

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 (62) hide show
  1. package/HIVE_RULES.md +11 -40
  2. package/LICENSE +21 -21
  3. package/README.md +421 -167
  4. package/core.js +91 -91
  5. package/docs/ALWAYS_ON.md +310 -0
  6. package/docs/GITHUB_WORKFLOW.md +334 -0
  7. package/docs/HIVE_RULES.md +41 -41
  8. package/docs/MCP.md +157 -118
  9. package/docs/QUICKSTART.md +266 -0
  10. package/docs/SELF_HOSTING.md +256 -256
  11. package/docs/SLACK_SETUP.md +450 -0
  12. package/docs/TROUBLESHOOTING.md +87 -0
  13. package/hive-activity.js +336 -310
  14. package/hive-await.js +207 -0
  15. package/hive-branch.js +686 -0
  16. package/hive-busy.js +88 -88
  17. package/hive-coord.js +110 -110
  18. package/hive-gate.js +135 -135
  19. package/hive-github.js +1904 -0
  20. package/hive-hook-install.js +179 -103
  21. package/hive-instructions.js +74 -66
  22. package/hive-listen.js +81 -0
  23. package/hive-mailbox.js +520 -386
  24. package/hive-mcp-http.js +580 -0
  25. package/hive-mcp.js +579 -137
  26. package/hive-mux-proto.js +58 -58
  27. package/hive-mux.js +350 -350
  28. package/hive-notify.js +233 -0
  29. package/hive-obligations.js +247 -247
  30. package/hive-pr-staleness.js +189 -189
  31. package/hive-pr.js +350 -0
  32. package/hive-privacy.js +327 -288
  33. package/hive-receipts.js +233 -233
  34. package/hive-relay-health.js +230 -225
  35. package/hive-room-key.js +209 -0
  36. package/hive-secrets.js +89 -0
  37. package/hive-session.js +114 -0
  38. package/hive-setup.js +558 -445
  39. package/hive-slack-agent.js +396 -0
  40. package/hive-slack.js +1809 -0
  41. package/hive-stdio.js +172 -168
  42. package/hive-stop-hook.js +293 -241
  43. package/hive-throttle.js +118 -118
  44. package/hive-time.js +71 -0
  45. package/hive-update.js +58 -58
  46. package/hive-version.js +110 -59
  47. package/hive-webhook.js +54 -0
  48. package/icr-merge.js +58 -58
  49. package/icr.js +851 -745
  50. package/lang-json.js +211 -211
  51. package/lang-ruby.js +230 -230
  52. package/lang-toml.js +136 -136
  53. package/lang-yaml.js +183 -183
  54. package/limits.js +413 -394
  55. package/package.json +189 -164
  56. package/reconcile.cjs +614 -477
  57. package/slack-app-manifest-agent.json +61 -0
  58. package/slack-app-manifest-inbound.json +36 -0
  59. package/slack-app-manifest.json +27 -0
  60. package/substrate.js +252 -252
  61. package/sync.js +1956 -177
  62. package/grm-link.cjs +0 -141
@@ -0,0 +1,54 @@
1
+ // hive-webhook -- one Slack or Discord incoming-webhook URL, and the rule for what may be sent to it.
2
+ //
3
+ // Shared by the GitHub watcher (hive-github.js) and the room notifier (hive-notify.js): the same
4
+ // URL works for both, and the same two promises hold for both.
5
+ // 1. Nothing with a key or token in it leaves: every message is scrubbed first.
6
+ // 2. A notification that fails is logged and dropped, never retried in a loop and never a reason
7
+ // to stop: it is a courtesy, the work is the point.
8
+ import { findSecrets } from './hive-secrets.js'
9
+
10
+ /** Every key or token in `text` replaced. Run on everything that goes to GitHub or a webhook. */
11
+ export function scrub(text) {
12
+ let s = String(text || '')
13
+ for (const f of findSecrets(s, { tokens: true })) s = s.split(f.value).join(`[${f.kind} removed]`)
14
+ return s
15
+ }
16
+
17
+ export function cleanError(e, url) {
18
+ let s = String(e && (e.message || e) || 'unknown error')
19
+ if (url) s = s.split(url).join('[webhook url removed]')
20
+ try {
21
+ const u = new URL(url)
22
+ if (u.pathname && u.pathname !== '/') s = s.split(u.pathname).join('[path removed]')
23
+ } catch { }
24
+ s = scrub(s)
25
+ s = s.replace(/Failed to parse URL from .+/i, 'Failed to parse URL')
26
+ return s
27
+ }
28
+
29
+ /** `send(text)` resolves true when the webhook took it. Slack wants {text}, Discord {content}; any
30
+ * other host gets Slack format ({text}). */
31
+ export function makeNotifier(url, { fetchImpl = globalThis.fetch, log = () => {} } = {}) {
32
+ if (!url) return async () => false
33
+ let host = ''
34
+ try { host = new URL(url).hostname.toLowerCase() } catch { host = '' }
35
+ const slack = /(^|\.)slack\.com$/.test(host)
36
+ const discord = /(^|\.)discord(app)?\.com$/.test(host)
37
+ const kind = discord ? 'discord' : slack ? 'slack' : 'other'
38
+ const send = async (text) => {
39
+ const msg = scrub(text)
40
+ // allowed_mentions: an issue titled "@everyone" must not page a whole Discord server.
41
+ const body = discord ? { content: msg.slice(0, 1900), allowed_mentions: { parse: [] } } : { text: msg }
42
+ try {
43
+ const r = await fetchImpl(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body), signal: AbortSignal.timeout(10000) })
44
+ if (!r.ok) { log(`notification not delivered: HTTP ${r.status}`); return false }
45
+ return true
46
+ } catch (e) {
47
+ log(`notification not delivered: ${cleanError(e, url)}`)
48
+ return false
49
+ }
50
+ }
51
+ send.host = host
52
+ send.kind = kind
53
+ return send
54
+ }
package/icr-merge.js CHANGED
@@ -1,58 +1,58 @@
1
- // icr-merge.js — the bridge that brings ICR into the live product, SAFELY.
2
- //
3
- // Hivecode's reconcile() merges concurrent edits with merge3() (a line-level 3-way
4
- // merge). merge3 is convergent: across many peers it always settles on one canonical
5
- // text. ICR's structural merge produces a DIFFERENT canonical form (it may reorder or
6
- // reformat). Mixing two merge algorithms in a live multi-peer sync means the same logical
7
- // content has two byte-forms, and peers ping-pong between them — which corrupts files
8
- // (duplicated/*dropped* lines). So we do NOT change merge bytes with ICR here.
9
- //
10
- // Instead ICR runs in ADVISORY mode: merge3 still produces the bytes (full convergence
11
- // preserved, identical to pre-ICR behavior), and ICR runs alongside purely to DETECT the
12
- // meaning-level problems a line merge can't see — a dangling reference, a rename, a
13
- // same-declaration clash — and surface them as a warning on the activity feed. This ships
14
- // ICR's core value (catching semantic breaks) with zero divergence risk.
15
- //
16
- // ICR's full structural/rename AUTO-MERGE lives in the library (icr.js) and is proven by
17
- // the test suite + fuzzer; turning it on in the product is a later step that needs a
18
- // format-preserving, single-canonical-form merge so peers can't diverge.
19
-
20
- import { structuralMerge, supports } from './icr.js'
21
- import { merge3 } from './core.js'
22
-
23
- // Turn ICR's machine conflict keys into a human sentence for the activity feed / chat.
24
- function describeConflicts(conflicts) {
25
- return (conflicts || []).map((c) => {
26
- if (c.startsWith('ref:')) return `'${c.slice(4)}' was removed or renamed but is still used`
27
- if (c.startsWith('fn:')) return `both sides changed function ${c.slice(3)}`
28
- if (c.startsWith('class:')) return `both sides changed class ${c.slice(6)}`
29
- if (c.startsWith('var:')) return `both sides changed ${c.slice(4)}`
30
- return `both sides changed ${c}`
31
- }).join('; ')
32
- }
33
-
34
- // Same shape as merge3 — { text, conflict } — plus an `icr` tag ('structural'|'rename')
35
- // and an optional `icrWarning`. ICR's format-preserving merge is convergent (symmetric +
36
- // fixed-point, verified by icr-converge-test.js), so its 'auto' result is used as the
37
- // merged bytes for supported files; merge3 covers conflicts, fallbacks, and other languages.
38
- export function icrMerge3(base, mine, theirs, relPath) {
39
- const lm = merge3(base, mine, theirs)
40
- if (!supports(relPath)) return lm
41
-
42
- let r
43
- try { r = structuralMerge(base, mine, theirs, { filename: relPath }) }
44
- catch { return lm } // ICR must never break a sync
45
-
46
- if (r.status === 'auto') {
47
- const renamed = r.renames && r.renames.length
48
- return {
49
- text: r.text, conflict: false, icr: renamed ? 'rename' : 'structural',
50
- icrWarning: renamed ? `auto-applied rename ${r.renames.join(', ')} — call sites updated` : undefined,
51
- }
52
- }
53
- // A meaning-level conflict the line merge can't represent (dangling ref / clash): keep
54
- // the safe line-merge bytes, but surface what ICR caught so it isn't shipped silently.
55
- if (r.status === 'semantic-conflict')
56
- return { ...lm, icrWarning: describeConflicts(r.conflicts) }
57
- return lm // 'fallback'
58
- }
1
+ // icr-merge.js — the bridge that brings ICR into the live product, SAFELY.
2
+ //
3
+ // Hivecode's reconcile() merges concurrent edits with merge3() (a line-level 3-way
4
+ // merge). merge3 is convergent: across many peers it always settles on one canonical
5
+ // text. ICR's structural merge produces a DIFFERENT canonical form (it may reorder or
6
+ // reformat). Mixing two merge algorithms in a live multi-peer sync means the same logical
7
+ // content has two byte-forms, and peers ping-pong between them — which corrupts files
8
+ // (duplicated/*dropped* lines). So we do NOT change merge bytes with ICR here.
9
+ //
10
+ // Instead ICR runs in ADVISORY mode: merge3 still produces the bytes (full convergence
11
+ // preserved, identical to pre-ICR behavior), and ICR runs alongside purely to DETECT the
12
+ // meaning-level problems a line merge can't see — a dangling reference, a rename, a
13
+ // same-declaration clash — and surface them as a warning on the activity feed. This ships
14
+ // ICR's core value (catching semantic breaks) with zero divergence risk.
15
+ //
16
+ // ICR's full structural/rename AUTO-MERGE lives in the library (icr.js) and is proven by
17
+ // the test suite + fuzzer; turning it on in the product is a later step that needs a
18
+ // format-preserving, single-canonical-form merge so peers can't diverge.
19
+
20
+ import { structuralMerge, supports } from './icr.js'
21
+ import { merge3 } from './core.js'
22
+
23
+ // Turn ICR's machine conflict keys into a human sentence for the activity feed / chat.
24
+ function describeConflicts(conflicts) {
25
+ return (conflicts || []).map((c) => {
26
+ if (c.startsWith('ref:')) return `'${c.slice(4)}' was removed or renamed but is still used`
27
+ if (c.startsWith('fn:')) return `both sides changed function ${c.slice(3)}`
28
+ if (c.startsWith('class:')) return `both sides changed class ${c.slice(6)}`
29
+ if (c.startsWith('var:')) return `both sides changed ${c.slice(4)}`
30
+ return `both sides changed ${c}`
31
+ }).join('; ')
32
+ }
33
+
34
+ // Same shape as merge3 — { text, conflict } — plus an `icr` tag ('structural'|'rename')
35
+ // and an optional `icrWarning`. ICR's format-preserving merge is convergent (symmetric +
36
+ // fixed-point, verified by icr-converge-test.js), so its 'auto' result is used as the
37
+ // merged bytes for supported files; merge3 covers conflicts, fallbacks, and other languages.
38
+ export function icrMerge3(base, mine, theirs, relPath) {
39
+ const lm = merge3(base, mine, theirs)
40
+ if (!supports(relPath)) return lm
41
+
42
+ let r
43
+ try { r = structuralMerge(base, mine, theirs, { filename: relPath }) }
44
+ catch { return lm } // ICR must never break a sync
45
+
46
+ if (r.status === 'auto') {
47
+ const renamed = r.renames && r.renames.length
48
+ return {
49
+ text: r.text, conflict: false, icr: renamed ? 'rename' : 'structural',
50
+ icrWarning: renamed ? `auto-applied rename ${r.renames.join(', ')} — call sites updated` : undefined,
51
+ }
52
+ }
53
+ // A meaning-level conflict the line merge can't represent (dangling ref / clash): keep
54
+ // the safe line-merge bytes, but surface what ICR caught so it isn't shipped silently.
55
+ if (r.status === 'semantic-conflict')
56
+ return { ...lm, icrWarning: describeConflicts(r.conflicts) }
57
+ return lm // 'fallback'
58
+ }