hivecode-mcp 0.12.1 → 0.12.4
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/LICENSE +21 -21
- package/README.md +188 -8
- package/core.js +91 -91
- package/docs/HIVE_RULES.md +41 -41
- package/docs/MCP.md +5 -5
- package/docs/SELF_HOSTING.md +256 -256
- package/hive-activity.js +336 -310
- package/hive-await.js +187 -0
- package/hive-branch.js +656 -0
- package/hive-busy.js +88 -88
- package/hive-coord.js +110 -110
- package/hive-gate.js +135 -135
- package/hive-hook-install.js +179 -103
- package/hive-instructions.js +74 -66
- package/hive-listen.js +81 -0
- package/hive-mailbox.js +517 -386
- package/hive-mcp.js +483 -126
- package/hive-mux-proto.js +58 -58
- package/hive-mux.js +350 -350
- package/hive-obligations.js +247 -247
- package/hive-pr-staleness.js +189 -189
- package/hive-pr.js +337 -0
- package/hive-privacy.js +327 -288
- package/hive-receipts.js +233 -233
- package/hive-relay-health.js +7 -2
- package/hive-secrets.js +89 -0
- package/hive-setup.js +548 -445
- package/hive-stdio.js +172 -168
- package/hive-stop-hook.js +290 -241
- package/hive-throttle.js +118 -118
- package/hive-update.js +58 -58
- package/hive-version.js +110 -59
- package/icr-merge.js +58 -58
- package/icr.js +121 -15
- 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 +20 -11
- package/reconcile.cjs +614 -477
- package/substrate.js +252 -252
- package/sync.js +4691 -3005
- package/grm-link.cjs +0 -141
package/grm-link.cjs
DELETED
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
// Gives a Hivecode room a MEMORY, by handing its history to GRM.
|
|
2
|
-
//
|
|
3
|
-
// A room already records everything worth remembering -- HIVE_CHAT.md is the participants'
|
|
4
|
-
// conversation, HIVE_BOARD.md is what was rewritten and by whom, HIVE_TASKS.md is the
|
|
5
|
-
// directed work and its approvals. All of it is written to the room folder as ordinary
|
|
6
|
-
// markdown. And GRM already indexes named files from `code_roots.txt`, re-read on every
|
|
7
|
-
// watcher pass, so a path added here takes effect without restarting anything.
|
|
8
|
-
//
|
|
9
|
-
// So the two halves need no new format, daemon or protocol between them: name those files,
|
|
10
|
-
// and an agent that joins the room tomorrow can ask what was decided in it last week --
|
|
11
|
-
// including in conversations it was never present for. Neither product can do that alone.
|
|
12
|
-
//
|
|
13
|
-
// CommonJS on purpose. sync.js is ESM and the VS Code extension is CJS; a .cjs file is the
|
|
14
|
-
// one shape both can load, so this exists ONCE. The duplicated reconcile()/HIVE_RULES text
|
|
15
|
-
// across those two files has already drifted twice and cost real bugs -- not repeating it.
|
|
16
|
-
|
|
17
|
-
const fs = require('fs')
|
|
18
|
-
const os = require('os')
|
|
19
|
-
const path = require('path')
|
|
20
|
-
const crypto = require('crypto')
|
|
21
|
-
|
|
22
|
-
// The room's own record. NOT the room folder: `code_roots.txt` carries a warning written
|
|
23
|
-
// after adding one directory appended 90,215 blocks in a single pass and had to be cut back
|
|
24
|
-
// out of the journal -- "a root is a whole repository unless you check". Naming four files
|
|
25
|
-
// gives the decision history and nothing else.
|
|
26
|
-
const HISTORY_FILES = ['HIVE_CHAT.md', 'HIVE_BOARD.md', 'HIVE_TASKS.md', 'HIVE_MEMBERS.md']
|
|
27
|
-
|
|
28
|
-
function grmHome() {
|
|
29
|
-
const env = (process.env.GRM_HOME || '').trim()
|
|
30
|
-
return env || path.join(os.homedir(), '.grm')
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function rootsPath() {
|
|
34
|
-
return path.join(grmHome(), 'code_roots.txt')
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Keyed on the resolved directory so the same room in two folders gets two blocks, and the
|
|
38
|
-
// same folder rejoined a hundred times keeps exactly one.
|
|
39
|
-
function tagFor(dir) {
|
|
40
|
-
return 'hivecode:' + crypto.createHash('sha256').update(path.resolve(dir)).digest('hex').slice(0, 12)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// The file is written by GRM's installer with a UTF-8 BOM. GRM's own reader strips it, but
|
|
44
|
-
// this must not *drop* it on the way through -- the file belongs to GRM, and a tool that
|
|
45
|
-
// silently reformats another tool's config is how you get a bug nobody can find.
|
|
46
|
-
function readRoots(p) {
|
|
47
|
-
const raw = fs.readFileSync(p, 'utf8')
|
|
48
|
-
return raw.charCodeAt(0) === 0xfeff ? { bom: '', body: raw.slice(1) } : { bom: '', body: raw }
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function writeRootsAtomic(p, bom, body) {
|
|
52
|
-
const tmp = p + '.hivecode.tmp'
|
|
53
|
-
fs.writeFileSync(tmp, bom + body, 'utf8')
|
|
54
|
-
fs.renameSync(tmp, p) // the watcher re-reads on every pass; never let it see a half-written file
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Drop any block previously written for this room. Returns the body without it.
|
|
58
|
-
function stripBlock(body, tag) {
|
|
59
|
-
const lines = body.split(/\r?\n/)
|
|
60
|
-
const out = []
|
|
61
|
-
let inside = false
|
|
62
|
-
for (const line of lines) {
|
|
63
|
-
if (!inside && line.startsWith('# >>> ' + tag)) { inside = true; continue }
|
|
64
|
-
if (inside) { if (line.startsWith('# <<< ' + tag)) inside = false; continue }
|
|
65
|
-
out.push(line)
|
|
66
|
-
}
|
|
67
|
-
return out.join('\n')
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function blockFor(dir, tag, files) {
|
|
71
|
-
const head = `# >>> ${tag} Hivecode room at ${path.resolve(dir)}`
|
|
72
|
-
const why = '# Added by Hivecode so this room\'s decisions are in memory. Safe to delete.'
|
|
73
|
-
const tail = `# <<< ${tag}`
|
|
74
|
-
return [head, why, ...files, tail].join('\n')
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Point GRM at this room's history. Idempotent and cheap: it rewrites `code_roots.txt`
|
|
79
|
-
* only when the set of existing history files has actually changed, so it is fine to call
|
|
80
|
-
* on every join and again whenever one of those files is written.
|
|
81
|
-
*
|
|
82
|
-
* Returns { linked, files, reason }. Never throws -- a memory that cannot be wired up is
|
|
83
|
-
* not a reason to fail somebody's join.
|
|
84
|
-
*/
|
|
85
|
-
function linkRoom(dir, log = () => {}) {
|
|
86
|
-
try {
|
|
87
|
-
const p = rootsPath()
|
|
88
|
-
|
|
89
|
-
// DO NOT CREATE THIS FILE. GRM's reader has three states, and they are not two:
|
|
90
|
-
// no file at all -> fall back to the default root; a file naming paths that all fail
|
|
91
|
-
// to resolve -> watch NOTHING, deliberately, so a typo is visible rather than buried.
|
|
92
|
-
// Creating one here containing only HIVE_*.md would therefore convert "GRM indexes my
|
|
93
|
-
// projects" into "GRM indexes this room and nothing else" the moment the paths lag
|
|
94
|
-
// behind the folder. Only ever append to a file GRM already owns.
|
|
95
|
-
if (!fs.existsSync(p)) return { linked: false, files: [], reason: 'GRM is not installed on this machine' }
|
|
96
|
-
|
|
97
|
-
const files = HISTORY_FILES
|
|
98
|
-
.map((f) => path.resolve(dir, f))
|
|
99
|
-
.filter((f) => { try { return fs.statSync(f).isFile() } catch { return false } })
|
|
100
|
-
if (!files.length) return { linked: false, files: [], reason: 'room has not written its history yet' }
|
|
101
|
-
|
|
102
|
-
const tag = tagFor(dir)
|
|
103
|
-
const { bom, body } = readRoots(p)
|
|
104
|
-
const stripped = stripBlock(body, tag)
|
|
105
|
-
const next = stripped.replace(/\s*$/, '') + '\n\n' + blockFor(dir, tag, files) + '\n'
|
|
106
|
-
|
|
107
|
-
if (bom + next === bom + body) return { linked: true, files, reason: 'already linked' } // no write, no watcher churn
|
|
108
|
-
writeRootsAtomic(p, bom, next)
|
|
109
|
-
log(`[hive] room history handed to GRM (${files.length} file(s)) -- agents here can now recall what this room decided before they joined.`)
|
|
110
|
-
return { linked: true, files, reason: 'linked' }
|
|
111
|
-
} catch (e) {
|
|
112
|
-
log(`[hive] could not hand this room's history to GRM: ${e && e.message}`)
|
|
113
|
-
return { linked: false, files: [], reason: String((e && e.message) || e) }
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Remove this room from GRM's roots. Called on leave, so a room you are no longer in stops
|
|
119
|
-
* contributing to what your agents recall.
|
|
120
|
-
*/
|
|
121
|
-
function unlinkRoom(dir, log = () => {}) {
|
|
122
|
-
try {
|
|
123
|
-
const p = rootsPath()
|
|
124
|
-
if (!fs.existsSync(p)) return { unlinked: false, reason: 'GRM is not installed on this machine' }
|
|
125
|
-
const tag = tagFor(dir)
|
|
126
|
-
const { bom, body } = readRoots(p)
|
|
127
|
-
if (!body.includes('# >>> ' + tag)) return { unlinked: false, reason: 'was not linked' }
|
|
128
|
-
writeRootsAtomic(p, bom, stripBlock(body, tag).replace(/\s*$/, '') + '\n')
|
|
129
|
-
log('[hive] room history removed from GRM.')
|
|
130
|
-
return { unlinked: true, reason: 'unlinked' }
|
|
131
|
-
} catch (e) {
|
|
132
|
-
return { unlinked: false, reason: String((e && e.message) || e) }
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/** Whether GRM is present at all -- used to decide whether to mention memory in the UI. */
|
|
137
|
-
function grmPresent() {
|
|
138
|
-
try { return fs.existsSync(rootsPath()) } catch { return false }
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
module.exports = { linkRoom, unlinkRoom, grmPresent, rootsPath, HISTORY_FILES }
|