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.
- package/HIVE_RULES.md +11 -40
- package/LICENSE +21 -21
- package/README.md +421 -167
- package/core.js +91 -91
- package/docs/ALWAYS_ON.md +310 -0
- package/docs/GITHUB_WORKFLOW.md +334 -0
- package/docs/HIVE_RULES.md +41 -41
- package/docs/MCP.md +157 -118
- package/docs/QUICKSTART.md +266 -0
- package/docs/SELF_HOSTING.md +256 -256
- package/docs/SLACK_SETUP.md +450 -0
- package/docs/TROUBLESHOOTING.md +87 -0
- package/hive-activity.js +336 -310
- package/hive-await.js +207 -0
- package/hive-branch.js +686 -0
- package/hive-busy.js +88 -88
- package/hive-coord.js +110 -110
- package/hive-gate.js +135 -135
- package/hive-github.js +1904 -0
- package/hive-hook-install.js +179 -103
- package/hive-instructions.js +74 -66
- package/hive-listen.js +81 -0
- package/hive-mailbox.js +520 -386
- package/hive-mcp-http.js +580 -0
- package/hive-mcp.js +579 -137
- package/hive-mux-proto.js +58 -58
- package/hive-mux.js +350 -350
- package/hive-notify.js +233 -0
- package/hive-obligations.js +247 -247
- package/hive-pr-staleness.js +189 -189
- package/hive-pr.js +350 -0
- package/hive-privacy.js +327 -288
- package/hive-receipts.js +233 -233
- package/hive-relay-health.js +230 -225
- package/hive-room-key.js +209 -0
- package/hive-secrets.js +89 -0
- package/hive-session.js +114 -0
- package/hive-setup.js +558 -445
- package/hive-slack-agent.js +396 -0
- package/hive-slack.js +1809 -0
- package/hive-stdio.js +172 -168
- package/hive-stop-hook.js +293 -241
- package/hive-throttle.js +118 -118
- package/hive-time.js +71 -0
- package/hive-update.js +58 -58
- package/hive-version.js +110 -59
- package/hive-webhook.js +54 -0
- package/icr-merge.js +58 -58
- package/icr.js +851 -745
- package/lang-json.js +211 -211
- package/lang-ruby.js +230 -230
- package/lang-toml.js +136 -136
- package/lang-yaml.js +183 -183
- package/limits.js +413 -394
- package/package.json +189 -164
- package/reconcile.cjs +614 -477
- package/slack-app-manifest-agent.json +61 -0
- package/slack-app-manifest-inbound.json +36 -0
- package/slack-app-manifest.json +27 -0
- package/substrate.js +252 -252
- package/sync.js +1956 -177
- package/grm-link.cjs +0 -141
package/hive-webhook.js
ADDED
|
@@ -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
|
+
}
|